4.9 KiB
4.9 KiB
Excititor Graph Overlay Contract (v1.0.0)
Updated: 2025-12-10 | Owners: Excititor Core + UI Guilds | Scope: EXCITITOR-GRAPH-21-001..005, EXCITITOR-POLICY-20-001/002, EXCITITOR-RISK-66-001
Purpose
Defines the graph-ready overlay built from Link-Not-Merge observations/linksets so Console, Vuln Explorer, Policy, and Risk surfaces consume a single deterministic shape. This freezes the contract for Postgres materialization and cache APIs, unblocking Sprint 0120 tasks.
Schema
- JSON Schema:
docs/modules/excititor/schemas/vex_overlay.schema.json(draft 2020-12, schemaVersion1.0.0). - Required fields:
schemaVersion,generatedAt,tenant,purl,advisoryId,source,status,observations[],provenance. - Status enum:
affected|not_affected|under_investigation|fixed|unknown. - Ordering: observations are sorted by
source, advisoryId, fetchedAt(Link-Not-Merge invariant) and emitted in that order. Overlays are returned in request PURL order, then byadvisoryId, thensource. - Provenance: carries
linksetId,linksetHash,observationHashes[], optionalpolicyHash,sbomContextHash, andplanCacheKeyfor replay.
Postgres materialization (IAppendOnlyLinksetStore)
- Table
vex_overlays(materialized cache):- Primary key:
(tenant, purl, advisory_id, source). - Columns:
status,justifications(jsonb),conflicts(jsonb),observations(jsonb),provenance(jsonb),cached_at,ttl_seconds,schema_version. - Indexes: unique
(tenant, purl, advisory_id, source), plus(tenant, cached_at)for TTL sweeps.
- Primary key:
- Overlay rows are regenerated when linkset hash or observation hash set changes; cache evictions use
cached_at + ttl_seconds. - Linksets and observation hashes come from the append-only linkset store (
IAppendOnlyLinksetStore) to preserve Aggregation-Only Contract guarantees.
API shape (Graph/Vuln Explorer)
- Endpoint:
GET /v1/graph/overlays?purl=<purl>&purl=<purl>&includeJustifications=true|false. - Response items follow
vex_overlay.schema.json;cachestanza signalscached,cachedAt, andttlSeconds. - Cursoring: stable order (input PURL list) with
nextPageTokenbased on(tenant, purl, advisoryId, source, generatedAt). - Telemetry:
excititor.graph.overlays.cache{tenant,hit}counter;excititor.graph.overlays.latency_mshistogram tagged withcached.
Sample (abridged)
{
"schemaVersion": "1.0.0",
"generatedAt": "2025-12-10T00:00:00Z",
"tenant": "tenant-default",
"purl": "pkg:maven/org.example/foo@1.2.3",
"advisoryId": "GHSA-xxxx-yyyy-zzzz",
"source": "ghsa",
"status": "affected",
"justifications": [
{
"kind": "known_affected",
"reason": "Upstream GHSA reports affected range <1.3.0.",
"evidence": ["concelier:ghsa:obs:6561e41b3e3f4a6e9d3b91c1"],
"weight": 0.8
}
],
"conflicts": [
{
"field": "affected.versions",
"reason": "vendor_range_differs",
"values": ["<1.2.0", "<=1.3.0"],
"sourceIds": ["concelier:redhat:obs:...","concelier:ghsa:obs:..."]
}
],
"observations": [
{
"id": "concelier:ghsa:obs:6561e41b3e3f4a6e9d3b91c1",
"contentHash": "sha256:1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd",
"fetchedAt": "2025-11-19T00:00:00Z"
}
],
"provenance": {
"linksetId": "concelier:ghsa:linkset:6561e41b3e3f4a6e9d3b91d0",
"linksetHash": "sha256:deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead",
"observationHashes": ["sha256:1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd"],
"policyHash": "sha256:0f7c...9ad3",
"sbomContextHash": "sha256:421af53f9eeba6903098d292fbd56f98be62ea6130b5161859889bf11d699d18",
"planCacheKey": "tenant-default|pkg:maven/org.example/foo@1.2.3|GHSA-xxxx-yyyy-zzzz"
},
"cache": {
"cached": true,
"cachedAt": "2025-12-10T00:00:00Z",
"ttlSeconds": 300
}
}
Validation & determinism
- Validate overlays against
vex_overlay.schema.jsonin CI and during materialization; reject or warn when fields drift. - Deterministic ordering: input PURL order, then
advisoryId, thensource; observation list sorted bysource, advisoryId, fetchedAt. - No mutation: overlays are append-only; regeneration inserts a new row/version, leaving prior cache entries for audit until TTL expires.
Handoff
- Consumers (Console, Vuln Explorer, Policy Engine, Risk) should treat
vex_overlay.schema.jsonas the authoritative contract. - Offline kits must bundle the schema file and sample payloads under
docs/samples/excititor/with SHA256 manifests. - Future schema versions must bump
schemaVersionand add migration notes to this document anddocs/modules/excititor/architecture.md. - Policy and Risk surfaces in WebService now read overlays directly (with claim-store fallback for policy tests) to produce lookup and risk feeds; overlay cache/store are selected per tenant (in-memory by default, Postgres
vex.graph_overlayswhen configured).