course refresh: the notebooks enter the SLH-DSA era

The 14-notebook course predated the post-quantum campaign entirely
(coherence findings 10, 11). Now, authored in the GENERATOR and
regenerated (AGENTS.md rule):
- notebook 06: new section 'The second signature that actually shipped:
  SLH-DSA' — the deterministic co-signature since tree size 14, chosen
  because the log attests its own parameter set's verify path (leaf 18,
  11 certs); absent-not-failed for older heads; determinism as an audit
  primitive; verify-only always. Plus a runnable keygen/sign/verify/
  re-sign-byte-equality demo (honest skip below OpenSSL 3.5) and the
  --slhdsa-public-key consumer flag in the policy list.
- notebook 09: the 'post-quantum line' is now three-legged — Ed25519
  proven-verify dogfood, SLH-DSA shipped-and-attested, ML-DSA required-
  but-honest-unavailable — with the closing point that a slot stops
  being aspirational the day its verify path enters the log; stale
  16/16 provenance count -> 44/44 (leaf 13 re-attestation).
- notebook 07: policy exercise extended with the co-signature question;
  00 course map goal updated; README course listing for 06/09.
- GENERATOR DRIFT REPAIRED in passing: notebook 10's cockpit cell had
  been added to the .ipynb but never backported to the generator —
  regeneration would have silently dropped it; the cell is now IN the
  generator and round-trips (19 cells, content identical).
Suite 157 green.
This commit is contained in:
mrwulf 2026-08-22 21:18:01 +02:00
parent 31a9dee10a
commit 1086a3ba02
8 changed files with 893 additions and 801 deletions

View file

@ -125,11 +125,11 @@ The `notebooks/` directory contains a zero-to-hero teaching sequence for undergr
- `03_lean_replay_and_axiom_audit.ipynb`: replay versus transpilation, Lean invocation, axiom audits. - `03_lean_replay_and_axiom_audit.ipynb`: replay versus transpilation, Lean invocation, axiom audits.
- `04_proof_hygiene_and_boundaries.ipynb`: `sorry`, local axioms, trivial targets, manifest coverage. - `04_proof_hygiene_and_boundaries.ipynb`: `sorry`, local axioms, trivial targets, manifest coverage.
- `05_third_party_attestation_provider.ipynb`: provider trust transformation and signed attestations. - `05_third_party_attestation_provider.ipynb`: provider trust transformation and signed attestations.
- `06_merkle_transparency_logs.ipynb`: RFC 9162-style Merkle proofs, STHs, Ed25519/ML-DSA policy. - `06_merkle_transparency_logs.ipynb`: RFC 9162-style Merkle proofs, STHs, the dual-signature head policy (Ed25519 + the shipped SLH-DSA co-signature) and the fail-closed ML-DSA slot.
- `06a_provider_build_the_log.ipynb` / `06b_agent_verify_inclusion.ipynb`: the MIRRORED PAIR - one provider (builds, Lean-verifies, signs with the merkleized library, self-checks its own inclusion), many agents (verify inclusion from scratch in ~25 lines, no Lean); the domain separation is the design and the lecture structure mirrors it. - `06a_provider_build_the_log.ipynb` / `06b_agent_verify_inclusion.ipynb`: the MIRRORED PAIR - one provider (builds, Lean-verifies, signs with the merkleized library, self-checks its own inclusion), many agents (verify inclusion from scratch in ~25 lines, no Lean); the domain separation is the design and the lecture structure mirrors it.
- `07_agent_consequences.ipynb`: receipt-gated artifact builds and wallet-denial policy. - `07_agent_consequences.ipynb`: receipt-gated artifact builds and wallet-denial policy.
- `08_capstone_research_program.ipynb`: audit the shipped R4 evidence; design the R5 discharge plan. - `08_capstone_research_program.ipynb`: audit the shipped R4 evidence; design the R5 discharge plan.
- `09_dogfood_verified_crypto.ipynb`: the proven-path verifier in the agent's own loop; hybrid-PQC posture. - `09_dogfood_verified_crypto.ipynb`: the proven-path verifier in the agent's own loop; the three-legged PQ posture (Ed25519 dogfood, shipped SLH-DSA, honest ML-DSA slot).
- `10_verified_custody_wallet.ipynb`: warden - the quorum custody boundary and signing firewall, ratchet-rule (toy 3-of-3, then the real four proven forks), plus the counterparty recomputing a custody card's inclusion proof. - `10_verified_custody_wallet.ipynb`: warden - the quorum custody boundary and signing firewall, ratchet-rule (toy 3-of-3, then the real four proven forks), plus the counterparty recomputing a custody card's inclusion proof.
- `11_the_customers_eye_view.ipynb`: the counterparty's seat - what a customer can and cannot recompute about someone else's wallet from its custody card and the public log. - `11_the_customers_eye_view.ipynb`: the counterparty's seat - what a customer can and cannot recompute about someone else's wallet from its custody card and the public log.

