# Promotion Evidence Contract ## Purpose This contract defines the evidence payload that promotion gates consume. It freezes ownership boundaries and required fields for deterministic and offline-capable release decisions. ## Ownership Boundaries | Capability | Owning module | Notes | | --- | --- | --- | | Evidence storage and retrieval | EvidenceLocker | Stores immutable evidence; does not decide allow/deny. | | DSSE signing | Signer | Produces signed envelopes and signer metadata. | | Transparency logging and proof material | Attestor | Produces Rekor UUID/log index/proof references and verifies proofs. | | PASS/FAIL policy decisioning | Policy Engine | Evaluates gate logic and emits decision outputs. | | Promotion orchestration | Release Orchestrator | Consumes evidence + policy outputs to progress promotion state. | ## Canonical Promotion Gate Input The promotion gate input MUST be serializable as canonical JSON with stable key order and UTC timestamps. ```json { "artifact": { "artifactId": "string", "digest": "sha256:..." }, "evidence": { "evidenceScore": "lowercase-hex-sha256", "bundleId": "guid", "bundleDigest": "sha256:..." }, "sbom": { "canonicalDigest": "sha256:...", "format": "cyclonedx|spdx" }, "attestations": [ { "predicateType": "string", "dsseDigest": "sha256:...", "signerKeyId": "string" } ], "transparency": { "rekorUuid": "string", "logIndex": 0, "proofRef": "cas://..." }, "vex": { "mergedDigest": "sha256:...", "statusSummary": "affected|not_affected|under_investigation|mixed" }, "inToto": { "layoutDigest": "sha256:...", "linkDigests": ["sha256:..."] } } ``` ## EvidenceLocker API Mapping | Endpoint | Contract fields populated | | --- | --- | | `POST /evidence` | `artifact.*`, `evidence.evidenceScore` | | `GET /evidence/score?artifact_id=...` | `evidence.evidenceScore` | | `POST /evidence/snapshot` | `evidence.bundleId`, `evidence.bundleDigest` | | `GET /evidence/{bundleId}` | `sbom.*`, `attestations[]`, `vex.*`, `inToto.*` | | `POST /evidence/verify` | `transparency.*` verification status and proof linkage | | `GET /evidence/{bundleId}/portable` | Offline bundle material for air-gapped verification | ## Determinism Requirements - Serialize gate input with stable ordering and invariant casing. - Use UTC RFC3339 timestamps only. - Sort all arrays that do not carry semantic order by deterministic key: - `attestations[]` by `predicateType`, then `dsseDigest` - `inToto.linkDigests[]` lexicographically - Treat digest values as lowercase. - Fail closed if required evidence fields are missing. ## Offline Verification Requirements - Promotion gates MUST accept proof references from offline bundles. - Rekor verification may use tile/proof material from local mirrors. - If transparency data is unavailable, gate outcome must be explicit policy output (for example deny, or break-glass path with auditable reason). ## Implementation References - EvidenceLocker architecture: `docs/modules/evidence-locker/architecture.md` - EvidenceLocker attestation contract: `docs/modules/evidence-locker/attestation-contract.md` - Policy ownership contract: `docs/modules/policy/promotion-gate-ownership-contract.md` - Release Orchestrator runtime gap plan: `docs/modules/release-orchestrator/promotion-runtime-gap-closure-plan.md`