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
+74
-13
@@ -752,11 +752,20 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
|
||||
(`mama_forth_words.c:485-500`+) does the live re-verify-and-compare, gated on
|
||||
`((VM*)entry.vm_ptr)->identity.installed`.
|
||||
|
||||
- [ ] Implement detach behavior on console disconnect or VM teardown. **SCOPED 2026-08-27
|
||||
- [x] Implement detach behavior on console disconnect or VM teardown. **SCOPED 2026-08-27
|
||||
(`FABRIC-3.md` §F.10)**: a new `EJECT` word — flush via the existing `blk_vm_flush_all()`,
|
||||
reset the console's active VM if bound to the departing VM, then `capsule_vm_kill()` (already
|
||||
complete, no new teardown mechanism needed). Needs one small new piece of state `WIREBIND`
|
||||
must own: which `VMUuid` is currently attached via the home-blocks USB path.
|
||||
must own: which `VMUuid` is currently attached via the home-blocks USB path. **DONE
|
||||
2026-09-04**: built exactly as scoped. `capsule_wirebind.c` now owns the tracked `VMUuid`
|
||||
(set at the end of a successful `capsule_wirebind_try_attach()`); `capsule_wirebind_eject()`
|
||||
and `capsule_wirebind_unclean_detach()` both resolve it via `capsule_vm_registry_get()`,
|
||||
reset `sk_repl_get_active_vm()`/`sk_repl_set_active_vm()` if bound, then
|
||||
`capsule_vm_kill(entry.name)`, then clear the tracked state. `EJECT` registered as a new
|
||||
Hera-only word (`mama_forth_words.c`, alongside `KILL`). The abrupt path calls
|
||||
`capsule_wirebind_unclean_detach()` from the existing `bot_msc_detach_pending` hook
|
||||
(`repl.c`) — no flush attempted there, matching decision 2. Verified 3-arch boot to `ok>`
|
||||
(amd64/aarch64/riscv64, each in the foreground, `clean qemu`).
|
||||
|
||||
### From FABRIC-2.md §X, Milestone 6 — Kernel/capsule PKI signing chain
|
||||
|
||||
@@ -1229,6 +1238,18 @@ other three become honest, explicit punch-list items instead of being touched sp
|
||||
**`EXPIRE` stays explicitly deferred until Phase 8 (PKI/zuse session minting) lands** — not
|
||||
abandoned, not left ambiguous: revisit it as part of that work, admitting the session itself
|
||||
(not a word) as the patron, once there is something real for it to represent.
|
||||
|
||||
**CLOSED 2026-09-04, and decision 2 above is now shown wrong in a way this session's own
|
||||
§H.1 had already implied without anyone tracing it back here**: "admit the zuse session as
|
||||
a Stadium patron" was never buildable — Zuse authenticates directly onto Hera
|
||||
(`capsule_zuse_boot.c`), and Hera is patron zero, permanently pinned, exempt from `COOL`;
|
||||
there is no patron for a reap sweep to ever find. `EXPIRE`'s real intent (decision 4: revoke
|
||||
elevated privilege, drop back to non-zuse) is built instead as a **detach-triggered**
|
||||
action, not a TTL/reap one — `capsule_zuse_boot_logout()` clears `zuse_session` when Zuse's
|
||||
own attached drive leaves, the same trigger `EJECT`/hot-unplug already use for regular
|
||||
WIREBIND users (`FABRIC-3.md` §F.10/§I.3), generalized so neither identity is a special
|
||||
case. See §I.8 for the full build note, including the `install_and_activate()` re-login fix
|
||||
this required. Verified 3-arch boot to `ok>`.
|
||||
- [x] Fixed (2026-08-26): `ONTOLOGY.md` §IX's "words (dictionary, warehouse-resident today,
|
||||
not yet migrated)" line was stale — corrected to state words are fully migrated and live
|
||||
via `stadium_word_dispatch()`. Doc-only, no build/boot verification needed.
|
||||
@@ -1533,14 +1554,14 @@ graph TD
|
||||
BMAPWRITE["✅ Write block-map to drive (M3)<br/>CLOSED 2026-08-27 (§F.12) — already built, blocked only on W10"]
|
||||
BMAPREAD["✅ Read/validate block-map on insert (M3)<br/>CLOSED 2026-08-27 (§F.13) — already built"]
|
||||
MIGSM["🟡 Migration state machine (M3)<br/>partially answered: ACL decides *when*"]
|
||||
UNCLEAN["❌ Unclean-removal handling (M3)<br/>DESIGN CLOSED 2026-08-27 (§F.10), code not yet written"]
|
||||
UNCLEAN["✅ Unclean-removal handling (M3)<br/>BUILT 2026-09-04 (§F.10) — capsule_wirebind_unclean_detach()"]
|
||||
|
||||
ACLKEY["❌ ACL 'bumps and holes' comparison<br/>semantics — SCOPED 2026-08-27 (§F.2)"]
|
||||
WIREBIND["❌ Wire hotplug → capsule_birth_baby() (M5)"]
|
||||
BINDSTEP["❌ Extend sk_repl_set_active_vm()<br/>with key-match (M5) — SCOPED 2026-08-27 (§F.9)"]
|
||||
DETACH["❌ Detach behavior / new EJECT word (M5)<br/>SCOPED 2026-08-27 (§F.10)"]
|
||||
DETACH["✅ Detach behavior / new EJECT word (M5)<br/>BUILT 2026-09-04 (§F.10)"]
|
||||
|
||||
EXPIRE["✅ EXPIRE reframed: session end<br/>= VM detach via COOL (§B + D.2)"]
|
||||
EXPIRE["✅ EXPIRE: session end on device detach<br/>BUILT 2026-09-04 (§B+D.2+H.1+I.8) — COOL for<br/>WIREBIND users, logout for Zuse (no patron of her own)"]
|
||||
RUNCAP["❌ Runtime capsule construction<br/>from thumbdrive content — SCOPED 2026-08-27 (§F.6)"]
|
||||
MINT["❌ Ongoing MINT word (Phase 8 + D.3)<br/>SCOPED 2026-08-27 (§F.8) — GPT dropped, single-device confirmed OK"]
|
||||
PENTAGON["📍 Pentagon: Hera/Hermes/Artemis/<br/>User-VM/Console, K5 (D.2b)"]
|
||||
@@ -1590,8 +1611,8 @@ graph TD
|
||||
classDef unblocked fill:#a83,stroke:#333,color:#fff
|
||||
classDef open fill:#666,stroke:#333,color:#fff
|
||||
classDef partial fill:#883,stroke:#333,color:#fff
|
||||
class M6,PH8,EXPIRE,MSGSHAPE,HOTPLUG,BMAPWRITE,BMAPREAD,SSDSCOPE,ROUNDTRIP,POLYBLOCK,W10 done
|
||||
class STALL,FIRSTTOUCH,WIREBIND,BINDSTEP,DETACH,MINT,BMAPFMT,CERTVERIFY,RUNCAP,UNCLEAN,MSGMIGRATE,ACLKEY blocked
|
||||
class M6,PH8,EXPIRE,MSGSHAPE,HOTPLUG,BMAPWRITE,BMAPREAD,SSDSCOPE,ROUNDTRIP,POLYBLOCK,W10,DETACH,UNCLEAN done
|
||||
class STALL,FIRSTTOUCH,WIREBIND,BINDSTEP,MINT,BMAPFMT,CERTVERIFY,RUNCAP,MSGMIGRATE,ACLKEY blocked
|
||||
class MIGSM partial
|
||||
```
|
||||
|
||||
@@ -2169,6 +2190,17 @@ the latter, given the single-USB-device constraint confirmed in `F.8` means ther
|
||||
than one candidate — not committed here); the console message text distinguishing a graceful
|
||||
eject from an unclean one in the transcript.
|
||||
|
||||
**BUILT 2026-09-04, closing this node's remaining deferrals:** the tracked state is
|
||||
`g_wirebind_attached_vm_id`/`g_wirebind_attached_valid`, static in `capsule_wirebind.c`, set at
|
||||
the end of a successful `capsule_wirebind_try_attach()`. `EJECT` takes no name argument, per
|
||||
the leaning above — single-USB-device constraint confirmed still holding. Console text
|
||||
distinguishes the two paths: `"EJECT: <name> flushed and detached"` vs. `"WIREBIND: <name>
|
||||
detached without flush (device already gone)"`. Both new entry points
|
||||
(`capsule_wirebind_eject()`, `capsule_wirebind_unclean_detach()`) share a
|
||||
`wirebind_resolve_attached()` helper that's idempotent the same way `capsule_vm_kill()` already
|
||||
is — resolving to nothing tracked, or a since-killed entry, is a clean no-op, not an error.
|
||||
Verified 3-arch boot to `ok>` (amd64/aarch64/riscv64, each in the foreground).
|
||||
|
||||
### F.11 — `FIRSTTOUCH` (identity → system-device block-range allocation)
|
||||
|
||||
Traced `g.total_user_lbn` (`block_subsystem.c:634,650,655,677,688,753`) and `blk_meta_t`'s
|
||||
@@ -4294,12 +4326,14 @@ stale the way the original carry-forwards did.
|
||||
compudynamics-heat governs *when*; migration itself expected rare, not routine) but no state
|
||||
machine designed. *(Same location as above.)*
|
||||
|
||||
### I.3 — Milestone 5 remainder (BINDSTEP)
|
||||
### I.3 — Milestone 5 remainder (BINDSTEP) — CLOSED 2026-09-04
|
||||
|
||||
- [ ] **`EJECT` / detach behavior.** Scoped 2026-08-27 (§F.10): flush via
|
||||
- [x] **`EJECT` / detach behavior.** Scoped 2026-08-27 (§F.10): flush via
|
||||
`blk_vm_flush_all()`, reset the console's active VM if bound to the departing VM, then
|
||||
`capsule_vm_kill()` (already complete). Needs one new piece of state `WIREBIND` must own —
|
||||
which `VMUuid` is currently attached via the home-blocks USB path. Not built. *(Full entry:
|
||||
which `VMUuid` is currently attached via the home-blocks USB path. **DONE 2026-09-04** — see
|
||||
the full closure note at its original §A location (same line as this item's *(Full entry: ...)*
|
||||
pointer below); verified 3-arch boot to `ok>`. *(Full entry:
|
||||
§A, "From FABRIC-2.md §X, Milestone 5 — Console/VM key-match binding.")*
|
||||
|
||||
### I.4 — Milestone 6 remainder (PKI)
|
||||
@@ -4341,9 +4375,9 @@ FABRIC-2.md §X, Milestone 8 — Bare-metal boot from physical USB.")*
|
||||
- [ ] Explicitly deferred until Milestone 7 closes — its own precondition isn't met, nothing
|
||||
to do here yet. *(Full entry: §A, "From FABRIC-2.md §X, Milestone 9.")*
|
||||
|
||||
### I.8 — `EXPIRE` (ACL) — deferred, but its stated blocker is now stale
|
||||
### I.8 — `EXPIRE` (ACL) — CLOSED 2026-09-04, re-scoped away from the Stadium-patron model
|
||||
|
||||
- [ ] Decided 2026-08-26 (§B): a zuse superuser session, not a per-word ACL entry, is the
|
||||
- [x] Decided 2026-08-26 (§B): a zuse superuser session, not a per-word ACL entry, is the
|
||||
right unit to admit as a Stadium patron and reap on expiry — but explicitly deferred "until
|
||||
Phase 8 (PKI/zuse session minting) lands," since building a reap mechanism with nothing real
|
||||
to feed it would be speculative infrastructure ahead of its only consumer.
|
||||
@@ -4356,4 +4390,31 @@ FABRIC-2.md §X, Milestone 8 — Bare-metal boot from physical USB.")*
|
||||
all (`zuse_session` is set once by `ACL-ZUSE-BOOT` and never cleared), and Stadium's own
|
||||
generic ttl-decrement/reap mechanism still doesn't exist either — both real gaps, narrower
|
||||
than "Phase 8 doesn't exist," worth re-scoping against before treating this as unblocked.
|
||||
*(Full entry: §B, "From FABRIC-2.md §... — EXPIRE (ACL).")*
|
||||
**Invalidated a second time, more specifically, 2026-09-04**: this decision's own premise
|
||||
(admit the zuse session *as a Stadium patron*) doesn't hold at all, for a structural reason
|
||||
§H.1 had already stated in general terms without anyone connecting it back to this item —
|
||||
Zuse authenticates directly onto Hera (`capsule_zuse_boot.c`'s `install_and_activate()` sets
|
||||
fields on `mama_vm`, not on a session-owning VM of her own), and Hera is patron zero,
|
||||
permanently pinned, exempt from `COOL`/`stadium_evict()` (§H.1: "pinned sessions simply have
|
||||
no D.2-style 'ending' at all"). There was never a patron for `EXPIRE` to reap here — not a
|
||||
missing mechanism, a missing *referent*. **Built 2026-09-04, per direct instruction to build
|
||||
a real logout rather than re-defer**: `capsule_zuse_boot_logout(mama_vm)`
|
||||
(`capsule_zuse_boot.c`) clears `zuse_session` — never `zuse_cert_installed`, which stays a
|
||||
permanent one-way ratchet by design — when Zuse's own attached device detaches, mirroring
|
||||
D.2's real mechanism (detach-triggered, not TTL) at the point where it actually applies to
|
||||
her: no Stadium patron, no reap sweep, just the same "session ends on device removal" model
|
||||
§I.3's `EJECT`/hot-unplug work just built for regular users, generalized so no identity is
|
||||
handled specially. `EJECT` (`mama_word_eject()`) now calls both
|
||||
`capsule_wirebind_eject()` and `capsule_zuse_boot_logout()`; the hot-unplug path in
|
||||
`repl.c` calls both `capsule_wirebind_unclean_detach()` and `capsule_zuse_boot_logout()`; at
|
||||
most one of each pair ever does anything, per the single-USB-device constraint (§F.8).
|
||||
**Companion fix required to make this actually work, found while building it**:
|
||||
`install_and_activate()` used to skip re-running `ACL-ZUSE-BOOT` whenever
|
||||
`vm_zuse_cert_install()` reported "already installed" (its own deliberate one-way return),
|
||||
which made a logout permanent for the rest of the boot — no way back in. Now
|
||||
`ACL-ZUSE-BOOT` always re-runs on a verified re-attach, and
|
||||
`capsule_zuse_boot_try_attach()`'s own outer gate checks `zuse_session` (clears on logout)
|
||||
instead of `zuse_cert_installed` (never clears) — the check was always meant to mean
|
||||
"already logged in this session," not "has ever been minted." Verified 3-arch boot to `ok>`
|
||||
(amd64/aarch64/riscv64, each in the foreground). *(Full entry: §B, "From FABRIC-2.md §... —
|
||||
EXPIRE (ACL).")*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-03T17:03:38Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-04T12:45:58Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
@@ -78,6 +78,40 @@ 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-3.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.
|
||||
*
|
||||
* Single-USB-device constraint (§F.8) means there is never more than one
|
||||
* candidate, so this always targets "whatever's currently attached" --
|
||||
* no name argument.
|
||||
*
|
||||
* @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-3.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. Otherwise identical to capsule_wirebind_eject(): same
|
||||
* active-VM reset-before-kill step, same tracked-state clear.
|
||||
*/
|
||||
void capsule_wirebind_unclean_detach(void);
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
#endif /* STARKERNEL_CAPSULE_WIREBIND_H */
|
||||
|
||||
@@ -58,6 +58,28 @@ void capsule_zuse_boot_try_attach(struct blkio_dev *dev,
|
||||
const homeblocks_sig_t *sig,
|
||||
VM *mama_vm);
|
||||
|
||||
/**
|
||||
* capsule_zuse_boot_logout - End Zuse's session when her own attached
|
||||
* drive detaches (FABRIC-3.md §I.8, re-scoped 2026-09-04: no identity is
|
||||
* different here -- Zuse logs out on device removal exactly like a
|
||||
* WIREBIND user does, not via a Stadium-patron TTL. She has no separate
|
||||
* VM or blocks of her own, so unlike capsule_wirebind_eject()/
|
||||
* _unclean_detach() there is no flush step to skip on the abrupt path --
|
||||
* one function covers both the graceful (EJECT) and abrupt (hot-unplug)
|
||||
* call sites identically.
|
||||
*
|
||||
* No-op if the currently-tracked attached device isn't Zuse's own
|
||||
* (nothing to do -- some other identity's drive is what's leaving, or
|
||||
* nothing is attached at all). Clears mama_vm->zuse_session only --
|
||||
* zuse_cert_installed and the cert itself stay put, permanently, per
|
||||
* vm_zuse_cert_install()'s own one-way design; re-attaching her own
|
||||
* drive re-authenticates via capsule_zuse_boot_try_attach() without
|
||||
* re-minting anything.
|
||||
*
|
||||
* @param mama_vm Hera's own VM (zuse_session lives here).
|
||||
*/
|
||||
void capsule_zuse_boot_logout(VM *mama_vm);
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
#endif /* STARKERNEL_CAPSULE_ZUSE_BOOT_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
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
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
@@ -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