Skip to content

feat: white-label branding support#18

Merged
tac0turtle merged 5 commits intomainfrom
pierrick/white-labeling
Mar 19, 2026
Merged

feat: white-label branding support#18
tac0turtle merged 5 commits intomainfrom
pierrick/white-labeling

Conversation

@pthmas
Copy link
Collaborator

@pthmas pthmas commented Feb 25, 2026

Summary

  • Add runtime-configurable branding via 7 optional env vars (CHAIN_NAME, CHAIN_LOGO_URL, ACCENT_COLOR, BACKGROUND_COLOR_DARK, BACKGROUND_COLOR_LIGHT, SUCCESS_COLOR, ERROR_COLOR)
  • New backend GET /api/config endpoint serves branding config from environment variables
  • Frontend BrandingContext fetches config once on load, applies CSS custom properties for accent colors and derives surface palettes from background colors
  • Logo, chain name, favicon, and page title update dynamically
  • All values optional — defaults match current Atlas branding when unset

Test plan

  • Set custom CHAIN_NAME and ACCENT_COLOR in .env, verify title/links/buttons change
  • Set BACKGROUND_COLOR_DARK / BACKGROUND_COLOR_LIGHT, verify surface colors adapt in both themes
  • Set CHAIN_LOGO_URL pointing to mounted branding asset, verify logo in navbar and welcome page
  • Verify with no branding env vars set, UI looks identical to current Atlas
  • Test dark/light theme toggle with custom backgrounds
  • Verify bunx vite build succeeds
  • Verify docker compose build && docker compose up -d works end-to-end

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added customizable branding configuration: set chain name, logo, accent color, and theme colors via environment variables
    • Custom logos now display in navbar, welcome page, and favicon
    • Dynamic color theming applied across UI buttons, badges, and status indicators
  • Documentation

    • Added branding configuration guide with setup instructions

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces white-label/branding configuration to allow customization of UI elements (chain name, logo, accent colors, background colors) across the application. Adds backend environment variable support and API endpoint to serve branding config; adds frontend React context to consume config, derive color palettes, and apply theming via CSS custom properties; includes documentation, Docker configuration updates, and .gitignore rules.

Changes

Cohort / File(s) Summary
Configuration & Documentation
.env.example, README.md, docs/WHITE_LABELING.md
Added branding configuration variables template, documentation links, and comprehensive white-labeling guide explaining environment variables, logo/favicon rendering, Docker mounting, development workflow, and runtime color application flow.
Infrastructure & Deployment
docker-compose.yml, frontend/nginx.conf, .gitignore, frontend/.gitignore
Updated Docker Compose services to inject branding environment variables into backend and mount branding directory in frontend; added Nginx route for /branding/ static content with 1-hour cache; added gitignore rules for branding directory.
Backend Configuration & API
backend/crates/atlas-server/src/config.rs, backend/crates/atlas-server/src/api/mod.rs, backend/crates/atlas-server/src/api/handlers/config.rs, backend/crates/atlas-server/src/main.rs
Extended Config struct to parse branding environment variables; added new AppState fields for branding metadata; implemented GET /api/config endpoint returning BrandingConfig JSON; added helper for optional environment variable parsing with whitespace trimming.
Backend Test Updates
backend/crates/atlas-server/src/api/handlers/faucet.rs, backend/crates/atlas-server/src/api/handlers/status.rs, backend/crates/atlas-server/src/api/handlers/mod.rs
Updated test helpers to initialize new branding fields in AppState construction; exposed config handler module.
Frontend Context & State Management
frontend/src/context/branding-context.ts, frontend/src/context/BrandingContext.tsx, frontend/src/hooks/useBranding.ts, frontend/src/api/config.ts
Created BrandingContext and BrandingContextValue interface for state; implemented BrandingProvider that fetches config on mount, updates document title/favicon, derives color palettes from theme, and applies CSS custom properties; added useBranding() hook; added getConfig() API client function.
Frontend UI Integration
frontend/src/App.tsx, frontend/src/components/Layout.tsx, frontend/src/pages/WelcomePage.tsx
Wrapped app tree with BrandingProvider; updated Layout and WelcomePage to use useBranding() for dynamic chain name and logo URL; changed status indicator color to accent theme; updated accessibility labels and image alt text.
Frontend Theming & Styling
frontend/src/utils/color.ts, frontend/src/index.css, frontend/tailwind.config.js
Added color utility functions (deriveSurfaceShades, applyPalette) for HEX/RGB/HSL conversion and palette generation; updated CSS custom properties for accent colors; changed button/badge styling to use CSS variables instead of hard-coded values; updated Tailwind config to reference accent color variables.