View file

@ -30,7 +30,7 @@
"- Perform a proof hygiene scan and explain why `sorry`, local axioms, and trivial theorem targets are dangerous.\n", "- Perform a proof hygiene scan and explain why `sorry`, local axioms, and trivial theorem targets are dangerous.\n",
"- Explain how a third-party proof-checking provider changes the trusted base.\n", "- Explain how a third-party proof-checking provider changes the trusted base.\n",
"- Implement and verify RFC 9162-style Merkle inclusion and consistency proofs.\n", "- Implement and verify RFC 9162-style Merkle inclusion and consistency proofs.\n",
"- Explain why Signed Tree Heads need accountable signatures, why Ed25519 is useful here, and why ML-DSA requires a real backend.\n", "- Explain why Signed Tree Heads need accountable signatures, why Ed25519 is useful here, why ML-DSA requires a real backend, and how the shipped SLH-DSA co-signature differs from both.\n",
"- Design policy gates that convert verification evidence into consequences.\n", "- Design policy gates that convert verification evidence into consequences.\n",
"- Read R4 four-tier apex evidence, name its residual blockers, and write a research plan toward R5 production assurance.\n" "- Read R4 four-tier apex evidence, name its residual blockers, and write a research plan toward R5 production assurance.\n"
] ]
@ -150,26 +150,6 @@
"- NIST FIPS 204, Module-Lattice-Based Digital Signature Standard: https://csrc.nist.gov/pubs/fips/204/final\n", "- NIST FIPS 204, Module-Lattice-Based Digital Signature Standard: https://csrc.nist.gov/pubs/fips/204/final\n",
"- PACTA README: `../README.md`\n" "- PACTA README: `../README.md`\n"
] ]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The course's operational twin: the cockpit and the lab manual\n",
"\n",
"These notebooks are the *code-level* track: you build the machinery with your own hands.\n",
"Two sibling surfaces teach the *operational* track on the very same system:\n",
"\n",
"- **The custody cockpit** — `pacta wallet cockpit --demo` serves a read-only bridge of six\n",
" role stations (proposer, quorum bench, operator, cryptographer, architect, newcomer) over\n",
" live evidence, with a tmux-style deck (`/deck`) showing every station in parallel.\n",
"- **The lab manual** (`docs/warden-lab-manual.md`, served at `/manual`) — a study-club course\n",
" that teaches each *role* hands-on: labs with checkpoints, a safe ledger-tamper drill,\n",
" self-tests, and a capstone incident walked through all six chairs.\n",
"\n",
"Recommended weave: notebooks 0009 first (build it), then the lab manual (run it as a crew),\n",
"with notebook 10 and the manual's warden sessions read side by side."
]
} }
], ],
"metadata": { "metadata": {

View file

@ -128,7 +128,8 @@
"Policy matters:\n", "Policy matters:\n",
"\n", "\n",
"- `require-signatures ed25519`: verify Ed25519 and allow ML-DSA to be unavailable.\n", "- `require-signatures ed25519`: verify Ed25519 and allow ML-DSA to be unavailable.\n",
"- `require-signatures both`: require Ed25519 and ML-DSA verified. If ML-DSA is unavailable, fail closed.\n" "- `require-signatures both`: require Ed25519 and ML-DSA verified. If ML-DSA is unavailable, fail closed.\n",
"- `--slhdsa-public-key <pem>`: additionally verify the second (post-quantum) SLH-DSA co-signature on the head; heads before tree size 14 report `absent` (allowed), a present-but-wrong signature fails closed.\n"
] ]
}, },
{ {
@ -156,6 +157,59 @@
"ML-DSA adds post-quantum robustness for the accumulator signature layer. But it must be a real signature, not an aspirational label. If a host lacks ML-DSA, the correct result is an explicit blocker.\n" "ML-DSA adds post-quantum robustness for the accumulator signature layer. But it must be a real signature, not an aspirational label. If a host lacks ML-DSA, the correct result is an explicit blocker.\n"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The second signature that actually shipped: SLH-DSA\n",
"\n",
"Since tree size 14, every head of the LIVE log carries a second,\n",
"deterministic **SLH-DSA-SHA2-128s** (FIPS 205) signature beside the\n",
"required Ed25519 one. This is not the ML-DSA slot above - it is a\n",
"hash-based scheme, and it was chosen because the estate has PROVEN\n",
"its verify path (eleven certificates, log leaf 18): the log\n",
"co-signs with the parameter set whose verification path it itself\n",
"attests. Three design facts worth internalizing:\n",
"\n",
"1. Heads published before size 14 carry no co-signature, and\n",
" verifiers report them `ABSENT` rather than failing them - an\n",
" append-only log keeps the history of its own signature-scheme\n",
" upgrades.\n",
"2. The co-signature is deterministic on purpose: re-signing the\n",
" same payload is byte-comparable, so \"same input, same\n",
" signature\" becomes a diff you can run, not an assurance you\n",
" must trust.\n",
"3. Signing is still never proven - here, as everywhere in this\n",
" estate, certificates cover the VERIFY path only.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Runnable where OpenSSL >= 3.5 is present; honest skip otherwise.\n",
"import tempfile\n",
"from pathlib import Path\n",
"\n",
"from pacta import slhdsa\n",
"\n",
"tmp = Path(tempfile.mkdtemp(prefix=\"nb06-slhdsa-\"))\n",
"try:\n",
" slhdsa.generate_slhdsa_keypair(tmp / \"slh.key\", tmp / \"slh.pub\")\n",
"except Exception as exc:\n",
" print(\"SLH-DSA unavailable on this host (OpenSSL >= 3.5 needed):\", exc)\n",
"else:\n",
" payload = b\"canonical STH payload bytes\"\n",
" block = slhdsa.slh_dsa_signature_block(payload, tmp / \"slh.key\", tmp / \"slh.pub\")\n",
" ok, err = slhdsa.verify_payload_slhdsa(payload, block[\"signature_base64\"], tmp / \"slh.pub\")\n",
" print(\"co-signature verifies:\", ok, err or \"\")\n",
" block2 = slhdsa.slh_dsa_signature_block(payload, tmp / \"slh.key\", tmp / \"slh.pub\")\n",
" print(\"deterministic (byte-equal re-sign):\",\n",
" block[\"signature_base64\"] == block2[\"signature_base64\"])\n"
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},

View file

@ -201,6 +201,7 @@
"- Modify a claim card to R2 and show that `build-library` is refused.\n", "- Modify a claim card to R2 and show that `build-library` is refused.\n",
"- Explain why a denial artifact is useful for auditability.\n", "- Explain why a denial artifact is useful for auditability.\n",
"- Design a policy where an agent requires `both` Ed25519 and ML-DSA signatures for production deployment but allows Ed25519-only in a local lab.\n", "- Design a policy where an agent requires `both` Ed25519 and ML-DSA signatures for production deployment but allows Ed25519-only in a local lab.\n",
"- Extend it: when should the agent also require the SLH-DSA co-signature, given that heads before tree size 14 legitimately lack it?\n",
"- Write a downstream Rust pseudo-code snippet that imports the generated capsule before enabling a code path.\n" "- Write a downstream Rust pseudo-code snippet that imports the generated capsule before enabling a code path.\n"
] ]
} }

