Local Dashboard (nova serve) — User Reference

Status: Experimental. Read this entire page before relying on the dashboard for anything that matters. Per: ADR-0027, Non-goals First shipped: v0.7

The dashboard is an opt-in local-only HTTP server (nova serve --experimental) that renders an interactive view over the same registry SQLite, lineage SQLite, and capsule directories the CLI reads. The CLI is the canonical interface; the dashboard is a satellite that surfaces the equivalent CLI command for every action.


Dashboard tabs (complete inventory)

The dashboard ships 29 tabs in 7 balanced workflow groups (Overview, Runs & Debug, Govern & Promote, Provenance & Trust, Compliance, Platform, Reports & Export). Every tab has a stable two-key navigation shortcut — press g then the tab's key (shown on hover and in the ? overlay). Since v0.98.3 the sidebar groups start collapsed (each showing its tab count); the group containing the active tab is always expanded, and manual expand/collapse persists per browser in novafabric.sidebar-groups. Below 1024px the sidebar auto-collapses to the icon rail (v0.97.0); expanding it explicitly wins for the rest of the session. (Source of truth: web/src/components/dashboard/Sidebar.tsx; verified by count against NAV_GROUPS in that file.)

Deep links. Every tab is addressable as ?tab=<id>, and the Compliance tab is a hub whose five panel groups are addressable as ?tab=compliance&sub=<group> (frameworks · audits · privacy · exports · assurance, v0.97.0) — the same groups the ⌘K palette lists. Since v0.98.2 all three navigation paths write the URL: clicking a sidebar item, a g-key sequence, and the command palette all update ?tab= (and clear a stale ?sub= when you leave the hub), so any view you reach by keyboard is shareable and survives a reload. Tab ids have not changed since before the v0.97.0 regrouping, so existing links still resolve.

Tab Group Since What it does
Home Overview v0.8 Journey cards, status bar, resume session
Analytics Overview v0.62.0 Time-bucketed run analytics from the runs index: volume + failure stacked bars, duration p50/p95 lines, stat tiles, 7/30/90-day ranges, chart/table toggle (/api/analytics/summary)
Alerts Platform v0.63.0 Operational alerts feed (quota/rate-limit/policy/drift/seal/backup) + delivery outcomes from the audit log; severity badges, stat tiles, live refresh (/api/alerts/recent, ADR-0192)
Runs Runs & Debug v0.7 Run list, search/filter, inspect capsule, validate, replay, verify; capsule tree, run lineage edges, secret scan (v0.46.0); v0.64.0: per-run Forensics timeline view (/api/runs/{id}/forensics-timeline, P5) + saved filter presets (E2)
Diff Runs & Debug v0.7 N-run comparison (2–5), word-level diff, mutation badges
Registry Govern & Promote v0.7 Asset lifecycle: eval, promote, rollback, register, suggest-register, unregister
Governance Govern & Promote v0.16.0 Classify (EU AI Act/NIST/OMB), audit (6 profiles), export-examiner, policy sign
Eval Govern & Promote v0.55.0 Eval suites (eval list), run a suite (eval run), regression compare (eval compare)
Risk Govern & Promote v0.55.0 OWASP LLM assurance (assure), secret scan, failure attribution (diagnose), risk-tier classify, MCP scan
Lineage Provenance & Trust v0.7 Provenance / blast-radius / replay-chain DAG, interactive query, OpenLineage/PROV export
KG Provenance & Trust v0.17.0 Capsule knowledge graph: query, audit, entity queue, alias mgmt, ingest
Cost Overview v0.17.0 Cost report, pricing, burn analysis (ClickHouse-backed); v0.64.0: cost-analytics tools trio — attribution / fairness / usage-breakdown (/api/cost/{attribute,fairness,usage-breakdown}, P6)
Schema Compliance v0.17.0 Schema registry (JSON Schema + proto3)
Evidence Provenance & Trust v0.9 Bundle list, DSSE/TSR/Merkle verification, download, in-browser ed25519 verify
Audit Provenance & Trust v0.7 Dashboard mutation audit log, action-type filter
Holds Govern & Promote v0.11 Place/release legal holds, sidebar badge count
Policy Govern & Promote v0.11 Interactive OPA/Rego check, ALLOW/DENY badge, explain trace
Seal Provenance & Trust v0.13.0 NovaSeal verify, Sigstore sign/verify, linked-envelope proposals, bypass; v0.98.0: trust-surface views — Trust Radar SVG glyph (ADR-0173) and Redaction X-Ray heat overlay + coverage meter (ADR-0174), reading /api/runs/{run_id}/{trust-radar,redaction-xray}. An unverifiable guarantee renders n/a (hollow tick, excluded from the claim polygon), never fail; the X-Ray shows paths and states only — never a field value
Spine Provenance & Trust v0.56.0 Accountability Spine: per-run energy receipts + conservation check (ADR-0093), append-only ledger verify (ADR-0094), structured safety case (ADR-0095) — read-only (/api/runs/{id}/{energy,ledger,safety-case})
Compliance Compliance v0.15.2 GDPR RoPA, AI-SBOM, NIST RMF, CRA coverage, erasure, audit bundle/verify (8+ panels)
Incidents Compliance v0.55.0 EU AI Act Art. 73 incident records with a live reporting-deadline clock; open/list/transition/export (AIM, NIS2)
Capture Runs & Debug v0.7 Capture matrix (4 runners + distributed + MCP/API proxies), adapters, recent capsules
Infra Platform v0.12.6 Phase 0–6 component status cards (NovaSeal, Collector, OCS, MetaDB, Lineage, …); v0.64.0: Backups card (/api/infra/backups, P7 — read-only manifest listing) + Maintenance card of idempotent confirm-gated safe mutations (reindex-runs / topology re-seed / KG rebuild, P8-P10)
Storage Platform v0.55.0 WORM object-store stats/validate/inspect, manifest chain, collector status, gated DB upgrade + rebuild-metadata-db, system-card export
Ops Platform v0.55.0 Installation diagnostics (doctor), warm-daemon liveness (read-only), JWKS flush
Admin Platform v0.7 Token management, role management (API v0.14.3; UI partial), JWKS flush, new-run-id, DB ops
Commands Platform v0.8 13 hand-curated command builders at launch; v0.60.0: now mirrors the complete CLI (every nova command, 289 today) via an auto-generated registry — see the CLI-parity row below
Reports Reports & Export v0.17.0+ 9 report templates (see below)
Export Reports & Export v0.64.0 Export Center hub (E1) — the server-catalog-driven registry export panel (/api/compliance/export/kinds) plus a jump-link directory to the evidence / lineage / reports / compliance export surfaces

