# Capsule Loader — Missing Persistence Boundary Check **Date:** 2026-07-09 **Branch:** `lithosananke` **Status:** CLOSED — not a bug. Captain Bob's direct correction (2026-07-10), after this finding drove a design doc (`CAPSULE-LOADER-DEFINITIONS-ONLY-DESIGN-20260709.md`) and a real implementation that removed the write entirely — both wrong, both reverted (commits `43d81d49`/`548062ab`, three-arch Tripod acceptance re-confirmed at `ee91280d`): *"all capsule does is LOAD a block with a given Block xxxxx header to THAT BLOCK. that's all. They are not ordered in the capsule necessarily."* The behavior documented below — `write_ramdrive_block` writing every capsule's content to the literal LBN named by its own `Block N` header, unconditionally, including onto Artemis's real disk for `4000+`-numbered capsules — is the intended mechanism, not a boundary violation. Neither candidate fix in "Not fixed" below applies; no fix is needed. Kept as a record of a wrong turn, per this repo's revision-history convention, not deleted. **Author:** Captain Bob / Claude Code Surfaced while scoping `HERMES-MESSAGE-BLOCK-STORAGE-DESIGN-20260708.md` (picking an LBN range for Hermes's new message-block storage) — not something either doc set out to find, but relevant to both. --- ## The architecture, as confirmed by Captain Bob Inside a VM, block-addressed storage has two internal surfaces: - **RAM surface** — LBN 0–2047. Fast working memory, presented in block shape. - **Ramdrive** — LBN 2048–3071. A mounted block device, still internal to the VM (volatile, RAM-backed via `krd_buf`) — not real disk. Both are internal to the VM. Capsule persistence mechanics — the part of capsule loading that writes a capsule's parsed blocks somewhere so a later `LOAD N` can find them — are meant to reach only into these two surfaces, never past LBN 3071 into a real external device. ## The defect `write_ramdrive_block()` (`src/starkernel/capsule/capsule_loader.c:146`) has no boundary check. It calls `blk_get_buffer(block_num, 1)` (`src/block_subsystem.c:576`) with the literal `block_num` taken from a capsule's own `Block N` source header — unconditionally, regardless of value. `blk_get_buffer()` resolves any LBN against the full chain of registered block devices, in registration order (`src/block_subsystem.c:491-546`): LBN 0–2047 → RAM surface (`g.ram_user = BLK_RAM_BLOCKS(2080) - BLK_FORTH_SYS_RESERVED(32) = 2048`), LBN 2048–3071 → the ramdrive raw device (`KRD_MAX_BLOCKS = 1024`, registered in `capsule_blk_init()`), LBN 3072+ → whatever device is attached next. In the current boot sequence that next device is Artemis's virtio-blk disk, attached in `kernel_main.c:520-530` via `virtio_blk_find_artemis()` + `blk_subsys_attach_device()`, whose own `start_lbn` is `3072` (`artemis/init.4th`'s own `3072 CONSTANT ART-HDR-LBN`, matching the `total_user_lbn` accumulated by the two prior device registrations). So a capsule whose `Block N` headers are `≥ 3072` has its content written by `write_ramdrive_block()` directly onto whatever real device occupies that LBN — currently Artemis's persistent disk — as an unbounded side effect of capsule loading. This happens on every VM birth, not just the interactive `EXEC` word: `capsule_birth_set_hooks()` (`src/starkernel/capsule/capsule_vm_hooks.c:121-124`) registers `capsule_exec_hook` (which wraps `capsule_exec_payload`) as the `vm_exec_fn` used by both `capsule_birth_mama()` and `capsule_birth_baby()` (`src/starkernel/capsule/capsule_birth.c:374, 449`). **One corroborating detail:** `capsule_loader.c:50-51` defines `CAPSULE_RAM_OFFSET 2048u` with the comment `dest = source - CAPSULE_RAM_OFFSET`, but `write_ramdrive_block()` never references this macro — `block_num` is passed to `blk_get_buffer()` unmodified. The macro is unused dead code; whatever offsetting scheme its comment describes was never implemented. ## Scope — which capsules are affected From `capsules/BLOCK_MAP.md` (auto-generated), every capsule with `Block N` headers `≥ 4000` is exposed: `ACL.4th` (4000–4007, 4015), `zuse.4th` (4016–4018), `lib.4th` (4050), `common:msg.4th` (4055), `doe-campaign.4th` (4060–4065), `hermes:init.4th` (4100–4153), **`artemis:init.4th` itself** (4110–4141, 4851–4852), and the `init-1.4th` through `init-9.4th` / L8-variant capsules (4406+, 4506+, 4606+, 4706+, 4806–4842). Capsules whose headers stay within 2048–3071 (`init.4th`, `init-l8-omni.4th`, `init-6.4th`, `doe.4th`, `init-4.4th`, `init-7.4th`, `init-8.4th`, `init-0.4th`) are unaffected — their writes land in the volatile ramdrive regardless. ## Mitigating factor, also confirmed `blk_get_buffer()` returns `NULL` for an LBN with no device registered there; `write_ramdrive_block()` no-ops on a `NULL` buffer (`capsule_loader.c:150-151`). So on any boot where Artemis's virtio-blk disk isn't attached (`kernel_main.c:528`, "Artemis: no virtio-blk disk (continuing without)"), every `4000+`-numbered capsule's persistence write silently fails rather than landing anywhere — harmless in that case, but also meaning `LOAD N` on those block numbers would return whatever stale/zeroed content already occupies that LBN, not the capsule's own text. ## Not fixed — because there is nothing to fix Two candidate fix shapes were raised in conversation. Both are now moot, confirmed directly: 1. A hard boundary check in `write_ramdrive_block()` — rejected. Nothing is meant to reject or no-op a write based on `block_num`. 2. Renumber the `4000+` capsules' `Block N` headers back under 3072 — also unnecessary; a capsule's header number naming a real block anywhere in the address space, including Artemis's disk, is correct. `write_ramdrive_block()` and its call site in `capsule_exec_payload` remain exactly as they were before this finding was ever filed.