fix(security): no VM but Artemis and Hera can make Hera run text; a message runs once; word-ACL elevation removed

Any identity could have arbitrary FORTH run as Zuse: KH-ELEVATE-SEND and
KH-BLK-ATTACH-SEND sent any text to Hera, from any VM (shown live).  And the
drain popped a message after running it, so a payload calling a colon word
ran twice.  Both fixed test first (FABRIC-3 SXXXVII):

- KH-BLK-ATTACH-SEND is ( dev-addr ok? -- sent? ), Artemis only; the kernel
  writes the reply text.  BLK-ATTACH-ACK is Hera's only.
- The drain pops before it runs.
- Elevation, ruled not needed: KH-ELEVATE-SEND, message type 8,
  zuse-eligibility.4th, ZUSE-ELIGIBLE?/ZUSE-ELIGIBILITY-ADD/
  ELEVATE-PUBKEY-UNPACK and the eligibility list removed; its disk area
  stays reserved, unused.  FABRIC-3.7 closed.

Three ISAs: POST 1052 passed; new parity baseline word_count=523
hash=0xb73b58052d591638 (deliberate).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-14 11:38:23 -04:00
co-authored by Claude Opus 5.5
parent 029a8ed9cf
commit 5e26fad30d
35 changed files with 72785 additions and 659 deletions
+62 -67
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-10-08T00:35:37Z -->
<!-- Generated by mkcapsule --manifest 2026-10-14T15:35:50Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
@@ -10,7 +10,7 @@
|---------|----------------|----------|--------|
| `ACL.4th` | 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4015 | `0xf8890c05c0d8f921` | n/a |
| `acl-std79.4th` | 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048 | `0x773bf9209df191d1` | n/a |
| `artemis:init.4th` | 4110, 4111, 4112, 4113, 4122, 4123, 4124, 4125, 4126, 4127, 4128, 4129, 4130, 4131, 4132, 4133, 4134, 4135, 4136, 4137, 4138, 4139, 4140, 4141, 4160, 4161, 4162, 4163, 4164, 4165, 4166, 4167, 4168, 4169, 4170, 4171, 4172, 4173, 4174, 4177, 4178, 4179, 4180, 4181, 4182, 4851, 4852, 4853, 4854, 4856, 4857, 4858, 4860 | `0xe6fba9ae56e1c916` | n/a |
| `artemis:init.4th` | 4110, 4111, 4112, 4113, 4122, 4123, 4124, 4125, 4126, 4127, 4128, 4129, 4130, 4131, 4132, 4133, 4134, 4135, 4136, 4137, 4138, 4139, 4140, 4141, 4160, 4161, 4162, 4163, 4164, 4165, 4166, 4167, 4168, 4169, 4170, 4171, 4172, 4173, 4174, 4177, 4178, 4179, 4180, 4181, 4182, 4851, 4852, 4853, 4854, 4856, 4860 | `0x486772c973d0d087` | n/a |
| `block-acl.4th` | 4019, 4020 | `0xf6cc2a59e3a6734e` | n/a |
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x26fb485e5c9dc6ff` | n/a |
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xf154616d248e861f` | n/a |
@@ -23,7 +23,7 @@
| `init-l8-temporal.4th` | 4830, 4831 | `0x51abd4c138246651` | n/a |
| `init-l8-transition.4th` | 4840, 4841, 4842 | `0xbcc1a81976f0a4c9` | n/a |
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | n/a |
| `init.4th` | 2049, 2050, 2057 | `0xea038ba684c53443` | n/a |
| `init.4th` | 2049, 2050, 2057 | `0xd09db9461b336431` | n/a |
| `lib.4th` | 4050 | `0x4b216635c359ef73` | n/a |
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0x22140588ee7c39e6` | n/a |
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` | n/a |
@@ -44,8 +44,7 @@
| `workload-8.4th` | 2160 | `0x56b7f2f0efa000df` | n/a |
| `workload-9.4th` | 4706, 4715, 4725, 4735, 4745 | `0x3f2bec73142aa424` | n/a |
| `workload-calib1.4th` | 5043 | `0x3b9f2d17b554fabc` | n/a |
| `zuse-eligibility.4th` | 4021, 4022 | `0x7b28f4776a32e0b7` | n/a |
| `zuse.4th` | 4016, 4017, 4018 | `0xa14d5a7a88791cf6` | n/a |
| `zuse.4th` | 4016, 4017, 4018 | `0x490ded9be257a90b` | n/a |
*Signed column is `n/a`: this manifest run had no `--sign-key`. Re-run with `--sign-key <path>` to check signing status (does not modify or require rebuilding capsule_generated.c).*
@@ -53,9 +52,9 @@
| LBN | Capsule | xxHash64 | Status |
|-----|---------|----------|--------|
| 2049 | `init.4th` | `0xea038ba684c53443` | ok |
| 2050 | `init.4th` | `0xea038ba684c53443` | ok |
| 2057 | `init.4th` | `0xea038ba684c53443` | ok |
| 2049 | `init.4th` | `0xd09db9461b336431` | ok |
| 2050 | `init.4th` | `0xd09db9461b336431` | ok |
| 2057 | `init.4th` | `0xd09db9461b336431` | ok |
| 2064 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2065 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
| 2066 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
@@ -113,13 +112,11 @@
| 4007 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
| 4008 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
| 4015 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
| 4016 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
| 4017 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
| 4018 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
| 4016 | `zuse.4th` | `0x490ded9be257a90b` | ok |
| 4017 | `zuse.4th` | `0x490ded9be257a90b` | ok |
| 4018 | `zuse.4th` | `0x490ded9be257a90b` | ok |
| 4019 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
| 4020 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
| 4021 | `zuse-eligibility.4th` | `0x7b28f4776a32e0b7` | ok |
| 4022 | `zuse-eligibility.4th` | `0x7b28f4776a32e0b7` | ok |
| 4023 | `acl-std79.4th` | `0x773bf9209df191d1` | ok |
| 4024 | `acl-std79.4th` | `0x773bf9209df191d1` | ok |
| 4025 | `acl-std79.4th` | `0x773bf9209df191d1` | ok |
@@ -153,51 +150,51 @@
| 4063 | `doe-campaign.4th` | `0x26fb485e5c9dc6ff` | ok |
| 4064 | `doe-campaign.4th` | `0x26fb485e5c9dc6ff` | ok |
| 4065 | `doe-campaign.4th` | `0x26fb485e5c9dc6ff` | ok |
| 4110 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4111 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4112 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4113 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4122 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4123 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4124 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4125 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4126 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4127 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4128 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4129 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4130 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4131 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4132 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4133 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4134 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4135 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4136 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4137 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4138 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4139 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4140 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4141 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4160 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4161 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4162 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4163 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4164 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4165 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4166 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4167 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4168 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4169 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4170 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4171 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4172 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4173 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4174 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4177 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4178 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4179 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4180 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4181 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4182 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4110 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4111 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4112 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4113 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4122 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4123 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4124 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4125 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4126 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4127 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4128 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4129 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4130 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4131 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4132 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4133 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4134 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4135 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4136 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4137 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4138 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4139 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4140 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4141 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4160 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4161 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4162 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4163 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4164 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4165 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4166 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4167 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4168 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4169 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4170 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4171 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4172 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4173 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4174 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4177 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4178 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4179 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4180 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4181 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4182 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4200 | `user-font-demo.4th` | `0xce1fd7d1b581a56d` | ok |
| 4201 | `user-font-demo.4th` | `0xce1fd7d1b581a56d` | ok |
| 4202 | `user-font-demo.4th` | `0xce1fd7d1b581a56d` | ok |
@@ -235,14 +232,12 @@
| 4840 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4841 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4842 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
| 4851 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4852 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4853 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4854 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4856 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4857 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4858 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4860 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
| 4851 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4852 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4853 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4854 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4856 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4860 | `artemis:init.4th` | `0x486772c973d0d087` | ok |
| 4900 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
| 4901 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
| 4902 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
@@ -384,4 +379,4 @@
None.
---
*38 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
*37 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
+2 -2
View File
@@ -49,7 +49,7 @@ Blocks: **2049–2052, 2057**
| Block | Immutable | Justification |
|-------|-----------|---------------|
| 2049 | No | Hera VM init: `VM-TREE`/`VM-PARENT`/`VM-CHILDREN`; loads `ACL.4th`, `block-acl.4th`, `zuse-eligibility.4th`, `lib.4th`. **Corrected 2026-09-19 (three times)** — first pass removed a stale listing of `compudynamics`/`common:msg`/`fleet-k`/`process` (none loaded here; two already-deleted, two stripped in the same pass, FABRIC-3.6.md tasks 0.2/0.3). Second pass (same day): `fabric.4th`/`font.4th` **removed** from this list — relocated to `hestia/init.4th` (FABRIC-3.6.md tasks 1.6/1.7), Hera no longer loads either. Third pass (2026-09-22, FABRIC-3.6.md Phase 4 Stage E): `common:messaging.4th` + `MSG-CD-INIT` **removed** — the file itself is deleted (see "Deleted capsules" below); every FORTH-owned message type was cut over to kernel-Hermes first (Phase 3) |
| 2049 | No | Hera VM init: `VM-TREE`/`VM-PARENT`/`VM-CHILDREN`; loads `ACL.4th`, `block-acl.4th`, `lib.4th` (`zuse-eligibility.4th` removed 2026-10-14 with word-ACL elevation — FABRIC-3.md §XXXVII). **Corrected 2026-09-19 (three times)** — first pass removed a stale listing of `compudynamics`/`common:msg`/`fleet-k`/`process` (none loaded here; two already-deleted, two stripped in the same pass, FABRIC-3.6.md tasks 0.2/0.3). Second pass (same day): `fabric.4th`/`font.4th` **removed** from this list — relocated to `hestia/init.4th` (FABRIC-3.6.md tasks 1.6/1.7), Hera no longer loads either. Third pass (2026-09-22, FABRIC-3.6.md Phase 4 Stage E): `common:messaging.4th` + `MSG-CD-INIT` **removed** — the file itself is deleted (see "Deleted capsules" below); every FORTH-owned message type was cut over to kernel-Hermes first (Phase 3) |
| 2050 | No | BOOT-BANNER call — separated so the banner block can be swapped without touching init logic |
| 2051 | No | TRIPOD-TEST — 6 acceptance gates per TRIPOD.md. Dead EVENT-WAIT step replaced with HERMES-TICK liveness check (`3436d564`) |
| 2052 | No | TRIPOD-TEST invocation block |
@@ -350,7 +350,7 @@ three-architecture acceptance for the deletion: `logs/20260922-181141/amd64/`,
Phase 4 entry for the full account, including one found-not-fixed collateral
defect (`SEND-ELEVATE-REQUEST`, this file's own former caller into
`zuse-eligibility.4th`'s `ELEVATE-GRANT`, is now unreachable — Captain Bob's
2026-09-22 decision: leave it, Phase 8 PKI will build its own entrypoint).
2026-09-22 decision: leave it, Phase 8 PKI will build its own entrypoint). Removed 2026-10-14: elevation was ruled not needed, and `zuse-eligibility.4th` (blocks 4021–4022), its C words and the eligibility list went with it (FABRIC-3.md §XXXVII).
Former blocks 4100–4153 (ex-Artemis-gaps) are now UNASSIGNED — see Unassigned
Ranges below. The per-block justification table this section used to carry
(arena allocator ABI, message/channel field accessors, the VM-name routing
+9 -34
View File
@@ -498,41 +498,16 @@ Block 4854
STARTUP-BANNER
Block 4856
( Storage-attach ack builder. Hera sends "<dev-addr> )
( HERA-BLK-ATTACH-REQ"; reply is "<dev-addr> <ok?> )
( BLK-ATTACH-ACK" so identity birth only proceeds once )
( storage is confirmed (repl.c's BLK-ATTACH-ACK). Bob, )
( 2026-09-07. )
CREATE ATTACH-ACK-BUF 64 ALLOT
VARIABLE ATTACH-ACK-LEN
: ACK-APPEND ( src-addr len -- )
DUP >R
ATTACH-ACK-BUF ATTACH-ACK-LEN @ +
SWAP CMOVE
ATTACH-ACK-LEN @ R> + ATTACH-ACK-LEN ! ;
Block 4857
: ACK-APPEND-CHAR ( char -- )
ATTACH-ACK-BUF ATTACH-ACK-LEN @ + C!
ATTACH-ACK-LEN @ 1+ ATTACH-ACK-LEN ! ;
: ACK-APPEND-NUM ( n -- )
0 SWAP <# #S #> ACK-APPEND ;
Block 4858
( HERA-BLK-ATTACH-REQ ( dev-addr -- ): VM-EXEC'd by MSG- )
( DELIVER when Hera's request arrives. Runs BLK-ATTACH on )
( Artemis's own dictionary, then replies to Hera via kernel- )
( Hermes's KH-BLK-ATTACH-SEND (repl.c) -- FABRIC-3.6.md task )
( 3.8 Stage C, FORTH Hermes no longer sees this message type. )
( HERA-BLK-ATTACH-REQ, dev-addr -- : Hera's storage-attach )
( request, VM-EXEC'd into Artemis. Runs BLK-ATTACH here, then )
( replies with KH-BLK-ATTACH-SEND, dev-addr ok? -- sent? : )
( the kernel writes the reply text for Hera itself, from the )
( two numbers, for Artemis only -- so identity birth only )
( proceeds once storage is confirmed. Until 2026-10-14 )
( Artemis built that text here and the word sent any text )
( from any VM, which Hera ran as Zuse: FABRIC-3.md SXXXVII. )
: HERA-BLK-ATTACH-REQ ( dev-addr -- )
0 ATTACH-ACK-LEN !
DUP ACK-APPEND-NUM
BL ACK-APPEND-CHAR
DUP BLK-ATTACH ACK-APPEND-NUM
DROP
BL ACK-APPEND-CHAR
S" BLK-ATTACH-ACK" ACK-APPEND
ATTACH-ACK-BUF ATTACH-ACK-LEN @ KH-BLK-ATTACH-SEND DROP ;
DUP BLK-ATTACH KH-BLK-ATTACH-SEND DROP ;
Block 4860
( LOG-APPEND ( level ts msg-addr msg-u -- ) )
-1
View File
@@ -15,7 +15,6 @@ Block 2049
: VM-CHILDREN ( -- ) ." (none)" CR ;
S" ACL.4th" EXEC
S" block-acl.4th" EXEC
S" zuse-eligibility.4th" EXEC
S" lib.4th" EXEC
( FABRIC-3.6.md Phase 4, Stage E: common:messaging.4th/ )
( MSG-CD-INIT removed -- no longer exists, no longer needed. )
-26
View File
@@ -1,26 +0,0 @@
Block 4021
( zuse-eligibility.4th - H.5/H.8 elevation grant )
( C prims: ZUSE-ELIGIBILITY-ADD ZUSE-ELIGIBLE? )
( NAME>XT ELEVATE-PUBKEY-UNPACK )
( Policy word this file: ELEVATE-GRANT. Runs in )
( Hera's own dict -- delivered here by kernel-Hermes's own )
( drain checkpoint (FABRIC-3.6.md task 3.10), not FORTH )
( MSG-DELIVER (messaging.4th, removed Phase 4 Stage E). )
( FABRIC-2.md H.12 step 21, 2026-09-03. )
CREATE ELEVATE-PK-BUF 32 ALLOT
Block 4022
( ELEVATE-GRANT ( waddr wu pk0 pk1 pk2 pk3 -- ) )
( Reconstructs the caller's pubkey, checks Zuse's )
( eligibility list, and on match grants the named )
( word ACL-ALLOW!/ACL-TTL! via the same reused )
( ACL-TTL mechanism H.5 decided on. No grant, no )
( error, on any mismatch -- silent refusal. )
: ELEVATE-GRANT ( waddr wu pk0 pk1 pk2 pk3 -- )
ELEVATE-PK-BUF ELEVATE-PUBKEY-UNPACK
ELEVATE-PK-BUF ZUSE-ELIGIBLE? IF
NAME>XT DUP IF
1 OVER ACL-ALLOW!
ACL-BASE-TTL SWAP ACL-TTL!
ELSE DROP THEN
ELSE 2DROP THEN ;
-4
View File
@@ -12,8 +12,6 @@ Block 4016
( >BODY-then-store, so a pinned CONSTANT isn't tamper-proof. )
( Read with ZUSE-PUBKEY@ / ZUSE-CERT-INSTALLED? -- both C )
( primitives, read-only; the seed has no FORTH access at all. )
( ZUSE-ELIGIBILITY-ADD denied by default -- see block 4017. )
0 ['] ZUSE-ELIGIBILITY-ADD ACL-ALLOW!
Block 4017
( ACL-ZUSE-BOOT ( -- ) re-invokable: capsule_zuse_boot.c )
@@ -25,8 +23,6 @@ Block 4017
: ACL-ZUSE-BOOT ( -- )
ZUSE-CERT-INSTALLED? IF
ZUSE-AUTHENTICATE
1 ['] ZUSE-ELIGIBILITY-ADD ACL-ALLOW!
['] ZUSE-ELIGIBILITY-ADD ACL-PIN
LOG-INFO" zuse: activated"
ELSE
LOG-INFO" zuse: NOT activated -- no cert installed"
+13 -1
View File
@@ -1,6 +1,18 @@
# FABRIC-3.7.md — Phase 8 PKI: the elevation entrypoint
**Status: OPEN — design only, no code written or authorized.**
**Status: CLOSED 2026-10-14 — not needed; elevation removed, not built.** Captain Bob, asked
"is it even a necessary capability?": privilege comes from the drive (Zuse's drive, or the
personality an identity is minted with), so a runtime request-and-grant channel only adds attack
surface — and the old one was a live hole: `KH-ELEVATE-SEND` let any identity have arbitrary
FORTH run as Zuse. `ELEVATE-GRANT` granted automatically on eligibility-list membership (no
human approves), into Hera's own dictionary (never the requester's), and could never reach a
FORTH-79-locked identity's words (pinned, and pins are one-way). Removed: `KH-ELEVATE-SEND`,
message type 8, `zuse-eligibility.4th`, `ZUSE-ELIGIBLE?`/`ZUSE-ELIGIBILITY-ADD`/
`ELEVATE-PUBKEY-UNPACK`, the eligibility list itself and Part B's gate in `zuse.4th`;
`devblock_from_top` 1–63 on Artemis's disk stay reserved, unused (his ruling). See
`FABRIC-3.md` §XXXVII. Everything below is kept as the record of the design.
**Former status: OPEN — design only, no code written or authorized.**
**CORRECTION (2026-09-22, before any code was written against this document): §2's central
claim — that the old `SEND-ELEVATE-REQUEST` passed a raw cross-VM address into `ELEVATE-GRANT`'s
+53
View File
@@ -5580,3 +5580,56 @@ second apart at the prompt all attach (their attach acknowledgements are message
POST 1052 tests passed, `PARITY:M7.1a ... hash=0x08873e0f44b7cb2a` unchanged, no unknown word
(`logs/20261014-103248`, `-103409`, `-103616`). Superseded attempts, kept: `-092406` (wrong `USE`
syntax), `-094908` (before the LIVE-on-yield fix, one line only).
## XXXVII. Any identity could run FORTH as Zuse; a message could run twice; elevation removed (2026-10-14)
Found while building FABRIC-3.7's elevation entrypoint, proven live, fixed test first.
**1. Two words let any VM have Hera run any text, in her own dictionary -- Zuse's.**
`KH-ELEVATE-SEND ( paddr plen -- ok? )` and `KH-BLK-ATTACH-SEND ( paddr plen -- ok? )`
(`repl.c`) are registered in every VM (`word_registry.c`), and each sent whatever text it was
handed to Hera as a kernel-Hermes message, which Hera interprets. From a WIREBIND identity's
console: `S" 777 ." KH-ELEVATE-SEND .` and `S" 778 ." KH-BLK-ATTACH-SEND .` -- Hera printed
`777` and `778` (`logs/20261014-105930`, `-110937`). A line holding `"` is run on the identity's
console VM instead of relayed (`repl.c`'s own v1 limitation), which is what reached them.
**2. One message could run twice.** `sk_hermes_drain_checkpoint()` popped a message only after
running it; a payload that runs a colon word reaches the checkpoint in `execute_colon_word()`,
which drains the same VM again -- and found the same message at the head. A relayed `T3`
(`: T3 333 . ;`) printed `333 333`; `778` above printed twice (`logs/20261014-110937`).
**3. Elevation itself: not needed (Captain Bob, 2026-10-14).** Asked whether it was even a
necessary capability, given (1): privilege comes from the drive -- Zuse's, or the personality an
identity is minted with -- and a runtime request-and-grant channel only adds attack surface.
`ELEVATE-GRANT` granted on eligibility-list membership alone (no human approves), into Hera's own
dictionary (never the requester's), and could never reach a FORTH-79-locked identity's words (all
pinned; pins are one-way). Its only caller had been gone since 2026-09-22 and nothing missed it.
`FABRIC-3.7.md` is closed as not needed.
**Fixes.**
- Removed: `KH-ELEVATE-SEND`; message type 8 (retired, not reused); `capsules/zuse-eligibility.4th`
(`ELEVATE-GRANT`, blocks 4021-4022) and its `EXEC` in `init.4th`; the C words `ZUSE-ELIGIBLE?`,
`ZUSE-ELIGIBILITY-ADD`, `ELEVATE-PUBKEY-UNPACK`; the eligibility list (`zuse_eligibility.c`,
`zuse_eligibility.h`, `zuse_eligibility_list.h`); FABRIC-3.7 Part B's gate on
`ZUSE-ELIGIBILITY-ADD` in `zuse.4th`. `devblock_from_top` 1-63 on Artemis's disk stay reserved,
unused (his ruling). `NAME>XT` stays: general-purpose.
- `KH-BLK-ATTACH-SEND` is now `( dev-addr ok? -- sent? )`, refused unless Artemis calls it; the
kernel writes `<dev-addr> <0|1> BLK-ATTACH-ACK` itself. Artemis's `HERA-BLK-ATTACH-REQ` is now
`DUP BLK-ATTACH KH-BLK-ATTACH-SEND DROP` (its text builder is gone, and with it Artemis's one
"Unterminated comment" warning, a nested-paren comment).
- `BLK-ATTACH-ACK` is refused unless Hera runs it.
- The drain pops a message before running it; the message, and so its payload, is still held
until released after.
**Verified.** The same session after the fix (`logs/20261014-112514`): `333` once,
`KH-BLK-ATTACH-SEND: refused -- only Artemis replies to a storage attach`, `UNKNOWN WORD:
'KH-ELEVATE-SEND'`, identity `00` attaches. Five identities hot-plugged at the prompt all attach
(`logs/20261014-113615`). Standard acceptance, all three ISAs: `zuse@Hera`, POST 1052 passed, no
unknown word, **`PARITY:M7.1a word_count=523 ... hash=0xb73b58052d591638` -- a deliberate new
baseline** (was 530 / `0x08873e0f44b7cb2a`: the four C words and `zuse-eligibility.4th`'s
definitions are gone from Hera's dictionary) (`logs/20261014-113112`, `-113232`, `-113450`). A first attempt (`-111859`) broke
attach two ways, both mine: nested parens in the new capsule comment, and `%lld`, which the
kernel's freestanding `snprintf` does not do.
**Found, not fixed (pre-existing, unrelated):** Hera prints two and Hestia three `( Unterminated
comment` warnings at every boot, in logs from before this work too.
+10 -14
View File
@@ -63,10 +63,10 @@
* "top-of-device, outside the user-addressable LBN pool" region
* block_subsystem.c's own meta_fence_blocks reservation (128 devblocks by
* default, BLK_META_FENCE_INIT) already carves out for exactly this kind
* of system metadata, and where Zuse's own genesis marker/eligibility list
* already live (blk_meta_zone_read()/write(), devblock_from_top 0 and 1+
* respectively). This format deliberately does NOT go through
* blk_meta_zone_*, though: that accessor requires the device to already be
* of system metadata, and where Zuse's own genesis marker already lives
* (blk_meta_zone_read()/write(), devblock_from_top 0; 1-63 reserved,
* unused, since the eligibility list was removed 2026-10-14). This
* format deliberately does NOT go through blk_meta_zone_*, though: that accessor requires the device to already be
* blk_subsys_attach_device()'d and format-detected (first_disk_slot()) --
* exactly the state generic pre-attach discovery (repl.c's idle-loop
* USB-MSC scan) doesn't have yet, which is the entire reason this format
@@ -74,11 +74,8 @@
* the same top-of-device arithmetic independently via blkio_info(), so
* they work on a raw, not-yet-attached device exactly like
* homeblocks_sig_check() already does. ARTEMIS_SIG_DEVBLOCK_FROM_TOP is
* fixed well clear of Zuse's two tenants (0 and 1+, open-ended but
* realistically small -- 127 pubkeys per chained devblock) so the two
* subsystems' independent top-of-device math can never collide, without
* this format needing to know how far the eligibility chain has actually
* grown on any given boot.
* fixed well clear of Zuse's genesis marker (0) and the reserved,
* unused 1-63.
*
* Reserves offset/size pointers to the growable per-VM log-persistence
* region (FABRIC-3.md §XXVI follow-on's own log-record work), the same way
@@ -123,11 +120,10 @@ extern "C" {
* last devblock, 1 = second-to-last, ...), computed independently here via
* blkio_info() rather than through that accessor (which needs an already-
* attached, format-detected slot this code runs before). Fixed well past
* Zuse's genesis marker (devblock_from_top 0) and eligibility list
* (devblock_from_top 1, chained upward as needed) -- see
* zuse_eligibility_list.h -- so the two subsystems' independent top-of-
* device math can never collide regardless of how large the eligibility
* chain grows in practice. Well inside BLK_META_FENCE_INIT (128 devblocks,
* Zuse's genesis marker (devblock_from_top 0) and devblock_from_top
* 1-63 -- once Zuse's eligibility list, reserved and unused since that
* list was removed with word-ACL elevation (2026-10-14, FABRIC-3.md
* SXXXVII) -- so nothing here can collide with what stood there. Well inside BLK_META_FENCE_INIT (128 devblocks,
* block_subsystem.h) on any real Artemis disk. */
#define ARTEMIS_SIG_DEVBLOCK_FROM_TOP 64u
+4 -6
View File
@@ -31,7 +31,7 @@
* only artifact both platforms share is whatever got persisted to disk.
*
* Lives in the SAME top-of-device system-metadata fence artemis_sig_t and
* Zuse's genesis marker/eligibility list already use
* Zuse's genesis marker already uses
* (block_subsystem.h's blk_meta_zone_read()/write(), devblock_from_top
* addressing) -- reached the SAME way, deliberately NOT through
* artemis_sig.c's own independent blkio_info()-based arithmetic. That
@@ -52,11 +52,9 @@
* 64 -- artemis_sig_t itself (ARTEMIS_SIG_DEVBLOCK_FROM_TOP)
* 65 -- this ring's control header (LOG_REGION_DEVBLOCK_FROM_TOP_BASE)
* 66-96 -- this ring's slot devblocks, growable up to LOG_REGION_MAX_DEVBLOCKS
* Chosen clear of Zuse's genesis marker (devblock_from_top=0) and
* eligibility list (chains upward from 1, unbounded in code -- see
* zuse_eligibility_list.h's own CORRECTION comment, updated alongside this
* file, for the honest real-world headroom that leaves: devblocks 1-63,
* ~8000 possible eligible identities before ever reaching 64).
* Chosen clear of Zuse's genesis marker (devblock_from_top=0) and of
* devblocks 1-63, reserved and unused since Zuse's eligibility list was
* removed (2026-10-14, FABRIC-3.md SXXXVII).
*
* Ring granularity is one whole devblock-quarter (LOG_SLOT_SIZE, 1024
* bytes = exactly one blkio forth-block) per record -- avoids any
+4 -14
View File
@@ -734,19 +734,9 @@ int sk_hermes_reassemble(SkHermesMessage **chunks, int n_chunks,
* that type's real cutover, not a fresh kernel-Hermes-space number. */
#define SK_HERMES_MSG_TYPE_CONSOLE_CMD 7
/* SK_HERMES_MSG_TYPE_ELEVATE_REQUEST (FABRIC-3.6.md task 3.10, Stage D)
* -- reuses messaging.4th's own now-historical ELEVATE-REQUEST value (8).
* At task 3.10, SEND-ELEVATE-REQUEST/ELEVATE-GRANT/CH-REQUEST all FIND
* live in Hera's own dictionary and SEND-ELEVATE-REQUEST was a real,
* complete, directly-callable entrypoint (H.5/H.8's own design: any VM
* wanting word-ACL elevation calls it with its own pubkey and the target
* word name). FABRIC-3.6.md Phase 4, Stage E deleted messaging.4th
* (SEND-ELEVATE-REQUEST's only definition) -- KH-ELEVATE-SEND below has
* had no FORTH caller since, and ELEVATE-GRANT (zuse-eligibility.4th,
* still loaded at boot) is unreachable. Found, not fixed -- Phase 8 PKI
* decision needed before Phase 5 close-out, see FABRIC-3.6.md's own
* Phase 4 entry. */
#define SK_HERMES_MSG_TYPE_ELEVATE_REQUEST 8
/* Type 8 (ELEVATE-REQUEST, FABRIC-3.6.md task 3.10) is retired: word-ACL
* elevation and Zuse's eligibility list were removed 2026-10-14 (FABRIC-3.7,
* closed as not needed; FABRIC-3.md SXXXVII). Not reused. */
/* The five negotiation types above (20-24) were chosen clear of
* messaging.4th's own live/reserved type space (PAUSE-EVENT=2,
@@ -754,7 +744,7 @@ int sk_hermes_reassemble(SkHermesMessage **chunks, int n_chunks,
* BLK-ATTACH-EVENT=9, MSG-NACKED=253, MSG-DELIVERED=255) precisely
* because none of them had a real cutover yet -- kernel-Hermes stayed
* its own inert, parallel type space for those. SK_HERMES_MSG_TYPE_
* BLK_ATTACH, _CONSOLE_CMD, and _ELEVATE_REQUEST above are the
* BLK_ATTACH and _CONSOLE_CMD above (and the retired 8) are the
* deliberate exceptions: tasks 3.8/3.9/3.10 ARE those types' real
* cutovers, so they reuse the FORTH-side value rather than picking a
* fresh one. */
@@ -1,56 +0,0 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/**
* zuse_eligibility.h - Read/add/membership-check over the on-disk
* elevation eligibility list (FABRIC-2.md §H.5/§H.12 Phase 6,
* zuse_eligibility_list.h's zuse_eligibility_devblock_t chain). Zuse
* checks zuse_eligibility_is_member() before honoring any
* ELEVATE-REQUEST (§H.7/§H.8) -- a gating layer on top of the
* message-based elevation trigger, not a replacement for it. Adding an
* entry (zuse_eligibility_add()) is meant to be reachable only from a
* Zuse-only FORTH word (§H.12 item 19, gated by zuse_session), not
* called directly from arbitrary session code.
*/
#ifndef STARKERNEL_ZUSE_ELIGIBILITY_H
#define STARKERNEL_ZUSE_ELIGIBILITY_H
#ifdef __STARKERNEL__
#include <stdint.h>
/**
* zuse_eligibility_is_member - Check whether pubkey appears anywhere in
* the eligibility list chain. Fail-closed: an empty/nonexistent list, a
* corrupt/foreign devblock encountered mid-chain, or any I/O error all
* read as "not eligible" (0), never "eligible" -- this gates elevation,
* so an unreadable list must never be treated as permissive.
*
* @param pubkey 32-byte Ed25519 public key to look up.
* @return 1 if found, 0 otherwise (including on any error).
*/
int zuse_eligibility_is_member(const uint8_t pubkey[32]);
/**
* zuse_eligibility_add - Add pubkey to the eligibility list, creating
* the list's head devblock if it doesn't exist yet and chaining a fresh
* devblock onto the tail if the current tail is full. Idempotent: adding
* an already-present pubkey is a no-op success, not a duplicate entry.
*
* @param pubkey 32-byte Ed25519 public key to add.
* @return 0 on success (added, or already present), -1 on
* failure (fence write failed, fence budget exhausted, or
* a corrupt devblock was encountered mid-chain).
*/
int zuse_eligibility_add(const uint8_t pubkey[32]);
#endif /* __STARKERNEL__ */
#endif /* STARKERNEL_ZUSE_ELIGIBILITY_H */
@@ -1,99 +0,0 @@
/*
* zuse_eligibility_list.h -- on-disk record format for Zuse's word/block
* elevation eligibility list (FABRIC-2.md §H.5/§H.12 Phase 6): a simple
* growable list of owner_pubkey[32] entries, no extra per-entry metadata
* ("simple list, no extra metadata, unless we find a reason this won't
* work"). Zuse checks this list before honoring any ELEVATE-REQUEST
* (§H.7/§H.8) -- gating layer on top of the message-based elevation
* trigger, not a replacement for it.
*
* Lives in the same top-of-device system-metadata fence as
* zuse_genesis_marker_t (block_subsystem.h's blk_meta_zone_read()/
* write(), Phase 8/§C), one slot over: the genesis marker owns
* devblock_from_top=0, this list starts at devblock_from_top=1. Growable
* across multiple devblocks via a singly-linked chain (each devblock's
* `next_devblock_from_top` points at the next one, ZULIST_NO_NEXT means
* "this is the last devblock in the chain") -- entries are appended by
* filling the current tail devblock, then chaining a fresh one out of the
* already-reserved BLK_META_FENCE_INIT budget once it's full. No fence
* growth logic is needed yet.
*
* CORRECTION (FABRIC-3.md SXXVI follow-on, Step 4, 2026-09-13): the fence
* is no longer this list's alone to grow into. artemis_sig_t now owns
* devblock_from_top=64 (include/starkernel/artemis_sig.h), and the
* per-VM log-persistence ring now owns devblock_from_top 65-96
* (include/starkernel/log_region.h) -- both fixed, compile-time
* constants, chosen deliberately clear of this list's own growth
* direction (chaining upward from devblock_from_top=1). This list's real
* remaining headroom is devblocks 1-63 (not "126 more slots" as this
* comment used to say): 63 devblocks x ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK
* (127) = 8001 possible eligible identities before ever reaching
* devblock_from_top=64 -- vastly beyond any plausible real deployment of
* this project, but genuinely unenforced: there is no code-level ceiling
* stopping this chain from growing into devblock 64+ if that number were
* ever actually approached. Noted here, not silently assumed safe, after
* a fixed-offset collision was caught and fixed once already this session
* (Artemis's own disk vs. its live BAM, artemis_sig.h's own CORRECTION
* comment) -- the lesson being to state the real bound in writing rather
* than trust "it'll never get that big."
*
* Raw, unpacked 4 KiB devblock -- same convention as
* zuse_genesis_marker_t/homeblocks_sig_t: real CRC from day one, this
* gates a real security check, not a placeholder.
*/
#ifndef STARKERNEL_ZUSE_ELIGIBILITY_LIST_H
#define STARKERNEL_ZUSE_ELIGIBILITY_LIST_H
#include <stdint.h>
/* Packed via shifts, not a hand-computed hex literal -- see
* zuse_genesis_marker.h's own note on this project's standing
* numeric-constant convention. */
#define ZUSE_ELIGIBILITY_LIST_MAGIC \
((uint32_t)'Z' | ((uint32_t)'E' << 8) | ((uint32_t)'L' << 16) | ((uint32_t)'G' << 24))
#define ZUSE_ELIGIBILITY_LIST_VERSION 1u
/* devblock_from_top of this list's head devblock -- one past the genesis
* marker's devblock_from_top=0. */
#define ZUSE_ELIGIBILITY_LIST_HEAD_DEVBLOCK 1u
/* next_devblock_from_top value meaning "no further devblock -- this is
* the tail of the chain." 0xFFFFFFFF can never be a real
* devblock_from_top (BLK_META_FENCE_INIT is 128), so it is unambiguous. */
#define ZUSE_ELIGIBILITY_LIST_NO_NEXT 0xFFFFFFFFu
/* How many owner_pubkey[32] entries fit in one devblock alongside the
* header/crc/next-pointer overhead below. */
#define ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK 127u
typedef struct {
uint32_t magic; /* ZUSE_ELIGIBILITY_LIST_MAGIC; anything else on
* the head devblock means "list not created
* yet" (blank/foreign bytes), not a
* format-corruption error. A non-head devblock
* is only ever read by following a real
* next_devblock_from_top link, so its own
* magic is still checked the same way to catch
* a corrupt/foreign devblock mid-chain. */
uint32_t version; /* ZUSE_ELIGIBILITY_LIST_VERSION */
uint32_t count; /* Number of valid entries in this devblock's
* own entries[] (0..ZUSE_ELIGIBILITY_ENTRIES_
* PER_DEVBLOCK), not a running total across the
* whole chain. */
uint32_t next_devblock_from_top; /* Next devblock in the chain, or
* ZUSE_ELIGIBILITY_LIST_NO_NEXT if this is the
* tail. */
uint8_t entries[ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK][32]; /* Ed25519
* public keys eligible for elevation. Only the
* first `count` entries are meaningful. */
uint64_t crc; /* CRC-64/ISO (block_subsystem.h's
* compute_crc64()) over every byte of this
* struct up to (not including) this field. */
uint8_t _pad[4096 - (4 + 4 + 4 + 4 + (ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK * 32) + 8)];
} zuse_eligibility_devblock_t;
_Static_assert(sizeof(zuse_eligibility_devblock_t) == 4096,
"zuse_eligibility_devblock_t must be exactly one 4 KiB devblock");
#endif /* STARKERNEL_ZUSE_ELIGIBILITY_LIST_H */
-106
View File
@@ -49,7 +49,6 @@
#include "starkernel/capsule_mint.h"
#include "starkernel/user_identity_seed.h"
#include "starkernel/vm_identity.h"
#include "starkernel/zuse_eligibility.h"
#include "starkernel/capsule_loader.h"
#include "starkernel/capsule_run.h"
#include "starkernel/capsule_loader.h"
@@ -1345,70 +1344,6 @@ static void mama_word_mint_scratch_emit(VM *vm)
vm_push(vm, 1);
}
/**
* @brief ZUSE-ELIGIBILITY-ADD ( c-addr -- ok? )
* Add the 32-byte Ed25519 public key at c-addr to Zuse's elevation
* eligibility list (FABRIC-2.md §H.5/§H.12 item 19). Plain, unconditional
* primitive -- no authorization check here or anywhere else in this
* codebase gates on vm->zuse_session. Zuse's authority is the *absence*
* of any ACL restricting her, not a bit this or any other word checks;
* restricting who may call this word, if ever wanted, is the same
* standing word-level ACL mechanism (acl_allow/ACL-PIN in ACL.4th) any
* other word would use, applied later if and when actually needed --
* not invented here.
*/
static void mama_word_zuse_eligibility_add(VM *vm)
{
if (vm->dsp < 0) {
console_println("ZUSE-ELIGIBILITY-ADD: stack underflow");
vm->error = 1;
vm_push(vm, 0);
return;
}
cell_t caddr = vm_pop(vm);
const uint8_t *pubkey = vm_ptr(vm, (vaddr_t)caddr);
if (!pubkey) {
console_println("ZUSE-ELIGIBILITY-ADD: invalid address on the stack");
vm->error = 1;
vm_push(vm, 0);
return;
}
if (zuse_eligibility_add(pubkey) != 0) {
console_println("ZUSE-ELIGIBILITY-ADD: FAILED -- fence write error");
vm_push(vm, 0);
return;
}
vm_push(vm, 1);
}
/**
* @brief ZUSE-ELIGIBLE? ( c-addr -- flag )
* Membership check over Zuse's elevation eligibility list (FABRIC-2.md
* §H.5/§H.12 item 21). Plain, unconditional wrapper over
* zuse_eligibility_is_member() -- same "no bespoke gate" convention as
* ZUSE-ELIGIBILITY-ADD above; is_member() itself is already fail-closed.
*/
static void mama_word_zuse_eligible_query(VM *vm)
{
if (vm->dsp < 0) {
log_message(LOG_ERROR, "ZUSE-ELIGIBLE?: stack underflow");
vm->error = 1;
vm_push(vm, 0);
return;
}
cell_t caddr = vm_pop(vm);
const uint8_t *pubkey = vm_ptr(vm, (vaddr_t)caddr);
if (!pubkey) {
log_message(LOG_ERROR, "ZUSE-ELIGIBLE?: invalid address on the stack");
vm->error = 1;
vm_push(vm, 0);
return;
}
vm_push(vm, zuse_eligibility_is_member(pubkey) ? -1 : 0);
}
/**
* @brief NAME>XT ( c-addr u -- xt|0 )
* Dynamic dictionary lookup for a name already sitting in a data buffer
@@ -1444,41 +1379,6 @@ static void mama_word_name_to_xt(VM *vm)
vm_push(vm, (cell_t)(uintptr_t)e);
}
/**
* @brief ELEVATE-PUBKEY-UNPACK ( pk0 pk1 pk2 pk3 buf-addr -- )
* Reconstructs a 32-byte Ed25519 pubkey from 4 cells into the 32-byte
* buffer at buf-addr, matching ZUSE-PUBKEY@'s own convention exactly:
* chunk i (0..3) is the 8-byte little-endian encoding of pubkey bytes
* [i*8 .. i*8+7] -- this is that packing's inverse, so a pubkey sent as
* 4 cells (the convention SEND-ELEVATE-REQUEST used before it was
* deleted with messaging.4th, FABRIC-3.6.md Phase 4 Stage E) round-trips
* byte-exact regardless of any individual chunk's sign bit.
*/
static void mama_word_elevate_pubkey_unpack(VM *vm)
{
if (vm->dsp < 4) {
log_message(LOG_ERROR, "ELEVATE-PUBKEY-UNPACK: stack underflow");
vm->error = 1;
return;
}
cell_t buf_addr = vm_pop(vm);
cell_t pk[4];
for (int i = 3; i >= 0; i--) pk[i] = vm_pop(vm);
uint8_t *buf = vm_ptr(vm, (vaddr_t)buf_addr);
if (!buf) {
log_message(LOG_ERROR, "ELEVATE-PUBKEY-UNPACK: invalid address on the stack");
vm->error = 1;
return;
}
for (int i = 0; i < 4; i++) {
uint64_t chunk = (uint64_t)pk[i];
for (int b = 0; b < 8; b++) {
buf[i * 8 + b] = (uint8_t)(chunk >> (8 * b));
}
}
}
/**
* @brief RUNCAP-TEST ( caddr u -- ok? rc )
* Diagnostic-only word (FABRIC-2.md §F.6/§F.18): calls
@@ -2375,10 +2275,7 @@ void register_mama_forth_words(VM *vm)
register_word(vm, "WORKER-BIRTH", mama_word_worker_birth);
register_word(vm, "UNATTENDED-BIRTH", mama_word_unattended_birth);
register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach);
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
register_word(vm, "ZUSE-ELIGIBLE?", mama_word_zuse_eligible_query);
register_word(vm, "NAME>XT", mama_word_name_to_xt);
register_word(vm, "ELEVATE-PUBKEY-UNPACK", mama_word_elevate_pubkey_unpack);
register_word(vm, "RUNCAP-TEST", mama_word_runcap_test);
register_word(vm, "PAIR-TEST", mama_word_pair_test);
register_word(vm, "MAMA-VM-ID", mama_word_mama_vm_id);
@@ -2440,10 +2337,7 @@ void register_mama_forth_words(VM *vm)
register_word(vm, "WORKER-BIRTH", mama_word_worker_birth);
register_word(vm, "UNATTENDED-BIRTH", mama_word_unattended_birth);
register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach);
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
register_word(vm, "ZUSE-ELIGIBLE?", mama_word_zuse_eligible_query);
register_word(vm, "NAME>XT", mama_word_name_to_xt);
register_word(vm, "ELEVATE-PUBKEY-UNPACK", mama_word_elevate_pubkey_unpack);
register_word(vm, "RUNCAP-TEST", mama_word_runcap_test);
register_word(vm, "PAIR-TEST", mama_word_pair_test);
register_word(vm, "MAMA-VM-ID", mama_word_mama_vm_id);
-107
View File
@@ -1,107 +0,0 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
#ifndef __STARKERNEL__
#error "zuse_eligibility.c is kernel-only"
#endif
#include "starkernel/zuse_eligibility.h"
#include "starkernel/zuse_eligibility_list.h"
#include "block_subsystem.h" /* compute_crc64(), blk_meta_zone_read/write */
#include <string.h>
#include <stddef.h>
/* Read and validate one devblock of the chain. Mirrors
* capsule_zuse_boot.c's genesis_marker_read() convention exactly: magic
* mismatch, version mismatch, and CRC mismatch are all merged into a
* single -1 ("treat as absent/corrupt, not present") -- the fence region
* is never zeroed at format time (block_subsystem.c's blk_commit_format()
* only zeroes BAM/reloc), so blank foreign bytes and genuine corruption
* are indistinguishable and must be handled identically here. */
static int list_devblock_read(uint32_t devblock_from_top, zuse_eligibility_devblock_t *out) {
if (blk_meta_zone_read(devblock_from_top, (uint8_t *)out) != 0) return -1;
if (out->magic != ZUSE_ELIGIBILITY_LIST_MAGIC) return -1;
if (out->version != ZUSE_ELIGIBILITY_LIST_VERSION) return -1;
if (out->count > ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK) return -1;
uint64_t want_crc = compute_crc64((const uint8_t *)out,
offsetof(zuse_eligibility_devblock_t, crc));
if (want_crc != out->crc) return -1;
return 0;
}
static int list_devblock_write(uint32_t devblock_from_top, zuse_eligibility_devblock_t *blk) {
blk->crc = compute_crc64((const uint8_t *)blk, offsetof(zuse_eligibility_devblock_t, crc));
return blk_meta_zone_write(devblock_from_top, (const uint8_t *)blk) == 0 ? 0 : -1;
}
int zuse_eligibility_is_member(const uint8_t pubkey[32]) {
uint32_t devblock = ZUSE_ELIGIBILITY_LIST_HEAD_DEVBLOCK;
zuse_eligibility_devblock_t blk;
for (;;) {
if (list_devblock_read(devblock, &blk) != 0) return 0; /* fail-closed */
for (uint32_t i = 0; i < blk.count; i++) {
if (memcmp(blk.entries[i], pubkey, 32) == 0) return 1;
}
if (blk.next_devblock_from_top == ZUSE_ELIGIBILITY_LIST_NO_NEXT) return 0;
devblock = blk.next_devblock_from_top;
}
}
int zuse_eligibility_add(const uint8_t pubkey[32]) {
uint32_t devblock = ZUSE_ELIGIBILITY_LIST_HEAD_DEVBLOCK;
zuse_eligibility_devblock_t blk;
if (list_devblock_read(devblock, &blk) != 0) {
/* List doesn't exist yet (blank fence, or this is genuinely the
* first entry ever added) -- create the head devblock fresh. */
memset(&blk, 0, sizeof(blk));
blk.magic = ZUSE_ELIGIBILITY_LIST_MAGIC;
blk.version = ZUSE_ELIGIBILITY_LIST_VERSION;
blk.count = 1;
blk.next_devblock_from_top = ZUSE_ELIGIBILITY_LIST_NO_NEXT;
memcpy(blk.entries[0], pubkey, 32);
return list_devblock_write(devblock, &blk);
}
/* Walk the existing chain: bail out early (idempotent success) if
* already present, otherwise track the tail devblock to append to. */
for (;;) {
for (uint32_t i = 0; i < blk.count; i++) {
if (memcmp(blk.entries[i], pubkey, 32) == 0) return 0; /* already eligible */
}
if (blk.next_devblock_from_top == ZUSE_ELIGIBILITY_LIST_NO_NEXT) break;
devblock = blk.next_devblock_from_top;
if (list_devblock_read(devblock, &blk) != 0) return -1; /* corrupt mid-chain */
}
if (blk.count < ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK) {
memcpy(blk.entries[blk.count], pubkey, 32);
blk.count++;
return list_devblock_write(devblock, &blk);
}
/* Tail devblock is full -- chain a fresh one drawn from the
* already-reserved fence budget, one devblock_from_top past the
* current tail (blk_meta_zone_write() itself refuses any offset at
* or beyond the on-disk meta_fence_blocks reservation, so an
* exhausted budget surfaces here as an ordinary write failure). */
uint32_t new_devblock = devblock + 1;
zuse_eligibility_devblock_t new_blk;
memset(&new_blk, 0, sizeof(new_blk));
new_blk.magic = ZUSE_ELIGIBILITY_LIST_MAGIC;
new_blk.version = ZUSE_ELIGIBILITY_LIST_VERSION;
new_blk.count = 1;
new_blk.next_devblock_from_top = ZUSE_ELIGIBILITY_LIST_NO_NEXT;
memcpy(new_blk.entries[0], pubkey, 32);
if (list_devblock_write(new_devblock, &new_blk) != 0) return -1;
blk.next_devblock_from_top = new_devblock;
return list_devblock_write(devblock, &blk);
}
+2 -3
View File
@@ -96,9 +96,8 @@ static int log_region_write_slot(uint32_t slot_index, const log_slot_t *slot) {
* granularity, and a devblock holds LOG_SLOTS_PER_DEVBLOCK slots, so a
* single-slot write must preserve its siblings. A read failure on a
* never-yet-written devblock (blank fence content) is not fatal here --
* proceed with a zeroed buffer, matching zuse_eligibility.c's own
* "blank fence content is indistinguishable from absent, treat as
* absent" discipline. */
* proceed with a zeroed buffer: blank fence content is
* indistinguishable from absent, and is treated as absent. */
if (blk_meta_zone_read(dft, devblock_buf) != 0) {
memset(devblock_buf, 0, sizeof(devblock_buf));
}
+51 -94
View File
@@ -260,6 +260,15 @@ static void sk_word_blk_attach_ack(VM *vm) {
cell_t dev_addr = vm_pop(vm);
blkio_dev_t *dev = (blkio_dev_t *)(uintptr_t)dev_addr;
/* Hera's own, run from Artemis's reply (2026-10-14): every VM has this
* word (word_registry.c), and it births identities -- from any other
* VM it is refused (FABRIC-3.md SXXXVII). */
if (vm != (VM *)sk_get_mama_vm()) {
console_println("BLK-ATTACH-ACK: refused -- Hera's own, from Artemis's reply");
vm->error = 1;
return;
}
if (!g_usb_blk_dev_slots || !g_blk_attach_pending) return;
uint32_t found_slot = 0;
@@ -359,11 +368,9 @@ static void sk_word_blk_attach_ack(VM *vm) {
* message type that actually flowed through a messaging layer.
*
* sk_hermes_drain_checkpoint() (task 3.4) calls vm_interpret() directly
* on a message's payload_addr, so it must be NUL-terminated -- Artemis's
* own ATTACH-ACK-BUF (artemis:init.4th) is a raw CMOVE'd byte buffer
* with no such guarantee, so this copies into its own NUL-terminated
* buffer rather than passing ATTACH-ACK-BUF's address through
* unchanged.
* on a message's payload_addr, so it must be NUL-terminated; since
* 2026-10-14 the kernel writes the whole reply text into this buffer
* itself (KH-BLK-ATTACH-SEND, below), from two numbers.
*
* CORRECTED 2026-09-22 (real defect, this task's own findings log):
* this comment used to claim "static, not stack-local" was load-bearing
@@ -383,47 +390,47 @@ static void sk_word_blk_attach_ack(VM *vm) {
#define SK_KH_BLK_ATTACH_BUF_SIZE 80
static char g_kh_blk_attach_buf[SK_KH_BLK_ATTACH_BUF_SIZE];
/* KH-BLK-ATTACH-SEND ( paddr plen -- ok? ): copies the caller's payload
* (Artemis's own ATTACH-ACK-BUF content) into the NUL-terminated buffer
* above and sends it to Hera via kernel-Hermes. `from`/`to` are derived
* from the calling VM and sk_get_mama_vm() -- the caller never has to
* name a VMUuid, matching how HERA-BLK-ATTACH-REQ never had to before
* either (FORTH's own IDX 0/1/2 convention did that translation for
* it). Refusal (reservoir/arena/destination-queue exhaustion) is logged
* rather than silently dropped -- FABRIC-3.5.md SXXXV.0 names silent
* failure as this project's single most common defect shape, and this
* is exactly the class of message (identity birth gates on it) where a
* silent drop would be expensive to ever notice. */
/* KH-BLK-ATTACH-SEND ( dev-addr ok? -- sent? ): Artemis's storage-attach
* reply to Hera. The kernel builds the message text itself --
* "<dev-addr> <ok?> BLK-ATTACH-ACK" -- from the two numbers, and only for
* Artemis. Until 2026-10-14 it took ( paddr plen ), any text at all, from
* any VM, and Hera ran that text in her own dictionary, Zuse's: a
* WIREBIND identity could have any FORTH run as Zuse (shown live,
* FABRIC-3.md SXXXVII). `from`/`to` are derived from the calling VM and
* sk_get_mama_vm() -- the caller never names a VMUuid. Refusal
* (reservoir/arena/destination-queue exhaustion) is logged rather than
* silently dropped -- FABRIC-3.5.md SXXXV.0 names silent failure as this
* project's single most common defect shape, and identity birth gates on
* this message. */
static void sk_word_kh_blk_attach_send(VM *vm) {
VMRegistryEntry artemis;
VM *mama_vm = (VM *)sk_get_mama_vm();
cell_t ok_flag, dev_addr;
int n, rc;
if (vm->dsp < 1) {
log_message(LOG_ERROR, "KH-BLK-ATTACH-SEND: stack underflow");
console_println("KH-BLK-ATTACH-SEND: expects dev-addr ok? -- nothing on the stack");
vm->error = 1;
return;
}
cell_t plen = vm_pop(vm);
cell_t paddr = vm_pop(vm);
/* paddr is a VM-relative offset (vaddr_t), not a host pointer --
* CLAUDE.md's own "Important Conventions": "Stack values are VM
* offsets (vaddr_t), not C pointers -- use VM_ADDR()/CELL()".
* mama_forth_words.c's own VM-EXEC/VM-CALL words all translate a
* popped paddr the same way (vm_ptr(vm, (vaddr_t)caddr)) before
* touching it as a C pointer -- found live, not assumed: the first
* cut here raw-cast paddr directly and silently read all-zero
* memory (some unmapped/zeroed low address), producing an empty
* NUL-terminated payload that vm_interpret() executed as a no-op --
* no crash, no error, just a message that arrived and did nothing.
* Diagnosed via a temporary probe printing the copied buffer content
* at send time (per feedback_revert_probes_after_capture); reverted
* once this fix confirmed correct. */
const char *src = (const char *)vm_ptr(vm, (vaddr_t)paddr);
size_t n = (size_t)plen;
VM *mama_vm = (VM *)sk_get_mama_vm();
size_t i;
int rc;
ok_flag = vm_pop(vm);
dev_addr = vm_pop(vm);
if (n >= SK_KH_BLK_ATTACH_BUF_SIZE) n = SK_KH_BLK_ATTACH_BUF_SIZE - 1;
for (i = 0; i < n; i++) g_kh_blk_attach_buf[i] = src[i];
g_kh_blk_attach_buf[n] = '\0';
if (capsule_vm_find_by_name_nocase("Artemis", &artemis) != 0 ||
artemis.vm_ptr != (void *)vm) {
console_println("KH-BLK-ATTACH-SEND: refused -- only Artemis replies to a storage attach");
vm->error = 1;
return;
}
n = snprintf(g_kh_blk_attach_buf, sizeof(g_kh_blk_attach_buf),
"%llu %u BLK-ATTACH-ACK",
(unsigned long long)(uint64_t)dev_addr, ok_flag ? 1u : 0u);
if (n <= 0 || (size_t)n >= sizeof(g_kh_blk_attach_buf)) {
console_println("KH-BLK-ATTACH-SEND: FAILED -- reply does not fit");
vm_push(vm, 0);
return;
}
rc = sk_hermes_send_one(vm->stadium_vm_id, mama_vm->stadium_vm_id,
SK_HERMES_MSG_TYPE_BLK_ATTACH, 0,
@@ -434,64 +441,14 @@ static void sk_word_kh_blk_attach_send(VM *vm) {
vm_push(vm, rc == 0 ? 1 : 0);
}
/* FABRIC-3.6.md task 3.10 (Stage D): sized to match messaging.4th's own
* ELEVATE-REQ-BUF (256 bytes) -- the FORTH-side sender never builds a
* longer payload than that buffer allows, so this is a safe match, not
* an arbitrary choice. static, not stack-local: safe now that
* sk_hermes_send_one() copies into the message's own storage (the
* payload-aliasing fix landed before this task started) -- no lifetime
* caveat to carry forward this time, unlike g_kh_blk_attach_buf/
* g_kh_console_cmd_buf's own now-corrected history. */
#define SK_KH_ELEVATE_BUF_SIZE 256
static char g_kh_elevate_buf[SK_KH_ELEVATE_BUF_SIZE];
/* KH-ELEVATE-SEND ( paddr plen -- ok? ): built for messaging.4th's own
* SEND-ELEVATE-REQUEST (a FORTH command string calling ELEVATE-GRANT,
* zuse-eligibility.4th block 4022), which handed its payload here
* instead of CH-REQUEST's old COMMON-CH/MSG-SEND path. `from`/`to` are
* derived the same way KH-BLK-ATTACH-SEND's own already does (the
* calling VM and sk_get_mama_vm() -- ELEVATE-GRANT always runs on Hera,
* per zuse-eligibility.4th's own header comment) -- the caller never
* supplies either, which is a real correctness improvement over the
* FORTH path it replaced: CH-REQUEST's own "initiator-only gate...
* refuses if from doesn't match MY-CH-ID" existed only because a caller
* COULD claim to be a different VM by passing the wrong stack value;
* deriving `from` from the C-level calling VM's own identity makes that
* spoof structurally impossible rather than merely gated. FABRIC-3.6.md
* Phase 4, Stage E deleted messaging.4th -- SEND-ELEVATE-REQUEST no
* longer exists anywhere, so this word currently has no FORTH caller.
* Found, not fixed; see FABRIC-3.6.md's own Phase 4 entry. */
static void sk_word_kh_elevate_send(VM *vm) {
if (vm->dsp < 1) {
log_message(LOG_ERROR, "KH-ELEVATE-SEND: stack underflow");
vm->error = 1;
return;
}
cell_t plen = vm_pop(vm);
cell_t paddr = vm_pop(vm);
const char *src = (const char *)vm_ptr(vm, (vaddr_t)paddr);
size_t n = (size_t)plen;
VM *mama_vm = (VM *)sk_get_mama_vm();
size_t i;
int rc;
if (n >= SK_KH_ELEVATE_BUF_SIZE) n = SK_KH_ELEVATE_BUF_SIZE - 1;
for (i = 0; i < n; i++) g_kh_elevate_buf[i] = src[i];
g_kh_elevate_buf[n] = '\0';
rc = sk_hermes_send_one(vm->stadium_vm_id, mama_vm->stadium_vm_id,
SK_HERMES_MSG_TYPE_ELEVATE_REQUEST, 0,
g_kh_elevate_buf, (uint32_t)(n + 1));
if (rc != 0) {
log_message(LOG_ERROR, "KH-ELEVATE-SEND: kernel-Hermes refused the send");
}
vm_push(vm, rc == 0 ? 1 : 0);
}
/* KH-ELEVATE-SEND was here until 2026-10-14: it sent any text to Hera as
* an ELEVATE-REQUEST, and Hera ran it as Zuse. Removed with word-ACL
* elevation itself (FABRIC-3.7, closed as not needed; FABRIC-3.md
* SXXXVII). */
void sk_repl_register_words(VM *vm) {
register_word(vm, "BLK-ATTACH-ACK", sk_word_blk_attach_ack);
register_word(vm, "KH-BLK-ATTACH-SEND", sk_word_kh_blk_attach_send);
register_word(vm, "KH-ELEVATE-SEND", sk_word_kh_elevate_send);
}
/*===========================================================================
+7 -21
View File
@@ -656,26 +656,13 @@ int sk_hermes_drain_checkpoint(VM *vm) {
console_println(line);
}
/* FABRIC-3.6.md task 3.10 evidence: ELEVATE-REQUEST also has no fixed
* C-side handler word -- its payload is a FORTH command string
* calling ELEVATE-GRANT (zuse-eligibility.4th), interpreted as-is
* below, same shape as CONSOLE-CMD-EVENT above. Same mid-hold-instant
* evidence, same reasoning; `self` here is always Hera (`vm`'s own
* caller already resolved it that way in KH-ELEVATE-SEND, since
* ELEVATE-GRANT only ever runs on Hera). */
if (msg->type == SK_HERMES_MSG_TYPE_ELEVATE_REQUEST) {
uint64_t held, pulled, returned, consumed;
char line[160];
int conserved = stadium_conserved(self);
sk_hermes_ledger(&held, &pulled, &returned, &consumed);
snprintf(line, sizeof(line),
"Kernel-Hermes ELEVATE-REQUEST (real, Stage D): ledger held=%llu "
"pulled=%llu returned=%llu consumed=%llu stadium_conserved(self)=%s",
(unsigned long long)held, (unsigned long long)pulled,
(unsigned long long)returned, (unsigned long long)consumed,
conserved ? "true" : "FALSE");
console_println(line);
}
/* Off the queue BEFORE it runs (2026-10-14): running it can reach a
* colon-word checkpoint that drains this same VM again, and that
* drain must find the next message, not this one -- popped only after,
* a message whose payload calls a colon word ran twice (FABRIC-3.md
* SXXXVII). The message itself, and so its payload bytes, is still
* held until sk_hermes_release() below. */
sk_hermes_pending_pop(self);
vm->mode = MODE_INTERPRET; /* a payload is never compiled into whatever
* the enclosing context was mid-defining */
@@ -691,7 +678,6 @@ int sk_hermes_drain_checkpoint(VM *vm) {
vm->abort_requested = saved_abort;
sk_hermes_release(msg);
sk_hermes_pending_pop(self);
return drain_error ? -1 : 1;
}
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
+5 -4
View File
@@ -255,10 +255,11 @@ typedef struct {
* vm.c:611-624's exact pattern, applied to a
* block instead of a word. */
uint32_t acl_ttl; /* countdown, same shape as DictEntry's
* acl_ttl -- blocks support temporary
* elevation too, same ACL-TTL-reuse
* mechanism and Zuse-eligibility-list gating
* as the word card (§H.5). */
* acl_ttl, the same ACL-TTL mechanism as
* the word card (§H.5). (Elevation on
* request, and the Zuse eligibility list
* that gated it, were removed 2026-10-14 --
* FABRIC-3.md SXXXVII.) */
uint8_t acl_reserved[3]; /* still genuinely undecided -- deliberate
* slack per "flexibility until we understand
* the recipe," not a placeholder to fill