Reports tab — 9 templates: run-history, eval-regression, capsule-compare, cost-burn, throughput, evidence-inventory, policy-audit, seal-verification, release-comparison.

Compliance tab panels: GDPR Art.30 RoPA, AI-SBOM (CycloneDX ML-BOM), NIST AI RMF, AI-SBOM coverage/CRA deadline, GDPR erasure request/status (real DEK crypto-shred execution since ADR-0210, experimental, confirmed: true required), audit coverage/bundle/verify, C2PA, RO-Crate, EU AI Act Art.12.

Governance tab panels: risk classification, audit-profile coverage, examiner export, policy signing, regulatory vocabularies + manual classification + eval suites/run (v0.46.0).

v0.46.0 parity additions (12 panels across 7 tabs): eval suites list + eval run (Governance), policy inventory + policy sign (Policy), regulatory vocabularies + manual classification (Governance), generate AI-SBOM (Compliance), reindex capsules (Admin), distributed capsule tree + run lineage edges + secret scan with --fail-on gate (Runs), lineage-store deployment profile (Infra).

v0.55.0 coverage additions: 5 new tabs (Eval, Risk, Storage, Incidents, Ops) plus Seal → Ratchet (seal ratchet init/rotate/status) and Evidence → Assertions (evidence completeness/bind) panels, surfacing ~30 previously CLI-only commands. Destructive / process-control actions (DB rebuild, ratchet rotate) are confirmation-gated and daemon control is read-only over HTTP ("safe mutations only"). New cross-cutting UX: app-wide toast notifications, a unified action/confirm pattern, a virtualized data table, per-tab help, deep-linkable tabs (?tab=), and global entity search (runs/assets/incidents) in the ⌘K palette.

The dashboard server exposes 203 REST endpoints (verified by counting @app.get/post/put/delete and @router.get/post decorators under src/novafabric/serve/, excluding WebSocket routes) — the full machine list is in api-reference.md (and GET /openapi.json at runtime).


Install + run

# 1. Install the optional [serve] extra (FastAPI + uvicorn, both Tier-A licenses)
pip install 'novafabric[serve]'

# 2. Start the server (--experimental is mandatory)
nova serve --experimental

# 3. Open the URL the panel prints (it embeds a one-shot session token).
# Or paste the token manually if you prefer to type the URL.

Notable flags:

Flag Default Purpose
--experimental (required) Acknowledges the experimental gate.
--port 4321 TCP port.
--host 127.0.0.1 Bind address. Refuses non-localhost without --insecure.
--capsule-dir ./.novafabric/runs/ Where to look for capsules.
--db-path ~/.novafabric/registry.db Registry/lineage SQLite path.
--no-browser off Don't auto-open a browser tab.
--topology off Enable the live topology dashboard (/topology/).
--tv5 off Enable the experimental 3D topology view (implied by --topology).
--topology-louvain-resolution 1.0 (or NOVA_TOPOLOGY_LOUVAIN_RESOLUTION) Louvain clustering resolution. Lower values merge into fewer/larger clusters; higher values split further.

Stop with Ctrl+C. The session token is rotated on every start; the previous token is invalidated.

Topology view modes

The topology dashboard (/topology/) offers a view-mode switcher over five complementary lenses on the same data — pick the right one for the task:

Mode What it shows Best for
Cluster (default) 2D force layout of cluster super-nodes, sized by agent count; click a cluster to expand its agents Structure overview + drill-in
Call-graph 2D layered (dagre) layout following the directed run → model → tool flow Reading call direction/hierarchy
Treemap Rectangles with area proportional to agent count Spotting the biggest clusters at a glance
Table Sortable, searchable exact list of clusters Precise lookup; the anti-hairball
3D (experimental) Three.js orbit view (TV-5); labels appear on hover/selection Spatial exploration (not default)

Graph modes provide on-screen + / − / Fit zoom controls. Labels are decluttered — only large, hovered, or selected nodes are labeled — so the full-graph view stays legible at scale. Clusters whose only member is a single agent (e.g. runs with no captured model calls) are collapsed into one "misc" super-node rather than scattered as visual noise.


Capability matrix: dashboard vs CLI

The dashboard exposes the majority of the read-side and many of the write-side capabilities of the CLI, but a number of operations are intentionally CLI-only. Every dashboard action surfaces its equivalent nova command in the confirm dialog.

