Skip to content

gordon-console

Purpose

gordon-console is the operator UI for the Gordon v7 platform. It replaces the retired gordon-ui monolith. Built on Next.js 16 App Router with React 19 and Tailwind 4, it surfaces dashboards, bot control, symbol subscription, Loki log viewer, incident console, and deploy management. All stateful reads and writes flow through the gordon-manager BFF — the console holds no DB client, no trading keys, and no service-specific token surfaces other than the manager operator token. Live events arrive over the manager /ws WebSocket. Authentication is handled by Auth.js v5 + Kanidm OIDC with a three-layer auth model.

Version + port

FieldValue
Version1.28.0
Port3000 (Next.js default, behind nginx on srv-apps)
Imageghcr.io/dlepaux/gordon-console

Only this port is operator-exposed on srv-apps. All backend services are Docker-internal.

Auth model

LayerFileRole
Loopback binddocker-compose.ymlContainer binds 127.0.0.1:3000; only nginx can reach it
Edge fast-failsrc/proxy.tsRedirects unauthenticated requests to /api/auth/signin; PUBLIC_PATHS allowlist
DAL boundarysrc/lib/dal.tsverifyAuth() / withAuth() as first statement of every protected page, action, and route handler

check-dal-coverage.sh fails CI if any page/layout/action/route under src/app/ is missing a verifyAuth() call and not on the explicit allowlist.

Typed-client pipeline

All API and WS clients are generated from committed specs in specs/. Drift is caught by the gen:check pre-commit hook.

ScriptWhat it does
pnpm gen:restRuns cargo run -- openapi export per service → specs/<svc>.jsonsrc/api/generated/rest/<svc>.ts
pnpm gen:wsCopies <svc>/asyncapi.bundled.yamlspecs/ → Modelina → src/api/generated/ws/<svc>.ts
pnpm gen:checkReruns both generators; fails on any diff in specs/ or src/api/generated/

AsyncAPI schemas come from the @dlepaux/gordon-contracts-schemas npm package (GitHub registry). Raw fetch() / new WebSocket() outside generated clients is banned by grep-lints (CI + pre-commit).

NATS subjects

The console does not connect to NATS directly. Live events arrive via the gordon-manager /ws WebSocket. For stateless realtime subjects (future NATS-WS gateway), the browser will subscribe direct with a manager-issued JWT.

Database access

None. Zero DB client packages. Zero trading.* / market_data.* references in source. All reads and writes go through gordon-manager BFF.

Key env vars

VariablePurpose
AUTH_SECRETAuth.js session encryption secret
AUTH_URLAuth.js base URL
AUTH_TRUST_HOSTSet true behind nginx reverse proxy
AUTH_KANIDM_*Kanidm OIDC provider config (client ID, secret, issuer)
GORDON_MANAGER_OPERATOR_TOKENToken injected as X-Operator-Token on BFF mutation requests
GORDON_CONSOLE_E2E_AUTH_TOKENE2E auth token (e2e only, never set on srv-apps)
NPM_TOKENGitHub package registry token for @dlepaux/gordon-contracts-schemas

GORDON_RISK_OPERATOR_TOKEN is retired per DP-12. Manager now holds the forward token.

Invariants

  • Server-first. RSC by default; "use client" only at real interaction/browser-API boundaries.
  • No direct backend calls. All data reads and writes go through gordon-manager BFF.
  • One token surface. GORDON_MANAGER_OPERATOR_TOKEN is the only operator-bearer the console holds. It is injected as X-Operator-Token on every mutation. Never expose via NEXT_PUBLIC_*.
  • Design tokens enforced. Single dark theme via Tailwind 4 @theme in src/app/globals.css. No hex/rgb/oklch literals. No text-*/NN opacity modifiers. Enforced by grep-lints.
  • Typed clients from specs. No hand-written DTOs. gen:check enforces spec freshness on every commit.
  • Known BFF rule drift (DP-12, open). The console still calls POST /risk/resume directly on gordon-risk via riskWriterClient (src/app/(ops)/incident/_actions.ts). The manager-side POST /bff/risk/resume proxy is wired. The console update is the remaining open work for DP-12.

Status

R/W finalisation epic closed (story 12, 2026-04-29). All mutation surfaces shipped and audited. DP-12 console-side update is the one open item. Phase 7 cutover (Story 24) gates production use.

Gordon — keep compounding without blowing up