96 lines
3.3 KiB
Markdown
96 lines
3.3 KiB
Markdown
# 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`
|