Reading the "Runs tab → row X button" entries below. Since v0.98.2 the per-row action buttons are revealed, not always visible: they render for the selected row and on hover or keyboard focus of any row. (Ten always-visible buttons per row wrapped onto four lines and left only three runs on screen.) The actions are unchanged and never more than one interaction away — click or tab to the row, then click the action.

Read operations (Layer A — fully covered)

Capability CLI Dashboard Notes
List capsules under .novafabric/runs/ nova (no direct command — discovers them) ✅ Runs tab Search by run_id or command, filter by status, sort by time/duration, opt-in 5s auto-refresh. v0.12.9 (DU-1): status filter pill-bar (All / running / success / failure / error) above the table. v0.12.9 (DU-2): hover-reveal copy icon on each run ID cell.
Inspect a single capsule nova inspect <run-id> ✅ Runs tab → click row File-tree + YAML pane + expandable JSONL rows + in-browser Ajv validate.
Validate a capsule against its schema nova validate <capsule> ✅ Capsule view → "Validate" button Same run-capsule.schema.json compiled in-browser via Ajv.
List registered assets nova list [--type] [--status] ✅ Registry tab Filters not yet exposed in UI; visible via the live API.
Get a single asset nova inspect <name@version> ✅ Registry tab → click row Eval results for the selected asset are lazy-fetched.
Lineage queries nova lineage provenance / blast-radius / replay-chain ✅ Lineage tab Full DAG render via React Flow + dagre. Click (or double-click) a node to see incoming/outgoing edges. v0.12.6: interactive QueryPanel — type a ref, pick mode (provenance / blast-radius / replay-chain), hit Run; results appear as a sortable table; CLI equivalent preview updates live. v0.12.7: ref input autocompletes name@version (provenance/blast-radius) or run_id (replay-chain) from loaded data. v0.12.9 (DU-3): when a node is selected, a breadcrumb row above the graph shows the full ancestry chain (root → … → parent → selected); each ancestor is clickable. v0.13.4: zoomOnDoubleClick disabled; double-clicking a node selects it (identical to single-click) instead of triggering zoom-to-fit.
Cluster-scale infrastructure status (no single command — consult each Phase 0–6 component separately) ✅ Infra tab (v0.12.6) 10 component cards: NovaSeal, Collector, Object Store, Metadata DB, Lineage at Scale, Parent/Child Capsule, Server Mode, Eval Suites, Policy Gates, Run Capsule — each with a shipped / partial / placeholder / planned status badge plus the relevant CLI commands. Lets operators confirm which Phase 0–6 components are active without leaving the dashboard.
CLI command reference with live preview (all nova sub-commands) ✅ Commands tab v0.8: 13 command builders for core capture, registry, and replay. v0.12.6: expanded to 35 hand-curated builders across 4 journey tracks. v0.60.0: now mirrors the complete CLI — every nova command (289 today) has a fillable form. Hand-curated builders still provide richer copy for the most-used commands; the rest are auto-generated from the live Typer app (web/scripts/gen-command-registry.pygeneratedCommands.ts), so the tab stays in sync as commands are added/removed (guarded by tests/serve/test_command_registry_coverage.py). Fields, choices, defaults and flags come from the CLI's own parameter metadata. Each builder renders a live command preview with a copy button; a filter box and per-group counts navigate the full surface. Copy-only (Layer C, ADR-0027) — the dashboard never executes commands.
Structural diff nova diff <run-a> <run-b> ✅ Diff tab Aligned with diff-report.schema.json; renders environment / model_calls / tool_calls / outputs sections. v0.9 additions: 'Compare against…' in CapsuleInspector, word-level diff for model responses, mutation badge on tool call pairs. v0.12 addition (C-4): Multi-select checkboxes in RunsTab — check any 2 rows, click "Compare selected ⊕", jumps directly to Diff tab with both IDs pre-filled and diff auto-triggered; no copy-paste. See ADR-0036. v0.12.7: both run A and run B inputs autocomplete from the loaded runs list. v0.12.9 (DU-4): last comparison (from/to/result) persisted in sessionStorage — navigating away and back restores the previous comparison without re-fetching. v0.13.3 (C-5): Checkbox cap lifted 2→5; selecting 3–5 runs triggers N-run mode — first run is the baseline, N-1 parallel diffs fire via runMultiDiff, results shown as stacked collapsible MultiDiffCard panels with change-count badges. URL format migrated from ?run_a=&run_b= to ?run_ids=a,b,c (old params still parsed for backward compat).
Read evidence bundle (no read-only command — open the ZIP) ✅ Evidence tab (v0.9) Lists all bundles from ~/.novafabric/evidence/, shows DSSE statement, in-browser ed25519 verify via SubtleCrypto, ZIP download. See ADR-0037.
Verify evidence bundle cryptographic integrity nova verify <capsule> (NovaSeal) ✅ Evidence tab → Verify button (v0.11) POST /api/evidence/{id}/verify — Ed25519 DSSE signature + RFC 3161 TSR + NovaSeal Merkle log inclusion. Inline sig ✓, tsr ✓, log – badges. log – shown when NovaSeal is not configured locally.
Validate a capsule's schema and file structure nova validate <capsule> ✅ Runs tab → Validate button (v0.11) POST /api/runs/{id}/validate — checks required files, YAML parse, JSONL presence. Green ✓ valid or red expandable error list.
View secret scan results nova scan-secrets <capsule> ✅ Runs tab → Secrets button on card (quick-access, v0.11) or → Secrets tab in detail panel GET /api/runs/{id}/redaction-proof — shows scanner+packs, targets (hash before/after), per-finding severity badges. Includes re-scan trigger.
Report generation nova report [--format markdown|json|html|pdf] ◐ Reports tab The Reports tab has its own registry-driven builders with CSV/JSON download plus self-contained HTML and PDF artifacts with embedded charts (GET /api/reports/{id}/export, ADR-0201; PDF needs the optional WeasyPrint extra — the UI surfaces the 501 install hint). The CLI's asset-inventory report (nova report) additionally supports `--format html
Forensics timeline nova forensics timeline <capsule> ✅ Runs tab → Forensics view (v0.64.0, P5) GET /api/runs/{id}/forensics-timeline reconstructs a deterministic timeline from the run's own sealed capsule via the same merge_timeline core. Honest scope: run-lifecycle + model/tool-call events only; missing timestamps and the absent lineage collector are shown as gaps, never fabricated.
Cost attribution / fairness / usage-breakdown nova cost attribute / fairness / usage-breakdown ✅ Cost tab → tools panel (v0.64.0, P6) Three pure POST endpoints (/api/cost/{attribute,fairness,usage-breakdown}) wrapping the same cores the CLI calls, given a document instead of a file path. Descriptive only — no cost verdict.
Backup-set status nova backup verify (listing has no direct command) ✅ Infra tab → Backups card (v0.64.0, P7) GET /api/infra/backups lists NOVA_BACKUP_DIR archives from their manifest.json (manifest-claimed, not hash-verified — that stays nova backup verify). Degrades to {detected:false} when unconfigured.
Lineage time-travel queries nova lineage time-travel <ref> --asof <ts> ❌ — Not yet wired into the dashboard; CLI-only in v0.7.
Lineage OpenLineage emission nova lineage emit-openlineage ❌ — Integration target; CLI-only.

