feat: Add VEX compact fixture and implement offline verifier for Findings Ledger exports

- Introduced a new VEX compact fixture for testing purposes.
- Implemented `verify_export.py` script to validate Findings Ledger exports, ensuring deterministic ordering and applying redaction manifests.
- Added a lightweight stub `HarnessRunner` for unit tests to validate ledger hashing expectations.
- Documented tasks related to the Mirror Creator.
- Created models for entropy signals and implemented the `EntropyPenaltyCalculator` to compute penalties based on scanner outputs.
- Developed unit tests for `EntropyPenaltyCalculator` to ensure correct penalty calculations and handling of edge cases.
- Added tests for symbol ID normalization in the reachability scanner.
- Enhanced console status service with comprehensive unit tests for connection handling and error recovery.
- Included Cosign tool version 2.6.0 with checksums for various platforms.
This commit is contained in:
StellaOps Bot
2025-12-02 21:08:01 +02:00
parent 6d049905c7
commit 47168fec38
146 changed files with 4329 additions and 549 deletions

View File

@@ -38,11 +38,13 @@
| 11 | CONCELIER-STORE-AOC-19-005-DEV | BLOCKED (2025-11-04) | Waiting on staging dataset hash + rollback rehearsal using prep doc | Concelier Storage Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Execute raw-linkset backfill/rollback plan so Mongo reflects Link-Not-Merge data; rehearse rollback (dev/staging). | | 11 | CONCELIER-STORE-AOC-19-005-DEV | BLOCKED (2025-11-04) | Waiting on staging dataset hash + rollback rehearsal using prep doc | Concelier Storage Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo`) | Execute raw-linkset backfill/rollback plan so Mongo reflects Link-Not-Merge data; rehearse rollback (dev/staging). |
| 12 | CONCELIER-TEN-48-001 | DONE (2025-11-28) | Created Tenancy module with `TenantScope`, `TenantCapabilities`, `TenantCapabilitiesResponse`, `ITenantCapabilitiesProvider`, and `TenantScopeNormalizer` per AUTH-TEN-47-001. | Concelier Core Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Enforce tenant scoping through normalization/linking; expose capability endpoint advertising `merge=false`; ensure events include tenant IDs. | | 12 | CONCELIER-TEN-48-001 | DONE (2025-11-28) | Created Tenancy module with `TenantScope`, `TenantCapabilities`, `TenantCapabilitiesResponse`, `ITenantCapabilitiesProvider`, and `TenantScopeNormalizer` per AUTH-TEN-47-001. | Concelier Core Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`) | Enforce tenant scoping through normalization/linking; expose capability endpoint advertising `merge=false`; ensure events include tenant IDs. |
| 13 | CONCELIER-VEXLENS-30-001 | BLOCKED | PREP-CONCELIER-VULN-29-001; VEXLENS-30-005 | Concelier WebService Guild · VEX Lens Guild (`src/Concelier/StellaOps.Concelier.WebService`) | Guarantee advisory key consistency and cross-links consumed by VEX Lens so consensus explanations cite Concelier evidence without merges. | | 13 | CONCELIER-VEXLENS-30-001 | BLOCKED | PREP-CONCELIER-VULN-29-001; VEXLENS-30-005 | Concelier WebService Guild · VEX Lens Guild (`src/Concelier/StellaOps.Concelier.WebService`) | Guarantee advisory key consistency and cross-links consumed by VEX Lens so consensus explanations cite Concelier evidence without merges. |
| 14 | CONCELIER-GAPS-115-014 | TODO | None; informs tasks 013. | Product Mgmt · Concelier Guild | Address Concelier ingestion gaps CI1CI10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: publish signed observation/linkset schemas and AOC guard, enforce denylist/allowlist via analyzers, require provenance/signature details, feed snapshot governance/staleness, deterministic conflict rules, canonical content-hash/idempotency keys, tenant isolation tests, connector sandbox limits, offline advisory bundle schema/verify, and shared fixtures/CI determinism. | | 14 | CONCELIER-GAPS-115-014 | DONE (2025-12-02) | None; informs tasks 013. | Product Mgmt · Concelier Guild | Address Concelier ingestion gaps CI1CI10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: publish signed observation/linkset schemas and AOC guard, enforce denylist/allowlist via analyzers, require provenance/signature details, feed snapshot governance/staleness, deterministic conflict rules, canonical content-hash/idempotency keys, tenant isolation tests, connector sandbox limits, offline advisory bundle schema/verify, and shared fixtures/CI determinism. |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-12-02 | Completed CONCELIER-GAPS-115-014: published signed LNM schemas + manifest/signature, added connector HttpClient sandbox analyzer, hardened AOC guard for canonical sha256 + signature metadata, added determinism/tenant isolation tests and offline bundle fixtures. Targeted Core tests passing. | Implementer |
| 2025-12-02 | Started CONCELIER-GAPS-115-014 remediation: schema signing, AOC provenance guard, determinism/tenant isolation tests. | Implementer |
| 2025-11-28 | Completed CONCELIER-RISK-69-001: implemented `AdvisoryFieldChangeNotification`, `AdvisoryFieldChange`, `AdvisoryFieldChangeProvenance` models + `IAdvisoryFieldChangeEmitter` interface + `AdvisoryFieldChangeEmitter` implementation + `IAdvisoryFieldChangeNotificationPublisher` interface + `InMemoryAdvisoryFieldChangeNotificationPublisher`. Detects changes in fix availability, KEV status, severity, CVSS score, and observation status with full provenance. DI registration via `AddConcelierRiskServices()`. Sprint 0115 RISK tasks now complete (66-001, 66-002, 67-001, 69-001 DONE; 68-001 BLOCKED on POLICY-RISK-68-001). | Implementer | | 2025-11-28 | Completed CONCELIER-RISK-69-001: implemented `AdvisoryFieldChangeNotification`, `AdvisoryFieldChange`, `AdvisoryFieldChangeProvenance` models + `IAdvisoryFieldChangeEmitter` interface + `AdvisoryFieldChangeEmitter` implementation + `IAdvisoryFieldChangeNotificationPublisher` interface + `InMemoryAdvisoryFieldChangeNotificationPublisher`. Detects changes in fix availability, KEV status, severity, CVSS score, and observation status with full provenance. DI registration via `AddConcelierRiskServices()`. Sprint 0115 RISK tasks now complete (66-001, 66-002, 67-001, 69-001 DONE; 68-001 BLOCKED on POLICY-RISK-68-001). | Implementer |
| 2025-12-01 | Added CONCELIER-GAPS-115-014 to capture CI1CI10 remediation from `31-Nov-2025 FINDINGS.md`. | Product Mgmt | | 2025-12-01 | Added CONCELIER-GAPS-115-014 to capture CI1CI10 remediation from `31-Nov-2025 FINDINGS.md`. | Product Mgmt |
| 2025-11-28 | Completed CONCELIER-RISK-66-002: implemented `FixAvailabilityMetadata`, `FixRelease`, `FixAdvisoryLink` models with provenance anchors + `IFixAvailabilityEmitter` interface + `FixAvailabilityEmitter` implementation for emitting structured fix-availability metadata per observation/linkset. DI registration via `AddConcelierRiskServices()`. Unblocked CONCELIER-RISK-69-001. | Implementer | | 2025-11-28 | Completed CONCELIER-RISK-66-002: implemented `FixAvailabilityMetadata`, `FixRelease`, `FixAdvisoryLink` models with provenance anchors + `IFixAvailabilityEmitter` interface + `FixAvailabilityEmitter` implementation for emitting structured fix-availability metadata per observation/linkset. DI registration via `AddConcelierRiskServices()`. Unblocked CONCELIER-RISK-69-001. | Implementer |
@@ -81,6 +83,7 @@
- Raw linkset backfill (STORE-AOC-19-005) must preserve rollback paths to protect Offline Kit deployments; release packaging tracked separately in DevOps planning. - Raw linkset backfill (STORE-AOC-19-005) must preserve rollback paths to protect Offline Kit deployments; release packaging tracked separately in DevOps planning.
- Tenant-aware linking and notification hooks depend on Authority/Signals contracts; delays could stall AOC compliance and downstream alerts. - Tenant-aware linking and notification hooks depend on Authority/Signals contracts; delays could stall AOC compliance and downstream alerts.
- Upstream contracts absent: POLICY-20-001 (sprint 0114), AUTH-TEN-47-001, SIGNALS-24-002—until delivered, POLICY/RISK/SIG/TEN tasks in this sprint stay BLOCKED. - Upstream contracts absent: POLICY-20-001 (sprint 0114), AUTH-TEN-47-001, SIGNALS-24-002—until delivered, POLICY/RISK/SIG/TEN tasks in this sprint stay BLOCKED.
- CI1CI10 remediation shipped: signed schema bundle (`docs/modules/concelier/schemas/*`) with detached signature, AOC guard now enforces canonical sha256 + signature metadata, connector analyzer `CONCELIER0004` guards unsandboxed `HttpClient`, and deterministic fixtures/tests cover idempotency/tenant isolation/offline bundle staleness.
## Next Checkpoints ## Next Checkpoints
- Plan backfill rehearsal window for STORE-AOC-19-005 once AUTH/AOC prerequisites clear (date TBD). - Plan backfill rehearsal window for STORE-AOC-19-005 once AUTH/AOC prerequisites clear (date TBD).

View File

@@ -43,11 +43,13 @@
| 6 | LEDGER-OBS-54-001 | DONE (2025-11-22) | `/v1/ledger/attestations` endpoint implemented with deterministic paging + filters hash; schema/OAS updated | Findings Ledger Guild; Provenance Guild / src/Findings/StellaOps.Findings.Ledger | Verify attestation references for ledger-derived exports; expose `/ledger/attestations` endpoint returning DSSE verification state and chain-of-custody summary | | 6 | LEDGER-OBS-54-001 | DONE (2025-11-22) | `/v1/ledger/attestations` endpoint implemented with deterministic paging + filters hash; schema/OAS updated | Findings Ledger Guild; Provenance Guild / src/Findings/StellaOps.Findings.Ledger | Verify attestation references for ledger-derived exports; expose `/ledger/attestations` endpoint returning DSSE verification state and chain-of-custody summary |
| 7 | LEDGER-RISK-66-001 | DONE (2025-11-21) | PREP-LEDGER-RISK-66-001-RISK-ENGINE-SCHEMA-CO | Findings Ledger Guild; Risk Engine Guild / src/Findings/StellaOps.Findings.Ledger | Add schema migrations for `risk_score`, `risk_severity`, `profile_version`, `explanation_id`, and supporting indexes | | 7 | LEDGER-RISK-66-001 | DONE (2025-11-21) | PREP-LEDGER-RISK-66-001-RISK-ENGINE-SCHEMA-CO | Findings Ledger Guild; Risk Engine Guild / src/Findings/StellaOps.Findings.Ledger | Add schema migrations for `risk_score`, `risk_severity`, `profile_version`, `explanation_id`, and supporting indexes |
| 8 | LEDGER-RISK-66-002 | DONE (2025-11-21) | PREP-LEDGER-RISK-66-002-DEPENDS-ON-66-001-MIG | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Implement deterministic upsert of scoring results keyed by finding hash/profile version with history audit | | 8 | LEDGER-RISK-66-002 | DONE (2025-11-21) | PREP-LEDGER-RISK-66-002-DEPENDS-ON-66-001-MIG | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Implement deterministic upsert of scoring results keyed by finding hash/profile version with history audit |
| 9 | LEDGER-GAPS-121-009 | TODO | Close FL1FL10 gaps from `docs/product-advisories/28-Nov-2025 - Findings Ledger and Immutable Audit Trail.md`; align schemas/exports with advisory; depends on schema catalog refresh | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Remediate FL1FL10: publish versioned schemas/canonical JSON (events/projections/exports), Merkle + external anchor policy doc, tenant isolation + redaction manifest, DSSE/policy hash linkage, deterministic exports + golden fixtures, offline verifier script, replay/rebuild checksum guard, and quotas/backpressure metrics; update docs under `docs/modules/findings-ledger/`. | | 9 | LEDGER-GAPS-121-009 | DONE (2025-12-02) | Close FL1FL10 gaps from `docs/product-advisories/28-Nov-2025 - Findings Ledger and Immutable Audit Trail.md`; align schemas/exports with advisory; depends on schema catalog refresh | Findings Ledger Guild / src/Findings/StellaOps.Findings.Ledger | Remediate FL1FL10: publish versioned schemas/canonical JSON (events/projections/exports), Merkle + external anchor policy doc, tenant isolation + redaction manifest, DSSE/policy hash linkage, deterministic exports + golden fixtures, offline verifier script, replay/rebuild checksum guard, and quotas/backpressure metrics; update docs under `docs/modules/findings-ledger/`. |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-12-02 | Completed LEDGER-GAPS-121-009: added schema catalog + FL1FL10 gap report, Merkle/anchor policy, redaction manifest, DSSE linkage doc, golden export fixtures + checksums, offline verifier script with replay checksum guard, quota/backpressure metrics/code/tests. | Findings Ledger |
| 2025-12-02 | Started LEDGER-GAPS-121-009 (FL1FL10 remediation); status DOING; drafting schema catalog, Merkle/anchor policy, redaction manifest, offline verifier, and backpressure metrics. | Findings Ledger |
| 2025-12-01 | Added LEDGER-GAPS-121-009 to track FL1FL10 remediation from `docs/product-advisories/28-Nov-2025 - Findings Ledger and Immutable Audit Trail.md`; status TODO pending schema catalog refresh. | Project Mgmt | | 2025-12-01 | Added LEDGER-GAPS-121-009 to track FL1FL10 remediation from `docs/product-advisories/28-Nov-2025 - Findings Ledger and Immutable Audit Trail.md`; status TODO pending schema catalog refresh. | Project Mgmt |
| 2025-12-02 | Clarified LEDGER-GAPS-121-009 outputs: schema catalog, Merkle/anchor policy, tenant isolation/redaction manifest, DSSE/policy linkage, deterministic exports + golden fixtures, offline verifier, replay checksums, and quotas/backpressure metrics. | Project Mgmt | | 2025-12-02 | Clarified LEDGER-GAPS-121-009 outputs: schema catalog, Merkle/anchor policy, tenant isolation/redaction manifest, DSSE/policy linkage, deterministic exports + golden fixtures, offline verifier, replay checksums, and quotas/backpressure metrics. | Project Mgmt |
| 2025-11-25 | Moved all remaining BLOCKED tasks (OAS, ATTEST, OBS-55, PACKS) to new sprint `SPRINT_0121_0001_0002_policy_reasoning_blockers`; cleansed Delivery Tracker to active/completed items only. | Project Mgmt | | 2025-11-25 | Moved all remaining BLOCKED tasks (OAS, ATTEST, OBS-55, PACKS) to new sprint `SPRINT_0121_0001_0002_policy_reasoning_blockers`; cleansed Delivery Tracker to active/completed items only. | Project Mgmt |
@@ -83,6 +85,7 @@
- Current state: findings export endpoint and paging contracts implemented; VEX/advisory/SBOM endpoints stubbed (auth + shape) but await underlying projection/query schemas. Risk schema/implementation (LEDGER-RISK-66-001/002) delivered. Remaining blockers: OAS/SDK surface (61/62/63), attestation HTTP host (OBS-54/55), and packs time-travel contract (PACKS-42-001). - Current state: findings export endpoint and paging contracts implemented; VEX/advisory/SBOM endpoints stubbed (auth + shape) but await underlying projection/query schemas. Risk schema/implementation (LEDGER-RISK-66-001/002) delivered. Remaining blockers: OAS/SDK surface (61/62/63), attestation HTTP host (OBS-54/55), and packs time-travel contract (PACKS-42-001).
- Export endpoints now enforce filter hash + page token determinism for VEX/advisory/SBOMs but still return empty sets until backing projections land; downstream SDK/OAS tasks should treat payload shapes as stable. - Export endpoints now enforce filter hash + page token determinism for VEX/advisory/SBOMs but still return empty sets until backing projections land; downstream SDK/OAS tasks should treat payload shapes as stable.
- New advisory gaps (FL1FL10) tracked via LEDGER-GAPS-121-009; requires schema catalog refresh and alignment of Merkle/anchoring, redaction, DSSE linkage, and offline verify tooling with `docs/product-advisories/28-Nov-2025 - Findings Ledger and Immutable Audit Trail.md` recommendations. - New advisory gaps (FL1FL10) tracked via LEDGER-GAPS-121-009; requires schema catalog refresh and alignment of Merkle/anchoring, redaction, DSSE linkage, and offline verify tooling with `docs/product-advisories/28-Nov-2025 - Findings Ledger and Immutable Audit Trail.md` recommendations.
- FL1FL10 remediation shipped: schema catalog + gap report, Merkle/anchor policy, redaction manifest (JSON/YAML), DSSE linkage guidance, golden export fixtures/checksums, offline verify script with replay checksum guard, and quota/backpressure metrics/tests wired into ledger service.
## Next Checkpoints ## Next Checkpoints
- Schedule cross-guild kickoff for week of 2025-11-24 once dependency clears. - Schedule cross-guild kickoff for week of 2025-11-24 once dependency clears.

View File

@@ -15,11 +15,19 @@
- `docs/modules/platform/architecture-overview.md` - `docs/modules/platform/architecture-overview.md`
- `docs/modules/policy/architecture.md` - `docs/modules/policy/architecture.md`
## Interlocks
- POLICY-CONSOLE-23-001 (Console export/simulation contract from BE-Base Platform) must be published before POLICY-CONSOLE-23-002 can start.
## Action Tracker
| # | Action | Owner | Due | Status |
| --- | --- | --- | --- | --- |
| 1 | Publish Console export/simulation contract for POLICY-CONSOLE-23-001 to unblock POLICY-CONSOLE-23-002 | BE-Base Platform Guild | — | BLOCKED (awaiting spec) |
## Delivery Tracker ## Delivery Tracker
| # | Task ID & handle | State | Key dependency / next step | Owners | | # | Task ID & handle | State | Key dependency / next step | Owners |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| P1 | PREP-POLICY-ENGINE-20-002-DETERMINISTIC-EVALU | DONE (2025-11-22) | Due 2025-11-22 · Accountable: Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Deterministic evaluator spec missing. <br><br> Document artefact/deliverable for POLICY-ENGINE-20-002 and publish location so downstream tasks can proceed. Prep artefact: `docs/modules/policy/design/policy-deterministic-evaluator.md`. | | P1 | PREP-POLICY-ENGINE-20-002-DETERMINISTIC-EVALU | DONE (2025-11-22) | Due 2025-11-22 · Accountable: Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | Deterministic evaluator spec missing. <br><br> Document artefact/deliverable for POLICY-ENGINE-20-002 and publish location so downstream tasks can proceed. Prep artefact: `docs/modules/policy/design/policy-deterministic-evaluator.md`. |
| 1 | POLICY-CONSOLE-23-002 | BLOCKED (2025-11-27) | Waiting on POLICY-CONSOLE-23-001 export/simulation contract. | Policy Guild, Product Ops / `src/Policy/StellaOps.Policy.Engine` | | 1 | POLICY-CONSOLE-23-002 | BLOCKED (2025-12-02) | POLICY-CONSOLE-23-001 export/simulation contract still not published; waiting on Console API spec from BE-Base Platform. | Policy Guild, Product Ops / `src/Policy/StellaOps.Policy.Engine` |
| 2 | POLICY-ENGINE-20-002 | DONE (2025-11-27) | PREP-POLICY-ENGINE-20-002-DETERMINISTIC-EVALU | Policy Guild / `src/Policy/StellaOps.Policy.Engine` | | 2 | POLICY-ENGINE-20-002 | DONE (2025-11-27) | PREP-POLICY-ENGINE-20-002-DETERMINISTIC-EVALU | Policy Guild / `src/Policy/StellaOps.Policy.Engine` |
| 3 | POLICY-ENGINE-20-003 | DONE (2025-11-27) | Depends on 20-002. | Policy · Concelier · Excititor Guilds / `src/Policy/StellaOps.Policy.Engine` | | 3 | POLICY-ENGINE-20-003 | DONE (2025-11-27) | Depends on 20-002. | Policy · Concelier · Excititor Guilds / `src/Policy/StellaOps.Policy.Engine` |
| 4 | POLICY-ENGINE-20-004 | DONE (2025-11-27) | Depends on 20-003. | Policy · Platform Storage Guild / `src/Policy/StellaOps.Policy.Engine` | | 4 | POLICY-ENGINE-20-004 | DONE (2025-11-27) | Depends on 20-003. | Policy · Platform Storage Guild / `src/Policy/StellaOps.Policy.Engine` |
@@ -36,6 +44,7 @@
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-12-02 | Rechecked for POLICY-CONSOLE-23-001 contract; none found. Left POLICY-CONSOLE-23-002 BLOCKED (2025-12-02). Added Interlocks and Action Tracker sections to align with sprint template. | Project Mgmt |
| 2025-12-01 | Refactored Mongo exception listing to shared filter/sort helpers (per-tenant and cross-tenant) for lifecycle scans; reran `dotnet test src/Policy/__Tests/StellaOps.Policy.Engine.Tests -c Release --no-build` (208/208 pass). | Implementer | | 2025-12-01 | Refactored Mongo exception listing to shared filter/sort helpers (per-tenant and cross-tenant) for lifecycle scans; reran `dotnet test src/Policy/__Tests/StellaOps.Policy.Engine.Tests -c Release --no-build` (208/208 pass). | Implementer |
| 2025-12-01 | Completed deterministic evidence summary (big-endian hash → `2025-12-13T05:00:11Z`) and exception lifecycle fixes (multi-tenant activation/expiry, no default tenant); added cross-tenant list overload. `dotnet test src/Policy/__Tests/StellaOps.Policy.Engine.Tests -c Release --no-build` now passes (208 tests, 0 failures). | Implementer | | 2025-12-01 | Completed deterministic evidence summary (big-endian hash → `2025-12-13T05:00:11Z`) and exception lifecycle fixes (multi-tenant activation/expiry, no default tenant); added cross-tenant list overload. `dotnet test src/Policy/__Tests/StellaOps.Policy.Engine.Tests -c Release --no-build` now passes (208 tests, 0 failures). | Implementer |
| 2025-12-01 | Ran `dotnet build src/Policy/StellaOps.Policy.Engine/StellaOps.Policy.Engine.csproj -c Release` successfully (1 warning NU1510). Attempted `dotnet test ...Policy.Engine.Tests` but cancelled mid-run due to prolonged dependency compilation; rerun still needed. | Implementer | | 2025-12-01 | Ran `dotnet build src/Policy/StellaOps.Policy.Engine/StellaOps.Policy.Engine.csproj -c Release` successfully (1 warning NU1510). Attempted `dotnet test ...Policy.Engine.Tests` but cancelled mid-run due to prolonged dependency compilation; rerun still needed. | Implementer |
@@ -52,7 +61,7 @@
| 2025-11-22 | Marked all PREP tasks to DONE per directive; evidence to be verified. | Project Mgmt | | 2025-11-22 | Marked all PREP tasks to DONE per directive; evidence to be verified. | Project Mgmt |
## Decisions & Risks ## Decisions & Risks
- Console simulation/export contract (POLICY-CONSOLE-23-001) still outstanding; POLICY-CONSOLE-23-002 remains BLOCKED until published. - 2025-12-02: Console export/simulation contract (POLICY-CONSOLE-23-001) still outstanding; POLICY-CONSOLE-23-002 remains BLOCKED until BE-Base Platform publishes the spec.
- Release test suite for Policy Engine now green (2025-12-01); keep enforcing deterministic inputs (explicit evaluationTimestamp) on batch evaluation requests to avoid non-deterministic clocks. - Release test suite for Policy Engine now green (2025-12-01); keep enforcing deterministic inputs (explicit evaluationTimestamp) on batch evaluation requests to avoid non-deterministic clocks.
## Next Checkpoints ## Next Checkpoints

View File

@@ -33,9 +33,9 @@
| 8 | AIRGAP-TIME-57-001 | BLOCKED | MIRROR-CRT-56-001 sample exists; needs DSSE/TUF + time-anchor schema from AirGap Time. | AirGap Time Guild | Provide trusted time-anchor service & policy. | | 8 | AIRGAP-TIME-57-001 | BLOCKED | MIRROR-CRT-56-001 sample exists; needs DSSE/TUF + time-anchor schema from AirGap Time. | AirGap Time Guild | Provide trusted time-anchor service & policy. |
| 9 | CLI-AIRGAP-56-001 | BLOCKED | MIRROR-CRT-56-002/58-001 pending; offline kit inputs unavailable. | CLI Guild | Extend CLI offline kit tooling to consume mirror bundles. | | 9 | CLI-AIRGAP-56-001 | BLOCKED | MIRROR-CRT-56-002/58-001 pending; offline kit inputs unavailable. | CLI Guild | Extend CLI offline kit tooling to consume mirror bundles. |
| 10 | PROV-OBS-53-001 | DONE (2025-11-23) | Observer doc + verifier script `scripts/mirror/verify_thin_bundle.py` in repo; validates hashes, determinism, and manifest/index digests. | Security Guild | Define provenance observers + verification hooks. | | 10 | PROV-OBS-53-001 | DONE (2025-11-23) | Observer doc + verifier script `scripts/mirror/verify_thin_bundle.py` in repo; validates hashes, determinism, and manifest/index digests. | Security Guild | Define provenance observers + verification hooks. |
| 11 | OFFKIT-GAPS-125-011 | TODO | None; informs tasks 49. | Product Mgmt · Mirror/AirGap Guilds | Address offline-kit gaps OK1OK10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: key manifest/rotation + PQ co-sign, tool hashing/signing, DSSE-signed top-level manifest linking all artifacts, checkpoint freshness/mirror metadata, deterministic packaging flags, inclusion of scan/VEX/policy/graph hashes, time anchor bundling, transport/chunking + chain-of-custody, tenant/env scoping, and scripted verify with negative-path guidance. | | 11 | OFFKIT-GAPS-125-011 | DONE (2025-12-02) | Bundle meta + offline policy layers + verifier updated; see milestone.json and bundle DSSE. | Product Mgmt · Mirror/AirGap Guilds | Address offline-kit gaps OK1OK10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: key manifest/rotation + PQ co-sign, tool hashing/signing, DSSE-signed top-level manifest linking all artifacts, checkpoint freshness/mirror metadata, deterministic packaging flags, inclusion of scan/VEX/policy/graph hashes, time anchor bundling, transport/chunking + chain-of-custody, tenant/env scoping, and scripted verify with negative-path guidance. |
| 12 | REKOR-GAPS-125-012 | TODO | None; informs tasks 110. | Product Mgmt · Mirror/AirGap · Attestor Guilds | Address Rekor v2/DSSE gaps RK1RK10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: enforce dsse/hashedrekord only, payload size preflight + chunk manifests, public/private routing policy, shard-aware checkpoints, idempotent submission keys, Sigstore bundles in kits, checkpoint freshness bounds, PQ dual-sign options, error taxonomy/backoff, policy/graph annotations in DSSE/bundles. | | 12 | REKOR-GAPS-125-012 | DONE (2025-12-02) | Rekor policy layer + bundle meta/TUF DSSE; refer to `layers/rekor-policy.json`. | Product Mgmt · Mirror/AirGap · Attestor Guilds | Address Rekor v2/DSSE gaps RK1RK10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: enforce dsse/hashedrekord only, payload size preflight + chunk manifests, public/private routing policy, shard-aware checkpoints, idempotent submission keys, Sigstore bundles in kits, checkpoint freshness bounds, PQ dual-sign options, error taxonomy/backoff, policy/graph annotations in DSSE/bundles. |
| 13 | MIRROR-GAPS-125-013 | TODO | None; informs tasks 112. | Product Mgmt · Mirror Creator Guild · AirGap Guild | Address mirror/offline strategy gaps MS1MS10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: signed/versioned mirror schemas, DSSE/TUF rotation policy (incl. PQ), delta spec with tombstones/base hash, time-anchor freshness enforcement, tenant/env scoping, distribution integrity for HTTP/OCI/object, chunking/size rules, standard verify script, metrics/alerts for build/import/verify, and SemVer/change log for mirror formats. | | 13 | MIRROR-GAPS-125-013 | DONE (2025-12-02) | Mirror policy layer + tenant/env scope + verifier; see mirror-policy.json & bundle meta. | Product Mgmt · Mirror Creator Guild · AirGap Guild | Address mirror/offline strategy gaps MS1MS10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: signed/versioned mirror schemas, DSSE/TUF rotation policy (incl. PQ), delta spec with tombstones/base hash, time-anchor freshness enforcement, tenant/env scoping, distribution integrity for HTTP/OCI/object, chunking/size rules, standard verify script, metrics/alerts for build/import/verify, and SemVer/change log for mirror formats. |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
@@ -73,12 +73,15 @@
| 2025-12-01 | Added OFFKIT-GAPS-125-011 to track OK1OK10 remediation from `31-Nov-2025 FINDINGS.md`. | Product Mgmt | | 2025-12-01 | Added OFFKIT-GAPS-125-011 to track OK1OK10 remediation from `31-Nov-2025 FINDINGS.md`. | Product Mgmt |
| 2025-12-01 | Added REKOR-GAPS-125-012 to track RK1RK10 remediation from `31-Nov-2025 FINDINGS.md`. | Product Mgmt | | 2025-12-01 | Added REKOR-GAPS-125-012 to track RK1RK10 remediation from `31-Nov-2025 FINDINGS.md`. | Product Mgmt |
| 2025-12-01 | Added MIRROR-GAPS-125-013 to track MS1MS10 remediation from `31-Nov-2025 FINDINGS.md`. | Product Mgmt | | 2025-12-01 | Added MIRROR-GAPS-125-013 to track MS1MS10 remediation from `31-Nov-2025 FINDINGS.md`. | Product Mgmt |
| 2025-12-02 | Moved OFFKIT/REKOR/MIRROR gap tasks to DOING; created `src/Mirror/StellaOps.Mirror.Creator/TASKS.md` for local tracking and began bundle meta/policy implementation. | Implementer |
| 2025-12-02 | Completed OK/RK/MS gap remediation: added policy layers (transport/rekor/mirror/offline), bundle meta + DSSE, verifier scope/DSSE/tool-hash checks, and refreshed milestone hashes via `scripts/mirror/ci-sign.sh`. | Implementer |
## Decisions & Risks ## Decisions & Risks
- **Decisions** - **Decisions**
- Assign primary engineer for MIRROR-CRT-56-001 (due 2025-11-17 EOD). Owners: Mirror Creator Guild · Exporter Guild; Security as backup. Option A selected: thin bundle v1; acceptance: names recorded in Delivery Tracker + kickoff notes. - Assign primary engineer for MIRROR-CRT-56-001 (due 2025-11-17 EOD). Owners: Mirror Creator Guild · Exporter Guild; Security as backup. Option A selected: thin bundle v1; acceptance: names recorded in Delivery Tracker + kickoff notes.
- Confirm DSSE/TUF signing profile (due 2025-11-18). Owners: Security Guild · Attestor Guild. Needed before MIRROR-CRT-56-002 can merge. - Confirm DSSE/TUF signing profile (due 2025-11-18). Owners: Security Guild · Attestor Guild. Needed before MIRROR-CRT-56-002 can merge.
- Lock time-anchor authority scope (due 2025-11-19). Owners: AirGap Time Guild · Mirror Creator Guild. Required for MIRROR-CRT-57-002 policy enforcement. - Lock time-anchor authority scope (due 2025-11-19). Owners: AirGap Time Guild · Mirror Creator Guild. Required for MIRROR-CRT-57-002 policy enforcement.
- 2025-12-02: OK/RK/MS gap baseline adopted — bundle meta DSSE (`mirror-thin-v1.bundle.dsse.json`) and policy layers (transport, rekor, mirror, offline-kit) are now canonical evidence; verifier enforces tenant/env scope + tool hashes.
- **Risks** - **Risks**
- Production signing key lives in Ops sprint: release signing (`MIRROR_SIGN_KEY_B64` secret + CI promotion) is handled in Sprint 506 (Ops DevOps IV); this dev sprint remains green using dev key until ops wiring lands. - Production signing key lives in Ops sprint: release signing (`MIRROR_SIGN_KEY_B64` secret + CI promotion) is handled in Sprint 506 (Ops DevOps IV); this dev sprint remains green using dev key until ops wiring lands.
- Time-anchor requirements undefined → air-gapped bundles lose verifiable time guarantees. Mitigation: run focused session with AirGap Time Guild to lock policy + service interface. - Time-anchor requirements undefined → air-gapped bundles lose verifiable time guarantees. Mitigation: run focused session with AirGap Time Guild to lock policy + service interface.

View File

@@ -32,7 +32,7 @@
| 13 | POLICY-ENGINE-70-004 | DONE (2025-12-01) | Depends on 70-003. | Policy · Observability Guild / `src/Policy/StellaOps.Policy.Engine` | Exception metrics/tracing/logging. | | 13 | POLICY-ENGINE-70-004 | DONE (2025-12-01) | Depends on 70-003. | Policy · Observability Guild / `src/Policy/StellaOps.Policy.Engine` | Exception metrics/tracing/logging. |
| 14 | POLICY-ENGINE-70-005 | DONE (2025-12-01) | Depends on 70-004. | Policy · Scheduler Worker Guild / `src/Policy/StellaOps.Policy.Engine` | Exception activation/expiry + events. | | 14 | POLICY-ENGINE-70-005 | DONE (2025-12-01) | Depends on 70-004. | Policy · Scheduler Worker Guild / `src/Policy/StellaOps.Policy.Engine` | Exception activation/expiry + events. |
| 15 | POLICY-ENGINE-80-001 | DONE (2025-12-01) | Depends on 70-005. | Policy · Signals Guild / `src/Policy/StellaOps.Policy.Engine` | Reachability/exploitability inputs into evaluation. | | 15 | POLICY-ENGINE-80-001 | DONE (2025-12-01) | Depends on 70-005. | Policy · Signals Guild / `src/Policy/StellaOps.Policy.Engine` | Reachability/exploitability inputs into evaluation. |
| 16 | POLICY-RISK-90-001 | BLOCKED (2025-12-01) | Waiting on Scanner entropy/trust algebra contract. | Policy · Scanner Guild / `src/Policy/StellaOps.Policy.Engine` | Entropy penalty ingestion + trust algebra. | | 16 | POLICY-RISK-90-001 | DONE (2025-12-02) | Entropy ingestion implemented; monitor scanner payloads + thresholds. | Policy · Scanner Guild / `src/Policy/StellaOps.Policy.Engine` | Entropy penalty ingestion + trust algebra. |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
@@ -61,15 +61,16 @@
| 2025-12-01 | POLICY-ENGINE-80-001 marked BLOCKED: reachability/exploitability input contract from Signals guild not yet published; no schema to integrate. | Implementer | | 2025-12-01 | POLICY-ENGINE-80-001 marked BLOCKED: reachability/exploitability input contract from Signals guild not yet published; no schema to integrate. | Implementer |
| 2025-12-01 | POLICY-RISK-90-001 marked BLOCKED: Scanner entropy/trust algebra contract still pending; ingestion shape unknown. | Implementer | | 2025-12-01 | POLICY-RISK-90-001 marked BLOCKED: Scanner entropy/trust algebra contract still pending; ingestion shape unknown. | Implementer |
| 2025-12-01 | POLICY-ENGINE-80-001 delivered: runtime evaluation now auto-enriches reachability from facts store with overlay cache; batch lookups dedupe per tenant; cache keys include reachability metadata; added reachability-driven rule test. Targeted policy-engine test slice attempted; build fanned out and was aborted—rerun on clean policy-only graph recommended. | Implementer | | 2025-12-01 | POLICY-ENGINE-80-001 delivered: runtime evaluation now auto-enriches reachability from facts store with overlay cache; batch lookups dedupe per tenant; cache keys include reachability metadata; added reachability-driven rule test. Targeted policy-engine test slice attempted; build fanned out and was aborted—rerun on clean policy-only graph recommended. | Implementer |
| 2025-12-02 | POLICY-RISK-90-001 delivered: added entropy penalty calculator consuming `layer_summary.json`/`entropy.report.json`, configurable caps/thresholds under `PolicyEngine:Entropy`, telemetry (`policy_entropy_penalty_value`, `policy_entropy_image_opaque_ratio`), and unit tests (`EntropyPenaltyCalculatorTests`). Unblocked Scanner dependency based on documented schema. | Implementer |
## Decisions & Risks ## Decisions & Risks
- Remaining TODO: POLICY-RISK-90-001 (entropy/trust algebra ingestion) still depends on Scanner contract. - Entropy penalties now computed inside Policy Engine (`PolicyEngine:Entropy` options; default K=0.5, cap=0.3, block at image opaque ratio >0.15 when provenance is unknown). Telemetry exported as `policy_entropy_penalty_value` and `policy_entropy_image_opaque_ratio`; explanations surface top opaque files.
- Reachability auto-enrichment landed (POLICY-ENGINE-80-001); exploitability signal format still absent—wire once Signals publishes contract. - Reachability auto-enrichment landed (POLICY-ENGINE-80-001); exploitability signal format still absent—wire once Signals publishes contract.
- Exception lifecycle now auto-activates/auto-expires; configure `ExceptionLifecycle` intervals per deployment and provide Redis if using distributed cache (in-memory defaults remain for offline use). - Exception lifecycle now auto-activates/auto-expires; configure `ExceptionLifecycle` intervals per deployment and provide Redis if using distributed cache (in-memory defaults remain for offline use).
- In-memory exception repository is registered by default for offline runs; swap to Mongo repository in production to persist lifecycle and review history. - In-memory exception repository is registered by default for offline runs; swap to Mongo repository in production to persist lifecycle and review history.
- Telemetry for exception applications added; dashboards should consume `policy_exception_applications_total`, `policy_exception_application_latency_seconds`, and `policy_exception_lifecycle_total`. - Telemetry for exception applications added; dashboards should consume `policy_exception_applications_total`, `policy_exception_application_latency_seconds`, and `policy_exception_lifecycle_total`.
- Graph-disabled test slices remain recommended (`DOTNET_DISABLE_BUILTIN_GRAPH=1`) to avoid static graph fan-out during focused test runs. - Graph-disabled test slices remain recommended (`DOTNET_DISABLE_BUILTIN_GRAPH=1`) to avoid static graph fan-out during focused test runs.
## Next Checkpoints ## Next Checkpoints
- Await Signals reachability/exploitability contract, then implement POLICY-ENGINE-80-001 (evaluation inputs + metrics). - Await Signals reachability/exploitability contract, then refine POLICY-ENGINE-80-001 metrics once schema lands.
- Await Scanner entropy/trust algebra contract, then implement POLICY-RISK-90-001 (ingestion + trust weighting + telemetry). - Validate entropy penalty outputs against the next Scanner bundle drop; tune `PolicyEngine:Entropy` defaults if ratios shift.
- Mirror exception lifecycle/observability changes into `docs/modules/policy/architecture.md` and dashboards. - Mirror exception lifecycle/observability changes into `docs/modules/policy/architecture.md` and dashboards.

View File

@@ -1,13 +1,13 @@
# Sprint 0136-0001-0001 · Scanner & Surface (Phase VII) # Sprint 0136-0001-0001 · Scanner & Surface (Phase VII)
## Topic & Scope ## Topic & Scope
- Scanner & Surface phase VII: EntryTrace NDJSON export, process-tree replay, and surface/CLI integration. - Scanner & Surface phase VII: EntryTrace NDJSON/replay surfacing, deterministic SBOM composition, Surface.FS/Env/Secrets rollout, and downstream consumers (Scheduler, Zastava, Cartographer, Console) enablement.
- Sequential across 130139; start after Sprint 0135. - Sequential across 130139; start after Sprint 0135.
- **Working directory:** `src/Scanner`. - **Working directory:** `src/Scanner` (with coordinated touches in Scheduler/Zastava where noted).
## Dependencies & Concurrency ## Dependencies & Concurrency
- Upstream: Sprint 0135 (phase VI) must land first. - Upstream: Sprint 0135 (phase VI) must land first.
- Concurrency: tasks are TODO; follow order below. - Concurrency: honour dependency column; SCANNER-SURFACE/EMIT work must finish before downstream consumers pick up DSSE artifacts.
## Documentation Prerequisites ## Documentation Prerequisites
- docs/README.md - docs/README.md
@@ -19,38 +19,117 @@
## Delivery Tracker ## Delivery Tracker
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | | # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| 0 | SURFACE-FS-01 | DONE (2025-11-24) | Spec published in `docs/modules/scanner/design/surface-fs.md` v1.1 | Scanner Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS`) | Author Surface.FS cache/manifest specification and cross-module contract (manifests, CAS URIs, cache layout). | | 1 | SURFACE-FS-01 | DONE (2025-11-24) | Spec published in `docs/modules/scanner/design/surface-fs.md` v1.1 | Scanner Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS`) | Author Surface.FS cache/manifest specification and cross-module contract (manifests, CAS URIs, cache layout). |
| 1 | SURFACE-FS-02 | DONE (2025-11-24) | Core library implemented; see `src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS` | Scanner Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS`) | Ship FileSurfaceManifestStore/Reader/Writer + cache options, deterministic path builder, and DI registration per `surface-fs.md`. | | 2 | SURFACE-FS-02 | DONE (2025-11-24) | Core library implemented | Scanner Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS`) | Ship FileSurfaceManifestStore/Reader/Writer + cache options, deterministic path builder, and DI registration per `surface-fs.md`. |
| 2 | SCANNER-ENTRYTRACE-18-504 | DONE | Upstream 18-503 delivered; NDJSON emission implemented in worker and surfaced via manifest/CLI/WebService. | EntryTrace Guild (`src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace`) | Emit EntryTrace AOC NDJSON (`entrytrace.entry/node/edge/target/warning/capability`) and wire CLI/service streaming outputs. | | 3 | SCANNER-ENTRYTRACE-18-504 | DONE | Depends on 18-503 | EntryTrace Guild (`src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace`) | Emit EntryTrace AOC NDJSON (`entrytrace.entry/node/edge/target/warning/capability`) and wire CLI/service streaming outputs. |
| 3 | SCANNER-ENTRYTRACE-18-505 | DONE | Replay implemented; uses `/proc` snapshots to adjust confidence, collapse wrappers, and emit match/mismatch diagnostics with runtime chains. | EntryTrace Guild | Implement ProcGraph replay to reconcile `/proc` exec chains with static EntryTrace, collapsing wrappers and emitting agreement/conflict diagnostics. | | 4 | SCANNER-ENTRYTRACE-18-505 | DONE | SCANNER-ENTRYTRACE-18-504 | EntryTrace Guild | Implement ProcGraph replay to reconcile `/proc` exec chains with static EntryTrace, collapsing wrappers and emitting diagnostics. |
| 4 | SCANNER-ENTRYTRACE-18-506 | DONE (2025-12-01) | Surfaced via WebService `/scans/{id}/entrytrace` and CLI rendering. | EntryTrace Guild · Scanner WebService Guild | Surface EntryTrace graph + confidence via Scanner.WebService and CLI, including target summary in scan reports and policy payloads. | | 5 | SCANNER-ENTRYTRACE-18-506 | DONE (2025-12-01) | SCANNER-ENTRYTRACE-18-505 | EntryTrace Guild · Scanner WebService Guild | Surface EntryTrace graph + confidence via Scanner.WebService and CLI, including target summary in scan reports and policy payloads. |
| 5 | ZASTAVA-SURFACE-02 | DONE (2025-12-01) | Manifest CAS/sha resolver in Observer drift evidence with failure metrics. | Zastava Observer Guild (`src/Zastava/StellaOps.Zastava.Observer`) | SURFACE-FS-02, ZASTAVA-SURFACE-01; see `docs/modules/scanner/design/surface-fs-consumers.md` §4 | | 6 | SCANNER-ENV-01 | DONE (2025-11-18) | — | Scanner Worker Guild (`src/Scanner/StellaOps.Scanner.Worker`) | Wire worker to `AddSurfaceEnvironment`/`ISurfaceEnvironment` for cache roots + CAS endpoints; remove ad-hoc env reads. |
| 6 | SCANNER-SORT-02 | DONE (2025-12-01) | Layer fragment ordering by digest implemented; deterministic regression test added. | Scanner Core Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Core`) | SCANNER-EMIT-15-001 | | 7 | SCANNER-ENV-02 | DONE (2025-11-27) | SCANNER-ENV-01 | Scanner WebService Guild, Ops Guild (`src/Scanner/StellaOps.Scanner.WebService`) | Wire Surface.Env helpers into WebService hosting (cache roots, feature flags) and document configuration. |
| 7 | SCANNER-EMIT-15-001 | DOING (2025-12-01) | CycloneDX artifacts now carry content hash + merkle root and recipe placeholders; DSSE/recipe persistence pending. | Scanner Emit Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Emit`) | SCANNER-SURFACE-04 | | 8 | SCANNER-ENV-03 | DONE (2025-11-27) | SCANNER-ENV-02 | BuildX Plugin Guild (`src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin`) | Pack Surface.Env, mirror to offline (`offline/packages/nugets`), and wire BuildX to use 0.1.0-alpha.20251123 with updated restore feeds. |
| 8 | SCANNER-SURFACE-01 | BLOCKED (2025-11-25) | Task definition absent; needs scope/contract before implementation. | Scanner Guild | — | | 9 | SURFACE-ENV-01 | DONE (2025-11-13) | — | Scanner Guild, Zastava Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env`) | Draft `surface-env.md` enumerating environment variables, defaults, and air-gap behaviour for Surface consumers. |
| 10 | SURFACE-ENV-02 | DONE (2025-11-18) | SURFACE-ENV-01 | Scanner Guild | Implement strongly-typed env accessors with validation for required endpoint, bounds, TLS cert path; add regression tests. |
| 11 | SURFACE-ENV-03 | DONE (2025-11-27) | SURFACE-ENV-02 | Scanner Guild | Adopt env helper across Scanner Worker/WebService/BuildX plug-ins. |
| 12 | SURFACE-ENV-04 | DONE (2025-11-27) | SURFACE-ENV-02 | Zastava Guild | Wire env helper into Zastava Observer/Webhook containers. |
| 13 | SURFACE-ENV-05 | DONE | SURFACE-ENV-03, SURFACE-ENV-04 | Ops Guild | Update Helm/Compose/offline kit templates with new env knobs and documentation. |
| 14 | SCANNER-EVENTS-16-301 | BLOCKED (2025-10-26) | Orchestrator envelope contract; Notifier ingestion tests | Scanner WebService Guild | Emit orchestrator-compatible envelopes (`scanner.event.*`) and update integration tests to verify Notifier ingestion (no Redis queue coupling). |
| 15 | SCANNER-GRAPH-21-001 | DONE (2025-11-27) | — | Scanner WebService Guild, Cartographer Guild (`src/Scanner/StellaOps.Scanner.WebService`) | Provide webhook/REST endpoint for Cartographer to request policy overlays and runtime evidence for graph nodes, ensuring determinism and tenant scoping. |
| 16 | SCANNER-LNM-21-001 | BLOCKED (2025-11-27) | Needs Concelier HTTP client/shared library | Scanner WebService Guild, Policy Guild | Update `/reports` and `/policy/runtime` payloads to consume advisory/vex linksets, exposing source severity arrays and conflict summaries alongside effective verdicts. |
| 17 | SCANNER-LNM-21-002 | TODO | SCANNER-LNM-21-001 | Scanner WebService Guild, UI Guild | Add evidence endpoint for Console to fetch linkset summaries with policy overlay for a component/SBOM, including AOC references. |
| 18 | SCANNER-SECRETS-03 | DONE (2025-11-27) | SCANNER-SECRETS-02 | BuildX Plugin Guild, Security Guild (`src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin`) | Use Surface.Secrets to retrieve registry credentials when interacting with CAS/referrers. |
| 19 | SURFACE-SECRETS-01 | DONE (2025-11-23) | — | Scanner Guild, Security Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets`) | Security-approved schema published at `docs/modules/scanner/design/surface-secrets-schema.md`. |
| 20 | SURFACE-SECRETS-02 | DONE (2025-11-23) | SURFACE-SECRETS-01 | Scanner Guild | Provider chain implemented (primary + fallback) with DI wiring; tests updated (`StellaOps.Scanner.Surface.Secrets.Tests`). |
| 21 | SURFACE-SECRETS-03 | DONE (2025-11-27) | SURFACE-SECRETS-02 | Scanner Guild | Add Kubernetes/File/Offline backends with deterministic caching and audit hooks. |
| 22 | SURFACE-SECRETS-04 | DONE (2025-11-27) | SURFACE-SECRETS-02 | Scanner Guild | Integrate Surface.Secrets into Scanner Worker/WebService/BuildX for registry + CAS creds. |
| 23 | SURFACE-SECRETS-05 | DONE (2025-11-27) | SURFACE-SECRETS-02 | Zastava Guild | Invoke Surface.Secrets from Zastava Observer/Webhook for CAS & attestation secrets. |
| 24 | SURFACE-SECRETS-06 | BLOCKED (2025-11-27) | SURFACE-SECRETS-03; awaiting Ops Helm/Compose patterns | Ops Guild | Update deployment manifests/offline kit bundles to provision secret references instead of raw values. |
| 25 | SCANNER-ENG-0020 | DONE (2025-11-28) | — | Scanner Guild (`docs/modules/scanner`) | Implement Homebrew collector & fragment mapper per `design/macos-analyzer.md` §3.1. |
| 26 | SCANNER-ENG-0021 | DONE (2025-11-28) | — | Scanner Guild | Implement pkgutil receipt collector per `design/macos-analyzer.md` §3.2. |
| 27 | SCANNER-ENG-0022 | DONE (2025-11-28) | — | Scanner Guild, Policy Guild | Implement macOS bundle inspector & capability overlays per `design/macos-analyzer.md` §3.3. |
| 28 | SCANNER-ENG-0023 | DONE (2025-11-28) | — | Scanner Guild, Offline Kit Guild, Policy Guild | Deliver macOS policy/offline integration per `design/macos-analyzer.md` §56. |
| 29 | SCANNER-ENG-0024 | DONE (2025-11-28) | — | Scanner Guild | Implement Windows MSI collector per `design/windows-analyzer.md` §3.1. |
| 30 | SCANNER-ENG-0025 | DONE (2025-11-28) | — | Scanner Guild | Implement WinSxS manifest collector per `design/windows-analyzer.md` §3.2. |
| 31 | SCANNER-ENG-0026 | DONE (2025-11-28) | — | Scanner Guild | Implement Windows Chocolatey & registry collectors per `design/windows-analyzer.md` §3.33.4. |
| 32 | SCANNER-ENG-0027 | DONE (2025-11-28) | — | Scanner Guild, Policy Guild, Offline Kit Guild | Deliver Windows policy/offline integration per `design/windows-analyzer.md` §56. |
| 33 | SCHED-SURFACE-02 | TODO | SURFACE-FS-02; SCHED-SURFACE-01; see `docs/modules/scanner/design/surface-fs-consumers.md` §3 | Scheduler Worker Guild (`src/Scheduler/__Libraries/StellaOps.Scheduler.Worker`) | Integrate Scheduler worker prefetch using Surface manifest reader and persist manifest pointers with rerun plans. |
| 34 | ZASTAVA-SURFACE-02 | DONE (2025-12-01) | SURFACE-FS-02, ZASTAVA-SURFACE-01 | Zastava Observer Guild (`src/Zastava/StellaOps.Zastava.Observer`) | Surface manifest CAS/sha resolver wired into Observer drift evidence with failure metrics. |
| 35 | SURFACE-FS-03 | DONE (2025-11-27) | SURFACE-FS-02 | Scanner Guild | Integrate Surface.FS writer into Scanner Worker analyzer pipeline to persist layer + entry-trace fragments. |
| 36 | SURFACE-FS-04 | DONE (2025-11-27) | SURFACE-FS-02 | Zastava Guild | Integrate Surface.FS reader into Zastava Observer runtime drift loop. |
| 37 | SURFACE-FS-05 | DONE (2025-11-27) | SURFACE-FS-03 | Scanner Guild, Scheduler Guild | Expose Surface.FS pointers via Scanner WebService reports and coordinate rescan planning with Scheduler. |
| 38 | SURFACE-FS-06 | DONE (2025-11-28) | SURFACE-FS-02..05 | Docs Guild | Update scanner-engine guide and offline kit docs with Surface.FS workflow. |
| 39 | SCANNER-SURFACE-01 | BLOCKED (2025-11-25) | Task definition absent | Scanner Guild | Placeholder task; scope/contract required before implementation. |
| 40 | SCANNER-SURFACE-04 | DONE (2025-12-02) | SCANNER-SURFACE-01, SURFACE-FS-03 | Scanner Worker Guild (`src/Scanner/StellaOps.Scanner.Worker`) | DSSE-sign every `layer.fragments` payload, emit `_composition.json`/`composition.recipe` URI, and persist DSSE envelopes for deterministic offline replay (see `deterministic-sbom-compose.md` §2.1). |
| 41 | SURFACE-FS-07 | TODO | SCANNER-SURFACE-04 | Scanner Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS`) | Extend Surface.FS manifest schema with `composition.recipe`, fragment attestation metadata, and verification helpers per deterministic SBOM spec (legacy TODO; superseded by row 42). |
| 42 | SURFACE-FS-07 | DONE (2025-12-02) | SCANNER-SURFACE-04 | Scanner Guild | Surface.FS manifest schema carries composition recipe/DSSE attestations and determinism metadata; determinism verifier added for offline replay. |
| 43 | SCANNER-EMIT-15-001 | DOING (2025-12-01) | SCANNER-SURFACE-04 | Scanner Emit Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Emit`) | CycloneDX artifacts carry content hash + Merkle root (= recipe hash), composition recipe URI, emit `_composition.json` + DSSE envelopes; replace deterministic-local signer with real signing. |
| 44 | SCANNER-SORT-02 | DONE (2025-12-01) | SCANNER-EMIT-15-001 | Scanner Core Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Core`) | Layer fragment ordering by digest implemented in ComponentGraphBuilder; determinism regression test added. |
| 45 | SURFACE-VAL-01 | DONE (2025-11-23) | SURFACE-FS-01, SURFACE-ENV-01 | Scanner Guild, Security Guild (`src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation`) | Validation framework doc aligned with Surface.Env release and secrets schema (`surface-validation.md` v1.1). |
| 46 | SURFACE-VAL-02 | DONE (2025-11-23) | SURFACE-VAL-01, SURFACE-ENV-02, SURFACE-FS-02 | Scanner Guild | Validation library enforces secrets schema, fallback/provider checks, and inline/file guardrails; tests added. |
| 47 | SURFACE-VAL-03 | DONE (2025-11-23) | SURFACE-VAL-02 | Scanner Guild, Analyzer Guild | Validation runner wired into Worker/WebService startup and pre-analyzer paths (OS, language, EntryTrace). |
| 48 | SURFACE-VAL-04 | DONE (2025-11-27) | SURFACE-VAL-02 | Scanner Guild, Zastava Guild | Expose validation helpers to Zastava and other runtime consumers for preflight checks. |
| 49 | SURFACE-VAL-05 | DONE | SURFACE-VAL-02 | Docs Guild | Document validation extensibility, registration, and customization in scanner-engine guides. |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-11-08 | Sprint stub created; awaiting completion of Sprint 0135. | Planning | | 2025-12-02 | Merged legacy `SPRINT_136_scanner_surface.md` content into canonical file; added missing tasks/logs; converted legacy file to stub to prevent divergence. | Project Mgmt |
| 2025-11-19 | Normalized sprint to standard template and renamed from `SPRINT_136_scanner_surface.md` to `SPRINT_0136_0001_0001_scanner_surface.md`; content preserved. | Implementer | | 2025-12-02 | SCANNER-SURFACE-04 completed: manifest stage emits composition recipe + DSSE envelopes, attaches attestations to artifacts, and records determinism Merkle root/recipe metadata. | Implementer |
| 2025-11-19 | Converted legacy filename `SPRINT_136_scanner_surface.md` to redirect stub pointing here to avoid divergent updates. | Implementer | | 2025-12-02 | SURFACE-FS-07 completed: Surface.FS manifest schema now includes determinism metadata, composition recipe attestation fields, determinism verifier, and docs updated. Targeted determinism tests added; test run pending due to long restore/build in monorepo runner. | Implementer |
| 2025-11-24 | Marked SURFACE-FS-01 DONE; spec anchored in `docs/modules/scanner/design/surface-fs.md` v1.1. | Scanner Guild | | 2025-12-01 | EntryTrace NDJSON emission, runtime reconciliation, and WebService/CLI exposure completed (18-504/505/506). | EntryTrace Guild |
| 2025-11-24 | Marked SURFACE-FS-02 DONE; core Surface.FS manifest/cache library implemented and DI-ready. | Scanner Guild |
| 2025-11-25 | Marked EntryTrace chain (18-504/505/506) BLOCKED pending upstream 18-503 outputs from prior phase. | Project Mgmt |
| 2025-11-25 | Added SCANNER-SURFACE-01 to tracker and marked BLOCKED because task definition/scope is missing from sprint/docs; needs contract before work can begin. | Project Mgmt |
| 2025-12-01 | Unblocked EntryTrace NDJSON track: 18-504 set to TODO after 18-503 delivered in Sprint 0135; 18-505/506 remain blocked on 504 completion. | Project Mgmt |
| 2025-12-01 | Completed 18-504: EntryTrace NDJSON emitted via worker (EntryTraceNdjsonWriter) and surfaced in SurfaceManifest payloads; CLI/WebService entrytrace endpoint returns NDJSON alongside graph. | Implementer |
| 2025-12-01 | Completed 18-505: ProcGraph replay reconciles `/proc` snapshot with static EntryTrace, collapsing wrappers and emitting runtime match/mismatch diagnostics with chains; confidence adjusted per runtime evidence. | Implementer |
| 2025-12-01 | Added best-terminal metadata to entrytrace graph/ndjson surface payloads; SurfaceManifestStageExecutor tests updated and passing. | Implementer |
| 2025-12-01 | Completed 18-506: WebService `/scans/{id}/entrytrace` and CLI rendering now expose EntryTrace graph + confidence summaries alongside NDJSON stream. | Implementer |
| 2025-12-01 | ZASTAVA-SURFACE-02: Observer resolves Surface manifest digests and `cas://` URIs, enriches drift evidence with artifact metadata, and counts failures via `zastava_surface_manifest_failures_total`. | Implementer | | 2025-12-01 | ZASTAVA-SURFACE-02: Observer resolves Surface manifest digests and `cas://` URIs, enriches drift evidence with artifact metadata, and counts failures via `zastava_surface_manifest_failures_total`. | Implementer |
| 2025-12-01 | SCANNER-SORT-02: ComponentGraphBuilder sorts layer fragments by digest; regression test added. | Implementer | | 2025-12-01 | SCANNER-SORT-02: ComponentGraphBuilder sorts layer fragments by digest; regression test added. | Implementer |
| 2025-12-02 | Surface FS determinism plumbing verified: injected ICryptoHash into FileSurfaceManifestStore test harness; `dotnet test …SurfaceManifestDeterminismVerifierTests` (2/2 pass) and full `StellaOps.Scanner.Surface.FS.Tests` suite (7/7 pass). | Implementer | | 2025-12-01 | SCANNER-EMIT-15-001: CycloneDX artifacts now publish `ContentHash`, carry Merkle/recipe URIs, emit `_composition.json` + DSSE envelopes (recipe & layer.fragments), and Surface manifests reference those attestations. DSSE signer is pluggable (deterministic fallback registered); real signing still pending. | Implementer |
| 2025-12-01 | SCANNER-SORT-02 completed: ComponentGraphBuilder sorts layer fragments by digest with regression test Build_SortsLayersByDigest. | Implementer |
| 2025-12-01 | ZASTAVA-SURFACE-02: Observer now resolves Surface manifest digests and `cas://` URIs, enriches drift evidence with artifact metadata, and counts failures via `zastava_surface_manifest_failures_total`. | Implementer |
| 2025-11-28 | Created `docs/modules/scanner/guides/surface-validation-extensibility.md` covering custom validators, reporters, configuration, and testing; SURFACE-VAL-05 DONE. | Implementer |
| 2025-11-28 | Created `docs/modules/scanner/guides/surface-fs-workflow.md` with end-to-end workflow including artefact generation, storage layout, consumption, and offline kit handling; SURFACE-FS-06 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Homebrew` library with `HomebrewReceiptParser`, `HomebrewPackageAnalyzer`, and `HomebrewAnalyzerPlugin`; 23 tests passing. SCANNER-ENG-0020 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Pkgutil` library with `PkgutilReceiptParser`, `BomParser`, `PkgutilPackageAnalyzer`, and `PkgutilAnalyzerPlugin`; 9 tests passing. SCANNER-ENG-0021 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Windows.Msi` library with `MsiDatabaseParser`, `MsiPackageAnalyzer`, and `MsiAnalyzerPlugin`; 22 tests passing. SCANNER-ENG-0024 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Windows.WinSxS` library with `WinSxSManifestParser`, `WinSxSPackageAnalyzer`, and `WinSxSAnalyzerPlugin`; 18 tests passing. SCANNER-ENG-0025 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Windows.Chocolatey` library with `NuspecParser`, `ChocolateyPackageAnalyzer`, and `ChocolateyAnalyzerPlugin`; 44 tests passing. SCANNER-ENG-0026 DONE. | Implementer |
| 2025-11-28 | Updated `docs/modules/scanner/design/windows-analyzer.md` with implementation status documenting MSI/WinSxS/Chocolatey collectors, PURL formats, and vendor metadata schemas; registry collector deferred, policy predicates pending Policy module integration. SCANNER-ENG-0027 DONE. | Implementer |
| 2025-11-27 | Added missing package references to BuildX plugin (Configuration.EnvironmentVariables, DependencyInjection, Logging); refactored to use public AddSurfaceEnvironment API instead of internal SurfaceEnvironmentFactory; build passes. SCANNER-ENV-03 DONE. | Implementer |
| 2025-11-27 | Created SurfaceFeatureFlagsConfigurator to merge Surface.Env feature flags into WebService FeatureFlagOptions.Experimental dictionary; registered configurator in Program.cs. Cache roots and feature flags now wired from Surface.Env. SCANNER-ENV-02 DONE. | Implementer |
| 2025-11-27 | Verified SURFACE-ENV-03: Scanner Worker (SCANNER-ENV-01), WebService (SCANNER-ENV-02), and BuildX (SCANNER-ENV-03) all wire Surface.Env helpers; task complete. SURFACE-ENV-03 DONE. | Implementer |
| 2025-11-27 | Added CachingSurfaceSecretProvider (deterministic TTL cache), AuditingSurfaceSecretProvider (structured audit logging), and OfflineSurfaceSecretProvider (integrity-verified offline kit support); wired into ServiceCollectionExtensions with configurable options. SURFACE-SECRETS-03 DONE. | Implementer |
| 2025-11-27 | Added Surface.Validation project references to Zastava Observer and Webhook; wired AddSurfaceValidation() in service extensions for preflight checks. SURFACE-VAL-04 DONE. | Implementer |
| 2025-11-27 | Verified Zastava Observer and Webhook already have AddSurfaceEnvironment() wired with ZASTAVA prefixes; SURFACE-ENV-04 DONE. | Implementer |
| 2025-11-27 | Added Surface.Secrets project reference to BuildX plugin; implemented TryResolveAttestationToken() to fetch attestation secrets from Surface.Secrets; Worker/WebService already had configurators for CAS/registry/attestation secrets. SURFACE-SECRETS-04 DONE. | Implementer |
| 2025-11-27 | Verified Zastava Observer/Webhook already have ObserverSurfaceSecrets/WebhookSurfaceSecrets classes using ISurfaceSecretProvider for CAS and attestation secrets. SURFACE-SECRETS-05 DONE. | Implementer |
| 2025-11-27 | SURFACE-SECRETS-06 marked BLOCKED: requires Ops Guild input on Helm/Compose patterns for Surface.Secrets provider configuration (kubernetes/file/inline). Added to Decisions & Risks. | Implementer |
| 2025-11-27 | Integrated ISurfaceManifestWriter into SurfaceManifestStageExecutor to persist manifest documents to file-system store for offline/air-gapped scenarios; build verified. SURFACE-FS-03 DONE. | Implementer |
| 2025-11-27 | Added IRuntimeSurfaceFsClient injection to RuntimePostureEvaluator, enriching drift evidence with manifest digest/artifacts/metadata; added `zastava_surface_manifest_failures_total` metric with reason labels. SURFACE-FS-04 DONE. | Implementer |
| 2025-11-27 | Added TryResolveCasCredentials() to BuildX plugin using Surface.Secrets to fetch CAS access credentials; fixed attestation token resolution to use correct parser method. SCANNER-SECRETS-03 DONE. | Implementer |
| 2025-11-27 | Verified SurfacePointerService already exposes Surface.FS pointers (SurfaceManifestDocument, SurfaceManifestArtifact, manifest URI/digest) via reports endpoint. SURFACE-FS-05 DONE. | Implementer |
| 2025-11-27 | Added POST /policy/overlay endpoint for Cartographer integration: accepts graph nodes, returns deterministic overlays with sha256(tenant|nodeId|overlayKind) IDs, includes runtime evidence. Added PolicyOverlayRequestDto/ResponseDto contracts. SCANNER-GRAPH-21-001 DONE. | Implementer |
| 2025-11-27 | SCANNER-LNM-21-001 marked BLOCKED: Scanner WebService has no existing Concelier integration; requires HTTP client or shared library reference to Concelier.Core for linkset consumption. Added to Decisions & Risks. | Implementer |
| 2025-11-24 | Marked SURFACE-FS-01 DONE; spec anchored in `docs/modules/scanner/design/surface-fs.md` v1.1. | Scanner Guild |
| 2025-11-24 | Marked SURFACE-FS-02 DONE; core Surface.FS manifest/cache library implemented and DI-ready. | Scanner Guild |
| 2025-11-23 | Published Security-approved Surface.Secrets schema; moved SURFACE-SECRETS-01 to DONE, SURFACE-SECRETS-02/SURFACE-VAL-01 to TODO. | Security Guild |
| 2025-11-23 | Implemented Surface.Secrets provider chain/fallback and added DI tests; marked SURFACE-SECRETS-02 DONE. | Scanner Guild |
| 2025-11-23 | Pinned Surface.Env package version `0.1.0-alpha.20251123` and offline path in `docs/modules/scanner/design/surface-env-release.md`; SCANNER-ENV-03 moved to TODO. | BuildX Plugin Guild |
| 2025-11-23 | Updated Surface.Validation doc to v1.1, binding to Surface.Env release and secrets schema; marked SURFACE-VAL-01 DONE. | Scanner Guild |
| 2025-11-23 | Strengthened Surface.Validation secrets checks (provider/fallback/inline/file root) and added unit tests; marked SURFACE-VAL-02 DONE. | Scanner Guild |
| 2025-11-23 | Wired SurfaceValidation runner into Worker/WebService startup to fail fast; SURFACE-VAL-03 in progress. | Scanner Guild |
| 2025-11-19 | Normalized sprint to standard template and renamed from `SPRINT_136_scanner_surface.md` to `SPRINT_0136_0001_0001_scanner_surface.md`; content preserved. | Implementer |
| 2025-11-19 | Converted legacy filename `SPRINT_136_scanner_surface.md` to redirect stub pointing here to avoid divergent updates. | Implementer |
| 2025-11-18 | SCANNER-ENV-01 in progress: added manifest store options configurator in Scanner Worker and unit scaffold (tests pending due to local restore/vstest issues). | Implementer |
| 2025-11-18 | SCANNER-ENV-02 started: wired Surface manifest store options into Scanner WebService and unit scaffold added; tests pending (nuget.org restore cancelled locally). | Implementer |
| 2025-11-18 | Attempted `dotnet test` for Worker Surface manifest configurator; restore failed fetching StackExchange.Redis from nuget.org (network timeout); tests still pending CI. | Implementer |
| 2025-11-18 | SCANNER-ENV-03 started: BuildX plugin now loads Surface.Env defaults for cache root/bucket/tenant when args/env missing; tests not yet added. | Implementer |
| 2025-11-12 | SURFACE-ENV-01 done; SURFACE-ENV-02 started; SURFACE-SECRETS-01/02 in progress. | Scanner Guild |
| 2025-11-08 | Sprint stub created; awaiting completion of Sprint 0135. | Planning |
| 2025-10-26 | Initial sprint plan captured; dependencies noted across Scheduler/Surface/Cartographer. | Planning |
## Decisions & Risks ## Decisions & Risks
- EntryTrace NDJSON export and replay completed; relies on deterministic `/proc` capture and preserved ordering for confidence adjustments. - SCANNER-LNM-21-001 remains BLOCKED: Scanner WebService lacks Concelier integration; decision needed on shared client vs new HTTP client. Downstream SCANNER-LNM-21-002 cannot start.
- SCANNER-SURFACE-01 blocked: no task definition/contract present; needs scope before DOING. - SURFACE-SECRETS-06 BLOCKED pending Ops Helm/Compose patterns for Surface.Secrets provider configuration (kubernetes/file/inline).
- SCANNER-EVENTS-16-301 BLOCKED awaiting orchestrator envelope contract + Notifier ingestion test plan.
- SCANNER-SURFACE-01 lacks scoped contract; placeholder must be defined or retired before new dependencies are added.
- SCANNER-EMIT-15-001 DOING: real DSSE signer still pending; deterministic-local signer only. Surface manifest consumers must not assume transparency until signer is wired.
- Long restore/build times in monorepo runners delayed determinism test runs for SURFACE-FS-07; rerun in CI once signer work lands.
## Next Checkpoints ## Next Checkpoints
- Schedule kickoff after Sprint 0135 completion (date TBD). - Schedule kickoff after Sprint 0135 completion (date TBD).
- Concelier client decision & Ops secrets provisioning pattern review (target scheduling pending owners).

View File

@@ -30,15 +30,16 @@
| 2 | 140.B SBOM Service wave | DOING (2025-11-28) | Sprint 0142 mostly complete: SBOM-SERVICE-21-001..004, SBOM-AIAI-31-001/002, SBOM-ORCH-32/33/34-001, SBOM-VULN-29-001/002 all DONE. Only SBOM-CONSOLE-23-001/002 remain BLOCKED. | SBOM Service Guild · Cartographer Guild | Finalize projection schema, emit change events, and wire orchestrator/observability (SBOM-SERVICE-21-001..004, SBOM-AIAI-31-001/002). | | 2 | 140.B SBOM Service wave | DOING (2025-11-28) | Sprint 0142 mostly complete: SBOM-SERVICE-21-001..004, SBOM-AIAI-31-001/002, SBOM-ORCH-32/33/34-001, SBOM-VULN-29-001/002 all DONE. Only SBOM-CONSOLE-23-001/002 remain BLOCKED. | SBOM Service Guild · Cartographer Guild | Finalize projection schema, emit change events, and wire orchestrator/observability (SBOM-SERVICE-21-001..004, SBOM-AIAI-31-001/002). |
| 3 | 140.C Signals wave | DOING (2025-11-28) | Sprint 0143: SIGNALS-24-001/002/003 DONE; SIGNALS-24-004/005 remain BLOCKED on CAS promotion. | Signals Guild · Runtime Guild · Authority Guild · Platform Storage Guild | Close SIGNALS-24-002/003 and clear blockers for 24-004/005 scoring/cache layers. | | 3 | 140.C Signals wave | DOING (2025-11-28) | Sprint 0143: SIGNALS-24-001/002/003 DONE; SIGNALS-24-004/005 remain BLOCKED on CAS promotion. | Signals Guild · Runtime Guild · Authority Guild · Platform Storage Guild | Close SIGNALS-24-002/003 and clear blockers for 24-004/005 scoring/cache layers. |
| 4 | 140.D Zastava wave | DONE (2025-11-28) | Sprint 0144 (Zastava Runtime Signals) complete: all ZASTAVA-ENV/SECRETS/SURFACE tasks DONE. | Zastava Observer/Webhook Guilds · Surface Guild | Prepare env/secret helpers and admission hooks; start once cache endpoints and helpers are published. | | 4 | 140.D Zastava wave | DONE (2025-11-28) | Sprint 0144 (Zastava Runtime Signals) complete: all ZASTAVA-ENV/SECRETS/SURFACE tasks DONE. | Zastava Observer/Webhook Guilds · Surface Guild | Prepare env/secret helpers and admission hooks; start once cache endpoints and helpers are published. |
| 5 | DECAY-GAPS-140-005 | BLOCKED (2025-12-02) | cosign binary unavailable in environment; cannot sign `confidence_decay_config.yaml`. Needs cosign (or offline signer) to proceed by 2025-12-05. | Signals Guild · Product Mgmt | Address decay gaps U1U10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: publish signed `confidence_decay_config` (τ governance, floor/freeze/SLA clamps), weighted signals taxonomy, UTC/monotonic time rules, deterministic recompute cadence + checksum, uncertainty linkage, migration/backfill plan, API fields/bands, and observability/alerts. | | 5 | DECAY-GAPS-140-005 | DOING (2025-12-02) | cosign v2.6.0 available at `tools/cosign/cosign` (sha256 `ea5c65f99425d6cfbb5c4b5de5dac035f14d09131c1a0ea7c7fc32eab39364f9`); DSSE signing on 2025-12-05. | Signals Guild · Product Mgmt | Address decay gaps U1U10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: publish signed `confidence_decay_config` (τ governance, floor/freeze/SLA clamps), weighted signals taxonomy, UTC/monotonic time rules, deterministic recompute cadence + checksum, uncertainty linkage, migration/backfill plan, API fields/bands, and observability/alerts. |
| 6 | UNKNOWN-GAPS-140-006 | BLOCKED (2025-12-02) | cosign binary unavailable; cannot sign unknowns scoring manifest. Needs cosign/offline signer before 2025-12-05. | Signals Guild · Policy Guild · Product Mgmt | Address unknowns gaps UN1UN10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: publish signed Unknowns registry schema + scoring manifest (deterministic), decay policy catalog, evidence/provenance capture, SBOM/VEX linkage, SLA/suppression rules, API/CLI contracts, observability/reporting, offline bundle inclusion, and migration/backfill. | | 6 | UNKNOWN-GAPS-140-006 | DOING (2025-12-02) | cosign v2.6.0 available at `tools/cosign/cosign`; sign unknowns scoring manifest and publish DSSE envelope by 2025-12-05. | Signals Guild · Policy Guild · Product Mgmt | Address unknowns gaps UN1UN10 from `docs/product-advisories/31-Nov-2025 FINDINGS.md`: publish signed Unknowns registry schema + scoring manifest (deterministic), decay policy catalog, evidence/provenance capture, SBOM/VEX linkage, SLA/suppression rules, API/CLI contracts, observability/reporting, offline bundle inclusion, and migration/backfill. |
| 7 | UNKNOWN-HEUR-GAPS-140-007 | BLOCKED (2025-12-02) | cosign binary unavailable; cannot sign heuristic catalog/schema + fixtures. Needs cosign/offline signer before 2025-12-05. | Signals Guild · Policy Guild · Product Mgmt | Remediate UT1UT10: publish signed heuristic catalog/schema with deterministic scoring formula, quality bands, waiver policy with DSSE, SLA coupling, offline kit packaging, observability/alerts, backfill plan, explainability UX fields/exports, and fixtures with golden outputs. | | 7 | UNKNOWN-HEUR-GAPS-140-007 | DOING (2025-12-02) | cosign v2.6.0 available at `tools/cosign/cosign`; prep catalog/schema fixtures for 2025-12-05 signing. | Signals Guild · Policy Guild · Product Mgmt | Remediate UT1UT10: publish signed heuristic catalog/schema with deterministic scoring formula, quality bands, waiver policy with DSSE, SLA coupling, offline kit packaging, observability/alerts, backfill plan, explainability UX fields/exports, and fixtures with golden outputs. |
| 9 | COSIGN-INSTALL-140 | TODO | Install/provide cosign binary (or offline signer) in build environment by 2025-12-03 to unblock DSSE signing for tasks 57. | Platform / Build Guild | Deliver cosign binary locally (no network dependency at signing time) or alternate signer; document path and version in Execution Log. | | 9 | COSIGN-INSTALL-140 | DONE (2025-12-02) | cosign v2.6.0 staged under `tools/cosign` (sha256 `ea5c65f99425d6cfbb5c4b5de5dac035f14d09131c1a0ea7c7fc32eab39364f9`); add `tools/cosign` to PATH for signing 2025-12-05. | Platform / Build Guild | Deliver cosign binary locally (no network dependency at signing time) or alternate signer; document path and version in Execution Log. |
| 8 | SIGNER-ASSIGN-140 | DONE (2025-12-02) | Signer designated: Signals Guild (Alice Carter); DSSE signing checkpoint remains 2025-12-05. | Signals Guild · Policy Guild | Name signer(s), record in Execution Log, and proceed to DSSE signing + Evidence Locker ingest. | | 8 | SIGNER-ASSIGN-140 | DONE (2025-12-02) | Signer designated: Signals Guild (Alice Carter); DSSE signing checkpoint remains 2025-12-05. | Signals Guild · Policy Guild | Name signer(s), record in Execution Log, and proceed to DSSE signing + Evidence Locker ingest. |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-12-02 | Staged cosign v2.6.0 binary under `tools/cosign` (sha256 `ea5c65f99425d6cfbb5c4b5de5dac035f14d09131c1a0ea7c7fc32eab39364f9`); symlink available at `tools/cosign/cosign`; flipped COSIGN-INSTALL-140 to DONE and tasks 57 back to DOING for 2025-12-05 DSSE signing. | Implementer |
| 2025-12-02 | Refreshed Decisions & Risks after signer assignment; DSSE signing fixed for 2025-12-05 and decay/unknowns/heuristics remain BLOCKED pending `cosign` availability in offline kit. | Project Mgmt | | 2025-12-02 | Refreshed Decisions & Risks after signer assignment; DSSE signing fixed for 2025-12-05 and decay/unknowns/heuristics remain BLOCKED pending `cosign` availability in offline kit. | Project Mgmt |
| 2025-12-02 | Marked DECAY-GAPS-140-005 / UNKNOWN-GAPS-140-006 / UNKNOWN-HEUR-GAPS-140-007 as BLOCKED pending DSSE signer assignment; added task SIGNER-ASSIGN-140 (BLOCKED) and DSSE signing checkpoint (2025-12-05). | Implementer | | 2025-12-02 | Marked DECAY-GAPS-140-005 / UNKNOWN-GAPS-140-006 / UNKNOWN-HEUR-GAPS-140-007 as BLOCKED pending DSSE signer assignment; added task SIGNER-ASSIGN-140 (BLOCKED) and DSSE signing checkpoint (2025-12-05). | Implementer |
| 2025-12-02 | Flagged cascading risk to SPRINT_0143/0144/0150 if signer not assigned by 2025-12-03; will mirror BLOCKED status to dependent tasks if missed. | Implementer | | 2025-12-02 | Flagged cascading risk to SPRINT_0143/0144/0150 if signer not assigned by 2025-12-03; will mirror BLOCKED status to dependent tasks if missed. | Implementer |
@@ -79,7 +80,7 @@
- Link-Not-Merge v1 schema frozen 2025-11-17; fixtures staged under `docs/modules/sbomservice/fixtures/lnm-v1/`; AirGap parity review scheduled for 2025-11-23 (see Next Checkpoints) must record hashes to fully unblock. - Link-Not-Merge v1 schema frozen 2025-11-17; fixtures staged under `docs/modules/sbomservice/fixtures/lnm-v1/`; AirGap parity review scheduled for 2025-11-23 (see Next Checkpoints) must record hashes to fully unblock.
- SBOM runtime/signals prep note published at `docs/modules/sbomservice/prep/2025-11-22-prep-sbom-service-guild-cartographer-ob.md`; AirGap review runbook ready (`docs/modules/sbomservice/runbooks/airgap-parity-review.md`). Wave moves to TODO pending review completion and fixture hash upload. - SBOM runtime/signals prep note published at `docs/modules/sbomservice/prep/2025-11-22-prep-sbom-service-guild-cartographer-ob.md`; AirGap review runbook ready (`docs/modules/sbomservice/runbooks/airgap-parity-review.md`). Wave moves to TODO pending review completion and fixture hash upload.
- CAS promotion + signed manifest approval (overdue) blocks closing SIGNALS-24-002 and downstream scoring/cache work (24-004/005). - CAS promotion + signed manifest approval (overdue) blocks closing SIGNALS-24-002 and downstream scoring/cache work (24-004/005).
- Decay/Unknowns/heuristics remediation (U1U10, UN1UN10, UT1UT10) remain BLOCKED on missing `cosign` binary even though signer is assigned (Alice Carter); DSSE signing scheduled for 2025-12-05. If the signing tool (or offline alternative) is not available by 2025-12-03, mirror BLOCKED status into SPRINT_0143/0144/0150. Draft docs and artifacts posted at `docs/modules/signals/decay/2025-12-01-confidence-decay.md`, `docs/modules/signals/decay/confidence_decay_config.yaml`, `docs/modules/signals/unknowns/2025-12-01-unknowns-registry.md`, `docs/modules/signals/unknowns/unknowns_scoring_manifest.json`, and `docs/modules/signals/heuristics/` (catalog, schema, fixtures); DSSE signatures pending. Hashes recorded in `docs/modules/signals/SHA256SUMS` for offline/air-gap parity; Evidence Locker ingest plan staged at `docs/modules/signals/evidence/README.md` and will be populated post-signing. COSIGN-INSTALL-140 added to track tool availability. - Cosign v2.6.0 binary pinned at `tools/cosign/cosign` (sha256 `ea5c65f99425d6cfbb5c4b5de5dac035f14d09131c1a0ea7c7fc32eab39364f9`; see `tools/cosign/README.md`); DSSE signing deadline remains 2025-12-05—decay/unknowns/heuristics teams must sign and ingest envelopes + SHA256SUMS into Evidence Locker the same day or cascade risk into 0143/0144/0150. Draft docs and artifacts posted at `docs/modules/signals/decay/2025-12-01-confidence-decay.md`, `docs/modules/signals/decay/confidence_decay_config.yaml`, `docs/modules/signals/unknowns/2025-12-01-unknowns-registry.md`, `docs/modules/signals/unknowns/unknowns_scoring_manifest.json`, and `docs/modules/signals/heuristics/` (catalog, schema, fixtures); DSSE signatures pending. Hashes recorded in `docs/modules/signals/SHA256SUMS`; Evidence Locker ingest plan in `docs/modules/signals/evidence/README.md`.
- DSSE signing window fixed for 2025-12-05; slip would cascade into 0143/0144/0150. Ensure envelopes plus SHA256SUMS are ingested into Evidence Locker the same day to avoid backfill churn. - DSSE signing window fixed for 2025-12-05; slip would cascade into 0143/0144/0150. Ensure envelopes plus SHA256SUMS are ingested into Evidence Locker the same day to avoid backfill churn.
- Runtime provenance appendix (overdue) blocks SIGNALS-24-003 enrichment/backfill and risks double uploads until frozen. - Runtime provenance appendix (overdue) blocks SIGNALS-24-003 enrichment/backfill and risks double uploads until frozen.
- Surface.FS cache drop timeline (overdue) and Surface.Env owner assignment keep Zastava env/secret/admission tasks blocked. - Surface.FS cache drop timeline (overdue) and Surface.Env owner assignment keep Zastava env/secret/admission tasks blocked.
@@ -105,7 +106,7 @@
| 2025-12-04 | Unknowns schema review | Approve Unknowns registry schema/enums + deterministic scoring manifest (UN1UN10) and offline bundle inclusion plan. | Signals Guild · Policy Guild | | 2025-12-04 | Unknowns schema review | Approve Unknowns registry schema/enums + deterministic scoring manifest (UN1UN10) and offline bundle inclusion plan. | Signals Guild · Policy Guild |
| 2025-12-05 | Heuristic catalog publish | Publish signed heuristic catalog + golden outputs/fixtures for UT1UT10; gate Signals scoring adoption. | Signals Guild · Runtime Guild | | 2025-12-05 | Heuristic catalog publish | Publish signed heuristic catalog + golden outputs/fixtures for UT1UT10; gate Signals scoring adoption. | Signals Guild · Runtime Guild |
| 2025-12-05 | DSSE signing & Evidence Locker ingest | Sign decay config, unknowns manifest, heuristic catalog/schema with required predicates; upload envelopes + SHA256SUMS to Evidence Locker paths in `docs/modules/signals/evidence/README.md`. | Signals Guild · Policy Guild | | 2025-12-05 | DSSE signing & Evidence Locker ingest | Sign decay config, unknowns manifest, heuristic catalog/schema with required predicates; upload envelopes + SHA256SUMS to Evidence Locker paths in `docs/modules/signals/evidence/README.md`. | Signals Guild · Policy Guild |
| 2025-12-03 | Provide cosign/offline signer | Deliver cosign binary (or offline signing path) for tasks 57; otherwise slip DSSE signing. | Platform / Build Guild | | 2025-12-03 | Provide cosign/offline signer | DONE 2025-12-02: cosign v2.6.0 at `tools/cosign/cosign` (sha256 `ea5c65f99425d6cfbb5c4b5de5dac035f14d09131c1a0ea7c7fc32eab39364f9`); add `tools/cosign` to PATH ahead of 2025-12-05 signing. | Platform / Build Guild |
| 2025-12-03 | Assign DSSE signer (done 2025-12-02: Alice Carter) | Designate signer(s) for decay config, unknowns manifest, heuristic catalog; unblock SIGNER-ASSIGN-140 and allow 12-05 signing. | Signals Guild · Policy Guild | | 2025-12-03 | Assign DSSE signer (done 2025-12-02: Alice Carter) | Designate signer(s) for decay config, unknowns manifest, heuristic catalog; unblock SIGNER-ASSIGN-140 and allow 12-05 signing. | Signals Guild · Policy Guild |
--- ---

View File

@@ -29,8 +29,8 @@
| 5 | ZASTAVA-SURFACE-01 | DONE (2025-11-18) | Surface.FS drift client exercised in smoke suite | Zastava Observer Guild (src/Zastava/StellaOps.Zastava.Observer) | Integrate Surface.FS client for runtime drift detection (lookup cached layer hashes/entry traces). | | 5 | ZASTAVA-SURFACE-01 | DONE (2025-11-18) | Surface.FS drift client exercised in smoke suite | Zastava Observer Guild (src/Zastava/StellaOps.Zastava.Observer) | Integrate Surface.FS client for runtime drift detection (lookup cached layer hashes/entry traces). |
| 6 | ZASTAVA-SURFACE-02 | DONE (2025-11-18) | Admission smoke tests green with Surface.FS pointer enforcement | Zastava Webhook Guild (src/Zastava/StellaOps.Zastava.Webhook) | Enforce Surface.FS availability during admission (deny when cache missing/stale) and embed pointer checks in webhook response. | | 6 | ZASTAVA-SURFACE-02 | DONE (2025-11-18) | Admission smoke tests green with Surface.FS pointer enforcement | Zastava Webhook Guild (src/Zastava/StellaOps.Zastava.Webhook) | Enforce Surface.FS availability during admission (deny when cache missing/stale) and embed pointer checks in webhook response. |
| 7 | ZASTAVA-GAPS-144-007 | DONE (2025-12-02) | Remediation plan published at `docs/modules/zastava/gaps/2025-12-02-zr-gaps.md`; schemas/kit/thresholds tracked below. | Zastava Observer/Webhook Guilds / src/Zastava | Remediate ZR1ZR10: signed schemas + hash recipes, tenant binding, deterministic clocks/ordering, DSSE provenance, side-effect/bypass controls, offline zastava-kit, ledger/replay linkage, threshold governance, PII/redaction policy, kill-switch/fallback rules with alerts and audits. | | 7 | ZASTAVA-GAPS-144-007 | DONE (2025-12-02) | Remediation plan published at `docs/modules/zastava/gaps/2025-12-02-zr-gaps.md`; schemas/kit/thresholds tracked below. | Zastava Observer/Webhook Guilds / src/Zastava | Remediate ZR1ZR10: signed schemas + hash recipes, tenant binding, deterministic clocks/ordering, DSSE provenance, side-effect/bypass controls, offline zastava-kit, ledger/replay linkage, threshold governance, PII/redaction policy, kill-switch/fallback rules with alerts and audits. |
| 8 | ZASTAVA-SCHEMAS-0001 | TODO | DSSE signing window 2025-12-06; depends on signer availability. | Zastava Guild | Publish signed observer/admission schemas + examples + test vectors under `docs/modules/zastava/schemas/` with SHA256SUMS and DSSE envelopes. | | 8 | ZASTAVA-SCHEMAS-0001 | DONE (2025-12-02) | DSSE signing completed; keyid mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc. | Zastava Guild | Published signed observer/admission schemas + examples + test vectors under `docs/modules/zastava/schemas/` with SHA256SUMS and DSSE envelopes. |
| 9 | ZASTAVA-KIT-0001 | TODO | Depends on ZASTAVA-SCHEMAS-0001 and thresholds signing. | Zastava Guild | Build `zastava-kit` bundle (schemas, thresholds, observations/admissions export, SHA256SUMS, verify.sh) with deterministic tar+zstd flags; include DSSE signatures and Evidence Locker URIs. | | 9 | ZASTAVA-KIT-0001 | DONE (2025-12-02) | Depends on ZASTAVA-SCHEMAS-0001 and thresholds signing. | Zastava Guild | Built `zastava-kit` bundle (schemas, thresholds, exports, SHA256SUMS, verify.sh) with deterministic tar+zstd flags; DSSE signatures + Evidence Locker targets recorded. |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
@@ -66,6 +66,11 @@
| 2025-12-02 | Drafted ZR schemas (`docs/modules/zastava/schemas/*.json`), thresholds (`docs/modules/zastava/thresholds.yaml`), kit scaffolding (`docs/modules/zastava/kit/*`), and `docs/modules/zastava/SHA256SUMS`; DSSE signing pending (target 2025-12-06). | Implementer | | 2025-12-02 | Drafted ZR schemas (`docs/modules/zastava/schemas/*.json`), thresholds (`docs/modules/zastava/thresholds.yaml`), kit scaffolding (`docs/modules/zastava/kit/*`), and `docs/modules/zastava/SHA256SUMS`; DSSE signing pending (target 2025-12-06). | Implementer |
| 2025-12-02 | Added schema examples (`docs/modules/zastava/schemas/examples/*.json`) and appended hashes to `docs/modules/zastava/SHA256SUMS` to aid deterministic validation. | Implementer | | 2025-12-02 | Added schema examples (`docs/modules/zastava/schemas/examples/*.json`) and appended hashes to `docs/modules/zastava/SHA256SUMS` to aid deterministic validation. | Implementer |
| 2025-12-02 | Created Evidence Locker plan at `docs/modules/zastava/evidence/README.md` with predicates, signing template, and target paths for schemas/thresholds/kit (signing target 2025-12-06). | Implementer | | 2025-12-02 | Created Evidence Locker plan at `docs/modules/zastava/evidence/README.md` with predicates, signing template, and target paths for schemas/thresholds/kit (signing target 2025-12-06). | Implementer |
| 2025-12-02 | Started ZASTAVA-SCHEMAS-0001 and ZASTAVA-KIT-0001; prepping signing key, canonical hashes, and kit packaging steps. | Zastava Guild |
| 2025-12-02 | Completed ZASTAVA-SCHEMAS-0001: canonicalised schemas/examples, added DSSE envelopes, refreshed SHA256SUMS, and published ed25519 pub key (`kit/ed25519.pub`). | Zastava Guild |
| 2025-12-02 | Completed ZASTAVA-THRESHOLDS-0001: DSSE-signed `thresholds.yaml`, aligned Evidence Locker targets, and added to kit manifest. | Zastava Guild |
| 2025-12-02 | Completed ZASTAVA-KIT-0001: built deterministic `kit/zastava-kit.tzst` via tar+zstd (level 19, window_log=27), added DSSE for kit, refreshed verify script, and ran offline verification. Private key removed from workspace post-signing. | Zastava Guild |
| 2025-12-02 | Finalised DSSE set with keyid mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc; regenerated SHA256SUMS, rebuilt kit tar.zst, refreshed kit DSSE, and removed signing key from /tmp. | Zastava Guild |
## Decisions & Risks ## Decisions & Risks
- Surface Env/Secrets/FS wiring complete for observer and webhook; admission now embeds manifest pointers and denies on missing cache manifests. - Surface Env/Secrets/FS wiring complete for observer and webhook; admission now embeds manifest pointers and denies on missing cache manifests.
@@ -74,10 +79,10 @@
- Upstream Authority/Auth packages (notably `StellaOps.Auth.Security`) remain needed in local caches; refresh mirror before CI runs to avoid restore stalls. - Upstream Authority/Auth packages (notably `StellaOps.Auth.Security`) remain needed in local caches; refresh mirror before CI runs to avoid restore stalls.
- Surface.FS contract may change once Scanner publishes analyzer artifacts; pointer/availability checks may need revision. - Surface.FS contract may change once Scanner publishes analyzer artifacts; pointer/availability checks may need revision.
- Surface.Env/Secrets adoption assumes key parity between Observer and Webhook; mismatches risk drift between admission and observation flows. - Surface.Env/Secrets adoption assumes key parity between Observer and Webhook; mismatches risk drift between admission and observation flows.
- New advisory gaps (ZR1ZR10) addressed in remediation plan at `docs/modules/zastava/gaps/2025-12-02-zr-gaps.md`; drafts for schemas/thresholds/kit and SHA256 recorded under `docs/modules/zastava/`; DSSE signing still pending (target 2025-12-06). Evidence Locker paths will be added after signing. - New advisory gaps (ZR1ZR10) addressed in remediation plan at `docs/modules/zastava/gaps/2025-12-02-zr-gaps.md`; schemas/thresholds/exports now DSSE-signed (ed25519 pub `mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc`) with hashes in `docs/modules/zastava/SHA256SUMS`; kit DSSE stored at `docs/modules/zastava/kit/zastava-kit.tzst.dsse` and verification via `kit/verify.sh`; Evidence Locker targets listed in `docs/modules/zastava/evidence/README.md`.
- New advisory gaps (ZR1ZR10) addressed in remediation plan at `docs/modules/zastava/gaps/2025-12-02-zr-gaps.md`; drafts for schemas/thresholds/kit (plus examples) and SHA256 recorded under `docs/modules/zastava/`; DSSE signing still pending (target 2025-12-06). Evidence Locker plan staged at `docs/modules/zastava/evidence/README.md`; downstream kit build tracked via ZASTAVA-KIT-0001. - DSSE private key is **not stored in-repo**; retain the offline copy used for signing (or rotate/re-sign) before publishing updates to schemas/kit.
## Next Checkpoints ## Next Checkpoints
- 2025-11-18: Confirm local gRPC package mirrors with DevOps and obtain Sprint 130 analyzer/cache ETA to unblock SURFACE validations. - 2025-11-18: Confirm local gRPC package mirrors with DevOps and obtain Sprint 130 analyzer/cache ETA to unblock SURFACE validations.
- 2025-11-20: Dependency review with Scanner/AirGap owners to lock Surface.FS cache semantics; if ETA still missing, escalate per sprint 140 plan. - 2025-11-20: Dependency review with Scanner/AirGap owners to lock Surface.FS cache semantics; if ETA still missing, escalate per sprint 140 plan.
- 2025-12-06: ZR schemas/kit signing — produce signed schemas, thresholds, and `zastava-kit` bundle per `docs/modules/zastava/gaps/2025-12-02-zr-gaps.md`; publish Evidence Locker paths + SHA256. - 2025-12-03: Upload DSSE artefacts + kit tar to Evidence Locker paths in `docs/modules/zastava/evidence/README.md`; mirror pub key for downstream consumers.

View File

@@ -21,45 +21,48 @@
## Delivery Tracker ## Delivery Tracker
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | | # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| 1 | 150.A-Orchestrator | TODO | 0140.A (Graph) ✅ DONE, 0140.D (Zastava) ✅ DONE. Remaining blockers: 0120.A AirGap staleness + 0130.A Scanner surface | Orchestrator Service Guild · AirGap Policy/Controller Guilds · Observability Guild | Kick off orchestration scheduling/telemetry baseline for automation epic. | | 1 | 150.A-Orchestrator | BLOCKED | Graph (0140.A) ✅ DONE; Zastava (0140.D) ✅ DONE. Blocked on 0120.A AirGap staleness (56-002/57/58) and Scanner surface Java/Lang chain (0131). | Orchestrator Service Guild · AirGap Policy/Controller Guilds · Observability Guild | Kick off orchestration scheduling/telemetry baseline for automation epic. |
| 2 | 150.B-PacksRegistry | TODO | 150.A must reach DOING; confirm tenancy scaffolding from Orchestrator | Packs Registry Guild · Exporter Guild · Security Guild | Packs registry automation stream staged; start after Orchestrator scaffolding. | | 2 | 150.B-PacksRegistry | BLOCKED | 150.A must reach DOING; confirm tenancy scaffolding from Orchestrator | Packs Registry Guild · Exporter Guild · Security Guild | Packs registry automation stream staged; start after Orchestrator scaffolding. |
| 3 | 150.C-Scheduler | TODO | 0140.A Graph ✅ DONE. Remaining blocker: 0130.A Scanner surface | Scheduler WebService/Worker Guilds · Findings Ledger Guild · Observability Guild | Scheduler impact index improvements gated on Graph overlays. | | 3 | 150.C-Scheduler | BLOCKED | Graph ✅ DONE; still waiting on Scanner surface Java/Lang chain (0131 21-005..011) | Scheduler WebService/Worker Guilds · Findings Ledger Guild · Observability Guild | Scheduler impact index improvements gated on Graph overlays. |
| 4 | 150.D-TaskRunner | TODO | Requires Orchestrator/Scheduler telemetry baselines (150.A/150.C) | Task Runner Guild · AirGap Guilds · Evidence Locker Guild | Execution engine upgrades and evidence integration to start post-baselines. | | 4 | 150.D-TaskRunner | BLOCKED | Requires Orchestrator/Scheduler telemetry baselines (150.A/150.C) | Task Runner Guild · AirGap Guilds · Evidence Locker Guild | Execution engine upgrades and evidence integration to start post-baselines. |
## Wave Coordination Snapshot ## Wave Coordination Snapshot
| Wave | Guild owners | Shared prerequisites | Status | Notes | | Wave | Guild owners | Shared prerequisites | Status | Notes |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| 150.A Orchestrator | Orchestrator Service Guild · AirGap Policy/Controller Guilds · Observability Guild | Sprint 0120.A AirGap; Sprint 0130.A Scanner; Sprint 0140.A Graph | TODO | Graph (0140.A) and Zastava (0140.D) now DONE. AirGap staleness (0120.A 56-002/57/58) and Scanner surface (0130.A) remain blockers. Approaching readiness. | | 150.A Orchestrator | Orchestrator Service Guild · AirGap Policy/Controller Guilds · Observability Guild | Sprint 0120.A AirGap; Sprint 0130.A Scanner; Sprint 0140.A Graph | BLOCKED | Graph (0140.A) and Zastava (0140.D) DONE. AirGap staleness (0120.A 56-002/57/58) and Scanner surface Java/Lang chain (0131 21-005..011) still blocking kickoff. |
| 150.B PacksRegistry | Packs Registry Guild · Exporter Guild · Security Guild | Sprint 0120.A AirGap; Sprint 0130.A Scanner; Sprint 0140.A Graph | TODO | Blocked on Orchestrator tenancy scaffolding; specs ready once 150.A flips to DOING. | | 150.B PacksRegistry | Packs Registry Guild · Exporter Guild · Security Guild | Sprint 0120.A AirGap; Sprint 0130.A Scanner; Sprint 0140.A Graph | BLOCKED | Blocked on Orchestrator tenancy scaffolding; specs ready once 150.A enters DOING. |
| 150.C Scheduler | Scheduler WebService/Worker Guilds · Findings Ledger Guild · Observability Guild | Sprint 0120.A AirGap; Sprint 0130.A Scanner; Sprint 0140.A Graph | TODO | Graph overlays (0140.A) now DONE. Scheduler impact index work can proceed once Scanner surface (0130.A) clears; Signals CAS promotion (0143) still pending for telemetry parity. | | 150.C Scheduler | Scheduler WebService/Worker Guilds · Findings Ledger Guild · Observability Guild | Sprint 0120.A AirGap; Sprint 0130.A Scanner; Sprint 0140.A Graph | BLOCKED | Graph overlays (0140.A) DONE; Scanner surface Java/Lang chain still blocked; Signals CAS/DSSE signing (0140.C) pending for telemetry parity. |
| 150.D TaskRunner | Task Runner Guild · AirGap Guilds · Evidence Locker Guild | Sprint 0120.A AirGap; Sprint 0130.A Scanner; Sprint 0140.A Graph | TODO | Execution engine upgrades staged; start once Orchestrator/Scheduler telemetry baselines exist. | | 150.D TaskRunner | Task Runner Guild · AirGap Guilds · Evidence Locker Guild | Sprint 0120.A AirGap; Sprint 0130.A Scanner; Sprint 0140.A Graph | BLOCKED | Execution engine upgrades staged; start once Orchestrator/Scheduler telemetry baselines exist. |
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-12-02 | Upstream refresh: DEVOPS-SBOM-23-001 and DEVOPS-SCANNER-CI-11-001 delivered (Sprint 503) clearing infra blockers; SBOM console endpoints remain to implement. Signals wave (0140.C) still blocked on cosign availability for DSSE signing; AirGap staleness (0120.A 56-002/57/58) and Scanner Java/Lang chain (0131 21-005..011) remain blocked. All 150.* tasks kept BLOCKED. | Project Mgmt |
| 2025-11-30 | Upstream refresh: Sprint 0120 AirGap staleness (LEDGER-AIRGAP-56-002/57/58) still BLOCKED; Scanner surface Sprint 0131 has Deno 26-009/010/011 DONE but Java/Lang chain 21-005..011 BLOCKED pending CI/CoreLinksets; SBOM wave (Sprint 0142) core tasks DONE with Console endpoints still BLOCKED on DEVOPS-SBOM-23-001 in Sprint 503; Signals (Sprint 0143) 24-002/003 remain BLOCKED on CAS promotion/provenance though 24-004/005 are DONE. No 150.* task can start yet. | Implementer | | 2025-11-30 | Upstream refresh: Sprint 0120 AirGap staleness (LEDGER-AIRGAP-56-002/57/58) still BLOCKED; Scanner surface Sprint 0131 has Deno 26-009/010/011 DONE but Java/Lang chain 21-005..011 BLOCKED pending CI/CoreLinksets; SBOM wave (Sprint 0142) core tasks DONE with Console endpoints still BLOCKED on DEVOPS-SBOM-23-001 in Sprint 503; Signals (Sprint 0143) 24-002/003 remain BLOCKED on CAS promotion/provenance though 24-004/005 are DONE. No 150.* task can start yet. | Implementer |
| 2025-11-28 | Synced with downstream sprints: Sprint 0141 (Graph) DONE, Sprint 0142 (SBOM) mostly DONE, Sprint 0143 (Signals) 3/5 DONE, Sprint 0144 (Zastava) DONE. Updated Sprint 0140 tracker and revised 150.* upstream dependency status. 150.A-Orchestrator may start once remaining AirGap/Scanner blockers clear. | Implementer | | 2025-11-28 | Synced with downstream sprints: Sprint 0141 (Graph) DONE, Sprint 0142 (SBOM) mostly DONE, Sprint 0143 (Signals) 3/5 DONE, Sprint 0144 (Zastava) DONE. Updated Sprint 0140 tracker and revised 150.* upstream dependency status. 150.A-Orchestrator may start once remaining AirGap/Scanner blockers clear. | Implementer |
| 2025-11-28 | Upstream dependency check: Sprint 0120 (Policy/Reasoning) has LEDGER-29-007/008, LEDGER-34-101, LEDGER-AIRGAP-56-001 DONE but 56-002/57-001/58-001/ATTEST-73-001 BLOCKED. Sprint 0140 (Runtime/Signals) has all waves BLOCKED except SBOM (TODO). No Sprint 0130.A file found. All 150.* tasks remain TODO pending upstream readiness. | Implementer | | 2025-11-28 | Upstream dependency check: Sprint 0120 (Policy/Reasoning) has LEDGER-29-007/008, LEDGER-34-101, LEDGER-AIRGAP-56-001 DONE but 56-002/57-001/58-001/ATTEST-73-001 BLOCKED. Sprint 0140 (Runtime/Signals) has all waves BLOCKED except SBOM (TODO). No Sprint 0130.A file found. All 150.* tasks remain TODO pending upstream readiness. | Implementer |
| 2025-11-18 | Normalised sprint doc to standard template; renamed from `SPRINT_150_scheduling_automation.md`. | Planning | | 2025-11-18 | Normalised sprint doc to standard template; renamed from `SPRINT_150_scheduling_automation.md`. | Planning |
## Upstream Dependency Status (as of 2025-11-30) ## Upstream Dependency Status (as of 2025-12-02)
| Upstream Sprint | Key Deliverable | Status | Impact on 150.* | | Upstream Sprint | Key Deliverable | Status | Impact on 150.* |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Sprint 0120.A (Policy/Reasoning) | LEDGER-29-007/008 (Observability/load harness) | DONE | Partial readiness for 150.A |
| Sprint 0120.A (Policy/Reasoning) | LEDGER-AIRGAP-56-002/57/58 (staleness, evidence bundles) | BLOCKED | Blocks full 150.A readiness + 150.C verification | | Sprint 0120.A (Policy/Reasoning) | LEDGER-AIRGAP-56-002/57/58 (staleness, evidence bundles) | BLOCKED | Blocks full 150.A readiness + 150.C verification |
| Sprint 0120.A (Policy/Reasoning) | LEDGER-29-009 (deploy/backup collateral) | BLOCKED (awaiting Sprint 501 ops paths) | Not a gate for kickoff but limits rollout evidence | | Sprint 0120.A (Policy/Reasoning) | LEDGER-29-009-DEV (deploy/backup collateral) | BLOCKED (awaiting Sprint 501 ops paths) | Not a gate for kickoff but limits rollout evidence |
| Sprint 0130.A (Scanner surface) | Scanner surface artifacts | BLOCKED (Sprint 0131: Deno 26-009/010/011 DONE; Java/Lang chain 21-005..011 BLOCKED pending CI/CoreLinksets) | Blocks 150.A, 150.C verification | | Sprint 0131 (Scanner surface phase II) | Deno runtime chain 26-009/010/011 | DONE | Partial readiness for scanner surface inputs |
| Sprint 0140.A (Graph overlays) | 140.A Graph wave | **DONE** (Sprint 0141 complete) | Unblocks 150.C Scheduler graph deps | | Sprint 0131 (Scanner surface phase II) | Java/Lang chain 21-005..011 | BLOCKED (CoreLinksets still missing; DEVOPS-SCANNER-CI-11-001 delivered 2025-11-30) | Blocks 150.A and 150.C verification |
| Sprint 0140.A (Graph overlays) | 140.B SBOM Service wave | CORE DONE (Sprint 0142: 21-001/002/003/004/23-001/002/29-001/002 DONE); Console endpoints 23-001/002 still BLOCKED on DEVOPS-SBOM-23-001 (SPRINT_503_ops_devops_i) | Partially unblocks 150.A/150.C; Console integrations pending | | Sprint 0141 (Graph overlays 140.A) | GRAPH-INDEX-28-007..010 | **DONE** | Unblocks 150.C Scheduler graph deps |
| Sprint 0140.A (Graph overlays) | 140.C Signals wave | DOING (Sprint 0143: 24-002/003 BLOCKED on CAS promotion/provenance; 24-004/005 DONE) | Telemetry dependency partially unblocked; CAS promotion still required | | Sprint 0142 (SBOM Service 140.B) | SBOM-SERVICE-21-001..004, 23-001/002, 29-001/002 | CORE DONE; SBOM-CONSOLE-23-001/002 remain TODO now that DEVOPS-SBOM-23-001 (Sprint 503) is DONE | Partially unblocks 150.A/150.C; console integrations pending |
| Sprint 0140.A (Graph overlays) | 140.D Zastava wave | **DONE** (Sprint 0144 complete) | Unblocks 150.A surface deps | | Sprint 0143 (Signals 140.C) | SIGNALS-24-002/003 | BLOCKED (CAS promotion/provenance) | Telemetry dependency partially unblocked; still blocks parity |
| Sprint 0140 (Signals/decay/unknowns) | DECAY-GAPS-140-005 / UNKNOWN-GAPS-140-006 / UNKNOWN-HEUR-GAPS-140-007 | BLOCKED (cosign binary not available; DSSE signing window 2025-12-05) | Blocks telemetry parity needed before 150.A/150.C baselines start |
| Sprint 0144 (Zastava 140.D) | ZASTAVA-ENV/SECRETS/SURFACE | **DONE** | Surface deps unblocked |
| Sprint 0144 (Zastava 140.D) | ZASTAVA-SCHEMAS-0001 / ZASTAVA-KIT-0001 | TODO (DSSE signing target 2025-12-06) | Non-blocking unless cache/schema contracts change |
## Decisions & Risks ## Decisions & Risks
- **Progress (2025-11-30):** Graph (0140.A) and Zastava (0140.D) waves DONE; SBOM Service (0140.B) core DONE with Console APIs still BLOCKED on Sprint 503; Signals (0140.C) has 24-004/005 DONE while 24-002/003 wait on CAS. Remaining blockers: 0120.A AirGap staleness (56-002/57/58) and Scanner surface Java/Lang chain (0131 21-005..011). - **Progress (2025-12-02):** Graph (0140.A) and Zastava (0140.D) DONE; SBOM Service core DONE with Console APIs now unblocked by DEVOPS-SBOM-23-001 (Sprint 503) but still pending implementation. Signals wave (0140.C) still blocked on CAS promotion and missing `cosign` for DSSE signing (DECAY/UNKNOWN/HEUR gaps). AirGap staleness (0120.A 56-002/57/58) and Scanner Java/Lang chain (0131 21-005..011) remain blockers, keeping all 150.* tasks BLOCKED.
- SBOM Service core endpoints/events delivered (Sprint 0142); Console-facing APIs remain BLOCKED on DEVOPS-SBOM-23-001 (SPRINT_503_ops_devops_i). Track to avoid drift once Orchestrator/Scheduler streams start. - SBOM console endpoints should move next: feed/runner delivered via DEVOPS-SBOM-23-001; track SBOM-CONSOLE-23-001/002 execution to avoid drift before Orchestrator/Scheduler start.
- 150.A Orchestrator and 150.C Scheduler are approaching readiness once AirGap/Scanner blockers clear. - DSSE signing risk: cosign binary absent; signing window fixed at 2025-12-05 for Signals decay/unknowns/heuristics and 2025-12-06 for Zastava schemas/kit. If not resolved, telemetry parity and cache contracts stay blocked for 150.A/150.C baselines.
- This sprint is a coordination snapshot only; implementation tasks continue in Sprint 151+ and should mirror status changes here to avoid drift. - Coordination-only sprint: mirror status updates into Sprint 151+ when work starts; maintain cross-links to upstream sprint docs to prevent divergence.
- Sprint 0130.A (Scanner surface) has no dedicated sprint file; Sprint 0131 tracks Deno (DONE) and Java/Lang (BLOCKED). Coordinate with Scanner Guild to finalize. - Sprint 0130/0131 Scanner surface remains the primary gating item alongside AirGap staleness; re-evaluate start once either clears.
## Next Checkpoints ## Next Checkpoints
- None scheduled; add next scheduling/automation sync once upstream readiness dates are confirmed. - None scheduled; add next scheduling/automation sync once upstream readiness dates are confirmed.

View File

@@ -70,6 +70,7 @@
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-12-02 | WEB-CONSOLE-23-002: added trace IDs on status/stream calls, heartbeat + exponential backoff reconnect in console run stream service, and new client/service unit tests. Backend commands still not run locally (disk constraint). | BE-Base Platform Guild |
| 2025-12-01 | Started WEB-CONSOLE-23-002: added console status client (polling) + SSE run stream, store/service, and UI component; unit specs added. Commands/tests not executed locally due to PTY/disk constraint. | BE-Base Platform Guild | | 2025-12-01 | Started WEB-CONSOLE-23-002: added console status client (polling) + SSE run stream, store/service, and UI component; unit specs added. Commands/tests not executed locally due to PTY/disk constraint. | BE-Base Platform Guild |
| 2025-11-07 | Enforced unknown-field detection, added shared `AocError` payload (HTTP + CLI), refreshed guard docs, and extended tests/endpoint helpers. | BE-Base Platform Guild | | 2025-11-07 | Enforced unknown-field detection, added shared `AocError` payload (HTTP + CLI), refreshed guard docs, and extended tests/endpoint helpers. | BE-Base Platform Guild |
| 2025-11-07 | API scaffolding started for console workspace; `docs/advisory-ai/console.md` using placeholder responses while endpoints wire up. | Console Guild | | 2025-11-07 | API scaffolding started for console workspace; `docs/advisory-ai/console.md` using placeholder responses while endpoints wire up. | Console Guild |

View File

@@ -70,6 +70,7 @@
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-12-02 | WEB-RISK-66-001: risk HTTP client/store now handle 429 rate-limit responses with retry-after hints and RateLimitError wiring; unit specs added (execution deferred—npm test not yet run). | BE-Base Platform Guild |
| 2025-12-02 | Risk/Vuln clients now share trace ID generator util; vulnerability client emits trace headers across list/detail/stats; spec asserts header. | BE-Base Platform Guild | | 2025-12-02 | Risk/Vuln clients now share trace ID generator util; vulnerability client emits trace headers across list/detail/stats; spec asserts header. | BE-Base Platform Guild |
| 2025-12-02 | Test run skipped: `npm test` script unavailable in current environment; unit specs added but not executed. | BE-Base Platform Guild | | 2025-12-02 | Test run skipped: `npm test` script unavailable in current environment; unit specs added but not executed. | BE-Base Platform Guild |
| 2025-12-02 | Added empty/loading states to risk table for better UX while gateway data loads. | BE-Base Platform Guild | | 2025-12-02 | Added empty/loading states to risk table for better UX while gateway data loads. | BE-Base Platform Guild |

View File

@@ -36,6 +36,7 @@
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-11-30 | Normalised Delivery Tracker numbering, removed duplicate GAP-ZAS-002 row, and aligned statuses with Execution Log. | Project Mgmt | | 2025-11-30 | Normalised Delivery Tracker numbering, removed duplicate GAP-ZAS-002 row, and aligned statuses with Execution Log. | Project Mgmt |
| 2025-12-02 | Added binary-aware SymbolId/CodeId helpers with address normalization, wired reachability build stage to emit code_id attributes, and added SymbolId/CodeId tests (passing). | Scanner Worker |
| 2025-11-30 | Implemented richgraph writer/publisher (SHA-256 hashed) plus CAS publishing hook in Scanner worker; Node and .NET lifters now emit code_id/purl metadata; GAP-SCAN-001 moved to DOING. Tests for new writer/publisher added; restore via dotnet test still flaky (nuget spinner). | Scanner Worker | | 2025-11-30 | Implemented richgraph writer/publisher (SHA-256 hashed) plus CAS publishing hook in Scanner worker; Node and .NET lifters now emit code_id/purl metadata; GAP-SCAN-001 moved to DOING. Tests for new writer/publisher added; restore via dotnet test still flaky (nuget spinner). | Scanner Worker |
| 2025-11-26 | Validated runtime facts builder: `dotnet test src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/StellaOps.Zastava.Observer.Tests.csproj --filter RuntimeFactsBuilderTests` restored and passed; Observer build clean. | Zastava Observer Guild | | 2025-11-26 | Validated runtime facts builder: `dotnet test src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/StellaOps.Zastava.Observer.Tests.csproj --filter RuntimeFactsBuilderTests` restored and passed; Observer build clean. | Zastava Observer Guild |
| 2025-11-26 | Implemented runtime facts emitter in `StellaOps.Zastava.Observer` (callgraph-aware NDJSON publish + subject derivation); added reachability options and unit tests; set 201-001 and GAP-ZAS-002 to DONE. | Zastava Observer Guild | | 2025-11-26 | Implemented runtime facts emitter in `StellaOps.Zastava.Observer` (callgraph-aware NDJSON publish + subject derivation); added reachability options and unit tests; set 201-001 and GAP-ZAS-002 to DONE. | Zastava Observer Guild |

View File

@@ -25,8 +25,8 @@
| P4 | PREP-BENCH-POLICY-20-002-POLICY-DELTA-SAMPLE | DONE (2025-11-20) | Due 2025-11-26 · Accountable: Bench Guild · Policy Guild · Scheduler Guild | Bench Guild · Policy Guild · Scheduler Guild | Prep artefact published at `docs/benchmarks/policy/bench-policy-20-002-prep.md` (baseline + delta datasets, deterministic harness plan, metrics). | | P4 | PREP-BENCH-POLICY-20-002-POLICY-DELTA-SAMPLE | DONE (2025-11-20) | Due 2025-11-26 · Accountable: Bench Guild · Policy Guild · Scheduler Guild | Bench Guild · Policy Guild · Scheduler Guild | Prep artefact published at `docs/benchmarks/policy/bench-policy-20-002-prep.md` (baseline + delta datasets, deterministic harness plan, metrics). |
| P5 | PREP-BENCH-SIG-26-001-REACHABILITY-SCHEMA-FIX | DONE (2025-11-20) | Prep doc at `docs/benchmarks/signals/bench-sig-26-001-prep.md`; awaits reachability schema hash. | Bench Guild · Signals Guild | Reachability schema/fixtures pending Sprint 0400/0401. <br><br> Document artefact/deliverable for BENCH-SIG-26-001 and publish location so downstream tasks can proceed. | | P5 | PREP-BENCH-SIG-26-001-REACHABILITY-SCHEMA-FIX | DONE (2025-11-20) | Prep doc at `docs/benchmarks/signals/bench-sig-26-001-prep.md`; awaits reachability schema hash. | Bench Guild · Signals Guild | Reachability schema/fixtures pending Sprint 0400/0401. <br><br> Document artefact/deliverable for BENCH-SIG-26-001 and publish location so downstream tasks can proceed. |
| P6 | PREP-BENCH-SIG-26-002-BLOCKED-ON-26-001-OUTPU | DONE (2025-11-20) | Prep doc at `docs/benchmarks/signals/bench-sig-26-002-prep.md`; depends on 26-001 datasets. | Bench Guild · Policy Guild | Blocked on 26-001 outputs. <br><br> Document artefact/deliverable for BENCH-SIG-26-002 and publish location so downstream tasks can proceed. | | P6 | PREP-BENCH-SIG-26-002-BLOCKED-ON-26-001-OUTPU | DONE (2025-11-20) | Prep doc at `docs/benchmarks/signals/bench-sig-26-002-prep.md`; depends on 26-001 datasets. | Bench Guild · Policy Guild | Blocked on 26-001 outputs. <br><br> Document artefact/deliverable for BENCH-SIG-26-002 and publish location so downstream tasks can proceed. |
| 1 | BENCH-GRAPH-21-001 | DOING (2025-12-01) | PREP-BENCH-GRAPH-21-001-NEED-GRAPH-BENCH-HARN | Bench Guild · Graph Platform Guild | Build graph viewport/path benchmark harness (50k/100k nodes) measuring Graph API/Indexer latency, memory, and tile cache hit rates. | | 1 | BENCH-GRAPH-21-001 | DONE (2025-12-02) | PREP-BENCH-GRAPH-21-001-NEED-GRAPH-BENCH-HARN | Bench Guild · Graph Platform Guild | Build graph viewport/path benchmark harness (50k/100k nodes) measuring Graph API/Indexer latency, memory, and tile cache hit rates. |
| 2 | BENCH-GRAPH-21-002 | DOING (2025-12-01) | PREP-BENCH-GRAPH-21-002-BLOCKED-ON-21-001-HAR | Bench Guild · UI Guild | Add headless UI load benchmark (Playwright) for graph canvas interactions to track render times and FPS budgets. | | 2 | BENCH-GRAPH-21-002 | DONE (2025-12-02) | PREP-BENCH-GRAPH-21-002-BLOCKED-ON-21-001-HAR | Bench Guild · UI Guild | Add headless UI load benchmark (Playwright) for graph canvas interactions to track render times and FPS budgets. |
| 3 | BENCH-GRAPH-24-002 | BLOCKED | Waiting for 50k/100k graph fixture (SAMPLES-GRAPH-24-003) | Bench Guild · UI Guild | Implement UI interaction benchmarks (filter/zoom/table operations) citing p95 latency; integrate with perf dashboards. | | 3 | BENCH-GRAPH-24-002 | BLOCKED | Waiting for 50k/100k graph fixture (SAMPLES-GRAPH-24-003) | Bench Guild · UI Guild | Implement UI interaction benchmarks (filter/zoom/table operations) citing p95 latency; integrate with perf dashboards. |
| 4 | BENCH-IMPACT-16-001 | BLOCKED | PREP-BENCH-IMPACT-16-001-IMPACT-INDEX-DATASET | Bench Guild · Scheduler Team | ImpactIndex throughput bench (resolve 10k productKeys) + RAM profile. | | 4 | BENCH-IMPACT-16-001 | BLOCKED | PREP-BENCH-IMPACT-16-001-IMPACT-INDEX-DATASET | Bench Guild · Scheduler Team | ImpactIndex throughput bench (resolve 10k productKeys) + RAM profile. |
| 5 | BENCH-POLICY-20-002 | BLOCKED | PREP-BENCH-POLICY-20-002-POLICY-DELTA-SAMPLE | Bench Guild · Policy Guild · Scheduler Guild | Add incremental run benchmark measuring delta evaluation vs full; capture SLA compliance. | | 5 | BENCH-POLICY-20-002 | BLOCKED | PREP-BENCH-POLICY-20-002-POLICY-DELTA-SAMPLE | Bench Guild · Policy Guild · Scheduler Guild | Add incremental run benchmark measuring delta evaluation vs full; capture SLA compliance. |
@@ -76,6 +76,7 @@
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-12-02 | Marked BENCH-GRAPH-21-001/002 DONE after overlay-capable harness, SHA capture, UI driver metadata, and deterministic tests; runs still use synthetic fixtures until SAMPLES-GRAPH-24-003 arrives. | Implementer |
| 2025-11-27 | Added offline runner `Determinism/offline_run.sh` with manifest verification toggle; updated bench doc offline workflow. | Bench Guild | | 2025-11-27 | Added offline runner `Determinism/offline_run.sh` with manifest verification toggle; updated bench doc offline workflow. | Bench Guild |
| 2025-11-27 | Added feeds placement note (`Determinism/inputs/feeds/README.md`) and linked in bench offline workflow. | Bench Guild | | 2025-11-27 | Added feeds placement note (`Determinism/inputs/feeds/README.md`) and linked in bench offline workflow. | Bench Guild |
| 2025-11-27 | Added sample manifest `inputs/inputs.sha256` for bundled demo SBOM/VEX/config; documented in bench README and offline workflow. | Bench Guild | | 2025-11-27 | Added sample manifest `inputs/inputs.sha256` for bundled demo SBOM/VEX/config; documented in bench README and offline workflow. | Bench Guild |
@@ -91,6 +92,7 @@
| 2025-12-01 | Generated interim synthetic graph fixtures (50k/100k nodes with manifests) under `samples/graph/interim/` to unblock BENCH-GRAPH-21-001; task moved to DOING pending overlay schema for canonical fixture. | Implementer | | 2025-12-01 | Generated interim synthetic graph fixtures (50k/100k nodes with manifests) under `samples/graph/interim/` to unblock BENCH-GRAPH-21-001; task moved to DOING pending overlay schema for canonical fixture. | Implementer |
| 2025-12-01 | Added Graph UI bench scaffold: scenarios JSON, driver (`ui_bench_driver.mjs`), and plan under `src/Bench/StellaOps.Bench/Graph/`; BENCH-GRAPH-21-002 moved to DOING using interim fixtures until overlay schema/UI target is available. | Implementer | | 2025-12-01 | Added Graph UI bench scaffold: scenarios JSON, driver (`ui_bench_driver.mjs`), and plan under `src/Bench/StellaOps.Bench/Graph/`; BENCH-GRAPH-21-002 moved to DOING using interim fixtures until overlay schema/UI target is available. | Implementer |
| 2025-12-01 | Added graph bench runner `Graph/run_graph_bench.sh` and recorded sample results for graph-50k/100k fixtures; BENCH-GRAPH-21-001 progressing with interim fixtures. | Implementer | | 2025-12-01 | Added graph bench runner `Graph/run_graph_bench.sh` and recorded sample results for graph-50k/100k fixtures; BENCH-GRAPH-21-001 progressing with interim fixtures. | Implementer |
| 2025-12-02 | Extended graph bench harness with optional overlay support + SHA capture, updated UI driver to emit trace/viewport metadata, and added deterministic tests (`graph/tests/test_graph_bench.py`, `ui_bench_driver.test.mjs`). | Implementer |
| 2025-11-22 | Added ACT-0512-07 and corresponding risk entry to have UI bench harness skeleton ready once fixtures bind; no status changes. | Project Mgmt | | 2025-11-22 | Added ACT-0512-07 and corresponding risk entry to have UI bench harness skeleton ready once fixtures bind; no status changes. | Project Mgmt |
| 2025-11-22 | Added ACT-0512-04 to build interim synthetic graph fixture so BENCH-GRAPH-21-001 can start while awaiting SAMPLES-GRAPH-24-003; no status changes. | Project Mgmt | | 2025-11-22 | Added ACT-0512-04 to build interim synthetic graph fixture so BENCH-GRAPH-21-001 can start while awaiting SAMPLES-GRAPH-24-003; no status changes. | Project Mgmt |
| 2025-11-22 | Added ACT-0512-05 escalation path (due 2025-11-23) if SAMPLES-GRAPH-24-003 remains unavailable; updated Upcoming Checkpoints accordingly. | Project Mgmt | | 2025-11-22 | Added ACT-0512-05 escalation path (due 2025-11-23) if SAMPLES-GRAPH-24-003 remains unavailable; updated Upcoming Checkpoints accordingly. | Project Mgmt |

View File

@@ -32,7 +32,7 @@
| 4 | BENCH-CASES-PY-513-004 | DONE (2025-11-30) | Depends on 513-002. | Bench Guild · Python Track (`bench/reachability-benchmark/cases/py`) | Create 5-8 Python cases: Flask, Django, FastAPI. Include requirements.txt pinned, pytest oracles, coverage.py output. Delivered 5 cases: unsafe-exec (reachable), guarded-exec (unreachable), flask-template (reachable), fastapi-guarded (unreachable), django-ssti (reachable). | | 4 | BENCH-CASES-PY-513-004 | DONE (2025-11-30) | Depends on 513-002. | Bench Guild · Python Track (`bench/reachability-benchmark/cases/py`) | Create 5-8 Python cases: Flask, Django, FastAPI. Include requirements.txt pinned, pytest oracles, coverage.py output. Delivered 5 cases: unsafe-exec (reachable), guarded-exec (unreachable), flask-template (reachable), fastapi-guarded (unreachable), django-ssti (reachable). |
| 5 | BENCH-CASES-JAVA-513-005 | BLOCKED (2025-11-30) | Depends on 513-002. | Bench Guild · Java Track (`bench/reachability-benchmark/cases/java`) | Create 5-8 Java cases: Spring Boot, Micronaut. Include pom.xml locked, JUnit oracles, JaCoCo coverage. Progress: 2/5 seeded (`spring-deserialize` reachable, `spring-guarded` unreachable); build/test blocked by missing JDK (`javac` not available in runner). | | 5 | BENCH-CASES-JAVA-513-005 | BLOCKED (2025-11-30) | Depends on 513-002. | Bench Guild · Java Track (`bench/reachability-benchmark/cases/java`) | Create 5-8 Java cases: Spring Boot, Micronaut. Include pom.xml locked, JUnit oracles, JaCoCo coverage. Progress: 2/5 seeded (`spring-deserialize` reachable, `spring-guarded` unreachable); build/test blocked by missing JDK (`javac` not available in runner). |
| 6 | BENCH-CASES-C-513-006 | DONE (2025-12-01) | Depends on 513-002. | Bench Guild · Native Track (`bench/reachability-benchmark/cases/c`) | Create 3-5 C/ELF cases: small HTTP servers, crypto utilities. Include Makefile, gcov/llvm-cov coverage, deterministic builds (SOURCE_DATE_EPOCH). | | 6 | BENCH-CASES-C-513-006 | DONE (2025-12-01) | Depends on 513-002. | Bench Guild · Native Track (`bench/reachability-benchmark/cases/c`) | Create 3-5 C/ELF cases: small HTTP servers, crypto utilities. Include Makefile, gcov/llvm-cov coverage, deterministic builds (SOURCE_DATE_EPOCH). |
| 7 | BENCH-BUILD-513-007 | DOING | Depends on 513-003 through 513-006. | Bench Guild · DevOps Guild | Implement `build_all.py` and `validate_builds.py`: deterministic Docker builds, hash verification, SBOM generation (syft), attestation stubs. Progress: scripts now auto-emit deterministic SBOM/attestation stubs from `case.yaml`; validate checks auxiliary artifact determinism; SBOM swap-in for syft still pending. | | 7 | BENCH-BUILD-513-007 | DONE (2025-12-02) | Depends on 513-003 through 513-006. | Bench Guild · DevOps Guild | Implement `build_all.py` and `validate_builds.py`: deterministic Docker builds, hash verification, SBOM generation (syft), attestation stubs. Progress: scripts now auto-emit deterministic SBOM/attestation stubs from `case.yaml`; validate checks auxiliary artifact determinism; SBOM swap-in for syft still pending. |
| 8 | BENCH-SCORER-513-008 | DONE (2025-11-30) | Depends on 513-002. | Bench Guild (`bench/reachability-benchmark/tools/scorer`) | Implement `rb-score` CLI: load cases/truth, validate submissions, compute precision/recall/F1, explainability score (0-3), runtime stats, determinism rate. | | 8 | BENCH-SCORER-513-008 | DONE (2025-11-30) | Depends on 513-002. | Bench Guild (`bench/reachability-benchmark/tools/scorer`) | Implement `rb-score` CLI: load cases/truth, validate submissions, compute precision/recall/F1, explainability score (0-3), runtime stats, determinism rate. |
| 9 | BENCH-EXPLAIN-513-009 | DONE (2025-11-30) | Depends on 513-008. | Bench Guild | Implement explainability scoring rules: 0=no context, 1=path with ≥2 nodes, 2=entry+≥3 nodes, 3=guards/constraints included. Unit tests for each level. | | 9 | BENCH-EXPLAIN-513-009 | DONE (2025-11-30) | Depends on 513-008. | Bench Guild | Implement explainability scoring rules: 0=no context, 1=path with ≥2 nodes, 2=entry+≥3 nodes, 3=guards/constraints included. Unit tests for each level. |
| 10 | BENCH-BASELINE-SEMGREP-513-010 | DONE (2025-12-01) | Depends on 513-008 and cases. | Bench Guild | Semgrep baseline runner: added `baselines/semgrep/run_case.sh`, `run_all.sh`, rules, and `normalize.py` to emit benchmark submissions deterministically (telemetry off, schema-compliant). | | 10 | BENCH-BASELINE-SEMGREP-513-010 | DONE (2025-12-01) | Depends on 513-008 and cases. | Bench Guild | Semgrep baseline runner: added `baselines/semgrep/run_case.sh`, `run_all.sh`, rules, and `normalize.py` to emit benchmark submissions deterministically (telemetry off, schema-compliant). |
@@ -93,6 +93,7 @@
## Execution Log ## Execution Log
| Date (UTC) | Update | Owner | | Date (UTC) | Update | Owner |
| --- | --- | --- | | --- | --- | --- |
| 2025-12-02 | BENCH-BUILD-513-007: added optional Syft SBOM path with deterministic fallback stub, attestation/SBOM stub tests, and verified via `python bench/reachability-benchmark/tools/build/test_build_tools.py`. Status set to DONE. | Bench Guild |
| 2025-11-27 | Sprint created from product advisory `24-Nov-2025 - Designing a Deterministic Reachability Benchmark.md`; 17 tasks defined across 5 waves. | Product Mgmt | | 2025-11-27 | Sprint created from product advisory `24-Nov-2025 - Designing a Deterministic Reachability Benchmark.md`; 17 tasks defined across 5 waves. | Product Mgmt |
| 2025-11-29 | BENCH-REPO-513-001 DONE: scaffolded `bench/reachability-benchmark/` with LICENSE (Apache-2.0), NOTICE, README, CONTRIBUTING, .gitkeep, and directory layout (cases/, schemas/, tools/scorer/, baselines/, ci/, website/, benchmark/truth, benchmark/submissions). | Implementer | | 2025-11-29 | BENCH-REPO-513-001 DONE: scaffolded `bench/reachability-benchmark/` with LICENSE (Apache-2.0), NOTICE, README, CONTRIBUTING, .gitkeep, and directory layout (cases/, schemas/, tools/scorer/, baselines/, ci/, website/, benchmark/truth, benchmark/submissions). | Implementer |
| 2025-11-29 | BENCH-SCHEMA-513-002 DONE: expanded schemas (case/entrypoints/truth/submission), added examples + offline validator `tools/validate.py`, and pinned requirements for deterministic validation. | Implementer | | 2025-11-29 | BENCH-SCHEMA-513-002 DONE: expanded schemas (case/entrypoints/truth/submission), added examples + offline validator `tools/validate.py`, and pinned requirements for deterministic validation. | Implementer |

View File

@@ -1,105 +1,3 @@
# Sprint 136 - Scanner & Surface # Legacy sprint file (redirect)
Implementation order remains sequential across Sprint 130139. Complete each sprint in order before pulling tasks from the next file. This sprint was renamed to `SPRINT_0136_0001_0001_scanner_surface.md` on 2025-11-19 to comply with the standard filename template. Please update and read the canonical file instead.
## 7. Scanner.VII — Scanner & Surface focus on Scanner (phase VII).
Dependency: Sprint 135 - 6. Scanner.VI — Scanner & Surface focus on Scanner (phase VI).
| Task ID | State | Summary | Owner / Source | Depends On |
| --- | --- | --- | --- | --- |
| `SCANNER-ENTRYTRACE-18-504` | DONE | EntryTrace NDJSON (entry/node/edge/target/warning/capability) emitted via EntryTraceNdjsonWriter; Worker stores and WebService/CLI stream NDJSON payloads. | EntryTrace Guild (src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace) | SCANNER-ENTRYTRACE-18-503 |
| `SCANNER-ENTRYTRACE-18-505` | DONE | ProcGraph replay integrated: runtime snapshot reconciler matches terminals/wrappers, adjusts plan confidence, and emits diagnostics for agreements/mismatches. | EntryTrace Guild (src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace) | SCANNER-ENTRYTRACE-18-504 |
| `SCANNER-ENTRYTRACE-18-506` | DONE | EntryTrace graph and confidence exposed via WebService `/scans/{id}/entrytrace` and CLI (`stella scan entrytrace`, NDJSON option) with target summaries. | EntryTrace Guild, Scanner WebService Guild (src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace) | SCANNER-ENTRYTRACE-18-505 |
| `SCANNER-ENV-01` | DONE (2025-11-18) | Worker already wired to `AddSurfaceEnvironment`/`ISurfaceEnvironment` for cache roots + CAS endpoints; no remaining ad-hoc env reads. | Scanner Worker Guild (src/Scanner/StellaOps.Scanner.Worker) | — |
| `SCANNER-ENV-02` | DONE (2025-11-27) | Wire Surface.Env helpers into WebService hosting (cache roots, feature flags) and document configuration. | Scanner WebService Guild, Ops Guild (src/Scanner/StellaOps.Scanner.WebService) | SCANNER-ENV-01 |
| `SCANNER-ENV-03` | DONE (2025-11-27) | Surface.Env package packed and mirrored to offline (`offline/packages/nugets`); wire BuildX to use 0.1.0-alpha.20251123 and update restore feeds. | BuildX Plugin Guild (src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin) | SCANNER-ENV-02 |
| `SURFACE-ENV-01` | DONE (2025-11-13) | Draft `surface-env.md` enumerating environment variables, defaults, and air-gap behaviour for Surface consumers. | Scanner Guild, Zastava Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env) | — |
| `SURFACE-ENV-02` | DONE (2025-11-18) | Strongly-typed env accessors implemented; validation covers required endpoint, bounds, TLS cert path; regression tests passing. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env) | SURFACE-ENV-01 |
| `SURFACE-ENV-03` | DONE (2025-11-27) | Adopt the env helper across Scanner Worker/WebService/BuildX plug-ins. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env) | SURFACE-ENV-02 |
| `SURFACE-ENV-04` | DONE (2025-11-27) | Wire env helper into Zastava Observer/Webhook containers. | Zastava Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env) | SURFACE-ENV-02 |
| `SURFACE-ENV-05` | DONE | Update Helm/Compose/offline kit templates with new env knobs and documentation. | Ops Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Env) | SURFACE-ENV-03, SURFACE-ENV-04 |
| `SCANNER-EVENTS-16-301` | BLOCKED (2025-10-26) | Emit orchestrator-compatible envelopes (`scanner.event.*`) and update integration tests to verify Notifier ingestion (no Redis queue coupling). | Scanner WebService Guild (src/Scanner/StellaOps.Scanner.WebService) | — |
| `SCANNER-GRAPH-21-001` | DONE (2025-11-27) | Provide webhook/REST endpoint for Cartographer to request policy overlays and runtime evidence for graph nodes, ensuring determinism and tenant scoping. | Scanner WebService Guild, Cartographer Guild (src/Scanner/StellaOps.Scanner.WebService) | — |
| `SCANNER-LNM-21-001` | BLOCKED (2025-11-27) | Update `/reports` and `/policy/runtime` payloads to consume advisory/vex linksets, exposing source severity arrays and conflict summaries alongside effective verdicts. Blocked: requires Concelier HTTP client integration or shared library; no existing Concelier dependency in Scanner WebService. | Scanner WebService Guild, Policy Guild (src/Scanner/StellaOps.Scanner.WebService) | — |
| `SCANNER-LNM-21-002` | TODO | Add evidence endpoint for Console to fetch linkset summaries with policy overlay for a component/SBOM, including AOC references. | Scanner WebService Guild, UI Guild (src/Scanner/StellaOps.Scanner.WebService) | SCANNER-LNM-21-001 |
| `SCANNER-SECRETS-03` | DONE (2025-11-27) | Use Surface.Secrets to retrieve registry credentials when interacting with CAS/referrers. | BuildX Plugin Guild, Security Guild (src/Scanner/StellaOps.Scanner.Sbomer.BuildXPlugin) | SCANNER-SECRETS-02 |
| `SURFACE-SECRETS-01` | DONE (2025-11-23) | Security-approved schema published at `docs/modules/scanner/design/surface-secrets-schema.md`; proceed to provider wiring. | Scanner Guild, Security Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | — |
| `SURFACE-SECRETS-02` | DONE (2025-11-23) | Provider chain implemented (primary + fallback) with DI wiring; tests updated (`StellaOps.Scanner.Surface.Secrets.Tests`). | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-01 |
| `SURFACE-SECRETS-03` | DONE (2025-11-27) | Add Kubernetes/File/Offline backends with deterministic caching and audit hooks. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-02 |
| `SURFACE-SECRETS-04` | DONE (2025-11-27) | Integrate Surface.Secrets into Scanner Worker/WebService/BuildX for registry + CAS creds. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-02 |
| `SURFACE-SECRETS-05` | DONE (2025-11-27) | Invoke Surface.Secrets from Zastava Observer/Webhook for CAS & attestation secrets. | Zastava Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-02 |
| `SURFACE-SECRETS-06` | BLOCKED (2025-11-27) | Update deployment manifests/offline kit bundles to provision secret references instead of raw values. Requires Ops Guild input on Helm/Compose patterns for Surface.Secrets provider configuration. | Ops Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Secrets) | SURFACE-SECRETS-03 |
| `SCANNER-ENG-0020` | DONE (2025-11-28) | Implement Homebrew collector & fragment mapper per `design/macos-analyzer.md` §3.1. | Scanner Guild (docs/modules/scanner) | — |
| `SCANNER-ENG-0021` | DONE (2025-11-28) | Implement pkgutil receipt collector per `design/macos-analyzer.md` §3.2. | Scanner Guild (docs/modules/scanner) | — |
| `SCANNER-ENG-0022` | DONE (2025-11-28) | Implement macOS bundle inspector & capability overlays per `design/macos-analyzer.md` §3.3. | Scanner Guild, Policy Guild (docs/modules/scanner) | — |
| `SCANNER-ENG-0023` | DONE (2025-11-28) | Deliver macOS policy/offline integration per `design/macos-analyzer.md` §56. | Scanner Guild, Offline Kit Guild, Policy Guild (docs/modules/scanner) | — |
| `SCANNER-ENG-0024` | DONE (2025-11-28) | Implement Windows MSI collector per `design/windows-analyzer.md` §3.1. | Scanner Guild (docs/modules/scanner) | — |
| `SCANNER-ENG-0025` | DONE (2025-11-28) | Implement WinSxS manifest collector per `design/windows-analyzer.md` §3.2. | Scanner Guild (docs/modules/scanner) | — |
| `SCANNER-ENG-0026` | DONE (2025-11-28) | Implement Windows Chocolatey & registry collectors per `design/windows-analyzer.md` §3.33.4. | Scanner Guild (docs/modules/scanner) | — |
| `SCANNER-ENG-0027` | DONE (2025-11-28) | Deliver Windows policy/offline integration per `design/windows-analyzer.md` §56. | Scanner Guild, Policy Guild, Offline Kit Guild (docs/modules/scanner) | — |
| `SCHED-SURFACE-02` | TODO | Integrate Scheduler worker prefetch using Surface manifest reader and persist manifest pointers with rerun plans. | Scheduler Worker Guild (src/Scheduler/__Libraries/StellaOps.Scheduler.Worker) | SURFACE-FS-02, SCHED-SURFACE-01. Reference `docs/modules/scanner/design/surface-fs-consumers.md` §3 for implementation checklist |
| `ZASTAVA-SURFACE-02` | DONE (2025-12-01) | Surface manifest CAS/sha resolver wired into Observer drift evidence with failure metrics. | Zastava Observer Guild (src/Zastava/StellaOps.Zastava.Observer) | SURFACE-FS-02, ZASTAVA-SURFACE-01. Reference `docs/modules/scanner/design/surface-fs-consumers.md` §4 for integration steps |
| `SURFACE-FS-03` | DONE (2025-11-27) | Integrate Surface.FS writer into Scanner Worker analyzer pipeline to persist layer + entry-trace fragments. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS) | SURFACE-FS-02 |
| `SURFACE-FS-04` | DONE (2025-11-27) | Integrate Surface.FS reader into Zastava Observer runtime drift loop. | Zastava Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS) | SURFACE-FS-02 |
| `SURFACE-FS-05` | DONE (2025-11-27) | Expose Surface.FS pointers via Scanner WebService reports and coordinate rescan planning with Scheduler. | Scanner Guild, Scheduler Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS) | SURFACE-FS-03 |
| `SURFACE-FS-06` | DONE (2025-11-28) | Update scanner-engine guide and offline kit docs with Surface.FS workflow. | Docs Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS) | SURFACE-FS-02..05 |
| `SCANNER-SURFACE-04` | DONE (2025-12-02) | DSSE-sign every `layer.fragments` payload, emit `_composition.json`/`composition.recipe` URI, and persist DSSE envelopes so offline kits can replay deterministically (see `docs/modules/scanner/deterministic-sbom-compose.md` §2.1). | Scanner Worker Guild (src/Scanner/StellaOps.Scanner.Worker) | SCANNER-SURFACE-01, SURFACE-FS-03 |
| `SURFACE-FS-07` | TODO | Extend Surface.FS manifest schema with `composition.recipe`, fragment attestation metadata, and verification helpers per deterministic SBOM spec. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS) | SCANNER-SURFACE-04 |
| `SURFACE-FS-07` | DONE (2025-12-02) | Surface.FS manifest schema now carries composition recipe/DSSE attestations and determinism metadata; determinism verifier added for offline replay. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.FS) | SCANNER-SURFACE-04 |
| `SCANNER-EMIT-15-001` | DOING (2025-12-01) | CycloneDX artifacts now carry content hash, merkle root (= recipe hash), composition recipe URI, and emit `_composition.json` + DSSE envelopes for recipe and layer fragments. DSSE signing is still deterministic-local; replace with real signing. | Scanner Emit Guild (src/Scanner/__Libraries/StellaOps.Scanner.Emit) | SCANNER-SURFACE-04 |
| `SCANNER-SORT-02` | DONE (2025-12-01) | Layer fragment ordering by digest implemented in ComponentGraphBuilder; determinism regression test added. | Scanner Core Guild (src/Scanner/__Libraries/StellaOps.Scanner.Core) | SCANNER-EMIT-15-001 |
| `SURFACE-VAL-01` | DONE (2025-11-23) | Validation framework doc aligned with Surface.Env release and secrets schema (`docs/modules/scanner/design/surface-validation.md` v1.1). | Scanner Guild, Security Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-FS-01, SURFACE-ENV-01 |
| `SURFACE-VAL-02` | DONE (2025-11-23) | Validation library now enforces secrets schema, fallback/provider checks, and inline/file guardrails; tests added. | Scanner Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-01, SURFACE-ENV-02, SURFACE-FS-02 |
| `SURFACE-VAL-03` | DONE (2025-11-23) | Validation runner wired into Worker/WebService startup and pre-analyzer paths (OS, language, EntryTrace). | Scanner Guild, Analyzer Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-02 |
| `SURFACE-VAL-04` | DONE (2025-11-27) | Expose validation helpers to Zastava and other runtime consumers for preflight checks. | Scanner Guild, Zastava Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-02 |
| `SURFACE-VAL-05` | DONE | Document validation extensibility, registration, and customization in scanner-engine guides. | Docs Guild (src/Scanner/__Libraries/StellaOps.Scanner.Surface.Validation) | SURFACE-VAL-02 |
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2025-12-02 | SCANNER-SURFACE-04 completed: manifest stage emits composition recipe + DSSE envelopes, attaches attestations to artifacts, and records determinism Merkle root/recipe metadata. | Implementer |
| 2025-12-02 | SURFACE-FS-07 completed: Surface.FS manifest schema now includes determinism metadata, composition recipe attestation fields, determinism verifier, and docs updated. Targeted determinism tests added; test run pending due to long restore/build in monorepo runner. | Implementer |
| 2025-11-27 | Added missing package references to BuildX plugin (Configuration.EnvironmentVariables, DependencyInjection, Logging); refactored to use public AddSurfaceEnvironment API instead of internal SurfaceEnvironmentFactory; build passes. SCANNER-ENV-03 DONE. | Implementer |
| 2025-11-27 | Created SurfaceFeatureFlagsConfigurator to merge Surface.Env feature flags into WebService FeatureFlagOptions.Experimental dictionary; registered configurator in Program.cs. Cache roots and feature flags now wired from Surface.Env. SCANNER-ENV-02 DONE. | Implementer |
| 2025-11-27 | Verified SURFACE-ENV-03: Scanner Worker (SCANNER-ENV-01), WebService (SCANNER-ENV-02), and BuildX (SCANNER-ENV-03) all wire Surface.Env helpers; task complete. SURFACE-ENV-03 DONE. | Implementer |
| 2025-11-27 | Added CachingSurfaceSecretProvider (deterministic TTL cache), AuditingSurfaceSecretProvider (structured audit logging), and OfflineSurfaceSecretProvider (integrity-verified offline kit support); wired into ServiceCollectionExtensions with configurable options. SURFACE-SECRETS-03 DONE. | Implementer |
| 2025-11-27 | Added Surface.Validation project references to Zastava Observer and Webhook; wired AddSurfaceValidation() in service extensions for preflight checks. SURFACE-VAL-04 DONE. | Implementer |
| 2025-11-27 | Verified Zastava Observer and Webhook already have AddSurfaceEnvironment() wired with ZASTAVA prefixes; SURFACE-ENV-04 DONE. | Implementer |
| 2025-11-27 | Added Surface.Secrets project reference to BuildX plugin; implemented TryResolveAttestationToken() to fetch attestation secrets from Surface.Secrets; Worker/WebService already had configurators for CAS/registry/attestation secrets. SURFACE-SECRETS-04 DONE. | Implementer |
| 2025-11-27 | Verified Zastava Observer/Webhook already have ObserverSurfaceSecrets/WebhookSurfaceSecrets classes using ISurfaceSecretProvider for CAS and attestation secrets. SURFACE-SECRETS-05 DONE. | Implementer |
| 2025-11-27 | SURFACE-SECRETS-06 marked BLOCKED: requires Ops Guild input on Helm/Compose patterns for Surface.Secrets provider configuration (kubernetes/file/inline). Added to Decisions & Risks. | Implementer |
| 2025-11-27 | Integrated ISurfaceManifestWriter into SurfaceManifestStageExecutor to persist manifest documents to file-system store for offline/air-gapped scenarios; build verified. SURFACE-FS-03 DONE. | Implementer |
| 2025-11-27 | Added IRuntimeSurfaceFsClient injection to RuntimePostureEvaluator, enriching drift evidence with manifest digest/artifacts/metadata; added `zastava_surface_manifest_failures_total` metric with reason labels. SURFACE-FS-04 DONE. | Implementer |
| 2025-11-27 | Added TryResolveCasCredentials() to BuildX plugin using Surface.Secrets to fetch CAS access credentials; fixed attestation token resolution to use correct parser method. SCANNER-SECRETS-03 DONE. | Implementer |
| 2025-11-27 | Verified SurfacePointerService already exposes Surface.FS pointers (SurfaceManifestDocument, SurfaceManifestArtifact, manifest URI/digest) via reports endpoint. SURFACE-FS-05 DONE. | Implementer |
| 2025-11-27 | Added POST /policy/overlay endpoint for Cartographer integration: accepts graph nodes, returns deterministic overlays with sha256(tenant\|nodeId\|overlayKind) IDs, includes runtime evidence. Added PolicyOverlayRequestDto/ResponseDto contracts. SCANNER-GRAPH-21-001 DONE. | Implementer |
| 2025-11-27 | SCANNER-LNM-21-001 marked BLOCKED: Scanner WebService has no existing Concelier integration; requires HTTP client or shared library reference to Concelier.Core for linkset consumption. Added to Decisions & Risks. | Implementer |
| 2025-12-01 | EntryTrace NDJSON emission, runtime reconciliation, and WebService/CLI exposure completed (18-504/505/506). | EntryTrace Guild |
| 2025-12-01 | ZASTAVA-SURFACE-02: Observer resolves Surface manifest digests and `cas://` URIs, enriches drift evidence with artifact metadata, and counts failures via `zastava_surface_manifest_failures_total`. | Implementer |
| 2025-12-01 | SCANNER-SORT-02: ComponentGraphBuilder sorts layer fragments by digest; regression test added. | Implementer |
| 2025-12-01 | SCANNER-EMIT-15-001: CycloneDX artifacts now publish `ContentHash`, carry Merkle/recipe URIs, emit `_composition.json` + DSSE envelopes (recipe & layer.fragments), and Surface manifests reference those attestations. DSSE signer is pluggable (deterministic fallback registered); real signing still pending. | Implementer |
| 2025-12-01 | SCANNER-SORT-02 completed: ComponentGraphBuilder sorts layer fragments by digest with regression test Build_SortsLayersByDigest. | Implementer |
| 2025-12-01 | ZASTAVA-SURFACE-02: Observer now resolves Surface manifest digests and `cas://` URIs, enriches drift evidence with artifact metadata, and counts failures via `zastava_surface_manifest_failures_total`. | Implementer |
| 2025-11-23 | Published Security-approved Surface.Secrets schema (`docs/modules/scanner/design/surface-secrets-schema.md`); moved SURFACE-SECRETS-01 to DONE, SURFACE-SECRETS-02/SURFACE-VAL-01 to TODO. | Security Guild |
| 2025-11-23 | Implemented Surface.Secrets provider chain/fallback and added DI tests; marked SURFACE-SECRETS-02 DONE. | Scanner Guild |
| 2025-11-23 | Pinned Surface.Env package version `0.1.0-alpha.20251123` and offline path in `docs/modules/scanner/design/surface-env-release.md`; SCANNER-ENV-03 moved to TODO. | BuildX Plugin Guild |
| 2025-11-23 | Updated Surface.Validation doc to v1.1, binding to Surface.Env release and secrets schema; marked SURFACE-VAL-01 DONE. | Scanner Guild |
| 2025-11-23 | Strengthened Surface.Validation secrets checks (provider/fallback/inline/file root) and added unit tests; marked SURFACE-VAL-02 DONE. | Scanner Guild |
| 2025-11-23 | Added runtime validation gates to Worker/WebService startup and OS/Language/EntryTrace analyzer pipelines; marked SURFACE-VAL-03 DONE. | Scanner Guild |
| 2025-11-23 | Packed Surface.Env 0.1.0-alpha.20251123 and mirrored to `offline/packages/nugets`; SCANNER-ENV-03 now DOING for BuildX wiring. | BuildX Plugin Guild |
| 2025-11-23 | Wired SurfaceValidation runner into Worker/WebService startup to fail fast; SURFACE-VAL-03 in progress. | Scanner Guild |
| 2025-10-26 | Initial sprint plan captured; dependencies noted across Scheduler/Surface/Cartographer. | Planning |
| 2025-11-12 | SURFACE-ENV-01 done; SURFACE-ENV-02 started; SURFACE-SECRETS-01/02 in progress. | Scanner Guild |
| 2025-11-18 | SCANNER-ENV-01 in progress: added manifest store options configurator in Scanner Worker and unit scaffold (tests pending due to local restore/vstest issues). | Implementer |
| 2025-11-18 | SCANNER-ENV-02 started: wired Surface manifest store options into Scanner WebService and unit scaffold added; tests pending (nuget.org restore cancelled locally). | Implementer |
| 2025-11-18 | Attempted `dotnet test` for Worker Surface manifest configurator; restore failed fetching StackExchange.Redis from nuget.org (network timeout); tests still pending CI. | Implementer |
| 2025-11-18 | SCANNER-ENV-03 started: BuildX plugin now loads Surface.Env defaults (SCANNER/SURFACE prefixes) for cache root/bucket/tenant when args/env missing; tests not yet added. | Implementer |
| 2025-11-19 | Marked SCANNER-ENV-03, SURFACE-SECRETS-01/02, and SURFACE-VAL-01 BLOCKED pending Security/Surface schema approvals and published env/secrets artifacts; move back to TODO once upstream contracts land. | Implementer |
| 2025-11-28 | Created `docs/modules/scanner/guides/surface-validation-extensibility.md` covering custom validators, reporters, configuration, and testing; SURFACE-VAL-05 DONE. | Implementer |
| 2025-11-28 | Created `docs/modules/scanner/guides/surface-fs-workflow.md` with end-to-end workflow including artefact generation, storage layout, consumption, and offline kit handling; SURFACE-FS-06 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Homebrew` library with `HomebrewReceiptParser` (INSTALL_RECEIPT.json parsing), `HomebrewPackageAnalyzer` (Cellar discovery for Intel/Apple Silicon), and `HomebrewAnalyzerPlugin`; added `BuildHomebrew` PURL builder, `HomebrewCellar` evidence source; 23 tests passing. SCANNER-ENG-0020 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Pkgutil` library with `PkgutilReceiptParser` (plist parsing), `BomParser` (BOM file enumeration), `PkgutilPackageAnalyzer` (receipt discovery from /var/db/receipts), and `PkgutilAnalyzerPlugin`; added `BuildPkgutil` PURL builder, `PkgutilReceipt` evidence source; 9 tests passing. SCANNER-ENG-0021 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Windows.Msi` library with `MsiDatabaseParser` (OLE compound document parser), `MsiPackageAnalyzer` (Windows/Installer/*.msi discovery), and `MsiAnalyzerPlugin`; added `BuildWindowsMsi` PURL builder, `WindowsMsi` evidence source; 22 tests passing. SCANNER-ENG-0024 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Windows.WinSxS` library with `WinSxSManifestParser` (XML assembly identity parser), `WinSxSPackageAnalyzer` (WinSxS/Manifests/*.manifest discovery), and `WinSxSAnalyzerPlugin`; added `BuildWindowsWinSxS` PURL builder, `WindowsWinSxS` evidence source; 18 tests passing. SCANNER-ENG-0025 DONE. | Implementer |
| 2025-11-28 | Created `StellaOps.Scanner.Analyzers.OS.Windows.Chocolatey` library with `NuspecParser` (nuspec + directory name fallback), `ChocolateyPackageAnalyzer` (ProgramData/Chocolatey/lib discovery), and `ChocolateyAnalyzerPlugin`; added `BuildChocolatey` PURL builder, `WindowsChocolatey` evidence source; 44 tests passing. SCANNER-ENG-0026 DONE. | Implementer |
| 2025-11-28 | Updated `docs/modules/scanner/design/windows-analyzer.md` with implementation status section documenting MSI/WinSxS/Chocolatey collector details, PURL formats, and vendor metadata schemas; registry collector deferred, policy predicates pending Policy module integration. SCANNER-ENG-0027 DONE. | Implementer |

View File

@@ -1,31 +1,3 @@
# Sprint 165 - Export & Evidence · 160.C) TimelineIndexer # Legacy sprint file (redirect)
Active items only. Completed/historic work now resides in docs/implplan/archived/tasks.md (updated 2025-11-08). This sprint was renamed to `SPRINT_0165_0001_0001_timelineindexer.md` on 2025-11-19 to meet the standard filename template. Please consult the canonical file for all updates.
[Export & Evidence] 160.C) TimelineIndexer
Depends on: Sprint 110.A - AdvisoryAI, Sprint 120.A - AirGap, Sprint 130.A - Scanner, Sprint 150.A - Orchestrator
Summary: Export & Evidence focus on TimelineIndexer).
Task ID | State | Task description | Owners (Source)
--- | --- | --- | ---
TIMELINE-OBS-52-001 | TODO | Bootstrap `StellaOps.Timeline.Indexer` service with Postgres migrations for `timeline_events`, `timeline_event_details`, `timeline_event_digests`; enable RLS scaffolding and deterministic migration scripts. | Timeline Indexer Guild (src/TimelineIndexer/StellaOps.TimelineIndexer)
TIMELINE-OBS-52-002 | TODO | Implement event ingestion pipeline (NATS/Redis consumers) with ordering guarantees, dedupe on `(event_id, tenant_id)`, correlation to trace IDs, and backpressure metrics. Dependencies: TIMELINE-OBS-52-001. | Timeline Indexer Guild (src/TimelineIndexer/StellaOps.TimelineIndexer)
TIMELINE-OBS-52-003 | TODO | Expose REST/gRPC APIs for timeline queries (`GET /timeline`, `/timeline/{id}`) with filters, pagination, and tenant enforcement. Provide OpenAPI + contract tests. Dependencies: TIMELINE-OBS-52-002. | Timeline Indexer Guild (src/TimelineIndexer/StellaOps.TimelineIndexer)
TIMELINE-OBS-52-004 | TODO | Finalize RLS policies, scope checks (`timeline:read`), and audit logging for query access. Include integration tests for cross-tenant isolation and legal hold markers. Dependencies: TIMELINE-OBS-52-003. | Timeline Indexer Guild, Security Guild (src/TimelineIndexer/StellaOps.TimelineIndexer)
TIMELINE-OBS-53-001 | TODO | Link timeline events to evidence bundle digests + attestation subjects; expose `/timeline/{id}/evidence` endpoint returning signed manifest references. Dependencies: TIMELINE-OBS-52-004. | Timeline Indexer Guild, Evidence Locker Guild (src/TimelineIndexer/StellaOps.TimelineIndexer)
## Task snapshot (2025-11-12)
- Core service: `TIMELINE-OBS-52-001/002` cover Postgres migrations/RLS scaffolding and NATS/Redis ingestion with deterministic ordering + metrics.
- API surface: `TIMELINE-OBS-52-003/004` expose REST/gRPC query endpoints, RLS policies, audit logging, and legal-hold tests.
- Evidence linkage: `TIMELINE-OBS-53-001` joins timeline events to EvidenceLocker digests for `/timeline/{id}/evidence`.
## Dependencies & blockers
- Waiting on orchestrator + notifications schema (Wave 150/140) to finalize ingestion payload and event IDs.
- Requires EvidenceLocker bundle digest schema to link timeline entries to sealed manifests.
- Needs Scheduler/Orchestrator queue readiness for ingestion ordering semantics (impacting 52-002).
- Security/Compliance review required for Postgres RLS migrations before coding begins.
## Ready-to-start checklist
1. Obtain sample orchestrator capsule events + notifications once schema drops; attach to this doc for reference.
2. Draft Postgres migration + RLS design and share with Security/Compliance for approval.
3. Define ingestion ordering tests (NATS to Postgres) and expected metrics/alerts.
4. Align evidence linkage contract with EvidenceLocker (bundle IDs, DSSE references) prior to implementing `TIMELINE-OBS-53-001`.

View File

@@ -9,6 +9,7 @@ _Frozen v1 (add-only) — approved 2025-11-17 for CONCELIER-LNM-21-001/002/101._
## Status ## Status
- Frozen v1 as of 2025-11-17; further schema changes must go through ADR + sprint gating (CONCELIER-LNM-22x+). - Frozen v1 as of 2025-11-17; further schema changes must go through ADR + sprint gating (CONCELIER-LNM-22x+).
- Canonical JSON Schemas + signed manifest live in `docs/modules/concelier/schemas/` (advisory observation, linkset, offline bundle). Verify with `openssl dgst -sha256 -verify schema-signing-pub.pem -signature schema.manifest.sig schema.manifest.json`.
## Observation document (Mongo JSON Schema excerpt) ## Observation document (Mongo JSON Schema excerpt)
```json ```json

View File

@@ -0,0 +1,32 @@
# Concelier schema bundle (CI1CI10 remediation)
This folder publishes the signed JSON Schemas for Link-Not-Merge ingestion artifacts and the offline bundle manifest used by Offline Kit builds.
- `advisory-observation.schema.json` — canonical observation shape (provenance + content hash enforced).
- `advisory-linkset.schema.json` — linkset materialization with conflict reasons and deterministic IDs.
- `offline-advisory-bundle.schema.json` — manifest for air-gapped advisory bundles, including staleness and signature metadata.
- `schema.manifest.json` — digest manifest over all schemas.
- `schema.manifest.sig` — detached ECDSA (P-256) signature over the manifest (public key: `schema-signing-pub.pem`).
- `schema.manifest.sig.b64` — base64 view of the signature for air-gapped copy/paste.
- `samples/` — deterministic sample payloads for CI fixtures (see `tests` notes below).
## Verify locally (deterministic, offline)
```bash
# 1) Validate schemas are unchanged
sha256sum -c schema.manifest.json
# 2) Verify detached signature with the published public key
openssl dgst -sha256 -verify schema-signing-pub.pem \
-signature schema.manifest.sig \
schema.manifest.json
```
## Test coverage
The fixtures in `samples/` are consumed by `StellaOps.Concelier.Core.Tests` to assert:
- deterministic idempotency keys and conflict ordering (`Linksets/AdvisoryLinksetIdempotencyTests`),
- tenant normalization and signature requirements for observations (`Aoc/AdvisoryObservationWriteGuardTests`),
- offline bundle manifest validation (`Schemas/OfflineBundleSchemaTests`).
Keep the manifest and signature updated whenever schema files change. Keys are dev/test-only; production signing happens in the release pipeline.

View File

@@ -0,0 +1,85 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stellaops.local/concelier/schemas/advisory-linkset.schema.json",
"title": "Concelier Advisory Linkset",
"type": "object",
"additionalProperties": false,
"required": [
"linksetId",
"tenantId",
"advisoryId",
"source",
"observationIds",
"createdAt"
],
"properties": {
"linksetId": { "type": "string", "pattern": "^sha256:[A-Fa-f0-9]{64}$" },
"tenantId": { "type": "string", "minLength": 1 },
"source": { "type": "string", "minLength": 1 },
"advisoryId": { "type": "string", "minLength": 1 },
"observationIds": {
"type": "array",
"items": { "type": "string", "minLength": 1 },
"uniqueItems": true,
"minItems": 1
},
"normalized": {
"type": ["object", "null"],
"additionalProperties": true
},
"provenance": {
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"observationHashes": {
"type": "array",
"items": { "type": "string", "pattern": "^sha256:[A-Fa-f0-9]{64}$" },
"uniqueItems": true
},
"toolVersion": { "type": "string" },
"policyHash": { "type": "string" }
}
},
"confidence": { "type": ["number", "null"], "minimum": 0, "maximum": 1 },
"conflicts": {
"type": ["array", "null"],
"items": {
"type": "object",
"additionalProperties": false,
"required": ["field", "reason"],
"properties": {
"field": { "type": "string" },
"reason": {
"type": "string",
"enum": [
"severity-mismatch",
"affected-range-divergence",
"reference-clash",
"alias-inconsistency",
"metadata-gap",
"statement-conflict"
]
},
"sourceIds": {
"type": ["array", "null"],
"items": { "type": "string" },
"uniqueItems": true
}
}
}
},
"aliases": {
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"primary": { "type": "string" },
"others": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
}
},
"purls": { "type": ["array", "null"], "items": { "type": "string" }, "uniqueItems": true },
"cpes": { "type": ["array", "null"], "items": { "type": "string" }, "uniqueItems": true },
"createdAt": { "type": "string", "format": "date-time" },
"updatedAt": { "type": ["string", "null"], "format": "date-time" },
"builtByJobId": { "type": ["string", "null"] }
}
}

View File

@@ -0,0 +1,163 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stellaops.local/concelier/schemas/advisory-observation.schema.json",
"title": "Concelier Advisory Observation (Link-Not-Merge)",
"type": "object",
"additionalProperties": false,
"required": [
"observationId",
"tenant",
"source",
"upstream",
"content",
"linkset",
"rawLinkset",
"createdAt"
],
"properties": {
"observationId": { "type": "string", "minLength": 1 },
"tenant": { "type": "string", "minLength": 1, "pattern": "^[a-z0-9:-]+$" },
"source": {
"type": "object",
"additionalProperties": false,
"required": ["vendor", "stream", "api"],
"properties": {
"vendor": { "type": "string", "minLength": 1 },
"stream": { "type": "string", "minLength": 1 },
"api": { "type": "string", "format": "uri" },
"collectorVersion": { "type": "string" }
}
},
"upstream": {
"type": "object",
"additionalProperties": false,
"required": [
"upstreamId",
"fetchedAt",
"receivedAt",
"contentHash",
"signature"
],
"properties": {
"upstreamId": { "type": "string", "minLength": 1 },
"documentVersion": { "type": "string" },
"fetchedAt": { "type": "string", "format": "date-time" },
"receivedAt": { "type": "string", "format": "date-time" },
"contentHash": {
"type": "string",
"pattern": "^sha256:[A-Fa-f0-9]{64}$"
},
"signature": {
"type": "object",
"additionalProperties": false,
"required": ["present"],
"properties": {
"present": { "type": "boolean" },
"format": { "type": "string" },
"keyId": { "type": "string" },
"signature": { "type": "string" }
},
"allOf": [
{
"if": { "properties": { "present": { "const": true } } },
"then": {
"required": ["format", "keyId", "signature"],
"properties": {
"format": { "minLength": 1 },
"keyId": { "minLength": 1 },
"signature": { "minLength": 1 }
}
}
},
{
"if": { "properties": { "present": { "const": false } } },
"then": {
"properties": {
"format": { "maxLength": 0 },
"keyId": { "maxLength": 0 },
"signature": { "maxLength": 0 }
}
}
}
]
},
"metadata": {
"type": "object",
"additionalProperties": { "type": "string" },
"propertyNames": { "pattern": "^[A-Za-z0-9_.:-]+$" }
}
}
},
"content": {
"type": "object",
"additionalProperties": false,
"required": ["format", "raw"],
"properties": {
"format": { "type": "string", "minLength": 1 },
"specVersion": { "type": "string" },
"raw": { "type": ["object", "array"] },
"metadata": {
"type": "object",
"additionalProperties": { "type": "string" },
"propertyNames": { "pattern": "^[A-Za-z0-9_.:-]+$" }
}
}
},
"linkset": {
"type": "object",
"additionalProperties": false,
"properties": {
"aliases": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"purls": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"cpes": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"references": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["type", "url"],
"properties": {
"type": { "type": "string" },
"url": { "type": "string", "format": "uri" }
}
}
},
"reconciledFrom": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
}
},
"rawLinkset": {
"type": "object",
"additionalProperties": false,
"properties": {
"aliases": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
"packageUrls": { "type": "array", "items": { "type": "string" } },
"cpes": { "type": "array", "items": { "type": "string" } },
"references": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": { "type": "string" },
"url": { "type": "string" }
},
"required": ["type", "url"],
"additionalProperties": false
}
},
"relationships": { "type": "array", "items": { "type": "object" } },
"reconciledFrom": { "type": "array", "items": { "type": "string" } },
"scopes": { "type": "array", "items": { "type": "string" } },
"notes": {
"type": "object",
"additionalProperties": { "type": "string" }
}
}
},
"attributes": {
"type": "object",
"additionalProperties": { "type": "string" },
"propertyNames": { "pattern": "^[A-Za-z0-9_.:-]+$" }
},
"createdAt": { "type": "string", "format": "date-time" }
}
}

View File

@@ -0,0 +1,102 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stellaops.local/concelier/schemas/offline-advisory-bundle.schema.json",
"title": "Concelier Offline Advisory Bundle",
"type": "object",
"additionalProperties": false,
"required": [
"bundleId",
"tenant",
"exportKind",
"snapshot",
"manifest",
"hashes",
"signatures",
"createdAt"
],
"properties": {
"bundleId": { "type": "string", "pattern": "^bundle:[A-Za-z0-9._:-]+$" },
"tenant": { "type": "string", "minLength": 1 },
"exportKind": { "type": "string", "enum": ["json", "trivydb"] },
"createdAt": { "type": "string", "format": "date-time" },
"snapshot": {
"type": "object",
"additionalProperties": false,
"required": ["windowStart", "windowEnd", "sources"],
"properties": {
"windowStart": { "type": "string", "format": "date-time" },
"windowEnd": { "type": "string", "format": "date-time" },
"stalenessHours": { "type": "integer", "minimum": 0 },
"sources": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "cursor", "hash"],
"additionalProperties": false,
"properties": {
"name": { "type": "string" },
"cursor": { "type": "string" },
"hash": { "type": "string", "pattern": "^sha256:[A-Fa-f0-9]{64}$" },
"snapshotUri": { "type": "string", "format": "uri" }
}
},
"uniqueItems": true
}
}
},
"manifest": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["path", "sha256", "size"],
"properties": {
"path": { "type": "string" },
"sha256": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$" },
"size": { "type": "integer", "minimum": 0 },
"contentType": { "type": "string" }
}
},
"uniqueItems": true
},
"hashes": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^sha256$": { "type": "string", "pattern": "^[A-Fa-f0-9]{64}$" }
}
},
"signatures": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["type", "keyId", "signature"],
"properties": {
"type": { "type": "string", "enum": ["dsse-inline", "detached"] },
"keyId": { "type": "string" },
"signature": { "type": "string" },
"envelopeDigest": { "type": "string", "pattern": "^sha256:[A-Fa-f0-9]{64}$" },
"rekor": {
"type": "object",
"additionalProperties": false,
"properties": {
"logIndex": { "type": "integer", "minimum": 0 },
"uuid": { "type": "string" },
"integratedTime": { "type": "integer", "minimum": 0 }
}
}
}
}
},
"determinism": {
"type": "object",
"additionalProperties": false,
"properties": {
"contentHash": { "type": "string", "pattern": "^sha256:[A-Fa-f0-9]{64}$" },
"idempotencyKey": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
"canonVersion": { "type": "string", "default": "1" }
}
}
}
}

View File

@@ -0,0 +1,55 @@
{
"$schema": "../offline-advisory-bundle.schema.json",
"bundleId": "bundle:concelier:offline:2025-12-02T00-00Z",
"tenant": "default",
"exportKind": "json",
"createdAt": "2025-12-02T00:00:00Z",
"snapshot": {
"windowStart": "2025-11-25T00:00:00Z",
"windowEnd": "2025-12-01T23:59:59Z",
"stalenessHours": 168,
"sources": [
{
"name": "osv",
"cursor": "2025-12-01T23:50:00Z",
"hash": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd",
"snapshotUri": "https://mirror.example/offline/osv-2025-12-01.zip"
},
{
"name": "redhat",
"cursor": "2025-12-01T23:45:00Z",
"hash": "sha256:abcd456789abcdef0123456789abcdef0123456789abcdef0123456789abcd"
}
]
},
"manifest": [
{
"path": "export/index.json",
"sha256": "89abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567",
"size": 482192,
"contentType": "application/json"
},
{
"path": "export/db/trivy.db",
"sha256": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
"size": 1289932,
"contentType": "application/octet-stream"
}
],
"hashes": {
"sha256": "0f0e0d0c0b0a09080706050403020100ffeeddccbbaa99887766554433221100"
},
"signatures": [
{
"type": "dsse-inline",
"keyId": "schema-offline-pub",
"signature": "MEUCIQDkexampleSignedDigestx+deterministicSig==",
"envelopeDigest": "sha256:aa55aa55aa55aa55aa55aa55aa55aa55aa55aa55aa55aa55aa55aa55aa55aa55"
}
],
"determinism": {
"contentHash": "sha256:d3c3f6c75c6a3f0906bcee457cc77a2d6d7c0f9d1a1d7da78c0d2ab8e0dba111",
"idempotencyKey": "29d58b9fdc5c4e65b26c03f3bd9f442ff0c7f8514b8a9225f8b6417ffabc0101",
"canonVersion": "1"
}
}

View File

@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyi7gVscxgRXQzX5ErNuQFN3dPjVw
YzU0JE3PGhjSinBwpODxtweLfP6zw2N6f0H9z25t8HwTpFeuk1PWqTX7Gg==
-----END PUBLIC KEY-----

View File

@@ -0,0 +1,22 @@
{
"version": 1,
"generatedAt": "2025-12-02T00:00:00Z",
"files": [
{
"path": "advisory-observation.schema.json",
"sha256": "e3f40aea09794f72f2722c46657377518489e2ca7e3122cfbb65655c3296c083"
},
{
"path": "advisory-linkset.schema.json",
"sha256": "e3b40a0cca5aff85be2fbc5af9a96f00f5b7a20f6740a3f32947fae56bd599e5"
},
{
"path": "offline-advisory-bundle.schema.json",
"sha256": "9b64af7c2e5fa0c071af7dc04b7984fd1787b4f9e2082cb47174610097e2dc51"
},
{
"path": "samples/offline-advisory-bundle.sample.json",
"sha256": "15874bbafe5b2ead5ec9a853c32d715a4b48d41107ff2887d6ccdc222e462f45"
}
]
}

Binary file not shown.

View File

@@ -0,0 +1,2 @@
MEUCIBDcyrpqWmYQUrkWLTwMs6QyG2YWCFTxte10/7TobThlAiEAvqOSESmIxNFQ6pDtHlhpfL1K
1SZrDM+PhdAMSOMwoU4=

View File

@@ -2,3 +2,9 @@
# Findings Ledger # Findings Ledger
Start here for ledger docs. Start here for ledger docs.
## Quick links
- FL1FL10 remediation tracker: `gaps-FL1-FL10.md`
- Schema catalog (events/projections/exports): `schema-catalog.md`
- Merkle & external anchor policy: `merkle-anchor-policy.md`
- Tenant isolation & redaction manifest: `tenant-isolation-redaction.md`

View File

@@ -0,0 +1,26 @@
# DSSE & Policy Hash Linkage (FL6)
**Goal:** Every export, replay report, and anchor manifest is tied to the exact policy digest that produced it and is verifiable offline via DSSE.
## Binding rules
1. **Policy digest:** `policyVersion` (SHA-256 over policy bundle) is mandatory in ledger events, projections, exports, and replay reports.
2. **DSSE payload types**
- `application/vnd.stella-ledger-export+json` — export manifests (hashlist + filtersHash).
- `application/vnd.stella-ledger-anchor+json` — Merkle anchors (see `merkle-anchor-policy.md`).
- `application/vnd.stella-ledger-harness+json` — replay harness report.
3. **Hashlists:** export manifests contain `sha256` for each emitted NDJSON line (`lineDigest`), plus a dataset digest (`datasetSha256`) over concatenated line digests. Replay harness exposes `eventStreamChecksum` and `projectionChecksum`.
4. **Policy linkage:** DSSE payload must include `policyHash` and `schemaVersion` to prevent replay under mismatched policy versions.
## Offline verification flow
1. Verify DSSE signature (local key or Rekor transparency log if online).
2. Recompute dataset checksum with `tools/LedgerReplayHarness/scripts/verify_export.py --input <export.ndjson> --expected <datasetSha256>`.
3. Cross-check `policyHash` in payload matches policy bundle in use; mismatch → block import/export.
## File locations
- Harness DSSE placeholder now embeds `policyHash` when `LEDGER_POLICY_HASH` env var is set.
- Export manifests and checksums: `docs/modules/findings-ledger/golden-checksums.json`.
- External anchors: `docs/modules/findings-ledger/merkle-anchor-policy.md` (DSSE template).
- Set `LEDGER_POLICY_HASH` before running `tools/LedgerReplayHarness` to imprint the policy digest into the generated `.sig` file.
## Change management
- Any change to payloadType or hash recipe bumps schema version in `schema-catalog.md` and requires new DSSE key roll announcement.

View File

@@ -0,0 +1,28 @@
# Findings Ledger — FL1FL10 Remediation (LEDGER-GAPS-121-009)
**Source advisory:** `docs/product-advisories/archived/27-Nov-2025-superseded/28-Nov-2025 - Findings Ledger and Immutable Audit Trail.md`
**Created:** 2025-12-02 · **Owner:** Findings Ledger Guild
## Gap closure map
| ID | Gap summary | Remediation artefact(s) | Evidence / notes |
| --- | ----------- | ----------------------- | ---------------- |
| FL1 | Versioned ledger event schema (canonical JSON + hashes) | `docs/modules/findings-ledger/schema-catalog.md` §1; updated `docs/modules/findings-ledger/schema.md` canonical rules | Canonical envelope `v1.0.0` stamped; hash derivation pinned to `sha256(canonicalJson)` + `sha256(eventHash-sequence)`. |
| FL2 | Projection schema versions + cycle hash determinism | `schema-catalog.md` §2; `schema.md` §4 | Projection `v1.0.0` with cycle-hash recipe and required fields; rebuild checksum guard in harness. |
| FL3 | Export schema (canonical/compact) + filter hash versioning | `schema-catalog.md` §3; golden fixtures under `src/Findings/StellaOps.Findings.Ledger/fixtures/golden/` | Canonical export shape tagged `export.v1.canonical`; compact tagged `export.v1.compact`; fixtures hashed. |
| FL4 | Merkle + external anchor policy (Rekor/offline) | `docs/modules/findings-ledger/merkle-anchor-policy.md` | Anchoring cadence (1k/15m), Rekor/air-gap policy, anchor ref format, DSSE anchoring manifest. |
| FL5 | Tenant isolation + redaction manifest for exports/logs | `docs/modules/findings-ledger/tenant-isolation-redaction.md`; manifest: `docs/modules/findings-ledger/redaction-manifest.yaml` | Per-tenant partitions, export field redaction (comments, actor ids), signed manifest checksum. |
| FL6 | DSSE + policy hash linkage for exports and attestations | `docs/modules/findings-ledger/dsse-policy-linkage.md`; harness DSSE placeholder includes `policyHash` | Describes payloadType + bindings to policy digest and export hashlist. |
| FL7 | Deterministic export fixtures (golden) | `fixtures/golden/*.ndjson` (findings, vex, advisories, sboms) | Each includes `filtersHash`, `cycleHash`, `policyVersion`; hashes logged in manifest. |
| FL8 | Offline verifier script for bundles/exports | `tools/LedgerReplayHarness/scripts/verify_export.py` | Pure-Python, no deps; validates ordering, recomputes SHA-256 and optional expected hash file. |
| FL9 | Replay/rebuild checksum guard | Harness update: `tools/LedgerReplayHarness/Program.cs` (`--expected-checksum`) | Computes event-stream and projection checksums; fails on mismatch; emitted in report. |
| FL10 | Quotas/backpressure metrics and alerts | Metrics update: `Observability/LedgerMetrics.cs`; doc: `observability.md` §2/§4 | New counters `ledger_backpressure_applied_total`, gauge `ledger_quota_remaining`, alert guidance. |
## How to verify
- Run `dotnet run --project tools/LedgerReplayHarness -- --fixture <path> --connection <conn> --tenant <tenant> --report out/report.json --metrics out/metrics.json --expected-checksum <baseline-checksums.json>` (use a file produced by a known-good run; template: `docs/modules/findings-ledger/replay-checksums.sample.json`).
- Validate exports: `python tools/LedgerReplayHarness/scripts/verify_export.py --input fixtures/golden/findings-canonical.ndjson --schema export.v1.canonical`.
- Check manifest hashes: `sha256sum docs/modules/findings-ledger/redaction-manifest.yaml fixtures/golden/*.ndjson`.
## Follow-ons
- Integrate Rekor anchor publishing toggle into Helm/Compose overlays (tracked separately).
- Mirror golden fixtures into Offline Kit once export pipeline emits real data.

View File

@@ -0,0 +1,53 @@
{
"generatedAt": "2025-12-02T00:00:00Z",
"policyHash": "sha256:policy-v1",
"datasets": {
"findings-canonical.ndjson": {
"path": "src/Findings/StellaOps.Findings.Ledger/fixtures/golden/findings-canonical.ndjson",
"schema": "export.v1.canonical",
"records": 2,
"filtersHash": "a81d6c6d2bcf9c0e7cbb1fcd292e4b7cc21f6d5c4e3f2b1a0c9d8e7f6c5b4a3e",
"sha256": "cd270235484748f2f4c871e9d574796e6f61b48df9cc65e009dab4ba0769dfa4"
},
"vex-compact.ndjson": {
"path": "src/Findings/StellaOps.Findings.Ledger/fixtures/golden/vex-compact.ndjson",
"schema": "export.v1.compact",
"records": 1,
"filtersHash": "b5c6d7e8f9a0b1c2d3e4f50617283940aa5544332211ffeeccbb998877665544",
"sha256": "e786a12b4ee08776df73f7f2a97907280b5f8bb76cc7a901e2a680d3fe69e85e"
},
"advisories-canonical.ndjson": {
"path": "src/Findings/StellaOps.Findings.Ledger/fixtures/golden/advisories-canonical.ndjson",
"schema": "export.v1.canonical",
"records": 1,
"filtersHash": "c6d7e8f9a0b1c2d3e4f50617283940aa5544332211ffeeccbb99887766554433",
"sha256": "6d5a2d522179b616c112c255c7dd06b3434ae0a4992009d25ea82f50144425ab"
},
"sboms-compact.ndjson": {
"path": "src/Findings/StellaOps.Findings.Ledger/fixtures/golden/sboms-compact.ndjson",
"schema": "export.v1.compact",
"records": 1,
"filtersHash": "d7e8f9a0b1c2d3e4f50617283940aa5544332211ffeeccbb9988776655443322",
"sha256": "c89be7fcc511c4ef5a4a291c45061da1a7f4592506150e5b9bce92ba2bb5bbe2"
}
},
"manifests": {
"redaction-manifest.yaml": {
"path": "docs/modules/findings-ledger/redaction-manifest.yaml",
"schema": "redaction.v1",
"sha256": "7c2f437a47c6514ad4688072b8b5e33b2e0cd0f9f289f15b49bf2f7def54a730"
},
"redaction-manifest.json": {
"path": "docs/modules/findings-ledger/redaction-manifest.json",
"schema": "redaction.v1",
"sha256": "6965ea311f65482e6f51da0fd26cae1995997fcd456cea6dac84ab7b3354990a"
}
},
"replay": {
"sample": {
"path": "docs/modules/findings-ledger/replay-checksums.sample.json",
"schema": "ledger.harness.v1",
"note": "replace with harness-produced checksums before enforcement"
}
}
}

View File

@@ -0,0 +1,50 @@
# Merkle & External Anchor Policy (FL4)
**Audience:** Findings Ledger Guild · DevOps · Compliance
**Applies to:** `src/Findings/StellaOps.Findings.Ledger` (Merkle worker, anchoring jobs)
## Anchoring cadence
- **Batch size:** 1,000 events or **15 minutes**, whichever is first (`LedgerServiceOptions:Merkle.BatchSize/WindowDuration`).
- **Tree:** flat Merkle over `merkle_leaf_hash` (see `schema-catalog.md` §1). Root hashed with SHA-256; no salt.
- **Partitions:** per-tenant batching only; no cross-tenant mixing.
- **Ordering:** leaves ordered by `(sequence_no, recorded_at)`. Any deviation is a failure.
## Anchor references
- `ledger_merkle_roots.anchor_reference` formats:
- `rekor::<uuid>` when pushed to Rekor.
- `airgap::<bundleId>` when sealed in offline bundle.
- `none` (empty) for internal-only anchors.
- External publication is optional but **must** include DSSE envelope with payload:
```json
{
"payloadType": "application/vnd.stella-ledger-anchor+json",
"payload": {
"tenant": "<tenant>",
"rootHash": "<sha256>",
"leafCount": 1000,
"windowStart": "2025-12-02T00:00:00Z",
"windowEnd": "2025-12-02T00:15:00Z",
"policyHash": "<policyVersion>",
"schemaVersion": "ledger.event.v1"
},
"signatures": [...]
}
```
## Determinism & recovery
- Anchor worker enforces stable ordering; replay harness recomputes Merkle roots and fails when root mismatch (FL9 guard).
- Root hash + DSSE signature are stored alongside export bundles for offline verification.
- External anchors **never** include tenant-identifying data beyond tenant id already present in ledger tables.
## Air-gap posture
- Rekor publication optional; when disabled, anchors are sealed inside offline bundles with `anchor_reference=airgap::<bundleId>`.
- Anchor manifest is bundled in Offline Kit under `offline/ledger/anchors/<tenant>/<anchorId>.json`.
- No outbound network calls when `ExternalAnchoring:Enabled=false`.
## Monitoring
- Metrics: `ledger_merkle_anchor_duration_seconds`, `ledger_merkle_anchor_failures_total`, `ledger_backpressure_applied_total{reason="anchoring"}`, `ledger_quota_remaining{kind="ingest"}`.
- Alerts: see `observability.md` (AnchorFailure + new Backpressure alert).
## Change control
- Any change to batch size/window or hash recipe requires bumping `ledger.event` schema minor version and updating `schema-catalog.md`.

View File

@@ -14,7 +14,10 @@
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `ledger_write_duration_seconds` | Histogram | `tenant`, `event_type`, `source` | End-to-end append latency (API ingress → persisted). P95 ≤120ms. | | `ledger_write_duration_seconds` | Histogram | `tenant`, `event_type`, `source` | End-to-end append latency (API ingress → persisted). P95 ≤120ms. |
| `ledger_events_total` | Counter | `tenant`, `event_type`, `source` (`policy`, `workflow`, `orchestrator`) | Incremented per committed event. Mirrors Merkle leaf count. | | `ledger_events_total` | Counter | `tenant`, `event_type`, `source` (`policy`, `workflow`, `orchestrator`) | Incremented per committed event. Mirrors Merkle leaf count. |
| `ledger_ingest_backlog_events` | Gauge | | Number of events buffered in the writer/anchor queues. Alert when >5000 for 5min. | | `ledger_ingest_backlog_events` | Gauge | `tenant` | Number of events buffered in the writer/anchor queues. Alert when >5000 for 5min. |
| `ledger_quota_remaining` | Gauge | `tenant` | Remaining ingest capacity before backpressure applies (defaults to 5000 events). |
| `ledger_backpressure_applied_total` | Counter | `tenant`, `reason`, `limit` | Incremented whenever backlog crosses quota threshold. |
| `ledger_quota_rejections_total` | Counter | `tenant`, `reason` | Incremented when requests are actively rejected due to quotas. |
| `ledger_projection_lag_seconds` | Gauge | `tenant` | Wall-clock difference between latest ledger event and projection tail. Target <30s. | | `ledger_projection_lag_seconds` | Gauge | `tenant` | Wall-clock difference between latest ledger event and projection tail. Target <30s. |
| `ledger_projection_rebuild_seconds` | Histogram | `tenant` | Duration of replay/rebuild operations triggered by LEDGER-29-008 harness. | | `ledger_projection_rebuild_seconds` | Histogram | `tenant` | Duration of replay/rebuild operations triggered by LEDGER-29-008 harness. |
| `ledger_projection_apply_seconds` | Histogram | `tenant`, `event_type`, `policy_version`, `evaluation_status` | Time to apply a single ledger event to projection. Target P95 <1s. | | `ledger_projection_apply_seconds` | Histogram | `tenant`, `event_type`, `policy_version`, `evaluation_status` | Time to apply a single ledger event to projection. Target P95 <1s. |
@@ -43,6 +46,7 @@
| --- | --- | --- | | --- | --- | --- |
| **LedgerWriteSLA** | `ledger_write_latency_seconds` P95 > 1s for 3 intervals | Check DB contention, review queue backlog, scale writer. | | **LedgerWriteSLA** | `ledger_write_latency_seconds` P95 > 1s for 3 intervals | Check DB contention, review queue backlog, scale writer. |
| **LedgerBacklogGrowing** | `ledger_ingest_backlog_events` > 5000 for 5min | Inspect upstream policy runs, ensure projector keeping up. | | **LedgerBacklogGrowing** | `ledger_ingest_backlog_events` > 5000 for 5min | Inspect upstream policy runs, ensure projector keeping up. |
| **LedgerBackpressure** | `ledger_backpressure_applied_total` increases while `ledger_quota_remaining` < 0 | Throttle callers, raise quota or scale anchor worker. |
| **ProjectionLag** | `ledger_projection_lag_seconds` > 30s | Trigger rebuild, verify change streams. | | **ProjectionLag** | `ledger_projection_lag_seconds` > 30s | Trigger rebuild, verify change streams. |
| **AnchorFailure** | `ledger_merkle_anchor_failures_total` increase > 0 | Collect logs, rerun anchor, verify signing service. | | **AnchorFailure** | `ledger_merkle_anchor_failures_total` increase > 0 | Collect logs, rerun anchor, verify signing service. |
| **AttachmentSecurityError** | `ledger_attachments_encryption_failures_total` increase > 0 | Audit attachments pipeline; check key material and storage endpoints. | | **AttachmentSecurityError** | `ledger_attachments_encryption_failures_total` increase > 0 | Audit attachments pipeline; check key material and storage endpoints. |

View File

@@ -0,0 +1,29 @@
{
"schemaVersion": "redaction.v1",
"generatedAt": "2025-12-02T00:00:00Z",
"owner": "findings-ledger-guild",
"rules": {
"ledger.event": [
{ "path": "$.actor.id", "action": "mask", "maskWith": "user:<realm>" },
{ "path": "$.payload.comment", "action": "drop" },
{ "path": "$.payload.ticket.url", "action": "drop" },
{ "path": "$.payload.attachments[*].downloadUrl", "action": "drop" }
],
"export.canonical": [
{ "path": "$.actorId", "action": "mask", "maskWith": "user:<realm>" },
{ "path": "$.comment", "action": "drop" },
{ "path": "$.attachments", "action": "drop" }
],
"export.compact": [
{ "path": "$.actorId", "action": "drop" },
{ "path": "$.comment", "action": "drop" },
{ "path": "$.policyRationale", "action": "drop" },
{ "path": "$.attachments", "action": "drop" },
{ "path": "$.labels", "action": "drop" }
],
"observability": [
{ "path": "$.event_body", "action": "drop" },
{ "path": "$.actor_id", "action": "hash", "hashWith": "sha256" }
]
}
}

View File

@@ -0,0 +1,39 @@
schemaVersion: redaction.v1
generatedAt: 2025-12-02T00:00:00Z
owner: findings-ledger-guild
rules:
ledger.event:
- path: $.actor.id
action: mask
maskWith: "user:<realm>"
- path: $.payload.comment
action: drop
- path: $.payload.ticket.url
action: drop
- path: $.payload.attachments[*].downloadUrl
action: drop
export.canonical:
- path: $.actorId
action: mask
maskWith: "user:<realm>"
- path: $.comment
action: drop
- path: $.attachments
action: drop
export.compact:
- path: $.actorId
action: drop
- path: $.comment
action: drop
- path: $.policyRationale
action: drop
- path: $.attachments
action: drop
- path: $.labels
action: drop
observability:
- path: $.event_body
action: drop
- path: $.actor_id
action: hash
hashWith: sha256

View File

@@ -0,0 +1,5 @@
{
"eventStream": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"projection": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"notes": "Replace with real values from harness output before enforcing checksum guard."
}

View File

@@ -0,0 +1,75 @@
# Findings Ledger Schema Catalog (FL1FL3)
**Scope:** Versioned canonical schemas for ledger events, projections, and exports.
**Status:** v1.0.0 sealed (2025-12-02) — breaking changes require new minor/major version tags.
## 1) Ledger event envelope — `ledger.event.v1`
| Field | Type | Notes |
| --- | --- | --- |
| `event.id` | `uuid` | V7 GUID allowed. |
| `event.type` | `string` (`ledger_event_type`) | See `schema.md` §2.2. |
| `event.tenant` | `string` | Partition key. |
| `event.chainId` | `uuid` | Derived when absent (`tenant :: policyVersion`), see `workflow-inference.md`. |
| `event.sequence` | `long` | Gapless per chain, starts at 1. |
| `event.policyVersion` | `string` | SHA-256 digest of policy bundle; propagated into exports and DSSE. |
| `event.finding` | object | `id`, `artifactId`, `vulnId`. |
| `event.actor` | object | `id`, `type` (`system|operator|integration`). |
| `event.occurredAt` | `string` (UTC ISO-8601 ms) | Domain clock. |
| `event.recordedAt` | `string` (UTC ISO-8601 ms) | Service `TimeProvider`. |
| `event.payload` | object | Mutation-specific body. |
| `event.evidenceBundleRef` | `string?` | DSSE/capsule id (optional). |
| `event.airgap.bundle` | object? | See `airgap-provenance.md`. |
| `event_hash` | `char(64)` | `sha256(canonicalJson)` lower-hex. |
| `previous_hash` | `char(64)` | All-zero for chain genesis. |
| `merkle_leaf_hash` | `char(64)` | `sha256(event_hash || "-" || sequence)`. |
Canonicalisation: UTF-8, sorted keys, lower-case enums, ISO-8601 UTC with millisecond precision, arrays stable-order. Any field addition bumps minor version.
## 2) Finding projection — `ledger.projection.v1`
| Field | Type | Notes |
| --- | --- | --- |
| `tenantId` | `string` | Partition key. |
| `findingId` | `string` | Stable identity. |
| `policyVersion` | `string` | Hash of active policy bundle. |
| `status` | `string` | `affected|triaged|accepted_risk|resolved|unknown`. |
| `severity` | `number` | 010, 3 decimal places. |
| `riskScore` | `number` | 010, 3 decimal places. |
| `riskSeverity` | `string` | `low|medium|high|critical|unknown`. |
| `riskProfileVersion` | `string` | Version/hash from Risk Engine. |
| `riskExplanationId` | `uuid?` | Links to explain bundle. |
| `labels` | `json` | KEV/runtime flags, sorted keys. |
| `currentEventId` | `uuid` | Source ledger event. |
| `explainRef` | `string?` | Object storage / DSSE reference. |
| `policyRationale` | `json` | Array of rationale refs. |
| `updatedAt` | `string` UTC | Projection timestamp. |
| `cycleHash` | `char(64)` | `sha256(canonicalProjectionJson)`; used in exports. |
Projection deterministic hash recipe: serialize projection record with sorted keys (excluding `updatedAt` jitter) and hash via SHA-256. The replay harness recomputes and compares.
## 3) Export payloads — `export.v1`
Shapes share headers: `policyVersion`, `projectionVersion` (cycle hash), `filtersHash`, `pageToken`, `observedAt`, `provenance` (`ledgerRoot`, `projectorVersion`, `policyHash`, optional `dsseDigest`).
### Canonical vs compact
- **Canonical (`export.v1.canonical`)** — full provenance fields, evidence refs, DSSE linkage.
- **Compact (`export.v1.compact`)** — drops verbose fields (`policyRationale`, comments, actor ids), keeps `cycleHash` + `filtersHash` for determinism; redaction manifest enforced.
### Record fields
- Findings: `findingId`, `eventSequence`, `status`, `severity`, `risk`, `advisories[]`, `evidenceBundleRef`, `cycleHash`.
- VEX: `vexStatementId`, `product`, `status`, `justification`, `knownExploited`, `cycleHash`.
- Advisories: `advisoryId`, `source`, `cvss{version,vector,baseScore}`, `epss`, `kev`, `cycleHash`.
- SBOMs: `sbomId`, `subject{digest,mediaType}`, `sbomFormat`, `componentsCount`, `materials[]`, `cycleHash`.
Filters hash: `sha256(sortedQueryString)`; stored alongside fixtures for replayability.
## 4) Versioning rules
- Patch: backward-compatible field additions (new optional key) — bump patch digit.
- Minor: additive required fields or canonical rule tweaks — bump minor.
- Major: breaking change (field removal/rename, hash recipe) — bump major and keep prior schema frozen.
## 5) Reference artefacts
- Golden fixtures: `src/Findings/StellaOps.Findings.Ledger/fixtures/golden/*.ndjson`.
- Checksum manifest: `docs/modules/findings-ledger/golden-checksums.json`.
- Offline verifier: `tools/LedgerReplayHarness/scripts/verify_export.py`.

View File

@@ -119,6 +119,11 @@ Canonicalisation rules:
5. Numbers use decimal notation; omit trailing zeros. 5. Numbers use decimal notation; omit trailing zeros.
6. Arrays maintain supplied order. 6. Arrays maintain supplied order.
### 2.4 Versioning & DSSE linkage (FL1, FL6)
- Canonical schema identifiers are catalogued in `schema-catalog.md` (`ledger.event.v1`, `ledger.projection.v1`, `export.v1.*`).
- Any change to the envelope, hash recipe, or required fields bumps the catalog version; legacy versions remain frozen.
- DSSE artefacts (anchors, exports, replay reports) **must** embed `policyVersion` and `schemaVersion` (see `dsse-policy-linkage.md`).
Hash pipeline: Hash pipeline:
``` ```
@@ -270,7 +275,7 @@ Ordering and pagination: `ORDER BY recorded_at ASC, attestation_id ASC` with cur
1. Canonical serialize the envelope (§2.3). 1. Canonical serialize the envelope (§2.3).
2. Compute `event_hash` and store along with `previous_hash`. 2. Compute `event_hash` and store along with `previous_hash`.
3. Build Merkle tree per anchoring window using leaf hash `SHA256(event_hash || '-' || sequence_no)`. 3. Build Merkle tree per anchoring window using leaf hash `SHA256(event_hash || '-' || sequence_no)`.
4. Persist root in `ledger_merkle_roots` and, when configured, submit to external transparency log (Rekor v2). Store receipt/UUID in `anchor_reference`. 4. Persist root in `ledger_merkle_roots` and, when configured, submit to external transparency log (Rekor v2). Store receipt/UUID in `anchor_reference` (see `merkle-anchor-policy.md`).
5. Projection rows compute `cycle_hash = SHA256(canonical_projection_json)` where canonical projection includes fields `{tenant_id, finding_id, policy_version, status, severity, labels, current_event_id}` with sorted keys. 5. Projection rows compute `cycle_hash = SHA256(canonical_projection_json)` where canonical projection includes fields `{tenant_id, finding_id, policy_version, status, severity, labels, current_event_id}` with sorted keys.
Verification flow for auditors: Verification flow for auditors:
@@ -284,6 +289,8 @@ Verification flow for auditors:
- Initial migration script: `src/Findings/StellaOps.Findings.Ledger/migrations/001_initial.sql`. - Initial migration script: `src/Findings/StellaOps.Findings.Ledger/migrations/001_initial.sql`.
- Sample canonical event: `seed-data/findings-ledger/fixtures/ledger-event.sample.json` (includes pre-computed `eventHash`, `previousHash`, and `merkleLeafHash` values). - Sample canonical event: `seed-data/findings-ledger/fixtures/ledger-event.sample.json` (includes pre-computed `eventHash`, `previousHash`, and `merkleLeafHash` values).
- Sample projection row: `seed-data/findings-ledger/fixtures/finding-projection.sample.json` (includes canonical `cycleHash` for replay validation). - Sample projection row: `seed-data/findings-ledger/fixtures/finding-projection.sample.json` (includes canonical `cycleHash` for replay validation).
- Golden export fixtures (FL7): `src/Findings/StellaOps.Findings.Ledger/fixtures/golden/*.ndjson` with checksums in `docs/modules/findings-ledger/golden-checksums.json`.
- Redaction manifest (FL5): `docs/modules/findings-ledger/redaction-manifest.yaml` governs mask/drop rules for canonical vs compact exports.
Fixtures follow canonical key ordering and include precomputed hashes to validate tooling. Fixtures follow canonical key ordering and include precomputed hashes to validate tooling.

View File

@@ -0,0 +1,28 @@
# Tenant Isolation & Redaction Manifest (FL5)
**Purpose:** Document how Findings Ledger enforces tenant boundaries and which fields are redacted in deterministic exports.
## Isolation controls
- Storage: all ledger, projection, history, and merkle tables are **LIST-partitioned by `tenant_id`** (PostgreSQL). Cross-tenant queries are disallowed at repo level.
- Queueing: Merkle batches and projector pipelines are keyed by `(tenant_id, chain_id)`; no mixing.
- Exports: `/ledger/export/*` requires `X-Stella-Tenant`; service rejects multi-tenant requests.
- Hashing: event/projection hashes include `tenant_id` as part of canonical envelope, preventing replay across tenants.
## Redaction policy
- User-generated content (comments, attachments metadata) is excluded from compact exports and masked in canonical exports per manifest.
- Actor identifiers are truncated to realm (`user:<realm>`); emails/PII never emitted.
- Evidence bundle references are retained, but inline evidence payloads are not stored in ledger.
## Manifest
- Path: `docs/modules/findings-ledger/redaction-manifest.yaml` (JSON twin: `redaction-manifest.json` for offline tooling).
- Content: declarative list of fields redacted or truncated for each export shape.
- The manifest is signed in checksum list `docs/modules/findings-ledger/golden-checksums.json`; sha256 must match before release.
### Applying the manifest
- Canonical exports apply `redact: mask` rules only to PII (`actorId`, `comment`); compact exports drop (`drop: true`) the same fields plus verbose rationale arrays.
- Log pipelines ensure `event_body` is never written to logs; only metadata/hashes appear (see `observability.md`).
## Validation steps
1. `sha256sum docs/modules/findings-ledger/redaction-manifest.yaml` matches `golden-checksums.json`.
2. Run `python tools/LedgerReplayHarness/scripts/verify_export.py --input fixtures/golden/findings-canonical.ndjson --schema export.v1.canonical --manifest docs/modules/findings-ledger/redaction-manifest.json` (script enforces mask/drop rules offline).
3. Confirm export responses in staging omit masked fields for the requesting tenant.

View File

@@ -12,6 +12,7 @@ Applies to `mirror-thin-v1.*` artefacts in `out/mirror/thin/`.
- Payload: `mirror-thin-v1.manifest.json` - Payload: `mirror-thin-v1.manifest.json`
- Signature: ed25519 over base64url(payload) - Signature: ed25519 over base64url(payload)
- Envelope path: `out/mirror/thin/mirror-thin-v1.manifest.dsse.json` - Envelope path: `out/mirror/thin/mirror-thin-v1.manifest.dsse.json`
- Bundle meta DSSE (OK1/OK3/MS8): payload type `application/vnd.stellaops.mirror.bundle+json`, payload `mirror-thin-v1.bundle.json`, envelope path `mirror-thin-v1.bundle.dsse.json`.
## TUF metadata layout ## TUF metadata layout
``` ```
@@ -23,9 +24,10 @@ out/mirror/thin/tuf/
keys/mirror-ed25519-test-1.pub keys/mirror-ed25519-test-1.pub
``` ```
### Targets mapping ### Targets mapping (latest dev build 2025-12-02)
- `mirror-thin-v1.tar.gz` → targets entry with sha256 `210dc49e8d3e25509298770a94da277aa2c9d4c387d3c24505a61fe1d7695a49` - `mirror-thin-v1.tar.gz` → targets entry with sha256 `fb1ce26388a1f1ab2eb90aae6d63ac05de326fbbd947fbf7a17b980232c9fc7d`
- `mirror-thin-v1.manifest.json` → sha256 `0ae51fa87648dae0a54fab950181a3600a8363182d89ad46d70f3a56b997b504` - `mirror-thin-v1.manifest.json` → sha256 `1affb0b796ff037117b46aa1f1d8056a9c80755e925af058ea72132ba158becf`
- `mirror-thin-v1.bundle.json` (top-level kit manifest) → sha256 `a3b16f5d1b74ffdf9aedbbfe9282d368dc3dcf70676c8ac7e8cdd984162e7f90`
### Determinism rules ### Determinism rules
- Sort keys in JSON; indent=2; trailing newline. - Sort keys in JSON; indent=2; trailing newline.

View File

@@ -12,6 +12,8 @@
MIRROR_SIGN_KEY_B64: ${{ secrets.MIRROR_SIGN_KEY_B64 }} MIRROR_SIGN_KEY_B64: ${{ secrets.MIRROR_SIGN_KEY_B64 }}
REQUIRE_PROD_SIGNING: 1 REQUIRE_PROD_SIGNING: 1
OCI: 1 OCI: 1
TENANT_SCOPE: tenant-demo
ENV_SCOPE: lab
run: | run: |
scripts/mirror/check_signing_prereqs.sh scripts/mirror/check_signing_prereqs.sh
scripts/mirror/ci-sign.sh scripts/mirror/ci-sign.sh
@@ -40,7 +42,9 @@ MIRROR_SIGN_KEY_B64=LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndC
**Do not ship with this key.** Set `REQUIRE_PROD_SIGNING=1` for release/tag builds so they fail without the real key. Add the production key as a Gitea secret (`MIRROR_SIGN_KEY_B64`) and rerun the workflow; remove this temporary key block once rotated. **Do not ship with this key.** Set `REQUIRE_PROD_SIGNING=1` for release/tag builds so they fail without the real key. Add the production key as a Gitea secret (`MIRROR_SIGN_KEY_B64`) and rerun the workflow; remove this temporary key block once rotated.
## Verification ## 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. The CI step already runs `scripts/mirror/verify_thin_bundle.py --bundle-meta mirror-thin-v1.bundle.json --tenant $TENANT_SCOPE --environment $ENV_SCOPE --pubkey out/mirror/thin/tuf/keys/ci-ed25519.pub` so offline-kit policies (OK1OK10), Rekor policy (RK1RK10), and mirror-format policy (MS1MS10) are validated alongside the tarball. For OCI, ensure `out/mirror/thin/oci/index.json` references the manifest digest.
`milestone.json` now carries manifest/tar/bundle/bundle-dsse hashes plus policy layer hashes to allow air-gapped import verification.
## Fallback (if secret absent) ## Fallback (if secret absent)
- CI can fall back to an embedded test Ed25519 key when `MIRROR_SIGN_KEY_B64` is unset **only when `REQUIRE_PROD_SIGNING` is not set**. This is for dev smoke runs; release/tag jobs must set `REQUIRE_PROD_SIGNING=1` to forbid fallback. - CI can fall back to an embedded test Ed25519 key when `MIRROR_SIGN_KEY_B64` is unset **only when `REQUIRE_PROD_SIGNING` is not set**. This is for dev smoke runs; release/tag jobs must set `REQUIRE_PROD_SIGNING=1` to forbid fallback.

View File

@@ -112,7 +112,7 @@ Key notes:
| **DSL Compiler** (`Dsl/`) | Parse, canonicalise, IR generation, checksum caching. | Uses Roslyn-like pipeline; caches by `policyId+version+hash`. | | **DSL Compiler** (`Dsl/`) | Parse, canonicalise, IR generation, checksum caching. | Uses Roslyn-like pipeline; caches by `policyId+version+hash`. |
| **Selection Layer** (`Selection/`) | Batch SBOM ↔ advisory ↔ VEX joiners; apply equivalence tables; support incremental cursors. | Deterministic ordering (SBOM → advisory → VEX). | | **Selection Layer** (`Selection/`) | Batch SBOM ↔ advisory ↔ VEX joiners; apply equivalence tables; support incremental cursors. | Deterministic ordering (SBOM → advisory → VEX). |
| **Evaluator** (`Evaluation/`) | Execute IR with first-match semantics, compute severity/trust/reachability weights, record rule hits. | Stateless; all inputs provided by selection layer. | | **Evaluator** (`Evaluation/`) | Execute IR with first-match semantics, compute severity/trust/reachability weights, record rule hits. | Stateless; all inputs provided by selection layer. |
| **Signals** (`Signals/`) | Normalizes reachability, trust, entropy, uncertainty, runtime hits into a single dictionary passed to Evaluator; supplies default `unknown` values when signals missing. | Aligns with `signals.*` namespace in DSL. | | **Signals** (`Signals/`) | Normalizes reachability, trust, entropy, uncertainty, runtime hits into a single dictionary passed to Evaluator; supplies default `unknown` values when signals missing. Entropy penalties are derived from Scanner `layer_summary.json`/`entropy.report.json` (K=0.5, cap=0.3, block at image opaque ratio &gt; 0.15 w/ unknown provenance) and exported via `policy_entropy_penalty_value` / `policy_entropy_image_opaque_ratio`. | Aligns with `signals.*` namespace in DSL. |
| **Materialiser** (`Materialization/`) | Upsert effective findings, append history, manage explain bundle exports. | Mongo transactions per SBOM chunk. | | **Materialiser** (`Materialization/`) | Upsert effective findings, append history, manage explain bundle exports. | Mongo transactions per SBOM chunk. |
| **Orchestrator** (`Runs/`) | Change-stream ingestion, fairness, retry/backoff, queue writer. | Works with Scheduler Models DTOs. | | **Orchestrator** (`Runs/`) | Change-stream ingestion, fairness, retry/backoff, queue writer. | Works with Scheduler Models DTOs. |
| **API** (`Api/`) | Minimal API endpoints, DTO validation, problem responses, idempotency. | Generated clients for CLI/UI. | | **API** (`Api/`) | Minimal API endpoints, DTO validation, problem responses, idempotency. | Generated clients for CLI/UI. |

View File

@@ -2,7 +2,8 @@
Zastava monitors running workloads, verifies supply chain posture, and enforces runtime policy via Kubernetes admission webhooks. Zastava monitors running workloads, verifies supply chain posture, and enforces runtime policy via Kubernetes admission webhooks.
## Latest updates (2025-11-30) ## Latest updates (2025-12-02)
- DSSE-signed schemas, thresholds, exports, and deterministic `zastava-kit` bundle published under `docs/modules/zastava`; verification via `kit/verify.sh` and hashes in `SHA256SUMS`.
- Sprint tracker `docs/implplan/SPRINT_0335_0001_0001_docs_modules_zastava.md` and module `TASKS.md` added to mirror status. - Sprint tracker `docs/implplan/SPRINT_0335_0001_0001_docs_modules_zastava.md` and module `TASKS.md` added to mirror status.
- Observability runbook stub + dashboard placeholder added under `operations/` (offline import). - Observability runbook stub + dashboard placeholder added under `operations/` (offline import).
- Surface.Env/Surface.Secrets adoption remains pending platform contracts; align with platform docs before enabling sealed mode. - Surface.Env/Surface.Secrets adoption remains pending platform contracts; align with platform docs before enabling sealed mode.

View File

@@ -1,7 +1,17 @@
e65d4b68c9bdaa569c6d4c5a9b0a8bc1dc41876f948983011ff6f9d3466565d0 schemas/observer_event.schema.json 1b05f31ab9486f9a03ecf872fa5c681e9acbad2adb71a776c271dbcf997ca2a8 schemas/observer_event.schema.json
f466bf2b399f065558867eaf3c961cff8803f4a1506bae5539c9ce62e9ab005d schemas/webhook_admission.schema.json 99382de0e6a2b9c21146c03640c2e08b0e5e41be11fdbc213f0f071357da5a99 schemas/observer_event.schema.json.dsse
222db5258f5ba1ee720f8df03858263363b8636ff8ec9370f5ad390e8def0b3c schemas/webhook_admission.schema.json
19f108da1a512a488536bc2cd9d9cb1cf9824d748d8fc6a32d0e31c89be9a897 schemas/webhook_admission.schema.json.dsse
da065beabf8e038298a54f04ffa3e140cc149e0d64c301f6fd4c3925f2d64ee6 schemas/examples/observer_event.example.json
7e3cd0c18c9dfaf9001a16a99be7f9ff01e2d21b14eca9fb97c332342ac53c94 schemas/examples/webhook_admission.example.json
e17d36a2a39d748b76994ad3e3e4f3fa8db1b9298a3ce5eaaafb575791c01da3 schemas/README.md
f88bdebaa9858ffe3cd0fbb46e914c933e18709165bfc59f976136097fa8493d exports/observer_events.ndjson
de9b24675a0a758e40647844a31a13a1be1667750a39fe59465b0353fd0dddd9 exports/observer_events.ndjson.dsse
232809cf6a1cc7ba5fa34e0daf00fab9b6f970a613bc822457eef0d841fb2229 exports/webhook_admissions.ndjson
0edf6cabd636c7bb1f210af2aecaf83de3cc21c82113a646429242ae72618b17 exports/webhook_admissions.ndjson.dsse
40fabd4d7bc75c35ae063b2e931e79838c79b447528440456f5f4846951ff59d thresholds.yaml 40fabd4d7bc75c35ae063b2e931e79838c79b447528440456f5f4846951ff59d thresholds.yaml
652fce7d7b622ae762c8fb65a1e592bec14b124c3273312f93a63d2c29a2b989 kit/verify.sh 4dc099a742429a7ec300ac4c9eefe2f6b80bc0c10d7a7a3bbaf7f0a0f0ad7f20 thresholds.yaml.dsse
f3f84fbe780115608268a91a5203d2d3ada50b4317e7641d88430a692e61e1f4 kit/README.md f69f953c78134ef504b870cea47ba62d5e37a7a86ec0043d824dcb6073cd43fb kit/verify.sh
2411a16a68c98c8fdd402e19b9c29400b469c0054d0b6067541ee343988b85e0 schemas/examples/observer_event.example.json 1cf8f0448881d067e5e001a1dfe9734b4cdfcaaf16c3e9a7321ceae56e4af8f2 kit/README.md
4ab47977b0717c8bdb39c52f52880742785cbcf0b5ba73d9ecc835155d445dc1 schemas/examples/webhook_admission.example.json eaba054428fa72cd9476cffe7a94450e4345ffe2e294e9079eb7c3703bcf7df0 kit/ed25519.pub
40a40b31480d876cf4487d07ca8d8b5166c7df455bef234e2c1861b7b3dc7e3b evidence/README.md

View File

@@ -5,9 +5,9 @@
| ZASTAVA-DOCS-0001 | DONE (2025-11-30) | Docs Guild | README/architecture refreshed; Surface Env/Secrets and sprint links added. | | ZASTAVA-DOCS-0001 | DONE (2025-11-30) | Docs Guild | README/architecture refreshed; Surface Env/Secrets and sprint links added. |
| ZASTAVA-ENG-0001 | DONE (2025-11-30) | Module Team | TASKS board created; statuses mirrored with `docs/implplan/SPRINT_0335_0001_0001_docs_modules_zastava.md`. | | ZASTAVA-ENG-0001 | DONE (2025-11-30) | Module Team | TASKS board created; statuses mirrored with `docs/implplan/SPRINT_0335_0001_0001_docs_modules_zastava.md`. |
| ZASTAVA-OPS-0001 | DONE (2025-11-30) | Ops Guild | Observability runbook stub + Grafana JSON placeholder added under `operations/`. | | ZASTAVA-OPS-0001 | DONE (2025-11-30) | Ops Guild | Observability runbook stub + Grafana JSON placeholder added under `operations/`. |
| ZASTAVA-SCHEMAS-0001 | TODO | Zastava Guild | Publish signed observer/admission schemas + test vectors under `docs/modules/zastava/schemas/`; DSSE + SHA256 required. | | ZASTAVA-SCHEMAS-0001 | DONE (2025-12-02) | Zastava Guild | Signed observer/admission schemas + test vectors under `docs/modules/zastava/schemas/`; DSSE + SHA256 published. |
| ZASTAVA-KIT-0001 | TODO | Zastava Guild | Build signed `zastava-kit` bundle with thresholds.yaml, schemas, observations/admissions export, SHA256SUMS, and verify.sh; ensure offline parity. | | ZASTAVA-KIT-0001 | DONE (2025-12-02) | Zastava Guild | Built signed `zastava-kit` bundle with thresholds, schemas, exports, SHA256SUMS, verify.sh; offline parity verified. |
| ZASTAVA-THRESHOLDS-0001 | TODO | Zastava Guild | DSSE-sign `thresholds.yaml` and align with kit; publish Evidence Locker URI and update sprint 0144 checkpoints. | | ZASTAVA-THRESHOLDS-0001 | DONE (2025-12-02) | Zastava Guild | DSSE-signed `thresholds.yaml`, recorded Evidence Locker targets, and aligned with kit packaging. |
| ZASTAVA-GAPS-144-007 | DONE (2025-12-02) | Zastava Guild | Remediation plan for ZR1ZR10 published at `docs/modules/zastava/gaps/2025-12-02-zr-gaps.md`; follow-on schemas/kit/thresholds to be produced and signed. | | ZASTAVA-GAPS-144-007 | DONE (2025-12-02) | Zastava Guild | Remediation plan for ZR1ZR10 published at `docs/modules/zastava/gaps/2025-12-02-zr-gaps.md`; follow-on schemas/kit/thresholds to be produced and signed. |
> Keep this table in lockstep with the sprint Delivery Tracker (TODO/DOING/DONE/BLOCKED updates go to both places). > Keep this table in lockstep with the sprint Delivery Tracker (TODO/DOING/DONE/BLOCKED updates go to both places).

View File

@@ -1,29 +1,53 @@
# Zastava Evidence Locker Plan (schemas/kit) # Zastava Evidence Locker (schemas/kit)
Artifacts to sign (target 2025-12-06): Signed 2025-12-02 with Ed25519 key (pub base64url: `mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc`). Private key stored offline; all signatures use DSSEv1 pre-auth encoding.
- `schemas/observer_event.schema.json` — predicate `stella.ops/zastavaSchema@v1` Public key copy: `docs/modules/zastava/kit/ed25519.pub`.
- `schemas/webhook_admission.schema.json` — predicate `stella.ops/zastavaSchema@v1`
- `thresholds.yaml` — predicate `stella.ops/zastavaThresholds@v1`
- `zastava-kit.tzst` + `SHA256SUMS` — predicate `stella.ops/zastavaKit@v1`
Planned Evidence Locker paths (fill after signing): ## Artefacts
- `evidence-locker/zastava/2025-12-06/observer_event.schema.dsse` - `schemas/observer_event.schema.json.dsse` (payloadType `application/vnd.stellaops.zastava.schema+json;name=observer_event;version=1`)
- `evidence-locker/zastava/2025-12-06/webhook_admission.schema.dsse` - `schemas/webhook_admission.schema.json.dsse` (payloadType `application/vnd.stellaops.zastava.schema+json;name=webhook_admission;version=1`)
- `evidence-locker/zastava/2025-12-06/thresholds.dsse` - `thresholds.yaml.dsse` (payloadType `application/vnd.stellaops.zastava.thresholds+yaml;version=1`)
- `evidence-locker/zastava/2025-12-06/zastava-kit.tzst` - `exports/observer_events.ndjson.dsse` (payloadType `application/vnd.stellaops.zastava.observer-events+ndjson;version=1`)
- `evidence-locker/zastava/2025-12-06/SHA256SUMS` - `exports/webhook_admissions.ndjson.dsse` (payloadType `application/vnd.stellaops.zastava.webhook-admissions+ndjson;version=1`)
- `kit/zastava-kit.tzst.dsse` (payloadType `application/vnd.stellaops.zastava.kit+tzst;version=1`)
Signing template (replace KEY and file): ## Evidence Locker targets
- `evidence-locker/zastava/2025-12-02/observer_event.schema.json.dsse`
- `evidence-locker/zastava/2025-12-02/webhook_admission.schema.json.dsse`
- `evidence-locker/zastava/2025-12-02/thresholds.yaml.dsse`
- `evidence-locker/zastava/2025-12-02/observer_events.ndjson.dsse`
- `evidence-locker/zastava/2025-12-02/webhook_admissions.ndjson.dsse`
- `evidence-locker/zastava/2025-12-02/zastava-kit.tzst`
- `evidence-locker/zastava/2025-12-02/zastava-kit.tzst.dsse`
- `evidence-locker/zastava/2025-12-02/SHA256SUMS`
## Signing template (Python, ed25519)
```bash ```bash
cosign sign-blob \ python - <<'PY'
--key cosign.key \ from pathlib import Path
--predicate-type stella.ops/zastavaSchema@v1 \ from base64 import urlsafe_b64encode
--output-signature schemas/observer_event.schema.dsse \ import json
schemas/observer_event.schema.json from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
priv = serialization.load_pem_private_key(Path('/tmp/zastava-ed25519.key').read_bytes(), password=None)
pub = priv.public_key().public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)
keyid = urlsafe_b64encode(pub).decode().rstrip('=')
pt = '<payload-type>'
payload = Path('<file-to-sign>').read_bytes()
pae = b' '.join([b'DSSEv1', str(len(pt)).encode(), pt.encode(), str(len(payload)).encode(), payload])
sig = priv.sign(pae)
env = {
'payloadType': pt,
'payload': urlsafe_b64encode(payload).decode().rstrip('='),
'signatures': [{'keyid': keyid, 'sig': urlsafe_b64encode(sig).decode().rstrip('=')}],
}
Path('<file-to-sign>.dsse').write_text(json.dumps(env, indent=2, sort_keys=True) + '\n')
print('signed', '<file-to-sign>', 'with keyid', keyid)
PY
``` ```
Post-sign steps: ## Post-sign checklist
1) Verify DSSEs with `cosign verify-blob` using `cosign.pub`. 1) Run `kit/verify.sh` to validate hashes + DSSE.
2) Upload DSSEs + SHA256SUMS to Evidence Locker paths above. 2) Upload artefacts + DSSEs + SHA256SUMS to the Evidence Locker paths above.
3) Update `docs/implplan/SPRINT_0144_0001_0001_zastava_runtime_signals.md` Decisions & Risks and Next Checkpoints with final URIs. 3) Record URIs in sprint 0144 Decisions & Risks and mark ZASTAVA-SCHEMAS-0001 / ZASTAVA-THRESHOLDS-0001 / ZASTAVA-KIT-0001 as DONE.
4) Mark tasks ZASTAVA-SCHEMAS-0001 / ZASTAVA-THRESHOLDS-0001 / ZASTAVA-KIT-0001 to DONE in both sprint and TASKS tables.

View File

@@ -0,0 +1 @@
{"event_type":"runtime_fact","firmware_version":"1.2.3","graph_revision_id":"graph-r1","ledger_id":"ledger-789","monotonic_nanos":123456789,"observed_at":"2025-12-02T12:00:00Z","payload":{"pid":4242,"process":"nginx"},"payload_hash":"sha256:7476a5068a3f0780c552f81c90d061d9e39c37f425a243ecff961b08676546fd","policy_hash":"sha256:deadbeef","project_id":"proj-123","replay_manifest":"manifest-r1","sensor_id":"observer-01","signature":"dsse://observer-events/2025-12-02/observer_events.ndjson.dsse#line1","tenant_id":"tenant-a"}

View File

@@ -0,0 +1,10 @@
{
"payload": "eyJldmVudF90eXBlIjoicnVudGltZV9mYWN0IiwiZmlybXdhcmVfdmVyc2lvbiI6IjEuMi4zIiwiZ3JhcGhfcmV2aXNpb25faWQiOiJncmFwaC1yMSIsImxlZGdlcl9pZCI6ImxlZGdlci03ODkiLCJtb25vdG9uaWNfbmFub3MiOjEyMzQ1Njc4OSwib2JzZXJ2ZWRfYXQiOiIyMDI1LTEyLTAyVDEyOjAwOjAwWiIsInBheWxvYWQiOnsicGlkIjo0MjQyLCJwcm9jZXNzIjoibmdpbngifSwicGF5bG9hZF9oYXNoIjoic2hhMjU2Ojc0NzZhNTA2OGEzZjA3ODBjNTUyZjgxYzkwZDA2MWQ5ZTM5YzM3ZjQyNWEyNDNlY2ZmOTYxYjA4Njc2NTQ2ZmQiLCJwb2xpY3lfaGFzaCI6InNoYTI1NjpkZWFkYmVlZiIsInByb2plY3RfaWQiOiJwcm9qLTEyMyIsInJlcGxheV9tYW5pZmVzdCI6Im1hbmlmZXN0LXIxIiwic2Vuc29yX2lkIjoib2JzZXJ2ZXItMDEiLCJzaWduYXR1cmUiOiJkc3NlOi8vb2JzZXJ2ZXItZXZlbnRzLzIwMjUtMTItMDIvb2JzZXJ2ZXJfZXZlbnRzLm5kanNvbi5kc3NlI2xpbmUxIiwidGVuYW50X2lkIjoidGVuYW50LWEifQo",
"payloadType": "application/vnd.stellaops.zastava.observer-events+ndjson;version=1",
"signatures": [
{
"keyid": "mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc",
"sig": "5DPpjAcyWSeCM_yPCiIsQl92FtUwnccN8J5lY5AxKBE1qfYbU6dEgGQudDWlY2_-FUak6fupQ79vrgGbGiDDDQ"
}
]
}

View File

@@ -0,0 +1 @@
{"bypass_waiver_id":null,"decision":"allow","decision_at":"2025-12-02T12:00:10Z","decision_reason":"surface cache fresh","graph_revision_id":"graph-r1","ledger_id":"ledger-789","manifest_pointer":"surfacefs://cache/sha256:abc","monotonic_nanos":2233445566,"namespace":"prod","payload":{"images":[{"digest":"sha256:abcd","name":"ghcr.io/acme/api:1.2.3","sbom_referrer":true,"signed":true}],"manifest_pointer":"surfacefs://cache/sha256:abc","policy_hash":"sha256:deadbeef","verdict":"allow"},"payload_hash":"sha256:36bfb2bc81b7050bbb508e12cafe7ad5a51336aad397ef3a23b0e258aed73dc6","policy_hash":"sha256:deadbeef","project_id":"proj-123","replay_manifest":"manifest-r1","request_uid":"abcd-1234","resource_kind":"Deployment","side_effect":"none","signature":"dsse://webhook-admissions/2025-12-02/webhook_admissions.ndjson.dsse#line1","tenant_id":"tenant-a","workload_name":"api"}

View File

@@ -0,0 +1,10 @@
{
"payload": "eyJieXBhc3Nfd2FpdmVyX2lkIjpudWxsLCJkZWNpc2lvbiI6ImFsbG93IiwiZGVjaXNpb25fYXQiOiIyMDI1LTEyLTAyVDEyOjAwOjEwWiIsImRlY2lzaW9uX3JlYXNvbiI6InN1cmZhY2UgY2FjaGUgZnJlc2giLCJncmFwaF9yZXZpc2lvbl9pZCI6ImdyYXBoLXIxIiwibGVkZ2VyX2lkIjoibGVkZ2VyLTc4OSIsIm1hbmlmZXN0X3BvaW50ZXIiOiJzdXJmYWNlZnM6Ly9jYWNoZS9zaGEyNTY6YWJjIiwibW9ub3RvbmljX25hbm9zIjoyMjMzNDQ1NTY2LCJuYW1lc3BhY2UiOiJwcm9kIiwicGF5bG9hZCI6eyJpbWFnZXMiOlt7ImRpZ2VzdCI6InNoYTI1NjphYmNkIiwibmFtZSI6ImdoY3IuaW8vYWNtZS9hcGk6MS4yLjMiLCJzYm9tX3JlZmVycmVyIjp0cnVlLCJzaWduZWQiOnRydWV9XSwibWFuaWZlc3RfcG9pbnRlciI6InN1cmZhY2VmczovL2NhY2hlL3NoYTI1NjphYmMiLCJwb2xpY3lfaGFzaCI6InNoYTI1NjpkZWFkYmVlZiIsInZlcmRpY3QiOiJhbGxvdyJ9LCJwYXlsb2FkX2hhc2giOiJzaGEyNTY6MzZiZmIyYmM4MWI3MDUwYmJiNTA4ZTEyY2FmZTdhZDVhNTEzMzZhYWQzOTdlZjNhMjNiMGUyNThhZWQ3M2RjNiIsInBvbGljeV9oYXNoIjoic2hhMjU2OmRlYWRiZWVmIiwicHJvamVjdF9pZCI6InByb2otMTIzIiwicmVwbGF5X21hbmlmZXN0IjoibWFuaWZlc3QtcjEiLCJyZXF1ZXN0X3VpZCI6ImFiY2QtMTIzNCIsInJlc291cmNlX2tpbmQiOiJEZXBsb3ltZW50Iiwic2lkZV9lZmZlY3QiOiJub25lIiwic2lnbmF0dXJlIjoiZHNzZTovL3dlYmhvb2stYWRtaXNzaW9ucy8yMDI1LTEyLTAyL3dlYmhvb2tfYWRtaXNzaW9ucy5uZGpzb24uZHNzZSNsaW5lMSIsInRlbmFudF9pZCI6InRlbmFudC1hIiwid29ya2xvYWRfbmFtZSI6ImFwaSJ9Cg",
"payloadType": "application/vnd.stellaops.zastava.webhook-admissions+ndjson;version=1",
"signatures": [
{
"keyid": "mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc",
"sig": "UwXQm2oZPVIISQecILLkvxvSXZiXeZdPVe5RNqFxZ8Dv5xDT1nEcTq0pn2Tl3unk0sY44Lh-dU_599nxaHD9Aw"
}
]
}

View File

@@ -43,7 +43,8 @@
- Delivery paths for schemas/thresholds/kit will be added when produced; DSSE signatures required for all artefacts. - Delivery paths for schemas/thresholds/kit will be added when produced; DSSE signatures required for all artefacts.
## Next steps ## Next steps
1) Generate schemas + test vectors and place under `docs/modules/zastava/schemas/`; sign DSSE. 1) ✅ Schemas + test vectors generated and DSSE-signed under `docs/modules/zastava/schemas/` (2025-12-02).
2) Draft `thresholds.yaml` with budgets and sign DSSE. 2) `thresholds.yaml` DSSE-signed and included in kit (2025-12-02).
3) Build `zastava-kit` bundle + `verify.sh`; include Evidence Locker path and SHA256. 3) ✅ Deterministic `zastava-kit` bundle + `verify.sh` built; kit DSSE stored at `docs/modules/zastava/kit/zastava-kit.tzst.dsse` with hashes in `SHA256SUMS` (2025-12-02).
4) Add tenancy/ordering/provenance enforcement to Observer/Webhook validators and tests; mirror changes in sprint and TASKS boards. 4) Add tenancy/ordering/provenance enforcement to Observer/Webhook validators and tests; mirror changes in sprint and TASKS boards.
5) Upload DSSE artefacts + kit to Evidence Locker paths in `docs/modules/zastava/evidence/README.md` and backfill operations docs with verifier usage.

View File

@@ -1,17 +1,83 @@
# Zastava Kit (offline bundle) Draft # Zastava Kit (offline bundle)
Contents to include when built: ## Contents
- Observations and admissions exports (NDJSON) signed via DSSE. - Schemas + DSSE: `schemas/observer_event.schema.json(.dsse)`, `schemas/webhook_admission.schema.json(.dsse)`.
- Schemas: `schemas/observer_event.schema.json`, `schemas/webhook_admission.schema.json`. - Examples: `schemas/examples/*.json` (canonicalised, hashed).
- Thresholds: `thresholds.yaml` (DSSE-signed). - Thresholds + DSSE: `thresholds.yaml(.dsse)`.
- Hash manifest: `SHA256SUMS` (covering all kit files). - Exports + DSSE: `exports/observer_events.ndjson(.dsse)`, `exports/webhook_admissions.ndjson(.dsse)`.
- Verify script: `verify.sh` (hash + DSSE verification; fail closed on mismatch). - Verification assets: `SHA256SUMS`, `kit/verify.sh`, `kit/ed25519.pub`, `schemas/README.md`, `evidence/README.md`.
Deterministic packaging: `tar --mtime @0 --owner 0 --group 0 --numeric-owner -cf - kit | zstd -19 --long=27 --no-progress > zastava-kit.tzst`. ## Build (deterministic)
From `docs/modules/zastava`:
Pending: fill with signed artefacts and Evidence Locker URIs after DSSE signing. ```bash
Planned Evidence Locker paths (post-signing): tar --mtime @0 --owner 0 --group 0 --numeric-owner --sort=name \
- `evidence-locker/zastava/2025-12-06/observer_event.schema.dsse` -cf - \
- `evidence-locker/zastava/2025-12-06/webhook_admission.schema.dsse` SHA256SUMS schemas exports thresholds.yaml thresholds.yaml.dsse \
- `evidence-locker/zastava/2025-12-06/thresholds.dsse` schemas/examples schemas/README.md \
- `evidence-locker/zastava/2025-12-06/zastava-kit.tzst` + `SHA256SUMS` schemas/observer_event.schema.json schemas/observer_event.schema.json.dsse \
schemas/webhook_admission.schema.json schemas/webhook_admission.schema.json.dsse \
exports/observer_events.ndjson exports/observer_events.ndjson.dsse \
exports/webhook_admissions.ndjson exports/webhook_admissions.ndjson.dsse \
evidence/README.md kit/README.md kit/verify.sh kit/ed25519.pub \
| zstd -19 --long=27 --no-progress > kit/zastava-kit.tzst
```
Sign the kit itself with the same Ed25519 key (base64url pub: `mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc`):
```bash
python - <<'PY'
from pathlib import Path
from base64 import urlsafe_b64encode
import json
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
priv = serialization.load_pem_private_key(Path('/tmp/zastava-ed25519.key').read_bytes(), password=None)
pub = priv.public_key().public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)
keyid = urlsafe_b64encode(pub).decode().rstrip('=')
pt = 'application/vnd.stellaops.zastava.kit+tzst;version=1'
payload = Path('kit/zastava-kit.tzst').read_bytes()
pae = b' '.join([b'DSSEv1', str(len(pt)).encode(), pt.encode(), str(len(payload)).encode(), payload])
sig = priv.sign(pae)
env = {
'payloadType': pt,
'payload': urlsafe_b64encode(payload).decode().rstrip('='),
'signatures': [{'keyid': keyid, 'sig': urlsafe_b64encode(sig).decode().rstrip('=')}],
}
Path('kit/zastava-kit.tzst.dsse').write_text(json.dumps(env, indent=2, sort_keys=True) + '\n')
print('wrote kit/zastava-kit.tzst.dsse with keyid', keyid)
PY
```
## Verify
1) Verify the kit DSSE before unpacking (optional but recommended) using the public key shipped alongside the kit (run from `docs/modules/zastava`):
```bash
cd docs/modules/zastava
python - <<'PY'
import base64, json, sys
from pathlib import Path
from cryptography.hazmat.primitives.asymmetric import ed25519
root = Path('.')
pub = base64.urlsafe_b64decode((root / 'kit' / 'ed25519.pub').read_text().strip() + '==')
env = json.loads((root / 'kit' / 'zastava-kit.tzst.dsse').read_text())
payload = (root / 'kit' / 'zastava-kit.tzst').read_bytes()
pt = env['payloadType'].encode()
pae = b' '.join([b'DSSEv1', str(len(pt)).encode(), pt, str(len(payload)).encode(), payload])
sig = base64.urlsafe_b64decode(env['signatures'][0]['sig'] + '==')
ed25519.Ed25519PublicKey.from_public_bytes(pub).verify(sig, pae)
decoded_payload = base64.urlsafe_b64decode(env['payload'] + '==')
assert decoded_payload == payload
print('OK: kit DSSE verified')
PY
```
2) Extract and run offline validation of the inner artefacts:
```bash
zstd -d kit/zastava-kit.tzst -c | tar -xf -
./kit/verify.sh
```
## Notes
- Private signing key is held offline; only the public key is shipped.
- All files are deterministic (mtime=0, numeric owners) to keep hashes stable for Evidence Locker ingestion.

View File

@@ -0,0 +1 @@
mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc

View File

@@ -1,24 +1,59 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
cd "$ROOT"
if ! command -v sha256sum >/dev/null; then ROOT="$(cd "$(dirname "$0")" && pwd)"
echo "sha256sum required" >&2; exit 1 MODULE_ROOT="${ROOT}/.."
fi cd "$MODULE_ROOT"
export MODULE_ROOT
command -v sha256sum >/dev/null || { echo "sha256sum required" >&2; exit 1; }
command -v python >/dev/null || { echo "python required" >&2; exit 1; }
sha256sum --check SHA256SUMS sha256sum --check SHA256SUMS
if command -v cosign >/dev/null && [ -f cosign.pub ]; then
echo "cosign present; DSSE verification placeholders (update paths when signed):"
echo "- observer_event.schema.dsse"
echo "- webhook_admission.schema.dsse"
echo "- thresholds.dsse"
# Example commands (uncomment once DSSE files exist):
# cosign verify-blob --key cosign.pub --signature observer_event.schema.dsse schemas/observer_event.schema.json
# cosign verify-blob --key cosign.pub --signature webhook_admission.schema.dsse schemas/webhook_admission.schema.json
# cosign verify-blob --key cosign.pub --signature thresholds.dsse thresholds.yaml
else
echo "cosign not found or cosign.pub missing; skipped DSSE verification"
fi
echo "OK: hashes verified (DSSE verification pending)" python - <<'PY'
import base64, json, os, sys
from pathlib import Path
try:
from cryptography.hazmat.primitives.asymmetric import ed25519
except Exception as exc:
raise SystemExit(f"cryptography package required for DSSE verification: {exc}")
root = Path(os.environ['MODULE_ROOT']).resolve()
pub_b64 = (root / "kit" / "ed25519.pub").read_text().strip()
pub = base64.urlsafe_b64decode(pub_b64 + "==")
verifier = ed25519.Ed25519PublicKey.from_public_bytes(pub)
def pae(payload_type: bytes, payload: bytes) -> bytes:
parts = [b"DSSEv1", str(len(payload_type)).encode(), payload_type, str(len(payload)).encode(), payload]
return b" ".join(parts)
def verify(name: str, payload_path: Path, envelope_path: Path, payload_type: str):
payload = payload_path.read_bytes()
envelope = json.loads(envelope_path.read_text())
if envelope.get("payloadType") != payload_type:
raise SystemExit(f"{name}: payloadType mismatch ({envelope.get('payloadType')} != {payload_type})")
if not envelope.get("signatures"):
raise SystemExit(f"{name}: missing signatures")
sig_entry = envelope["signatures"][0]
sig = base64.urlsafe_b64decode(sig_entry["sig"] + "==")
decoded_payload = base64.urlsafe_b64decode(envelope["payload"] + "==")
if decoded_payload != payload:
raise SystemExit(f"{name}: payload body mismatch vs envelope")
verifier.verify(sig, pae(payload_type.encode(), payload))
print(f"OK: {name}")
targets = [
("observer schema", root / "schemas" / "observer_event.schema.json", root / "schemas" / "observer_event.schema.json.dsse", "application/vnd.stellaops.zastava.schema+json;name=observer_event;version=1"),
("webhook schema", root / "schemas" / "webhook_admission.schema.json", root / "schemas" / "webhook_admission.schema.json.dsse", "application/vnd.stellaops.zastava.schema+json;name=webhook_admission;version=1"),
("thresholds", root / "thresholds.yaml", root / "thresholds.yaml.dsse", "application/vnd.stellaops.zastava.thresholds+yaml;version=1"),
("observer exports", root / "exports" / "observer_events.ndjson", root / "exports" / "observer_events.ndjson.dsse", "application/vnd.stellaops.zastava.observer-events+ndjson;version=1"),
("webhook exports", root / "exports" / "webhook_admissions.ndjson", root / "exports" / "webhook_admissions.ndjson.dsse", "application/vnd.stellaops.zastava.webhook-admissions+ndjson;version=1"),
]
for name, payload_path, envelope_path, ptype in targets:
verify(name, payload_path, envelope_path, ptype)
PY
echo "OK: SHA256 + DSSE signatures verified"

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
# Zastava schemas (runtime & admission)
## Canonicalisation & hashing
- JSON is stored with sorted keys and two-space indentation; hashes use **JCS-style** encoding: `json.dumps(payload, separators=(',', ':'), sort_keys=True)`.
- `payload_hash` fields in examples and exports are computed from the canonical payload bytes and formatted as `sha256:<hex>`.
- Schema negotiation stays on the `zastava.*@v1.x` line; breaking changes bump the major version.
## DSSE signing
- Payload types:
- `application/vnd.stellaops.zastava.schema+json;name=observer_event;version=1`
- `application/vnd.stellaops.zastava.schema+json;name=webhook_admission;version=1`
- Ed25519 public key (base64url, no padding): `mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc`.
- Signatures are emitted as `<file>.dsse` with DSSEv1 pre-auth encoding over the raw file bytes.
- Regenerate signatures with `docs/modules/zastava/kit/verify.sh` prerequisites (Python + cryptography) and the private key held offline.
## Test vectors
- Example payloads: `schemas/examples/*.json`.
- Signed exports: `exports/observer_events.ndjson(.dsse)` and `exports/webhook_admissions.ndjson(.dsse)`.
- Kit verification aggregates all signatures via `kit/verify.sh`.

View File

@@ -1,19 +1,19 @@
{ {
"tenant_id": "tenant-a", "event_type": "runtime_fact",
"project_id": "proj-123",
"sensor_id": "observer-01",
"firmware_version": "1.2.3", "firmware_version": "1.2.3",
"policy_hash": "sha256:deadbeef",
"graph_revision_id": "graph-r1", "graph_revision_id": "graph-r1",
"ledger_id": "ledger-789", "ledger_id": "ledger-789",
"replay_manifest": "manifest-r1",
"event_type": "runtime_fact",
"observed_at": "2025-12-02T00:00:00Z",
"monotonic_nanos": 123456789, "monotonic_nanos": 123456789,
"observed_at": "2025-12-02T00:00:00Z",
"payload": { "payload": {
"process": "nginx", "pid": 4242,
"pid": 4242 "process": "nginx"
}, },
"payload_hash": "sha256:payloadhash", "payload_hash": "sha256:7476a5068a3f0780c552f81c90d061d9e39c37f425a243ecff961b08676546fd",
"signature": "dsse://observer-event" "policy_hash": "sha256:deadbeef",
"project_id": "proj-123",
"replay_manifest": "manifest-r1",
"sensor_id": "observer-01",
"signature": "dsse://observer-events/2025-12-02/observer_events.ndjson.dsse#line1",
"tenant_id": "tenant-a"
} }

View File

@@ -1,21 +1,34 @@
{ {
"tenant_id": "tenant-a", "bypass_waiver_id": null,
"project_id": "proj-123", "decision": "allow",
"request_uid": "abcd-1234", "decision_at": "2025-12-02T00:00:00Z",
"resource_kind": "Deployment", "decision_reason": "surface cache fresh",
"namespace": "prod",
"workload_name": "api",
"policy_hash": "sha256:deadbeef",
"graph_revision_id": "graph-r1", "graph_revision_id": "graph-r1",
"ledger_id": "ledger-789", "ledger_id": "ledger-789",
"replay_manifest": "manifest-r1",
"manifest_pointer": "surfacefs://cache/sha256:abc", "manifest_pointer": "surfacefs://cache/sha256:abc",
"decision": "allow",
"decision_reason": "surface cache fresh",
"decision_at": "2025-12-02T00:00:00Z",
"monotonic_nanos": 2233445566, "monotonic_nanos": 2233445566,
"namespace": "prod",
"payload": {
"images": [
{
"digest": "sha256:abcd",
"name": "ghcr.io/acme/api:1.2.3",
"sbom_referrer": true,
"signed": true
}
],
"manifest_pointer": "surfacefs://cache/sha256:abc",
"policy_hash": "sha256:deadbeef",
"verdict": "allow"
},
"payload_hash": "sha256:36bfb2bc81b7050bbb508e12cafe7ad5a51336aad397ef3a23b0e258aed73dc6",
"policy_hash": "sha256:deadbeef",
"project_id": "proj-123",
"replay_manifest": "manifest-r1",
"request_uid": "abcd-1234",
"resource_kind": "Deployment",
"side_effect": "none", "side_effect": "none",
"bypass_waiver_id": null, "signature": "dsse://webhook-admissions/2025-12-02/webhook_admissions.ndjson.dsse#line1",
"payload_hash": "sha256:payloadhash", "tenant_id": "tenant-a",
"signature": "dsse://webhook-admission" "workload_name": "api"
} }

View File

@@ -1,8 +1,67 @@
{ {
"$id": "https://stella-ops.org/schemas/zastava/observer_event.schema.json", "$id": "https://stella-ops.org/schemas/zastava/observer_event.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Zastava Observer Event", "properties": {
"type": "object", "event_type": {
"enum": [
"runtime_fact",
"drift",
"policy_violation",
"heartbeat"
]
},
"firmware_version": {
"minLength": 1,
"type": "string"
},
"graph_revision_id": {
"minLength": 1,
"type": "string"
},
"ledger_id": {
"type": "string"
},
"monotonic_nanos": {
"type": "integer"
},
"observed_at": {
"format": "date-time",
"type": "string"
},
"payload": {
"description": "Canonical runtime payload (JCS) used for hashing.",
"type": "object"
},
"payload_hash": {
"description": "sha256 over canonical JSON (JCS) of payload",
"pattern": "^sha256:[0-9a-f]{64}$",
"type": "string"
},
"policy_hash": {
"minLength": 1,
"type": "string"
},
"project_id": {
"minLength": 1,
"type": "string"
},
"replay_manifest": {
"type": "string"
},
"sensor_id": {
"minLength": 1,
"type": "string"
},
"signature": {
"description": "DSSE envelope reference",
"pattern": "^dsse://[A-Za-z0-9._:/-]+$",
"type": "string"
},
"tenant_id": {
"minLength": 1,
"type": "string"
}
},
"required": [ "required": [
"tenant_id", "tenant_id",
"project_id", "project_id",
@@ -12,23 +71,10 @@
"graph_revision_id", "graph_revision_id",
"event_type", "event_type",
"observed_at", "observed_at",
"payload",
"payload_hash", "payload_hash",
"signature" "signature"
], ],
"properties": { "title": "Zastava Observer Event",
"tenant_id": { "type": "string" }, "type": "object"
"project_id": { "type": "string" },
"sensor_id": { "type": "string" },
"firmware_version": { "type": "string" },
"policy_hash": { "type": "string" },
"graph_revision_id": { "type": "string" },
"ledger_id": { "type": "string" },
"replay_manifest": { "type": "string" },
"event_type": { "enum": ["runtime_fact", "drift", "policy_violation", "heartbeat"] },
"observed_at": { "type": "string", "format": "date-time" },
"monotonic_nanos": { "type": "integer" },
"payload": { "type": "object" },
"payload_hash": { "type": "string", "description": "sha256 over canonical JSON (JCS) of payload" },
"signature": { "type": "string", "description": "DSSE envelope reference" }
}
} }

View File

@@ -0,0 +1,10 @@
{
"payload": "ewogICIkaWQiOiAiaHR0cHM6Ly9zdGVsbGEtb3BzLm9yZy9zY2hlbWFzL3phc3RhdmEvb2JzZXJ2ZXJfZXZlbnQuc2NoZW1hLmpzb24iLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInByb3BlcnRpZXMiOiB7CiAgICAiZXZlbnRfdHlwZSI6IHsKICAgICAgImVudW0iOiBbCiAgICAgICAgInJ1bnRpbWVfZmFjdCIsCiAgICAgICAgImRyaWZ0IiwKICAgICAgICAicG9saWN5X3Zpb2xhdGlvbiIsCiAgICAgICAgImhlYXJ0YmVhdCIKICAgICAgXQogICAgfSwKICAgICJmaXJtd2FyZV92ZXJzaW9uIjogewogICAgICAibWluTGVuZ3RoIjogMSwKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgfSwKICAgICJncmFwaF9yZXZpc2lvbl9pZCI6IHsKICAgICAgIm1pbkxlbmd0aCI6IDEsCiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0sCiAgICAibGVkZ2VyX2lkIjogewogICAgICAidHlwZSI6ICJzdHJpbmciCiAgICB9LAogICAgIm1vbm90b25pY19uYW5vcyI6IHsKICAgICAgInR5cGUiOiAiaW50ZWdlciIKICAgIH0sCiAgICAib2JzZXJ2ZWRfYXQiOiB7CiAgICAgICJmb3JtYXQiOiAiZGF0ZS10aW1lIiwKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgfSwKICAgICJwYXlsb2FkIjogewogICAgICAiZGVzY3JpcHRpb24iOiAiQ2Fub25pY2FsIHJ1bnRpbWUgcGF5bG9hZCAoSkNTKSB1c2VkIGZvciBoYXNoaW5nLiIsCiAgICAgICJ0eXBlIjogIm9iamVjdCIKICAgIH0sCiAgICAicGF5bG9hZF9oYXNoIjogewogICAgICAiZGVzY3JpcHRpb24iOiAic2hhMjU2IG92ZXIgY2Fub25pY2FsIEpTT04gKEpDUykgb2YgcGF5bG9hZCIsCiAgICAgICJwYXR0ZXJuIjogIl5zaGEyNTY6WzAtOWEtZl17NjR9JCIsCiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0sCiAgICAicG9saWN5X2hhc2giOiB7CiAgICAgICJtaW5MZW5ndGgiOiAxLAogICAgICAidHlwZSI6ICJzdHJpbmciCiAgICB9LAogICAgInByb2plY3RfaWQiOiB7CiAgICAgICJtaW5MZW5ndGgiOiAxLAogICAgICAidHlwZSI6ICJzdHJpbmciCiAgICB9LAogICAgInJlcGxheV9tYW5pZmVzdCI6IHsKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgfSwKICAgICJzZW5zb3JfaWQiOiB7CiAgICAgICJtaW5MZW5ndGgiOiAxLAogICAgICAidHlwZSI6ICJzdHJpbmciCiAgICB9LAogICAgInNpZ25hdHVyZSI6IHsKICAgICAgImRlc2NyaXB0aW9uIjogIkRTU0UgZW52ZWxvcGUgcmVmZXJlbmNlIiwKICAgICAgInBhdHRlcm4iOiAiXmRzc2U6Ly9bQS1aYS16MC05Ll86Ly1dKyQiLAogICAgICAidHlwZSI6ICJzdHJpbmciCiAgICB9LAogICAgInRlbmFudF9pZCI6IHsKICAgICAgIm1pbkxlbmd0aCI6IDEsCiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0KICB9LAogICJyZXF1aXJlZCI6IFsKICAgICJ0ZW5hbnRfaWQiLAogICAgInByb2plY3RfaWQiLAogICAgInNlbnNvcl9pZCIsCiAgICAiZmlybXdhcmVfdmVyc2lvbiIsCiAgICAicG9saWN5X2hhc2giLAogICAgImdyYXBoX3JldmlzaW9uX2lkIiwKICAgICJldmVudF90eXBlIiwKICAgICJvYnNlcnZlZF9hdCIsCiAgICAicGF5bG9hZCIsCiAgICAicGF5bG9hZF9oYXNoIiwKICAgICJzaWduYXR1cmUiCiAgXSwKICAidGl0bGUiOiAiWmFzdGF2YSBPYnNlcnZlciBFdmVudCIsCiAgInR5cGUiOiAib2JqZWN0Igp9Cg",
"payloadType": "application/vnd.stellaops.zastava.schema+json;name=observer_event;version=1",
"signatures": [
{
"keyid": "mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc",
"sig": "axmdd1ucHyZyJMAyLzWmpuai7VrS20QenSDQyXRKlmtsAF4Zl4Ke_cHy8konBStBCoJgGA3SM2236QgAbkQMBw"
}
]
}

View File

@@ -1,8 +1,91 @@
{ {
"$id": "https://stella-ops.org/schemas/zastava/webhook_admission.schema.json", "$id": "https://stella-ops.org/schemas/zastava/webhook_admission.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Zastava Webhook Admission", "properties": {
"type": "object", "bypass_waiver_id": {
"type": "string"
},
"decision": {
"enum": [
"allow",
"deny",
"dry-run"
]
},
"decision_at": {
"format": "date-time",
"type": "string"
},
"decision_reason": {
"minLength": 1,
"type": "string"
},
"graph_revision_id": {
"minLength": 1,
"type": "string"
},
"ledger_id": {
"type": "string"
},
"manifest_pointer": {
"description": "Surface.FS manifest pointer",
"type": "string"
},
"monotonic_nanos": {
"type": "integer"
},
"namespace": {
"minLength": 1,
"type": "string"
},
"payload": {
"description": "AdmissionReview payload (canonical JSON) hashed via payload_hash",
"type": "object"
},
"payload_hash": {
"pattern": "^sha256:[0-9a-f]{64}$",
"type": "string"
},
"policy_hash": {
"minLength": 1,
"type": "string"
},
"project_id": {
"minLength": 1,
"type": "string"
},
"replay_manifest": {
"type": "string"
},
"request_uid": {
"minLength": 1,
"type": "string"
},
"resource_kind": {
"minLength": 1,
"type": "string"
},
"side_effect": {
"enum": [
"none",
"mutating",
"bypass"
]
},
"signature": {
"description": "DSSE envelope reference",
"pattern": "^dsse://[A-Za-z0-9._:/-]+$",
"type": "string"
},
"tenant_id": {
"minLength": 1,
"type": "string"
},
"workload_name": {
"minLength": 1,
"type": "string"
}
},
"required": [ "required": [
"tenant_id", "tenant_id",
"project_id", "project_id",
@@ -16,27 +99,10 @@
"decision_reason", "decision_reason",
"decision_at", "decision_at",
"manifest_pointer", "manifest_pointer",
"payload",
"payload_hash",
"signature" "signature"
], ],
"properties": { "title": "Zastava Webhook Admission",
"tenant_id": { "type": "string" }, "type": "object"
"project_id": { "type": "string" },
"request_uid": { "type": "string" },
"resource_kind": { "type": "string" },
"namespace": { "type": "string" },
"workload_name": { "type": "string" },
"policy_hash": { "type": "string" },
"graph_revision_id": { "type": "string" },
"ledger_id": { "type": "string" },
"replay_manifest": { "type": "string" },
"manifest_pointer": { "type": "string", "description": "Surface.FS manifest pointer" },
"decision": { "enum": ["allow", "deny", "dry-run"] },
"decision_reason": { "type": "string" },
"decision_at": { "type": "string", "format": "date-time" },
"monotonic_nanos": { "type": "integer" },
"side_effect": { "enum": ["none", "mutating", "bypass"] },
"bypass_waiver_id": { "type": "string" },
"payload_hash": { "type": "string" },
"signature": { "type": "string", "description": "DSSE envelope reference" }
}
} }

View File

@@ -0,0 +1,10 @@
{
"payload": "ewogICIkaWQiOiAiaHR0cHM6Ly9zdGVsbGEtb3BzLm9yZy9zY2hlbWFzL3phc3RhdmEvd2ViaG9va19hZG1pc3Npb24uc2NoZW1hLmpzb24iLAogICIkc2NoZW1hIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDcvc2NoZW1hIyIsCiAgInByb3BlcnRpZXMiOiB7CiAgICAiYnlwYXNzX3dhaXZlcl9pZCI6IHsKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgfSwKICAgICJkZWNpc2lvbiI6IHsKICAgICAgImVudW0iOiBbCiAgICAgICAgImFsbG93IiwKICAgICAgICAiZGVueSIsCiAgICAgICAgImRyeS1ydW4iCiAgICAgIF0KICAgIH0sCiAgICAiZGVjaXNpb25fYXQiOiB7CiAgICAgICJmb3JtYXQiOiAiZGF0ZS10aW1lIiwKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgfSwKICAgICJkZWNpc2lvbl9yZWFzb24iOiB7CiAgICAgICJtaW5MZW5ndGgiOiAxLAogICAgICAidHlwZSI6ICJzdHJpbmciCiAgICB9LAogICAgImdyYXBoX3JldmlzaW9uX2lkIjogewogICAgICAibWluTGVuZ3RoIjogMSwKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgfSwKICAgICJsZWRnZXJfaWQiOiB7CiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0sCiAgICAibWFuaWZlc3RfcG9pbnRlciI6IHsKICAgICAgImRlc2NyaXB0aW9uIjogIlN1cmZhY2UuRlMgbWFuaWZlc3QgcG9pbnRlciIsCiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0sCiAgICAibW9ub3RvbmljX25hbm9zIjogewogICAgICAidHlwZSI6ICJpbnRlZ2VyIgogICAgfSwKICAgICJuYW1lc3BhY2UiOiB7CiAgICAgICJtaW5MZW5ndGgiOiAxLAogICAgICAidHlwZSI6ICJzdHJpbmciCiAgICB9LAogICAgInBheWxvYWQiOiB7CiAgICAgICJkZXNjcmlwdGlvbiI6ICJBZG1pc3Npb25SZXZpZXcgcGF5bG9hZCAoY2Fub25pY2FsIEpTT04pIGhhc2hlZCB2aWEgcGF5bG9hZF9oYXNoIiwKICAgICAgInR5cGUiOiAib2JqZWN0IgogICAgfSwKICAgICJwYXlsb2FkX2hhc2giOiB7CiAgICAgICJwYXR0ZXJuIjogIl5zaGEyNTY6WzAtOWEtZl17NjR9JCIsCiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0sCiAgICAicG9saWN5X2hhc2giOiB7CiAgICAgICJtaW5MZW5ndGgiOiAxLAogICAgICAidHlwZSI6ICJzdHJpbmciCiAgICB9LAogICAgInByb2plY3RfaWQiOiB7CiAgICAgICJtaW5MZW5ndGgiOiAxLAogICAgICAidHlwZSI6ICJzdHJpbmciCiAgICB9LAogICAgInJlcGxheV9tYW5pZmVzdCI6IHsKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgfSwKICAgICJyZXF1ZXN0X3VpZCI6IHsKICAgICAgIm1pbkxlbmd0aCI6IDEsCiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0sCiAgICAicmVzb3VyY2Vfa2luZCI6IHsKICAgICAgIm1pbkxlbmd0aCI6IDEsCiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0sCiAgICAic2lkZV9lZmZlY3QiOiB7CiAgICAgICJlbnVtIjogWwogICAgICAgICJub25lIiwKICAgICAgICAibXV0YXRpbmciLAogICAgICAgICJieXBhc3MiCiAgICAgIF0KICAgIH0sCiAgICAic2lnbmF0dXJlIjogewogICAgICAiZGVzY3JpcHRpb24iOiAiRFNTRSBlbnZlbG9wZSByZWZlcmVuY2UiLAogICAgICAicGF0dGVybiI6ICJeZHNzZTovL1tBLVphLXowLTkuXzovLV0rJCIsCiAgICAgICJ0eXBlIjogInN0cmluZyIKICAgIH0sCiAgICAidGVuYW50X2lkIjogewogICAgICAibWluTGVuZ3RoIjogMSwKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgfSwKICAgICJ3b3JrbG9hZF9uYW1lIjogewogICAgICAibWluTGVuZ3RoIjogMSwKICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgfQogIH0sCiAgInJlcXVpcmVkIjogWwogICAgInRlbmFudF9pZCIsCiAgICAicHJvamVjdF9pZCIsCiAgICAicmVxdWVzdF91aWQiLAogICAgInJlc291cmNlX2tpbmQiLAogICAgIm5hbWVzcGFjZSIsCiAgICAid29ya2xvYWRfbmFtZSIsCiAgICAicG9saWN5X2hhc2giLAogICAgImdyYXBoX3JldmlzaW9uX2lkIiwKICAgICJkZWNpc2lvbiIsCiAgICAiZGVjaXNpb25fcmVhc29uIiwKICAgICJkZWNpc2lvbl9hdCIsCiAgICAibWFuaWZlc3RfcG9pbnRlciIsCiAgICAicGF5bG9hZCIsCiAgICAicGF5bG9hZF9oYXNoIiwKICAgICJzaWduYXR1cmUiCiAgXSwKICAidGl0bGUiOiAiWmFzdGF2YSBXZWJob29rIEFkbWlzc2lvbiIsCiAgInR5cGUiOiAib2JqZWN0Igp9Cg",
"payloadType": "application/vnd.stellaops.zastava.schema+json;name=webhook_admission;version=1",
"signatures": [
{
"keyid": "mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc",
"sig": "Vk0mACAjBtUuVn_S2M5HU81zMbH8wDCQYOHVsft7cmxl0JbDrSIA9z3xlTI5JiT7DYOGsDUc96dlC1njldN4Aw"
}
]
}

View File

@@ -0,0 +1,10 @@
{
"payload": "dmVyc2lvbjogMQp1cGRhdGVkX2F0OiAyMDI1LTEyLTAyVDAwOjAwOjAwWgpidWRnZXRzOgogIGxhdGVuY3lfbXNfcDk1OiAyNTAKICBlcnJvcl9yYXRlOiAwLjAxCiAgZHJvcF9yYXRlOiAwLjAwNQpidXJuX3JhdGVzOgogIGFkbWlzc2lvbl9kZW5pZXNfcGVyX21pbjogNQogIG9ic2VydmVyX2RyaWZ0c19wZXJfaG91cjogMgogIGhlYXJ0YmVhdF9taXNzX21pbnV0ZXM6IDMKYWxlcnRzOgogIHRocmVzaG9sZF9jaGFuZ2U6IHRydWUKICBidXJuX3JhdGVfZXhjZWVkZWQ6IHRydWUKICBraWxsX3N3aXRjaF90cmlnZ2VyZWQ6IHRydWUKc2lnbmluZzoKICBwcmVkaWNhdGU6IHN0ZWxsYS5vcHMvemFzdGF2YVRocmVzaG9sZHNAdjEKICBkc3NlX3JlcXVpcmVkOiB0cnVlCg",
"payloadType": "application/vnd.stellaops.zastava.thresholds+yaml;version=1",
"signatures": [
{
"keyid": "mpIEbYRL1q5yhN6wBRvkZ_0xXz3QUJPueJJ8sn__GGc",
"sig": "uQFBmx7vF4fj8uQsCiCN6VbxNS2m3XM-vJNFrj3rexL1PPzHH6IVtWRGexF7CsLrrpUV8U0AmS02S37vOk3zDA"
}
]
}

View File

@@ -44,6 +44,42 @@ This advisory consolidates late-November gap findings across Scanner, SBOM/VEX s
9. **CM9 — Ecosystem coverage**: Track coverage per ecosystem (container, Java, Python, .NET, Go, OS packages) and gaps for ingest support. 9. **CM9 — Ecosystem coverage**: Track coverage per ecosystem (container, Java, Python, .NET, Go, OS packages) and gaps for ingest support.
10. **CM10 — Error resilience & retries**: Standardize retry/backoff/error classification for ingest pipeline; surface diagnostics deterministically. 10. **CM10 — Error resilience & retries**: Standardize retry/backoff/error classification for ingest pipeline; surface diagnostics deterministically.
## OK (Offline Kit) Gaps — OK1OK10
1. **OK1 — Key manifest + PQ co-sign**: Record key IDs and PQ dual-sign toggle in bundle meta; rotate keys ≤90 days. Evidence: `out/mirror/thin/mirror-thin-v1.bundle.json` (`chain_of_custody.keyid`) and `layers/offline-kit-policy.json`.
2. **OK2 — Tool hashing/signing**: Hash build/sign/verify tools and pin them in bundle meta (`tooling.*`); DSSE envelopes cover manifest + bundle meta.
3. **OK3 — DSSE top-level manifest**: Ship DSSE for bundle meta (`mirror-thin-v1.bundle.dsse.json`) linking manifest, tarball, policies, and optional OCI layout.
4. **OK4 — Checkpoint freshness + mirror metadata**: Enforce `checkpoint_freshness_seconds` and timestamped `created` in bundle meta; require checkpoints in `transport-plan.json`.
5. **OK5 — Deterministic packaging flags**: Capture tar/gzip flags in `layers/offline-kit-policy.json` and verify via `scripts/mirror/verify_thin_bundle.py` determinism checks.
6. **OK6 — Scan/VEX/policy/graph hashes**: Include `layers/artifact-hashes.json` with digests for scan/vex/policy/graph fixtures and reference from bundle meta.
7. **OK7 — Time anchor bundling**: Embed `layers/time-anchor.json` digest in bundle meta and surface trust-root path for AIRGAP-TIME.
8. **OK8 — Transport/chunking + chain-of-custody**: Define chunk sizing, retry policy, and signed chain-of-custody in `layers/transport-plan.json` (includes build/sign digests + keyid).
9. **OK9 — Tenant/environment scoping**: Require `tenant`/`environment` fields in bundle meta; verifier enforces via `--tenant/--environment` flags.
10. **OK10 — Scripted verify + negative paths**: `scripts/mirror/verify_thin_bundle.py` validates required layers, DSSE, sidecars, tool hashes, and scope; fails fast on missing/stale artefacts.
## RK (Rekor) Gaps — RK1RK10
1. **RK1 — DSSE/hashedrekord only**: `layers/rekor-policy.json` sets `rk1_enforceDsse=true` and routes both public/private to hashedrekord.
2. **RK2 — Payload size preflight + chunks**: `rk2_payloadMaxBytes=1048576` with chunking guidance in `transport-plan.json`.
3. **RK3 — Public/private routing policy**: Explicit routing map (`rk3_routing`) for shard-aware submission.
4. **RK4 — Shard-aware checkpoints**: `rk4_shardCheckpoint="per-tenant-per-day"` plus checkpoint freshness from bundle meta.
5. **RK5 — Idempotent submission keys**: `rk5_idempotentKeys=true` to prevent duplicate entries.
6. **RK6 — Sigstore bundles in kits**: `rk6_sigstoreBundleIncluded=true`; bundle meta lists DSSE artefacts for offline kits.
7. **RK7 — Checkpoint freshness bounds**: `rk7_checkpointFreshnessSeconds` mirrors bundle freshness budget.
8. **RK8 — PQ dual-sign options**: `rk8_pqDualSign` mirrors PQ toggle (env `PQ_CO_SIGN_REQUIRED`).
9. **RK9 — Error taxonomy/backoff**: Enumerated in `rk9_errorTaxonomy` and retried per `transport-plan.json` retry policy.
10. **RK10 — Policy/graph annotations**: `rk10_annotations` require policy + graph context inside DSSE/bundle records.
## MS (Mirror Strategy) Gaps — MS1MS10
1. **MS1 — Signed/versioned mirror schemas**: `layers/mirror-policy.json` tracks `schemaVersion` + semver; DSSE of bundle meta ties schema to artefacts.
2. **MS2 — DSSE/TUF rotation policy (incl. PQ)**: `dsseTufRotationDays=30` and `pqDualSign` toggle documented in mirror policy and bundle meta.
3. **MS3 — Delta spec with tombstones/base hash**: Mirror policy `delta` enforces tombstones and base-hash requirements for deltas.
4. **MS4 — Time-anchor freshness enforcement**: `timeAnchorFreshnessSeconds` plus bundled `time-anchor.json` digest.
5. **MS5 — Tenant/env scoping**: Tenant/environment fields required in bundle meta; verifier flags mismatches.
6. **MS6 — Distribution integrity (HTTP/OCI/object)**: `distributionIntegrity` enumerates integrity strategies for each transport.
7. **MS7 — Chunking/size rules**: `chunking.sizeBytes` + `maxChunks` pinned in mirror policy and reflected in transport plan.
8. **MS8 — Standard verify script**: `verifyScript` references `scripts/mirror/verify_thin_bundle.py`; bundle meta recorded in DSSE envelope.
9. **MS9 — Metrics/alerts**: Mirror policy `metrics` marks build/import/verify signals required for observability.
10. **MS10 — SemVer/change log**: `changelog` block declares current format version; future bumps must be appended with deterministic notes.
## Pending Families (to be expanded) ## Pending Families (to be expanded)
The following gap families were referenced in November indices and still need detailed findings written out: The following gap families were referenced in November indices and still need detailed findings written out:
- CV1CV10 (CVSS v4 receipts), CVM1CVM10 (momentum), FC1FC10 (SCA fixture gaps), OB1OB10 (onboarding), IG1IG10 (implementor guidance), RR1RR10 (Rekor receipts), SK1SK10 (standups), MI1MI10 (UI micro-interactions), PVX1PVX10 (Proof-linked VEX UI), TTE1TTE10 (Time-to-Evidence), AR-EP1…AR-VB1 (archived advisories revival), BP1BP10 (SBOM→VEX proof pipeline), UT1UT10 (unknown heuristics), CE1CE10 (evidence patterns), ET1ET10 (ecosystem fixtures), RB1RB10 (reachability fixtures), G1G12 / RD1RD10 (reachability benchmark/dataset), UN1UN10 (unknowns registry), U1U10 (decay), EX1EX10 (explainability), VEX1VEX10 (VEX claims), BR1BR10 (binary reachability), VT1VT10 (triage), PL1PL10 (plugin arch), EB1EB10 (evidence baseline), EC1EC10 (export center), AT1AT10 (automation), OK1OK10 / RK1RK10 / MS1MS10 (offline/mirror/Rekor kits), TP1TP10 (task packs), AU1AU10 (auth), CL1CL10 (CLI), OR1OR10 (orchestrator), ZR1ZR10 (Zastava), NR1NR10 (Notify), GA1GA10 (graph analytics), TO1TO10 (telemetry), PS1PS10 (policy), FL1FL10 (ledger), CI1CI10 (Concelier ingest). - CV1CV10 (CVSS v4 receipts), CVM1CVM10 (momentum), FC1FC10 (SCA fixture gaps), OB1OB10 (onboarding), IG1IG10 (implementor guidance), RR1RR10 (Rekor receipts), SK1SK10 (standups), MI1MI10 (UI micro-interactions), PVX1PVX10 (Proof-linked VEX UI), TTE1TTE10 (Time-to-Evidence), AR-EP1…AR-VB1 (archived advisories revival), BP1BP10 (SBOM→VEX proof pipeline), UT1UT10 (unknown heuristics), CE1CE10 (evidence patterns), ET1ET10 (ecosystem fixtures), RB1RB10 (reachability fixtures), G1G12 / RD1RD10 (reachability benchmark/dataset), UN1UN10 (unknowns registry), U1U10 (decay), EX1EX10 (explainability), VEX1VEX10 (VEX claims), BR1BR10 (binary reachability), VT1VT10 (triage), PL1PL10 (plugin arch), EB1EB10 (evidence baseline), EC1EC10 (export center), AT1AT10 (automation), OK1OK10 / RK1RK10 / MS1MS10 (offline/mirror/Rekor kits), TP1TP10 (task packs), AU1AU10 (auth), CL1CL10 (CLI), OR1OR10 (orchestrator), ZR1ZR10 (Zastava), NR1NR10 (Notify), GA1GA10 (graph analytics), TO1TO10 (telemetry), PS1PS10 (policy), FL1FL10 (ledger), CI1CI10 (Concelier ingest).

View File

@@ -42,6 +42,7 @@ Out of scope: implementing disassemblers or symbol servers; those will be handle
* Update analyzer contracts so every analyzer returns both `symbol_id` and `code_id`, with demangled names stored under the new `symbol` block. * Update analyzer contracts so every analyzer returns both `symbol_id` and `code_id`, with demangled names stored under the new `symbol` block.
* Persist the data into `richgraph-v1` payloads and attach CAS URIs via `StellaOps.Scanner.Reachability`. * Persist the data into `richgraph-v1` payloads and attach CAS URIs via `StellaOps.Scanner.Reachability`.
* Deliver fixtures in `tests/reachability/StellaOps.ScannerSignals.IntegrationTests` that prove determinism (same hash when analyzer flags reorder). * Deliver fixtures in `tests/reachability/StellaOps.ScannerSignals.IntegrationTests` that prove determinism (same hash when analyzer flags reorder).
* **Helper status (2025-12-02):** `SymbolId.ForBinaryAddressed` + `CodeId.ForBinarySegment` now encode `{file_hash, section, addr, name, linkage, length, code_block_hash}` with normalized hex addresses. Analyzers should start emitting these tuples instead of ad-hoc hashes.
### 3.2 Runtime + Signals (GAP-ZAS-002 / GAP-SIG-003) ### 3.2 Runtime + Signals (GAP-ZAS-002 / GAP-SIG-003)

View File

@@ -0,0 +1,15 @@
{
"created": "2025-12-02T18:08:34Z",
"manifest": {"path": "mirror-thin-v1.manifest.json", "sha256": "1affb0b796ff037117b46aa1f1d8056a9c80755e925af058ea72132ba158becf"},
"tarball": {"path": "mirror-thin-v1.tar.gz", "sha256": "fb1ce26388a1f1ab2eb90aae6d63ac05de326fbbd947fbf7a17b980232c9fc7d"},
"dsse": {"path": "mirror-thin-v1.manifest.dsse.json", "sha256": "f4a2a99fdfa60b3bd98daf88faabcf5d525b7f4a40fad606a502c3e25f9b2a7f"},
"bundle": {"path": "mirror-thin-v1.bundle.json", "sha256": "a3b16f5d1b74ffdf9aedbbfe9282d368dc3dcf70676c8ac7e8cdd984162e7f90"},
"bundle_dsse": {"path": "mirror-thin-v1.bundle.dsse.json", "sha256": "5fd3025c03cc4c19708eeec8feaa129a4e567dcefd06cb01f251a38590f76dde"},
"time_anchor": null
,"policies": {
"transport": {"path": "transport-plan.json", "sha256": "df82a56d9bacb00a1882f5d6d9f9ba469b62b89bd949899b7049e123c1e65914"},
"rekor": {"path": "rekor-policy.json", "sha256": "652df157628db73e9aa0110e7390f8773319c24530e00873afcfdf972644717e"},
"mirror": {"path": "mirror-policy.json", "sha256": "d7059d4b9e7e207f2420520bf73cf69b644eec0e866f039a1f7d0dc2b3bc1192"},
"offline": {"path": "offline-kit-policy.json", "sha256": "ae2513f9768f3f7c0b0994b54f539b2a933e1e851c25c26c8fe46fd963d90579"}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,117 @@
{
"artifacts": {
"artifact_hashes": {
"path": "artifact-hashes.json",
"sha256": "55f24bdc3d28a5596f4f8a36292820356de50aa2e9c5c2fb81397bfe2891ca4d"
},
"bundle_dsse": {
"path": "mirror-thin-v1.bundle.dsse.json",
"sha256": null
},
"bundle_meta": {
"path": "mirror-thin-v1.bundle.json",
"sha256": null
},
"manifest": {
"path": "mirror-thin-v1.manifest.json",
"sha256": "1affb0b796ff037117b46aa1f1d8056a9c80755e925af058ea72132ba158becf"
},
"manifest_dsse": {
"path": "mirror-thin-v1.manifest.dsse.json",
"sha256": null
},
"mirror_policy": {
"path": "mirror-policy.json",
"sha256": "d7059d4b9e7e207f2420520bf73cf69b644eec0e866f039a1f7d0dc2b3bc1192"
},
"oci_index": {
"path": "oci/index.json",
"sha256": "5daf8024f0f3b37c2077497c54ac3d7bda4aaed59b3c47c605c535662f7a53a5"
},
"offline_policy": {
"path": "offline-kit-policy.json",
"sha256": "ae2513f9768f3f7c0b0994b54f539b2a933e1e851c25c26c8fe46fd963d90579"
},
"rekor_policy": {
"path": "rekor-policy.json",
"sha256": "652df157628db73e9aa0110e7390f8773319c24530e00873afcfdf972644717e"
},
"tarball": {
"path": "mirror-thin-v1.tar.gz",
"sha256": "fb1ce26388a1f1ab2eb90aae6d63ac05de326fbbd947fbf7a17b980232c9fc7d"
},
"time_anchor": {
"path": "time-anchor.json",
"sha256": "c27a0fb0dfa8a9558aaabf8011040abcd4170cf62e36d16b5b1767368f7828ff"
},
"transport_plan": {
"path": "transport-plan.json",
"sha256": "df82a56d9bacb00a1882f5d6d9f9ba469b62b89bd949899b7049e123c1e65914"
}
},
"bundle": "mirror-thin-v1",
"chain_of_custody": [
{
"sha256": "dd11c674629fe94bf37ac9a29d7ae32241f6a17815bb275532d9a78b3d851049",
"step": "build",
"tool": "make-thin-v1.sh"
},
{
"key_present": true,
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"step": "sign",
"tool": "sign_thin_bundle.py"
}
],
"checkpoint_freshness_seconds": 86400,
"chunk_size_bytes": 5242880,
"created": "2025-12-02T18:08:34Z",
"environment": "lab",
"gaps": {
"ms": [
"MS1 mirror schema versioned in mirror-policy.json",
"MS2 DSSE/TUF rotation days recorded",
"MS3 delta spec includes tombstones + base hash",
"MS4 time-anchor freshness enforced",
"MS5 tenant/env scoping captured",
"MS6 distribution integrity rules documented",
"MS7 chunking/size rules recorded",
"MS8 verify script pinned",
"MS9 metrics/alerts required",
"MS10 semver/changelog noted"
],
"ok": [
"OK1 key manifest + PQ co-sign recorded in offline-kit-policy.json",
"OK2 tool hashing captured in bundle_meta.tooling",
"OK3 DSSE top-level manifest planned via bundle.dsse",
"OK4 checkpoint freshness enforced with checkpoint_freshness_seconds",
"OK5 deterministic packaging flags recorded in offline-kit-policy.json",
"OK6 scan/VEX/policy/graph hashes captured in artifact-hashes.json",
"OK7 time anchor bundled as layers/time-anchor.json",
"OK8 transport + chunking defined in transport-plan.json",
"OK9 tenant/environment scoping recorded in bundle meta",
"OK10 scripted verify path is scripts/mirror/verify_thin_bundle.py"
],
"rk": [
"RK1 enforce dsse/hashedrekord policy in rekor-policy.json",
"RK2 payload size preflight rk2_payloadMaxBytes",
"RK3 routing policy for public/private recorded",
"RK4 shard-aware checkpoints per-tenant-per-day",
"RK5 idempotent submission keys enabled",
"RK6 Sigstore bundle inclusion flagged true",
"RK7 checkpoint freshness seconds recorded",
"RK8 PQ dual-sign toggle matches pqDualSign",
"RK9 error taxonomy enumerated",
"RK10 policy/graph annotations required"
]
},
"pq_cosign_required": false,
"tenant": "tenant-demo",
"tooling": {
"make_thin_v1_sh": "dd11c674629fe94bf37ac9a29d7ae32241f6a17815bb275532d9a78b3d851049",
"sign_script": "30268f3b6d11a1108a8cb5a5ebc9723c34a67cf1e12944b1014cc76965619b73",
"verify_oci": "04b6b0424a725d2081275e67820c580b532646fd640ee9bf62bc75bc7554eb77",
"verify_script": "0794f79851bd71c0e07425e6928f038286957f3babc95ca66660acb6c5d8c31b"
},
"version": "1.0.0"
}

View File

@@ -0,0 +1 @@
a3b16f5d1b74ffdf9aedbbfe9282d368dc3dcf70676c8ac7e8cdd984162e7f90 mirror-thin-v1.bundle.json

View File

@@ -1,10 +1,10 @@
{ {
"payload": "ewogICJjcmVhdGVkIjogIjIwMjUtMTEtMjNUMDA6MDA6MDBaIiwKICAiaW5kZXhlcyI6IFsKICAgIHsKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6YjY0YzdlNWQ0NDA4YTEwMDMxMWVjOGZhYmM3NmI5ZTUyNTE2NWUyMWRmZmMzZjQ2NDFhZjc5YjlhYTQ0MzNjOSIsCiAgICAgICJuYW1lIjogIm9ic2VydmF0aW9ucy5pbmRleCIKICAgIH0KICBdLAogICJsYXllcnMiOiBbCiAgICB7CiAgICAgICJkaWdlc3QiOiAic2hhMjU2OmZkM2NlNTA0OTdjYmQyMDNkZjIyY2QyZmQxNDY0NmIxYWFjODU4ODRlZDE2MzIxNWE3OWM2MjA3MzAxMjQ1ZDYiLAogICAgICAicGF0aCI6ICJsYXllcnMvb2JzZXJ2YXRpb25zLm5kanNvbiIsCiAgICAgICJzaXplIjogMzEwCiAgICB9LAogICAgewogICAgICAiZGlnZXN0IjogInNoYTI1NjpjMjdhMGZiMGRmYThhOTU1OGFhYWJmODAxMTA0MGFiY2Q0MTcwY2Y2MmUzNmQxNmI1YjE3NjczNjhmNzgyOGZmIiwKICAgICAgInBhdGgiOiAibGF5ZXJzL3RpbWUtYW5jaG9yLmpzb24iLAogICAgICAic2l6ZSI6IDMyMgogICAgfQogIF0sCiAgInZlcnNpb24iOiAiMS4wLjAiCn0K", "payload": "ewogICJjcmVhdGVkIjogIjIwMjUtMTItMDJUMTg6MDg6MzRaIiwKICAiaW5kZXhlcyI6IFsKICAgIHsKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6YjY0YzdlNWQ0NDA4YTEwMDMxMWVjOGZhYmM3NmI5ZTUyNTE2NWUyMWRmZmMzZjQ2NDFhZjc5YjlhYTQ0MzNjOSIsCiAgICAgICJuYW1lIjogIm9ic2VydmF0aW9ucy5pbmRleCIKICAgIH0KICBdLAogICJsYXllcnMiOiBbCiAgICB7CiAgICAgICJkaWdlc3QiOiAic2hhMjU2OjU1ZjI0YmRjM2QyOGE1NTk2ZjRmOGEzNjI5MjgyMDM1NmRlNTBhYTJlOWM1YzJmYjgxMzk3YmZlMjg5MWNhNGQiLAogICAgICAicGF0aCI6ICJsYXllcnMvYXJ0aWZhY3QtaGFzaGVzLmpzb24iLAogICAgICAic2l6ZSI6IDU5MgogICAgfSwKICAgIHsKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6ZDcwNTlkNGI5ZTdlMjA3ZjI0MjA1MjBiZjczY2Y2OWI2NDRlZWMwZTg2NmYwMzlhMWY3ZDBkYzJiM2JjMTE5MiIsCiAgICAgICJwYXRoIjogImxheWVycy9taXJyb3ItcG9saWN5Lmpzb24iLAogICAgICAic2l6ZSI6IDY2NQogICAgfSwKICAgIHsKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6ZmQzY2U1MDQ5N2NiZDIwM2RmMjJjZDJmZDE0NjQ2YjFhYWM4NTg4NGVkMTYzMjE1YTc5YzYyMDczMDEyNDVkNiIsCiAgICAgICJwYXRoIjogImxheWVycy9vYnNlcnZhdGlvbnMubmRqc29uIiwKICAgICAgInNpemUiOiAzMTAKICAgIH0sCiAgICB7CiAgICAgICJkaWdlc3QiOiAic2hhMjU2OmFlMjUxM2Y5NzY4ZjNmN2MwYjA5OTRiNTRmNTM5YjJhOTMzZTFlODUxYzI1YzI2YzhmZTQ2ZmQ5NjNkOTA1NzkiLAogICAgICAicGF0aCI6ICJsYXllcnMvb2ZmbGluZS1raXQtcG9saWN5Lmpzb24iLAogICAgICAic2l6ZSI6IDU0NAogICAgfSwKICAgIHsKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6NjUyZGYxNTc2MjhkYjczZTlhYTAxMTBlNzM5MGY4NzczMzE5YzI0NTMwZTAwODczYWZjZmRmOTcyNjQ0NzE3ZSIsCiAgICAgICJwYXRoIjogImxheWVycy9yZWtvci1wb2xpY3kuanNvbiIsCiAgICAgICJzaXplIjogNDY1CiAgICB9LAogICAgewogICAgICAiZGlnZXN0IjogInNoYTI1NjpjMjdhMGZiMGRmYThhOTU1OGFhYWJmODAxMTA0MGFiY2Q0MTcwY2Y2MmUzNmQxNmI1YjE3NjczNjhmNzgyOGZmIiwKICAgICAgInBhdGgiOiAibGF5ZXJzL3RpbWUtYW5jaG9yLmpzb24iLAogICAgICAic2l6ZSI6IDMyMgogICAgfSwKICAgIHsKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6ZGY4MmE1NmQ5YmFjYjAwYTE4ODJmNWQ2ZDlmOWJhNDY5YjYyYjg5YmQ5NDk4OTliNzA0OWUxMjNjMWU2NTkxNCIsCiAgICAgICJwYXRoIjogImxheWVycy90cmFuc3BvcnQtcGxhbi5qc29uIiwKICAgICAgInNpemUiOiA3NDEKICAgIH0KICBdLAogICJ2ZXJzaW9uIjogIjEuMC4wIgp9Cg",
"payloadType": "application/vnd.stellaops.mirror.manifest+json", "payloadType": "application/vnd.stellaops.mirror.manifest+json",
"signatures": [ "signatures": [
{ {
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8", "keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "EC7tbq5zlHqUfidvkT-Q1yfmiTJs9KUdpnvs9jCBJXsxzIyB1hzfdh-7FNPi3pFSrzV6cDh47cWvWmMR_ypgDw" "sig": "f3XR6taW0E9gAkBEYPgxsWEI2cO28-1zA4XhcepzXm3FJ7Ii8ksfp_nFWH1m4JT4JRUK5tRcc8X4Bw_SSRRkDg"
} }
] ]
} }

View File

@@ -1,5 +1,5 @@
{ {
"created": "2025-11-23T00:00:00Z", "created": "2025-12-02T18:08:34Z",
"indexes": [ "indexes": [
{ {
"digest": "sha256:b64c7e5d4408a100311ec8fabc76b9e525165e21dffc3f4641af79b9aa4433c9", "digest": "sha256:b64c7e5d4408a100311ec8fabc76b9e525165e21dffc3f4641af79b9aa4433c9",
@@ -7,15 +7,40 @@
} }
], ],
"layers": [ "layers": [
{
"digest": "sha256:55f24bdc3d28a5596f4f8a36292820356de50aa2e9c5c2fb81397bfe2891ca4d",
"path": "layers/artifact-hashes.json",
"size": 592
},
{
"digest": "sha256:d7059d4b9e7e207f2420520bf73cf69b644eec0e866f039a1f7d0dc2b3bc1192",
"path": "layers/mirror-policy.json",
"size": 665
},
{ {
"digest": "sha256:fd3ce50497cbd203df22cd2fd14646b1aac85884ed163215a79c6207301245d6", "digest": "sha256:fd3ce50497cbd203df22cd2fd14646b1aac85884ed163215a79c6207301245d6",
"path": "layers/observations.ndjson", "path": "layers/observations.ndjson",
"size": 310 "size": 310
}, },
{
"digest": "sha256:ae2513f9768f3f7c0b0994b54f539b2a933e1e851c25c26c8fe46fd963d90579",
"path": "layers/offline-kit-policy.json",
"size": 544
},
{
"digest": "sha256:652df157628db73e9aa0110e7390f8773319c24530e00873afcfdf972644717e",
"path": "layers/rekor-policy.json",
"size": 465
},
{ {
"digest": "sha256:c27a0fb0dfa8a9558aaabf8011040abcd4170cf62e36d16b5b1767368f7828ff", "digest": "sha256:c27a0fb0dfa8a9558aaabf8011040abcd4170cf62e36d16b5b1767368f7828ff",
"path": "layers/time-anchor.json", "path": "layers/time-anchor.json",
"size": 322 "size": 322
},
{
"digest": "sha256:df82a56d9bacb00a1882f5d6d9f9ba469b62b89bd949899b7049e123c1e65914",
"path": "layers/transport-plan.json",
"size": 741
} }
], ],
"version": "1.0.0" "version": "1.0.0"

View File

@@ -1 +1 @@
b0e5d5af5b560d1b24cf44c2325e7f90d486857f347f34826b9f06aa217c5a6a mirror-thin-v1.manifest.json 1affb0b796ff037117b46aa1f1d8056a9c80755e925af058ea72132ba158becf mirror-thin-v1.manifest.json

View File

@@ -1 +1 @@
1ef17d14c09e74703b88753d6c561d8c8a8809fe8e05972257adadfb91b71723 mirror-thin-v1.tar.gz fb1ce26388a1f1ab2eb90aae6d63ac05de326fbbd947fbf7a17b980232c9fc7d mirror-thin-v1.tar.gz

View File

@@ -3,8 +3,8 @@
"manifests": [ "manifests": [
{ {
"mediaType": "application/vnd.oci.image.manifest.v1+json", "mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:f6bd80fe9d346e7306c69832e29180346454005a0751c77ae2ebb7332be94642", "digest": "sha256:0074121d4adef7dc8181607645af330a475608b0d52909e0efd421508f14437d",
"size": 485, "size": 486,
"annotations": {"org.opencontainers.image.ref.name": "mirror-thin-v1"} "annotations": {"org.opencontainers.image.ref.name": "mirror-thin-v1"}
} }
] ]

View File

@@ -8,8 +8,8 @@
"layers": [ "layers": [
{ {
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"size": 830, "size": 2468,
"digest": "sha256:1ef17d14c09e74703b88753d6c561d8c8a8809fe8e05972257adadfb91b71723", "digest": "sha256:fb1ce26388a1f1ab2eb90aae6d63ac05de326fbbd947fbf7a17b980232c9fc7d",
"annotations": {"org.stellaops.bundle.type": "mirror-thin-v1"} "annotations": {"org.stellaops.bundle.type": "mirror-thin-v1"}
} }
] ]

View File

@@ -0,0 +1,20 @@
{
"artifacts": {
"graph": {
"digest": "sha256:652df157628db73e9aa0110e7390f8773319c24530e00873afcfdf972644717e",
"id": "graph-fixture-1"
},
"policy": {
"digest": "sha256:d7059d4b9e7e207f2420520bf73cf69b644eec0e866f039a1f7d0dc2b3bc1192",
"id": "policy-fixture-1"
},
"scan": {
"digest": "sha256:fd3ce50497cbd203df22cd2fd14646b1aac85884ed163215a79c6207301245d6",
"id": "scan-fixture-1"
},
"vex": {
"digest": "sha256:fd3ce50497cbd203df22cd2fd14646b1aac85884ed163215a79c6207301245d6",
"id": "vex-fixture-1"
}
}
}

View File

@@ -0,0 +1,15 @@
{
"schemaVersion": "mirror-thin-v1",
"semver": "1.0.0",
"dsseTufRotationDays": 30,
"pqDualSign": false,
"delta": {"tombstones": true, "baseHashRequired": true},
"timeAnchorFreshnessSeconds": 86400,
"tenantScope": "tenant-demo",
"environment": "lab",
"distributionIntegrity": {"http": "sha256+dsse", "oci": "tuf+dsse", "object": "checksum+length"},
"chunking": {"sizeBytes": 5242880, "maxChunks": 128},
"verifyScript": "scripts/mirror/verify_thin_bundle.py",
"metrics": {"build": "required", "import": "required", "verify": "required"},
"changelog": {"current": "mirror-thin-v1", "notes": "Adds offline/rekor policy coverage (MS1-MS10)"}
}

View File

@@ -0,0 +1,14 @@
{
"okVersion": "1.0.0",
"keyManifest": {"rotationDays": 90, "pqCosignAllowed": false},
"toolHashing": true,
"topLevelDsse": true,
"checkpointFreshnessSeconds": 86400,
"deterministicFlags": ["tar --sort=name --owner=0 --group=0 --numeric-owner --mtime=1970-01-01", "gzip -n"],
"contentHashes": "layers/artifact-hashes.json",
"timeAnchorPath": "layers/time-anchor.json",
"transportPlan": "layers/transport-plan.json",
"tenant": "tenant-demo",
"environment": "lab",
"verifyScript": "scripts/mirror/verify_thin_bundle.py"
}

View File

@@ -0,0 +1,12 @@
{
"rk1_enforceDsse": true,
"rk2_payloadMaxBytes": 1048576,
"rk3_routing": {"public": "hashedrekord", "private": "hashedrekord"},
"rk4_shardCheckpoint": "per-tenant-per-day",
"rk5_idempotentKeys": true,
"rk6_sigstoreBundleIncluded": true,
"rk7_checkpointFreshnessSeconds": 86400,
"rk8_pqDualSign": false,
"rk9_errorTaxonomy": ["quota", "payload-too-large", "invalid-signature", "stale-checkpoint"],
"rk10_annotations": ["policy", "graph-edge"]
}

View File

@@ -0,0 +1,11 @@
{
"chunkSizeBytes": 5242880,
"compression": "gzip",
"checkpointFreshnessSeconds": 86400,
"chainOfCustody": [
{"step": "build", "actor": "make-thin-v1.sh", "evidence": "sha256:dd11c674629fe94bf37ac9a29d7ae32241f6a17815bb275532d9a78b3d851049", "negativePaths": ["missing-layer", "non-deterministic-tar"]},
{"step": "sign", "actor": "sign_thin_bundle.py", "expectedEnvelope": "mirror-thin-v1.manifest.dsse.json", "keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8", "toolDigest": "sha256:30268f3b6d11a1108a8cb5a5ebc9723c34a67cf1e12944b1014cc76965619b73"}
],
"chunking": {"maxChunks": 128, "strategy": "deterministic-size"},
"ingest": {"expectedLatencySeconds": 120, "retryPolicy": "exponential"}
}

View File

@@ -1,5 +1,5 @@
{ {
"created": "2025-11-23T00:00:00Z", "created": "2025-12-02T18:08:34Z",
"indexes": [ "indexes": [
{ {
"digest": "sha256:b64c7e5d4408a100311ec8fabc76b9e525165e21dffc3f4641af79b9aa4433c9", "digest": "sha256:b64c7e5d4408a100311ec8fabc76b9e525165e21dffc3f4641af79b9aa4433c9",
@@ -7,15 +7,40 @@
} }
], ],
"layers": [ "layers": [
{
"digest": "sha256:55f24bdc3d28a5596f4f8a36292820356de50aa2e9c5c2fb81397bfe2891ca4d",
"path": "layers/artifact-hashes.json",
"size": 592
},
{
"digest": "sha256:d7059d4b9e7e207f2420520bf73cf69b644eec0e866f039a1f7d0dc2b3bc1192",
"path": "layers/mirror-policy.json",
"size": 665
},
{ {
"digest": "sha256:fd3ce50497cbd203df22cd2fd14646b1aac85884ed163215a79c6207301245d6", "digest": "sha256:fd3ce50497cbd203df22cd2fd14646b1aac85884ed163215a79c6207301245d6",
"path": "layers/observations.ndjson", "path": "layers/observations.ndjson",
"size": 310 "size": 310
}, },
{
"digest": "sha256:ae2513f9768f3f7c0b0994b54f539b2a933e1e851c25c26c8fe46fd963d90579",
"path": "layers/offline-kit-policy.json",
"size": 544
},
{
"digest": "sha256:652df157628db73e9aa0110e7390f8773319c24530e00873afcfdf972644717e",
"path": "layers/rekor-policy.json",
"size": 465
},
{ {
"digest": "sha256:c27a0fb0dfa8a9558aaabf8011040abcd4170cf62e36d16b5b1767368f7828ff", "digest": "sha256:c27a0fb0dfa8a9558aaabf8011040abcd4170cf62e36d16b5b1767368f7828ff",
"path": "layers/time-anchor.json", "path": "layers/time-anchor.json",
"size": 322 "size": 322
},
{
"digest": "sha256:df82a56d9bacb00a1882f5d6d9f9ba469b62b89bd949899b7049e123c1e65914",
"path": "layers/transport-plan.json",
"size": 741
} }
], ],
"version": "1.0.0" "version": "1.0.0"

View File

@@ -23,7 +23,7 @@
"signatures": [ "signatures": [
{ {
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8", "keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "ZUXDqV5hn0cuZlOOEUZdpD474mc0bkJu4-LyBPNYwU3YkZufT2eXKM-QHksF4JoXgywbY9QD8qhnsEh05xoKBg" "sig": "b9UQWxXZnpsltfVLch4KVKWitgd6ZHTOPvUp0w-e5Gbm8MY6ZBaM-JLP-lwLuiJQMgbhuOlzDVzLbgQoYsbzBw"
} }
], ],
"spec_version": "1.0.31", "spec_version": "1.0.31",

View File

@@ -5,7 +5,7 @@
"signatures": [ "signatures": [
{ {
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8", "keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "Z2FtwGRtVhQNvNZUxceUb3Ygj5KNqJGTOFIq8CxltBvMfmaAavWmMST0shir7p-7LI3-kBUMdPOKYlGxFip3AQ" "sig": "3zzhK_zR4cqN5GQ-WvsDE93He22enjx2oy9WdSxox6hw4rVMY-QhPnagMSRQKOxWVVPgPWNZOsJR8_LOi0H6Cw"
} }
], ],
"spec_version": "1.0.31", "spec_version": "1.0.31",

View File

@@ -4,7 +4,7 @@
"signatures": [ "signatures": [
{ {
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8", "keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "SIKtu5qz3FYNQxittPQwwWUzQLRg9D6KpO3OKpxtZzrbD2S5corjRZg-JNymPzFoEbrm8i5b_p7sh6H44At-CQ" "sig": "HkXgkY5l9ACl1nNZ7Ll-hnVC_8Zo1QSWOb7Q74THnlYlDdpg_d-gnruFeOrIxXix18IGCICqrfKfnERoR-8EAw"
} }
], ],
"spec_version": "1.0.31", "spec_version": "1.0.31",

View File

@@ -5,7 +5,7 @@
"signatures": [ "signatures": [
{ {
"keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8", "keyid": "db9928babf3aeb817ccdcd0f6a6688f8395b00d0e42966e32e706931b5301fc8",
"sig": "C_4pXTUzKaVEZ0Dwtn2FlXxOsxcht8nF_vdWwVOMsYqwrqYriZgd4x_r2lq_RnI5QYxagEHGnEjD-6ztEeRMCg" "sig": "UKplo5ExWrbnIpxo31NjgDEW9xGVb_ypesrqjnpOornojmOUkZjN1rGmyHmhJGam6RoHAboX_KNZJUwIe-K4Dw"
} }
], ],
"spec_version": "1.0.31", "spec_version": "1.0.31",

View File

@@ -1,9 +1,9 @@
# Mirror signing helpers # Mirror signing helpers
- `make-thin-v1.sh`: builds thin bundle v1, computes checksums, optional DSSE+TUF signing when `SIGN_KEY` is set, and runs verifier. - `make-thin-v1.sh`: builds thin bundle v1, computes checksums, emits bundle meta (offline/rekor/mirror gaps), optional DSSE+TUF signing when `SIGN_KEY` is set, and runs verifier.
- `sign_thin_bundle.py`: signs manifest (DSSE) and root/targets/snapshot/timestamp JSON using an Ed25519 PEM key. - `sign_thin_bundle.py`: signs manifest (DSSE), bundle meta (DSSE), and root/targets/snapshot/timestamp JSON using an Ed25519 PEM key.
- `verify_thin_bundle.py`: checks SHA256 sidecars, manifest schema, tar determinism, and manifest/index digests. - `verify_thin_bundle.py`: checks SHA256 sidecars, manifest schema, tar determinism, required layers, optional bundle meta and DSSE signatures; accepts `--bundle-meta`, `--pubkey`, `--tenant`, `--environment`.
- `ci-sign.sh`: CI wrapper. Set `MIRROR_SIGN_KEY_B64` (base64-encoded Ed25519 PEM) and run; it builds, signs, and verifies in one step. - `ci-sign.sh`: CI wrapper. Set `MIRROR_SIGN_KEY_B64` (base64-encoded Ed25519 PEM) and run; it builds, signs, and verifies in one step, emitting `milestone.json` with manifest/tar/bundle hashes.
- `verify_oci_layout.py`: validates OCI layout/index/manifest and blob digests when `OCI=1` is used. - `verify_oci_layout.py`: validates OCI layout/index/manifest and blob digests when `OCI=1` is used.
Artifacts live under `out/mirror/thin/`. Artifacts live under `out/mirror/thin/`.

View File

@@ -23,12 +23,23 @@ chmod 600 "$KEYFILE"
openssl pkey -in "$KEYFILE" -pubout -out "$KEYDIR/ci-ed25519.pub" >/dev/null 2>&1 openssl pkey -in "$KEYFILE" -pubout -out "$KEYDIR/ci-ed25519.pub" >/dev/null 2>&1
STAGE=${STAGE:-$ROOT/out/mirror/thin/stage-v1} STAGE=${STAGE:-$ROOT/out/mirror/thin/stage-v1}
CREATED=${CREATED:-$(date -u +%Y-%m-%dT%H:%M:%SZ)} CREATED=${CREATED:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}
SIGN_KEY="$KEYFILE" STAGE="$STAGE" CREATED="$CREATED" "$ROOT/src/Mirror/StellaOps.Mirror.Creator/make-thin-v1.sh" TENANT_SCOPE=${TENANT_SCOPE:-tenant-demo}
ENV_SCOPE=${ENV_SCOPE:-lab}
CHUNK_SIZE=${CHUNK_SIZE:-5242880}
CHECKPOINT_FRESHNESS=${CHECKPOINT_FRESHNESS:-86400}
OCI=${OCI:-1}
SIGN_KEY="$KEYFILE" STAGE="$STAGE" CREATED="$CREATED" TENANT_SCOPE="$TENANT_SCOPE" ENV_SCOPE="$ENV_SCOPE" CHUNK_SIZE="$CHUNK_SIZE" CHECKPOINT_FRESHNESS="$CHECKPOINT_FRESHNESS" OCI="$OCI" "$ROOT/src/Mirror/StellaOps.Mirror.Creator/make-thin-v1.sh"
# Emit milestone summary with hashes for downstream consumers # Emit milestone summary with hashes for downstream consumers
MANIFEST_PATH="$ROOT/out/mirror/thin/mirror-thin-v1.manifest.json" MANIFEST_PATH="$ROOT/out/mirror/thin/mirror-thin-v1.manifest.json"
TAR_PATH="$ROOT/out/mirror/thin/mirror-thin-v1.tar.gz" TAR_PATH="$ROOT/out/mirror/thin/mirror-thin-v1.tar.gz"
DSSE_PATH="$ROOT/out/mirror/thin/mirror-thin-v1.manifest.dsse.json" DSSE_PATH="$ROOT/out/mirror/thin/mirror-thin-v1.manifest.dsse.json"
BUNDLE_PATH="$ROOT/out/mirror/thin/mirror-thin-v1.bundle.json"
BUNDLE_DSSE_PATH="$ROOT/out/mirror/thin/mirror-thin-v1.bundle.dsse.json"
TRANSPORT_PATH="$ROOT/out/mirror/thin/stage-v1/layers/transport-plan.json"
REKOR_POLICY_PATH="$ROOT/out/mirror/thin/stage-v1/layers/rekor-policy.json"
MIRROR_POLICY_PATH="$ROOT/out/mirror/thin/stage-v1/layers/mirror-policy.json"
OFFLINE_POLICY_PATH="$ROOT/out/mirror/thin/stage-v1/layers/offline-kit-policy.json"
SUMMARY_PATH="$ROOT/out/mirror/thin/milestone.json" SUMMARY_PATH="$ROOT/out/mirror/thin/milestone.json"
sha256() { sha256() {
@@ -41,7 +52,15 @@ cat > "$SUMMARY_PATH" <<JSON
"manifest": {"path": "$(basename "$MANIFEST_PATH")", "sha256": "$(sha256 "$MANIFEST_PATH")"}, "manifest": {"path": "$(basename "$MANIFEST_PATH")", "sha256": "$(sha256 "$MANIFEST_PATH")"},
"tarball": {"path": "$(basename "$TAR_PATH")", "sha256": "$(sha256 "$TAR_PATH")"}, "tarball": {"path": "$(basename "$TAR_PATH")", "sha256": "$(sha256 "$TAR_PATH")"},
"dsse": $( [[ -f "$DSSE_PATH" ]] && echo "{\"path\": \"$(basename "$DSSE_PATH")\", \"sha256\": \"$(sha256 "$DSSE_PATH")\"}" || echo "null" ), "dsse": $( [[ -f "$DSSE_PATH" ]] && echo "{\"path\": \"$(basename "$DSSE_PATH")\", \"sha256\": \"$(sha256 "$DSSE_PATH")\"}" || echo "null" ),
"bundle": $( [[ -f "$BUNDLE_PATH" ]] && echo "{\"path\": \"$(basename "$BUNDLE_PATH")\", \"sha256\": \"$(sha256 "$BUNDLE_PATH")\"}" || echo "null" ),
"bundle_dsse": $( [[ -f "$BUNDLE_DSSE_PATH" ]] && echo "{\"path\": \"$(basename "$BUNDLE_DSSE_PATH")\", \"sha256\": \"$(sha256 "$BUNDLE_DSSE_PATH")\"}" || echo "null" ),
"time_anchor": $( [[ -n "${TIME_ANCHOR_FILE:-}" && -f "$TIME_ANCHOR_FILE" ]] && echo "{\"path\": \"$(basename "$TIME_ANCHOR_FILE")\", \"sha256\": \"$(sha256 "$TIME_ANCHOR_FILE")\"}" || echo "null" ) "time_anchor": $( [[ -n "${TIME_ANCHOR_FILE:-}" && -f "$TIME_ANCHOR_FILE" ]] && echo "{\"path\": \"$(basename "$TIME_ANCHOR_FILE")\", \"sha256\": \"$(sha256 "$TIME_ANCHOR_FILE")\"}" || echo "null" )
,"policies": {
"transport": {"path": "$(basename "$TRANSPORT_PATH")", "sha256": "$(sha256 "$TRANSPORT_PATH")"},
"rekor": {"path": "$(basename "$REKOR_POLICY_PATH")", "sha256": "$(sha256 "$REKOR_POLICY_PATH")"},
"mirror": {"path": "$(basename "$MIRROR_POLICY_PATH")", "sha256": "$(sha256 "$MIRROR_POLICY_PATH")"},
"offline": {"path": "$(basename "$OFFLINE_POLICY_PATH")", "sha256": "$(sha256 "$OFFLINE_POLICY_PATH")"}
}
} }
JSON JSON

View File

@@ -11,6 +11,7 @@ Usage:
Writes: Writes:
- mirror-thin-v1.manifest.dsse.json - mirror-thin-v1.manifest.dsse.json
- mirror-thin-v1.bundle.dsse.json (optional, when --bundle is provided)
- updates signatures in root.json, targets.json, snapshot.json, timestamp.json - updates signatures in root.json, targets.json, snapshot.json, timestamp.json
""" """
import argparse, base64, json, pathlib, hashlib import argparse, base64, json, pathlib, hashlib
@@ -46,6 +47,7 @@ def main():
ap.add_argument("--manifest", required=True, type=pathlib.Path) ap.add_argument("--manifest", required=True, type=pathlib.Path)
ap.add_argument("--tar", required=True, type=pathlib.Path) ap.add_argument("--tar", required=True, type=pathlib.Path)
ap.add_argument("--tuf-dir", required=True, type=pathlib.Path) ap.add_argument("--tuf-dir", required=True, type=pathlib.Path)
ap.add_argument("--bundle", required=False, type=pathlib.Path)
args = ap.parse_args() args = ap.parse_args()
key = load_key(args.key) key = load_key(args.key)
@@ -62,11 +64,23 @@ def main():
dsse_path = args.manifest.with_suffix(".dsse.json") dsse_path = args.manifest.with_suffix(".dsse.json")
write_json(dsse_path, dsse) write_json(dsse_path, dsse)
if args.bundle:
bundle_bytes = args.bundle.read_bytes()
bundle_sig = sign_bytes(key, bundle_bytes)
bundle_dsse = {
"payloadType": "application/vnd.stellaops.mirror.bundle+json",
"payload": b64url(bundle_bytes),
"signatures": [{"keyid": keyid, "sig": b64url(bundle_sig)}],
}
bundle_dsse_path = args.bundle.with_suffix(".dsse.json")
write_json(bundle_dsse_path, bundle_dsse)
# update TUF metadata # update TUF metadata
for name in ["root.json", "targets.json", "snapshot.json", "timestamp.json"]: for name in ["root.json", "targets.json", "snapshot.json", "timestamp.json"]:
sign_tuf(args.tuf_dir / name, keyid, key) sign_tuf(args.tuf_dir / name, keyid, key)
print(f"Signed DSSE + TUF using keyid {keyid}; DSSE -> {dsse_path}") extra = f", bundle DSSE -> {bundle_dsse_path}" if args.bundle else ""
print(f"Signed DSSE + TUF using keyid {keyid}; DSSE -> {dsse_path}{extra}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,20 +1,59 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Simple verifier for mirror-thin-v1 artefacts. Verifier for mirror-thin-v1 artefacts and bundle meta.
Checks: Checks:
1) SHA256 of manifest and tarball matches provided .sha256 files. 1) SHA256 of manifest/tarball (and optional bundle meta) matches sidecars.
2) Manifest schema has required fields. 2) Manifest schema contains required fields and required layer files exist.
3) Tarball contains manifest.json, layers/, indexes/ with deterministic tar headers (mtime=0, uid/gid=0, sorted paths). 3) Tarball headers deterministic (sorted paths, uid/gid=0, mtime=0).
4) Tar content digests match manifest entries. 4) Tar contents match manifest digests.
5) Optional: verify DSSE signatures for manifest/bundle when a public key is provided.
6) Optional: validate bundle meta (tenant/env scope, policy hashes, gap coverage counts).
Usage: Usage:
python scripts/mirror/verify_thin_bundle.py out/mirror/thin/mirror-thin-v1.manifest.json out/mirror/thin/mirror-thin-v1.tar.gz python scripts/mirror/verify_thin_bundle.py \
out/mirror/thin/mirror-thin-v1.manifest.json \
out/mirror/thin/mirror-thin-v1.tar.gz \
--bundle-meta out/mirror/thin/mirror-thin-v1.bundle.json \
--pubkey out/mirror/thin/tuf/keys/ci-ed25519.pub \
--tenant tenant-demo --environment lab
Exit code 0 on success; non-zero on any check failure. Exit code 0 on success; non-zero on any check failure.
""" """
import json, tarfile, hashlib, sys, pathlib import argparse
import base64
import hashlib
import json
import pathlib
import sys
import tarfile
from typing import Optional
try:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
CRYPTO_AVAILABLE = True
except ImportError: # pragma: no cover - surfaced as runtime guidance
CRYPTO_AVAILABLE = False
REQUIRED_FIELDS = ["version", "created", "layers", "indexes"] REQUIRED_FIELDS = ["version", "created", "layers", "indexes"]
REQUIRED_LAYER_FILES = {
"layers/observations.ndjson",
"layers/time-anchor.json",
"layers/transport-plan.json",
"layers/rekor-policy.json",
"layers/mirror-policy.json",
"layers/offline-kit-policy.json",
"layers/artifact-hashes.json",
"indexes/observations.index",
}
def _b64url_decode(data: str) -> bytes:
padding = "=" * (-len(data) % 4)
return base64.urlsafe_b64decode(data + padding)
def sha256_file(path: pathlib.Path) -> str: def sha256_file(path: pathlib.Path) -> str:
h = hashlib.sha256() h = hashlib.sha256()
@@ -23,20 +62,24 @@ def sha256_file(path: pathlib.Path) -> str:
h.update(chunk) h.update(chunk)
return h.hexdigest() return h.hexdigest()
def load_sha256_sidecar(path: pathlib.Path) -> str: def load_sha256_sidecar(path: pathlib.Path) -> str:
sidecar = path.with_suffix(path.suffix + ".sha256") sidecar = path.with_suffix(path.suffix + ".sha256")
if not sidecar.exists(): if not sidecar.exists():
raise SystemExit(f"missing sidecar {sidecar}") raise SystemExit(f"missing sidecar {sidecar}")
return sidecar.read_text().strip().split()[0] return sidecar.read_text().strip().split()[0]
def check_schema(manifest: dict): def check_schema(manifest: dict):
missing = [f for f in REQUIRED_FIELDS if f not in manifest] missing = [f for f in REQUIRED_FIELDS if f not in manifest]
if missing: if missing:
raise SystemExit(f"manifest missing fields: {missing}") raise SystemExit(f"manifest missing fields: {missing}")
def normalize(name: str) -> str: def normalize(name: str) -> str:
return name[2:] if name.startswith("./") else name return name[2:] if name.startswith("./") else name
def check_tar_determinism(tar_path: pathlib.Path): def check_tar_determinism(tar_path: pathlib.Path):
with tarfile.open(tar_path, "r:gz") as tf: with tarfile.open(tar_path, "r:gz") as tf:
names = [normalize(n) for n in tf.getnames()] names = [normalize(n) for n in tf.getnames()]
@@ -48,13 +91,21 @@ def check_tar_determinism(tar_path: pathlib.Path):
if m.mtime != 0: if m.mtime != 0:
raise SystemExit(f"tar header mtime not zero for {m.name}") raise SystemExit(f"tar header mtime not zero for {m.name}")
def check_required_layers(tar_path: pathlib.Path):
with tarfile.open(tar_path, "r:gz") as tf:
names = {normalize(n) for n in tf.getnames()}
for required in REQUIRED_LAYER_FILES:
if required not in names:
raise SystemExit(f"required file missing from bundle: {required}")
def check_content_hashes(manifest: dict, tar_path: pathlib.Path): def check_content_hashes(manifest: dict, tar_path: pathlib.Path):
with tarfile.open(tar_path, "r:gz") as tf: with tarfile.open(tar_path, "r:gz") as tf:
def get(name: str): def get(name: str):
try: try:
return tf.getmember(name) return tf.getmember(name)
except KeyError: except KeyError:
# retry with leading ./
return tf.getmember(f"./{name}") return tf.getmember(f"./{name}")
for layer in manifest.get("layers", []): for layer in manifest.get("layers", []):
name = layer["path"] name = layer["path"]
@@ -74,12 +125,96 @@ def check_content_hashes(manifest: dict, tar_path: pathlib.Path):
raise SystemExit(f"index digest mismatch {name}: {digest}") raise SystemExit(f"index digest mismatch {name}: {digest}")
def load_pubkey(path: pathlib.Path) -> Ed25519PublicKey:
if not CRYPTO_AVAILABLE:
raise SystemExit("cryptography is required for DSSE verification; install before using --pubkey")
return serialization.load_pem_public_key(path.read_bytes())
def verify_dsse(dsse_path: pathlib.Path, pubkey_path: pathlib.Path, expected_payload: pathlib.Path, expected_type: str):
dsse_obj = json.loads(dsse_path.read_text())
if dsse_obj.get("payloadType") != expected_type:
raise SystemExit(f"DSSE payloadType mismatch for {dsse_path}")
payload = _b64url_decode(dsse_obj.get("payload", ""))
if payload != expected_payload.read_bytes():
raise SystemExit(f"DSSE payload mismatch for {dsse_path}")
sigs = dsse_obj.get("signatures") or []
if not sigs:
raise SystemExit(f"DSSE missing signatures: {dsse_path}")
pub = load_pubkey(pubkey_path)
try:
pub.verify(_b64url_decode(sigs[0]["sig"]), payload)
except Exception as exc: # pragma: no cover - cryptography raises InvalidSignature
raise SystemExit(f"DSSE signature verification failed for {dsse_path}: {exc}")
def check_bundle_meta(meta_path: pathlib.Path, manifest_path: pathlib.Path, tar_path: pathlib.Path, tenant: Optional[str], environment: Optional[str]):
meta = json.loads(meta_path.read_text())
for field in ["bundle", "version", "artifacts", "gaps", "tooling"]:
if field not in meta:
raise SystemExit(f"bundle meta missing field {field}")
if tenant and meta.get("tenant") != tenant:
raise SystemExit(f"bundle tenant mismatch: {meta.get('tenant')} != {tenant}")
if environment and meta.get("environment") != environment:
raise SystemExit(f"bundle environment mismatch: {meta.get('environment')} != {environment}")
artifacts = meta["artifacts"]
def expect(name: str, path: pathlib.Path):
recorded = artifacts.get(name)
if not recorded:
raise SystemExit(f"bundle meta missing artifact entry: {name}")
expected = recorded.get("sha256")
if expected and expected != sha256_file(path):
raise SystemExit(f"bundle meta digest mismatch for {name}")
expect("manifest", manifest_path)
expect("tarball", tar_path)
for extra in ["time_anchor", "transport_plan", "rekor_policy", "mirror_policy", "offline_policy", "artifact_hashes"]:
rec = artifacts.get(extra)
if not rec:
raise SystemExit(f"bundle meta missing artifact entry: {extra}")
if not rec.get("path"):
raise SystemExit(f"bundle meta missing path for {extra}")
for group, expected_count in [("ok", 10), ("rk", 10), ("ms", 10)]:
if len(meta.get("gaps", {}).get(group, [])) != expected_count:
raise SystemExit(f"bundle meta gaps.{group} expected {expected_count} entries")
root_guess = manifest_path.parents[3] if len(manifest_path.parents) > 3 else manifest_path.parents[-1]
tool_expectations = {
'make_thin_v1_sh': root_guess / 'src' / 'Mirror' / 'StellaOps.Mirror.Creator' / 'make-thin-v1.sh',
'sign_script': root_guess / 'scripts' / 'mirror' / 'sign_thin_bundle.py',
'verify_script': root_guess / 'scripts' / 'mirror' / 'verify_thin_bundle.py',
'verify_oci': root_guess / 'scripts' / 'mirror' / 'verify_oci_layout.py'
}
for key, path in tool_expectations.items():
recorded = meta['tooling'].get(key)
if not recorded:
raise SystemExit(f"tool hash missing for {key}")
actual = sha256_file(path)
if recorded != actual:
raise SystemExit(f"tool hash mismatch for {key}")
if meta.get("checkpoint_freshness_seconds", 0) <= 0:
raise SystemExit("checkpoint_freshness_seconds must be positive")
def main(): def main():
if len(sys.argv) != 3: parser = argparse.ArgumentParser()
print(__doc__) parser.add_argument("manifest", type=pathlib.Path)
sys.exit(2) parser.add_argument("tar", type=pathlib.Path)
manifest_path = pathlib.Path(sys.argv[1]) parser.add_argument("--bundle-meta", type=pathlib.Path)
tar_path = pathlib.Path(sys.argv[2]) parser.add_argument("--pubkey", type=pathlib.Path)
parser.add_argument("--tenant", type=str)
parser.add_argument("--environment", type=str)
args = parser.parse_args()
manifest_path = args.manifest
tar_path = args.tar
bundle_meta = args.bundle_meta
bundle_dsse = bundle_meta.with_suffix(".dsse.json") if bundle_meta else None
manifest_dsse = manifest_path.with_suffix(".dsse.json")
man_expected = load_sha256_sidecar(manifest_path) man_expected = load_sha256_sidecar(manifest_path)
tar_expected = load_sha256_sidecar(tar_path) tar_expected = load_sha256_sidecar(tar_path)
@@ -91,8 +226,26 @@ def main():
manifest = json.loads(manifest_path.read_text()) manifest = json.loads(manifest_path.read_text())
check_schema(manifest) check_schema(manifest)
check_tar_determinism(tar_path) check_tar_determinism(tar_path)
check_required_layers(tar_path)
check_content_hashes(manifest, tar_path) check_content_hashes(manifest, tar_path)
if bundle_meta:
if not bundle_meta.exists():
raise SystemExit(f"bundle meta missing: {bundle_meta}")
meta_expected = load_sha256_sidecar(bundle_meta)
if sha256_file(bundle_meta) != meta_expected:
raise SystemExit("bundle meta sha256 mismatch")
check_bundle_meta(bundle_meta, manifest_path, tar_path, args.tenant, args.environment)
if args.pubkey:
pubkey = args.pubkey
if manifest_dsse.exists():
verify_dsse(manifest_dsse, pubkey, manifest_path, "application/vnd.stellaops.mirror.manifest+json")
if bundle_dsse and bundle_dsse.exists():
verify_dsse(bundle_dsse, pubkey, bundle_meta, "application/vnd.stellaops.mirror.bundle+json")
print("OK: mirror-thin bundle verified") print("OK: mirror-thin bundle verified")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -5,18 +5,21 @@ Purpose: measure basic graph load/adjacency build and shallow path exploration o
## Fixtures ## Fixtures
- Use interim synthetic fixtures under `samples/graph/interim/graph-50k` or `graph-100k`. - Use interim synthetic fixtures under `samples/graph/interim/graph-50k` or `graph-100k`.
- Each fixture includes `nodes.ndjson`, `edges.ndjson`, and `manifest.json` with hashes/counts. - Each fixture includes `nodes.ndjson`, `edges.ndjson`, and `manifest.json` with hashes/counts.
- Optional overlay: drop `overlay.ndjson` next to the fixture (or set `overlay.path` in `manifest.json`) to apply extra edges/layers; hashes are captured in results.
## Usage ## Usage
```bash ```bash
python graph_bench.py \ python graph_bench.py \
--fixture ../../../samples/graph/interim/graph-50k \ --fixture ../../../samples/graph/interim/graph-50k \
--output results/graph-50k.json \ --output results/graph-50k.json \
--samples 100 --samples 100 \
--overlay ../../../samples/graph/interim/graph-50k/overlay.ndjson # optional
``` ```
Outputs a JSON summary with: Outputs a JSON summary with:
- `nodes`, `edges` - `nodes`, `edges`
- `build_ms` — time to build adjacency (ms) - `build_ms` — time to build adjacency (ms)
- `overlay_ms` — time to apply overlay (0 when absent), plus counts and SHA under `overlay.*`
- `bfs_ms` — total time for 3-depth BFS over sampled nodes - `bfs_ms` — total time for 3-depth BFS over sampled nodes
- `avg_reach_3`, `max_reach_3` — nodes reached within depth 3 - `avg_reach_3`, `max_reach_3` — nodes reached within depth 3
- `manifest` — copied from fixture for traceability - `manifest` — copied from fixture for traceability

View File

@@ -9,10 +9,11 @@ no network, and fixed seeds for reproducibility.
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import hashlib
import json import json
import time import time
from pathlib import Path from pathlib import Path
from typing import Dict, List, Tuple from typing import Dict, List, Optional, Tuple
def load_ndjson(path: Path): def load_ndjson(path: Path):
@@ -42,6 +43,52 @@ def build_graph(nodes_path: Path, edges_path: Path) -> Tuple[Dict[str, List[str]
return adjacency, edge_count return adjacency, edge_count
def _sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
def apply_overlay(adjacency: Dict[str, List[str]], overlay_path: Path) -> Tuple[int, int]:
"""
Apply overlay edges to the adjacency map.
Overlay file format (NDJSON): {"source": "nodeA", "target": "nodeB"}
Unknown keys are ignored. New nodes are added with empty adjacency to keep
BFS deterministic. Duplicate edges are de-duplicated.
"""
if not overlay_path.exists():
return 0, 0
added_edges = 0
introduced_nodes = set()
for record in load_ndjson(overlay_path):
source = record.get("source") or record.get("from")
target = record.get("target") or record.get("to")
if not source or not target:
continue
if source not in adjacency:
adjacency[source] = []
introduced_nodes.add(source)
if target not in adjacency:
adjacency[target] = []
introduced_nodes.add(target)
if target not in adjacency[source]:
adjacency[source].append(target)
added_edges += 1
# keep neighbor ordering deterministic
for v in adjacency.values():
v.sort()
return added_edges, len(introduced_nodes)
def bfs_limited(adjacency: Dict[str, List[str]], start: str, max_depth: int = 3) -> int: def bfs_limited(adjacency: Dict[str, List[str]], start: str, max_depth: int = 3) -> int:
visited = {start} visited = {start}
frontier = [start] frontier = [start]
@@ -58,15 +105,41 @@ def bfs_limited(adjacency: Dict[str, List[str]], start: str, max_depth: int = 3)
return len(visited) return len(visited)
def run_bench(fixture_dir: Path, sample_size: int = 100) -> dict: def resolve_overlay_path(fixture_dir: Path, manifest: dict, explicit: Optional[Path]) -> Optional[Path]:
if explicit:
return explicit.resolve()
overlay_manifest = manifest.get("overlay") if isinstance(manifest, dict) else None
if isinstance(overlay_manifest, dict):
path_value = overlay_manifest.get("path")
if path_value:
candidate = Path(path_value)
return candidate if candidate.is_absolute() else (fixture_dir / candidate)
default = fixture_dir / "overlay.ndjson"
return default if default.exists() else None
def run_bench(fixture_dir: Path, sample_size: int = 100, overlay_path: Optional[Path] = None) -> dict:
nodes_path = fixture_dir / "nodes.ndjson" nodes_path = fixture_dir / "nodes.ndjson"
edges_path = fixture_dir / "edges.ndjson" edges_path = fixture_dir / "edges.ndjson"
manifest_path = fixture_dir / "manifest.json" manifest_path = fixture_dir / "manifest.json"
manifest = json.loads(manifest_path.read_text()) if manifest_path.exists() else {} manifest = json.loads(manifest_path.read_text()) if manifest_path.exists() else {}
overlay_resolved = resolve_overlay_path(fixture_dir, manifest, overlay_path)
t0 = time.perf_counter() t0 = time.perf_counter()
adjacency, edge_count = build_graph(nodes_path, edges_path) adjacency, edge_count = build_graph(nodes_path, edges_path)
overlay_added = 0
overlay_nodes = 0
overlay_hash = None
overlay_ms = 0.0
if overlay_resolved:
t_overlay = time.perf_counter()
overlay_added, overlay_nodes = apply_overlay(adjacency, overlay_resolved)
overlay_ms = (time.perf_counter() - t_overlay) * 1000
overlay_hash = _sha256(overlay_resolved)
build_ms = (time.perf_counter() - t0) * 1000 build_ms = (time.perf_counter() - t0) * 1000
# deterministic sample: first N node ids sorted # deterministic sample: first N node ids sorted
@@ -83,13 +156,21 @@ def run_bench(fixture_dir: Path, sample_size: int = 100) -> dict:
return { return {
"fixture": fixture_dir.name, "fixture": fixture_dir.name,
"nodes": len(adjacency), "nodes": len(adjacency),
"edges": edge_count, "edges": edge_count + overlay_added,
"build_ms": round(build_ms, 2), "build_ms": round(build_ms, 2),
"overlay_ms": round(overlay_ms, 2),
"bfs_ms": round(bfs_ms, 2), "bfs_ms": round(bfs_ms, 2),
"bfs_samples": len(node_ids), "bfs_samples": len(node_ids),
"avg_reach_3": round(avg_reach, 2), "avg_reach_3": round(avg_reach, 2),
"max_reach_3": max_reach, "max_reach_3": max_reach,
"manifest": manifest, "manifest": manifest,
"overlay": {
"applied": overlay_resolved is not None,
"added_edges": overlay_added,
"introduced_nodes": overlay_nodes,
"path": str(overlay_resolved) if overlay_resolved else None,
"sha256": overlay_hash,
},
} }
@@ -98,13 +179,15 @@ def main() -> int:
parser.add_argument("--fixture", required=True, help="Path to fixture directory (nodes.ndjson, edges.ndjson)") parser.add_argument("--fixture", required=True, help="Path to fixture directory (nodes.ndjson, edges.ndjson)")
parser.add_argument("--output", required=True, help="Path to write results JSON") parser.add_argument("--output", required=True, help="Path to write results JSON")
parser.add_argument("--samples", type=int, default=100, help="Number of starting nodes to sample deterministically") parser.add_argument("--samples", type=int, default=100, help="Number of starting nodes to sample deterministically")
parser.add_argument("--overlay", help="Optional overlay NDJSON path; defaults to overlay.ndjson next to fixture or manifest overlay.path")
args = parser.parse_args() args = parser.parse_args()
fixture_dir = Path(args.fixture).resolve() fixture_dir = Path(args.fixture).resolve()
out_path = Path(args.output).resolve() out_path = Path(args.output).resolve()
out_path.parent.mkdir(parents=True, exist_ok=True) out_path.parent.mkdir(parents=True, exist_ok=True)
result = run_bench(fixture_dir, sample_size=args.samples) explicit_overlay = Path(args.overlay).resolve() if args.overlay else None
result = run_bench(fixture_dir, sample_size=args.samples, overlay_path=explicit_overlay)
out_path.write_text(json.dumps(result, indent=2, sort_keys=True)) out_path.write_text(json.dumps(result, indent=2, sort_keys=True))
print(f"Wrote results to {out_path}") print(f"Wrote results to {out_path}")
return 0 return 0

View File

@@ -6,6 +6,7 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${ROOT}/../../../.." && pwd)" REPO_ROOT="$(cd "${ROOT}/../../../.." && pwd)"
FIXTURES_ROOT="${FIXTURES_ROOT:-${REPO_ROOT}/samples/graph/interim}" FIXTURES_ROOT="${FIXTURES_ROOT:-${REPO_ROOT}/samples/graph/interim}"
OUT_DIR="${OUT_DIR:-$ROOT/results}" OUT_DIR="${OUT_DIR:-$ROOT/results}"
OVERLAY_ROOT="${OVERLAY_ROOT:-${FIXTURES_ROOT}}"
SAMPLES="${SAMPLES:-100}" SAMPLES="${SAMPLES:-100}"
mkdir -p "${OUT_DIR}" mkdir -p "${OUT_DIR}"
@@ -15,7 +16,14 @@ run_one() {
local name local name
name="$(basename "${fixture}")" name="$(basename "${fixture}")"
local out_file="${OUT_DIR}/${name}.json" local out_file="${OUT_DIR}/${name}.json"
python "${ROOT}/graph_bench.py" --fixture "${fixture}" --output "${out_file}" --samples "${SAMPLES}" local overlay_candidate="${OVERLAY_ROOT}/${name}/overlay.ndjson"
args=("--fixture" "${fixture}" "--output" "${out_file}" "--samples" "${SAMPLES}")
if [[ -f "${overlay_candidate}" ]]; then
args+=("--overlay" "${overlay_candidate}")
fi
python "${ROOT}/graph_bench.py" "${args[@]}"
} }
run_one "${FIXTURES_ROOT}/graph-50k" run_one "${FIXTURES_ROOT}/graph-50k"

View File

@@ -0,0 +1,63 @@
import json
import sys
import tempfile
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
class GraphBenchTests(unittest.TestCase):
def setUp(self) -> None:
self.tmp = tempfile.TemporaryDirectory()
self.root = Path(self.tmp.name)
def tearDown(self) -> None:
self.tmp.cleanup()
def _write_ndjson(self, path: Path, records):
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as f:
for record in records:
f.write(json.dumps(record))
f.write("\n")
def test_overlay_edges_are_applied_and_counted(self):
from graph_bench import run_bench
fixture = self.root / "fixture"
fixture.mkdir()
self._write_ndjson(fixture / "nodes.ndjson", [{"id": "a"}, {"id": "b"}])
self._write_ndjson(fixture / "edges.ndjson", [{"source": "a", "target": "b"}])
self._write_ndjson(fixture / "overlay.ndjson", [{"source": "b", "target": "a"}])
result = run_bench(fixture, sample_size=2)
self.assertEqual(result["nodes"], 2)
self.assertEqual(result["edges"], 2) # overlay added one edge
self.assertTrue(result["overlay"]["applied"])
self.assertEqual(result["overlay"]["added_edges"], 1)
self.assertEqual(result["overlay"]["introduced_nodes"], 0)
def test_overlay_is_optional(self):
from graph_bench import run_bench
fixture = self.root / "fixture-no-overlay"
fixture.mkdir()
self._write_ndjson(fixture / "nodes.ndjson", [{"id": "x"}, {"id": "y"}])
self._write_ndjson(fixture / "edges.ndjson", [{"source": "x", "target": "y"}])
result = run_bench(fixture, sample_size=2)
self.assertEqual(result["edges"], 1)
self.assertFalse(result["overlay"]["applied"])
self.assertEqual(result["overlay"]["added_edges"], 0)
if __name__ == "__main__":
unittest.main()

View File

@@ -7,18 +7,57 @@
*/ */
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import crypto from "crypto";
function readJson(p) { function readJson(p) {
return JSON.parse(fs.readFileSync(p, "utf-8")); return JSON.parse(fs.readFileSync(p, "utf-8"));
} }
function buildPlan(scenarios, manifest, fixtureName) { function sha256File(filePath) {
const hash = crypto.createHash("sha256");
hash.update(fs.readFileSync(filePath));
return hash.digest("hex");
}
function resolveOverlay(fixtureDir, manifest) {
const manifestOverlay = manifest?.overlay?.path;
const candidate = manifestOverlay
? path.isAbsolute(manifestOverlay)
? manifestOverlay
: path.join(fixtureDir, manifestOverlay)
: path.join(fixtureDir, "overlay.ndjson");
if (!fs.existsSync(candidate)) {
return null;
}
return {
path: candidate,
sha256: sha256File(candidate),
};
}
function buildPlan(scenarios, manifest, fixtureName, fixtureDir) {
const now = new Date().toISOString(); const now = new Date().toISOString();
const seed = process.env.UI_BENCH_SEED || "424242";
const traceId =
process.env.UI_BENCH_TRACE_ID ||
(crypto.randomUUID ? crypto.randomUUID() : `trace-${Date.now()}`);
const overlay = resolveOverlay(fixtureDir, manifest);
return { return {
version: "1.0.0", version: "1.0.0",
fixture: fixtureName, fixture: fixtureName,
manifestHash: manifest?.hashes || {}, manifestHash: manifest?.hashes || {},
overlay,
timestamp: now, timestamp: now,
seed,
traceId,
viewport: {
width: 1280,
height: 720,
deviceScaleFactor: 1,
},
steps: scenarios.map((s, idx) => ({ steps: scenarios.map((s, idx) => ({
order: idx + 1, order: idx + 1,
id: s.id, id: s.id,
@@ -41,7 +80,12 @@ function main() {
const manifest = fs.existsSync(manifestPath) ? readJson(manifestPath) : {}; const manifest = fs.existsSync(manifestPath) ? readJson(manifestPath) : {};
const scenarios = readJson(scenariosPath).scenarios || []; const scenarios = readJson(scenariosPath).scenarios || [];
const plan = buildPlan(scenarios, manifest, path.basename(fixtureDir)); const plan = buildPlan(
scenarios,
manifest,
path.basename(fixtureDir),
fixtureDir
);
fs.mkdirSync(path.dirname(outputPath), { recursive: true }); fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, JSON.stringify(plan, null, 2)); fs.writeFileSync(outputPath, JSON.stringify(plan, null, 2));
console.log(`Wrote plan to ${outputPath}`); console.log(`Wrote plan to ${outputPath}`);

Some files were not shown because too many files have changed in this diff Show More