Files
CASAN/packages/casan-control-panel
2026-07-19 09:37:16 +07:00
..
2026-07-19 09:37:16 +07:00
2026-07-19 09:37:16 +07:00

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).

Metrics export: GET /api/v1/metrics provides Prometheus text exposition for aggregate freshness, run/failure/cost/token and H4/H5/action/incident counters. It intentionally contains no tenant, actor, trace, prompt or Evidence Pack labels. In production restrict the path at the authenticated reverse proxy to the monitoring network or service account.

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 /command UI 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. Requires x-casan-role with write permission; calls rbac-check.py before control-plane-settings.py set.
  • POST /api/v1/settings/rollback — governed rollback through the same core CLI.

Goal workspace context:

  • GET /api/v1/goals/projects — lists active project IDs and context roots from the harness-owned project registry after RBAC filtering; browser-supplied paths are never accepted.
  • POST /api/v1/goals requires { goal, projectId }. H1 resolves the registry again, produces a size-limited redacted manifest/snapshot, and gives the exact same snapshot to local and cloud models. Account-model CLIs remain inside an empty temporary sandbox.
  • Goals requesting workspace side effects make the producer and reviewer return a unified patch. CASAN validates its paths and preconditions, stores it as a tenant-scoped artifact, creates a goal.workspace.execute proposal, and finishes as requires_approval without modifying source files.
  • POST /api/v1/goals/:id/apply accepts only a patch whose proposal is already approved by a different actor. The executor verifies the artifact hash, applies it, runs fixed project build/test commands, and reverses the patch if verification fails.

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 from kill-switch.sh status.
  • POST /api/v1/kill-switch/engage — RBAC-gated engage (operator or 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 harness approval-inbox.py + delegation-policy.yaml.
  • POST /api/v1/approvals/decide — approve/reject with SoD and reason; strict mode or supplied approvalJwt is verified by harness approval-verify.sh; approved settings proposals apply through control-plane-settings.py.

Governed Chat (Plan-18 MVP-0/1/2):

  • POST /api/v1/chat/ask — Ask CASAN endpoint. The API only wraps harness chat-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 from operator-actions.yaml; no free-command execution is exposed.
  • GET /api/v1/chat/agents — list governed agents from agent-registry.yaml with role visibility; selected agent/skill/delegation are bound by harness chat-agent-resolver.py.
  • GET /api/v1/chat/audit/verify — verifies the chat audit hash chain.
  • GET /api/v1/chat/replay?chatId=<id>&tenant=<id> — verifies chat-chain integrity, evidence artifact hashes, and OPERATOR loop trace replay through harness chat-replay.py; non-default tenants read only their tenant partition.
  • GET /api/v1/command — includes the chat_loop Command Center widget backed by chat audit/replay evidence, loop ticker rows, and token budget gauge.
  • Delegation escalation returns ESCALATED and creates a pending chat.escalate proposal in the approval inbox.
  • CODEGEN requests are draft-only: codegen-draft outputs a state artifact, runs artifact-scan.sh + tool-output-scan.sh, certifies through Plan-17 loop-run, and never writes to the source tree.
  • Non-default tenant chat state is resolved through tenant-store.sh; replay path overrides are guarded, audit snapshots are encrypted with tenant-crypt.sh, and tenant kill-switch/quota checks run before chat work.
  • /chat UI shows actor/role scope, mode/risk/decision badges, certified answer, evidence sources, registered operator actions, agent binding, loop certification, codegen draft scan status, action-gate status, router details, and audit hash. Side-effect requests outside registered actions return governed BLOCK or NOT_SUPPORTED responses; operator side effects are held until the loop draft is certified.
  • /command UI renders the Chat/Loop widget in the existing evidence drawer flow.
  • /approvals UI accepts an approval JWT for strict reviewer identity checks.

FinOps/SLO:

  • /finops UI reads GET /api/v1/cost plus GET /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_usd when configured; otherwise it shows not 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

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 is intentionally a stronger, fail-closed contract: it requires a DNS-matching TLS certificate, enterprise OIDC, non-dev Vault Transit, an actual S3 Object Lock COMPLIANCE write, and CI-attested digest-pinned images. Follow the Linux handoff guide, then run:

CASAN_CP_PROD_ENV=infra/production/casan-prod.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.