Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

.nornir/stinky/07-signing-map.md

07 — The signing map, the röra verdict, and the one chain

Lane D2, 2026-09-20. Answers the owner's question: "du får analysera gunnar igen — det skall finnas DSSE/SLSA där och/eller i tunnr/znippy. Eh, ok, en röra alltså?"

Every cell is marked MEASURED (code read at the cited file:line), READ (a comment or doc in the tree asserts it, code not read), or DESIGNED (a design document, not implemented). Nothing here is inferred from a running system — a new gunnar estate was being stood up while this was written and no gunnar was contacted.


0. Four claims from the previous lane, re-derived

The coordinator carried four claims into this lane. Re-deriving them changed three.

Claim carried in Verdict Evidence
"gunnar does NOT use DSSE at all" RIGHT, and for a stated reason MEASURED. grep -rniE 'dsse|in-toto|slsa' over every .rs in /home/rickard/git/gunnar hits only doc comments. crates/gunnar-core/src/provenance.rs:12-25 says it outright: the field set is SLSA's so an export is "a rename and not a redesign", but the envelope is sshsig because "SLSA leans on DSSE and, in practice, on Sigstore's public trust root" and gunnar's locked decision is its own issuer and its own log. This is a deliberate choice, not an oversight — see §2.
"published znippy-common 0.9.14/0.9.15 lacks dsse_verify" HALF WRONG — and this is the actionable one MEASURED. znippy-common 0.9.14 on crates.io ships src/sign.rs with 0 occurrences of dsse_verify. 0.9.15 ships it. Registry index: "vers":"0.9.15" … "pubtime":"2026-09-09T22:55:23Z","yanked":false, with the full sign feature (cms, x509-cert, ed25519-dalek, p256, …). diff of the published 0.9.15/src/sign.rs against /home/rickard/git/znippy/znippy-common/src/sign.rs → IDENTICAL.
"holger path-deps znippy-common, so every reuse breaks the standalone-clone rule" RIGHT in fact, WRONG in cause — and it is a one-line fix MEASURED. holger/server/lib/Cargo.toml:181 is znippy-common = { version = "0.9.6", path = "../../../znippy/znippy-common", optional = true }. But the workspace-level [patch.crates-io] for znippy was removed 2026-08-23 (holger/Cargo.toml:134) — only facett is still patched. The leaf path= and its comment at server/lib/Cargo.toml:173-178 ("needs znippy-common ≥ 0.9.15 on crates.io") are stale as of 2026-09-09. holger/update-bundle/Cargo.toml:42 and holger/traits/Cargo.toml:30 already take znippy-common from the registry with no path at all.
"holger re-implements DSSE verification in attest.rs (1 458 lines)" WRONG MEASURED. attest.rs is 1 458 lines, but server/lib/src/attest.rs:817-823 says "Delegates WHOLLY to znippy_common::sign::dsse_verify — the PAE, the algorithm dispatch off the signer certificate's SPKI and the chain walk are all already there", and :828 is use znippy_common::sign::{dsse_verify, CertStore, DsseEnvelope};. The 1 458 lines are the evidence store, the enrolment policy and the in-toto Statement parser, not a second DSSE. There is exactly one DSSE implementation in the house.

One more correction to the brief's own inventory: the WebAuthn count is five, not four (§2).


1. THE MAP

One row per repo. — means the repo does not do that thing.