Sequence Diagram

sequenceDiagram
    participant Client as Frontend (Browser)
    participant Provider as BrandingProvider
    participant API as Backend API
    participant Config as Environment Config
    participant DOM as DOM / CSS

    Client->>Provider: Mount App
    Provider->>API: GET /api/config
    API->>Config: Read env vars<br/>(CHAIN_NAME, LOGO_URL, colors)
    API-->>Provider: Return BrandingConfig JSON
    Provider->>Provider: Parse config<br/>Update state
    Provider->>DOM: Set document.title<br/>Update favicon link
    Provider->>DOM: Apply CSS custom properties<br/>(--color-accent-primary, etc.)
    alt Theme is Dark/Light with base color
        Provider->>Provider: deriveSurfaceShades(baseHex)
        Provider->>DOM: applyPalette(palette)<br/>Set surface/text/border vars
    end
    Provider-->>Client: Render BrandingContext.Provider
    Client->>Client: Components read<br/>useBranding() hook
    Client->>DOM: Update logo src<br/>Chain name in header/title
    DOM-->>Client: Display branded UI<br/>with custom colors
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • tac0turtle

Poem

🐰 A rabbit hops through colors fresh and new,
From logos bright to accent hues so true!
With CSS magic and a branding spree,
Each chain now shines the way it's meant to be! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: white-label branding support' directly and clearly describes the main objective of this PR, which is to add white-label branding support with runtime-configurable environment variables across both backend and frontend.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pierrick/white-labeling
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pthmas pthmas marked this pull request as ready for review February 27, 2026 13:41
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
.env.example (1)

23-27: Consider quoting hex color values for parser compatibility.

Some .env parsers may interpret # as the start of a comment, even without a preceding space. Quoting ensures the full hex value is captured.

🔧 Suggested fix
-ACCENT_COLOR=#dc2626                  # Primary accent color (links, buttons, active states)
-BACKGROUND_COLOR_DARK=#050505         # Dark mode base background
-BACKGROUND_COLOR_LIGHT=#f4ede6        # Light mode base background
-SUCCESS_COLOR=#22c55e                 # Success indicator color
-ERROR_COLOR=#dc2626                   # Error indicator color
+ACCENT_COLOR="#dc2626"                # Primary accent color (links, buttons, active states)
+BACKGROUND_COLOR_DARK="#050505"       # Dark mode base background
+BACKGROUND_COLOR_LIGHT="#f4ede6"      # Light mode base background
+SUCCESS_COLOR="#22c55e"               # Success indicator color
+ERROR_COLOR="#dc2626"                 # Error indicator color
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 23 - 27, The hex color values in the .env example
(ACCENT_COLOR, BACKGROUND_COLOR_DARK, BACKGROUND_COLOR_LIGHT, SUCCESS_COLOR,
ERROR_COLOR) may be parsed incorrectly due to the leading #; update each
variable to wrap the hex value in quotes (e.g., change ACCENT_COLOR=#dc2626 to
ACCENT_COLOR="#dc2626") and apply the same quoting to any other color vars in
the file so parsers don't treat # as a comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/crates/atlas-api/src/main.rs`:
- Line 51: The CHAIN_NAME env handling currently accepts an empty string; change
the retrieval logic for chain_name (the variable initialized where
std::env::var("CHAIN_NAME") is called) to treat empty or whitespace-only values
as unset by checking the env var result and falling back to "Atlas" when the
value is Err or when the Ok string is empty/only whitespace (e.g., trim and test
is_empty), so chain_name uses the default branding unless a non-empty value is
provided.

