docs(v4.0.0): storage -- ruled: a static view over a shifting chain, the kernel's mapper, private drives

MESH.md section 8 is no longer a proposal.  Ten rulings (Captain Bob,
2026-10-06), the design of step 6 as approved, what it leaves out, and
one case left open.  Step 6a is added for chains that change while
running; its acceptance is still to be approved.

V3-PARITY.md 1d stands: the mapper is the kernel's block subsystem.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-06 19:04:51 -04:00
co-authored by Claude Opus 5.5
parent 348eed7fa1
commit 8cacfb663c
+151 -15
View File
@@ -270,22 +270,144 @@ kernel-Hermes's work in v3 and is not decided for the mesh.
## 8. Storage
**Proposal.** A disk is a device on a port. Asking for a block is a
message to it — read block *n*, or write block *n* with 1024 characters —
and the answer is a message back; a block is exactly the most a message
carries. Under v3's one block-number space, each device has its own range
of numbers.
**Ruled 2026-10-06** (Captain Bob), in the order the rulings were given.
What is marked *step 6* is built there; what is marked *step 6a* is ruled
and waits for that step.
- A node wired to a disk asks it directly.
- A node not wired to one asks through the node that is: the message is
forwarded like any other. So the common SSD is shared by every node that
has a way to the node that holds it.
- A node with its own drive has that device on one of its own ports.
- A node with no way to any disk has no storage, and `BLOCK` says so.
### 8.1 The rulings
Who may have which block is the block card's, decided where the disk is,
under the identity in the message's ACL tag (`V3-PARITY.md` 1d). Not
checked in this step.
1. **A disk is a device on a port that speaks messages.** Asking for a
block is a message — read block *n*, or write block *n* with 1024
characters — and the answer is a message back.
2. **A node only ever asks for a block by number, and knows nothing else.**
It has one way to storage, as it has routes: a port, or nothing. This
replaces the earlier proposal that a node be told ranges of block
numbers. Nobody chooses ranges.
3. **Two numbers.** A logical block number is what `BLOCK n` and capsules
use. A physical block number is a place on a real device. A mapper
stands between them.
4. **The view is static; reality shifts to keep it so.** A logical number
always means the same block. Devices chain one after another in physical
space, and that chain changes as devices come and go; the mapper moves
data and changes its map so that the logical view does not change. All
the user sees change is how much storage there is.
5. **One metadata format for every block device** — a cloud store, a swap
file, an SSD, a USB drive, a thumbdrive, and any other within reason.
It is v3's (`v3/include/block_subsystem.h`): the `STFR` version 2 header,
the allocation map, the relocation table, and a card for every block
(`blk_meta_t`). A disk v3 formatted reads in v4.
6. **Plus an identity.** *(step 6a)* Two fields are added in the header's
spare space, as v3 added its relocation and fence fields: the device's
identity, and the identity of the chain it belongs to. A v3 disk reads
zero there: not yet given one. With them a device is known when it
returns, wherever it is plugged in.
7. **The mapper is the kernel's.** `V3-PARITY.md` section 1d stands: the
device chain, the metadata, first-touch claim, ACL, migration and the
Stadium touch stay in the kernel's block subsystem, which is v3's C
code. What a node finds on a storage port is that subsystem, speaking
messages in logical block numbers. (The mapper was first ruled to be a
capsule on the node that holds the devices; that contradicted 1d, and on
being shown the contradiction Captain Bob ruled that 1d stands.)
8. **A node's own drive is private.** It is a second chain in the kernel,
wired to that node's port, and its blocks are nobody else's. A node may
have it in addition to the common store or instead of it.
9. **Common from the bottom up, private from the top down.** A block of the
common store has the same logical number on every node that shares it,
from 2048 upward. A node's private blocks are numbered from the top of
the number space downward: private block *k* is logical number
2^32 - 1 - *k*, v3's block numbers being 32 bits. The two meet only if
the whole space is used up.
10. **A device leaves by being asked for.** *(step 6a)* Asked to release a
device, the mapper moves its blocks onto the devices that remain and
then says it may be pulled; if there is no room, the release is refused
with a message. A device pulled without asking leaves holes: its
logical numbers stay claimed and answer "no storage" until it returns,
is known by its identity, and its blocks are at the same numbers again.
### 8.2 What a node does *(step 6)*
`BLOCK`, `BUFFER`, `UPDATE` and `SAVE-BUFFERS` keep their two buffers and
their FORTH-79 behaviour. The one word beneath them in
`v4/capsule/blocks.v4` that read or wrote a block by storing to four
registers sends a message and waits for the answer instead.
- A node wired to storage writes the message to that port.
- A node that is not sends the same message toward the node that is, and
it is passed on like any other (section 7).
- A node with no way to storage has none: `BLOCK` is an error with a
message, and the node is back at its prompt.
The four storage registers and `v4_node_storage_attach` go from the engine.
### 8.3 The messages *(step 6)*
Four types, beside text, output and done (section 6):
| Type | Payload | Answered by |
|---|---|---|
| Block read | the block number, one word | Block data, or Block done with an error |
| Block write | the block number, one word; a Block data message follows it | Block done |
| Block data | 1024 characters | |
| Block done | one word: it worked, there is no storage, or it was refused | |
A write is two messages because a block is exactly the most a message
carries, so the number cannot go with the data. Whatever answers pairs the
two by who sent them.
### 8.4 What is on a storage port *(step 6)*
A device (4.3), in `v4/system/storage.c`, which the hosted and the
bare-metal builds share as they share `boot.c`. It speaks the messages of
8.3 and asks v3's block subsystem (`v3/src/block_subsystem.c`) for every
block. The engine (`v4/src`) knows nothing of it.
- v3's code does what it does: header, allocation map, block cards,
relocation, three blocks and their cards to a 4 KiB device block.
- The devices beneath are v3's own back ends: RAM and a file when hosted,
the virtio disk on bare metal.
- Blocks 0 to 2047 are the chain's fast RAM, as in v3; nodes that share
the common store share those too.
- The device holds a **view**: the common chain, or a private chain, or
both. A number at the top of the space (ruling 9) is the private
chain's; any other is the common chain's; a number in a chain the view
does not have is "no storage".
**A change to v3's C.** Its block subsystem is one global chain. For a
second chain its state becomes something there can be two of. The change
is mechanical and the one chain v3 uses behaves as it did. (Approved.)
Found 2026-10-06 and not yet ruled: `blk_subsys_init` must be given a v3
`VM`, and refuses without one, though the subsystem stores it and never
uses it (`block_subsystem.c` line 651 is its only mention). The hosted v4
system has no v3 `VM` to give. The same change would have to take that
argument away, which also changes the kernel's one call of it
(`kernel/src/capsule/capsule_loader.c`).
**On bare metal** the lone node's storage port fronts the kernel's real
chain — RAM, the ramdrive and the virtio disk — in place of the RAM array
`kernel/src/v4/sk_v4.c` gives it today. (Approved.) Found 2026-10-06:
`kernel_main.c` starts the v4 node (line 523) before it sets that chain up
(lines 620 to 668), and the v4 node never returns, so under `STARFORTH_V4`
the chain does not exist today. Step 6 has the kernel set it up before the
node starts.
### 8.5 Not in step 6
- **Who may have which block is not checked.** First-touch claim, the block
card's ACL and the Stadium touch need the node's identity, which is the
message's ACL tag (`V3-PARITY.md` 1d). Blocks are read and written
through v3's subsystem with nobody's claim on them. Not as intended yet.
- **Chains of more than one device, identities, release and holes** are
step 6a.
- **Cloud stores and real USB drives** wait for their drivers. The format
and the messages already fit them.
### 8.6 Open
A node that starts with only its own drive and later gains a way to the
common store. With no common store, its blocks from 2048 upward, a
capsule's among them, can only be on its own drive; when the common store
appears those numbers are claimed twice. Not ruled.
## 9. Birth, and Hera
@@ -476,7 +598,21 @@ Each is tested, committed and pushed before the next.
`BYE`, and nothing it asks is honoured, as ruled.
- The suite now takes about four minutes, eight under the sanitizers:
five POSTs.
6. **Storage (section 8).**
6. **Storage (section 8): the disk speaks messages, the kernel's mapper
answers, private drives.** Sections 8.2 to 8.4. Tests: a node reads and
writes blocks through a port, and a disk image v3's code formatted
reads back; in the unit of five, one outer node writes a block and
another reads it, one uses a drive of its own at numbers from the top
down, and one has no way to storage and `BLOCK` on it is an error with
a message (acceptance 3); POST 538 of 538 with `blocks.v4` changed, at
both widths and under the sanitizers; `hosted-check`; the three
bare-metal boots.
**6a. Storage that changes while running.** Rulings 6 and 10 of
section 8.1: chains of several devices, the device and chain
identities, a device joining at the end and known when it returns,
release by asking, holes. Its acceptance is to be approved before it is
built.
7. **A second unit; scaling while running; sleep, wake and kill by
command.**
8. **The hosted product is the five nodes**, on three ISAs: acceptance 1