nova kg: the capsule knowledge graph

Part of the NovaFabric CLI reference. Both nova and novafabric run the same binary.

nova kg — Capsule Knowledge Graph (v0.17.0, ADR-0067)

Requires: pip install 'novafabric[scale-kg]' (kuzu>=0.11.3).

The KG is a secondary derived artifact — separate from the per-run lineage graph — that aggregates observed entity relationships across all captured capsules: which agents called which models (CALLS), which tools they used (USES_TOOL), and which inference endpoints they routed to (ROUTES_TO).

Environment variable: NOVA_KG_PATH — override the default KuzuDB path.

nova kg init

Initialise the KG schema at the given path. Idempotent (safe to run multiple times).

nova kg init [--path PATH]
Flag Default Description
--path .nova/kg/nova_kg.kuzu KuzuDB path (also reads NOVA_KG_PATH)
nova kg init --path /data/nova/kg/nova_kg.kuzu
# KG schema initialised at /data/nova/kg/nova_kg.kuzu

nova kg status

Show KG store health, total edge count, and per-type node counts in a Rich text panel.

nova kg status [--path PATH]
nova kg status
# KG store health: ok
#   db_path:    .nova/kg/nova_kg.kuzu
#   edge_count: 1247
#   ┌─ Node counts per layer ──────────────┐
#   │ Node type         │ Count            │
#   │ Agent             │     12           │
#   │ Model             │      4           │
#   │ Tool              │     31           │
#   │ MCPServer         │      3           │
#   │ InferenceEndpoint │      2           │
#   └──────────────────────────────────────┘

nova kg ingest

Ingest events from a capsule directory (or all capsule directories) into the KG.

nova kg ingest [CAPSULE_DIR] [--all] [--source local-dir|nats] [--capsule-dir DIR]
               [--path PATH] [--verified/--no-verified]
               [--nats-url URL] [--nats-subject SUBJECT]
Argument / Flag Description
CAPSULE_DIR Path to a single capsule directory (local-dir mode). Omit when using --all or --source nats.
--all Scan and ingest all subdirectories under --capsule-dir (or $NOVAFABRIC_CAPSULE_DIR / $NOVAFABRIC_HOME/capsules).
--source local-dir|nats Ingest from capsule files (default) or drain a NATS JetStream subject once and exit.
--capsule-dir DIR Base directory to scan when using --all. Defaults to $NOVAFABRIC_CAPSULE_DIR or $NOVAFABRIC_HOME/capsules.
--path KuzuDB path (default: .nova/kg/nova_kg.kuzu).
--verified Mark all events as NovaSeal-verified (sets confidence=1.0).
--nats-url URL NATS server URL (used with --source nats).
--nats-subject SUBJECT NATS JetStream subject to consume (used with --source nats).

--source nats model/tool-call coverage (2026-07-30, ADR-0220 follow-up): capture/orchestrator.py's real producer now re-emits each locally-captured model/tool call as a ModelCallCompleted/ModelCallFailed/ ToolCallCompleted/ToolCallFailed spool event once the run finishes (no *Started variant — the source data has one record per completed/failed call, never a separate start event), so this ingestion path produces real CALLS/USES_TOOL edges from real NATS traffic — verified end-to-end by tests/kg/test_kg.py::test_real_producer_to_kg_pipeline_end_to_end. EndpointRouted is still never emitted by any producer. local-dir and --all read the same underlying model-calls.jsonl/tool-calls.jsonl files directly and were unaffected either way.

# Single capsule
nova kg ingest .novafabric/runs/01HXAY7M --path /data/nova/kg/nova_kg.kuzu
# Ingested 42 events (0 skipped) → wrote 7 KG edges to /data/nova/kg/nova_kg.kuzu

# All capsules in the default capsule directory
nova kg ingest --all

# All capsules in a specific directory (e.g. after a nova-testbench run)
nova kg ingest --all --capsule-dir ~/novafabric-data/capsules
# Bulk ingest complete: 57 capsule(s) scanned · 1423 events ingested · 38 KG edges written · 0 skipped · 0 failed

# Drain a NATS JetStream subject once and exit (see known gap above)
nova kg ingest --source nats --nats-url nats://localhost:4222

Supported event types (read from model-calls.jsonl, tool-calls.jsonl, or events.jsonl):

event_type Edge created
ModelCallCompleted, ModelCallStarted CALLS (Agent → Model)
ToolCallCompleted, ToolCallStarted USES_TOOL (Agent → Tool); if tool_name contains : (MCP format), also SERVED_BY (Tool → MCPServer)
EndpointRouted ROUTES_TO (Agent → InferenceEndpoint)

