Punch list item 1: scratch-device MINT-SCRATCH, verified live on all 3 arches

Implements FABRIC-3.md §XXXII.2's scratch-thumbdrive mint mechanism:
capsule_mint_identity_scratch() (capsule_mint.c/.h) builds a throwaway
RAM-backed blkio_dev via blkio_ram.c's backend and runs
capsule_mint_identity() against it completely unmodified -- same live
Zuse-signing operation, same rng_get_bytes() draw for drive_uuid a real
thumbdrive gets. Reads back only drive_uuid + the cert devblock; the
seed devblock is written into the scratch buffer internally but never
read out (no seed is ever baked into a capsule, per the ratified
no-seed decision).

New FORTH word MINT-SCRATCH (mama_forth_words.c), same stack signature
as MINT, mints into the scratch device instead of any attached drive
and never touches sk_repl_get_attached_blk_dev() or console-pairing
state. Prints the drive_uuid as hex so a live boot log itself proves
each call drew fresh entropy.

Build correction found along the way: blkio_ram.c was excluded from
the kernel build (Makefile.starkernel VM_EXCLUDE) alongside
blkio_factory.c/blkio_file.c. blkio_factory_open() unconditionally
references blkio_file.c's real fopen()/fread() file I/O, which has no
freestanding-kernel equivalent, so the factory function couldn't be
used as-is. blkio_ram.c itself is pure memcpy over a caller buffer --
pulled it alone into the kernel build and wired it directly in
capsule_mint.c, the same way blkio_factory.c's own extern declarations
do internally.

Verified live on amd64: two MINT-SCRATCH calls produced two genuinely
different drive_uuids (b533246d.../ae11b2b2...), confirming fresh
entropy per call rather than stale reuse; VM stayed healthy afterward
(5 6 + . -> 11). Clean 3-arch qemu boot (amd64/aarch64/riscv64),
logs and DoE CSVs committed per standing convention.

Remaining punch-list items (hand-transcription into a .4th block, the
unattended-birth call site, the ACL cap bit) not started.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
This commit is contained in:
Robert Allan James
2026-09-16 04:42:15 -04:00
co-authored by Claude Sonnet 5
parent 60bcdc09a7
commit 63864c4b01
12 changed files with 27358 additions and 2 deletions
+84
View File
@@ -1149,6 +1149,88 @@ static void mama_word_mint(VM *vm)
vm_push(vm, 0);
}
/**
* @brief MINT-SCRATCH ( fname-c fname-u uname-c uname-u email-c email-u
* phone-c phone-u restrict? -- ok? )
* Same arguments and dictionary meaning as MINT, but mints into a
* throwaway RAM-backed scratch device instead of the currently attached
* drive -- never touches sk_repl_get_attached_blk_dev() or any real
* block device at all (FABRIC-3.md §XXXII.2, unattended-identity punch
* list item 1, 2026-09-16). The resulting drive_uuid + cert are kept in
* g_mint_scratch_uuid/g_mint_scratch_cert for a follow-up word to read
* out for hand-transcription into a capsule -- that packaging step is
* its own punch-list item, not built here. The uuid is printed as hex
* immediately so a live boot log itself proves each mint drew fresh
* entropy rather than reusing stale state.
*/
static uint8_t g_mint_scratch_uuid[16];
static uint8_t g_mint_scratch_cert[4096];
static void mama_word_mint_scratch(VM *vm)
{
char phone[USER_IDENTITY_PHONE_MAX];
char email[USER_IDENTITY_EMAIL_MAX];
char username[USER_IDENTITY_USERNAME_MAX];
char full_name[USER_IDENTITY_FULL_NAME_MAX];
if (vm->dsp < 0) {
console_println("MINT-SCRATCH: expects a restrict? flag on top of the stack");
vm->error = 1;
return;
}
cell_t restrict_flag = vm_pop(vm);
if (mint_pop_string(vm, phone, sizeof(phone), "phone") != 0) return;
if (mint_pop_string(vm, email, sizeof(email), "email") != 0) return;
if (mint_pop_string(vm, username, sizeof(username), "username") != 0) return;
if (mint_pop_string(vm, full_name, sizeof(full_name), "full_name") != 0) return;
MintPersonality personality = restrict_flag
? MINT_PERSONALITY_STD79_LOCKDOWN
: MINT_PERSONALITY_DEFAULT;
MintResult r = capsule_mint_identity_scratch(vm, full_name, username, email, phone,
personality,
g_mint_scratch_uuid, g_mint_scratch_cert);
switch (r) {
case MINT_OK: {
static const char hex[] = "0123456789abcdef";
char uuid_hex[33];
int i;
for (i = 0; i < 16; i++) {
uuid_hex[i * 2] = hex[(g_mint_scratch_uuid[i] >> 4) & 0xF];
uuid_hex[i * 2 + 1] = hex[g_mint_scratch_uuid[i] & 0xF];
}
uuid_hex[32] = '\0';
console_puts("MINT-SCRATCH: identity minted (scratch device, no real drive touched) -- drive_uuid ");
console_println(uuid_hex);
vm_push(vm, 1);
return;
}
case MINT_ERR_ALREADY_MINTED:
console_println("MINT-SCRATCH: refused -- scratch device already reads as minted");
break;
case MINT_ERR_NO_ZUSE_CERT:
console_println("MINT-SCRATCH: refused -- Zuse has no installed cert to sign with");
break;
case MINT_ERR_NO_ENTROPY:
console_println("MINT-SCRATCH: refused -- no entropy source");
break;
case MINT_ERR_CERT_BUILD:
console_println("MINT-SCRATCH: FAILED -- cert construction error");
break;
case MINT_ERR_WRITE_FAIL:
console_println("MINT-SCRATCH: FAILED -- scratch device write error");
break;
case MINT_ERR_INVALID_PROFILE:
console_println("MINT-SCRATCH: refused -- full_name/username missing or a field too long");
break;
case MINT_ERR_VERIFY_FAILED:
console_println("MINT-SCRATCH: FAILED -- minted but post-write verification failed "
"(see log for which check)");
break;
}
vm_push(vm, 0);
}
/**
* @brief ZUSE-ELIGIBILITY-ADD ( c-addr -- ok? )
* Add the 32-byte Ed25519 public key at c-addr to Zuse's elevation
@@ -1802,6 +1884,7 @@ void register_mama_forth_words(VM *vm)
register_word(vm, "CAPSULE-BIRTH", mama_word_capsule_birth);
register_word(vm, "CAPSULE-RUN", mama_word_capsule_run);
register_word(vm, "MINT", mama_word_mint);
register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch);
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
register_word(vm, "ZUSE-ELIGIBLE?", mama_word_zuse_eligible_query);
register_word(vm, "NAME>XT", mama_word_name_to_xt);
@@ -1861,6 +1944,7 @@ void register_mama_forth_words(VM *vm)
register_word(vm, "CAPSULE-BIRTH", mama_word_capsule_birth);
register_word(vm, "CAPSULE-RUN", mama_word_capsule_run);
register_word(vm, "MINT", mama_word_mint);
register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch);
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
register_word(vm, "ZUSE-ELIGIBLE?", mama_word_zuse_eligible_query);
register_word(vm, "NAME>XT", mama_word_name_to_xt);