Write operations (Layer B — confirm-gated, audit-logged)

Capability CLI Dashboard Notes
Register an asset nova register <spec.yaml> ✅ Registry tab → "+ Register asset" Paste YAML into the dialog; spec validated by the same validate_spec the CLI uses.
Run eval suites nova eval <name@version> ✅ Registry tab → row "EVAL" button Runs the same run_evals entry point as the CLI. v0.9 addition: Eval history sparkline per asset (last 10 results, green/red bars) and full eval history panel in the eval dialog. See ADR-0038.
Promote an asset nova promote direct <name@version> --to <status> [--force] ✅ Registry tab → row "PROMOTE →" button Eval gate enforced for agents (use --force only with intent). Force is visually marked as destructive. v0.12.9: invalid target buttons disabled client-side (e.g. archived → production is dimmed); inline error banner shown on server rejection; eval-gate copy conditional on asset_type === 'agent'. v0.13.0 (D-5): nova promote is now a sub-group (direct / propose / approve). The dashboard Promote button maps to direct. For maker-checker SoD flow, use nova promote propose + nova promote approve from the CLI.
Bulk-promote multiple assets (run nova promote for each) ✅ Registry tab → checkbox column + floating action bar (v0.12.9) Select one or more assets, click "Promote N" in the floating bar; each is promoted to its next valid status in sequence. Select-all master checkbox in header. Floating bar appears only when ≥ 1 asset is checked.
Forensic replay nova replay <capsule> --mode forensic ✅ Runs tab → row "REPLAY" button Read-only inspection, no subprocess, safe.
Semantic replay analysis nova replay <capsule> --mode semantic ✅ Runs tab → row "SEMANTIC" button (v0.11) Compute pairwise text similarity across model call responses. Returns similarity_score (0–100%) and a gauge. Read-only, no subprocess.
Exact replay eligibility nova replay <capsule> --mode exact ✅ Runs tab → row "EXACT" button (v0.11) Check whether the capsule satisfies exact replay requirements: env.lock.lock_mode=deterministic and seed present on every model call. Returns exact_eligible (bool) + blocker list.
Re-scan & redact nova redact <capsule> ✅ Runs tab → row "REDACT" button Re-runs SecretScannerV0 and overwrites redaction-proof.json. Limitation: strategy overrides and unsafe-skip marking (--strategy-override, --mark-unsafe-skip, --rationale) are CLI-only.
Maintenance recompute (safe) (reindex has no direct command; KG/topology mirror nova kg ingest --all) ✅ Infra tab → Maintenance card (v0.64.0, P8-P10) Three idempotent, lossless, confirm-gated actions: reindex runs cache (POST /api/admin/reindex-runs — full INSERT-OR-REPLACE rebuild, an APIRouter module per the ADR-0183 route freeze), re-seed topology (/api/topology/seed), rebuild KG (/api/kg/ingest-all). None deletes user data. v0.98.0: reindex accepts optional "prune": true (default false) to drop index rows whose capsule directory no longer exists — dangling entries the additive rebuild cannot clear, which otherwise list in the Runs tab and 404 on every drill-in. Capsules are never touched; only the derived index row is removed.
Compare two asset spec versions nova diff <name@v1> <name@v2> ✅ Registry tab → "Compare…" (v0.11) GET /api/assets/{name}/diff?from_version=&to_version= — flattened field diff; green +, red , yellow ~ table rows. Available on assets with ≥ 2 registered versions.
Export signed evidence nova export-evidence <capsule> --output <zip> --key <pem> ✅ Runs tab → row "EXPORT ↗" button Signing key auto-generated at ~/.novafabric/keys/local-key.pem if missing (audit-logged). Output defaults to ~/.novafabric/evidence/<run_id>.zip. Limitation: custom output paths and --allow-unsafe-skips are wired in the API but not surfaced as form controls in the UI yet.
Place / release a legal hold nova hold create <registry> --reason … [--duration-days N] / nova hold release <hold-id> ✅ Holds tab (v0.11) POST /api/holds + POST /api/holds/{id}/release. Active holds grouped by registry; inline release button; place-hold form with optional duration. v0.12.7: registry name input autocompletes from the list of registries shown on-screen.
List active legal holds nova hold list <registry> ✅ Holds tab (v0.11) GET /api/holds — discovers all registries under .novafabric/registries/; sidebar badge shows active count.
Interactive policy check (no direct CLI equivalent — there is no nova policy check; the CLI surface is nova policy test for the Rego suite and nova policy explain <decision-id> for a past decision. Corrected 2026-08-01.) ✅ Policy tab (v0.11) POST /api/policy/check — evaluates a PolicyInput (action, subject, resource) via the OPA engine and returns a PolicyDecision; large ALLOW/DENY badge + reason + metadata; yellow warning when OPA is not installed. v0.12.7: resource ref autocompletes name@version when kind = asset, or run_id when kind = capsule/replay; loads at mount from GET /api/assets and GET /api/runs. v0.12.9 (DU-9): Rego source textarea with 300 ms debounced client-side syntax check (missing package, unbalanced braces); advisory warning banner, does not block submission. v0.13.2 (DC-5 Explain): "explain" checkbox triggers POST /api/policy/check with explain: true; backend runs a second OPA subprocess with --explain full --format pretty; trace_text returned in PolicyDecision; decision result shows "show trace ↓" toggle that reveals a max-h-64 scrollable monospace trace panel.
Export GDPR Art.30 RoPA entry nova export-ropa <capsule> --output ropa.json ✅ Compliance tab → GDPR Art.30 RoPA Export (v0.37.0) POST /api/compliance/export/ropa — optional controller_name / controller_contact; completeness badge + missing-fields list + JSON document display.
Export AI-SBOM (CycloneDX 1.7) nova export-aibom <capsule> [--output aibom.json] ✅ Compliance tab → AI-SBOM Export (v0.37.0) POST /api/compliance/export/aibom — component list (type/name/version/description), serial_number, bom_format. CycloneDX ML-BOM 1.7 (ECMA-424 2nd Edition) since v0.39.0.
Export NIST AI RMF report nova export-nist-rmf <capsule> --output report.json ✅ Compliance tab → NIST AI RMF Report (v0.37.0) POST /api/compliance/export/nist-rmf — GOVERN/MAP/MEASURE/MANAGE score bars, risk-level badge, missing-evidence list.
Check AIBOM coverage (CRA) nova aibom status [--capsules-dir <dir>] ✅ Compliance tab → AI-SBOM Coverage Status (v0.37.0) GET /api/aibom/status — auto-loads on mount; total/covered/missing capsule counts, coverage progress bar, CRA deadline 2026-09-11.
Execute GDPR Art.17 erasure nova pii erase <subject_id> ✅ Compliance tab → GDPR Erasure Request (experimental, ADR-0210) POST /api/compliance/erasure/requestreal crypto-shred execution (replaced the v0.18.0 no-op stub): persists the request in $NOVAFABRIC_HOME/erasure.db, then destroys the subject's AES-256-GCM DEK through the same DEKStore.erase_subject code path as the CLI. Safe-mutations gated — body must carry confirmed: true (400 otherwise, nothing mutated). Terminal states COMPLETED (receipt + receipt_sha256), DEFERRED (Art.17(3)(b) retention window, earliest_erasure_at), FAILED (error receipt; unknown subject → subject_not_found). Duplicate request while one is PENDING re-attaches to it (idempotency + crash recovery). Both audit surfaces record the action hash-only (subject_sha256, never the raw subject). GET /api/compliance/erasure/status lists the persisted queue. The /v0 server-mode mirror is planned (ADR-0210 D7), not implemented.
Mocked replay (re-execute agent code, cache hits) nova replay <capsule> --mode mocked ❌ — Mocked replay spawns the agent subprocess. That straddles Layer B/C; in v0.7 it is CLI-only until the sandbox model lands per ADR-0027.
Scan secrets nova scan-secrets <capsule> ❌ — Use redact from the dashboard, or nova scan-secrets from the terminal.

