# DSSE‑Signed Offline Scanner Updates — Developer Guidelines > **Date:** 2025-12-01 > **Status:** Advisory draft (from user-provided guidance) > **Scope:** Offline vulnerability DB bundles (Scanner), DSSE+Rekor v2 verification, offline kit alignment, activation rules, ops playbook. Here’s a tight, practical pattern to make your scanner’s vuln‑DB updates rock‑solid even when feeds hiccup: ## Offline, verifiable update bundles (DSSE + Rekor v2) **Idea:** distribute DB updates as offline tarballs. Each tarball ships with: * a **DSSE‑signed** statement (e.g., in‑toto style) over the bundle hash * a **Rekor v2 receipt** proving the signature/statement was logged * a small **manifest.json** (version, created_at, content hashes) **Startup flow (happy path):** 1. Load latest tarball from your local `updates/` cache. 2. Verify DSSE signature against your trusted public keys. 3. Verify Rekor v2 receipt (inclusion proof) matches the DSSE payload hash. 4. If both pass, unpack/activate; record the bundle’s **trust_id** (e.g., statement digest). 5. If anything fails, **keep using the last good bundle**. No service disruption. **Why this helps** * **Air‑gap friendly:** no live network needed at activation time. * **Tamper‑evident:** DSSE + Rekor receipt proves provenance and transparency. * **Operational stability:** feed outages become non‑events—scanner just keeps the last good state. --- ## File layout inside each bundle ``` /bundle-2025-11-29/ manifest.json # { version, created_at, entries[], sha256s } payload.tar.zst # the actual DB/indices payload.tar.zst.sha256 statement.dsse.json # DSSE-wrapped statement over payload hash rekor-receipt.json # Rekor v2 inclusion/verification material ``` --- ## Acceptance/Activation rules * **Trust root:** pin one (or more) publisher public keys; rotate via separate, out‑of‑band process. * **Monotonicity:** only activate if `manifest.version > current.version` (or if trust policy explicitly allows replay for rollback testing). * **Atomic switch:** unpack to `db/staging/`, validate, then symlink‑flip to `db/active/`. * **Quarantine on failure:** move bad bundles to `updates/quarantine/` with a reason code. --- ## Minimal .NET 10 verifier sketch (C#) ```csharp public sealed record BundlePaths(string Dir) { public string Manifest => Path.Combine(Dir, "manifest.json"); public string Payload => Path.Combine(Dir, "payload.tar.zst"); public string Dsse => Path.Combine(Dir, "statement.dsse.json"); public string Receipt => Path.Combine(Dir, "rekor-receipt.json"); } public async Task ActivateBundleAsync(BundlePaths b, TrustConfig trust, string activeDir) { var manifest = await Manifest.LoadAsync(b.Manifest); if (!await Hashes.VerifyAsync(b.Payload, manifest.PayloadSha256)) return false; // 1) DSSE verify (publisher keys pinned in trust) var (okSig, dssePayloadDigest) = await Dsse.VerifyAsync(b.Dsse, trust.PublisherKeys); if (!okSig || dssePayloadDigest != manifest.PayloadSha256) return false; // 2) Rekor v2 receipt verify (inclusion + statement digest == dssePayloadDigest) if (!await RekorV2.VerifyReceiptAsync(b.Receipt, dssePayloadDigest, trust.RekorPub)) return false; // 3) Stage, validate, then atomically flip var staging = Path.Combine(activeDir, "..", "staging"); DirUtil.Empty(staging); await TarZstd.ExtractAsync(b.Payload, staging); if (!await LocalDbSelfCheck.RunAsync(staging)) return false; SymlinkUtil.AtomicSwap(source: staging, target: activeDir); State.WriteLastGood(manifest.Version, dssePayloadDigest); return true; } ``` --- ## Operational playbook * **On boot & daily at HH:MM:** try `ActivateBundleAsync()` on the newest bundle; on failure, log and continue. * **Telemetry (no PII):** reason codes (SIG_FAIL, RECEIPT_FAIL, HASH_MISMATCH, SELFTEST_FAIL), versions, last_good. * **Keys & rotation:** keep `publisher.pub` and `rekor.pub` in a root‑owned, read‑only path; rotate via a separate signed “trust bundle”. * **Defense‑in‑depth:** verify both the **payload hash** and each file’s hash listed in `manifest.entries[]`. * **Rollback:** allow `--force-activate ` for emergency testing, but mark as **non‑monotonic** in state. --- ## What to hand your release team * A Make/CI target that: 1. Builds `payload.tar.zst` and computes hashes 2. Generates `manifest.json` 3. Creates and signs the **DSSE statement** 4. Submits to Rekor (or your mirror) and saves the **v2 receipt** 5. Packages the bundle folder and publishes to your offline repo * A checksum file (`*.sha256sum`) for ops to verify out‑of‑band. --- If you want, I can turn this into a Stella Ops spec page (`docs/modules/scanner/offline-bundles.md`) plus a small reference implementation (C# library + CLI) that drops right into your Scanner service. --- # Drop‑in Stella Ops Dev Guide (seed for `docs/modules/scanner/development/dsse-offline-updates.md`) > **Audience** > Scanner, Export Center, Attestor, CLI, and DevOps engineers implementing DSSE‑signed offline vulnerability updates and integrating them into the Offline Update Kit (OUK). > > **Context** > > * OUK already ships **signed, atomic offline update bundles** with merged vulnerability feeds, container images, and an attested manifest. > * DSSE + Rekor is already used for **scan evidence** (SBOM attestations, Rekor proofs). > * Sprints 160/162 add **attestation bundles** with manifest, checksums, DSSE signature, and optional transparency log segments, and integrate them into OUK and CLI flows. These guidelines tell you how to **wire all of that together** for “offline scanner updates” (feeds, rules, packs) in a way that matches Stella Ops’ determinism + sovereignty promises. ## 0. Mental model ```text Advisory mirrors / Feeds builders │ ▼ ExportCenter.AttestationBundles (creates DSSE + Rekor evidence for each offline update snapshot) │ ▼ Offline Update Kit (OUK) builder (adds feeds + evidence to kit tarball) │ ▼ stella offline kit import / admin CLI (verifies Cosign + DSSE + Rekor segments, then atomically swaps scanner feeds) ``` Online, Rekor is live; offline, you rely on **bundled Rekor segments / snapshots** and the existing OUK mechanics (import is atomic, old feeds kept until new bundle is fully verified). ## 1. Goals & non‑goals ### Goals 1. **Authentic offline snapshots**: every offline scanner update (OUK or delta) must be verifiably tied to a DSSE envelope, a certificate chain, and a Rekor v2 inclusion proof or bundled log segment. 2. **Deterministic replay**: given a kit + its DSSE attestation bundle, every verifier reaches the same verdict online or air‑gapped. 3. **Separation of concerns**: Export Center builds attestations; Scanner verifies/imports; Signer/Attestor handle DSSE/Rekor. 4. **Operational safety**: imports remain atomic/idempotent; old feeds stay live until full verification. ### Non‑goals * Designing new crypto or log formats. * Per‑feed DSSE envelopes (minimum contract is bundle‑level attestation). ## 2. Bundle contract for DSSE‑signed offline updates **Files to ship** (inside each offline kit or delta): ``` /attestations/ offline-update.dsse.json # DSSE envelope offline-update.rekor.json # Rekor entry + inclusion proof (or segment descriptor) /manifest/ offline-manifest.json # existing manifest offline-manifest.json.jws # existing detached JWS /feeds/ ... # existing feed payloads ``` **DSSE payload (minimum):** subject = kit name + tarball sha256; predicate fields include `offline_manifest_sha256`, feed entries, builder ID/commit, created_at UTC, and channel. **Rekor material:** submit DSSE to Rekor v2, store UUID/logIndex/inclusion proof as `offline-update.rekor.json`; for offline, embed a minimal log segment or rely on mirrored snapshots. ## 3. Implementation by module ### Export Center — attestation bundles * Compose attestation job: build DSSE payload, sign via Signer, submit to Rekor via Attestor, persist `offline-update.dsse.json` and `.rekor.json` (+ segments). * Integrate into offline kit packaging: place attestation files under `/attestations/`; list them in `offline-manifest.json` with sha256/size/capturedAt. * Define JSON schema for `offline-update.rekor.json`; version it and validate. ### Offline Update Kit builder * Preserve idempotent, atomic imports; include DSSE/Rekor files in kit staging tree. * Keep manifests deterministic: ordered file lists, UTC timestamps. * For delta kits, attest the resulting snapshot state (not just diffs). ### Scanner — import & activation * Verification sequence: Cosign tarball → manifest JWS → file digests (incl. attestation files) → DSSE verify → Rekor verify (online or offline segment) → atomic swap. * Config surface: `requireDsse`, `rekorOfflineMode`, `attestationVerifier` (env-var mirrored); allow observe→enforce rollout. * Failure behavior: keep old feeds; log structured failure fields; expose ProblemDetails. ### Signer & Attestor * Add predicate type/schema for offline updates; submit DSSE to Rekor; emit verification routines usable by CLI/Scanner with offline snapshot support. ### CLI & UI * CLI verbs to verify/import bundles with Rekor key; UI shows Cosign/JWS + DSSE/Rekor status and kit freshness. ## 4. Determinism & offline‑safety rules * No hidden network dependencies; offline must succeed with kit + Rekor snapshot. * Stable JSON serialization; UTC timestamps. * Replayable imports (idempotent); DSSE payload for a snapshot must be immutable. * Explainable failures with precise mismatch points. ## 5. Testing & CI expectations * Unit/integration: happy path, tampering cases (manifest/DSSE/Rekor), offline mode, rollback logic. * Metrics: `offlinekit_import_total{status}`, `attestation_verify_latency_seconds`, Rekor success/retry counts. * Golden fixtures: deterministic bundle + snapshot tests. ## 6. Developer checklist (TL;DR) 1. Read operator guides: `docs/modules/scanner/operations/dsse-rekor-operator-guide.md`, `docs/24_OFFLINE_KIT.md`, relevant sprints. 2. Implement: generate DSSE in Export Center; include attestation in kit; Scanner verifies before swap. 3. Test: bundle composition, import rollback, determinism. 4. Telemetry: counters + latency; log digests/UUIDs. 5. Document: update Export Center and Scanner architecture docs, OUK docs when flows/contracts change.