Refactor code structure for improved readability and maintainability; removed redundant code blocks and optimized function calls.
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled

This commit is contained in:
master
2025-11-20 07:50:52 +02:00
parent 616ec73133
commit 10212d67c0
473 changed files with 316758 additions and 388 deletions

View File

@@ -0,0 +1,15 @@
{
"tenantId": "urn:tenant:00000000-0000-0000-0000-000000000000",
"issuer": "https://auth.stellaops.local",
"scopes": ["concelier.read", "concelier.linkset.write"],
"capabilities": {
"mergeAllowed": false,
"offlineAllowed": true
},
"attribution": {
"actor": "service:concelier-web",
"traceId": "00000000000000000000000000000000"
},
"issuedAt": "2025-11-19T00:00:00Z",
"expiresAt": "2025-11-20T00:00:00Z"
}

View File

@@ -0,0 +1,36 @@
# AUTH-TEN-47-001 · Tenant Scope Contract (v1)
Purpose: define tenant scoping fields and enforcement expectations so Concelier tasks (CONCELIER-TEN-48-001) can proceed without merging behavior.
## Data contract
- `tenantId` (string, required): immutable per request; canonical form `urn:tenant:{uuid}`.
- `issuer` (string, required): authority instance issuing the token; aids audit.
- `scopes` (array<string>, required): must include `concelier.read` or `concelier.linkset.read` for evidence fetch; `concelier.linkset.write` for backfill/ingest; `concelier.tenant.admin` for tenancy capabilities endpoint.
- `capabilities` (object, optional):
- `mergeAllowed` (bool, default false): must remain false for Link-Not-Merge paths.
- `offlineAllowed` (bool, default true): governs offline bundle use.
- `attribution` (object, optional):
- `actor` (string): subject or client-id.
- `traceId` (string): optional trace correlation.
- `issuedAt` (string, ISO-8601 UTC), `expiresAt` (string, ISO-8601 UTC): required for enforcement.
## Enforcement rules (Authority)
- Tokens missing `tenantId` or `concelier.*` scopes are rejected with 403 and error code `auth/tenant-scope-missing`.
- `mergeAllowed` must be evaluated server-side; clients cannot set true when Link-Not-Merge mode is active.
- Refresh/rotation must preserve `tenantId`; changing tenant requires re-auth.
## Fixtures
- JSON fixture: `docs/modules/authority/fixtures/auth-ten-47-001.json` (included) shows a minimal token payload.
- Determinism: field order canonicalized lexicographically for hashing; timestamps normalized to `Z`.
## Actions for consumers (Concelier)
- Validate `tenantId` present and stable across request and event emission.
- Expose `/capabilities/tenant` endpoint echoing `tenantId`, scopes, and `mergeAllowed=false` when LNM is enabled.
- Log `tenantId`, `actor`, and `traceId` on every linkset backfill or advisory read for audit.
## Owners
- Authority Guild (contract)
- Concelier Core Guild (consumer)
## Change control
- Add-only evolution. New capabilities must default to the most restrictive value.

View File

@@ -0,0 +1,29 @@
# CLI Guardrail Artefacts (2025-11-19)
## CLI-VULN-29-001 artefacts
- Output bundle: `out/console/guardrails/cli-vuln-29-001/`
- Files:
- `sample-vuln-output.ndjson` — sample `stella vuln scan` output (offline profile, two CVEs, includes provenance and summary).
- `sample-sbom-context.json` — SBOM context export referenced by DOCS-AIAI-31-005/006/008/009.
- `hashes.sha256` — SHA256 for all files in folder.
- Hashes (duplicate of hashes file for convenience):
```
421af53f9eeba6903098d292fbd56f98be62ea6130b5161859889bf11d699d18 out/console/guardrails/cli-vuln-29-001/sample-sbom-context.json
e5aecfba5cee8d412408fb449f12fa4d5bf0a7cb7e5b316b99da3b9019897186 out/console/guardrails/cli-vuln-29-001/sample-vuln-output.ndjson
```
## CLI-VEX-30-001 artefacts
- Output bundle: `out/console/guardrails/cli-vex-30-001/`
- Files:
- `sample-vex-output.ndjson` — sample `stella vex export` output with single not_affected statement.
- `sample-pagination-note.txt` — paging/limit note (default 500, max 2000; cursor via --page-token).
- `hashes.sha256` — SHA256 for all files in folder.
- Hashes:
```
f450d914dfe5c8d80f773c1fd0113bbba921aca339316c6308f1c098199aef6b out/console/guardrails/cli-vex-30-001/sample-pagination-note.txt
2b11b1e2043c2ec1b0cb832c29577ad1c5cbc3fbd0b379b0ca0dee46c1bc32f6 out/console/guardrails/cli-vex-30-001/sample-vex-output.ndjson
```
## Notes
- Samples are deterministic; generated timestamp `2025-11-19T00:00:00Z`.
- Replace with live artefacts once CLI commands are executed against frozen datasets; keep paths/hashes updated here and in sprint logs.