Capture and orchestration (Layer C — deferred)

Capability CLI Dashboard Notes
Capture a command nova capture <command> ❌ Deferred to v0.9 Per ADR-0027 §1 Layer C, launching arbitrary subprocesses from a browser endpoint requires a sandboxed exec model, per-action audit log entries with full command text, and a confirmation flow that shows the command verbatim. None of those primitives are committed yet. Layer C may not ship at all if the security review concludes the risk outweighs the value. The Capture tab in the dashboard documents this and lists the four CLI alternatives (nova capture, --runner docker, nova mcp-proxy, nova api-proxy).
MCP proxy nova mcp-proxy <upstream> ❌ — Long-running stdio bridge; CLI-only.
LLM API proxy nova api-proxy --upstream-url <url> ❌ — Long-running HTTP server; CLI-only.
Capture with custom runner nova capture --runner {docker,kubernetes,slurm} ... ❌ — Same Layer C reasoning.

Operational / advanced (CLI-only by design)

These are intentionally not in the dashboard because the CLI is the right surface for them:

Capability CLI Why not in the dashboard
Schema validation of arbitrary files nova validate <spec.yaml> (asset specs) Use the dashboard's Register dialog for assets; pipe through nova validate for batch jobs.
Asset diff between two versions nova diff <name@v1> <name@v2> ✅ Registry tab → Compare (v0.11) — moved to the Write operations table above.
Streaming real-time output during a long-running job (the capture command itself) Dashboards aren't the right surface for tailing a single subprocess; use nova capture directly.
CI / non-interactive scripted runs All commands Dashboards require a browser; CI uses the CLI.
Cluster runners (Docker / K8s / Slurm) nova capture --runner ... Driven from the orchestration host, not from a browser tab.

