Retire emergency CLI: Zuse goes thumbdrive-resident, ACL.4th activated

Three tightly-coupled changes, verified together per Captain Bob's own
"getting rid of the emergency cli" direction:

1. Zuse's identity is thumbdrive-resident, never system-resident. New
   zuse_genesis_marker_t (magic/version/zuse_pubkey[32]/crc) replaces
   zuse_cert_devblock_t's slot in the top-of-device fence -- the system
   now remembers only that a root identity exists and its pubkey, never
   a seed. zuse_cert_devblock_t is kept in the repo, marked superseded,
   no longer written by any code path.

   capsule_mint_identity() grows a genesis mode (issuer_vm=NULL): no
   cert is built or written (Zuse isn't verified against a separate
   signer -- she's recognized by pubkey match against the marker) and
   two new optional out-params (out_pubkey/out_seed) let the caller
   install the cert immediately after a genesis mint.

   New capsule_zuse_boot_try_attach() (capsule_zuse_boot.c), called
   from sk_repl_idle() on every fresh USB attach (the only point in the
   boot lifecycle a thumbdrive can actually be detected -- attach
   polling doesn't exist yet at kernel_main.c's old one-shot mint point,
   which is why that whole block is gone): no marker + blank drive ->
   genesis-mint; marker present + matching drive -> read its own
   user_identity_seed_t, install the cert. Either way, re-runs
   ACL-ZUSE-BOOT (zuse.4th) so zuse_session activates exactly like it
   always has for a same-boot cert install -- ACL-PIN only blocks
   redefinition, not re-execution, so no new C-side auth logic needed.

2. ACL.4th activated (capsules/init.4th) -- inactive all session until
   now. Found and fixed a real bug this immediately surfaced: zuse.4th's
   ACL-ZUSE-BOOT tried `['] ACL-ZUSE-BOOT ACL-PIN` from inside its own
   still-compiling definition -- the word isn't findable yet at that
   point, so the whole definition silently failed to compile every
   previous boot this session (dormant, since ACL.4th never loaded).
   Fixed: pin after the definition closes, not from within it -- it
   only needs to happen once anyway, and pinning doesn't block the
   re-invocation genesis/attach needs.

3. The unauthenticated emergency-CLI ACL bypass is retired
   (repl.c): `emergency_console = is_hera ? (zuse_session ? 0 : 1) : 0`
   deleted from both sk_repl_step and sk_repl_run. Every word run from
   Hera's own bare prompt now goes through ordinary ACL enforcement;
   emergency_console is driven only by the genuine C-level fault
   handler again.

Added ZUSE-SESSION? (starforth_words.c), a read-only diagnostic
matching ZUSE-PUBKEY@'s own precedent, to verify the whole chain
directly rather than by inference.

Verified end-to-end live in QEMU: fresh boot, no thumbdrive ->
ZUSE-SESSION? reads 0. Attach a genuinely blank drive via QMP -> genesis
mint fires automatically (no typing) -> ZUSE-SESSION? reads -1 (true).
Hermes/Artemis both birth clean on all three architectures with ACL
now actually enforced for the first time all session -- no denials, no
UNKNOWN WORD beyond the deliberate POST self-test cases.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
This commit is contained in:
Robert Allan James
2026-08-28 16:10:40 -04:00
co-authored by Claude Sonnet 5
parent 7fc3e93358
commit cc9521d2cc
20 changed files with 55353 additions and 118 deletions
+9 -9
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-28T19:31:45Z -->
<!-- Generated by mkcapsule --manifest 2026-08-28T20:09:16Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
@@ -33,21 +33,21 @@
| `init-l8-temporal.4th` | 4830, 4831 | `0x51abd4c138246651` | yes |
| `init-l8-transition.4th` | 4840, 4841, 4842 | `0xbcc1a81976f0a4c9` | yes |
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | yes |
| `init.4th` | 2049, 2050, 2057 | `0xd1022c2e4331f244` | yes |
| `init.4th` | 2049, 2050, 2057 | `0x9ac6523d24b80cd0` | yes |
| `lib.4th` | 4050 | `0x4b216635c359ef73` | yes |
| `process.4th` | 4300, 4301 | `0x781afc1dbd0294f7` | yes |
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` | yes |
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | yes |
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | yes |
| `zuse.4th` | 4016, 4017, 4018 | `0x3b31872d02a43d83` | yes |
| `zuse.4th` | 4016, 4017, 4018 | `0x490ded9be257a90b` | yes |
## Block Map (sorted by LBN)
| LBN | Capsule | xxHash64 | Status |
|-----|---------|----------|--------|
| 2049 | `init.4th` | `0xd1022c2e4331f244` | ok |
| 2050 | `init.4th` | `0xd1022c2e4331f244` | ok |
| 2057 | `init.4th` | `0xd1022c2e4331f244` | ok |
| 2049 | `init.4th` | `0x9ac6523d24b80cd0` | ok |
| 2050 | `init.4th` | `0x9ac6523d24b80cd0` | ok |
| 2057 | `init.4th` | `0x9ac6523d24b80cd0` | ok |
| 2064 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2065 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2066 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
@@ -104,9 +104,9 @@
| 4006 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4007 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4015 | `ACL.4th` | `0xd781d22148ff171d` | ok |
| 4016 | `zuse.4th` | `0x3b31872d02a43d83` | ok |
| 4017 | `zuse.4th` | `0x3b31872d02a43d83` | ok |
| 4018 | `zuse.4th` | `0x3b31872d02a43d83` | ok |
| 4016 | `zuse.4th` | `0x490ded9be257a90b` | ok |
| 4017 | `zuse.4th` | `0x490ded9be257a90b` | ok |
| 4018 | `zuse.4th` | `0x490ded9be257a90b` | ok |
| 4050 | `lib.4th` | `0x4b216635c359ef73` | ok |
| 4055 | `common:msg.4th` | `0x850a0382344ea6c4` | ok |
| 4060 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
+1 -1
View File
@@ -13,7 +13,7 @@ Block 2049
: VM-TREE ( -- ) ." Hera[0]" CR ;
: VM-PARENT ( -- id ) 0 ;
: VM-CHILDREN ( -- ) ." (none)" CR ;
\ S" ACL.4th" EXEC
S" ACL.4th" EXEC
S" lib.4th" EXEC
S" fabric.4th" EXEC
S" font.4th" EXEC
+16 -11
View File
@@ -3,31 +3,36 @@ Block 4016
( Named for Konrad Zuse, pioneer of programmable computers. )
( Sole superuser; mints credentials; owns emergency REPL. )
( Loaded by ACL.4th; must not load before ACL.4th. )
( FUTURE: Replace with thumbdrive Ed25519 PKI. )
( Thumbdrive-resident Ed25519 PKI (2026-08-28) -- her seed )
( lives only on her own minted drive, never system-resident. )
( HUMAN-REVIEW: capsule hash = root of superuser trust. )
( Cert (seed+pubkey) lives in C-only VM fields, installed by )
( kernel_main.c's first-boot mint-or-load (NVRAM ZuseCert). )
( capsule_zuse_boot.c on genesis-mint or thumbdrive attach. )
( NOT a CONSTANT: ACL-PIN blocks redefinition, not a )
( >BODY-then-store, so a pinned CONSTANT isn't tamper-proof. )
( Read with ZUSE-PUBKEY@ / ZUSE-CERT-INSTALLED? -- both C )
( primitives, read-only; the seed has no FORTH access at all. )
Block 4017
( ACL-ZUSE-BOOT ( -- ) )
( Only authenticates if a real cert was installed this boot -- )
( refuses god-mode to a Zuse with no real identity behind her )
( (no runtime services, no entropy). Pins itself against )
( redefinition either way. ZUSE-AUTHENTICATE is C-only; no )
( FORTH word grants god-mode except through this sequence. )
( ACL-ZUSE-BOOT ( -- ) re-invokable: capsule_zuse_boot.c )
( calls it again once a thumbdrive attach installs a cert. )
( Only authenticates if a real cert is installed -- refuses )
( god-mode to a Zuse with no real identity behind her. )
( ZUSE-AUTHENTICATE is C-only; no FORTH word grants god-mode )
( except through this sequence. )
: ACL-ZUSE-BOOT ( -- )
ZUSE-CERT-INSTALLED? IF
ZUSE-AUTHENTICATE
LOG-INFO" zuse: activated"
ELSE
LOG-INFO" zuse: NOT activated -- no cert installed"
THEN
['] ACL-ZUSE-BOOT ACL-PIN ;
THEN ;
Block 4018
( Self-activation )
( Pin against redefinition -- once, after definition closes; )
( ['] from inside its own body can't find itself mid-compile, )
( found live 2026-08-28 activating ACL.4th for the first time. )
( Pinning doesn't block re-EXECUTION, only redefinition -- the )
( re-invoke above still works after this runs. Self-activates. )
['] ACL-ZUSE-BOOT ACL-PIN
ACL-ZUSE-BOOT
BIN
View File
Binary file not shown.
+18 -1
View File
@@ -58,16 +58,33 @@ typedef enum {
* @param dev Already-open block device for the target drive.
* @param issuer_vm The signing identity -- in practice always Hera's own
* VM (Zuse's cert lives there, vm.h's zuse_cert_seed).
* NULL means genesis mode (§F.21): no cert is built or
* written (cert_offset/cert_devblocks stay 0) and
* issuer_vm->zuse_cert_installed is never checked --
* used exactly once, to mint Zuse's own root identity,
* which by definition has no existing Zuse to sign it.
* @param full_name Required, NUL-terminated, fits user_identity_seed_t's
* full_name field (§F.20).
* @param username Required, NUL-terminated, fits its username field.
* @param email NULL or empty string = null (field stays empty).
* @param phone NULL or empty string = null (field stays empty).
* @param out_pubkey Optional (may be NULL): filled with the newly
* generated identity's own Ed25519 public key on
* success. Genesis mode's only caller needs this, to
* write it into the system-resident zuse_genesis_
* marker_t.
* @param out_seed Optional (may be NULL): filled with the newly
* generated identity's own Ed25519 seed on success.
* Genesis mode's only caller needs this too, to
* install the cert into Hera's own VM immediately
* (vm_zuse_cert_install()) -- the seed otherwise only
* ever lives on the minted thumbdrive.
* @return MINT_OK on success, an error code otherwise.
*/
MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
const char *full_name, const char *username,
const char *email, const char *phone);
const char *email, const char *phone,
uint8_t out_pubkey[32], uint8_t out_seed[32]);
#endif /* __STARKERNEL__ */
+63
View File
@@ -0,0 +1,63 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/**
* capsule_zuse_boot.h - Thumbdrive-resident Zuse genesis/attach
* (FABRIC-3.md §F.20/§F.21). Replaces kernel_main.c's old one-shot
* block-fence mint-or-load: Zuse's own identity now lives only on her
* own minted thumbdrive, never system-resident. Since USB attach
* detection only happens inside the idle loop (sk_repl_idle(), not at
* a fixed point in the boot sequence), this runs per-attach from there
* instead of once at boot.
*/
#ifndef STARKERNEL_CAPSULE_ZUSE_BOOT_H
#define STARKERNEL_CAPSULE_ZUSE_BOOT_H
#ifdef __STARKERNEL__
#include "starkernel/homeblocks_sig.h"
#include "vm.h"
struct blkio_dev;
/**
* capsule_zuse_boot_try_attach - Try to genesis-mint or authenticate
* Zuse from a just-attached drive.
*
* No-op if mama_vm->zuse_cert_installed is already 1 (Zuse already has a
* real identity this boot, from an earlier attach). Otherwise:
* - No genesis marker yet in the fence, drive reads HOMEBLOCKS_SIG_BLANK:
* mint Zuse's own identity onto it (capsule_mint_identity(), genesis
* mode), record the pubkey in the fence, install the cert, and
* re-run ACL-ZUSE-BOOT (zuse.4th) so zuse_session activates exactly
* like it always has for a same-boot-installed cert.
* - Genesis marker present, drive reads HOMEBLOCKS_SIG_OK: read its
* own user_identity_seed_t, compare pubkey against the marker: if it
* matches, install the cert and re-run ACL-ZUSE-BOOT the same way.
* If it doesn't match, this is some other identity's drive -- no-op
* here, that's a regular attach for BINDSTEP to handle later.
* - Anything else (foreign/corrupt media, no marker and non-blank
* drive): no-op.
*
* @param dev The just-attached, already-open block device.
* @param sig_rc homeblocks_sig_check()'s own result for this attach.
* @param sig The checked homeblocks_sig_t (only meaningful if
* sig_rc == HOMEBLOCKS_SIG_OK; may be NULL otherwise).
* @param mama_vm Hera's own VM (zuse_cert_seed/installed/session live
* here; also the target of the ACL-ZUSE-BOOT re-run).
*/
void capsule_zuse_boot_try_attach(struct blkio_dev *dev,
homeblocks_sig_result_t sig_rc,
const homeblocks_sig_t *sig,
VM *mama_vm);
#endif /* __STARKERNEL__ */
#endif /* STARKERNEL_CAPSULE_ZUSE_BOOT_H */
+14 -1
View File
@@ -1,5 +1,18 @@
/*
* zuse_cert_devblock.h -- on-disk record format for Zuse's cert, stored
* zuse_cert_devblock.h -- SUPERSEDED 2026-08-28 (FABRIC-3.md §F.20/§F.21).
* Zuse is now thumbdrive-resident, not system-resident: her seed lives
* only on her own minted thumbdrive, never written to the fence. The
* fence's devblock_from_top=0 slot this type used to occupy now holds
* zuse_genesis_marker_t (zuse_genesis_marker.h) instead -- pubkey only,
* no seed. This type is no longer written by any code path; kept in the
* repo as historical record of the format it replaced, per this
* project's own convention for superseded design (see e.g. the
* TRIPOD.md/HERMES.md/ARTEMIS.md/CONSOLE.md superseded-header pattern).
* Do not resurrect writes to this format.
*
* Original doc, kept for context:
*
* on-disk record format for Zuse's cert, stored
* in devblock_from_top=0 of the top-of-device system-metadata fence
* (block_subsystem.h's blk_meta_zone_read()/write(), Phase 8, FABRIC-3.md
* §C). Raw, unpacked 4 KiB devblock -- same convention as the volume
+46
View File
@@ -0,0 +1,46 @@
/*
* zuse_genesis_marker.h -- on-disk record format for the system-resident
* "a root Zuse identity already exists" marker (FABRIC-3.md §F.21),
* stored in devblock_from_top=0 of the top-of-device system-metadata
* fence (block_subsystem.h's blk_meta_zone_read()/write(), same location
* zuse_cert_devblock_t used to occupy).
*
* Supersedes zuse_cert_devblock_t (zuse_cert_devblock.h, kept in the repo
* as historical record, no longer written): that type stored Zuse's own
* *seed* system-resident. Under the thumbdrive-resident Zuse design
* (§F.20/§F.21), the seed lives only on Zuse's own minted thumbdrive --
* this record deliberately holds only her *public* key, enough to (a)
* know genesis has already happened, so a second blank thumbdrive
* attached on some later boot never mints a second competing root, and
* (b) recognize which attached identity is genuinely hers. Losing this
* fence record is not a security problem (it's not a secret); losing the
* thumbdrive itself is what actually loses the identity.
*/
#ifndef STARKERNEL_ZUSE_GENESIS_MARKER_H
#define STARKERNEL_ZUSE_GENESIS_MARKER_H
#include <stdint.h>
#define ZUSE_GENESIS_MARKER_MAGIC \
((uint32_t)'Z' | ((uint32_t)'G' << 8) | ((uint32_t)'E' << 16) | ((uint32_t)'N' << 24))
#define ZUSE_GENESIS_MARKER_VERSION 1u
typedef struct {
uint32_t magic; /* ZUSE_GENESIS_MARKER_MAGIC; anything else means
* "genesis has not happened yet" (blank/foreign
* bytes), not a format-corruption error. */
uint32_t version; /* ZUSE_GENESIS_MARKER_VERSION */
uint8_t zuse_pubkey[32]; /* Ed25519 public key of the root Zuse
* identity minted at genesis. No seed here --
* that lives only on Zuse's own thumbdrive. */
uint64_t crc; /* CRC-64/ISO (block_subsystem.h's compute_crc64())
* over every byte of this struct up to (not
* including) this field. */
uint8_t _pad[4096 - (4 + 4 + 32 + 8)];
} zuse_genesis_marker_t;
_Static_assert(sizeof(zuse_genesis_marker_t) == 4096,
"zuse_genesis_marker_t must be exactly one 4 KiB devblock");
#endif /* STARKERNEL_ZUSE_GENESIS_MARKER_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+33 -17
View File
@@ -58,8 +58,9 @@ static int write_devblock(struct blkio_dev *dev, uint32_t devblock,
MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
const char *full_name, const char *username,
const char *email, const char *phone) {
if (!dev || !issuer_vm || !full_name || !username) return MINT_ERR_WRITE_FAIL;
const char *email, const char *phone,
uint8_t out_pubkey[32], uint8_t out_seed[32]) {
if (!dev || !full_name || !username) return MINT_ERR_WRITE_FAIL;
/* full_name/username required and must fit; email/phone may be NULL
* (treated as empty/null) but must fit if given. */
@@ -83,13 +84,17 @@ MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
}
}
if (!issuer_vm->zuse_cert_installed) return MINT_ERR_NO_ZUSE_CERT;
if (!virtio_rng_ready()) return MINT_ERR_NO_ENTROPY;
/* issuer_vm==NULL is genesis mode (§F.21) -- no existing Zuse to
* require a cert from. */
if (issuer_vm && !issuer_vm->zuse_cert_installed) return MINT_ERR_NO_ZUSE_CERT;
if (!virtio_rng_ready()) return MINT_ERR_NO_ENTROPY;
/* Fresh identity keypair. */
uint8_t seed[32], pubkey[32];
if (virtio_rng_get_bytes(seed, sizeof(seed)) != 0) return MINT_ERR_NO_ENTROPY;
ed25519_keygen(seed, pubkey);
if (out_pubkey) memcpy(out_pubkey, pubkey, 32);
if (out_seed) memcpy(out_seed, seed, 32);
/* Fresh drive_uuid -- an independent random draw, not derived from
* the identity seed (§F.8 decision 3: "which physical drive," not
@@ -98,18 +103,29 @@ MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
if (virtio_rng_get_bytes(drive_uuid, sizeof(drive_uuid)) != 0)
return MINT_ERR_NO_ENTROPY;
/* Cert: SubjectPublicKeyInfo = new pubkey, serialNumber = drive_uuid,
* signed by Zuse's own on-device seed (§F.8 decision 4). */
uint8_t cert[512];
size_t cert_len = x509_build_user_cert(cert, sizeof(cert), pubkey,
drive_uuid, issuer_vm->zuse_cert_seed);
if (cert_len == 0 || cert_len > 4096) return MINT_ERR_CERT_BUILD;
uint32_t cert_devblock = 0;
uint32_t cert_devblocks = 0;
if (issuer_vm) {
/* Cert: SubjectPublicKeyInfo = new pubkey, serialNumber =
* drive_uuid, signed by Zuse's own on-device seed (§F.8
* decision 4). Genesis mode (issuer_vm==NULL) skips this
* entirely -- Zuse's own identity isn't verified against a
* separate signer, she's recognized directly by pubkey match
* against zuse_genesis_marker_t (§F.21). */
uint8_t cert[512];
size_t cert_len = x509_build_user_cert(cert, sizeof(cert), pubkey,
drive_uuid, issuer_vm->zuse_cert_seed);
if (cert_len == 0 || cert_len > 4096) return MINT_ERR_CERT_BUILD;
uint8_t cert_block[4096];
memset(cert_block, 0, sizeof(cert_block));
memcpy(cert_block, cert, cert_len);
if (write_devblock(dev, MINT_CERT_DEVBLOCK, cert_block) != 0)
return MINT_ERR_WRITE_FAIL;
uint8_t cert_block[4096];
memset(cert_block, 0, sizeof(cert_block));
memcpy(cert_block, cert, cert_len);
if (write_devblock(dev, MINT_CERT_DEVBLOCK, cert_block) != 0)
return MINT_ERR_WRITE_FAIL;
cert_devblock = MINT_CERT_DEVBLOCK;
cert_devblocks = MINT_CERT_DEVBLOCKS;
}
/* user_identity_seed_t record. */
user_identity_seed_t idrec;
@@ -145,8 +161,8 @@ MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
* anywhere in this codebase yet,
* §F.8's own open item */
sig.metadata_devblocks = MINT_METADATA_DEVBLOCKS;
sig.cert_offset = MINT_CERT_DEVBLOCK;
sig.cert_devblocks = MINT_CERT_DEVBLOCKS;
sig.cert_offset = cert_devblock;
sig.cert_devblocks = cert_devblocks;
sig.identity_src_offset = MINT_IDENTITY_SRC_OFFSET;
sig.identity_src_devblocks = MINT_IDENTITY_SRC_DEVBLOCKS;
sig.hdr_crc = homeblocks_sig_compute_crc(&sig);
+110
View File
@@ -0,0 +1,110 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
#ifndef __STARKERNEL__
#error "capsule_zuse_boot.c is kernel-only"
#endif
#include "starkernel/capsule_zuse_boot.h"
#include "starkernel/capsule_mint.h"
#include "starkernel/zuse_genesis_marker.h"
#include "starkernel/user_identity_seed.h"
#include "starkernel/console.h"
#include "block_subsystem.h" /* compute_crc64(), blk_meta_zone_read/write */
#include "blkio.h"
#include <string.h>
#include <stddef.h>
/* Read exactly one devblock (4096 bytes) at devblock offset `devblock`,
* as 4 consecutive 1KiB forth-block reads -- mirrors capsule_runcap.c's
* and homeblocks_sig_check()'s own read convention. */
static int read_devblock(struct blkio_dev *dev, uint32_t devblock, uint8_t *buf4096) {
uint32_t base = devblock * 4u;
for (uint32_t i = 0; i < 4u; i++) {
if (blkio_read((blkio_dev_t *)dev, base + i,
buf4096 + (size_t)i * BLKIO_FORTH_BLOCK_SIZE) != BLKIO_OK) {
return -1;
}
}
return 0;
}
static int genesis_marker_read(zuse_genesis_marker_t *out) {
if (blk_meta_zone_read(0, (uint8_t *)out) != 0) return -1;
if (out->magic != ZUSE_GENESIS_MARKER_MAGIC) return -1;
if (out->version != ZUSE_GENESIS_MARKER_VERSION) return -1;
uint64_t want_crc = compute_crc64((const uint8_t *)out, offsetof(zuse_genesis_marker_t, crc));
if (want_crc != out->crc) return -1;
return 0;
}
static void install_and_activate(VM *mama_vm, const uint8_t seed[32], const uint8_t pubkey[32]) {
if (vm_zuse_cert_install(mama_vm, seed, pubkey) != 0) return;
/* zuse.4th's ACL-ZUSE-BOOT self-activated once already at Mama's own
* birth, when no cert was installed yet (the thumbdrive wasn't
* attached at that early, one-shot point) -- ACL-PIN only blocks
* *redefinition*, not re-execution, so re-running the same policy
* word here is the correct, already-designed way to activate
* zuse_session now that a cert genuinely exists. No new C-side auth
* logic; policy stays in ACL.4th/zuse.4th per this project's own
* convention. */
vm_interpret(mama_vm, "ACL-ZUSE-BOOT");
}
void capsule_zuse_boot_try_attach(struct blkio_dev *dev,
homeblocks_sig_result_t sig_rc,
const homeblocks_sig_t *sig,
VM *mama_vm) {
if (!dev || !mama_vm) return;
if (mama_vm->zuse_cert_installed) return; /* already have Zuse this boot */
zuse_genesis_marker_t marker;
int have_marker = (genesis_marker_read(&marker) == 0);
if (!have_marker) {
if (sig_rc != HOMEBLOCKS_SIG_BLANK) return; /* not eligible for genesis */
uint8_t seed[32], pubkey[32];
MintResult r = capsule_mint_identity(dev, (VM *)0, "Zuse", "zuse",
(const char *)0, (const char *)0,
pubkey, seed);
if (r != MINT_OK) {
console_println("Zuse: genesis mint failed");
return;
}
zuse_genesis_marker_t wm;
memset(&wm, 0, sizeof(wm));
wm.magic = ZUSE_GENESIS_MARKER_MAGIC;
wm.version = ZUSE_GENESIS_MARKER_VERSION;
memcpy(wm.zuse_pubkey, pubkey, 32);
wm.crc = compute_crc64((const uint8_t *)&wm, offsetof(zuse_genesis_marker_t, crc));
if (blk_meta_zone_write(0, (const uint8_t *)&wm) != 0) {
console_println("Zuse: genesis minted but fence marker write FAILED (not persistent)");
} else {
console_println("Zuse: genesis minted onto attached thumbdrive");
}
install_and_activate(mama_vm, seed, pubkey);
return;
}
/* Marker present: genesis already happened, on some thumbdrive.
* Only act if THIS attach is that drive. */
if (sig_rc != HOMEBLOCKS_SIG_OK || !sig) return;
if (sig->identity_src_offset == 0 || sig->identity_src_devblocks < 1) return;
user_identity_seed_t idrec;
if (read_devblock(dev, sig->identity_src_offset, (uint8_t *)&idrec) != 0) return;
if (idrec.magic != USER_IDENTITY_SEED_MAGIC) return;
if (memcmp(idrec.pubkey, marker.zuse_pubkey, 32) != 0) return; /* not Zuse's drive */
console_println("Zuse: identity confirmed from attached thumbdrive");
install_and_activate(mama_vm, idrec.seed, idrec.pubkey);
}
+2 -1
View File
@@ -831,7 +831,8 @@ static void mama_word_mint(VM *vm)
return;
}
MintResult r = capsule_mint_identity(dev, vm, full_name, username, email, phone);
MintResult r = capsule_mint_identity(dev, vm, full_name, username, email, phone,
(uint8_t *)0, (uint8_t *)0);
switch (r) {
case MINT_OK:
console_println("MINT: identity minted");
+15 -58
View File
@@ -66,8 +66,6 @@ EFI_RUNTIME_SERVICES *g_sk_runtime_services = NULL;
#include "starkernel/pci.h"
#include "starkernel/virtio_blk.h"
#include "starkernel/virtio_rng.h"
#include "starkernel/ed25519.h"
#include "starkernel/zuse_cert_devblock.h"
#include "starkernel/virtio_input.h"
#include "starkernel/xhci_driver.h"
#include "block_subsystem.h"
@@ -607,62 +605,21 @@ static void kernel_main_deep(BootInfo *boot_info) {
}
}
/* Phase 8: Zuse first-boot mint-then-load, via the top-of-device
* system-metadata fence (block_subsystem.h's blk_meta_zone_read()/
* write(), FABRIC-3.md Phase 8 §C) -- NOT UEFI NVRAM. An earlier
* NVRAM-based attempt page-faulted inside OVMF's variable service on
* a real flash write (root-caused, documented, reverted); raw block
* I/O against Artemis's already-proven virtio-blk device has none of
* that risk and needs no runtime-services timing care at all. Runs
* here, before capsule_birth_mama() below, so ACL.4th/zuse.4th's
* self-activating ACL-ZUSE-BOOT sees a populated cert on its one,
* ordinary first pass -- no re-invocation workaround needed this
* time. "Mint once, ever": a valid ZUSE_CERT_DEVBLOCK_MAGIC record
* in the fence means a prior boot already minted -- load it back
* rather than generating a new one. Graceful no-op if there's no
* disk-backed device or no entropy source; Zuse simply won't
* authenticate this boot. */
{
zuse_cert_devblock_t rec;
int found = 0;
if (blk_meta_zone_read(0, (uint8_t *)&rec) == 0 &&
rec.magic == ZUSE_CERT_DEVBLOCK_MAGIC &&
rec.version == ZUSE_CERT_DEVBLOCK_VERSION) {
uint64_t want_crc = compute_crc64((const uint8_t *)&rec, offsetof(zuse_cert_devblock_t, crc));
if (want_crc == rec.crc) found = 1;
}
VM *zuse_vm = (VM *)mama_vm;
if (found) {
if (vm_zuse_cert_install(zuse_vm, rec.seed, rec.pubkey) == 0) {
console_println("Zuse: cert loaded from block fence");
}
} else if (virtio_rng_ready()) {
uint8_t seed[32];
if (virtio_rng_get_bytes(seed, sizeof(seed)) == 0) {
uint8_t pubkey[32];
ed25519_keygen(seed, pubkey);
if (vm_zuse_cert_install(zuse_vm, seed, pubkey) == 0) {
zuse_cert_devblock_t wrec;
memset(&wrec, 0, sizeof(wrec));
wrec.magic = ZUSE_CERT_DEVBLOCK_MAGIC;
wrec.version = ZUSE_CERT_DEVBLOCK_VERSION;
memcpy(wrec.seed, seed, 32);
memcpy(wrec.pubkey, pubkey, 32);
wrec.crc = compute_crc64((const uint8_t *)&wrec, offsetof(zuse_cert_devblock_t, crc));
if (blk_meta_zone_write(0, (const uint8_t *)&wrec) == 0) {
console_println("Zuse: minted, fuse blown");
} else {
console_println("Zuse: minted but fence write FAILED (not persistent)");
}
}
} else {
console_println("Zuse: entropy read failed, not minted this boot");
}
} else {
console_println("Zuse: no cert and no entropy source, not minted this boot");
}
}
/* Zuse identity: SUPERSEDED 2026-08-28 (FABRIC-3.md §F.20/§F.21).
* The one-shot block-fence mint-or-load that used to run here is
* gone -- Zuse is thumbdrive-resident now (her seed never touches
* system storage), and a thumbdrive can't be detected this early in
* boot anyway (USB attach polling only exists inside the REPL's own
* idle loop, which hasn't started yet at this point). The real
* genesis-mint/attach-authenticate logic now lives in
* capsule_zuse_boot_try_attach() (capsule_zuse_boot.c), called from
* sk_repl_idle() on every fresh USB attach; ACL.4th/zuse.4th's
* ACL-ZUSE-BOOT self-activation at Mama's own birth below will see
* no cert installed yet on a fresh boot (expected -- it gets
* re-invoked once a matching/genesis-eligible drive actually
* attaches). The system-resident fence slot this block used to write
* (zuse_cert_devblock_t, devblock_from_top=0) now holds
* zuse_genesis_marker_t instead -- pubkey only, never a seed. */
/* item 4.3.5c: virtio-keyboard-pci, riscv64 only today. Unconditional
* call site, same as virtio_blk_find_artemis() above -- the function
+32 -19
View File
@@ -38,6 +38,7 @@
#include "starkernel/blkio_usb.h"
#include "starkernel/homeblocks_sig.h"
#include "starkernel/capsule_birth.h"
#include "starkernel/capsule_zuse_boot.h"
#include "starkernel/capsule_run.h"
#include "starkernel/vm/bootstrap/sk_vm_bootstrap.h"
#include "block_subsystem.h"
@@ -183,6 +184,16 @@ static void sk_repl_idle(VM *active_vm)
console_println("xhci: USB drive signature check failed to read the device -- read-only general use only");
break;
}
/* FABRIC-3.md §F.20/§F.21: Zuse is thumbdrive-resident now,
* not system-resident -- this is the only point in the boot
* lifecycle a just-attached drive's sig result is known, so
* genesis-mint/attach-authenticate has to happen from here,
* not as a one-shot kernel_main.c step (a thumbdrive can't
* be detected before the REPL's own idle polling exists to
* detect it). No-ops immediately if Zuse already has a real
* identity this boot. */
capsule_zuse_boot_try_attach(&usb_blk_dev, sig_rc, &sig, (VM *)sk_get_mama_vm());
}
if (rc == 0 && blk_subsys_attach_device(&usb_blk_dev) == BLK_OK) {
xdev->bot_msc_attached = 1;
@@ -447,12 +458,17 @@ static int sk_readline(char *buf, int size, VM *active_vm)
}
/*===========================================================================
* sk_repl - Emergency FORTH REPL
* sk_repl - FORTH REPL
*
* FABRIC-3.md §F.20/§F.21 (2026-08-28): the unauthenticated emergency-CLI
* ACL bypass this REPL used to grant itself on Hera's own bare prompt is
* retired -- every word runs under ordinary ACL enforcement here now,
* console identity included. emergency_console still exists as a field
* (vm.h) and is still set, briefly, by the genuine C-level VM fault
* handler (EMERGENCY_CONSOLE_ENABLED build flag) for crash recovery --
* that's a distinct, narrower mechanism this REPL no longer touches.
*
* Mirrors vm_repl() from src/repl.c:
* - Sets vm->emergency_console = 1 for the duration (this IS the emergency
* console; bypasses ACL so zuse authentication is not required to recover)
* - Prints "zuse)ok> " when zuse_session=1, else "ok> "
* - Reads a line via sk_readline (non-blocking, heartbeat-serviced)
* - Calls vm_interpret
* - Prints " ok" or " ERROR"
@@ -487,12 +503,13 @@ int sk_repl_step(VM *vm)
{
/* Unified prompt (FABRIC.md 4.4a): console_putc()'s existing per-line
* "[VMName] " prefix (console.c, g_active_vm_name) already supplies the
* bracket -- print only "ok> " here, don't build a second one. The
* emergency_console bypass is a security decision, not a display one --
* it still applies only to Hera's bare prompt, per FABRIC.md 4.4. */
const char *vn = console_get_vm_name();
int is_hera = (!vn || (vn[0]=='H' && vn[1]=='e' && vn[2]=='r' && vn[3]=='a' && vn[4]=='\0'));
vm->emergency_console = is_hera ? (vm->zuse_session ? 0 : 1) : 0;
* bracket -- print only "ok> " here, don't build a second one.
* emergency_console is no longer set from here (FABRIC-3.md §F.20/
* §F.21: the emergency-CLI ACL bypass is retired) -- it's driven
* only by the genuine C-level fault handler now (vm.c's own
* emergency-fault-recovery use, EMERGENCY_CONSOLE_ENABLED). Every
* word run from this REPL, Hera's bare prompt included, goes
* through ordinary ACL enforcement. */
console_puts(SK_PROMPT_TEXT);
}
@@ -533,15 +550,11 @@ void sk_repl_run(VM *vm)
/* Unified prompt (FABRIC.md 4.4a): console_putc()'s existing per-line
* "[VMName] " prefix (console.c, g_active_vm_name) already supplies the
* bracket -- print only "ok> " here, don't build a second one. The
* emergency_console bypass is a security decision, not a display one --
* it still applies only to Hera's bare prompt, per FABRIC.md 4.4. */
{
const char *vn = console_get_vm_name();
int is_hera = (!vn || (vn[0]=='H' && vn[1]=='e' && vn[2]=='r' && vn[3]=='a' && vn[4]=='\0'));
active->emergency_console = is_hera ? (active->zuse_session ? 0 : 1) : 0;
console_puts(SK_PROMPT_TEXT);
}
* bracket -- print only "ok> " here, don't build a second one.
* emergency_console is no longer set from here (FABRIC-3.md §F.20/
* §F.21: the emergency-CLI ACL bypass is retired) -- see sk_repl_
* step()'s matching comment above. */
console_puts(SK_PROMPT_TEXT);
sk_readline(input, sizeof(input), active);
+11
View File
@@ -793,6 +793,15 @@ static void starforth_word_zuse_authenticate(VM *vm)
vm->zuse_session = 1;
}
/* ZUSE-SESSION? ( -- flag ) Read-only diagnostic (FABRIC-3.md §F.21,
* added 2026-08-28): confirms whether ZUSE-AUTHENTICATE has actually run
* this boot. No corresponding write access -- matches ZUSE-PUBKEY@'s own
* read-only-window convention. */
static void starforth_word_zuse_session(VM *vm)
{
vm_push(vm, vm->zuse_session ? -1 : 0);
}
/* ZUSE-PUBKEY@ ( i -- u ) Read-only: fetch 8-byte little-endian chunk i
* (0..3) of Zuse's 32-byte Ed25519 public key as one cell. Out-of-range i
* pushes 0 and sets vm->error rather than faulting. No FORTH word can
@@ -860,6 +869,7 @@ void register_starforth_words(VM* vm)
register_word(vm, "RANDOM", starforth_word_random);
register_word(vm, "WAIT", starforth_word_wait);
register_word(vm, "ZUSE-AUTHENTICATE", starforth_word_zuse_authenticate);
register_word(vm, "ZUSE-SESSION?", starforth_word_zuse_session);
register_word(vm, "ZUSE-PUBKEY@", starforth_word_zuse_pubkey_fetch);
register_word(vm, "ZUSE-CERT-INSTALLED?", starforth_word_zuse_cert_installed_query);
register_word(vm, "HEARTBEAT-TICKS@", starforth_word_heartbeat_ticks);
@@ -880,6 +890,7 @@ void register_starforth_words(VM* vm)
register_word(vm, "RANDOM", starforth_word_random);
register_word(vm, "WAIT", starforth_word_wait);
register_word(vm, "ZUSE-AUTHENTICATE", starforth_word_zuse_authenticate);
register_word(vm, "ZUSE-SESSION?", starforth_word_zuse_session);
register_word(vm, "ZUSE-PUBKEY@", starforth_word_zuse_pubkey_fetch);
register_word(vm, "ZUSE-CERT-INSTALLED?", starforth_word_zuse_cert_installed_query);
register_word(vm, "HEARTBEAT-TICKS@", starforth_word_heartbeat_ticks);