In `@docs/WHITE_LABELING.md`:
- Around line 5-13: The docs currently conflict on the default branding: the
intro claims the explorer falls back to "ev-node" branding while the CHAIN_NAME
table lists the default as "Atlas"; pick one canonical default and make the text
consistent by updating either the opening paragraph (replace "ev-node" with
"Atlas") or the table default (replace "`Atlas`" with "`ev-node`"), and ensure
the CHAIN_NAME entry and any other references to default branding in
WHITE_LABELING.md use that same value.

In `@frontend/src/context/BrandingContext.tsx`:
- Line 76: The forEach callback currently implicitly returns the result of
root.style.removeProperty(v) (vars.forEach(v => root.style.removeProperty(v));)
which violates the useIterableCallbackReturn rule; change the callback to a
block body so it returns void (e.g., vars.forEach(v => {
root.style.removeProperty(v); });) making sure the callback for vars.forEach
does not return any value.

In `@frontend/src/utils/color.ts`:
- Around line 34-40: Normalize and validate the input inside hexToRgb: trim and
ensure it starts with or without '#', accept 3- or 6-character hex forms (expand
3-char shorthand to 6-char), verify the final cleaned string is exactly 6 hex
digits (0-9a-f/A-F), and if validation fails either throw a clear error or
return a safe default RGB; then parse r/g/b with parseInt on the validated
string to avoid NaN channels. Ensure these checks are implemented in hexToRgb so
downstream CSS variable generation never receives invalid channel values.

---

