7.8 KiB
CASAN Ops Console (Plan-13 Track 1/2/3/4 + Command Center) — Control Panel
Real NestJS API + React UI that surfaces CASAN harness telemetry and governed settings
management. This is the Level-3 casan-platform Control Panel component.
Monitoring remains read-only ("Đọc ≠ Ghi"). Settings writes go through RBAC and the
harness-owned governance CLI; the UI never writes harness files directly or bypasses a gate.
backend/ NestJS API (/api/v1 + /healthz) over .specify telemetry + governed settings
frontend/ React + Vite + Tailwind + TanStack Query Ops Console + Settings/Approvals/Kill-switch/FinOps/Command/Chat pages
Run (local)
npm install # from repo root (picks up the workspaces)
npm run console:api # NestJS API → http://127.0.0.1:3010/api/v1
npm run console:ui # Vite UI → http://127.0.0.1:5174 (proxies to the API)
Open http://127.0.0.1:5174 — panels show REAL metrics from .specify/logs/**.
API (ok()-enveloped except /healthz)
GET /api/v1/overview · runs (+ runs/:traceId) · governance · security ·
incidents · tools · traceability · drift · cost · GET /healthz (200 fresh /
503 stale — fail-loud, mirrors dashboard-server.py).
Command Center:
GET /api/v1/command— Plan-13 §8.6 read-only executive surface. Returns eight evidence-backed widgets with provenance envelopes{source, artifact_path, commit, run_at, verified, status}, plus executive briefing rows and a live governance ticker. The/commandUI exposes the same data with a VI/EN briefing toggle and evidence drawer.
Settings management:
GET /api/v1/settings— list policy/current values/audit tail; viewer-readable.POST /api/v1/settings— governed setting write. Requiresx-casan-rolewith write permission; callsrbac-check.pybeforecontrol-plane-settings.py set.POST /api/v1/settings/rollback— governed rollback through the same core CLI.
Local management headers: x-casan-actor, x-casan-role, x-casan-project,
x-casan-tenant. Missing role defaults to viewer, so writes fail closed.
Kill-switch management:
GET /api/v1/kill-switch— list active kill-switches fromkill-switch.sh status.POST /api/v1/kill-switch/engage— RBAC-gated engage (operatoror stronger).POST /api/v1/kill-switch/clear— RBAC-gated clear (org-admin; strict approval remains enforced by the harness CLI in production mode).
Approval inbox / HITL:
GET /api/v1/approvals?status=pending— list proposals and oversight tail.POST /api/v1/approvals/submit— submit a governed proposal; delegation is resolved by harnessapproval-inbox.py+delegation-policy.yaml.POST /api/v1/approvals/decide— approve/reject with SoD and reason; strict mode or suppliedapprovalJwtis verified by harnessapproval-verify.sh; approved settings proposals apply throughcontrol-plane-settings.py.
Governed Chat (Plan-18 MVP-0/1 + MVP-2 Track 4, Operator Track 5/6, and Track 8.1-8.4):
POST /api/v1/chat/ask— Ask CASAN endpoint. The API only wraps harnesschat-turn.py; router verdicts, H4 input/output scan, action-gate decisions, Plan-17 loop-run certification, H5 chat audit, H6 token metrics, evidence source selection, and operator action execution remain harness-owned.GET /api/v1/chat/actions— list registered operator actions fromoperator-actions.yaml; no free-command execution is exposed.GET /api/v1/chat/agents— list governed agents fromagent-registry.yamlwith role visibility; selected agent/skill/delegation are bound by harnesschat-agent-resolver.py.GET /api/v1/chat/audit/verify— verifies the chat audit hash chain.GET /api/v1/chat/replay?chatId=<id>— verifies chat-chain integrity, evidence artifact hashes, and OPERATOR loop trace replay through harnesschat-replay.py.GET /api/v1/command— includes thechat_loopCommand Center widget backed by chat audit/replay evidence, loop ticker rows, and token budget gauge.- Delegation escalation returns
ESCALATEDand creates a pendingchat.escalateproposal in the approval inbox. /chatUI shows actor/role scope,mode/risk/decisionbadges, certified answer, evidence sources, registered operator actions, agent binding, loop certification, action-gate status, router details, and audit hash. Side-effect requests outside registered actions return governedBLOCKorNOT_SUPPORTEDresponses; operator side effects are held until the loop draft is certified./commandUI renders the Chat/Loop widget in the existing evidence drawer flow./approvalsUI accepts an approval JWT for strict reviewer identity checks.
FinOps/SLO:
/finopsUI readsGET /api/v1/costplusGET /api/v1/settings.- Provider cost/tokens come from provider usage telemetry.
- KPI/SLO tiles come from
14-business-kpi-report.json. - Budget status uses
cost.absolute_cap_usdwhen configured; otherwise it showsnot configured.
Data sources + aggregation mirror packages/casan-harness/tests/generate-agentops-dashboard.py.
App root + telemetry paths resolve via the same marker walk-up as casan-paths.sh
(.specify or packages/casan-harness) and honor CASAN_DASHBOARD_* env overrides.
Security posture (MVP)
Binds 127.0.0.1 by default. Refuses a non-loopback bind under CASAN_PROFILE=prod /
CASAN_CP_STRICT=1 unless CASAN_CP_TRUST_AUTH_PROXY=1 is set for an authenticated reverse
proxy that overwrites identity headers. Management endpoints are RBAC-gated via the harness
rbac-check.py; IdP group claims such as casan-approver are mapped to RBAC roles through
the same harness engine.
Test
npm run console:test # backend telemetry/settings/approvals/kill-switch/auth mapping/command/chat contract
npm run console:build # backend tsc + frontend typecheck/vite build
Production-Like Smoke
docker compose -f docker-compose.control-panel.yml config
bash packages/casan-control-panel/scripts/local-prod-smoke.sh
The scaffold includes Dockerfile.control-panel-api, Dockerfile.control-panel-ui, and
nginx/control-panel.conf. Nginx protects UI/API through oauth2-proxy auth_request,
overwrites browser-supplied X-CASAN-* headers, and passes IdP group claims to the API for
RBAC mapping. The local smoke starts a self-signed HTTPS stack with a mock OIDC IdP and
expects CP_LOCAL_SMOKE_PASS https_oidc=true actor=oidc-ops role=org-admin; it also
asserts the Command Center returns all nine widgets with provenance envelopes and invokes
managed-prod-smoke.sh with the authenticated mock-IdP cookie jar. A passing local run
therefore emits both CP_LOCAL_SMOKE_PASS ... and
CP_MANAGED_SMOKE_PASS actor=oidc-ops role=org-admin widgets=9.
Managed production readiness, once the host has real TLS files and an enterprise OIDC env file:
CASAN_CP_TLS_DIR=/opt/casan-control-panel/tls \
CASAN_CP_OAUTH_ENV=/opt/casan-control-panel/oauth2-proxy.env \
bash packages/casan-control-panel/scripts/prod-readiness-check.sh
Managed production endpoint smoke, once DNS/TLS/OIDC are deployed:
CASAN_CP_BASE_URL=https://control-panel.example.com \
bash packages/casan-control-panel/scripts/managed-prod-smoke.sh
That unauthenticated smoke must report auth protection and spoofed-header blocking. To also verify authenticated identity mapping and Command Center behind the enterprise IdP, pass an exported browser cookie jar for a real logged-in session:
CASAN_CP_BASE_URL=https://control-panel.example.com \
CASAN_CP_COOKIE_JAR=/secure/path/control-panel-cookies.txt \
bash packages/casan-control-panel/scripts/managed-prod-smoke.sh
Not in this pass
Live managed host/cert/enterprise IdP traffic cutover. See
docs/plans/CASAN_PLAN_13_CONTROL_PLANE.md.