nova serve automatically ingests new capsules at the interval set by NOVA_KG_INGEST_INTERVAL (default 60 seconds) without requiring manual nova kg ingest calls. Use nova kg ingest --all (or the dashboard Re-ingest All button) to trigger an immediate bulk ingest without waiting for the next auto-ingest tick. Already-ingested directories are tracked in a SQLite sidecar (ingest_tracker.db) that persists across server restarts.

nova kg build-provenance

Experimental (SPKG, ADR-0111; requires pip install novafabric[spkg]). Map a capsule's lineage.jsonl to a W3C PROV-O RDF graph — the canonical semantic layer of the Security & Provenance Knowledge Graph — and SHACL-validate it on the way out (ADR-0111 R11: invalid provenance facts are rejected). This is the provenance-graph export step; to populate the operational store used by nova kg detect, attack-path, and blast-radius, use nova kg build.

nova kg build-provenance CAPSULE_DIR [-o OUTPUT] [--format turtle|nt|json-ld] [--validate/--no-validate]
Argument / Flag Description
CAPSULE_DIR Path to a capsule directory (reads its lineage.jsonl).
--output, -o PATH Write the RDF graph to this file (default: stdout).
--format RDF serialization: turtle (default), nt, or json-ld.
--validate / --no-validate SHACL-validate the graph (default on). Exit 1 if invalid facts are found (R11 ingest gate).

Exit codes: 0 (built, and SHACL-valid when --validate), 1 (SHACL validation failed, or the [spkg] extra is not installed).

nova kg build-provenance .novafabric/runs/01HXAY7M
# ✓ SHACL-valid: 7 PROV-O triples from 01HXAY7M
# @prefix nf: <https://novafabric.io/ns/spkg#> . …

nova kg build-provenance .novafabric/runs/01HXAY7M -o prov.ttl --format turtle

Findings emitted by nova kg detect must map to a MITRE ATT&CK technique and/or a D3FEND countermeasure — a raw anomaly score alone is rejected by the nf:FindingShape SHACL constraint (ADR-0111 R2).

nova kg build

Experimental (SPKG, ADR-0111; requires pip install novafabric[spkg]). Build both SPKG layers for a capsule: the canonical W3C PROV-O RDF (SHACL-gated) and the operational KùzuDB labeled-property graph (LPG) that powers attack-path / blast-radius traversal. The canonical layer is validated first (ADR-0111 R11) — on failure nothing is written to the operational store; the LPG is then rebuilt from the same capsule, so it holds no state not derivable from a capsule (R4). Where build-provenance exports the RDF, build populates the stores.

nova kg build CAPSULE_DIR [--path KG_PATH] [--validate/--no-validate]
Argument / Flag Description
CAPSULE_DIR Path to a capsule directory (reads its lineage.jsonl).
--path KùzuDB path for the SPKG operational graph, separate from the Capsule KG (default: .nova/kg/spkg.kuzu; env NOVA_SPKG_PATH).
--validate / --no-validate SHACL-gate the canonical layer before writing the LPG (default on). Exit 1 if invalid facts are found (R11).

Exit codes: 0 (both layers built), 1 (SHACL validation failed, or the [spkg] extra is not installed).

nova kg build .novafabric/runs/01HXAY7M
# ✓ SPKG built from 01HXAY7M (SHACL-valid): 7 PROV-O triples · 3 LPG edges → .nova/kg/spkg.kuzu

nova kg detect

Experimental (SPKG, ADR-0111). Rank a capsule's most anomalous lineage edges with an unsupervised, label-free structural outlier detector: it learns the fleet's own edge distribution (from --baseline capsules, or the target itself) and flags edges with high combined surprisal (rare edge-type / entity / kind-triple). Every reported edge carries a MITRE ATT&CK technique (ADR-0111 R2 — never a bare score). This is the dependency-free SP-2 baseline; the PyGOD/TGN GNN detector is a later, resource-gated upgrade. Needs no optional extra (the detector is pure standard-library).

nova kg detect CAPSULE_DIR [--baseline DIR ...] [--top/-k K] [--json] [-o OUTPUT]
Argument / Flag Description
CAPSULE_DIR Capsule directory to score (reads its lineage.jsonl).
--baseline DIR Capsule dir(s) to learn "normal" from — repeatable. Default: self-baseline on the target.
--top, -k Number of most-anomalous edges to report (default 5).
--json Emit schema-valid AnomalyFinding records instead of the table.
--output, -o PATH Write findings JSON here (with --json).

Exit code 0 (an anomaly scan is informational — a finding is not a failure).

nova kg detect .novafabric/runs/01HXAY7M -k 10
# SPKG anomaly scan — 01HXAY7M (top 10, self-baseline)  → ranked table with ATT&CK column

nova kg detect suspect/ --baseline normal-week-1/ --baseline normal-week-2/ --json -o findings.json

nova kg attack-path