View File

@@ -0,0 +1,81 @@
# CONCELIER-VULN-29-001 · Concelier ↔ Vuln Explorer bridge (contract v0.1)
Purpose: unblock PREP-CONCELIER-VULN-29-001 by defining the request/response contract Concelier must expose for Vuln Explorer and VEX Lens to consume advisory evidence without merge semantics.
## API shape (Concelier WebService)
- Endpoint: `POST /v1/advisories/search`
- Auth: tenant-scoped token (`concelier.read` scope) with `mergeAllowed=false`.
- Request body:
```json
{
"query": {
"advisory_keys": ["GHSA-xxxx-xxxx"],
"purls": ["pkg:maven/org.example/app@1.2.3"],
"component_hashes": ["sha256:..."],
"has_vex": true
},
"page": {
"size": 50,
"cursor": null
}
}
```
- Response body:
```json
{
"results": [
{
"advisory_key": "GHSA-xxxx-xxxx",
"source": "ghsa",
"observations": [{
"id": "obs-123",
"purl": "pkg:maven/org.example/app@1.2.3",
"version_range": "[1.2.0,2.0.0)",
"status": "affected",
"published": "2025-11-01T00:00:00Z",
"fixed": null,
"provenance": {
"source": "ghsa",
"retrieved_at": "2025-11-18T12:00:00Z",
"hash": "sha256:..."
}
}],
"vex": [{
"statement_id": "st-001",
"status": "not_affected",
"justification": "component_not_present",
"impact_statement": "Library not shipped",
"provenance": {
"source": "vendor-vex",
"retrieved_at": "2025-11-18T12:00:00Z",
"hash": "sha256:..."
}
}]
}
],
"page": {"cursor": "next-token", "size": 50}
}
```
## Determinism requirements
- Sort observations by `purl`, then `published`, then `id`.
- Sort VEX statements by `statement_id`.
- Timestamps UTC ISO-8601 with `Z`.
- Omit merge-derived fields; only surface source-provided values.
## Fields Vuln Explorer expects
- `advisory_key`, `source`, `observations[*].purl`, `observations[*].status`, `observations[*].provenance.hash`.
- VEX fields: `status`, `justification`, `impact_statement`, `provenance.hash`.
- Optional: `fix_version` when present; keep absent otherwise (no empty strings).
## Test fixtures
- Location: `docs/samples/console/console-vex-30-001.json` already includes VEX sample keyed by advisory; add Concelier response sample to `docs/samples/console/concelier-vuln-29-001.json` (to be generated alongside implementation).
## Owners
- Concelier WebService Guild (producer)
- Vuln Explorer Guild / VEX Lens Guild (consumers)
## Next actions to unblock work
- Implement endpoint stub with deterministic ordering and fixture from above sample.
- Hook to Link-Not-Merge storage (fact-only) once available.
- Add contract reference to OAS (Sprint 0114 CONCELIER-OAS-61-001 depends on this).

View File

@@ -0,0 +1,7 @@
# ICSCISA / KISA Feed Provenance Notes (2025-11-19)
- Expected signing: not provided by sources; set `signature=null` and `skip_reason="unsigned"`.
- Hashing: sha256 of raw advisory payload before normalization.
- Transport: HTTPS; mirror to internal cache; record `fetched_at` UTC and `source_url`.
- Verification: compare hash vs previous run; emit delta report.
- Staleness guard: alert if `fetched_at` >14 days.

View File