Nitpick comments:
In @.env.example:
- Around line 23-27: The hex color values in the .env example (ACCENT_COLOR,
BACKGROUND_COLOR_DARK, BACKGROUND_COLOR_LIGHT, SUCCESS_COLOR, ERROR_COLOR) may
be parsed incorrectly due to the leading #; update each variable to wrap the hex
value in quotes (e.g., change ACCENT_COLOR=#dc2626 to ACCENT_COLOR="#dc2626")
and apply the same quoting to any other color vars in the file so parsers don't
treat # as a comment.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a488e55 and bc045f9.

📒 Files selected for processing (18)
  • .env.example
  • README.md
  • backend/crates/atlas-api/src/handlers/config.rs
  • backend/crates/atlas-api/src/handlers/mod.rs
  • backend/crates/atlas-api/src/main.rs
  • docker-compose.yml
  • docs/WHITE_LABELING.md
  • frontend/nginx.conf
  • frontend/src/App.tsx
  • frontend/src/api/config.ts
  • frontend/src/components/Layout.tsx
  • frontend/src/context/BrandingContext.tsx
  • frontend/src/context/branding-context.ts
  • frontend/src/hooks/useBranding.ts
  • frontend/src/index.css
  • frontend/src/pages/WelcomePage.tsx
  • frontend/src/utils/color.ts
  • frontend/tailwind.config.js

@pthmas pthmas requested a review from tac0turtle February 27, 2026 14:12
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/crates/atlas-api/src/main.rs (1)

17-30: ⚠️ Potential issue | 🟠 Major

AppState.chain_id bootstrap is still missing.

Line 17-Line 30 has no chain_id: u64 field, and Line 45-Line 94 does not fetch eth_chainId once at startup. This leaves the main.rs startup contract incomplete.

Based on learnings: "Applies to /atlas-api//main.rs : Fetch chain ID from RPC using eth_chainId once at startup and store in AppState.chain_id as u64".

Also applies to: 45-94

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/crates/atlas-api/src/main.rs` around lines 17 - 30, Add a chain_id:
u64 field to the AppState struct and populate it once during startup by calling
eth_chainId on the configured RPC URL; specifically, update the AppState
definition to include pub chain_id: u64, then in main (the
startup/initialization block where PgPool, rpc_url, solc_path, and admin_api_key
are created) perform a single JSON-RPC call to eth_chainId using the rpc_url,
parse the hex result to a u64, and assign that value to AppState.chain_id before
constructing the AppState instance (handle RPC errors gracefully and bubble them
up or log and exit).
🧹 Nitpick comments (1)
backend/crates/atlas-api/src/main.rs (1)

55-68: Normalize whitespace-only optional branding vars as unset.

Line 57, Line 58, Line 61, Line 64, Line 67, and Line 68 only check is_empty(), so values like " " are treated as configured. Consider trimming like CHAIN_NAME for consistent optional behavior.

Proposed refactor
 let chain_logo_url = std::env::var("CHAIN_LOGO_URL")
     .ok()
-    .filter(|s| !s.is_empty());
-let accent_color = std::env::var("ACCENT_COLOR").ok().filter(|s| !s.is_empty());
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
+let accent_color = std::env::var("ACCENT_COLOR")
+    .ok()
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
 let background_color_dark = std::env::var("BACKGROUND_COLOR_DARK")
     .ok()
-    .filter(|s| !s.is_empty());
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
 let background_color_light = std::env::var("BACKGROUND_COLOR_LIGHT")
     .ok()
-    .filter(|s| !s.is_empty());
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
 let success_color = std::env::var("SUCCESS_COLOR")
     .ok()
-    .filter(|s| !s.is_empty());
-let error_color = std::env::var("ERROR_COLOR").ok().filter(|s| !s.is_empty());
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
+let error_color = std::env::var("ERROR_COLOR")
+    .ok()
+    .map(|s| s.trim().to_string())
+    .filter(|s| !s.is_empty());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/crates/atlas-api/src/main.rs` around lines 55 - 68, The optional
branding env vars (chain_logo_url, accent_color, background_color_dark,
background_color_light, success_color, error_color) currently only check
.is_empty(), so whitespace-only values remain treated as set; update each
std::env::var(...).ok().filter(|s| !s.is_empty()) to trim the string before
emptiness check (e.g., .filter(|s| !s.trim().is_empty()) or map to
s.trim().to_string() then check), mirroring the handling used for CHAIN_NAME, so
whitespace-only values are normalized to unset.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/crates/atlas-api/src/main.rs`:
- Around line 51-54: The chain_name fallback is incorrect: update the code that
sets chain_name (the
std::env::var("CHAIN_NAME").ok().filter(...).unwrap_or_else(...) expression) to
return "Unknown" instead of "Atlas" so the project uses the required default
when CHAIN_NAME is not provided.

In `@docs/WHITE_LABELING.md`:
- Around line 27-34: The fenced code block that starts with ``` in
WHITE_LABELING.md is untyped and triggers MD040; update the opening fence to
include a language tag (e.g., change ``` to ```text) so the block becomes a
typed fenced code block and resolves the lint warning for the snippet showing
the atlas/ directory tree.

---

Outside diff comments:
In `@backend/crates/atlas-api/src/main.rs`:
- Around line 17-30: Add a chain_id: u64 field to the AppState struct and
populate it once during startup by calling eth_chainId on the configured RPC
URL; specifically, update the AppState definition to include pub chain_id: u64,
then in main (the startup/initialization block where PgPool, rpc_url, solc_path,
and admin_api_key are created) perform a single JSON-RPC call to eth_chainId
using the rpc_url, parse the hex result to a u64, and assign that value to
AppState.chain_id before constructing the AppState instance (handle RPC errors
gracefully and bubble them up or log and exit).

---

Nitpick comments:
In `@backend/crates/atlas-api/src/main.rs`:
- Around line 55-68: The optional branding env vars (chain_logo_url,
accent_color, background_color_dark, background_color_light, success_color,
error_color) currently only check .is_empty(), so whitespace-only values remain
treated as set; update each std::env::var(...).ok().filter(|s| !s.is_empty()) to
trim the string before emptiness check (e.g., .filter(|s| !s.trim().is_empty())
or map to s.trim().to_string() then check), mirroring the handling used for
CHAIN_NAME, so whitespace-only values are normalized to unset.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0a0d972-42cd-42be-b6dd-536c88ac6b04

📥 Commits

Reviewing files that changed from the base of the PR and between bc045f9 and 451a21d.

📒 Files selected for processing (3)
  • backend/crates/atlas-api/src/main.rs
  • docs/WHITE_LABELING.md
  • frontend/src/utils/color.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/utils/color.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Line 22: Replace the whitespace placeholder assignment for CHAIN_LOGO_URL with
a clean empty value (use CHAIN_LOGO_URL=) and move the explanatory text into a
commented line (e.g., # URL or path to logo (e.g., /branding/logo.png). Default:
bundled ev-node logo) so the env var is empty by default and the description
remains for contributors; update the CHAIN_LOGO_URL line in the example file
accordingly.
- Around line 23-27: Update the .env.example so the hex color values are quoted
to avoid parser/linter issues: wrap the values for ACCENT_COLOR,
BACKGROUND_COLOR_DARK, BACKGROUND_COLOR_LIGHT, SUCCESS_COLOR, and ERROR_COLOR in
quotes (e.g., " #... " or ' #... ') while keeping the existing inline comments
intact; this ensures cross-tool compatibility for dotenv parsers and linters
without changing the variable names or comments.

In `@backend/crates/atlas-api/src/main.rs`:
- Around line 55-68: Env vars for optional branding (chain_logo_url,
accent_color, background_color_dark, background_color_light, success_color,
error_color) should be normalized by trimming whitespace before empty-checking;
update each initializer to trim the retrieved String (e.g., via mapping to
s.trim().to_string() or using filter_map with s.trim()) and then filter out
empty results so whitespace-only values are treated as empty and not passed to
the frontend.
- Around line 51-68: Add a #[cfg(test)] mod tests block in the same file that
exercises the new env-parsing logic by setting and unsetting environment
variables and asserting the resulting values of chain_name, chain_logo_url,
accent_color, background_color_dark, background_color_light, success_color, and
error_color; specifically write tests for (1) unset vars producing None or the
default "Unknown" for chain_name, (2) empty-string vars treated as missing, and
(3) whitespace-only chain_name trimmed to default, using std::env::set_var and
std::env::remove_var to control state and re-evaluating the parsing code (call
the same expressions or extract the parsing into a small helper you can invoke
in tests), and run via cargo test --workspace.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b624a402-1e4c-4f38-9404-4e900ecf6480

📥 Commits

Reviewing files that changed from the base of the PR and between 3b2e1b5 and 6090c62.

📒 Files selected for processing (5)
  • .env.example
  • backend/crates/atlas-api/src/main.rs
  • docker-compose.yml
  • docs/WHITE_LABELING.md
  • frontend/src/context/branding-context.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/context/branding-context.ts
  • docker-compose.yml
  • docs/WHITE_LABELING.md

Copy link
Contributor

@tac0turtle tac0turtle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does it look on a slow internets, the default color would throw things off before the api config is loaded or is there a loading screen?

@pthmas pthmas force-pushed the pierrick/white-labeling branch from 6090c62 to 610df4a Compare March 15, 2026 20:35
pthmas added 2 commits March 19, 2026 11:46
Ports the white-labeling feature from pierrick/white-labeling onto main
(post indexer+api merge into atlas-server).

- Backend: add 6 branding fields to Config and AppState, new /api/config
  handler returns chain name, logo URL, and color settings from env vars
- Frontend: BrandingContext fetches /api/config on startup, applies CSS
  custom properties for accent/background colors, updates logo/title
- Color utility derives full surface palette from a single base hex
- Docker: branding env vars and volume mount for logo assets
- Docs: WHITE_LABELING.md explains full configuration options
@pthmas pthmas force-pushed the pierrick/white-labeling branch from 6c1de97 to 83f68e9 Compare March 19, 2026 11:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/crates/atlas-server/src/api/mod.rs (1)

16-30: ⚠️ Potential issue | 🟠 Major

AppState still misses required solc_path and admin_api_key fields.

At Line 16, the API state struct includes the new branding fields, but the required solc_path: String and admin_api_key: Option<String> are still absent from the contract.

Suggested patch (struct contract)
 pub struct AppState {
     pub pool: PgPool,
     pub block_events_tx: broadcast::Sender<()>,
     pub head_tracker: Arc<HeadTracker>,
     pub rpc_url: String,
+    pub solc_path: String,
+    pub admin_api_key: Option<String>,
     pub faucet: Option<SharedFaucetBackend>,
     pub chain_id: u64,
     pub chain_name: String,
     pub chain_logo_url: Option<String>,
     pub accent_color: Option<String>,
     pub background_color_dark: Option<String>,
     pub background_color_light: Option<String>,
     pub success_color: Option<String>,
     pub error_color: Option<String>,
 }

As per coding guidelines: Configure AppState to include pool: PgPool (API pool only), block_events_tx: broadcast::Sender<()> (shared with indexer), rpc_url: String, solc_path: String, and optional admin_api_key.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/crates/atlas-server/src/api/mod.rs` around lines 16 - 30, The
AppState struct is missing the required solc_path and admin_api_key fields;
update the AppState definition to include solc_path: String and admin_api_key:
Option<String>, keeping the existing pool: PgPool (API pool only) and
block_events_tx: broadcast::Sender<()> (shared with indexer) and all other
branding fields unchanged so downstream code referencing AppState::solc_path and
AppState::admin_api_key compiles.
🧹 Nitpick comments (1)
backend/crates/atlas-server/src/api/mod.rs (1)

154-156: Add a router test for the new /api/config wiring.

Line 155 introduces a new route, but this file’s tests do not currently assert route availability/response.

Suggested test addition
+    #[tokio::test]
+    async fn config_route_is_available() {
+        let app = build_router(test_state(None), None);
+
+        let response = app
+            .oneshot(
+                Request::builder()
+                    .uri("/api/config")
+                    .body(Body::empty())
+                    .unwrap(),
+            )
+            .await
+            .unwrap();
+
+        assert_eq!(response.status(), StatusCode::OK);
+    }

As per coding guidelines: Add unit tests for new logic in a #[cfg(test)] mod tests block in the same file; run with cargo test --workspace.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/crates/atlas-server/src/api/mod.rs` around lines 154 - 156, Add a
unit test that asserts the new "/api/config" route is wired to
handlers::config::get_config by creating a #[cfg(test)] mod tests in the same
mod.rs and using the same router/actix_web test utilities used elsewhere in this
file to build the App, send a GET request to "/api/config", and assert the
response status/body (e.g., 200 and expected JSON/schema). Locate the router
setup where .route("/api/config", get(handlers::config::get_config)) is added
and mirror that setup in the test to ensure route availability; run the test
with cargo test --workspace.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/crates/atlas-server/src/config.rs`:
- Around line 134-137: The chain_name assignment is not trimming non-empty
values, so values like "  MyChain  " will keep surrounding whitespace; change
the chain_name construction (the env::var("CHAIN_NAME") / chain_name field) to
trim the string before filtering and storing (e.g., map the env::var result
through trim -> to_string, then filter empty, then unwrap_or_else to "Unknown")
so stored chain_name has no leading/trailing spaces.

In `@frontend/src/context/BrandingContext.tsx`:
- Around line 45-53: The branding color setters call hexToRgbTriplet directly
and can throw on malformed hex strings; wrap each hexToRgbTriplet call in a
try/catch (for example around the blocks that set '--color-accent-primary',
'--color-accent-success', '--color-accent-error' and the other theme vars later
in BrandingContext.tsx), and only call root.style.setProperty when parsing
succeeds; on parse failure either skip setting that CSS variable or set a safe
default CSS value, and log a debug/warn using the existing logger so a single
bad env value won't crash runtime theming.

---

Outside diff comments:
In `@backend/crates/atlas-server/src/api/mod.rs`:
- Around line 16-30: The AppState struct is missing the required solc_path and
admin_api_key fields; update the AppState definition to include solc_path:
String and admin_api_key: Option<String>, keeping the existing pool: PgPool (API
pool only) and block_events_tx: broadcast::Sender<()> (shared with indexer) and
all other branding fields unchanged so downstream code referencing
AppState::solc_path and AppState::admin_api_key compiles.

---

Nitpick comments:
In `@backend/crates/atlas-server/src/api/mod.rs`:
- Around line 154-156: Add a unit test that asserts the new "/api/config" route
is wired to handlers::config::get_config by creating a #[cfg(test)] mod tests in
the same mod.rs and using the same router/actix_web test utilities used
elsewhere in this file to build the App, send a GET request to "/api/config",
and assert the response status/body (e.g., 200 and expected JSON/schema). Locate
the router setup where .route("/api/config", get(handlers::config::get_config))
is added and mirror that setup in the test to ensure route availability; run the
test with cargo test --workspace.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8909987b-6fa2-4dec-807e-227e16a4a946

📥 Commits

Reviewing files that changed from the base of the PR and between 6090c62 and 83f68e9.

📒 Files selected for processing (25)
  • .env.example
  • .gitignore
  • README.md
  • backend/crates/atlas-server/src/api/handlers/config.rs
  • backend/crates/atlas-server/src/api/handlers/faucet.rs
  • backend/crates/atlas-server/src/api/handlers/mod.rs
  • backend/crates/atlas-server/src/api/handlers/status.rs
  • backend/crates/atlas-server/src/api/mod.rs
  • backend/crates/atlas-server/src/config.rs
  • backend/crates/atlas-server/src/main.rs
  • branding/.gitkeep
  • docker-compose.yml
  • docs/WHITE_LABELING.md
  • frontend/.gitignore
  • frontend/nginx.conf
  • frontend/src/App.tsx
  • frontend/src/api/config.ts
  • frontend/src/components/Layout.tsx
  • frontend/src/context/BrandingContext.tsx
  • frontend/src/context/branding-context.ts
  • frontend/src/hooks/useBranding.ts
  • frontend/src/index.css
  • frontend/src/pages/WelcomePage.tsx
  • frontend/src/utils/color.ts
  • frontend/tailwind.config.js
✅ Files skipped from review due to trivial changes (12)
  • frontend/.gitignore
  • frontend/src/hooks/useBranding.ts
  • README.md
  • .gitignore
  • frontend/src/App.tsx
  • .env.example
  • backend/crates/atlas-server/src/api/handlers/status.rs
  • frontend/src/api/config.ts
  • frontend/tailwind.config.js
  • frontend/src/context/branding-context.ts
  • docs/WHITE_LABELING.md
  • frontend/src/pages/WelcomePage.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/nginx.conf
  • docker-compose.yml
  • frontend/src/index.css
  • frontend/src/components/Layout.tsx

pthmas added 3 commits March 19, 2026 12:12
- Trim CHAIN_NAME before storing (prevents " MyChain " propagating)
- Wrap color parsing in try/catch in BrandingContext (bad hex won't crash theming)
- Fix getConfig() to return response directly, not response.data (custom fetch client, not axios)
Read cached config synchronously before first render so custom colors
are applied immediately. Background fetch revalidates and updates the
cache — stale-while-revalidate is safe since branding config is
admin-controlled and rarely changes.
On first visit (no localStorage cache), show a full-screen spinner using
neutral slate tones instead of rendering the app with unbranded default
colors. Once the /api/config response arrives and colors are applied to
:root, the spinner disappears and the app renders correctly.

Return visits are unaffected: cached config is read synchronously in the
useState initializer so loaded starts as true and the spinner never shows.
@tac0turtle tac0turtle merged commit 1c00648 into main Mar 19, 2026
8 checks passed
@tac0turtle tac0turtle deleted the pierrick/white-labeling branch March 19, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants