archive audit attempts

This commit is contained in:
master
2026-02-19 22:00:31 +02:00
parent c2f13fe588
commit b5829dce5c
19638 changed files with 6366 additions and 7 deletions

View File

@@ -0,0 +1,241 @@
# 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:
1. The Evidence Thread API (`GET /api/v1/evidence/thread/{id}`)
2. Audit pack generation (export all evidence for an artifact)
3. Console UI proof chain visualization
## Record Schema
```json
{
"$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:
```sql
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_id` and `rekor_tile` fields are `null`
- A `transparency_status` field indicates `"offline"` with a `reason` sub-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_at` ascending, then `predicate_type` ascending
- VEX refs sorted by `vulnerability_id` ascending
- Referrers sorted by `media_type` ascending, then `descriptor_digest` ascending
## References
- `docs/contracts/canonical-sbom-id-v1.md` - Canonical ID computation
- `docs/modules/attestor/architecture.md` - DSSE envelope storage
- `docs/modules/evidence-locker/attestation-contract.md` - Evidence Bundle v1
- Endpoint contract: S00-T05-EVID-01