save checkpoint
This commit is contained in:
@@ -39,6 +39,15 @@ Key settings:
|
||||
## Related Documentation
|
||||
|
||||
- Operations: `./operations/` (if exists)
|
||||
- Portable pack contract: `./portable-audit-pack-contract.md`
|
||||
- Portable manifest schema: `./schemas/portable-audit-pack-manifest.v1.schema.json`
|
||||
- Portable compatibility mapping: `./portable-audit-pack-compatibility.md`
|
||||
- Portable determinism profile: `./portable-audit-pack-determinism.md`
|
||||
- Portable Rekor offline profile: `./portable-audit-pack-rekor-offline.md`
|
||||
- Portable CLI runbook: `./portable-audit-pack-cli-runbook.md`
|
||||
- Portable Parquet profile: `./portable-audit-pack-parquet-profile.md`
|
||||
- Portable verification matrix: `./portable-audit-pack-test-matrix.md`
|
||||
- Promotion evidence contract: `./promotion-evidence-contract.md`
|
||||
- ExportCenter: `../export-center/`
|
||||
- Attestor: `../attestor/`
|
||||
- High-Level Architecture: `../../ARCHITECTURE_OVERVIEW.md`
|
||||
|
||||
@@ -276,6 +276,7 @@ Bundle N-1 Bundle N Bundle N+1
|
||||
* Attestation contract: `./attestation-contract.md`
|
||||
* Evidence bundle spec: `./evidence-bundle-v1.md`
|
||||
* Evidence pack schema: `./guides/evidence-pack-schema.md`
|
||||
* Promotion gate evidence contract: `./promotion-evidence-contract.md`
|
||||
* Audit bundle index schema: `./schemas/audit-bundle-index.schema.json`
|
||||
* ExportCenter: `../export-center/architecture.md`
|
||||
* Attestor: `../attestor/architecture.md`
|
||||
|
||||
@@ -93,3 +93,23 @@ Validation is fail-closed:
|
||||
- reject invalid Rekor index values
|
||||
|
||||
This contract is authoritative for Sprint 110 and blocks CONCELIER-ATTEST-73-001/002 and EXCITITOR-ATTEST-01-003/73-001/73-002.
|
||||
|
||||
## Gate Artifact Extension (v1.1, 2026-02-10)
|
||||
|
||||
Promotion evidence consumers now rely on additional optional fields for policy-gate semantics:
|
||||
|
||||
- `producer_bundle.evidence_score_value` (0-100 numeric score for threshold checks)
|
||||
- `producer_bundle.build_link.exists` (bool)
|
||||
- `producer_bundle.build_link.product_digest.sha256|sha512` (optional digest binding inputs)
|
||||
- `producer_bundle.artifact_digest.sha256|sha512` (optional explicit artifact digest)
|
||||
- `producer_bundle.dsse_signatures[]`:
|
||||
- `key_id`
|
||||
- `algorithm`
|
||||
- `valid`
|
||||
- `producer_bundle.rekor.checked_at` (UTC RFC3339 timestamp for freshness TTL checks)
|
||||
- `producer_bundle.human_decision_dsse_ref` (optional DSSE reference for signed escalation disposition)
|
||||
|
||||
Offline exports must retain enough metadata for air-gapped gate replay:
|
||||
- Rekor proof references (`tile_id`, `inclusion_proof_path`) and freshness timestamp.
|
||||
- DSSE signer evidence needed for k-of-n verification.
|
||||
- Human decision DSSE reference when escalation policy requires signed disposition.
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# Portable Audit Pack CLI Runbook
|
||||
|
||||
Status: Target behavior for implementation sprint handoff (2026-02-10).
|
||||
|
||||
## Objective
|
||||
Define expected parity between generation and verification CLI flows for portable audit packs in connected and air-gapped environments.
|
||||
|
||||
## Export workflow (target)
|
||||
```bash
|
||||
stella auditpack export \
|
||||
--artifact myorg/myapp@sha256:<digest> \
|
||||
--bom sbom.json \
|
||||
--vex vex/*.json \
|
||||
--out artifact-audit-pack.tzst \
|
||||
--profile portable-v1 \
|
||||
--rekor-tiles fetch \
|
||||
--sign-key ed25519:stella-bom-signer@2026Q1
|
||||
```
|
||||
|
||||
Expected behavior:
|
||||
- Emits manifest conforming to `portable-audit-pack-manifest.v1.schema.json`.
|
||||
- Produces deterministic archive metadata and ordered contents.
|
||||
- Emits stable machine-readable summary ordered by file path.
|
||||
|
||||
## Verify workflow (target)
|
||||
```bash
|
||||
stella auditpack verify artifact-audit-pack.tzst --offline --profile portable-v1
|
||||
```
|
||||
|
||||
Required checks:
|
||||
- Manifest signature verification.
|
||||
- File digest and size verification.
|
||||
- DSSE payload digest binding verification.
|
||||
- Rekor inclusion/root verification from bundled material.
|
||||
- Optional Parquet fingerprint verification when present.
|
||||
|
||||
## Output contract
|
||||
- Human output grouped in fixed order: manifest -> file digests -> DSSE -> Rekor -> optional index.
|
||||
- JSON output fields sorted lexicographically for deterministic diffing.
|
||||
- Non-zero exit and stable error codes on first failure.
|
||||
|
||||
## Air-gap operator sequence
|
||||
1. Transfer bundle to offline verifier host.
|
||||
2. Run `stella auditpack verify ... --offline`.
|
||||
3. Archive verification output with audit evidence.
|
||||
4. Record profile version and verifier key IDs in release record.
|
||||
|
||||
## Documentation dependency
|
||||
- Keep this runbook aligned with:
|
||||
- `portable-audit-pack-contract.md`
|
||||
- `portable-audit-pack-rekor-offline.md`
|
||||
- `portable-audit-pack-test-matrix.md`
|
||||
@@ -0,0 +1,40 @@
|
||||
# Portable Audit Pack Compatibility Mapping
|
||||
|
||||
Status: Draft frozen for implementation handoff (2026-02-10).
|
||||
|
||||
## Purpose
|
||||
Map current StellaOps evidence bundle contracts to the portable audit pack profile so writer/reader implementations use one required field model.
|
||||
|
||||
## Canonical contract source
|
||||
- Manifest schema: `docs/modules/evidence-locker/schemas/portable-audit-pack-manifest.v1.schema.json`
|
||||
- Profile contract: `docs/modules/evidence-locker/portable-audit-pack-contract.md`
|
||||
|
||||
## Required field mapping
|
||||
| Portable field | Existing source contract | Notes |
|
||||
| --- | --- | --- |
|
||||
| `spec_version` | `bundle.manifest.schema.json` `manifestVersion` | Portable uses fixed `1.0`. |
|
||||
| `artifact.digest.sha256` | `evidence-bundle-v1.md` subject digest | Required, lowercase hex without `sha256:` prefix in manifest payload fields. |
|
||||
| `files[*].sha256` | `checksums.schema.json` + bundle manifest entries | Portable stores per-file metadata directly in `files` map. |
|
||||
| `digests.canonical_bom_sha256` | `stellaops-evidence-pack.v1.schema.json` digest fields | New explicit top-level binding for BOM canonical bytes. |
|
||||
| `digests.dsse_payload_digest.sha256` | `attestation-contract.md` producer bundle digest linkage | Required preimage binding for DSSE payload verification. |
|
||||
| `rekor.tile_refs[]` | `attestor/transparency.md` + Rekor receipt inputs | Portable requires deterministic path references under `rekor/`. |
|
||||
| `rekor.root_hash` | Attestor checkpoint verification contract | Captured at inclusion checkpoint used by offline verifier. |
|
||||
| `verifiers.pubkeys[]` | Existing key bundle references | Portable manifest contains verifier key references used by CLI/offline verifier. |
|
||||
|
||||
## Legacy bundle compatibility
|
||||
- Legacy `evidence-bundle-<id>.tar.gz` and `portable-bundle-v1.tgz` remain valid for existing tooling.
|
||||
- Portable audit pack profile is additive and must not reinterpret legacy fields silently.
|
||||
- Readers should apply this precedence:
|
||||
1. If `spec_version` exists and equals `1.0`, validate against portable schema.
|
||||
2. Else if `manifestVersion` exists, validate against legacy `bundle.manifest.schema.json`.
|
||||
3. Else fail closed with `ERR_MANIFEST_PROFILE_UNKNOWN`.
|
||||
|
||||
## Writer/reader alignment rules
|
||||
- Writers MUST populate every required portable field in schema v1.
|
||||
- Readers MUST reject packs missing any required portable field.
|
||||
- Writers/readers MUST share the same portable schema artifact ID and hash in release notes.
|
||||
|
||||
## Migration notes
|
||||
- Maintain both parsers during transition.
|
||||
- Export paths should emit explicit profile indicator in logs and operator output.
|
||||
- Verification output should identify which profile was validated.
|
||||
106
docs/modules/evidence-locker/portable-audit-pack-contract.md
Normal file
106
docs/modules/evidence-locker/portable-audit-pack-contract.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# Portable Audit Pack Contract (v1 Draft)
|
||||
|
||||
## Purpose
|
||||
Define a deterministic, offline-verifiable portable audit pack contract that unifies Stella Ops evidence export semantics across Attestor, EvidenceLocker, AuditPack, and CLI verification flows.
|
||||
|
||||
## Contract status
|
||||
- Status: Draft for implementation.
|
||||
- Source sprint: `docs-archived/implplan/2026-02-10-completed-sprints/SPRINT_20260210_003_DOCS_portable_audit_pack_translation.md`
|
||||
- Canonical schema: `docs/modules/evidence-locker/schemas/portable-audit-pack-manifest.v1.schema.json`
|
||||
|
||||
## Companion profile documents
|
||||
- Compatibility mapping: `docs/modules/evidence-locker/portable-audit-pack-compatibility.md`
|
||||
- Determinism profile: `docs/modules/evidence-locker/portable-audit-pack-determinism.md`
|
||||
- Rekor offline verification profile: `docs/modules/evidence-locker/portable-audit-pack-rekor-offline.md`
|
||||
- CLI runbook (target behavior): `docs/modules/evidence-locker/portable-audit-pack-cli-runbook.md`
|
||||
- Optional Parquet profile: `docs/modules/evidence-locker/portable-audit-pack-parquet-profile.md`
|
||||
- Verification test matrix: `docs/modules/evidence-locker/portable-audit-pack-test-matrix.md`
|
||||
|
||||
## Target bundle profile
|
||||
|
||||
### Required artifacts
|
||||
- `manifest.json` (JCS canonical JSON)
|
||||
- `manifest.sig` (DSSE envelope over canonical manifest, detached file)
|
||||
- `canonical_bom.json` (canonicalized BOM snapshot)
|
||||
- `dsse_envelope.json` (attestation envelope bound to BOM/subject)
|
||||
- `rekor/` proof material:
|
||||
- checkpoint note/signature
|
||||
- inclusion proof data
|
||||
- tile bundle reference material (`tile.tar` or equivalent deterministic bundle)
|
||||
|
||||
### Optional artifacts
|
||||
- `merged_vex.json` (canonical merged VEX view)
|
||||
- `components.parquet` (optional analytics profile)
|
||||
- `checksums.txt` / replay helper assets for operational workflows
|
||||
|
||||
## Manifest contract (portable profile)
|
||||
|
||||
### Core fields
|
||||
- `spec_version`
|
||||
- `created_utc`
|
||||
- `artifact` (`name`, `version`, `digest`, `media_type`)
|
||||
- `files` map with per-file:
|
||||
- `sha256`
|
||||
- `size`
|
||||
- `content_type`
|
||||
- profile-specific metadata (for example `compression`, `schema_fingerprint`)
|
||||
- `digests`:
|
||||
- `canonical_bom_sha256`
|
||||
- `dsse_payload_digest`
|
||||
- `rekor`:
|
||||
- `log_id`
|
||||
- `api_version`
|
||||
- `tile_refs`
|
||||
- `root_hash`
|
||||
- `timestamps`
|
||||
- `verifiers` (key references and trust metadata)
|
||||
|
||||
Schema note:
|
||||
- Required field set and allowed optional fields are frozen in `portable-audit-pack-manifest.v1.schema.json`.
|
||||
|
||||
### Determinism rules
|
||||
- JSON canonicalization MUST use RFC 8785/JCS-compatible canonical output.
|
||||
- Manifest signing input MUST be the canonical bytes of `manifest.json`.
|
||||
- File inventory MUST be sorted lexicographically by canonical path.
|
||||
- Archive metadata MUST be deterministic (mtime, uid/gid, mode, ordering).
|
||||
- Digests MUST be lowercase SHA-256 hex unless profile explicitly states otherwise.
|
||||
|
||||
## Verification contract
|
||||
1. Verify `manifest.sig` against canonical `manifest.json`.
|
||||
2. Verify every file digest/size in `manifest.files`.
|
||||
3. Verify DSSE envelope signature(s) and payload digest binding.
|
||||
4. Verify Rekor inclusion proof against checkpoint root using bundled proof/tile data.
|
||||
5. Verify artifact/BOM subject digest consistency.
|
||||
6. If `components.parquet` is present, validate schema fingerprint metadata.
|
||||
|
||||
Default policy is fail-closed for missing or invalid required verification inputs.
|
||||
|
||||
## Current state vs target (gap summary)
|
||||
- Implemented:
|
||||
- Detached `manifest.sig` support in audit bundle paths.
|
||||
- Rekor offline proof verification primitives.
|
||||
- EvidenceLocker fields for canonical BOM/payload digest and Rekor refs.
|
||||
- Gaps:
|
||||
- No single unified portable manifest schema with full required field set.
|
||||
- Non-uniform canonicalization implementations across pack writers.
|
||||
- Determinism not fully enforced across all packaging flows.
|
||||
- Optional Parquet profile not defined in portable pack contract.
|
||||
|
||||
## Ownership map
|
||||
- `Attestor`: DSSE/Rekor proof verification contract and tile/checkpoint binding.
|
||||
- `EvidenceLocker`: persistence/export schema and portable bundle profile publication.
|
||||
- `StellaOps.AuditPack`: deterministic pack write/read/sign/verify implementation.
|
||||
- `CLI`: pack generation and offline verification UX parity.
|
||||
- `QA`: deterministic fixtures, tamper matrix, replay verification.
|
||||
|
||||
## Implementation notes
|
||||
- Keep compatibility mapping for legacy bundle manifests; do not silently reinterpret fields.
|
||||
- Keep offline posture: no mandatory network calls in verification.
|
||||
- Prefer shared canonicalization libraries over local ad hoc JSON serializers.
|
||||
|
||||
## References
|
||||
- `docs/modules/attestor/repro-bundle-profile.md`
|
||||
- `docs/modules/attestor/transparency.md`
|
||||
- `docs/modules/evidence-locker/export-format.md`
|
||||
- `docs/modules/evidence-locker/schemas/audit-bundle-index.schema.json`
|
||||
- `docs/modules/evidence-locker/schemas/stellaops-evidence-pack.v1.schema.json`
|
||||
@@ -0,0 +1,46 @@
|
||||
# Portable Audit Pack Determinism Profile
|
||||
|
||||
Status: Draft frozen for implementation handoff (2026-02-10).
|
||||
|
||||
## Scope
|
||||
Deterministic requirements for portable pack generation (`manifest.json`, BOM, DSSE envelope, Rekor material, optional VEX/Parquet artifacts).
|
||||
|
||||
## Normative rules
|
||||
1. Canonical JSON MUST use RFC 8785/JCS-compatible serialization.
|
||||
2. File inventory in `manifest.files` MUST be lexicographically sorted by canonical path.
|
||||
3. Archive entries MUST have fixed metadata:
|
||||
- `mtime`: `2026-01-01T00:00:00Z`
|
||||
- `uid/gid`: `0/0`
|
||||
- file mode `0644`, directory mode `0755`
|
||||
4. Digests MUST be lowercase SHA-256 hex.
|
||||
5. Optional artifacts (`merged_vex.json`, `components.parquet`) MUST not change ordering of required files.
|
||||
6. Compression toolchain versions MUST be pinned in release manifests.
|
||||
|
||||
## Canonicalization conformance tests (required)
|
||||
- Nested object key ordering stability.
|
||||
- Unicode normalization and escaping stability.
|
||||
- Non-finite number rejection (`NaN`, `Infinity`).
|
||||
- DSSE payload preimage digest stability across repeated runs.
|
||||
|
||||
## Byte stability gate
|
||||
- CI must generate the same pack twice from identical frozen input fixtures.
|
||||
- Outputs must be byte-identical (`sha256sum pack1 == pack2`).
|
||||
- On mismatch, pipeline fails with `ERR_PACK_NON_DETERMINISTIC`.
|
||||
|
||||
## Deterministic fixture layout
|
||||
- `testvectors/portable-audit-pack/minimal/`
|
||||
- `testvectors/portable-audit-pack/with-vex/`
|
||||
- `testvectors/portable-audit-pack/with-parquet/`
|
||||
|
||||
Each fixture set should include:
|
||||
- inputs (`sbom.json`, optional `vex.json`)
|
||||
- expected canonical files
|
||||
- expected per-file SHA-256 digests
|
||||
- expected package archive digest
|
||||
|
||||
## Toolchain pin set (to be implemented)
|
||||
- JCS canonicalizer version
|
||||
- DSSE signer library version
|
||||
- tar implementation/version
|
||||
- compression implementation/version
|
||||
- Parquet writer version (if profile enabled)
|
||||
@@ -0,0 +1,43 @@
|
||||
# Portable Audit Pack Parquet Profile (Optional)
|
||||
|
||||
Status: Optional profile contract for implementation handoff (2026-02-10).
|
||||
|
||||
## Positioning
|
||||
`components.parquet` is optional and must not be required for baseline pack verification.
|
||||
|
||||
## Manifest integration
|
||||
When present, `manifest.files["components.parquet"]` must include:
|
||||
- `sha256`
|
||||
- `size`
|
||||
- `content_type` = `application/x-parquet`
|
||||
- `compression` = `snappy`
|
||||
- `schema_fingerprint`
|
||||
|
||||
## Recommended schema columns
|
||||
- `package_name` (STRING)
|
||||
- `package_version` (STRING)
|
||||
- `purl` (STRING)
|
||||
- `license` (STRING)
|
||||
- `component_hash_sha256` (STRING)
|
||||
- `artifact_digest_sha256` (STRING)
|
||||
- `cve_id` (STRING, nullable)
|
||||
- `vex_status` (STRING, nullable)
|
||||
- `introduced_range` (STRING, nullable)
|
||||
- `fixed_version` (STRING, nullable)
|
||||
- `source_bom_sha256` (STRING)
|
||||
|
||||
## Determinism rules
|
||||
- Stable row ordering: `(artifact_digest_sha256, package_name, package_version, purl)`.
|
||||
- Stable column ordering exactly as listed above.
|
||||
- Stable Parquet writer settings pinned by version and compression codec.
|
||||
- `schema_fingerprint` must be reproducible from logical schema definition.
|
||||
|
||||
## Feature gating
|
||||
- Default profile: disabled.
|
||||
- Enable only with explicit profile flag.
|
||||
- Verification ignores Parquet content when absent.
|
||||
- Verification fails with `ERR_PARQUET_FINGERPRINT_MISMATCH` when present but invalid.
|
||||
|
||||
## Operator guidance
|
||||
- Use Parquet profile for fleet-level offline analytics.
|
||||
- Keep analytics ingestion separate from baseline release gate verification.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Portable Audit Pack Rekor Offline Verification Profile
|
||||
|
||||
Status: Draft frozen for implementation handoff (2026-02-10).
|
||||
|
||||
## Required Rekor material in pack
|
||||
At least one of:
|
||||
- `rekor/tile.tar`
|
||||
- `rekor/tiles.bundle`
|
||||
|
||||
And manifest references:
|
||||
- `rekor.log_id`
|
||||
- `rekor.api_version` (`2`)
|
||||
- `rekor.tile_refs[]`
|
||||
- `rekor.root_hash`
|
||||
|
||||
## Offline verification flow
|
||||
1. Validate manifest signature and manifest file inventory/digests.
|
||||
2. Load bundled tile material referenced by `rekor.tile_refs[]`.
|
||||
3. Reconstruct inclusion proof path for covered digests.
|
||||
4. Validate Merkle root equals `rekor.root_hash`.
|
||||
5. Validate checkpoint key material from `verifiers.rekor_pub` when present.
|
||||
6. Fail closed on any missing tile/proof/checkpoint dependency.
|
||||
|
||||
## Stable failure codes
|
||||
- `ERR_REKOR_TILE_MISSING`
|
||||
- `ERR_REKOR_TILE_DIGEST_MISMATCH`
|
||||
- `ERR_REKOR_PROOF_INVALID`
|
||||
- `ERR_REKOR_CHECKPOINT_INVALID`
|
||||
- `ERR_REKOR_ROOT_MISMATCH`
|
||||
- `ERR_REKOR_REFERENCE_UNCOVERED`
|
||||
|
||||
## Tamper test requirements
|
||||
- Corrupt one tile byte -> `ERR_REKOR_TILE_DIGEST_MISMATCH`.
|
||||
- Modify inclusion path node -> `ERR_REKOR_PROOF_INVALID`.
|
||||
- Alter checkpoint signature -> `ERR_REKOR_CHECKPOINT_INVALID`.
|
||||
- Alter `rekor.root_hash` in manifest -> `ERR_REKOR_ROOT_MISMATCH`.
|
||||
|
||||
## Compatibility notes
|
||||
- Existing Rekor receipt contracts remain valid for legacy bundle profiles.
|
||||
- Portable profile requires deterministic file references under `rekor/` in the manifest.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Portable Audit Pack Verification Test Matrix
|
||||
|
||||
Status: QA handoff matrix for implementation sprint (2026-02-10).
|
||||
|
||||
## Coverage matrix
|
||||
| ID | Layer | Scenario | Expected result |
|
||||
| --- | --- | --- | --- |
|
||||
| PAP-T01 | Unit | Manifest canonicalization stable for nested objects/unicode | PASS |
|
||||
| PAP-T02 | Unit | Manifest missing required field | `ERR_MANIFEST_SCHEMA` |
|
||||
| PAP-T03 | Unit | Manifest signature mismatch | `ERR_MANIFEST_SIGNATURE` |
|
||||
| PAP-T04 | Unit | DSSE payload digest mismatch | `ERR_DSSE_PAYLOAD_DIGEST` |
|
||||
| PAP-T05 | Unit | File digest mismatch for required file | `ERR_FILE_DIGEST_MISMATCH` |
|
||||
| PAP-T06 | Integration | Rekor tile corruption | `ERR_REKOR_TILE_DIGEST_MISMATCH` |
|
||||
| PAP-T07 | Integration | Rekor proof node corruption | `ERR_REKOR_PROOF_INVALID` |
|
||||
| PAP-T08 | Integration | Checkpoint signature corruption | `ERR_REKOR_CHECKPOINT_INVALID` |
|
||||
| PAP-T09 | Integration | Root hash mismatch | `ERR_REKOR_ROOT_MISMATCH` |
|
||||
| PAP-T10 | Integration | Required Rekor material missing | `ERR_REKOR_TILE_MISSING` |
|
||||
| PAP-T11 | E2E | Full offline verification with valid pack | PASS |
|
||||
| PAP-T12 | E2E | Repeat generation from frozen input twice | identical SHA-256 output |
|
||||
| PAP-T13 | E2E | Optional Parquet absent | PASS |
|
||||
| PAP-T14 | E2E | Optional Parquet present with invalid fingerprint | `ERR_PARQUET_FINGERPRINT_MISMATCH` |
|
||||
|
||||
## Fixture expectations
|
||||
- Golden fixtures under `testvectors/portable-audit-pack/`.
|
||||
- Expected digests tracked in fixture metadata and CI assertions.
|
||||
- All fixtures must be network-independent and deterministic.
|
||||
|
||||
## Execution log template
|
||||
| Date (UTC) | Fixture set | Command | Result | Owner |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 2026-02-10 | Planning only | Matrix drafted for implementation sprint handoff. | N/A | QA/Test Automation |
|
||||
95
docs/modules/evidence-locker/promotion-evidence-contract.md
Normal file
95
docs/modules/evidence-locker/promotion-evidence-contract.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# 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`
|
||||
@@ -0,0 +1,331 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://stellaops.io/schemas/evidence-locker/portable-audit-pack-manifest.v1.schema.json",
|
||||
"title": "StellaOps Portable Audit Pack Manifest v1",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"spec_version",
|
||||
"created_utc",
|
||||
"artifact",
|
||||
"files",
|
||||
"digests",
|
||||
"rekor",
|
||||
"timestamps",
|
||||
"verifiers"
|
||||
],
|
||||
"properties": {
|
||||
"spec_version": {
|
||||
"type": "string",
|
||||
"const": "1.0"
|
||||
},
|
||||
"created_utc": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"artifact": {
|
||||
"$ref": "#/$defs/artifact"
|
||||
},
|
||||
"files": {
|
||||
"type": "object",
|
||||
"minProperties": 3,
|
||||
"required": [
|
||||
"canonical_bom.json",
|
||||
"dsse_envelope.json",
|
||||
"manifest.sig"
|
||||
],
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"pattern": "^[^\\\\]+$"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/fileEntry"
|
||||
}
|
||||
},
|
||||
"digests": {
|
||||
"$ref": "#/$defs/digests"
|
||||
},
|
||||
"rekor": {
|
||||
"$ref": "#/$defs/rekor"
|
||||
},
|
||||
"timestamps": {
|
||||
"$ref": "#/$defs/timestamps"
|
||||
},
|
||||
"verifiers": {
|
||||
"$ref": "#/$defs/verifiers"
|
||||
},
|
||||
"compatibility": {
|
||||
"$ref": "#/$defs/compatibility"
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{64}$"
|
||||
},
|
||||
"artifact": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"version",
|
||||
"digest",
|
||||
"media_type"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"digest": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"sha256"
|
||||
],
|
||||
"properties": {
|
||||
"sha256": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
}
|
||||
}
|
||||
},
|
||||
"media_type": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"fileEntry": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"sha256",
|
||||
"size",
|
||||
"content_type"
|
||||
],
|
||||
"properties": {
|
||||
"sha256": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"content_type": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"compression": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none",
|
||||
"gzip",
|
||||
"zstd",
|
||||
"snappy"
|
||||
]
|
||||
},
|
||||
"schema_fingerprint": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"digests": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"canonical_bom_sha256",
|
||||
"dsse_payload_digest"
|
||||
],
|
||||
"properties": {
|
||||
"canonical_bom_sha256": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
},
|
||||
"dsse_payload_digest": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"sha256"
|
||||
],
|
||||
"properties": {
|
||||
"sha256": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"rekor": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"log_id",
|
||||
"api_version",
|
||||
"tile_refs",
|
||||
"root_hash"
|
||||
],
|
||||
"properties": {
|
||||
"log_id": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"api_version": {
|
||||
"type": "string",
|
||||
"const": "2"
|
||||
},
|
||||
"tile_refs": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"path",
|
||||
"covers"
|
||||
],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"pattern": "^rekor/"
|
||||
},
|
||||
"covers": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^SHA256:[A-Fa-f0-9]{64}$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"root_hash": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timestamps": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"bom_canonicalized",
|
||||
"dsse_signed",
|
||||
"rekor_included"
|
||||
],
|
||||
"properties": {
|
||||
"bom_canonicalized": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"dsse_signed": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"rekor_included": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"verifiers": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"pubkeys"
|
||||
],
|
||||
"properties": {
|
||||
"pubkeys": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/$defs/pubkey"
|
||||
}
|
||||
},
|
||||
"rekor_pub": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"type",
|
||||
"key_material"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"rekor-checkpoint",
|
||||
"rekor-key-hash"
|
||||
]
|
||||
},
|
||||
"key_material": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"pubkey": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"id",
|
||||
"type",
|
||||
"public_key",
|
||||
"usage"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ed25519",
|
||||
"ecdsa-p256",
|
||||
"rsa-4096"
|
||||
]
|
||||
},
|
||||
"public_key": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"usage": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"dsse",
|
||||
"manifest-signing",
|
||||
"checkpoint-verification"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"compatibility": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"legacy_manifest_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"legacy_bundle_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"migration_notes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user