@@ -0,0 +1,46 @@
# ICSCISA / KISA Feed Remediation Plan (v0.1 · 2025-11-19)
## Purpose
Define a minimal, actionable plan to refresh overdue ICSCISA and KISA connectors, restore provenance freshness, and publish normalized payload fields for downstream Advisory AI and Concelier consumers.
## Owners
- Feed owners: Concelier Feed Guild
- Product advisory liaison: Product Advisory Guild
- Backup: Docs Guild
## Scope & cadence
- Feeds: ICSCISA, KISA (security advisories)
- Refresh cadence: weekly pull; publish hashlist and timestamps per run
- Staleness budget: <14 days; alert if exceeded
## Deliverables (for PREP-FEEDCONN-ICS-KISA-PLAN)
1) **Provenance refresh SOP**
- Mirror source URLs to internal cache
- Record `source_url`, `fetched_at` (UTC), `sha256`, `signature` (if present)
- Store run log under `out/feeds/icscisa-kisa/<YYYYMMDD>/fetch.log`
2) **Normalized payload fields**
- `advisory_id`, `title`, `summary`, `published`, `updated`, `severity` (pass-through), `cvss` (if provided), `cwe`, `affected_products` (list), `references` (list of URL strings), `signature` (object or null)
- Preserve source values; no inference or merging
3) **Backlog cleanup**
- Reprocess last 60 days; compare hash to prior ingests; flag changed advisories
- Emit delta report (`out/feeds/icscisa-kisa/<YYYYMMDD>/delta.json`): added/updated/removed ids, counts
4) **Provenance note**
- Publish `docs/modules/concelier/feeds/icscisa-kisa-provenance.md` with current signing keys/fingerprints, expected headers, and fallback when signatures missing
5) **Next review date**
- Set to 2025-12-03 (two-week check) and capture SIG verification status
## Actions & timeline
- T0 (2025-11-19): adopt SOP + field map; create delta report template
- T0+2d (2025-11-21): run backlog reprocess, publish artefacts + hashes
- T0+14d (2025-12-03): review staleness, adjust cadence if needed
## Artefact locations
- Normalized advisories: `out/feeds/icscisa-kisa/<YYYYMMDD>/advisories.ndjson`
- Fetch log + hashes: `out/feeds/icscisa-kisa/<YYYYMMDD>/fetch.log`, `hashes.sha256`
- Delta report: `out/feeds/icscisa-kisa/<YYYYMMDD>/delta.json`
- Provenance note: `docs/modules/concelier/feeds/icscisa-kisa-provenance.md`
## Risks & mitigations
- Source downtime mirror last good snapshot; retry daily for 3 days.
- Missing signatures record `signature=null`, log `skip_reason` in provenance note; do not infer validity.
- Schema drift treat as new fields, store raw, add to field map after review (no drop).

View File

@@ -0,0 +1,40 @@
# Concelier Advisory Chunk Cache
Status: draft · aligned with LNM v1 (frozen 2025-11-17)
## Purpose
- Reduce repeated `/advisories/{key}/chunks` rebuilds while preserving determinism and provenance integrity.
- Provide consoles and debugging tools with transparent cache provenance (key material and TTL) to keep evidence chains auditable.
## Cache key (deterministic)
- Inputs (pipe-delimited, all lower-case where applicable):
- `tenant`
- `advisoryKey`
- `chunkLimit`
- `observationLimit`
- `minimumLength`
- `sectionFilter` (sorted, case-insensitive)
- `formatFilter` (sorted, case-insensitive)
- advisory fingerprint (hash of canonical advisory)
- ordered observations: each `observationId@contentHash@createdAtTicks@format`
- Implementation: `AdvisoryChunkCacheKey.Create` (WebService). Normalized ordering ensures replayability across nodes.
- External disclosure: headers expose a hashed cache key (see below) rather than the full raw key.
## Response transparency headers
- `X-Stella-Cache-Key`: SHA-256 hex of the cache key (default 16 chars) for tamper-evident correlation.
- `X-Stella-Cache-Hit`: `1` on hit, `0` on miss.
- `X-Stella-Cache-Ttl`: configured TTL in seconds (0 when caching disabled).
- Consumers may log these headers; do **not** treat them as stable API contract for filtering.
## Determinism & safety
- No PII in keys or headers; key inputs are tenant/advisory identifiers already present in requests.
- Timestamps use UTC ticks; content hashes retain upstream digest prefix (e.g., `sha256:`) to distinguish sources.
- Cache TTL defaults to 0 (disabled) unless configured; safe for offline/air-gapped deployments.
## Testing notes
- Unit coverage: `AdvisoryChunkCacheKeyTests` (ordering, filter casing) and `AdvisoryChunkBuilderTests` (observationPath pointers influence chunk IDs).
- Integration tests should assert headers when cache is enabled; disable cache for tests that assert body-only determinism.
## TODOs / follow-ups
- Add integration test that exercises a cache hit path and validates transparency headers.
- Document cache configuration knobs in `appsettings.*` once finalized.

