From ecbc8813d6416d988ea1b8b400e9f317b797a777 Mon Sep 17 00:00:00 2001 From: Robert Allan James Date: Fri, 28 Aug 2026 14:45:53 -0400 Subject: [PATCH] =?UTF-8?q?FABRIC-3.md:=20close=20MINT=20(Phase=20E=20/=20?= =?UTF-8?q?=C2=A7F.19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD --- FABRIC-3.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/FABRIC-3.md b/FABRIC-3.md index 677df87..670399f 100644 --- a/FABRIC-3.md +++ b/FABRIC-3.md @@ -1978,6 +1978,11 @@ refuse-on-non-blank-media posture — reasonable by analogy, not decided here. T is unchanged and already modeled in the graph: `WRITE(10)` still doesn't exist, so `MINT` cannot write anything to real or QEMU-emulated USB media yet regardless of the above. +**BUILT + VERIFIED 2026-08-28 (§F.19)** — every "not yet scoped" item above resolved: default +personality is a real, working minimal capsule (not a stub); `minted_time_ns` stays `0`, no +precedent found or invented; `MINT` takes no name argument at all (see §F.19 for why); `MINT` +does refuse an already-recognized drive, decided rather than left as "reasonable by analogy." + ### F.9 — `BINDSTEP` (extending `sk_repl_set_active_vm()` with the `ACLKEY` check) Traced against the live retarget path before assuming new plumbing was needed: @@ -2662,3 +2667,86 @@ diagnostic-only and should not survive into that flow. A RUNCAP-born VM has no live as the idle pump's expected `MSG-TICK` refusal for `RunCapTest`/`RunCapTest2`, harmless since these are test-only VMs never present during a normal boot). `MINT` (§F.8) is now genuinely unblocked. + +### F.19 — `MINT` built + verified: the full Tripod identity vision works end to end + +With `CERTVERIFY` (§F.7/§B), `RUNCAP` (§F.6/§F.18), and Zuse's own on-device keypair +(Phase 8, `zuse_cert_devblock.h`) all real, `MINT` closed the last gap: nothing could +*produce* a drive RUNCAP/CERTVERIFY could consume. Same discipline as §F.18 — build against +the real code, verify live, don't trust the scoping pass's own assumptions without checking. + +**New DER capability: `x509_build_user_cert()`** (`x509_ed25519.h/.c`) — the encode-side +counterpart to the three existing decode functions. A minimal DER TLV writer (short-form +length only; every field here stays well under the 128-byte threshold except the two outer +`SEQUENCE` wrappers, hand-encoded with one long-form length byte). `issuer`/`validity`/ +`subject` are each an empty `SEQUENCE` — valid, zero-length TLVs the decode side already only +ever skips by length, never reads the content of; `version` is omitted entirely (implicit v1, +matching the decoder's own optional-version handling). **Host-tested round-trip before +trusting it in the kernel** (same precedent as `CERTVERIFY`'s own host test, §F.7): built two +certs (one with an ordinary serial, one with a high-bit-set serial forcing the DER +zero-pad-integer rule), decoded both back with the real `x509_extract_ed25519_pubkey()`/ +`x509_extract_serial()`/`x509_verify_signature()` — pubkey and serial round-trip exactly, +signature verifies against the real issuer, correctly rejects the subject's own key and a +corrupted signature. 10/10 assertions pass. Cert size: 155 bytes, comfortably one devblock. + +**Fixed devblock layout, decided rather than left open:** devblock 1 = `homeblocks_sig_t` +(unchanged, §F.13); devblock 2 = the DER cert (`cert_offset`/`cert_devblocks=1`); devblock 3 = +new `user_identity_seed_t` (magic/version/seed\[32\]/pubkey\[32\]/crc, same convention as +`zuse_cert_devblock_t` — a dedicated type, not a reuse, per this project's own "give +real-shaped data its own header" rule); devblock 4 = the default personality source. +`metadata_devblocks=5` (0..4 reserved; devblock 0 stays the block-subsystem's own generic +header, untouched). `homeblocks_sig_t` is written *last*, deliberately, so a crash partway +through minting never leaves a drive that reads as a complete, valid home-blocks drive with +content actually missing behind it. + +**Real keypair, real entropy, real signer** (§F.8 decisions 1/3/4, unchanged from scoping): +`virtio_rng_get_bytes()` + `ed25519_keygen()` for the identity's own seed/pubkey; a second, +independent `virtio_rng_get_bytes()` draw for `drive_uuid` (never derived from the identity +seed); the cert signed with the *caller's* `zuse_cert_seed` (in practice always Hera's own VM +struct, where Zuse's on-device key lives) via `ed25519_sign()`. + +**Default personality is real, working code, not a stub** — a small FORTH capsule defining and +immediately calling `WELCOME`, which prints a real banner. `§F.6`/`§F.8`'s "default +personality content: mint-time-fixed vs. user-editable" question stays genuinely open — this +doesn't answer it, but whatever mints today has to actually *do* something once RUNCAP births +it, matching this project's "no stubs" discipline even where the surrounding design question +is deliberately deferred. Needs the same `"Block NNNN\n"` header §F.18 found `capsule_exec_ +payload()` requires — baked in directly, not left for a future pass to discover the hard way +again. + +**One new decision, not left "reasonable by analogy" any longer:** `MINT` refuses to write +onto a drive that already reads as `HOMEBLOCKS_SIG_OK` (`MINT_ERR_ALREADY_MINTED`) — mirrors +`WRITE(10)`'s own refuse-on-non-blank posture, now decided rather than deferred. + +**No name argument, deliberately deviating from F.8's own original `S" name" MINT` sketch:** +this design never binds a VM name at mint time at all — the drive holds an anonymous, +self-contained identity; a name is assigned later, at RUNCAP birth, which is a `WIREBIND` +decision, not `MINT`'s. `MINT ( -- ok? )` takes nothing and operates on whatever +`sk_repl_get_attached_blk_dev()` (new accessor, `repl.h/.c`, mirrors §F.18's home-blocks-only +one but doesn't gate on recognition — a blank drive by definition never sets that one) +currently reports. + +**Verified end-to-end, live in QEMU — the full mint-to-birth flow working for the first time:** +a genuinely blank test image, hotplugged and confirmed "not recognized (blank or foreign +media)"; `MINT` ran and reported success; `RUNCAP-TEST` on the same drive then birthed a real +VM from its own freshly-minted content; `VM-EXEC`ing `WELCOME` printed the default personality +banner. Every layer built this session — `CERTVERIFY`'s DER decoder, `RUNCAP`'s capsule +construction, `MINT`'s DER encoder and keypair generation — composed correctly on the first +real end-to-end attempt. Clean 3-architecture regression boot (no test drive attached) +confirms no side effects. Commit `f6e2737`. + +**One real workflow gap found, deliberately not fixed in this pass:** a freshly-minted drive +is not recognized by `RUNCAP` until physically detached and reattached — the home-blocks sig +cache (§F.18's `g_homeblocks_dev`/`sig`) only refreshes at USB attach time, and `MINT` (which +just wrote a valid sig it knows is now correct) doesn't update that cache itself. Worked around +for this verification via a QMP detach/reattach; a real user plugging in a blank drive, minting +it, and expecting it to work without unplugging would hit this. Flagged for whoever builds the +real Console onboarding flow (§D.6) — that flow controls the actual mint UX and is the right +place to decide whether `MINT` should refresh the cache itself or the onboarding flow should +prompt for a reattach. + +**Still open:** `WIREBIND`/`BINDSTEP` (§F.5/§F.9) — the real call sites for both `RUNCAP` and +`MINT`; `RUNCAP-TEST` and `MINT`'s own bare console word remain diagnostic scaffolding, not +meant to survive into that flow unchanged. With `CERTVERIFY`, `RUNCAP`, and `MINT` all real and +composing, `WIREBIND` is now the last piece standing between this arc and D.7's original +message-only birth vision.