Files
LithosAnanake/include/starkernel/capsule_wirebind.h
T
Robert Allan JamesandClaude Sonnet 5 9f0f33dfc5 Stage 4 increment 1: per-device WIREBIND tracking, fixing a real multi-identity detach leak (FABRIC-3.md §XXVIII Stage 4)
capsule_wirebind_unclean_detach()/eject() tracked "the attached identity"
as a single global, correct for the console-pairing UX (one physical
console) but wrong for detach safety: since §XV/§XVI proved multiple
identities genuinely live simultaneously via this same attach path, every
attach after the first silently overwrote the singleton, so an unclean
detach of any but the most-recently-attached identity was silently
ignored -- that VM leaked forever, no trace in the log.

Adds a per-device live-identity table, separate from the (unchanged)
console-pairing singleton, so unclean-detach resolves any attached
device to its own identity. Sized off messaging.4th's own VM-MAX (16)
minus Tripod's 3 reserved slots, not an invented number. Corrects the
stale "single-USB-device constraint" doc claim in capsule_wirebind.h,
false since §XV/§XVI.

Groundwork for Stage 4's real deliverable (WIREBIND VMs as switch-signal
participants) -- this increment only fixes detach targeting; switch-
signal registration is next.

Verified clean on all 3 architectures (no WIREBIND attach happens in a
plain boot, so this is a regression check on the existing Tripod-only
path; live multi-identity verification comes with the switch-signal
registration increment).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
2026-09-15 00:02:20 -04:00

