docs(v4.0.0): storage brought back to the OS as designed -- every node asks the kernel

Ruled 2026-10-07 (Captain Bob): POST on every node, nodes without
storage, and block requests passed from node to node had left v3's
design.  MESH.md section 8 is rewritten: a block is a kernel request, as
ENGINE.md 3.3 already had it; every node has blocks; there is one chain.
Private drives and the message device are withdrawn, and listed in 8.5
so that they are not proposed again.  Acceptance 1 and 3 change with it.
A born node is not POSTed, and the kernel is to hold POST's cases.

The plan for step 6 is revised to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-07 07:21:10 -04:00
co-authored by Claude Opus 5.5
parent 4a505a154d
commit 26a0748455
2 changed files with 176 additions and 415 deletions
+95 -117
View File
@@ -27,8 +27,9 @@ before making v4 equal v3 on bare metal (`ENGINE.md` step 4), which waits.
until the neighbour writes (`DECOMPOSITION.md` section 6). What travels
is v3's Hermes message with what it carries, so v3's messaging rules are
not dropped: they go with the message.
2. **Some nodes have storage of their own**, in addition to or instead of
the common SSD. Some have none.
2. ~~Some nodes have storage of their own, in addition to or instead of
the common SSD. Some have none.~~ **Withdrawn 2026-10-07:** every node
has blocks, and asks the kernel for them (section 8).
3. **The first set of nodes is "2x2 + 1 central"**: five.
4. **The geometry is data, not design.** A node has a number of ports, and
that number is a parameter. Which port connects to what is a table that
@@ -53,16 +54,18 @@ before making v4 equal v3 on bare metal (`ENGINE.md` step 4), which waits.
1. **Five nodes come up from nothing.** Hera is born empty, takes in the
nucleus capsule and the FORTH-79 capsule, and passes POST. She births
the four outer nodes while running; each is born empty, takes in the
same capsules through its port from Hera, and passes POST. Each prints a
parity line; the four outer nodes' dictionary hashes are identical.
the four outer nodes while running; each is born empty and takes in the
same capsules through its port from Hera. Each prints a parity line;
the four outer nodes' dictionary hashes are identical. (Changed
2026-10-07: an outer node is not POSTed. POST is the kernel's, once, as
in v3.)
2. **They talk.** A line typed at the console reaches Hera as a message. A
message from Hera runs on an outer node and its output comes back. A
message between two outer nodes that are not wired to each other is
forwarded by a node in between.
3. **They share the SSD.** A block written by one node is read by another.
One outer node has a drive of its own and uses it. One has no storage,
and `BLOCK` on it is an error with a message.
3. **They share the SSD.** Every node asks the kernel for its blocks. A
block written by one node is read by another. (Changed 2026-10-07: no
node has a drive of its own and none is without storage.)
4. **It scales while running.** A second unit of five is born and joined
centre to centre. A message crosses from one unit to the other. The
second unit is removed and the first carries on.
@@ -270,22 +273,21 @@ kernel-Hermes's work in v3 and is not decided for the mesh.
## 8. Storage
**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.
**Ruled 2026-10-06 and 2026-10-07** (Captain Bob). On 2026-10-07 he found
this section to have left the OS as designed, and brought it back: every
node asks the kernel directly, as every v3 VM does. What that withdrew is
in 8.5, so that it is not proposed again.
### 8.1 The rulings
### 8.1 The rulings that stand
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.
1. **A node only ever asks for a block by number, and it asks the kernel.**
`V3-PARITY.md` 1d and `ENGINE.md` 3.3: a kernel request, written to the
port where the node's kernel is, served between the node's opcodes. No
node asks another node for a block, and none passes such a request on.
2. **Every node has blocks, always.** There is no node without storage.
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.
use. A physical block number is a place on a real device. The kernel's
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
@@ -301,121 +303,93 @@ and waits for that step.
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.
7. **The mapper is the kernel's**: 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.
8. **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.
registers makes a kernel request instead: it puts the block's number and
the word address of the buffer's 256 cells on the data stack and writes the
request's number to port 0, where its kernel is. When the write has been
served the status is on the stack in their place: 0 it worked, 2 it was
refused, 3 there is no such block.
- 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.
There are two requests, read a block and write a block. Their numbers are
the same for every node and are below zero; the requests a host names for
its own words (`KERNEL-WORD`) count up from 1.
The four storage registers and `v4_node_storage_attach` go from the engine.
### 8.3 The messages *(step 6)*
### 8.3 What the kernel does *(step 6)*
Four types, beside text, output and done (section 6):
Every node has its kernel on port 0, whatever else is there for it: Hera's
requests for nodes (section 9) are honoured only from Hera, and the two
block requests from every node.
| 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: 0 it worked, 1 there is no storage, 2 it was refused, 3 there is no such block | |
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.
`v4/system/blocks.c`, which the hosted and the bare-metal builds and the
tests share, serves them: it takes the number and the address from the
node's data stack, checks that the 256 cells are in the node's memory, and
reads or writes the block through v3's block subsystem
(`v3/src/block_subsystem.c`), four characters to a cell, the first lowest.
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. Private block *k* is the private chain's block 2048 + *k*, its
first on a device. A number is the private chain's when *k* is less than
the blocks that chain has on devices; any other is the common chain's,
and one past its end is "no such block". Numbers below 2048 are the
common chain's fast RAM, or the private chain's if the view has no
common chain. A number that belongs to no chain the view has is "no
storage".
- Whatever is on a storage port has a number, as a node has, and is sent
to and answers like one. Whoever wires a node tells it that number
(`STORAGE`) and the way to it (`ROUTE`). A node not told has no storage.
- A block number is not signed: on 32-bit cells the numbers from the top
down are the negative ones. Only 0 is no block.
- There is one chain, as in v3: fast RAM at blocks 0 to 2047, then the
devices. Every node sees the same blocks at the same numbers.
- The devices are v3's own back ends: RAM and a file when hosted, the
virtio disk on bare metal. A hosted program started with no disk has the
fast RAM alone, as hosted v3 has.
**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.)
`blk_subsys_init` took a v3 `VM`, stored it and never used it; the hosted
v4 system has none to give. **Ruled 2026-10-06:** the argument is removed.
`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. **Ruled
2026-10-06:** the argument is removed, in the same change; the kernel's
call of it (`kernel/src/capsule/capsule_loader.c`) changes with it.
**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
**On bare metal** the node's blocks are 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
### 8.4 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.
card's ACL and the Stadium touch need the node's identity
(`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.
- **Cloud stores and real USB drives** wait for their drivers.
### 8.6 Open
### 8.5 Withdrawn 2026-10-07
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.
Each of these was ruled on 2026-10-06 or stood in this document, and each
left v3's design. Captain Bob: "something is really off. it sounds like a
divergence from the os as designed"; and of the three below, "all three,
every node asks the kernel directly".
- **A disk as a device on a port that speaks messages**, and a block
request passed from node to node until it reaches the node wired to the
disk. Built and tested as far as one node (commit `4a505a15`), then
withdrawn. In v3 a VM calls the kernel.
- **Nodes with no storage** (ruling 2 of section 2, and acceptance 3 as it
was). In v3 every VM has blocks.
- **A drive of a node's own**, numbered from the top of the number space
downward. In v3 there is one chain.
- **POST on every node at its birth** (acceptance 1 as it was). In v3 the
kernel runs POST once, at boot, with block RAM it supplies, and a born VM
prints its parity. See section 9.
## 9. Birth, and Hera
@@ -428,6 +402,12 @@ same kind. Only Hera's requests are honoured.
The unit rule (ruling 5) is Hera's, in capsule code: it is what she does
with those requests. The engine and the host do not know it.
**Ruled 2026-10-07:** a node Hera births is not POSTed. POST is the
kernel's, run once at boot on Hera, as v3's kernel runs it once on its
first VM; a born node prints its parity. And the kernel is to hold POST's
cases and feed them to Hera itself, with no POST words loaded into her
dictionary — today they are a capsule she loads (`NUCLEUS.md` section 6).
## 10. Steps
Each is tested, committed and pushed before the next.
@@ -606,17 +586,15 @@ 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): 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.
6. **Storage (section 8): every node asks the kernel for its blocks.**
Sections 8.2 and 8.3. Tests: a node reads and writes blocks by kernel
request, and what v3's own calls wrote reads back; in the unit of five,
one node writes a block and another reads it (acceptance 3); Hera no
longer sends POST to the nodes she births; POST 538 of 538 on Hera 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
**6a. Storage that changes while running.** Rulings 6 and 8 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