2.7 KiB
2.7 KiB
Ledger Attestations Storage & Query Contract (LEDGER-OBS-54-001)
Status: PrepComplete (2025-11-20) Owners: Findings Ledger Guild · Provenance Guild
Goal
Provide a deterministic storage/view contract so the /v1/ledger/attestations endpoint can be implemented without further design work.
Table (proposed)
- Name:
ledger_attestations - Partitioning: tenant-scoped (same strategy as
ledger_events). - Columns:
tenant_id(text, not null)attestation_id(uuid, not null)artifact_id(text, not null) — OCI digest or SBOM idfinding_id(text, null)verification_status(text, not null;verified|failed|unknown)verification_time(timestamptz, not null)dsse_digest(text, not null; lowercase sha256)rekor_entry_id(text, null)evidence_bundle_ref(text, null)ledger_event_id(uuid, not null) — source ledger event linking the attestationrecorded_at(timestamptz, not null)merkle_leaf_hash(text, not null)root_hash(text, not null)cycle_hash(text, not null)projection_version(text, not null)
Indexes / ordering
- PK:
(tenant_id, attestation_id) - Paging index:
(tenant_id, recorded_at, attestation_id)to back deterministic sortrecorded_at ASC, attestation_id ASC. - Lookup indexes:
(tenant_id, artifact_id, recorded_at DESC)(tenant_id, finding_id, recorded_at DESC)(tenant_id, verification_status, recorded_at DESC)
Query contract for /v1/ledger/attestations
- Filters map to indexed columns:
artifactId,findingId,attestationId,status,sinceRecordedAt,untilRecordedAt. - Pagination token encodes
{ recordedAt, attestationId, filtersHash }; server must reject mismatched hash. - Response fields align 1:1 with columns above; no joins required.
- Determinism: sort strictly by
(recorded_at ASC, attestation_id ASC); no server clocks in payload.
Migration notes
- Add table and indexes in the same migration (see
src/Findings/StellaOps.Findings.Ledger/migrations/004_ledger_attestations.sql). - Backfill from existing provenance/verification store (if present) into this table with recorded_at = original verification timestamp.
- Ensure writes/coalescing happen via ledger projections to keep
ledger_event_id/cycle_hashconsistent.
Observability
- Logs:
ledger.attestations.query(tenant, filtersHash, limit, duration_ms, result_count). - Metrics:
ledger_attestations_queries_total{tenant,status},ledger_attestations_failures_total{reason}; reuse endpoint spans already defined in prep doc.
Artefact location
- Storage contract:
docs/modules/findings-ledger/prep/ledger-attestations-storage.md - HTTP contract:
docs/modules/findings-ledger/prep/ledger-attestations-http.md