feat: Neptune customizations — dark mode, auth, libraries #5

Closed
Gravity Bot wants to merge 0 commits from feature/neptune-customizations into main
Collaborator

Summary

Neptune deployment customizations for the self-hosted Excalidraw MCP canvas at draw.bros.ninja.

Dark mode & theme persistence

  • Default dark theme with #f5faff canvas background
  • Dark navbar styling (#2a2a2a)
  • Theme preference persisted in localStorage across reloads

Library persistence & server-side loading

  • Library items saved/loaded from localStorage (survive page reloads)
  • New /api/libraries endpoint serves .excalidrawlib files from mounted /libraries volume
  • Frontend auto-loads server libraries on startup and merges with local ones

OIDC + token authentication

  • Hand-rolled OIDC implementation using openid-client v5
  • OAuth2 code flow with PKCE (S256), HMAC-signed session cookies
  • MCP_AUTH_TOKEN bearer token for MCP agent API access
  • All write API routes (POST/PUT/DELETE) require authentication
  • Read routes (GET, WebSocket) remain public for viewers
  • Frontend viewModeEnabled for unauthenticated users (pan/zoom only)
  • Login/logout in header, sync/clear buttons hidden for view-only

UI polish

  • Favicon (excalidraw SVG from dashboard-icons)
  • Title changed to "Excalidraw Canvas"
  • Navbar controls flattened, auth info pushed right

Infrastructure

  • Docker healthcheck uses node -e fetch(...) instead of wget (not in slim image)
  • extra_hosts for hairpin NAT workaround (auth.bros.ninja → Janus Tailscale IP)
  • trust proxy enabled for correct cookie behavior behind nginx TLS termination

Deployment notes

Requires .env with:

  • MCP_AUTH_TOKEN — shared with Claude Code MCP config
  • OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_SECRET — Authentik provider
  • OIDC_BASE_URL — public URL (e.g. https://draw.bros.ninja)
  • OIDC_COOKIE_SECRET — random string for session encryption

Test plan

  • Dark mode persists across page reloads
  • Libraries load from server on fresh browser
  • Unauthenticated users see view-only canvas (no tools, no sync)
  • OIDC login flow via Authentik works end-to-end
  • MCP agents can create/update/delete elements with bearer token
  • MCP agents get 401 without token
  • WebSocket real-time updates work for view-only users
## Summary Neptune deployment customizations for the self-hosted Excalidraw MCP canvas at `draw.bros.ninja`. ### Dark mode & theme persistence - Default dark theme with `#f5faff` canvas background - Dark navbar styling (`#2a2a2a`) - Theme preference persisted in localStorage across reloads ### Library persistence & server-side loading - Library items saved/loaded from localStorage (survive page reloads) - New `/api/libraries` endpoint serves `.excalidrawlib` files from mounted `/libraries` volume - Frontend auto-loads server libraries on startup and merges with local ones ### OIDC + token authentication - Hand-rolled OIDC implementation using `openid-client` v5 - OAuth2 code flow with PKCE (S256), HMAC-signed session cookies - `MCP_AUTH_TOKEN` bearer token for MCP agent API access - All write API routes (POST/PUT/DELETE) require authentication - Read routes (GET, WebSocket) remain public for viewers - Frontend `viewModeEnabled` for unauthenticated users (pan/zoom only) - Login/logout in header, sync/clear buttons hidden for view-only ### UI polish - Favicon (excalidraw SVG from dashboard-icons) - Title changed to "Excalidraw Canvas" - Navbar controls flattened, auth info pushed right ### Infrastructure - Docker healthcheck uses `node -e fetch(...)` instead of `wget` (not in slim image) - `extra_hosts` for hairpin NAT workaround (`auth.bros.ninja` → Janus Tailscale IP) - `trust proxy` enabled for correct cookie behavior behind nginx TLS termination ## Deployment notes Requires `.env` with: - `MCP_AUTH_TOKEN` — shared with Claude Code MCP config - `OIDC_ISSUER_URL`, `OIDC_CLIENT_ID`, `OIDC_SECRET` — Authentik provider - `OIDC_BASE_URL` — public URL (e.g. `https://draw.bros.ninja`) - `OIDC_COOKIE_SECRET` — random string for session encryption ## Test plan - [x] Dark mode persists across page reloads - [x] Libraries load from server on fresh browser - [x] Unauthenticated users see view-only canvas (no tools, no sync) - [x] OIDC login flow via Authentik works end-to-end - [x] MCP agents can create/update/delete elements with bearer token - [x] MCP agents get 401 without token - [x] WebSocket real-time updates work for view-only users
- Default to dark theme with #f5faff canvas background, persisted in localStorage
- Dark navbar styling (#2a2a2a) matching the dark mode aesthetic
- Library items saved/loaded from localStorage so they survive page reloads
- New /api/libraries endpoint serves .excalidrawlib files from mounted volume
- Frontend auto-loads server libraries on startup and merges with local ones
- Theme preference tracked via onChange and persisted across sessions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add express-openid-connect for OIDC authentication via Authentik
- MCP agents authenticate with Bearer token (MCP_AUTH_TOKEN env var)
- All write API routes (POST/PUT/DELETE) require authentication
- Read routes (GET, WebSocket) remain public for viewers
- Frontend checks /api/auth/status and enables viewModeEnabled for
  unauthenticated users (read-only canvas, no sync/clear buttons)
- Login/logout links shown in header when OIDC is configured
- Dark navbar (#2a2a2a) for dark mode theme
- MCP client (index.ts) sends auth token via apiHeaders() helper
- compose.yaml configured with env vars for both auth methods
- .env.example with Authentik setup instructions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: replace express-openid-connect with hand-rolled OIDC using openid-client
All checks were successful
CI / build-and-test (20.x) (pull_request) Successful in 1m1s
f09deb31a6
The express-openid-connect package had persistent cookie issues behind
the Janus reverse proxy (Secure flag, state cookie not persisting on
callback). Replaced with a clean implementation using openid-client v5:

- Manual OAuth2 code flow with PKCE (S256)
- State/nonce/code_verifier stored in simple HttpOnly cookie (no Secure flag)
- HMAC-SHA256 signed session cookies with in-memory session store
- /auth/login, /auth/callback, /auth/logout routes
- trust proxy enabled for X-Forwarded-Proto behind nginx

Also:
- Favicon added (excalidraw SVG from dashboard-icons)
- Title changed to "Excalidraw Canvas"
- Navbar cleanup: flattened controls, auth info pushed right with marginLeft auto
- Sync controls and clear button properly hidden for view-only users

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mike Bros closed this pull request 2026-03-19 14:24:24 +00:00
All checks were successful
CI / build-and-test (20.x) (pull_request) Successful in 1m1s

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
mike/mcp_excalidraw!5
No description provided.