199 lines
10 KiB
Markdown
199 lines
10 KiB
Markdown
# 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)
|
||
```bash
|
||
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`).
|
||
|
||
Harness reports:
|
||
|
||
- `GET /api/v1/reports` — versioned H1–H7 report catalog and availability.
|
||
- `GET /api/v1/reports/h6?project=<id>&from=<ISO>&to=<ISO>&run=<id>` — H6 AgentOps
|
||
report from the same runtime metrics, provider usage and alert files as the Control Panel.
|
||
- `GET /api/v1/reports/h6/export?format=html|json&...` — standalone, print-ready HTML
|
||
or machine-readable JSON export. Both formats serialize the same filtered report object;
|
||
no maturity score is hard-coded.
|
||
- `/reports/h6` — UI report view with project, time-range and run filters plus source-level
|
||
freshness and data-quality warnings.
|
||
|
||
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`). The canonical source variables are
|
||
`CASAN_TELEMETRY_METRICS_LOG`, `CASAN_TELEMETRY_PROVIDER_LOG`, and
|
||
`CASAN_TELEMETRY_ALERTS_LOG`; legacy dashboard/control-panel names remain accepted as
|
||
compatibility aliases. Freshness is calculated independently from each file's mtime using
|
||
`CASAN_DASHBOARD_STALE_S` (default `3600`).
|
||
|
||
## 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
|
||
```bash
|
||
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
|
||
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](../../infra/production/README.md), then run:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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`.
|