Experimental (SPKG, ADR-0111; requires pip install novafabric[spkg]). Build the operational graph from a capsule's lineage, then run a bounded shortest-path query between two entities (UC2 lateral-movement). Entities are kind:ref pairs, e.g. run:attacker, dataset:aws_credentials. Informational — exit 0 whether or not a path exists.

nova kg attack-path CAPSULE_DIR --from KIND:REF --to KIND:REF [--max-depth N]
Argument / Flag Description
CAPSULE_DIR Capsule directory whose lineage.jsonl builds the SPKG graph.
--from KIND:REF Source entity, e.g. run:attacker. Required.
--to KIND:REF Target entity, e.g. dataset:aws_credentials. Required.
--max-depth N Maximum path length to search (default: 6).
nova kg attack-path .novafabric/runs/01HXAY7M \
  --from run:attacker --to dataset:aws_credentials
# ⚠ Attack path found: run:attacker → … → dataset:aws_credentials in 3 hop(s)

nova kg attack-path .novafabric/runs/01HXAY7M \
  --from agent:a1 --to artifact:report.md --max-depth 4
# ✓ No attack path from agent:a1 to artifact:report.md within 4 hop(s)

nova kg blast-radius

Experimental (SPKG, ADR-0111; requires pip install novafabric[spkg]). Build the operational graph from a capsule's lineage, then traverse the impact / blast radius of an entity (UC3 supply-chain propagation). --downstream (default) lists everything reachable from the entity — e.g. every run and artifact a poisoned model touched; --upstream lists the entity's provenance instead. Prints a Rich table of affected entities (kind, ref).

nova kg blast-radius CAPSULE_DIR --entity KIND:REF [--downstream|--upstream] [--max-depth N]
Argument / Flag Description
CAPSULE_DIR Capsule directory whose lineage.jsonl builds the SPKG graph.
--entity KIND:REF Entity to analyse, e.g. model:poisoned-model. Required.
--downstream / --upstream --downstream (default): what this entity affects (blast radius, UC3). --upstream: what influenced it (provenance).
--max-depth N Maximum traversal depth (default: 6).
# What did the poisoned model touch? (downstream / impact)
nova kg blast-radius .novafabric/runs/01HXAY7M --entity model:poisoned-model

# Where did this artifact come from? (upstream / provenance)
nova kg blast-radius .novafabric/runs/01HXAY7M --entity artifact:report.md --upstream

nova kg query

Query models and tools called by a specific agent.

nova kg query AGENT_ID [--path PATH] [--output json|text]
nova kg query my-agent --output json
# {
#   "agent_id": "my-agent",
#   "models": [
#     {"model_id": "gpt-4o", "provider": "openai", "call_count": 42, "confidence": 1.0}
#   ],
#   "tools": [
#     {"tool_id": "read_file", "tool_name": "read_file", "call_count": 18, "confidence": 0.0}
#   ],
#   "mcp_servers": [
#     {"server_id": "filesystem", "server_name": "filesystem", "call_count": 18}
#   ]
# }

The mcp_servers key lists MCPServer nodes reachable via the 2-hop path Agent → Tool → MCPServer. Empty when no MCP-namespaced tool calls were recorded.

Reference: src/novafabric/kg/, design/adr/0067-capsule-knowledge-graph-v1.md (private).

nova kg audit

Check KG store health: node/edge counts, orphaned edges, and zero-call-count anomalies.

nova kg audit [--path PATH] [--output json|text]
Flag Default Description
--path .nova/kg/nova_kg.kuzu KuzuDB path (also reads NOVA_KG_PATH)
--output, -o text Output format: text (Rich) or json
nova kg audit
# KG store health: ok
#   node_agent_count: 12
#   node_model_count: 4
#   edge_calls_count: 88
#   No issues detected.

nova kg audit --output json

Exit code 0 = no issues. Exit code 1 = issues detected.

GET /api/kg/topology (dashboard API)

Returns all KG nodes and edges for multi-layer topology visualization. Requires nova serve to be running with a KG store already initialised.

GET /api/kg/topology?max_nodes=500
Authorization: Bearer <token>

Response includes:

Used by the KGTab dashboard panel (Multi-Layer Topology section, lazy-loaded on demand). Response is cached for 30 seconds per serve process.

GET /api/kg/entity-queue (dashboard API)

Return all pending ReviewItem records from the Tier-3 human review queue. Requires nova serve.

GET /api/kg/entity-queue
GET /api/kg/entity-queue/stats
POST /api/kg/entity-queue/{item_id}/approve
POST /api/kg/entity-queue/{item_id}/reject

GET /api/kg/entity-queue returns { ok, count, items[] } where each item has item_id, candidate_name, entity_type, context_run_id, confidence, reason, status, created_at.