What the dashboard does that the CLI cannot

A short list, for completeness — the dashboard is not strictly a subset:


Security model


Audit log

Every Layer B mutation appends a JSONL record to ~/.novafabric/dashboard-audit.jsonl (override with NOVAFABRIC_DASHBOARD_AUDIT_FILE). Each record has:

The file is append-only with mode 0600. To read it: cat ~/.novafabric/dashboard-audit.jsonl | jq or use the dashboard's Audit tab.


Trace viewer (v0.7.1)

The Trace view (click a run row → "Trace" button) shows three sub-tabs:

Waterfall tab

Renders the trace.jsonl span tree as an indented Gantt chart.

Thread tab

Chronological sequence of every model call and tool call, styled as a conversation.

Swimlane tab

Gantt chart with one horizontal lane per agent — designed to make parallel evidence-gathering phases visible at a glance.

Adding agent attribution to your agent code

# When writing a model call record, add nova.agent.id:
capsule.append_model_call({
    "model_call_id": ...,
    "nova.agent.id": "kubernetes_sentinel",
    "nova.agent.type": "evidence",
    # ... rest of gen_ai.* fields
})

# Same for tool calls:
capsule.append_tool_call({
    "tool_call_id": ...,
    "nova.agent.id": "kubernetes_sentinel",
    # ... rest of fields
})

When nova.agent.id is present the swimlane and Thread views use it directly. When it is absent the Thread view falls back to system-prompt inference; the swimlane shows a single root lane.


Evidence Tab (v0.9 + v0.11)

See ADR-0037.

The Evidence Tab is a fully operational view over real bundles on disk. It requires nova export-evidence to have been run at least once (bundles live in ~/.novafabric/evidence/).

Bundle list view — table of all bundles found in ~/.novafabric/evidence/, showing run_id, timestamp, file size, and a manifest-hash integrity badge. The badge is a shallow check (SHA-256 of manifest.json fields) that runs at list time; use the Verify button for cryptographic assurance.

Detail pane — selecting a bundle opens a detail pane showing the DSSE statement (pretty-printed JSON), attestation hashes for each artifact, and the signing key fingerprint (first 16 hex chars of the SHA-256 of the public key).

Verify button (v0.11) — calls POST /api/evidence/{bundle_id}/verify server-side. Returns three independent checks:

Check What is verified When null
sig ✓ / ✗ Ed25519 DSSE signature on attestations/run.intoto.json against signatures/run.cert Never — always attempted
tsr ✓ / ✗ / – RFC 3161 TSR in manifest.dsse.tsr against the DSSE bytes When timestamping was not requested at export time
log ✓ / ✗ / – NovaSeal Merkle log inclusion for the associated capsule When novaseal.yaml is not configured or the capsule dir is not locally available

A bundle is valid when all non-null checks pass. The sig check must pass for the others to be meaningful.

Download link — direct link to download the raw ZIP for offline verification (openssl ts -verify) or sharing.


Cross-run comparison (v0.9)

Works today (shipped v0.9; see ADR-0036) — the compare-selected flow below is live in the Runs tab.

The current diff workflow has six friction steps: find run A in the Runs tab, copy its run_id, navigate to the Diff tab, paste it, find run B, copy and paste its run_id. The v0.9 additions remove the copy-paste steps entirely.

D2 — Two-click compare from RunsTab. Select any two rows in the Runs tab (checkbox or Shift-click) and a "Compare" button appears in the toolbar. Clicking it navigates directly to the Diff tab with both run_id values pre-filled and the diff auto-triggered.

D3 — Compare against… from CapsuleInspector. A "Compare against…" dropdown inside the CapsuleInspector lets you pick a second run from a recency-sorted list, then jumps to the Diff tab pre-filled.

D4 — Word-level LCS diff for model responses. Model call response text is diffed at the word level using a Longest Common Subsequence algorithm, with insertions highlighted green and deletions struck through in red — making prompt-sensitivity changes legible at a glance.

D5 — Mutation badge on tool call pairs. When a matched tool call pair has different arguments or outputs, a ⚡ mutation badge appears on the pair header. Badge tooltip shows argument key(s) that changed.


Infrastructure tab (v0.12.6)

The Infra sidebar entry (under the Infrastructure group) shows the operational status of all ten Phase 0–6 cluster-scale components as card tiles:

Component Status CLI to verify
Run Capsule (Phase 0 / v0.1) shipped nova inspect <run-id>
NovaSeal v0.1 (Phase 0) shipped nova verify <capsule>
Event Envelope v1 (Phase 1) shipped nova validate <capsule>
Collector tier (Phase 2) shipped nova collector rebuild (Python side) · novafabric-collector --config <yaml> (Go gateway, after go build)
Parent/Child Capsule (Phase 3) shipped nova run show <id> --with-children
Object Capsule Store (Phase 4) shipped nova storage validate / nova storage inspect
Metadata DB (Phase 5) shipped nova db upgrade
Lineage at Scale (Phase 6) shipped nova lineage provenance <ref>
Server Mode (v0.7) shipped nova server start
Eval Suites (v0.9) shipped nova eval run <capsule>

