7.6 KiB
Contract: Artifact Canonical Record (v1)
Status
- Status: DRAFT (2026-02-19)
- Owners: Attestor Guild, EvidenceLocker Guild
- Consumers: Evidence Thread API, Audit Pack Export, Console UI
- Sprint: SPRINT_20260219_009 (CID-03)
Purpose
Define a unified document that aggregates all evidence references for a single artifact identified by canonical_id. Today this information is distributed across Attestor (dsse_envelopes), Scanner (SBOM refs), VexLens (consensus records), and OCI registries (referrers). The Artifact Canonical Record provides a single query target for:
- The Evidence Thread API (
GET /api/v1/evidence/thread/{id}) - Audit pack generation (export all evidence for an artifact)
- Console UI proof chain visualization
Record Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Artifact Canonical Record v1",
"type": "object",
"required": ["canonical_id", "format", "sbom_ref", "created_at"],
"properties": {
"canonical_id": {
"type": "string",
"description": "sha256:<hex> computed per canonical-sbom-id-v1.md",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"format": {
"type": "string",
"description": "Canonicalization format identifier",
"const": "cyclonedx-jcs:1"
},
"sbom_ref": {
"type": "string",
"description": "Content-addressable reference to the SBOM (CAS URI or OCI ref)",
"examples": [
"cas://sbom/inventory/abc123.json",
"oci://registry/repo@sha256:abc123"
]
},
"attestations": {
"type": "array",
"description": "All DSSE attestations referencing this artifact",
"items": {
"type": "object",
"required": ["predicate_type", "dsse_digest", "signed_at"],
"properties": {
"predicate_type": {
"type": "string",
"description": "Predicate type URI from the predicate registry"
},
"dsse_digest": {
"type": "string",
"description": "SHA-256 of the DSSE envelope body",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"signer_keyid": {
"type": "string",
"description": "Key ID of the signer"
},
"rekor_entry_id": {
"type": "string",
"description": "Rekor transparency log entry UUID (null if offline)"
},
"rekor_tile": {
"type": "string",
"description": "Rekor tile URL for inclusion proof verification"
},
"signed_at": {
"type": "string",
"format": "date-time"
}
}
}
},
"referrers": {
"type": "array",
"description": "OCI referrers (symbol bundles, attestation manifests)",
"items": {
"type": "object",
"required": ["media_type", "descriptor_digest"],
"properties": {
"media_type": {
"type": "string",
"description": "OCI media type",
"examples": [
"application/vnd.stella.symbols+tar",
"application/vnd.in-toto+json"
]
},
"descriptor_digest": {
"type": "string",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"registry": {
"type": "string",
"description": "Registry hostname"
}
}
}
},
"vex_refs": {
"type": "array",
"description": "VEX consensus records targeting this artifact",
"items": {
"type": "object",
"required": ["vulnerability_id", "consensus_status"],
"properties": {
"vulnerability_id": {
"type": "string",
"description": "CVE or advisory ID"
},
"consensus_status": {
"type": "string",
"enum": ["affected", "not_affected", "under_investigation", "fixed"]
},
"confidence_score": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"consensus_digest": {
"type": "string",
"description": "SHA-256 of the VexLens consensus record"
},
"dsse_digest": {
"type": "string",
"description": "SHA-256 of the VEX attestation DSSE (if signed)"
},
"rekor_tile": {
"type": "string",
"description": "Rekor tile URL (if anchored)"
}
}
}
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
}
}
Content Addressing
The record itself is content-addressed:
record_id := sha256(JCS(record_json))
This enables deduplication and integrity verification of the record.
Storage Design
Option A: Materialized View (recommended for v1)
Create a PostgreSQL materialized view joining existing tables:
CREATE MATERIALIZED VIEW proofchain.artifact_canonical_records AS
SELECT
se.bom_digest AS canonical_id,
'cyclonedx-jcs:1' AS format,
se.artifact_digest,
se.purl,
se.created_at,
-- Attestations aggregated as JSONB array
COALESCE(
jsonb_agg(DISTINCT jsonb_build_object(
'predicate_type', de.predicate_type,
'dsse_digest', de.body_hash,
'signer_keyid', de.signer_keyid,
'rekor_entry_id', re.uuid,
'rekor_tile', re.log_id,
'signed_at', de.signed_at
)) FILTER (WHERE de.env_id IS NOT NULL),
'[]'::jsonb
) AS attestations
FROM proofchain.sbom_entries se
LEFT JOIN proofchain.dsse_envelopes de ON de.entry_id = se.entry_id
LEFT JOIN proofchain.rekor_entries re ON re.env_id = de.env_id
GROUP BY se.entry_id, se.bom_digest, se.artifact_digest, se.purl, se.created_at;
CREATE UNIQUE INDEX idx_acr_canonical_id ON proofchain.artifact_canonical_records(canonical_id);
Refresh strategy: REFRESH MATERIALIZED VIEW CONCURRENTLY triggered by:
- New DSSE envelope submission
- New Rekor entry confirmation
- Periodic schedule (every 5 minutes)
Option B: Dedicated Table (for v2 if write patterns emerge)
If the record needs to be written to directly (e.g., for referrers and VEX refs that come from different services), migrate to a dedicated table with trigger-based population from upstream sources.
API Integration
Evidence Thread Endpoint
GET /api/v1/evidence/thread/{canonical_id}
Returns the Artifact Canonical Record for the given canonical_id. The response includes all attestations, referrers, and VEX refs.
Query parameters:
include_attestations(boolean, default true)include_vex(boolean, default true)include_referrers(boolean, default true)
Evidence Thread by PURL
GET /api/v1/evidence/thread?purl={purl}
Resolves PURL to canonical_id via sbom_entries table, then returns the record.
Offline Mode
When Rekor is unavailable (air-gapped deployment):
rekor_entry_idandrekor_tilefields arenull- A
transparency_statusfield indicates"offline"with areasonsub-field - Verification uses bundled checkpoints instead of live Rekor queries
Determinism
The record's content is deterministic given frozen inputs:
- Attestation arrays sorted by
signed_atascending, thenpredicate_typeascending - VEX refs sorted by
vulnerability_idascending - Referrers sorted by
media_typeascending, thendescriptor_digestascending
References
docs/contracts/canonical-sbom-id-v1.md- Canonical ID computationdocs/modules/attestor/architecture.md- DSSE envelope storagedocs/modules/evidence-locker/attestation-contract.md- Evidence Bundle v1- Endpoint contract: S00-T05-EVID-01