Repo Signs what Algorithm Envelope Trust keyed on Verifies? default-on?
znippy (znippy-common) any payload; archive root digest ed25519 or ECDSA P-256, dispatched off the signer cert's SPKI (sign.rs:1370-1372) DSSE v1, byte-exact PAE (sign.rs:1190), payloadType free; and detached CMS SignedData RFC 5652 + M-of-N SignatureSet (sign.rs:1045 sign_multi, :1090 verify_threshold) X.509 cert SHA-256 fingerprint — it is literally the DSSE keyid (sign.rs:1360-1361) dsse_verify sign.rs:1375, verify_threshold :1090. OFF by default — znippy-common/Cargo.toml default = ["openzl"]; sign is opt-in
edda (nornir-build-thing) nothing — produces the statement + canonical bytes + SHA-256 digest; signing is a trait seam declares "ed25519"/"p256" as strings (provenance.rs:376,396) in-toto Statement v1 provenance.rs:426 + SLSA Provenance v1 :428; DSSE_PAYLOAD_TYPE_INTOTO = "application/vnd.in-toto+json" :430 signer_cn + keyid = X.509 fingerprint hex (:378,398,403-405) no verifier in this crate. ProvenanceSealer :412 / DsseSealer :422 are the seams; the only impl in edda is a test fake (image_build.rs:1834). Published 0.1.18, matches local (MEASURED against the crates.io index cache)
edda (nornir-auth-webauthn) — (verifier only) ES256 / P-256 only; RS256 and EdDSA refused (pure.rs:311, :115) WebAuthn assertion: authData ‖ SHA256(clientData), DER ECDSA per-credential raw P-256 pubkey (x,y) pure.rs:471 verify_es256, :359 verify_assertion. ON by default (default = ["webauthn-pure"]). NOT on crates.io (MEASURED: absent from the registry index)
edda (korp-installer) (a) app payload's blake3 root; (b) installed layout config; (c) UEFI PEs (a)(b) ed25519; (c) RSA+SHA-256 PKCS#7 Authenticode; (d) verifies ECDSA P-256 TPM quotes (a) bespoke KORPSIG1 = 8 magic ‖ 8 BE version ‖ 64 sig (rollback.rs:105-116), domain korp.root-of-trust.v1\0 (:100); (b) detached ed25519, domain korp.layout-config.v1\0 (layout.rs:783); (c) Authenticode SpcIndirectDataContent; (d) TPMS_ATTEST (a)(b) raw 32-byte ed25519 pubkey at /boot/korp/root.pub; (c) customer X.509 in UEFI PK/KEK/db; (d) TPM AK YES and FAIL-CLOSED. cmdline.rs:78-89 SignaturePolicy::#[default] RequiredByDefault; the only off-switch korp.require_signed=0 is honoured only when baked into the Authenticode-signed first stage (korp-start.rs:302-316). Verified at install (install.rs:784) and every boot (korp-start.rs:288-296). secure-boot feature is OFF by default
nornir the build-provenance record digest, sealed beside the artifact ed25519 (default) or P-256, via znippy_common::sign::ArchiveSigner CMS (ProvenanceMode::#[default] Cms, seal.rs:104-123) or DSSE/in-toto (seal.rs:87-102); archive <artifact>.build-provenance.znippy (seal.rs:238) X.509 chain to trusted DER roots → SignerId.common_name seal.rs:145 verify, :180 verify_dsse; a CMS verifier refuses a DSSE seal and vice versa (:154,:184). OFF by default — build-provenance not in Cargo.toml:66 default; --seal also opt-in. ⚠ the shipped CLI signs with ZnippySealer::dev() (src/bin/nornir.rs:6003,6031) which mints an in-memory CA and discards the root (seal.rs:58-66) — the signature chains to nothing
modgunn — (verifier only) reuses znippy-common/sign opens <artifact>.build-provenance.znippy, verifies the detached CMS seal X.509, trusted DER roots provenance.rs:700 verify_sealed_provenance. ON by default — the Cargo comment at :54-64 is the lie. Cargo.toml:18 is default = ["scan", "provenance-verify"] and the comment at :11-16 explains why it rides in default. The :54-64 feature doc still says "OFF by default"; it is stale
holger (server/lib/attest.rs) files + verifies in-toto attestations delegated to znippy (SPKI dispatch) DSSE v1 payloadType pinned to application/vnd.in-toto+json (:101, gate at :642-649); statement :95; also vulns/v0.1 :113, test-result/v0.1 :116; stored …/<predicate>-<envelope>.dsse.json (:71) X.509 cert SHA-256 fingerprint, must be enrolled; empty keyid refused (:670-675), unenrolled keyid refused (:677-681) YES. OFF by default — sign is not in server/lib/Cargo.toml:304 default; without it both entry points return VerificationUnavailable (:849-864)
holger (update-bundle) the release manifest, transitively every member ed25519 only; non-ed25519 signer cert refused (signer.rs:40) holger.update-bundle/v1 (schema.rs:4) — signatures/release-manifest.json + detached sigs + approval statements X.509 cert SHA-256 fingerprint, never the role string (approval.rs:3-5, policy.rs:225-229) YES, fail-closed. All six TUF-without-TUF properties CONFIRMED — see §1a
gunnar commits, push events, tree heads, build records, membership, webhooks, RPC credentials ed25519 over SSHSIG (ssh-key, HashAlg::Sha512) sshsig, seven namespaces — git (gunnar-policy/src/signature.rs:53), gunnar-forge (forge.rs:61), gunnar-transparency (transparency.rs:50), gunnar-provenance (provenance.rs:44), gunnar-membership (membership.rs:47), gunnar-webhook (webhook.rs:47), gunnar-key-attestation (key_attestation.rs:39); plus the RPC credential scheme gunnar-sshsig-v2 (gunnar-grpc/src/auth.rs:164) SSH key SHA-256 fingerprint YES, and one implementation for all of it — gunnar-core/src/lib.rs:133: "One signer and one verifier for every signed event log (one implementation)". BuildRecord carries SLSA's field set (provenance.rs:60+) in an sshsig envelope
gunnar (gunnar-clock) a time reply: nonce ‖ unix_ms ed25519 over SSHSIG SSHSIG PEM, namespace gunnar-time-v1@gunnar.rs (peer.rs:68), magic GUNNAR-TIME/1 (:66) SSH pubkey from the kernel command line (GUNNAR_CLOCK_KEY), never the writable key table (peer.rs:50-56) peer.rs:193 verify_reply. OFF by default (default = [])
monetize entitlement facts, snapshots, go-aheads, actor tickets ed25519 (ed25519_dalek) signature inside the object — canonical sorted-key JSON with the sig fields dropped (monetize-embed/src/signing.rs:84-111), sig in EntitlementFact.signature (monetize-product/src/lib.rs:453) raw 32-byte ed25519 pubkey (monetize-embed/src/lib.rs:123) YES. ON by default and no off switch — Entitlements cannot be built without a verifying key; monetize-embed/Cargo.toml has no [features] at all
monetize-impl — HMAC-SHA256 (Stripe webhooks) Stripe's own header Stripe webhook secret monetize-pay-impl/src/stripe/mod.rs:1052. External protocol, correct to implement
Skidbladnir the airgap bundle's root_sha256 (ASCII bytes) ed25519, verify_strict detached JSON sidecar <bundle>.sig.json = {root_sha256, signature_hex, signer_pubkey_hex, ca_cert_pem?} (archive.rs:428-438) raw 32-byte ed25519 pubkey, pinned out-of-band (TrustRoot.ca_pubkey_hex); the embedded X.509 PEM is carried and explicitly not trusted (pki.rs:352-355) fetch.rs:66, pki.rs:359. ON by default (default = ["pki"]). ⚠ caveat: authenticity is only checked if the caller passes a trust_root; None → signature_ok = None, integrity only (fetch.rs:144)
dwarves nothing. The release path publishes to crates.io unsigned — build receipt is an ELF section .nordisk.buildthing, read back by --attest — the type disclaims identity: "an attestation, not a signature — it says two files carry the same program, never who made them" (edda history.rs:267) — verifies hash equality only; --allow-unattested waives it (xtask/src/build_history.rs:88). Gate B (release_doctor/gate_b.rs) is a publish rehearsal, zero cryptography — and a build without --features rehearse now refuses --execute (release_doctor/mod.rs:412)
tunnr nothing. MEASURED: no ed25519, no CMS, no DSSE, no signature verification anywhere sha256/blake3 pins only (kernel.rs:78 TARBALL_SHA256, :261 TOOLCHAIN_IMAGE_SHA256) its build seal is edda's nornir-build-thing Attestation — a digest binding (digest_matches_record(), tunnr-build/tests/vm_boot.rs:101,105), unsigned — the PID-1 reads an UNSIGNED \korp\cmdline off the ESP, said so in four places (payloads/wg-appliance/src/main.rs:41,60,612,626; Cargo.toml:36). Secure Boot for the appliance is DESIGNED (.nornir/boot-chain-secure-boot-design.md) and implemented in korp-installer, not here
korp nothing. KORPSIG1 is not here — it is korp-installer's — — — consumes korp-installer's chain only in a dev-dependency e2e test
skade nothing signed. skade-flight's sign() is AEAD sealing, not a signature XChaCha20-Poly1305, key = SHA-256 of a domain string + the bearer secret bespoke: 1 B version ‖ 24 B nonce ‖ AEAD ct+tag symmetric shared bearer secret the AEAD tag decides before parsing; always on, no bypass found
znippy-zoomies none found (only cast_sign_loss lints) — — — —
draupnir none found (zero hits for ed25519/dsse/cms/sign_/verify_signature) — — — —
facett none found — sign_in/sign_out is OIDC login UI; facett-core self.sig is a layout hash — — — —

1a. holger.update-bundle/v1 — TUF's semantics without TUF, all six confirmed

Every property the brief listed is MEASURED true. This is the strongest link in the house.

Property file:line
dual sha256 + blake3 per member manifest.rs:25-26 fields, :55-56 built, :103-113 both checked
rejects unreferenced members manifest.rs:96 UnreferencedMember; also container.rs:299, verify.rs:437,636
rejects missing members manifest.rs:119 MissingMember
approver roles keyed by cert fingerprint, never the self-asserted role string approval.rs:3-5, :27, policy.rs:66-71, :225-229 authorizes_role, verify.rs:164-165
two-person rule verify.rs:300 enforce_two_person, distinct count :308-313, release signer must be distinct :315-322, per-role distinct-cert coverage :325-345
monotonic anti-rollback sequence core.rs:20-22, rollback.rs:76-82 SequenceRollback, :84-88 SequenceReplay
verify before decompress container.rs:219-222, verify.rs:482-484. ⚠ measured caveat: the control plane itself is decompressed before authentication (container.rs:131-144, said so plainly), bounded by MAX_MEMBERS / MAX_MEMBER_BYTES / MAX_EXPANSION_RATIO 100:1 (schema.rs:27-88)

2. Är det en röra? — the verdict

Ja, delvis — men mindre än fruktat, och röran sitter inte där man trodde.

The signing primitives are in good shape. The identity vocabularies and the WebAuthn copies are the röra. Counts, all MEASURED:

Count Detail
Signature envelopes in use 13 Standards adopted (2): DSSE v1, CMS/RFC 5652. House-invented (6): KORPSIG1, korp.layout-config.v1, holger.update-bundle/v1, gunnar-sshsig-v2, monetize's inline-signature-in-canonical-JSON, Skidbladnir's .sig.json. Standards we implement because someone else defined them (4): sshsig, Authenticode PKCS#7, TPMS_ATTEST, Stripe HMAC. Not a signature but often counted (1): skade-flight's AEAD handle.
DSSE implementations 1 znippy-common/src/sign.rs:1190/1347/1375. holger delegates (attest.rs:817-823), modgunn delegates, nornir delegates, edda declares the seam. This is the house's best-behaved primitive.
Independent ed25519 detached sign/verify call sites 5 znippy-common/sign.rs, holger/update-bundle/src/signer.rs:149-172, Skidbladnir/src/pki.rs:163/294, korp-installer/src/rootkey.rs:177 + trust.rs:59, monetize-embed/src/signing.rs:123. Each with its own domain separation and its own key format.
WebAuthn ES256 verifiers 5 (the brief said four) garmr crates/garmr-cli/src/api/passkey.rs:528; gunnar-ui crates/gunnar-ui-server/src/auth/mod.rs:1390; holger-ui crates/holger-ui-server/src/auth/mod.rs:1264; monetize crates/monetize-ui-server/src/auth.rs:431; edda nornir-auth-webauthn/src/pure.rs:471 — the shared one, used by none of the other four (zero hits for nornir-auth-webauthn in all four repos). gunnar-ui's and holger-ui's bodies are byte-identical, doc comment included. And they are on different p256 majors: garmr 0.14, gunnar-ui 0.13, holger-ui 0.13.
Identity vocabularies (trust stores a customer must hold) 5 1. X.509 cert SHA-256 fingerprint (znippy, nornir, modgunn, holger×2). 2. SSH key SHA-256 fingerprint (gunnar's seven namespaces, gunnar-clock). 3. Raw 32-byte ed25519 pubkey (Skidbladnir, monetize, korp-installer). 4. Per-credential raw P-256 pubkey (WebAuthn ×5). 5. X.509 in UEFI NVRAM PK/KEK/db (Secure Boot). The brief said three; three is the count for artifact trust, five for everything a customer actually enrols.
Whole vendored copies of a nordisk repo 5, in one repo garmr/vendor/ contains edda, facett, skade, znippy, znippy-zoomies — including a second znippy-common/src/sign.rs (garmr/vendor/znippy/znippy-common/src/sign.rs:488). That is a sixth crypto copy nobody counted.

What is ACCIDENTAL (collapse it)

  1. Five WebAuthn verifiers. No design reason. Two are byte-identical. edda already owns the shared crate. Pure accident of hand-porting. Collapse.
  2. modgunn's default-on verifier that holger refuses. modgunn default = ["scan", "provenance-verify"] compiles a real verifier in; holger pins default-features = false, features = ["scan"] (server/lib/Cargo.toml:137) and then builds its own filing/enrolment layer in attest.rs. Two gates, one job. Decide which.
  3. Three raw-ed25519-pubkey trust stores (Skidbladnir, monetize, korp-installer) that could all be one cert-fingerprint store, or at minimum one crate. Collapse the crate, keep the stores separate — see below for why keeping them separate is right.
  4. garmr's five vendored nordisk repos. Collapse to registry deps.
  5. holger's path= on znippy-common — obsolete since 2026-09-09. One line.
  6. modgunn's stale :54-64 comment claiming provenance-verify is off. Delete it.

What is DELIBERATE and correct (leave it)

  1. gunnar's sshsig, not DSSE. provenance.rs:17-25 states the reason: DSSE in practice means Sigstore's public trust root, and gunnar's locked decision is its own issuer and its own log so an on-prem install verifies with no third party. A customer can check a gunnar record with ssh-keygen -Y verify and no gunnar at all. Correct. The field set is already SLSA's, so the export is a rename — see §3 link 4.
  2. gunnar's seven sshsig namespaces. That is domain separation, not duplication — one implementation, seven domains, and a signature under one namespace cannot be replayed as another (provenance.rs:39-43). Correct and exemplary.
  3. KORPSIG1 and the layout-config domain. Both are 62–80-byte fixed binary records parsed by length-then-magic with no trial verification (rollback.rs:194-226) in a PID-1 that runs before any allocator or parser you would want. A JSON DSSE envelope at that point in the boot is the wrong tool. Correct.
  4. holger.update-bundle/v1. It is not a signature envelope competing with DSSE; it is a release policy format (two-person rule, roles, anti-rollback, member map). DSSE has none of that. Correct — and it should become the house's promotion format.
  5. CMS alongside DSSE in znippy. CMS is what an enterprise/government PKI verifier already reads; DSSE is what cosign/slsa-verifier/Rekor read. Both, from one key, is the point (sign.rs:1345). Correct.
  6. Skidbladnir pinning a raw pubkey and ignoring the bundled X.509. Across an airgap with no OCSP, no CRL and an untrustworthy clock, a pinned key is stronger than a chain. Correct — and it is why §4 proposes sharing the crate, not the store.
  7. skade's AEAD handle. It is a bearer token, not an attestation. Correct.

So: KORPSIG1 and update-bundle/v1 are not the röra. The röra is five WebAuthn verifiers, five vendored repos, two provenance gates, and a stale path=.


3. THE ONE CHAIN — ax till limpa

What a customer verifies, end to end. ⚠ = a missing or broken link today.

# Link Signed object By whom Envelope Verified by Crate that owns it
1 Source: the commit git commit developer's ssh key sshsig, namespace git gunnar-server on push gunnar-policy (src/signature.rs:53)
2 Push into the forge push event the forge sshsig, gunnar-forge gunnar-core gunnar-core::forge (forge.rs:61)
3 Transparency signed tree head (Merkle) the log sshsig, gunnar-transparency any client, offline gunnar-core::transparency (transparency.rs:50)
4 Build → artifact (gunnar's half) BuildRecord (SLSA field set: builder principal, source, recipe, materials, subjects) gunnar-server's runner sshsig, gunnar-provenance ⚠ nobody downstream. MEASURED: BuildRecord is signed at gunnar-server/src/runner.rs:378,420 and consumed only inside gunnar and its own tests gunnar-core::provenance
4b Build → artifact (the nordisk half) in-toto Statement v1 + SLSA Provenance v1 over the produced digests nornir's --seal DSSE or CMS, in <artifact>.build-provenance.znippy modgunn (provenance.rs:700, on by default) and holger attest.rs edda/nornir-build-thing (statement) + znippy-common/sign (signature) + nornir/src/build/seal.rs (the seal)
— ⚠ link 4 ↔ 4b do not join. gunnar signs SLSA's fields in sshsig; the house verifies SLSA's statement in DSSE. gunnar's own comment calls the export "a rename and not a redesign" — nobody has written the rename.
— ⚠ link 4b signs with a throw-away CA. nornir.rs:6003,6031 call ZnippySealer::dev(), which mints an in-memory P-256 CA + ed25519 leaf and drops the root. A verifier has no root to chain to. This is the single biggest hole in the chain.
— ⚠ dwarves — the path that actually publishes — signs nothing. A crate reaching crates.io via dwarves release carries no attestation of any kind. Gate B proves it builds, not who built it.
5 Artifact filed in the repository the DSSE envelope, byte-identical to what was signed — (verifier) DSSE, payloadType pinned, keyid must be enrolled holger holger-server-lib::attest ⚠ behind the non-default sign feature, and its znippy-common is still a path=
6 Policy gate — — — modgunn verify_sealed_provenance modgunn ⚠ holger pins features=["scan"] so this never compiles in holger
7 Promotion (the release decision) signatures/release-manifest.json, transitively every member two distinct approvers + a distinct release signer, all by cert fingerprint holger.update-bundle/v1 holger, fail-closed, before decompression holger-update-bundle ✅ the healthiest link
8 On-prem import across the airgap the bundle's root_sha256 the machine CA .sig.json sidecar Skidbladnir verify_on_receipt Skidbladnir::pki ⚠ a different trust store from link 7 (raw pubkey vs cert fingerprint); ⚠ trust_root: None silently degrades to integrity-only
9 Install / boot app payload blake3 root; layout config; the UEFI PEs the estate's root key; the customer's PK KORPSIG1; korp.layout-config.v1; Authenticode korp-installer at install and every boot, fail-closed by default edda/korp-installer ⚠ a third trust store (raw pubkey at /boot/korp/root.pub)
10 Runtime entitlement EntitlementFact, Snapshot, ActorTicket monetize signature inside the canonical-JSON object monetize-embed, no off switch monetize-embed::signing ⚠ a fourth trust store (raw ed25519 pubkey)
11 Trusted time under all of it nonce ‖ unix_ms the FRONT, never the internet sshsig, gunnar-time-v1@gunnar.rs gunnar-server gunnar-clock ⚠ exists, works, and nothing outside gunnar uses it — see §5

Links that do not exist today, in the order they hurt:

  1. A real signing key for link 4b. Until nornir --seal uses from_pkcs8 with persistent key material and a published root, every provenance signature in the house verifies against a CA that no longer exists. ZnippySealer::from_pkcs8 already exists (seal.rs:33-48) — the CLI just does not call it.
  2. The link-4 → link-4b rename. gunnar's BuildRecord → nornir_build_thing's to_slsa_statement(). Both field sets are SLSA's. Nothing joins them.
  3. A signature on the dwarves publish path.
  4. One trust store, or a stated reason for four. A customer today enrols a cert fingerprint (holger), an ssh fingerprint (gunnar), and three separate raw ed25519 keys (Skidbladnir, korp-installer, monetize).
  5. Trusted time in the verifiers. holger's own signer.rs:86-100 says it plainly: "on the far side of an airgap now is the weakest input in the system: a flat RTC battery hands the verifier 1970 … there is no NTP to correct either." And holger still calls Utc::now() (api.rs:31,45; verify.rs:495).

4. THE HOME — what goes in edda, and what must be published

The rule: gunnar and holger both depend on edda; neither depends on the other. edda exists for exactly this. The standalone-clone rule is real — it already forced holger-front to crates.io.

Already solved, nothing to publish

Crate Status Action
znippy-common (DSSE + CMS + M-of-N + cert fingerprints) 0.9.15 on crates.io since 2026-09-09, byte-identical to local, sign feature present holger/server/lib/Cargo.toml:181 → drop path=, bump to version = "0.9.15". One line. Delete the stale comment at :173-178. This closes the whole "reuse znippy's DSSE breaks the clone rule" objection.
edda/nornir-build-thing (in-toto + SLSA + the sealer seams) 0.1.18 published, matches local nothing. It is already the home for the statement half.

Must be published for the collapse to happen without path deps

# Crate Where it should live Why Publish via
1 nornir-auth-webauthn 0.1.2 already in edda; NOT on crates.io (MEASURED: absent from the registry index) the single blocker to deleting four hand-rolled ES256 verifiers in garmr, gunnar-ui, holger-ui and monetize-ui-server dwarves release
2 modgunn 0.0.6 its own repo, fine holger path-deps it (server/lib/Cargo.toml:137), so holger cannot be cloned standalone today dwarves release
3 facett its own repo the last remaining [patch.crates-io] in holger (holger/Cargo.toml:116) — local 0.1.16 vs registry 0.1.15. While it stands, 19 holger crates are fork-riders dwarves release
4 a new nordisk-clock (from gunnar-clock) edda §5 dwarves release
5 (optional, later) split znippy-sign out of znippy-common edda or znippy znippy-common drags arrow + openzl for a consumer that wants only PAE + a cert fingerprint. Not urgent — default-features = false already makes it tolerable dwarves release

Publishing mechanics, measured today: the subcommand is dwarves release, not crate-release. ~/.cargo/bin/dwarves is built without --features rehearse, so Gate B is compiled out; the rehearse build is /home/rickard/cargo-target/release/dwarves. Use that one. Gate B earned its keep today — it caught holger-errcode 0.1.1 being four rows short of its source before upload. A dwarves built without rehearse now refuses --execute outright (release_doctor/mod.rs:412), which is the right behaviour.

What should NOT be collapsed

Do not merge the trust stores. Skidbladnir's pinned raw key is correct for an airgap; korp-installer's /boot/korp/root.pub is correct for a PID-1 with no allocator; holger's cert fingerprints are correct for a two-person release board. Share the crate (one ed25519 detached sign/verify with domain separation, in edda), keep the stores.


5. gunnar-clock — yes, it belongs in the shared home

MEASURED:

Recommendation: move gunnar-clock to edda/crates/ as nordisk-clock (edda already hosts gunnar-bitmap, gunnar-delta, gunnar-pack, gunnar-pktline, gunnar-sendpack, gunnar-stage, so a gunnar-born crate in edda is the established pattern), set publish = true, and publish it. gunnar depends on edda; holger depends on edda; neither on the other. Keep default = [] so a consumer that only wants the report pays for no socket and no crypto.


6. What needs the owner

  1. Ruling: one provenance gate or two? modgunn ships a default-on verifier; holger pins scan and files attestations itself. Both are real, neither is wrong, and today neither runs in a shipped holger (holger's sign is not in default).
  2. Ruling: does gunnar export its BuildRecord to DSSE/in-toto? gunnar's own comment says it is a rename. Doing it joins gunnar's chain to the house's. Not doing it means gunnar's build provenance stops at gunnar's edge — which may be the correct answer for the airgap product.
  3. Go-ahead to publish four crates (nornir-auth-webauthn, modgunn, facett, nordisk-clock). The standing go for "publish what a build needs" probably covers this, but facett and a crate move are more than a version bump.
  4. Decision on the throw-away CA. nornir --seal signing with ZnippySealer::dev() is either a known placeholder awaiting a key store, or a bug. It reads as a placeholder; it ships as a signature nobody can verify.
  5. Decision on tunnr. tunnr signs and verifies nothing; its appliance PID-1 trusts an unsigned ESP cmdline by design, with Secure Boot designed here but implemented in korp-installer. Is that the settled shape, or a gap?