View File

@@ -0,0 +1,37 @@
# Evidence Locker Attestation Scope Note (v1) — 2025-11-19
## Scope & Coverage
- Predicates: `in-toto Provenance` (DSSE-wrapped) with claims for bundle inputs, normalization pipeline version, tenant scope, and content hashes; optional `Rekor` transparency pointer when online.
- Artefacts covered: Evidence Bundle v1 payloads (observations, linksets, normalization diffs) and mirror bundle manifest hash when present.
- Tenancy: tenant-id is lowercased, required, and included in subject, claims, and DSSE `_type` to keep air-gap parity.
- Transparency: if Rekor unavailable (air-gap), include `transparency.skip_reason` = `offline` and signed local timeline anchor.
## Required claims (PromotionAttestationBuilder input)
- `subject.digest` (sha256 of bundle tar) and `subject.name` (bundle_id).
- `bundle.created` (UTC RFC3339) and `bundle.version` (semantic).
- `pipeline.version` (build ID or git SHA) and `pipeline.inputs` (hashes of observation/linkset payloads).
- `tenant` (lowercase) and `scope` (advisory | vex | policy | mixed).
- `evidence_bundle` (path/doi) and `transparency` (rekor UUID or skip reason).
- `aoc.guardrails` (boolean) and `aoc.details` (list of enforced checks).
## Example builder payload
```json
{
"subject": {"name": "evidence-bundle-m0", "digest": "sha256:REPLACE"},
"bundle": {"id": "evidence-bundle-m0", "version": "1.0.0", "created": "2025-11-19T00:00:00Z"},
"pipeline": {"version": "git:abcd1234", "inputs": ["sha256:payload-hash-1", "sha256:payload-hash-2"]},
"tenant": "demo",
"scope": "vex",
"evidence_bundle": "out/evidence/bundles/evidence-bundle-m0.tar.gz",
"transparency": {"rekor_uuid": null, "skip_reason": "offline"},
"aoc": {"guardrails": true, "details": ["schema:frozen:1.0", "limits:chunk:max=2000"]}
}
```
## Placement
- File: `docs/modules/evidence-locker/attestation-scope-note.md` (this document).
- Reference in Evidence Bundle changelog and sprint `Execution Log` when updated.
## Next steps
- Swap placeholder digest values with real bundle hash after MIRROR-CRT-56-001 emits artefact.
- Attach this note to Concelier/Excititor attestation tasks (CONCELIER-ATTEST-73-001/002, EXCITITOR-ATTEST-73-001/002).

View File

@@ -0,0 +1,57 @@
# Evidence Bundle v1 Contract (2025-11-19)
## Scope
Frozen contract for Evidence Bundle v1 covering AdvisoryAI/Concelier/Excititor evidence exports used by air-gap and attestation flows.
## Artefact layout
- Tarball name: `evidence-bundle-<id>.tar.gz`
- Manifest (required): `manifest.json`
- Payloads (required): `observations.ndjson`, `linksets.ndjson`
- Optional: `timeline.ndjson` (time anchors), `transparency.json` (Rekor UUID or skip_reason)
- Hash list: `hashes.sha256` (sha256 of each file)
### manifest.json fields
```json
{
"bundle_id": "evidence-bundle-m0",
"version": "1.0.0",
"created": "2025-11-19T00:00:00Z",
"tenant": "demo",
"scope": "vex",
"inputs": ["sha256:payload-obs", "sha256:payload-linksets"],
"aoc": {"guardrails": true, "details": ["schema:frozen:1.0", "limits:chunk:max=2000"]}
}
```
### observations.ndjson (sample record)
```json
{"observationId":"obs-ossl-001","advisoryId":"CVE-2024-1234","component":"pkg:deb/openssl@1.1.1w","source":"nvd","fetchedAt":"2025-11-18T12:00:00Z"}
```
### linksets.ndjson (sample record)
```json
{"linksetId":"lnm-ossl-001","advisoryId":"CVE-2024-1234","components":["pkg:deb/openssl@1.1.1w"],"normalized":true,"createdAt":"2025-11-18T12:05:00Z"}
```
### transparency.json (optional)
```json
{"rekor_uuid": null, "skip_reason": "offline"}
```
## Determinism rules
- All timestamps must be UTC RFC3339.
- Ordering: sort NDJSON by `advisoryId`, then `component`, ascending.
- Hashes: compute sha256 on raw file bytes; record in `hashes.sha256` and in manifest `inputs`.
- Tenant must be lowercase; include in manifest and any attestation subject claims.
## Example bundle (sample)
- Path: `docs/samples/evidence-bundle/evidence-bundle-m0.tar.gz`
- SHA256: `$(cat docs/samples/evidence-bundle/evidence-bundle-m0.tar.gz.sha256 | awk '{print $1}')`
- Contains sample manifest/observations/linksets/transparency per above.
## Attestation linkage
- See `attestation-scope-note.md` for required claims.
- Subject digest should reference the tarball sha256; include `bundle_id` and `tenant`.
## Change log
- 2025-11-19: v1 frozen (initial publication). Add real sample tarball + hashes once produced.

