FABRIC-3.md §I.3/§I.8: WIREBIND EJECT/detach + EXPIRE re-scoped as logout
Closes §I.3 (Milestone 5 remainder): WIREBIND now tracks which VM is attached via the home-blocks USB path, and a new EJECT word plus the existing hot-unplug signal both flush/reset-console/kill through it (FABRIC-3.md §F.10). Closes §I.8 (EXPIRE/ACL), re-scoped: the original "admit the zuse session as a Stadium patron and reap on TTL" plan was invalidated a second time -- Zuse authenticates directly onto Hera, who is patron zero and permanently pinned, so there is no patron for a reap sweep to ever find. Built instead as a detach-triggered logout (capsule_zuse_boot_logout()), the same trigger EJECT/hot-unplug use for regular WIREBIND users, so neither identity is a special case. Required a companion fix: install_and_activate() used to skip re-running ACL-ZUSE-BOOT whenever the cert was already installed, which made a logout permanent for the rest of the boot; the outer re-attach gate now checks zuse_session (clears on logout) instead of zuse_cert_installed (a deliberate permanent one-way ratchet, left untouched). Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the foreground) after both steps; logs and DoE CSVs from this session's verification runs included per this repo's own audit-artifact convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
34203613bb
commit
60d9c2520e
@@ -18,12 +18,23 @@
|
||||
#include "starkernel/vm_identity.h"
|
||||
#include "starkernel/user_identity_seed.h"
|
||||
#include "starkernel/console.h"
|
||||
#include "starkernel/repl.h" /* sk_repl_get/set_active_vm() -- FABRIC-3.md §F.10 EJECT */
|
||||
#include "blkio.h"
|
||||
#include "block_subsystem.h" /* compute_crc64() */
|
||||
#include "word_source/include/block_words.h" /* blk_vm_flush_all() -- §F.10 EJECT */
|
||||
#include "freestanding/stdio.h"
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* FABRIC-3.md §F.10: "the VM currently attached via the home-blocks USB
|
||||
* path" -- WIREBIND's own to track, set at successful birth in
|
||||
* capsule_wirebind_try_attach(), read and cleared by both
|
||||
* capsule_wirebind_eject() and capsule_wirebind_unclean_detach(). Single-
|
||||
* USB-device constraint (§F.8) means there is never more than one
|
||||
* candidate, so one tracked id is enough -- no set/list. */
|
||||
static VMUuid g_wirebind_attached_vm_id;
|
||||
static int g_wirebind_attached_valid = 0;
|
||||
|
||||
/* WIREBIND_CERT_MAX_DEVBLOCKS: a sane upper bound on how much cert
|
||||
* content this reads, independent of whatever sig->cert_devblocks
|
||||
* claims -- MINT (capsule_mint.c) only ever writes 1 devblock's worth
|
||||
@@ -162,6 +173,13 @@ void capsule_wirebind_try_attach(struct blkio_dev *dev,
|
||||
((VM *)user_ctx)->identity = identity;
|
||||
}
|
||||
|
||||
/* FABRIC-3.md §F.10: this is the successful-birth point EJECT/UNCLEAN
|
||||
* need a tracked VMUuid from -- the user VM, not the console VM
|
||||
* (blk_vm_flush_all()/capsule_vm_kill() below both act on the VM that
|
||||
* actually owns block-subsystem state). */
|
||||
g_wirebind_attached_vm_id = user_id;
|
||||
g_wirebind_attached_valid = 1;
|
||||
|
||||
/* Register the pairing in the console's own routing table, index 3
|
||||
* -- the fixed convention sk_repl_dispatch_line() (repl.c) uses. */
|
||||
{
|
||||
@@ -176,3 +194,60 @@ void capsule_wirebind_try_attach(struct blkio_dev *dev,
|
||||
console_puts(username);
|
||||
console_println(" attached and ready -- USE it to begin");
|
||||
}
|
||||
|
||||
/* Shared by capsule_wirebind_eject() and capsule_wirebind_unclean_detach():
|
||||
* resolve the tracked attached-VM id to a live entry, or clear the tracked
|
||||
* state and report "nothing attached" if it's already gone (idempotent --
|
||||
* matches capsule_vm_kill()'s own convention). */
|
||||
static int wirebind_resolve_attached(VMRegistryEntry *out) {
|
||||
if (!g_wirebind_attached_valid) return -1;
|
||||
if (capsule_vm_registry_get(g_wirebind_attached_vm_id, out) != 0 ||
|
||||
out->state != VM_STATE_LIVE) {
|
||||
g_wirebind_attached_valid = 0;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int capsule_wirebind_eject(void) {
|
||||
VMRegistryEntry entry;
|
||||
if (wirebind_resolve_attached(&entry) != 0) {
|
||||
console_println("EJECT: nothing attached via WIREBIND");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Drive is still physically present -- flush before it's safe to
|
||||
* remove (§F.10 decision 1). */
|
||||
blk_vm_flush_all((VM *)entry.vm_ptr);
|
||||
|
||||
if (sk_repl_get_active_vm() == (VM *)entry.vm_ptr) {
|
||||
sk_repl_set_active_vm((VM *)0);
|
||||
}
|
||||
|
||||
capsule_vm_kill(entry.name);
|
||||
g_wirebind_attached_valid = 0;
|
||||
|
||||
console_puts("EJECT: ");
|
||||
console_puts(entry.name);
|
||||
console_println(" flushed and detached");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void capsule_wirebind_unclean_detach(void) {
|
||||
VMRegistryEntry entry;
|
||||
if (wirebind_resolve_attached(&entry) != 0) return;
|
||||
|
||||
/* Device is already gone (§F.10 decision 2) -- no flush attempted;
|
||||
* data since the last flush is lost, matching real-OS unclean-removal
|
||||
* semantics. */
|
||||
if (sk_repl_get_active_vm() == (VM *)entry.vm_ptr) {
|
||||
sk_repl_set_active_vm((VM *)0);
|
||||
}
|
||||
|
||||
capsule_vm_kill(entry.name);
|
||||
g_wirebind_attached_valid = 0;
|
||||
|
||||
console_puts("WIREBIND: ");
|
||||
console_puts(entry.name);
|
||||
console_println(" detached without flush (device already gone)");
|
||||
}
|
||||
|
||||
@@ -21,6 +21,14 @@
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* FABRIC-3.md §I.8 (re-scoped 2026-09-04): tracks whether the currently
|
||||
* home-blocks-attached device is Zuse's own -- the single-USB-device
|
||||
* constraint (§F.8) means capsule_zuse_boot_logout() only needs one flag,
|
||||
* not a device/uuid comparison, to know a detach event is hers to act on
|
||||
* (a regular WIREBIND user occupying the one slot instead is tracked
|
||||
* separately, in capsule_wirebind.c -- the two paths never overlap). */
|
||||
static int g_zuse_attached_this_device = 0;
|
||||
|
||||
/* 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. */
|
||||
@@ -45,7 +53,17 @@ static int genesis_marker_read(zuse_genesis_marker_t *out) {
|
||||
}
|
||||
|
||||
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;
|
||||
/* vm_zuse_cert_install() is deliberately one-way (returns -1, no-op,
|
||||
* once vm->zuse_cert_installed is already 1) -- that's a real
|
||||
* security property (the cert/pubkey must never be re-installed or
|
||||
* swapped) and stays untouched here. Its return is NOT used to gate
|
||||
* ACL-ZUSE-BOOT below anymore (FABRIC-3.md §I.8, re-scoped
|
||||
* 2026-09-04): re-authenticating after capsule_zuse_boot_logout()
|
||||
* needs ACL-ZUSE-BOOT to re-run and re-set zuse_session even when
|
||||
* the cert itself was already installed from an earlier boot-time
|
||||
* attach -- gating it on install's success/failure made re-login
|
||||
* impossible, since a second install always fails by design. */
|
||||
vm_zuse_cert_install(mama_vm, seed, pubkey);
|
||||
/* 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
|
||||
@@ -55,6 +73,7 @@ static void install_and_activate(VM *mama_vm, const uint8_t seed[32], const uint
|
||||
* logic; policy stays in ACL.4th/zuse.4th per this project's own
|
||||
* convention. */
|
||||
vm_interpret(mama_vm, "ACL-ZUSE-BOOT");
|
||||
g_zuse_attached_this_device = 1;
|
||||
}
|
||||
|
||||
void capsule_zuse_boot_try_attach(struct blkio_dev *dev,
|
||||
@@ -62,7 +81,13 @@ void capsule_zuse_boot_try_attach(struct blkio_dev *dev,
|
||||
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 */
|
||||
/* FABRIC-3.md §I.8, re-scoped 2026-09-04: gate on zuse_session, not
|
||||
* zuse_cert_installed. zuse_cert_installed never clears (one-way,
|
||||
* see install_and_activate()'s own comment) and would permanently
|
||||
* block re-authentication after a logout; zuse_session does clear
|
||||
* (capsule_zuse_boot_logout()), which is exactly the "already have
|
||||
* Zuse THIS SESSION" check this early-return is actually for. */
|
||||
if (mama_vm->zuse_session) return;
|
||||
|
||||
zuse_genesis_marker_t marker;
|
||||
int have_marker = (genesis_marker_read(&marker) == 0);
|
||||
@@ -117,3 +142,12 @@ void capsule_zuse_boot_try_attach(struct blkio_dev *dev,
|
||||
console_println("Zuse: identity confirmed from attached thumbdrive");
|
||||
install_and_activate(mama_vm, idrec.seed, idrec.pubkey);
|
||||
}
|
||||
|
||||
void capsule_zuse_boot_logout(VM *mama_vm) {
|
||||
if (!mama_vm || !g_zuse_attached_this_device) return;
|
||||
|
||||
mama_vm->zuse_session = 0;
|
||||
g_zuse_attached_this_device = 0;
|
||||
|
||||
console_println("Zuse: session ended -- reattach to re-authenticate");
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "starkernel/capsule_runcap.h"
|
||||
#include "starkernel/capsule_console.h"
|
||||
#include "starkernel/capsule_wirebind.h"
|
||||
#include "starkernel/capsule_zuse_boot.h" /* capsule_zuse_boot_logout() -- FABRIC-3.md §I.8 EJECT */
|
||||
#include "starkernel/homeblocks_sig.h"
|
||||
#include "freestanding/stdio.h"
|
||||
#include "starkernel/capsule_mint.h"
|
||||
@@ -580,6 +581,25 @@ void mama_word_kill(VM *vm)
|
||||
/* Stack clean on exit */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief EJECT ( -- )
|
||||
* Graceful detach of whatever identity is currently attached via the
|
||||
* home-blocks USB path (FABRIC-3.md §F.10, extended §I.8 2026-09-04) --
|
||||
* a regular WIREBIND user VM or Zuse herself, no identity handled any
|
||||
* differently. Single-USB-device constraint (§F.8) means there is only
|
||||
* ever one candidate, so at most one of the two calls below actually
|
||||
* does anything; the other is a no-op. capsule_wirebind_eject() flushes,
|
||||
* resets the console's active VM if bound to it, then kills a user VM;
|
||||
* capsule_zuse_boot_logout() clears zuse_session (no flush/kill -- Zuse
|
||||
* owns no separate VM or blocks of her own).
|
||||
*/
|
||||
void mama_word_eject(VM *vm)
|
||||
{
|
||||
capsule_wirebind_eject();
|
||||
capsule_zuse_boot_logout(vm);
|
||||
/* Stack clean on exit */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief VM-STEP ( c-addr u -- )
|
||||
* Give one REPL quantum to a named VM.
|
||||
@@ -1491,6 +1511,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "CONNECT-ARTEMIS", mama_word_connect_artemis);
|
||||
register_word(vm, "BIRTH", mama_word_birth);
|
||||
register_word(vm, "KILL", mama_word_kill);
|
||||
register_word(vm, "EJECT", mama_word_eject);
|
||||
register_word(vm, "START", mama_word_start);
|
||||
register_word(vm, "STOP", mama_word_stop);
|
||||
register_word(vm, "USE", mama_word_use);
|
||||
@@ -1527,6 +1548,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "CONNECT-ARTEMIS", mama_word_connect_artemis);
|
||||
register_word(vm, "BIRTH", mama_word_birth);
|
||||
register_word(vm, "KILL", mama_word_kill);
|
||||
register_word(vm, "EJECT", mama_word_eject);
|
||||
register_word(vm, "START", mama_word_start);
|
||||
register_word(vm, "STOP", mama_word_stop);
|
||||
register_word(vm, "USE", mama_word_use);
|
||||
|
||||
@@ -265,6 +265,17 @@ static void sk_repl_idle(VM *active_vm)
|
||||
if (g_attached_blk_dev == &usb_blk_dev) {
|
||||
g_attached_blk_dev = (void *)0;
|
||||
}
|
||||
|
||||
/* FABRIC-3.md §F.10 decision 2 (UNCLEAN, closed alongside EJECT):
|
||||
* the device is already gone -- no-op if WIREBIND never had
|
||||
* anything tracked (general-purpose USB use, not a home-blocks
|
||||
* identity drive). */
|
||||
capsule_wirebind_unclean_detach();
|
||||
|
||||
/* FABRIC-3.md §I.8, re-scoped 2026-09-04: Zuse logs out on device
|
||||
* removal exactly like a WIREBIND user -- no-op if the device
|
||||
* that just left wasn't hers. */
|
||||
capsule_zuse_boot_logout((VM *)sk_get_mama_vm());
|
||||
}
|
||||
|
||||
/* FABRIC.md/FABRIC-2.md Section V item 6: "a cheap 'anything dirty?
|
||||
|
||||
Reference in New Issue
Block a user