FABRIC-3.md: scope BINDSTEP -- extends the live, unguarded USE word

Traced the actual retarget path before assuming new plumbing: USE (mama_forth_words.c:430-480) already looks up a VM by name and calls sk_repl_set_active_vm() directly, completely unguarded. BINDSTEP is concretely "add the ACLKEY comparison to this one call site." Found one real gap: usb_blk_dev/xdev are function-static inside sk_repl_idle(), invisible to USE -- needs a small new accessor mirroring sk_repl_get_active_vm()'s own precedent. Decisions: re-verify the attached drive live on every USE call rather than trust a cached pubkey; VMs with no VMIdentity installed yet (Hera/Hermes/Artemis today) stay freely targetable, no regression; installed=1 targets refuse on no-drive-attached or pubkey mismatch, matching USE's existing refusal style. Zuse's override UX stays deferred per direct instruction.
This commit is contained in:
Robert Allan James
2026-08-27 14:30:30 -04:00
parent a45ea881c9
commit c4f9458ad6
+50 -2
View File
@@ -682,7 +682,10 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
- [ ] Implement the actual attach/bind step — extending `sk_repl_set_active_vm()` (confirmed - [ ] Implement the actual attach/bind step — extending `sk_repl_set_active_vm()` (confirmed
to exist, currently an unguarded raw pointer-set) with the key-match check from above, so a to exist, currently an unguarded raw pointer-set) with the key-match check from above, so a
console can only bind to the one VM whose lock matches its key. console can only bind to the one VM whose lock matches its key. **SCOPED 2026-08-27
(`FABRIC-3.md` §F.9)**: real gate is `USE` (`mama_forth_words.c:430-480`), the live,
already-working, currently-unguarded retarget word — check re-verifies the attached drive
live on every call; `installed=0` VMs stay freely targetable.
- [ ] Implement detach behavior on console disconnect or VM teardown. - [ ] Implement detach behavior on console disconnect or VM teardown.
@@ -1426,7 +1429,7 @@ graph TD
ACLKEY["❓ ACL 'bumps and holes' comparison<br/>semantics — SAME GAP in M5 and D.4"] ACLKEY["❓ ACL 'bumps and holes' comparison<br/>semantics — SAME GAP in M5 and D.4"]
WIREBIND["❌ Wire hotplug → capsule_birth_baby() (M5)"] WIREBIND["❌ Wire hotplug → capsule_birth_baby() (M5)"]
BINDSTEP["❌ Extend sk_repl_set_active_vm()<br/>with key-match (M5)"] BINDSTEP["❌ Extend sk_repl_set_active_vm()<br/>with key-match (M5) — SCOPED 2026-08-27 (§F.9)"]
DETACH["❌ Detach behavior (M5)"] DETACH["❌ Detach behavior (M5)"]
EXPIRE["✅ EXPIRE reframed: session end<br/>= VM detach via COOL (§B + D.2)"] EXPIRE["✅ EXPIRE reframed: session end<br/>= VM detach via COOL (§B + D.2)"]
@@ -1519,6 +1522,10 @@ finished). Dashed arrows = softer "gates/informs" relationships.
(`CERTVERIFY`, `WIREBIND`, `RUNCAP`, `HOTPLUG`) was already treating raw devblock-0 as the (`CERTVERIFY`, `WIREBIND`, `RUNCAP`, `HOTPLUG`) was already treating raw devblock-0 as the
working format. Dropping GPT converts all of those from "interim, revisit later" to simply working format. Dropping GPT converts all of those from "interim, revisit later" to simply
"the design," retroactively. "the design," retroactively.
- **`BINDSTEP` already has a live target to gate, not new plumbing** (§F.9) — `USE`
(`mama_forth_words.c`) is a real, working, currently-unguarded retarget word. The only
genuinely new code this node needs is a small cross-file accessor for the currently-attached
device (mirroring `sk_repl_get_active_vm()`'s own precedent) plus the comparison itself.
**Not yet done:** an ordered plan (which node to attack first, given the graph). Per Captain **Not yet done:** an ordered plan (which node to attack first, given the graph). Per Captain
Bob's own framing, that's the next pass — "start asking and answering questions iteratively Bob's own framing, that's the next pass — "start asking and answering questions iteratively
@@ -1876,6 +1883,47 @@ refuse-on-non-blank-media posture — reasonable by analogy, not decided here. T
is unchanged and already modeled in the graph: `WRITE(10)` still doesn't exist, so `MINT` is unchanged and already modeled in the graph: `WRITE(10)` still doesn't exist, so `MINT`
cannot write anything to real or QEMU-emulated USB media yet regardless of the above. cannot write anything to real or QEMU-emulated USB media yet regardless of the above.
### F.9 — `BINDSTEP` (extending `sk_repl_set_active_vm()` with the `ACLKEY` check)
Traced against the live retarget path before assuming new plumbing was needed:
`USE` (`mama_forth_words.c:430-480`) is the real, already-working console-retarget word —
`S" name" USE` looks a VM up by name (case-insensitive) and calls `sk_repl_set_active_vm()`
directly, completely unguarded today. `BINDSTEP` is concretely "add the `ACLKEY` (§F.2)
comparison to this one call site," not a new mechanism.
**Real gap found, not just a policy question:** the "re-verify live" approach (decided below)
needs access to whatever drive is currently attached, but `usb_blk_dev`/`xdev`
(`repl.c:107-108`) are function-static inside `sk_repl_idle()` — invisible to `USE`, which
lives in `mama_forth_words.c`. `BINDSTEP` needs a small new accessor exposing "the currently
attached home-blocks device," mirroring the existing `sk_repl_get_active_vm()` precedent
(`repl.h:67-69`) for exactly this kind of cross-file REPL-state access.
**Decisions made 2026-08-27 (iterative Q&A pass):**
1. **Check freshness: re-verify live, not cached.** Every `USE` call re-runs the
drive-check/`CERTVERIFY` path against whatever is currently attached, rather than trusting a
pubkey cached at original attach time. `USE` is a rare, human-triggered, interactive
operation — the repeated DER-parse + `ed25519_verify()` cost is a non-issue, and this avoids
any staleness question if a drive was swapped without a clean detach.
2. **VMs with no `VMIdentity` installed yet (`installed=0`) — Hera/Hermes/Artemis today, until
`D.5`'s per-VM-identity work lands — stay freely targetable, no check applied.** Preserves
`USE`'s current working behavior exactly; refusing on `installed=0` would be a real
regression today for VMs that were never in scope for this lock in the first place.
3. **For an `installed=1` target:** `BINDSTEP` refuses (matching `USE`'s existing clean
early-refusal style for not-found/dead-VM cases) whenever either no drive is currently
attached, or the freshly re-verified cert's owner pubkey doesn't match the target's
`VMIdentity.owner_pubkey`. No drive attached is a plain refusal, not a special case — it
simply means "no identity presented," which can never match.
4. **Zuse's override UX: deferred**, per direct instruction — this pass confirms the
architectural constraint from `F.2` still stands (a distinct, explicit call path, not a
branch folded into `USE`'s own logic) without committing to the exact word/interaction yet.
**Not yet scoped (deferred within this node):** the new accessor's exact name/signature; the
refusal message text (matching `USE`'s existing console-message style); whether a refused
`BINDSTEP` attempt should produce an audit-log record (this codebase has a real precedent for
this — `capsule_parity_log_birth_failed()` and friends, `parity.c` — but nothing says a
security-relevant refusal here needs the same treatment; not decided).
### D.5 — Scope expansion (2026-08-27): identity is common to every VM, not just users ### D.5 — Scope expansion (2026-08-27): identity is common to every VM, not just users
Surfaced while scoping `ACLKEY`, stated directly: *"the whole object is to deliver a Surfaced while scoping `ACLKEY`, stated directly: *"the whole object is to deliver a