Compliance Golden-Path Demo

A single, reproducible, signed walk through the full compliance evidence journey — the SE/auditor demo that makes Stella’s “signed evidence / deterministic replay / fail-closed” claims tangible, and the regression check that keeps “compliance works” green as services change.

Claim boundary — read this first. This demo proves operator-support: Stella produces signed, replayable compliance evidence. It does not make Stella (or your estate) “NIS2/CRA/DORA compliant”. The operator/customer remains the regulated and reporting party. Every pack runs evidence-only / operator-support; the demo never claims “compliant-in-a-box”.

Audience: solutions engineers and auditors running the live walkthrough, and maintainers who keep the regression lane green. For the product framing and canonical claim-boundary wording, start at Assurance and Compliance Packs.

What it walks

StepActionEvidences (regulation)
1Enable the NIS2 evidence-only packtenant opts into NIS2 evidence support (operator-owned config)
2Author a NIS2 control + applicabilityNIS2 Art. 21 risk-management control register (Policy-owned, canonical mappingHash)
3Scan the demo imagesoftware composition input for the SBOM/findings chain
4Upload + round-trip the SBOMCRA Annex I / NIS2 Art. 21(2)(d) software bill of materials, content-addressed
5Create a release + digest-pinned componentsupply-chain integrity — every component carries its sha256 (deploy-by-digest, not tag)
6Record a release-evidence packetDORA evidence retention — content-hashed, verifiable release truth
7Create + seal a decision capsuleDORA / NIS2 signed, replayable decision evidence; attestor.verdict_ledger increments; DSSE Ed25519 signature verifies
8SoA snapshot + signed SoA exportNIS2 Art. 21 Statement of Applicability — deterministic, signed, offline-verifiable bundle
9Drive an Article 23 incident timelineNIS2 Art. 23 early-warning (24h) / notification (72h) / final-report (1 month) milestones, signed + replayable, source-of-truth = Notify

Run it (headless — one command)

# Linux / WSL / git-bash
bash devops/compose/scripts/compliance-golden-path.sh \
  --evidence-dir docs/qa/feature-checks/runs/$(date +%F)-compliance-golden-path/evidence/run-1