GET /api/kg/entity-queue/stats returns { ok, pending, approved, rejected }.

POST .../approve body: { "canonical": "resolved-entity-id", "resolved_by": "username" }. POST .../reject body: { "resolved_by": "username" } (optional).

Used by the KGTab Entity Review Queue panel in the dashboard (v0.31.0).

GET /api/kg/aliases (dashboard API)

List or register KG alias-table entries. Requires nova serve to be running.

GET  /api/kg/aliases[?canonical=<id>]   # list all; optional canonical filter
POST /api/kg/aliases                     # register an alias

POST body: { "alias": "gpt4", "canonical": "openai/gpt-4", "entity_type": "model" } Optional POST fields: confidence (float, default 1.0), registered_by (string, default "api").

Used by the KGTab KG Alias Management panel in the dashboard.

nova kg alias list

List all aliases registered for a canonical entity name in the Tier-2 alias table (SQLite-backed, no KuzuDB dependency required).

nova kg alias list CANONICAL [--alias-db PATH] [--output json|text]
Argument / Flag Default Env var Description
CANONICAL (required) — Canonical entity name to look up
--alias-db .nova/kg/alias.db NOVA_KG_ALIAS_DB SQLite path for the alias table
--output, -o text — Output format: text (Rich table) or json
nova kg alias list gpt-4o
# Aliases for 'gpt-4o':
#  Alias          Entity Type  Confidence  Source   Created At
#  openai/gpt-4o  model        1.000       manual   2026-05-10T…

nova kg alias register

Manually register an alias → canonical mapping in the Tier-2 alias table.

nova kg alias register ALIAS CANONICAL [--type model|agent|tool|endpoint|mcp_server]
    [--confidence FLOAT] [--alias-db PATH]
Argument / Flag Default Description
ALIAS (required) Alias form seen in capsule event payloads
CANONICAL (required) Canonical entity name to map to
--type, -t model Entity type: model, agent, tool, endpoint, or mcp_server
--confidence, -c 1.0 Confidence score (0.0–1.0)
--alias-db .nova/kg/alias.db SQLite path for the alias table
nova kg alias register "openai/gpt-4o" "gpt-4o" --type model --confidence 1.0
# Registered: 'openai/gpt-4o' → 'gpt-4o' (type=model, confidence=1.000)

nova kg alias register "filesystem" "nova-mcp-filesystem" --type mcp_server
# Registered: 'filesystem' → 'nova-mcp-filesystem' (type=mcp_server, confidence=1.000)

nova kg entity-queue list

List all pending review items in the Tier-3 entity human review queue (SQLite-backed).

nova kg entity-queue list [--queue-db PATH] [--output json|text]
Flag Default Env var Description
--queue-db .nova/kg/review_queue.db NOVA_KG_QUEUE_DB SQLite path for the review queue
--output, -o text — Output format: text (Rich table) or json
nova kg entity-queue list
# Entity Review Queue (2 pending)
#  Item ID  Alias       Type   Suggested Canonical  Confidence  Created At
#  uuid-…   my-agent    agent  my-agent-v2          0.720       2026-05-15T…

nova kg entity-queue approve

Approve a review item and assign its canonical name.

nova kg entity-queue approve ITEM_ID --canonical NAME [--by REVIEWER]
    [--queue-db PATH]
Argument / Flag Default Description
ITEM_ID (required) UUID of the review item to approve
--canonical, -c (required) Canonical name to assign
--by cli Reviewer identifier (name or user ID)
--queue-db .nova/kg/review_queue.db SQLite path for the review queue
nova kg entity-queue approve e3f9… --canonical "my-agent-v2" --by alice
# Approved: e3f9… → canonical='my-agent-v2' (resolved_by='alice')

nova kg entity-queue reject

Reject a review item without assigning a canonical name.

nova kg entity-queue reject ITEM_ID [--by REVIEWER] [--queue-db PATH]
Argument / Flag Default Description
ITEM_ID (required) UUID of the review item to reject
--by cli Reviewer identifier (name or user ID)
--queue-db .nova/kg/review_queue.db SQLite path for the review queue
nova kg entity-queue reject e3f9… --by alice
# Rejected: e3f9… (resolved_by='alice')

nova kg entity-queue stats

Show pending / approved / rejected counts for the entity review queue.

nova kg entity-queue stats [--queue-db PATH] [--output json|text]
Flag Default Env var Description
--queue-db .nova/kg/review_queue.db NOVA_KG_QUEUE_DB SQLite path for the review queue
--output, -o text — Output format: text or json
nova kg entity-queue stats
# Entity Review Queue Stats
#   pending:  3
#   approved: 18
#   rejected: 2

nova kg entity-queue stats --output json
# {"pending": 3, "approved": 18, "rejected": 2}