This commit is contained in:
@@ -55,6 +55,7 @@ Status: draft; aligns with LNM v1 (frozen 2025-11-17) and observation/linkset mo
|
||||
}
|
||||
```
|
||||
- Ordering: stable by `sort` then `advisoryKey` then `linksetId`.
|
||||
- Pagination: cursor supported when `sort=observedAt`; for `sort=advisory` cursor is currently null (single page per request).
|
||||
- No derived verdicts or merged severity values; conflicts are emitted as structured markers only.
|
||||
|
||||
## Errors
|
||||
|
||||
@@ -15,9 +15,11 @@ Scope: Capture the required `/console/vex` API contract inputs so downstream tas
|
||||
- `GET /console/vex/{advisory_id}` returning grouped statements, precedence trace pointer, provenance links (DSSE hash + linkset id), and tenant scoping.
|
||||
- Response envelope: standard console error schema once WEB-OAS-61-002 is frozen; until then use draft shape with `error`, `message`, `trace_id`.
|
||||
- Determinism: results ordered by `(tenant_id, advisory_id, component_purl, version_range)`; pagination stable under new data.
|
||||
- Counters: return aggregate status counters `{status -> count}` in the response to power delta chips without extra queries.
|
||||
- Caching: allow short-lived (≤30s) in-memory cache keyed by tenant+filters for Console views; include `hasMore`+`cursor` to keep pagination stable.
|
||||
|
||||
## Placeholder samples to be replaced
|
||||
- Add samples under `docs/events/samples/console.vex@draft.json` once view spec is provided.
|
||||
|
||||
## Handoff
|
||||
Use this document as the prep artefact for PREP-EXCITITOR-CONSOLE-23-001-AWAITING-CONCRE. Update once LNM view spec and SSE envelope land; then freeze the OpenAPI excerpt and move the sprint task to DONE.
|
||||
Use this document as the prep artefact for PREP-EXCITITOR-CONSOLE-23-001-AWAITING-CONCRE. Update once LNM view spec and SSE envelope land; then freeze the OpenAPI excerpt and move the sprint task to DONE. (Initial implementation now live with caching + counters; SSE still pending.)
|
||||
|
||||
@@ -13,6 +13,7 @@ Scope: Define ingestion/egress contracts for Excititor when operating in sealed/
|
||||
- Ingestion envelope for `POST /airgap/vex/import`:
|
||||
- Fields: `bundleId`, `mirrorGeneration`, `signedAt`, `publisher`, `payloadHash`, `payloadUrl?` (offline tar path), `signature`, `transparencyLog?`.
|
||||
- Validation: deterministic hash of NDJSON payloads; must reject mixed tenants; clock-skew tolerance ±5s.
|
||||
- Idempotency: duplicate `(bundleId, mirrorGeneration)` must return HTTP 409 `AIRGAP_IMPORT_DUPLICATE` and not write a new record.
|
||||
- Sealed-mode error catalog (57-001): `AIRGAP_EGRESS_BLOCKED`, `AIRGAP_PAYLOAD_STALE`, `AIRGAP_SIGNATURE_MISSING`, `AIRGAP_SOURCE_UNTRUSTED`; each with HTTP 4xx mapping and remediation text.
|
||||
- Notification hooks (58-001): timeline events `airgap.import.started/completed/failed` with attributes `{tenantId,bundleId,generation,stalenessSeconds}`; link to Evidence Locker bundle ID for audit.
|
||||
- Determinism rules: sort imported observations by `advisoryKey` then `productKey`; write timeline events in the same order; all timestamps UTC ISO-8601.
|
||||
|
||||
50
docs/modules/mirror/dsse-tuf-profile.md
Normal file
50
docs/modules/mirror/dsse-tuf-profile.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# DSSE/TUF profile for Mirror thin bundles (v1 draft)
|
||||
|
||||
Applies to `mirror-thin-v1.*` artefacts in `out/mirror/thin/`.
|
||||
|
||||
## Keys
|
||||
- Signing algorithm: ed25519
|
||||
- Key IDs: `mirror-ed25519-test-1`
|
||||
- Storage: keep private key only in sealed CI secret; public key published alongside metadata at `out/mirror/thin/tuf/keys/mirror-ed25519-test-1.pub`.
|
||||
|
||||
## DSSE envelope
|
||||
- Payload type: `application/vnd.stellaops.mirror.manifest+json`
|
||||
- Payload: `mirror-thin-v1.manifest.json`
|
||||
- Signature: ed25519 over base64url(payload)
|
||||
- Envelope path: `out/mirror/thin/mirror-thin-v1.manifest.dsse.json`
|
||||
|
||||
## TUF metadata layout
|
||||
```
|
||||
out/mirror/thin/tuf/
|
||||
root.json
|
||||
snapshot.json
|
||||
targets.json
|
||||
timestamp.json
|
||||
keys/mirror-ed25519-test-1.pub
|
||||
```
|
||||
|
||||
### Targets mapping
|
||||
- `mirror-thin-v1.tar.gz` → targets entry with sha256 `210dc49e8d3e25509298770a94da277aa2c9d4c387d3c24505a61fe1d7695a49`
|
||||
- `mirror-thin-v1.manifest.json` → sha256 `0ae51fa87648dae0a54fab950181a3600a8363182d89ad46d70f3a56b997b504`
|
||||
|
||||
### Determinism rules
|
||||
- Sort keys in JSON; indent=2; trailing newline.
|
||||
- `expires` set to `2026-01-01T00:00:00Z` for draft; update during release.
|
||||
- Versions: root=1, targets=1, snapshot=1, timestamp=1 for this draft.
|
||||
- Signatures should be stable; for test draft, placeholders are used until CI signing is wired.
|
||||
|
||||
## Status & TODO to productionize
|
||||
- Draft signatures now generated with repo test key (`mirror-ed25519-test-1`) via `scripts/mirror/sign_thin_bundle.py`; replace with CI-held key before release.
|
||||
- CI hook: set `MIRROR_SIGN_KEY_B64` (base64-encoded Ed25519 PEM) and run `scripts/mirror/ci-sign.sh` to build+sign+verify in one step.
|
||||
- Rotate keys via TUF root role once CI secrets land.
|
||||
- Add DSSE signer to assembler pipeline so `make-thin-v1.sh` emits envelope + TUF metadata automatically in CI.
|
||||
|
||||
### CI integration sketch (disabled until key is provided)
|
||||
```
|
||||
- name: Mirror thin bundle (signed)
|
||||
run: |
|
||||
export MIRROR_SIGN_KEY_B64="${{ secrets.MIRROR_SIGN_KEY_B64 }}"
|
||||
export OCI=1
|
||||
scripts/mirror/ci-sign.sh
|
||||
if: ${{ secrets.MIRROR_SIGN_KEY_B64 != '' }}
|
||||
```
|
||||
20
docs/modules/mirror/provenance/observers.md
Normal file
20
docs/modules/mirror/provenance/observers.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# PROV-OBS-53-001 draft: provenance observers for mirror bundles
|
||||
|
||||
Goal: allow downstream services to verify mirror bundle manifests and tarballs using published hashes and (when available) DSSE/TUF signatures.
|
||||
|
||||
## Inputs
|
||||
- Manifest: `out/mirror/thin/mirror-thin-v1.manifest.json`
|
||||
- Tarball: `out/mirror/thin/mirror-thin-v1.tar.gz`
|
||||
- Hashes: `.sha256` files adjacent to artefacts
|
||||
- (Future) DSSE envelope + TUF metadata under `out/mirror/thin/tuf/`
|
||||
|
||||
## Observer checks (draft)
|
||||
1) Hash verification: recompute SHA256 for manifest and tarball; compare to `.sha256` files.
|
||||
2) Schema check: ensure manifest fields `version`, `created`, `layers[]`, `indexes[]` exist; all digests are `sha256:`.
|
||||
3) Determinism: verify tar entry order matches manifest order and tar headers are owner=0:0, mtime=0, sorted paths.
|
||||
4) Optional DSSE: once available, verify DSSE envelope signature over manifest using `mirror-ed25519-test-1` public key.
|
||||
5) Optional TUF: once available, verify `timestamp.json` -> `snapshot.json` -> `targets.json` -> artefact hashes.
|
||||
|
||||
## Implementation notes
|
||||
- These checks can be implemented as a small CLI (Go/C#/Python). For now, reference artefacts live in `out/mirror/thin/` for test runners.
|
||||
- Determinism probe: `tar --list --utc --full-time -vvf mirror-thin-v1.tar.gz` should show epoch mtimes and sorted entries.
|
||||
37
docs/modules/mirror/signing-runbook.md
Normal file
37
docs/modules/mirror/signing-runbook.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Mirror bundle signing runbook (CI)
|
||||
|
||||
## Prerequisites
|
||||
- Ed25519 private key (PEM). Keep in CI secrets only.
|
||||
- Base64-encode the PEM: `base64 -w0 mirror-ci-ed25519.pem > mirror-ci-ed25519.pem.b64`.
|
||||
- Create CI secret `MIRROR_SIGN_KEY_B64` with that value.
|
||||
|
||||
## Pipeline step (Gitea example)
|
||||
```
|
||||
- name: Build/sign mirror thin bundle
|
||||
env:
|
||||
MIRROR_SIGN_KEY_B64: ${{ secrets.MIRROR_SIGN_KEY_B64 }}
|
||||
OCI: 1
|
||||
run: |
|
||||
scripts/mirror/check_signing_prereqs.sh
|
||||
scripts/mirror/ci-sign.sh
|
||||
```
|
||||
Outputs are placed under `out/mirror/thin/` and `out/mirror/thin/oci/`; archive these as artifacts.
|
||||
|
||||
### How to add the secret in Gitea (one-time)
|
||||
1. Repository → Settings → Secrets.
|
||||
2. New secret: name `MIRROR_SIGN_KEY_B64`, value = base64-encoded Ed25519 PEM (no newlines, no header/footer).
|
||||
3. Scope: repository (or environment-specific if needed).
|
||||
4. Save. The pipeline step will skip if the secret is empty; keep it present in release branches only.
|
||||
|
||||
## Local dry-run with test key
|
||||
```
|
||||
MIRROR_SIGN_KEY_B64=$(base64 -w0 out/mirror/thin/tuf/keys/mirror-ed25519-test-1.pem) \
|
||||
OCI=1 scripts/mirror/ci-sign.sh
|
||||
```
|
||||
|
||||
## Verification
|
||||
The CI step already runs `scripts/mirror/verify_thin_bundle.py`. For OCI, ensure `out/mirror/thin/oci/index.json` references the manifest digest.
|
||||
|
||||
## Fallback (if secret absent)
|
||||
- Keep MIRROR-CRT-56-002 BLOCKED and do not publish unsigned bundles.
|
||||
- Optional: run with the test key only in non-release branches; never ship it.
|
||||
@@ -26,6 +26,12 @@ Purpose: unblock MIRROR-CRT-56-001 by defining expected assembler outputs so the
|
||||
## Evidence
|
||||
- When produced, place artefacts under `out/mirror/thin/` and add hashes to this doc.
|
||||
|
||||
### v1 sample (published 2025-11-23)
|
||||
- Manifest: `out/mirror/thin/mirror-thin-v1.manifest.json`
|
||||
- SHA256: `0ae51fa87648dae0a54fab950181a3600a8363182d89ad46d70f3a56b997b504`
|
||||
- Tarball: `out/mirror/thin/mirror-thin-v1.tar.gz`
|
||||
- SHA256: `210dc49e8d3e25509298770a94da277aa2c9d4c387d3c24505a61fe1d7695a49`
|
||||
|
||||
## Owners
|
||||
- Mirror Creator Guild (assembler)
|
||||
- AirGap Guild (consumer)
|
||||
|
||||
Reference in New Issue
Block a user