Badge colours: shipped (green), partial (accent), placeholder (amber), planned (muted). Each card shows the CLI command(s) for that component so operators can investigate from the terminal without navigating away.

Corrected 2026-08-01. This table previously named three commands that do not exist: nova store status (the group is nova storage, exposing validate and inspect), nova db status (nova db exposes upgrade and migrate-to-postgres only), and nova eval <name@version> (nova eval is a command group; the runner is nova eval run). The Collector row was also ambiguous: novafabric-collector is the Go gateway binary (source at collector/cmd/novafabric-collector, needs go build first — it is not a Python console script and is not on PATH after a pip/uv install); the Python-side surface is nova collector rebuild. All ten component statuses themselves are unchanged and correct — only the verification commands were wrong.


Eval trend chart (v0.9)

Works today (shipped v0.9; see ADR-0038) — the sparkline + history panel below are live in the Registry tab.

Per-asset eval sparkline. Every row in the Registry tab gains an 8×32px inline SVG bar chart showing the last 10 eval results for that asset — green bars for pass, red for fail. The sparkline is lazy-loaded via IntersectionObserver so it does not block initial table render.

Eval history panel. Clicking the "EVAL" button for an asset opens an expanded history panel (above the trigger form) showing the last 20 eval results in a table: suite name, score, pass/fail, and relative timestamp (e.g. "3 days ago"). The panel is fetched on demand and cached for the session.

v0.12.8 display fixes:


UX conveniences (v0.7)


Roadmap

The dashboard is judged on whether it earns its keep. If at any point it becomes maintenance-heavy without commensurate user value, the entire src/novafabric/serve/ package can be deleted in a single commit and ADR-0027 reverted, leaving no migration debt because the dashboard owns no persistent state of its own.


Downloading chart images (ADR-0201; shipped v0.64.0)