View file

@ -18,7 +18,7 @@
"- State precisely which parts of the dogfood verifier are certificate-covered and which are its trusted base.\n", "- State precisely which parts of the dogfood verifier are certificate-covered and which are its trusted base.\n",
"- Extract a raw Ed25519 key from an OpenSSL PEM by hand (napkin) and mechanically (real).\n", "- Extract a raw Ed25519 key from an OpenSSL PEM by hand (napkin) and mechanically (real).\n",
"- Demonstrate backend dispatch and the fail-closed `--require-verified-verifier` policy.\n", "- Demonstrate backend dispatch and the fail-closed `--require-verified-verifier` policy.\n",
"- Defend the hybrid post-quantum posture: one proven-classical signature plus one required-but-honest ML-DSA slot.\n" "- Defend the three-legged post-quantum posture: proven-classical Ed25519, the shipped SLH-DSA co-signature with its attested verify path, and a required-but-honest ML-DSA slot.\n"
] ]
}, },
{ {
@ -122,7 +122,8 @@
"leaf, against the very tree it is about to sign. The verdict is\n", "leaf, against the very tree it is about to sign. The verdict is\n",
"embedded in the signature block (`signing_provenance`: backend,\n", "embedded in the signature block (`signing_provenance`: backend,\n",
"library commit, leaf index, `self_inclusion: verified`,\n", "library commit, leaf index, `self_inclusion: verified`,\n",
"certificates 16/16). Lectures 6a/6b walk both sides of this.\n", "certificates 44/44 - the signer's source family was re-attested at 44\n",
"certificates as leaf 13). Lectures 6a/6b walk both sides of this.\n",
"Honesty note unchanged: the library's VERIFY path is\n", "Honesty note unchanged: the library's VERIFY path is\n",
"certificate-covered; the signing path is declared trusted base -\n", "certificate-covered; the signing path is declared trusted base -\n",
"but it is the attested artifact, not an un-attested third\n", "but it is the attested artifact, not an un-attested third\n",
@ -130,12 +131,13 @@
"\n", "\n",
"## The post-quantum line, held honestly\n", "## The post-quantum line, held honestly\n",
"\n", "\n",
"The dogfood loop deliberately does NOT extend to ML-DSA. There is no formally verified ML-DSA implementation in this corpus, and pretending otherwise would poison the whole posture. The hybrid strategy is therefore asymmetric on purpose:\n", "The posture has three legs now, and each is exactly as strong as it claims:\n",
"\n", "\n",
"- **Ed25519 (classical): proven path.** The signature everyone can check today runs on certificate-covered code.\n", "- **Ed25519 (classical): proven verify path, dogfooded.** The signature everyone can check today runs on certificate-covered code.\n",
"- **ML-DSA-65 (post-quantum): required, honest, unavailable-until-real.** The tree-head slot exists in every signed structure; `--require-signatures both` fails CLOSED on hosts without a real FIPS 204 backend; and when a real backend lands, the policy flips on without a schema change.\n", "- **SLH-DSA-SHA2-128s (post-quantum): shipped and attested.** Since tree size 14 every live head carries a second, deterministic SLH-DSA co-signature. The estate proved the VERIFY path of a pinned Rust FIPS 205 implementation (eleven certificates) and appended that attestation as leaf 18 - so the co-signature uses exactly the parameter set the log itself attests. Consumers check it with `pacta receipt-verify ... --slhdsa-public-key provider.slhdsa.pub` or the mirror's `verify.py`. Signing remains unproven - verify paths only, always.\n",
"- **ML-DSA-65 (lattice PQ): required, honest, unavailable-until-real.** The tree-head slot exists in every signed structure; `--require-signatures both` fails CLOSED on hosts without a real FIPS 204 backend; when a real backend lands, the policy flips on without a schema change.\n",
"\n", "\n",
"A migration strategy that records \"we cannot do this yet\" as a deployment blocker is strictly stronger than one that ships a placeholder. Blockers get fixed; placeholders get trusted.\n" "A migration strategy that records \"we cannot do this yet\" as a deployment blocker is strictly stronger than one that ships a placeholder. Blockers get fixed; placeholders get trusted. And the SLH-DSA leg shows the endgame: a slot stops being aspirational the day its verify path enters the log.\n"
] ]
}, },
{ {

View file

@ -45,7 +45,7 @@
"## Why a quorum, when one proof would do?\n", "## Why a quorum, when one proof would do?\n",
"\n", "\n",
"Each member is *proven* to decide the same predicate,\n", "Each member is *proven* to decide the same predicate,\n",
"`accept(A,m,R,s) ⇔ decompress(R) = [k](A) + [s]B`. So on the\n", "`accept(A,m,R,s) \u21d4 decompress(R) = [k](\u2212A) + [s]B`. So on the\n",
"proven domain they cannot disagree about *meaning*. Classic\n", "proven domain they cannot disagree about *meaning*. Classic\n",
"N-version programming hopes independent code won't share a bug;\n", "N-version programming hopes independent code won't share a bug;\n",
"we do not hope - we know the semantics coincide, so a runtime\n", "we do not hope - we know the semantics coincide, so a runtime\n",
@ -343,8 +343,8 @@
"\n", "\n",
"Open `/deck` for all six role stations live in parallel (the quorum bench you built is the\n", "Open `/deck` for all six role stations live in parallel (the quorum bench you built is the\n",
"indigo pane; the ledger you hash-chained is re-verified on every page load), and `/manual`\n", "indigo pane; the ledger you hash-chained is re-verified on every page load), and `/manual`\n",
"for the lab-manual sessions that teach each role Session 4's tamper drill breaks a *copy*\n", "for the lab-manual sessions that teach each role \u2014 Session 4's tamper drill breaks a *copy*\n",
"of a ledger exactly like this notebook's and watches two independent surfaces catch it." "of a ledger exactly like this notebook's and watches two independent surfaces catch it.\n"
] ]
} }
], ],