# Windows
pwsh devops/compose/scripts/compliance-golden-path.ps1 `
  -EvidenceDir docs/qa/feature-checks/runs/2026-05-30-compliance-golden-path/evidence/run-1

The script:

Expected tail on a healthy stack:

[golden-path] ===== SUMMARY =====
[golden-path]   passed assertions: 25
[golden-path]   warnings:          3
[golden-path]   FAILED assertions: 0
[golden-path] GOLDEN PATH OK — all assertions passed (3 warning(s)).

(The 3 warnings are the SoA live-run config gap — see Residual gaps.)

Signed artifacts + offline verification

Two signed artifacts are produced and verified:

  1. Sealed decision capsule (step 7) — a DSSE Ed25519 envelope.

    • Server-side verification: POST /api/v1/evidence/capsules/{id}/verify returns signatureValid:true + contentHashMatches:true.
    • Self-contained export bundle: POST /api/v1/evidence/capsules/{id}/export → a zip with manifest.json, manifest.dsse.json, assembly-inputs.json, metadata.json.
    • Fully-offline verify (SPRINT_20260530_024): the capsule signing key is now a STABLE Ed25519 key selected through the SignatureProviderSelection routine (EvidenceLocker__Signing__AllowEphemeralKeyMaterial=false + KeyMaterial__Ed25519PrivateKeyBase64 dev-stable seed; in production the same KeyId is pre-provisioned on a sovereign provider — HSM / Vault-wrapped / eidas.soft / fips). The matching public key is published as JWKS at GET /api/v1/evidence/capsules/signing-keys/jwks (anonymous), so an air-gapped operator can DSSE-verify the sealed capsule fully offline (recompute the canonical content hash, then verify the Ed25519 signature in the exported manifest.dsse.json against the published x public key) without contacting the stack. Capsule DSSE signatures are now reproducible across restarts for a fixed input + key.
  2. NIS2 SoA bundle (step 8b) — a deterministic DSSE bundle generated and verified offline by the stella CLI:

    dotnet src/Cli/StellaOps.Cli/bin/Debug/net10.0/StellaOps.Cli.dll \
      export nis2-soa --since 2026-05-01 \
      --snapshot snapshot.json --tenant-profile tenant-profile.json \
      --signing-key-file soa-hmac.key --signing-key-id <id> \
      --output nis2-soa-bundle.json --overwrite --json
    # -> "offlineVerified": true   (the CLI verifies the DSSE envelope offline)
    

    The bundle is byte-identical across runs for a fixed input + key — a genuine air-gap deliverable. The golden-path script runs this automatically when a built StellaOps.Cli.dll is present.

Console walk-through (operator narrative)

The Console authoring UI for the NIS2 control register shipped in SPRINT_20260530_020: a compliance officer authors/edits/removes controls and publishes the SoA snapshot entirely from /assurance/nis2/control-register. The page shows a 13-area completeness indicator and blocks “Publish SoA snapshot” (listing the missing areas) until every thematic area carries an applicability decision. The publish action drives the Policy-owned POST /api/policy/control-register/nis2/snapshot endpoint (scope policy:operate, gated by the same SoA completeness rule) and surfaces the returned contentHash / soaExportHash. This Policy control:snapshot_published path is distinct from — and complementary to — the ExportCenter signed SoA run (step 8); the headless script above exercises the same chain via the API and does not depend on the UI.

Determinism boundaries

Full boundary table + frozen-fixture values: docs/qa/feature-checks/runs/2026-05-30-compliance-golden-path/README.md.

Residual gaps (honest)

All three “Production hardening” follow-ups below are consolidated into one authoritative operator runbook: docs/ops/production-signing-runbook.md(per-surface provisioning, the dev-stable → sovereign before/after config diff, JWKS / trust-root offline-verify, and the eIDAS QES opt-in). The release checklist links it from its Production signing section.

GapImpactFollow-up
Live SoA signed run not configured (HTTP 501)CLOSED (SPRINT_20260530_024). ExportCenter export-web now carries Export:Nis2Soa:{AuthorityBaseUrl,SigningKeyId,SigningPrivateKeyPem,TrustRoots,StorageRoot}; the live POST /v1/exports/nis2/soa/runs produces a signed, offline-verifiable bundle. Set GP_REQUIRE_SOA_RUN=1 to assert it.Production hardening (runbook §3): drop the dev-stable SigningPrivateKeyPem and pre-provision SigningKeyId on a sovereign provider (HSM / Vault-wrapped / eidas.soft), supplying the real trust-root chain.
Ephemeral capsule signing keyCLOSED (SPRINT_20260530_024). The capsule key is now a stable key selected through SignatureProviderSelection, and the public key is published via JWKS (GET /api/v1/evidence/capsules/signing-keys/jwks), so the sealed capsule verifies fully offline.Production hardening (runbook §2): replace the dev-stable seed with a sovereign-provider-provisioned key (HSM / Vault-wrapped / eidas.soft / fips); the JWKS endpoint then publishes that provider’s public key unchanged.
Real HSM / QTSP (QES) backend not present in the lab.The lab uses a software dev-stable key (clearly marked); the pattern (stable key via the routine + published verification key) is production-shaped.Provision the signing KeyId on an HSM / Vault-wrapped / eu.eidas.soft provider and flip the eIDAS qualified posture — a selection/config change, not code (runbook §6; eidas-sim-crypto-demo.md §eIDAS production-signing on-ramp).

CI regression lane (TASK-021-5)

The journey is wired as a CI lane — .gitea/workflows/compliance-golden-path.yml(“Compliance Golden Path”) — so “compliance still works” is guarded as services change. The lane runs the script unattended against the live stack and gates the job on the script’s exit code: a non-zero exit names the broken leg (pack → author → scan → SBOM → release → sealed capsule → SoA export → Article 23 timeline). The per-step JSON + hashes.txt evidence is uploaded as a workflow artifact (compliance-golden-path-<run_id>) via the script’s --evidence-dir.

Key lane decisions:

To run it locally / unattended outside CI, invoke the script directly with an --evidence-dir (see Run it above); a non-zero exit is the same signal the lane gates on.