Every chart wrapped in the shared ChartCard (Analytics run-volume + duration charts, the Reports-tab chart preview, the Cost tab's cost-by-model chart) has a compact ⬇ SVG / PNG affordance. Export happens entirely client-side: the on-screen SVG is serialized with its computed styles inlined — so theme tokens resolve to the concrete colors of your active light/dark theme — and PNG is rasterized through a canvas at 2× for crisp retina output. No server round trip, no new dependencies.

Report artifacts: HTML + PDF with charts (ADR-0200/0201; shipped v0.64.0)

The Reports tab is backed by a server-side report registry (GET /api/reports/catalog) that declares, per report, its filters and — only where a genuine series exists — a chart spec (throughput stacked-bar, cost-burn bar, single-suite eval-regression line, release-comparison delta bar; nothing is fabricated for tabular-by-nature reports). Besides CSV/JSON, every report exports as:

Parity classification (ADR-0200; shipped v0.64.0)

web/src/components/dashboard/commands/commandParity.json classifies every nova CLI command as real-panel (a wired dashboard panel exists — claim is machine-verified against the API client and serve routes), builder-only (copy-only Commands-tab builder), or cli-only with a mandatory reason (process lifecycle, destructive filesystem operation, CI-oriented). tests/serve/test_command_parity_classification.py fails CI when a new CLI command lands unclassified — parity can no longer regress silently. Destructive operations staying cli-only with a recorded reason is the intended honest end-state, not a gap.

Scale characteristics and known limits

Understanding these limits prevents surprises when you grow beyond a small local setup. The limits are documented here so they are visible before they bite.

What works at scale today

Mechanism Where it lives What it handles
Cursor-based pagination on /api/runs/search serve/app.py:42–66 Fetches large run lists in pages without re-scanning from the start. A cursor is a bookmark: the server encodes (created_at, run_id) of the last returned item into a short base64 token. Send it back as ?cursor=... to get the next page. Nothing to do with the VS Code text cursor.
LIMIT/OFFSET on assets and runs serve/app.py Registry and runs list endpoints cap result sizes.
TanStack Virtual in RunsTab and RegistryTab web/src/components/dashboard/tabs/ Renders only the rows visible on screen — the DOM stays small even with thousands of rows loaded in memory.
SSE live feed for new capsules /api/runs/stream New capsule directories appear in the RunsTab without a full page reload.
5 SQL indexes on registry DB serve/app.py (v0.14.6) Keeps WHERE status = ? and ORDER BY created_at fast at tens of thousands of assets.
Shared keyset cursor codec + page envelope (ADR-0199, shipped v0.64.0) serve/pagination.py One (created_at, id) row-value cursor implementation reused by runs, run-history reports, and the audit log.
Reverse-block audit-log tail reads (ADR-0199, shipped v0.64.0) serve/audit.py::read_recent_tail /api/audit (and the alerts feed) read O(limit) 64 KiB blocks from EOF with byte-offset cursors instead of readlines() on the whole append-only file; AuditTab is virtualized with server-side action filter + load-more.
SQL aggregation pushdown for reports + analytics (ADR-0199, shipped v0.64.0) registry/runs_cache.py aggregate functions + substr(created_at,1,10) expression index Throughput, executive-summary, cost-burn, and the Analytics day buckets are computed in indexed GROUP BYs — the former limit=1_000_000 fetch-all is gone. Run-history is keyset-paged and its CSV export streams in pages (bounded memory at any store size).
Nightly 100K-row scale gate (ADR-0199, shipped v0.64.0) tests/bench/test_dashboard_scale_gate.py + the dashboard-scale CI job p95 latency thresholds per endpoint, recorded in docs/ops/dashboard-scale.md; regressions block release.
True keyset fast path on /api/runs/search (ADR-0199, B2) serve/app.py + registry/runs_cache.py::query_runs(after=) The decoded (created_at, run_id) cursor now seeks directly in the index — the former cursor→OFFSET COUNT(*) conversion (O(offset) per page) is gone; deep pages cost O(page).
Bounded policy audit reads (ADR-0199, B2) /api/policy/recent-decisions, /api/policy/explain Both iterate the dashboard audit log newest-first via audit.tail_lines (O(page) reverse 64 KiB blocks, ≤10,000 scanned lines per request) instead of reading the whole file; byte-offset cursor/next_cursor (same contract as /api/audit) plus truncated. explain gained limit (default 100, max 1000) and now returns matches newest-first.
Keyset cursor + totals on /api/lineage/edges (ADR-0199, B2) serve/app.py Additive cursor/next_cursor over (created_at, edge_id), true total edge count, and truncated: true when older edges were not returned; existing count/edges keys unchanged.
Bounded KG entity queue (ADR-0199, B2) /api/kg/entity-queue + kg/review_queue.py::list_pending(limit=) limit (default 200, max 1000) is pushed into the SQL read; response gains the true pending total and truncated.
Bounded admin listings (ADR-0199, B2) /api/admin/tokens, /api/admin/api-keys Tokens are read newest-first from tokens.jsonl via the reverse tail scan with a byte-offset cursor (limit default 200, max 2000; a revoke rewrites the file and invalidates outstanding cursors). API keys push limit (default 500, max 2000) into SQL; total is now a true COUNT(*) and truncated is additive.
Bounded asset eval history (ADR-0199, B2) /api/assets/{asset_id} eval_limit (default 50, max 500) bounds eval_results; additive eval_results_total + eval_results_truncated — no silent truncation.
Watermark cache on /api/analytics/summary (ADR-0199 rule 3) serve/routers/analytics.py A cheap indexed (COUNT(*), MAX(created_at)) watermark per window skips the O(rows) duration-percentile pass when nothing changed — the 30 s polling loop recomputes only when new runs actually landed (bounded 32-window cache).
Shared truncation affordance in the UI (ADR-0199, shipped v0.97.0) web/src/components/ui/TruncationNotice.tsx + lib/{useQuery,usePaginatedQuery}.ts The client-side half of "no silent truncation": every bounded list renders a footer reading Showing N of M — K more with a Load more button, and a ~ prefix plus an approximate badge whenever the server reported an estimated total (total_approx) rather than an exact COUNT(*). usePaginatedQuery speaks both server models — keyset cursor and limit/offset — so a list never has to choose between honesty and paging; DataTable additionally loads the next page on scroll. If a list can show you less than exists, it says so.
Conditional GET on all hot polled reads (ADR-0199 S6, B3) serve/http_cache.py::conditional_json on /api/stats, /api/runs, /api/alerts/recent, /api/incidents, /api/evidence, /api/kg/topology, /api/reports/catalog (plus the original /api/analytics/summary) Content-addressed strong ETags + Cache-Control: private, max-age — the dashboard's 30 s polling loop gets 304 Not Modified (empty body) whenever the underlying data hasn't changed, cutting transfer and client re-render on every idle poll.

Known hard limits (action items for future versions)

Limit Root cause Planned fix
~10,000 capsules before /api/runs slows noticeably list_run_summaries() (serve/capsule_loader.py:51) re-reads every capsule.yaml from disk on every request — O(N disk). Scale-S1 (shipped v0.32.0) + Scale-S3 (shipped v0.36.0): runs_cache SQLite index + CapsuleWatcher background indexer; nova ingest-capsule CLI for manual re-index.
NovaSeal Merkle log verify scans full SQLite table serve/app.py, nova seal log verify Scale-S4 (planned v1.x): Postgres path for the NovaSeal policy and Merkle log store.
SQLite registry breaks at ~1M rows No partitioning in SQLite Flip NOVAFABRIC_METADATA_BACKEND=postgres — the Postgres tier (PostgresMetadataStore) is already implemented.

Storage backends — three separate systems

Each backend is upgraded independently. Flipping one does not affect the others. See Storage Architecture for the full reference table and env var names.

Store Default Scalable tier
Capsule content Flat files under $NOVAFABRIC_HOME/capsules/ — always filesystem Object Capsule Store (NOVA_OCS_BACKEND)
Registry + lineage ~/.novafabric/registry.db (SQLite) NOVAFABRIC_METADATA_BACKEND=postgres + NOVAFABRIC_METADATA_DSN=...
NovaSeal policy + Merkle log ~/.novafabric/novaseal-merkle.db (SQLite) No Postgres path yet (Scale-S4)

KG topology vs. Live Topology

The dashboard exposes two topology views that are easy to confuse:

KG tab → Multi-Layer Topology Top-right "Topology" button
Backing store KuzuDB on disk (persistent) In-memory DuckDB + ring buffer (ephemeral)
Node types Agent, Model, Tool, MCPServer, InferenceEndpoint Agent only
Updated by Re-ingest All / auto-poll (60 s) Running agents in real time (< 5 s)
Survives restart ✗ (re-seeded from disk at startup)
Queryable ✓ (nova kg query) ✗ view only
Purpose Historical audit: what called what Operational: what is running now

Re-ingest All only updates the KG topology. It has no effect on the live Topology view. Restarting nova serve re-seeds the live Topology from disk. It does not change KuzuDB.

Full comparison with update-trigger matrix: design/architecture/lineage.md — KG topology vs. Live Topology.


See also