View File

@@ -85,5 +85,8 @@ This note defines the deterministic, aggregation-only contract that Excititor ex
- When mirror bundles are configured, `provenance.canonicalUri` points to the local bundle path; otherwise it is omitted.
- All payloads are side-effect free; no remote fetches occur while streaming.
## Samples
- NDJSON sample: `docs/samples/excititor/chunks-sample.ndjson` (hashes in `.sha256`) aligned to the schema above.
## Versioning
- Contract version: `v1` (this document). Changes must be additive; breaking changes require `v2` path and updated doc.

View File

@@ -0,0 +1,104 @@
# Excititor VEX linkset APIs (observations + linksets)
> Draft examples for Sprint 119 (EXCITITOR-LNM-21-203). Aligns with WebService endpoints implemented in `src/Excititor/StellaOps.Excititor.WebService/Program.cs`.
## /v1/vex/observations
### List
```
GET /v1/vex/observations?vulnerabilityId=CVE-2024-0001&productKey=pkg:maven/org.demo/app@1.2.3&providerId=ubuntu-csaf&status=affected&limit=2
Headers:
Authorization: Bearer <token>
X-Tenant: default
Response 200 (application/json):
{
"items": [
{
"tenant": "default",
"observationId": "vex:obs:sha256:...",
"providerId": "ubuntu-csaf",
"document": {
"digest": "sha256:...",
"uri": "https://example.com/csaf/1.json",
"signature": null
},
"scope": {
"vulnerabilityId": "CVE-2024-0001",
"productKey": "pkg:maven/org.demo/app@1.2.3"
},
"statements": [
{
"vulnerabilityId": "CVE-2024-0001",
"productKey": "pkg:maven/org.demo/app@1.2.3",
"status": "affected",
"justification": {
"type": "component_not_present",
"reason": "Not shipped in base profile"
},
"signals": { "severity": { "score": 7.5 } },
"provenance": {
"providerId": "ubuntu-csaf",
"sourceId": "USN-9999-1",
"fieldMasks": ["statements"]
}
}
],
"linkset": {
"aliases": ["USN-9999-1"],
"purls": ["pkg:maven/org.demo/app"],
"cpes": [],
"references": [{"type": "advisory", "url": "https://..."}],
"disagreements": []
},
"createdAt": "2025-11-18T12:34:56Z"
}
],
"nextCursor": "eyJ2dWxuZXJhYmlsaXR5SWQiOiJDVkUtMjAyNC0wMDAxIiwiY3JlYXRlZEF0IjoiMjAyNS0xMS0xOFQxMjozNDo1NloifQ=="
}
```
### Get by key
```
GET /v1/vex/observations/CVE-2024-0001/pkg:maven/org.demo/app@1.2.3
Headers: Authorization + X-Tenant
Response 200: same projection shape as list items (single object).
```
## /v1/vex/linksets
```
GET /v1/vex/linksets?vulnerabilityId=CVE-2024-0001&productKey=pkg:maven/org.demo/app@1.2.3&status=affected&limit=2
Headers: Authorization + X-Tenant
Response 200:
{
"items": [
{
"linksetId": "CVE-2024-0001:pkg:maven/org.demo/app@1.2.3",
"tenant": "default",
"vulnerabilityId": "CVE-2024-0001",
"productKey": "pkg:maven/org.demo/app@1.2.3",
"providers": ["ubuntu-csaf", "suse-csaf"],
"statuses": ["affected", "fixed"],
"aliases": ["USN-9999-1"],
"purls": ["pkg:maven/org.demo/app"],
"cpes": [],
"references": [{"type": "advisory", "url": "https://..."}],
"disagreements": [{"providerId": "suse-csaf", "status": "fixed", "justification": null, "confidence": null}],
"observations": [
{"observationId": "vex:obs:...", "providerId": "ubuntu-csaf", "status": "affected", "severity": 7.5},
{"observationId": "vex:obs:...", "providerId": "suse-csaf", "status": "fixed", "severity": null}
],
"createdAt": "2025-11-18T12:34:56Z"
}
],
"nextCursor": null
}
```
## Notes
- Pagination: `limit` (default 200, max 500) + `cursor` (opaque base64 of `vulnerabilityId` + `createdAt`).
- Filters: `vulnerabilityId`, `productKey`, `providerId`, `status`; multiple query values allowed.
- Headers: `Excititor-Results-Count`, `Excititor-Results-Cursor` (observations) and `Excititor-Results-Total` / `Excititor-Results-Truncated` (chunks) already implemented.
- Determinism: responses sorted by `vulnerabilityId`, then `productKey`; arrays sorted lexicographically.
## SDK generation
- Use this file plus `vex_observations.md` as the source of truth for SDK examples in EXCITITOR-LNM-21-203.