View file

@ -4,12 +4,12 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"# Lecture 11: The Customer's-Eye View — You Hold the Ruler\n", "# Lecture 11: The Customer's-Eye View \u2014 You Hold the Ruler\n",
"\n", "\n",
"Every earlier lecture looked at the log from the *operator's*\n", "Every earlier lecture looked at the log from the *operator's*\n",
"side: replaying proofs, signing attestations, building the\n", "side: replaying proofs, signing attestations, building the\n",
"tree. This one flips the telescope. You are now a customer an\n", "tree. This one flips the telescope. You are now a customer \u2014 an\n",
"agent, a wallet, a developer — who wants to trust one of the\n", "agent, a wallet, a developer \u2014 who wants to trust one of the\n",
"four verified Ed25519 forks and has no theorem prover, no Lean,\n", "four verified Ed25519 forks and has no theorem prover, no Lean,\n",
"no desire to spend hours re-checking anything. What actually\n", "no desire to spend hours re-checking anything. What actually\n",
"happens on your side? The answer reframes the whole system, and\n", "happens on your side? The answer reframes the whole system, and\n",
@ -24,7 +24,7 @@
"\n", "\n",
"- Separate the three roles that a verification touches:\n", "- Separate the three roles that a verification touches:\n",
" *measurement* (operator), *publication* (the log), and\n", " *measurement* (operator), *publication* (the log), and\n",
" *judgment* (you) — and see that only the last is yours.\n", " *judgment* (you) \u2014 and see that only the last is yours.\n",
"- Understand the \"allowed axioms\" list as a **requirements\n", "- Understand the \"allowed axioms\" list as a **requirements\n",
" card** you own, can read, and could write yourself.\n", " card** you own, can read, and could write yourself.\n",
"- Explain why a self-written card meeting the supply *exactly*\n", "- Explain why a self-written card meeting the supply *exactly*\n",
@ -38,7 +38,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## The card is a requirements card — and ideally you write it\n", "## The card is a requirements card \u2014 and ideally you write it\n",
"\n", "\n",
"The list of axioms a certificate is *allowed* to rest on is not\n", "The list of axioms a certificate is *allowed* to rest on is not\n",
"handed to you by the operator at verification time. It ships\n", "handed to you by the operator at verification time. It ships\n",
@ -85,12 +85,12 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## Wish meets supply — and it is no accident\n", "## Wish meets supply \u2014 and it is no accident\n",
"\n", "\n",
"Now compare your self-written card against what the four forks\n", "Now compare your self-written card against what the four forks\n",
"actually declare. They match. That convergence is *engineered*:\n", "actually declare. They match. That convergence is *engineered*:\n",
"the supply was deliberately shrunk — every axiom made to justify\n", "the supply was deliberately shrunk \u2014 every axiom made to justify\n",
"its existence — so that any reasonable person's independently\n", "its existence \u2014 so that any reasonable person's independently\n",
"written card lands on the same minimal list. When the wish meets\n", "written card lands on the same minimal list. When the wish meets\n",
"the supply exactly, it is because the supplier spent months\n", "the supply exactly, it is because the supplier spent months\n",
"making the supply as small as honesty allows.\n" "making the supply as small as honesty allows.\n"
@ -131,20 +131,20 @@
"\n", "\n",
"Only judgment is yours, and it is the only step that involves an\n", "Only judgment is yours, and it is the only step that involves an\n",
"opinion. The operator is trusted to *copy down what the kernel\n", "opinion. The operator is trusted to *copy down what the kernel\n",
"printed* — never to interpret it.\n" "printed* \u2014 never to interpret it.\n"
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## If you are happy — you are already finished\n", "## If you are happy \u2014 you are already finished\n",
"\n", "\n",
"If your card covers the observed cone, verification is: check\n", "If your card covers the observed cone, verification is: check\n",
"one signature, walk ~4 hashes to the signed root, compare cones\n", "one signature, walk ~4 hashes to the signed root, compare cones\n",
"to your card. Milliseconds, standard library, no Lean. Done.\n", "to your card. Milliseconds, standard library, no Lean. Done.\n",
"\n", "\n",
"## If you are NOT happy — there is nothing to negotiate\n", "## If you are NOT happy \u2014 there is nothing to negotiate\n",
"\n", "\n",
"Suppose your card is stricter: *\"I require SHA-512 itself\n", "Suppose your card is stricter: *\"I require SHA-512 itself\n",
"proven, not an oracle.\"* The system has no sales pitch for you.\n", "proven, not an oracle.\"* The system has no sales pitch for you.\n",
@ -152,7 +152,7 @@
"*not in supply; SHA-512-proven is the R5 frontier; here is the\n", "*not in supply; SHA-512-proven is the R5 frontier; here is the\n",
"exact list of what you would be accepting if you proceed\n", "exact list of what you would be accepting if you proceed\n",
"anyway.* Your decision is never \"lower my standards\" in the\n", "anyway.* Your decision is never \"lower my standards\" in the\n",
"abstract — it is a named line item: *SHA-512 as oracle: yes/no.*\n", "abstract \u2014 it is a named line item: *SHA-512 as oracle: yes/no.*\n",
"Informed compromise, not diffuse trust.\n" "Informed compromise, not diffuse trust.\n"
] ]
}, },
@ -180,7 +180,7 @@
"\n", "\n",
"The gap between anyone's dream card and today's supply is a\n", "The gap between anyone's dream card and today's supply is a\n",
"to-do list, not a wall. Prove SHA-512, certify the wire\n", "to-do list, not a wall. Prove SHA-512, certify the wire\n",
"parsers, and the new certificates enter the same log and\n", "parsers, and the new certificates enter the same log \u2014 and\n",
"stricter cards start passing. The log is additive in exactly\n", "stricter cards start passing. The log is additive in exactly\n",
"the way requirements are.\n", "the way requirements are.\n",
"\n", "\n",
@ -211,19 +211,6 @@
" card and the supply)? What single action by a customer\n", " card and the supply)? What single action by a customer\n",
" settles the argument in their favor?\n" " settles the argument in their favor?\n"
] ]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Do it in the cockpit: the Inspect instrument\n",
"\n",
"The recomputation this notebook performs by hand is exactly what the cockpit's **Inspect**\n",
"instrument (`/inspect`) runs on paste — same deployed verifier, verbatim diagnostics —\n",
"and what the lab manual's Cryptographer session drills: verify the sample evidence, then\n",
"deliberately corrupt one character and learn the distinct failure surfaces.\n",
"`pacta wallet cockpit --demo`, then Inspect → “Load the sample evidence”."
]
} }
], ],
"metadata": { "metadata": {

View file

@ -82,7 +82,7 @@ COURSE = {
- Perform a proof hygiene scan and explain why `sorry`, local axioms, and trivial theorem targets are dangerous. - Perform a proof hygiene scan and explain why `sorry`, local axioms, and trivial theorem targets are dangerous.
- Explain how a third-party proof-checking provider changes the trusted base. - Explain how a third-party proof-checking provider changes the trusted base.
- Implement and verify RFC 9162-style Merkle inclusion and consistency proofs. - Implement and verify RFC 9162-style Merkle inclusion and consistency proofs.
- Explain why Signed Tree Heads need accountable signatures, why Ed25519 is useful here, and why ML-DSA requires a real backend. - Explain why Signed Tree Heads need accountable signatures, why Ed25519 is useful here, why ML-DSA requires a real backend, and how the shipped SLH-DSA co-signature differs from both.
- Design policy gates that convert verification evidence into consequences. - Design policy gates that convert verification evidence into consequences.
- Read R4 four-tier apex evidence, name its residual blockers, and write a research plan toward R5 production assurance. - Read R4 four-tier apex evidence, name its residual blockers, and write a research plan toward R5 production assurance.
""" """
@ -1029,6 +1029,7 @@ COURSE = {
- `require-signatures ed25519`: verify Ed25519 and allow ML-DSA to be unavailable. - `require-signatures ed25519`: verify Ed25519 and allow ML-DSA to be unavailable.
- `require-signatures both`: require Ed25519 and ML-DSA verified. If ML-DSA is unavailable, fail closed. - `require-signatures both`: require Ed25519 and ML-DSA verified. If ML-DSA is unavailable, fail closed.
- `--slhdsa-public-key <pem>`: additionally verify the second (post-quantum) SLH-DSA co-signature on the head; heads before tree size 14 report `absent` (allowed), a present-but-wrong signature fails closed.
""" """
), ),
code( code(
@ -1050,6 +1051,53 @@ COURSE = {
ML-DSA adds post-quantum robustness for the accumulator signature layer. But it must be a real signature, not an aspirational label. If a host lacks ML-DSA, the correct result is an explicit blocker. ML-DSA adds post-quantum robustness for the accumulator signature layer. But it must be a real signature, not an aspirational label. If a host lacks ML-DSA, the correct result is an explicit blocker.
""" """
), ),
md(
"""
## The second signature that actually shipped: SLH-DSA
Since tree size 14, every head of the LIVE log carries a second,
deterministic **SLH-DSA-SHA2-128s** (FIPS 205) signature beside the
required Ed25519 one. This is not the ML-DSA slot above - it is a
hash-based scheme, and it was chosen because the estate has PROVEN
its verify path (eleven certificates, log leaf 18): the log
co-signs with the parameter set whose verification path it itself
attests. Three design facts worth internalizing:
1. Heads published before size 14 carry no co-signature, and
verifiers report them `ABSENT` rather than failing them - an
append-only log keeps the history of its own signature-scheme
upgrades.
2. The co-signature is deterministic on purpose: re-signing the
same payload is byte-comparable, so "same input, same
signature" becomes a diff you can run, not an assurance you
must trust.
3. Signing is still never proven - here, as everywhere in this
estate, certificates cover the VERIFY path only.
"""
),
code(
"""
# Runnable where OpenSSL >= 3.5 is present; honest skip otherwise.
import tempfile
from pathlib import Path
from pacta import slhdsa
tmp = Path(tempfile.mkdtemp(prefix="nb06-slhdsa-"))
try:
slhdsa.generate_slhdsa_keypair(tmp / "slh.key", tmp / "slh.pub")
except Exception as exc:
print("SLH-DSA unavailable on this host (OpenSSL >= 3.5 needed):", exc)
else:
payload = b"canonical STH payload bytes"
block = slhdsa.slh_dsa_signature_block(payload, tmp / "slh.key", tmp / "slh.pub")
ok, err = slhdsa.verify_payload_slhdsa(payload, block["signature_base64"], tmp / "slh.pub")
print("co-signature verifies:", ok, err or "")
block2 = slhdsa.slh_dsa_signature_block(payload, tmp / "slh.key", tmp / "slh.pub")
print("deterministic (byte-equal re-sign):",
block["signature_base64"] == block2["signature_base64"])
"""
),
md( md(
""" """
## Two domains, two notebooks - by design ## Two domains, two notebooks - by design
@ -1749,6 +1797,7 @@ COURSE = {
- Modify a claim card to R2 and show that `build-library` is refused. - Modify a claim card to R2 and show that `build-library` is refused.
- Explain why a denial artifact is useful for auditability. - Explain why a denial artifact is useful for auditability.
- Design a policy where an agent requires `both` Ed25519 and ML-DSA signatures for production deployment but allows Ed25519-only in a local lab. - Design a policy where an agent requires `both` Ed25519 and ML-DSA signatures for production deployment but allows Ed25519-only in a local lab.
- Extend it: when should the agent also require the SLH-DSA co-signature, given that heads before tree size 14 legitimately lack it?
- Write a downstream Rust pseudo-code snippet that imports the generated capsule before enabling a code path. - Write a downstream Rust pseudo-code snippet that imports the generated capsule before enabling a code path.
""" """
), ),
@ -1770,7 +1819,7 @@ COURSE = {
- State precisely which parts of the dogfood verifier are certificate-covered and which are its trusted base. - State precisely which parts of the dogfood verifier are certificate-covered and which are its trusted base.
- Extract a raw Ed25519 key from an OpenSSL PEM by hand (napkin) and mechanically (real). - Extract a raw Ed25519 key from an OpenSSL PEM by hand (napkin) and mechanically (real).
- Demonstrate backend dispatch and the fail-closed `--require-verified-verifier` policy. - Demonstrate backend dispatch and the fail-closed `--require-verified-verifier` policy.
- Defend the hybrid post-quantum posture: one proven-classical signature plus one required-but-honest ML-DSA slot. - Defend the three-legged post-quantum posture: proven-classical Ed25519, the shipped SLH-DSA co-signature with its attested verify path, and a required-but-honest ML-DSA slot.
""" """
), ),
md( md(
@ -1858,7 +1907,8 @@ COURSE = {
leaf, against the very tree it is about to sign. The verdict is leaf, against the very tree it is about to sign. The verdict is
embedded in the signature block (`signing_provenance`: backend, embedded in the signature block (`signing_provenance`: backend,
library commit, leaf index, `self_inclusion: verified`, library commit, leaf index, `self_inclusion: verified`,
certificates 16/16). Lectures 6a/6b walk both sides of this. certificates 44/44 - the signer's source family was re-attested at 44
certificates as leaf 13). Lectures 6a/6b walk both sides of this.
Honesty note unchanged: the library's VERIFY path is Honesty note unchanged: the library's VERIFY path is
certificate-covered; the signing path is declared trusted base - certificate-covered; the signing path is declared trusted base -
but it is the attested artifact, not an un-attested third but it is the attested artifact, not an un-attested third
@ -1866,12 +1916,13 @@ COURSE = {
## The post-quantum line, held honestly ## The post-quantum line, held honestly
The dogfood loop deliberately does NOT extend to ML-DSA. There is no formally verified ML-DSA implementation in this corpus, and pretending otherwise would poison the whole posture. The hybrid strategy is therefore asymmetric on purpose: The posture has three legs now, and each is exactly as strong as it claims:
- **Ed25519 (classical): proven path.** The signature everyone can check today runs on certificate-covered code. - **Ed25519 (classical): proven verify path, dogfooded.** The signature everyone can check today runs on certificate-covered code.
- **ML-DSA-65 (post-quantum): required, honest, unavailable-until-real.** The tree-head slot exists in every signed structure; `--require-signatures both` fails CLOSED on hosts without a real FIPS 204 backend; and when a real backend lands, the policy flips on without a schema change. - **SLH-DSA-SHA2-128s (post-quantum): shipped and attested.** Since tree size 14 every live head carries a second, deterministic SLH-DSA co-signature. The estate proved the VERIFY path of a pinned Rust FIPS 205 implementation (eleven certificates) and appended that attestation as leaf 18 - so the co-signature uses exactly the parameter set the log itself attests. Consumers check it with `pacta receipt-verify ... --slhdsa-public-key provider.slhdsa.pub` or the mirror's `verify.py`. Signing remains unproven - verify paths only, always.
- **ML-DSA-65 (lattice PQ): required, honest, unavailable-until-real.** The tree-head slot exists in every signed structure; `--require-signatures both` fails CLOSED on hosts without a real FIPS 204 backend; when a real backend lands, the policy flips on without a schema change.
A migration strategy that records "we cannot do this yet" as a deployment blocker is strictly stronger than one that ships a placeholder. Blockers get fixed; placeholders get trusted. A migration strategy that records "we cannot do this yet" as a deployment blocker is strictly stronger than one that ships a placeholder. Blockers get fixed; placeholders get trusted. And the SLH-DSA leg shows the endgame: a slot stops being aspirational the day its verify path enters the log.
""" """
), ),
code( code(
@ -2310,6 +2361,23 @@ COURSE = {
trusted for after you do. trusted for after you do.
""" """
), ),
md(
"""
## The human surface: see this wallet through the cockpit
Everything this notebook built programmatically has a read-only human console:
```
pacta wallet cockpit --demo # throwaway demo wallet, zero setup
pacta wallet cockpit --wallet DIR # the wallet you just sealed here
```
Open `/deck` for all six role stations live in parallel (the quorum bench you built is the
indigo pane; the ledger you hash-chained is re-verified on every page load), and `/manual`
for the lab-manual sessions that teach each role Session 4's tamper drill breaks a *copy*
of a ledger exactly like this notebook's and watches two independent surfaces catch it.
"""
),
] ]
), ),
"11_the_customers_eye_view.ipynb": notebook( "11_the_customers_eye_view.ipynb": notebook(