173 lines
8.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/**
* capsule_wirebind.h - WIREBIND: the real thumbdrive-attach call site
* (FABRIC-2.md §F.5/§F.23). Assembles pieces already built and
* individually verified this session -- CERTVERIFY (vm_identity.h's
* vm_identity_from_cert()), RUNCAP (capsule_runcap.h), the console-VM +
* user-VM pair (capsule_console.h, sk_repl_dispatch_line() in repl.c) --
* into one automatic sequence, replacing the RUNCAP-TEST/PAIR-TEST
* diagnostic words that exercised each piece by hand.
*/
#ifndef STARKERNEL_CAPSULE_WIREBIND_H
#define STARKERNEL_CAPSULE_WIREBIND_H
#ifdef __STARKERNEL__
#include "starkernel/homeblocks_sig.h"
#include "starkernel/vm_identity.h"
#include "vm.h"
struct blkio_dev;
/**
* capsule_wirebind_verify_cert - Read the cert region off dev and verify
* it against mama_vm's own Zuse identity. Shared by both
* capsule_wirebind_try_attach() (the original attach) and BINDSTEP
* (mama_word_use(), mama_forth_words.c -- re-verifies live on every USE
* of an identity-locked VM, per FABRIC-2.md §F.9 decision 1) so both
* call sites check the exact same thing the exact same way.
*
* No-op-and-fail (-1) if sig->cert_offset is 0 (no cert region -- a
* genesis-mode Zuse drive, or simply not a regular identity drive) or
* mama_vm has no installed Zuse cert yet.
*
* @param dev Already-open block device to read the cert from.
* @param sig Its already-checked homeblocks_sig_t.
* @param mama_vm Hera's own VM -- the trust root (zuse_cert_pubkey).
* @param out Filled with the verified identity on success.
* @return 0 on success, -1 on any failure (read, verify, or precondition).
*/
int capsule_wirebind_verify_cert(struct blkio_dev *dev,
const homeblocks_sig_t *sig,
VM *mama_vm, VMIdentity *out);
/**
* capsule_wirebind_try_attach - Try to verify and bind a just-attached
* regular (non-Zuse) identity drive.
*
* No-op if sig->cert_offset is 0 (a genesis-mode Zuse drive has no cert
* region -- that's capsule_zuse_boot_try_attach()'s own job, not this
* one's) or if mama_vm has no installed Zuse cert yet (nothing to verify
* the attached cert against). Otherwise: reads the cert devblock(s),
* calls vm_identity_from_cert() against mama_vm's own zuse_cert_pubkey
* and sig->drive_uuid. On success, reads the drive's own
* user_identity_seed_t for its username and births a console VM +
* RUNCAP-born user VM pair (idempotent -- no-ops if that username is
* already live this session), installs the verified VMIdentity onto the
* user VM, and registers the "<username>~user" pairing
* (sk_repl_dispatch_line(), repl.c, looks for this). Does NOT USE the
* new console automatically -- that stays an explicit, later,
* ACL-gated step (BINDSTEP, §F.9), not something a bare attach should
* trigger silently.
*
* @param dev The just-attached, already-open block device.
* @param sig Its already-checked homeblocks_sig_t.
* @param mama_vm Hera's own VM (the verifier -- her zuse_cert_pubkey is
* the trust root regular user certs are checked against).
*/
void capsule_wirebind_try_attach(struct blkio_dev *dev,
const homeblocks_sig_t *sig,
VM *mama_vm);
/**
* capsule_wirebind_eject - Graceful detach of whatever VM is currently
* attached via the home-blocks USB path (FABRIC-2.md §F.10, decision 1).
* The drive is still physically present when this runs.
*
* Sequence: resolve the tracked attached-VM id to a live registry entry
* (no-op, returns -1, if nothing is tracked or the entry is already
* dead/gone -- capsule_vm_kill()'s own idempotency covers a VM already
* killed by some other path); blk_vm_flush_all() while the VM is still
* alive; if the console's active VM is this same VM, reset it to Hera
* (sk_repl_set_active_vm(NULL)) *before* teardown -- required, not
* optional, to avoid a dangling console pointer; capsule_vm_kill() by
* name; clear the tracked state.
*
* EJECT is inherently about "whichever identity is currently paired to
* the one physical console" -- targets that singleton, no name argument.
* (Corrected 2026-09-14: this used to claim a "single-USB-device
* constraint (§F.8)" meant there was never more than one candidate --
* stale even at the time this correction was written; §XV/§XVI
* (2026-09-11/12) proved 9 identities genuinely simultaneously live via
* this same attach path. EJECT staying console-singleton-scoped is a
* deliberate UX choice now, not a hardware constraint -- see
* capsule_wirebind_unclean_detach()'s own doc below for the function
* that DOES need to reach every live identity, not just this one.)
*
* @return 0 on success, -1 if nothing was attached to eject.
*/
int capsule_wirebind_eject(void);
/**
* capsule_wirebind_unclean_detach - Abrupt-path counterpart to
* capsule_wirebind_eject() (FABRIC-2.md §F.10, decision 2 -- the UNCLEAN
* node, closed alongside EJECT). Called from the existing
* bot_msc_detach_pending hot-unplug signal (repl.c) -- the device is
* already gone by the time this runs, so no flush is attempted; data
* since the last flush is lost, which is correct unclean-removal
* semantics.
*
* FABRIC-3.md §VII follow-on, 2026-09-06: requires the departing device
* to actually be one WIREBIND is tracking -- a real bug otherwise, found
* live once genuine multi-device attach made a *different* device's
* detach reachable while a WIREBIND user's own stayed attached.
*
* FABRIC-3.md §XXVIII Stage 4, 2026-09-14: resolves the departing device
* against a per-device live-identity table now, not the single
* console-pairing global capsule_wirebind_eject() uses -- with several
* identities simultaneously live (§XV/§XVI), any one of them can be the
* device that just disappeared, not only the most recently attached.
* Refuses (rather than freeing) a VM currently VM_STATE_SWITCHED_OUT --
* see capsule_vm_kill()'s own guard -- leaving it tracked for the Stage 3
* switcher to reap on its own next resume attempt instead.
*
* @param dev The device that just detached; every other value is a no-op.
*/
void capsule_wirebind_unclean_detach(struct blkio_dev *dev);
/**
* capsule_wirebind_attached_username - The plain username (no "~user"
* registry-name suffix) of whichever identity is currently tracked as
* attached, or NULL if none is (FABRIC-2.md §I.1/4.4s -- the `(user)`
* console prompt segment reads this). Points into WIREBIND's own
* internal storage; valid only until the next attach/eject/detach, same
* caveat as console_get_vm_name().
*/
const char *capsule_wirebind_attached_username(void);
/**
* capsule_wirebind_overflow_idle_check - FABRIC-2.md §I.2's own "overflow
* trigger," decided and built 2026-09-05. Called once per idle tick
* (sk_repl_idle(), repl.c, alongside blk_migration_idle_check() -- same
* ~1 Hz cadence), same as that function's own convention.
*
* No-op if nothing is attached via WIREBIND. Otherwise reads the attached
* drive's own free/total via blk_get_device_free_blocks() (real numbers:
* a WIREBIND-attached drive is always HOMEBLOCKS_SIG_OK, i.e. already
* STFR/v2-formatted, by the time blk_subsys_attach_device() runs on the
* same dev pointer right after WIREBIND itself -- not PROVISIONAL, not
* raw). If free space is below a fixed threshold AND the attached
* identity does not already own a claim (blk_owner_has_claim() -- a disk
* scan, not a RAM flag, so this decision survives reboot/reattach for
* free), claims a fixed number of additional devblocks on Artemis's own
* device via blk_firsttouch_claim() -- a one-time-per-identity extension,
* not a growth loop, deliberately: this does not free space on the
* user's own drive, it only extends their pool onto system-resident
* space, so re-claiming every tick once already extended would walk
* Artemis's device to exhaustion for no benefit.
*/
void capsule_wirebind_overflow_idle_check(void);
#endif /* __STARKERNEL__ */
#endif /* STARKERNEL_CAPSULE_WIREBIND_H */