diff --git a/FABRIC-3.md b/FABRIC-3.md index 8b15ff4..dff5c33 100644 --- a/FABRIC-3.md +++ b/FABRIC-3.md @@ -135,8 +135,11 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR `RELOCATE-BLOCK`, `FABRIC-2.md`, commit `36d832f`. This item is about the identityβ†’range allocation that decides what to relocate blocks* into*, still unbuilt.)* -- [ ] Design the on-drive block-map format (Section U item 4) β€” what it records (block ranges - claimed? individual block liveness? something else), how it's serialized. +- [x] **SCOPED 2026-08-27 (FABRIC-3.md Β§F.4).** Design the on-drive block-map format (Section + U item 4). Resolved as: no separate table β€” repurpose the existing, fully-wired-but- + zero-callers `blk_meta_t`/`blk_get_meta()`/`blk_set_meta()` (`block_subsystem.c`), replacing + its stale POSIX-flavored ownership fields with a pubkey-fingerprint owner + a fast-deny ACL + bit + deliberate reserved slack. Field design done; the actual code edit is not. - [ ] Implement writing the block-map to a drive. @@ -1395,7 +1398,7 @@ graph TD CERTVERIFY["πŸ”“ CA-signed-cert verification path (M3)
UNBLOCKED by M6, not yet built"] FIRSTTOUCH["❌ First-touch identityβ†’block-range
allocation (M3)"] - BMAPFMT["❓ On-drive block-map format (M3)
real design decision, not started"] + BMAPFMT["❌ On-drive block-map format (M3)
SCOPED 2026-08-27 (Β§F.4) β€” repurpose blk_meta_t"] BMAPWRITE["❌ Write block-map to drive (M3)"] BMAPREAD["❌ Read/validate block-map on insert (M3)"] MIGSM["🟑 Migration state machine (M3)
partially answered: ACL decides *when*"] @@ -1452,9 +1455,9 @@ graph TD classDef open fill:#666,stroke:#333,color:#fff classDef partial fill:#883,stroke:#333,color:#fff class M6,PH8,EXPIRE,MSGSHAPE,HOTPLUG done - class W10,STALL,FIRSTTOUCH,BMAPWRITE,BMAPREAD,UNCLEAN,WIREBIND,BINDSTEP,DETACH,MINT blocked + class W10,STALL,FIRSTTOUCH,BMAPWRITE,BMAPREAD,UNCLEAN,WIREBIND,BINDSTEP,DETACH,MINT,BMAPFMT blocked class CERTVERIFY unblocked - class BMAPFMT,ACLKEY,RUNCAP,MSGMIGRATE,SSDSCOPE,ROUNDTRIP,POLYBLOCK open + class ACLKEY,RUNCAP,MSGMIGRATE,SSDSCOPE,ROUNDTRIP,POLYBLOCK open class MIGSM partial ``` @@ -1573,6 +1576,82 @@ yes β€” the *lock itself* being immutable once set at a VM's birth is a differen "binding stays retargetable," and shouldn't be conflated with decision 3 above) β€” worth a dedicated look when this is actually built, not decided in this pass. +### F.4 β€” `BMAPFMT` (on-drive block-map format) + +Worked through conversationally, one step at a time, rather than dumped as a finished design β€” +each step below only followed once the previous one was grounded in real code, not assumption. + +**Step 1 β€” does a map cell need a state field, not just ownership?** Yes, confirmed against +existing precedent before answering: the internal Artemis BAM entry +(`blk_bam_entry_t{allocated, dirty}`, `block_subsystem.h:257-261`) already conflates ownership +and state in the simplest map this codebase has. More importantly, `MIGSM` and `UNCLEAN` (two +already-identified, currently-blocked graph nodes) have nowhere else to record "this range is +mid-migration" or "this range's flush was interrupted" without a state field β€” this node was +quietly upstream of both. + +**Step 2 β€” should a cell also carry an ACL?** Yes, and it can't reuse `acl_mode`/`acl_allow`/ +`acl_pinned` directly for the same reason `ACLKEY` (F.2) couldn't β€” those are `DictEntry` +fields. Decided: the owner field and any future ACL grant should share **the same identity +representation** `VMIdentity` already established (a pubkey), not a second encoding β€” keeps +"identity is one common primitive" (D.5) actually true in practice, not just in name. + +**Step 3 β€” ACL check ordering.** "ACL denial is the fast exit path" β€” grounded directly +against `vm.c:611-624`'s live word-execution ACL check: a cached `acl_allow` bit is +checked *first*, before any other work, and denial short-circuits immediately. A block cell's +ACL needs the identical shape at its top: one cheap cached allow bit, checked before range/ +state/owner logic runs. + +**Step 4 β€” buffering/granularity, and a major discovery.** Confirmed: this kernel already +buffers a whole 4 KiB devblock on any block touch (`LOAD` and friends), and +`BLK_META_PER_BLOCK` (`block_subsystem.h:83`, `341u /* 341Γ—3 ~= 1023, padded to 1024 */`) +already packs **exactly** 3Γ—1 KiB Forth-block metadata slices into that same devblock's spare +1 KiB β€” the "3-block cluster + 1 KiB metadata" shape volunteered in conversation *already +exists as `blk_meta_t`*, with real, wired, non-stub accessors (`blk_get_meta()`/ +`blk_set_meta()`, cached, dirty-tracked, `block_subsystem.c:1175-1207`). **Grepped and +confirmed it has zero callers anywhere in the codebase** β€” fully built, fully unused. Its +existing "Security & ownership" 40 bytes (`owner_id`/`permissions`/`acl_block`/`signature[2]`) +predate and directly conflict with both the anti-POSIX principle and `VMIdentity`'s +pubkey-based model β€” flagged, not silently reused. + +**Decisions made 2026-08-27:** + +1. **`BMAPFMT` is not a new structure β€” it's repurposing `blk_meta_t`.** No separate on-drive + block-map table gets built. Ownership/ACL/state travel *with* the block itself + (distributed), not in a centralized table (the shape Milestone 3's original wording + pictured). One I/O gets payload and map info together; nothing separate to keep in sync. +2. **Consequence flagged, not silently absorbed:** `homeblocks_sig_t`'s reserved + `blockmap_offset`/`blockmap_devblocks` fields (`homeblocks_sig.h:93-95`) become unnecessary + under this decision β€” that header reserved space for a centralized table this design no + longer needs. Needs a comment update in that file once this is actually built, not left + silently stale. +3. **Replacement for the 40-byte "Security & ownership" block** (same budget the old + `owner_id`/`permissions`/`acl_block`/`signature[2]` occupied; nothing else in `blk_meta_t` + moves): + ``` + uint8_t owner_fp[8]; /* truncated fingerprint of owner's VMIdentity pubkey, not the + full 32 bytes -- keeps this cheap per-block; full pubkey + resolves via the drive's own identity record. */ + uint8_t acl_allow; /* cached fast-deny bit, checked first -- vm.c:611-624's exact + pattern, applied to a block instead of a word. */ + uint8_t acl_reserved[7]; /* explicitly undecided -- deliberate slack per "flexibility + until we understand the recipe," not a placeholder to fill + reflexively. */ + uint64_t reserved_future; /* untouched budget, same reasoning. */ + ``` + `flags` (already existing, already generic) does double duty as the **state** field from + Step 1 β€” no new field, just future-defined bit values (`CLAIMED`/`MIGRATING`/`STALE`/etc.). + Everything else in `blk_meta_t` (`checksum`, timestamps, `content_type`, hash, chain links, + `app_data[15]`) is untouched. +4. **Allocation granularity**: claims quantize to whole devblocks (3 Forth blocks), matching + the existing packing β€” a cell never needs to describe partial-devblock ranges. + +**Not yet scoped (deferred within this node):** the actual allow-list/grant shape beyond the +single fast-deny bit (lands in `acl_reserved`, once designed); the specific `flags` bit +values for each state; whether `blk_get_meta()`/`blk_set_meta()` need new FORTH word wrappers +or stay C-only like `zuse_cert_seed`'s "no FORTH access" precedent; the actual repurposing +edit to `block_subsystem.h`/`.c` itself (this pass produced the field design, not the code +change). + ### 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