View File

@@ -0,0 +1,30 @@
# Mirror Thin Bundle · Milestone 0 (sample)
## Scope
- Provide a deterministic placeholder thin bundle so downstream air-gap/console/attestation tracks can wire references while MIRROR-CRT-56-001 code lands.
- Sample bundle published at `out/mirror/thin/mirror-thin-m0-sample.tar.gz` (created 2025-11-19 UTC) with fixed metadata only; no advisories/policies/images included.
## Owners
- Primary: Alex Kim
- Backup: Priya Desai
## Artefacts
- Bundle: `out/mirror/thin/mirror-thin-m0-sample.tar.gz`
- SHA256: `bd1013885a27f651e28331c7a240d417d265bd411d09b51b47bd7c2196659674`
- Manifest inside bundle: `sample-m0/manifest.json`
- Notes: `sample-m0/README.txt`
## Layout (within tar.gz)
```
sample-m0/
manifest.json # version, bundle_id, created, notes
README.txt # purpose, determinism and replacement guidance
```
## Refresh cadence
- Replace this sample with real thin bundle once MIRROR-CRT-56-001 assembler emits manifests (target: 2025-11-20).
- Maintain same path prefix `out/mirror/thin/` and update hash in this file and sprint log when refreshed.
## Usage
- Downstream tasks may reference this path/hash to unblock contract wiring and CI harnesses.
- Do not ship to customers; for internal wiring/tests only.

View File

@@ -0,0 +1,34 @@
# MIRROR-CRT-56-001 · Thin bundle assembler handoff (v0.1)
Purpose: unblock MIRROR-CRT-56-001 by defining expected assembler outputs so the real thin bundle can replace the milestone-0 sample.
## Expected outputs
- Artifact: `out/mirror/thin/mirror-thin-v1.tar.gz`
- Manifest: `out/mirror/thin/mirror-thin-v1.manifest.json` containing:
- `version`: "1.0.0"
- `created`: UTC ISO-8601
- `layers`: array of `{ digest, size, path }`
- `indexes`: array of `{ name, digest }` for evidence/linkset indexes
- `hashes`: `{ tarball_sha256, manifest_sha256 }`
- Checksums: `.sha256` files for tarball and manifest stored alongside artifacts.
## Assembly steps (reference for assembler owners)
1) Produce layer tar parts deterministically (sorted entries, zeroed mtimes/uid/gid, pax headers disabled).
2) Compose `mirror-thin-v1.tar.gz` using stable order: `manifest.json`, `layers/*`, `indexes/*`.
3) Generate manifest JSON and compute SHA256 for both tarball and manifest; write `.sha256` files.
4) Sign manifest (DSSE optional) and place signature next to manifest if available.
## Determinism requirements
- POSIX tar with numeric owner 0:0, mtime 0, sorted paths.
- Gzip with `--no-name` and fixed timestamp 0.
- No duplicate files; symlinks forbidden.
## Evidence
- When produced, place artefacts under `out/mirror/thin/` and add hashes to this doc.
## Owners
- Mirror Creator Guild (assembler)
- AirGap Guild (consumer)
## Status
- Handoff doc published 2025-11-19; awaiting assembler output to replace milestone-0 sample.