Shipped in v0.29.0. This note explains the whole arc — why each piece exists, how they compose, and the principles the design holds. It is accurate to what shipped; where something is deliberately left out, it says so.
A unit is a process that can copy itself. Left alone, the cheapest thing a
self-replicator can do is fill its host: spawn until memory or CPU is gone, and
take the machine down with it. The interesting question is not how to
replicate — SPAWN already did that — but how a colony of selfish replicators,
each acting only on what it can see locally, ends up at a population that is
minimum-sufficient: enough units to serve the work, no more, spread across
hosts that have room, with no coordinator deciding any of it.
v0.29 answers that with four stones, each a separate commit, built in order so each one stands on the last:
- A sensor — read the host's real load (
src/resources.rs). - A wall and a local rule — refuse to grow past 80%, and replicate only on
sensed demand (
resources.rsceiling +multi_unit.rsrule). - A transport — move a complete self to another coordinate, never losing it
in transit (
src/transport.rs). - Placement — choose where to go, frugally, and expose it as a behavior a
unit chooses (
TRANSPORTword +choose_destination).
HostResources::measure() reads /proc/meminfo, /proc/loadavg, and the CPU
count on Linux. Two design choices matter:
- Utilization is the binding constraint:
max(memory_fraction, load_one / n_cpus). A host that is memory-bound and a host that is CPU-bound are both equally unfit to take another unit, so the tightest resource sets the pressure. Headroom is simply1 - utilization. - It can say "I don't know." On macOS, in a browser, or when
/proccan't be parsed,measure()returns a clearly-marked unavailable reading rather than guessing a number. That honesty is load-bearing for everything above it.
CEILING_UTILIZATION = 0.80 is the single source of truth, and its only
role is refusal. Nothing in the system grows toward 80%, optimizes to it, or
treats it as a setpoint. It is the line past which a coordinate says "no."
has_headroom() is the gate: valid && utilization < CEILING. It fails
closed — an unavailable reading returns false. A coordinate that cannot
measure itself does not replicate and does not accept a transport. This is the
recurring safety rule: when unsure, refuse.
The wall layers onto the existing spawn guards rather than replacing them —
SpawnState::can_spawn_within(&res) still honors quarantine, max_children, and
cooldown, and adds the ceiling on top.
Minimum-sufficient is emergent, not planned. The local rule a coordinate runs is just:
replicate one unit iff there is unmet demand I can serve and I have headroom.
"Unmet demand" is derived from signals that already exist — work waiting with
no idle unit to take it (senses_unmet_demand). There is no quorum, no global
counter, no target population anywhere. A unit with no work doesn't sit idle: it
falls through to GP-EVOLVE and speculatively evolves against open challenges.
Surplus doesn't need a culler — evolution costs energy, and a unit that can't
earn starves. That metabolism is the only population controller.
Relocation is replication-then-retirement, and the dangerous moment is the gap between them. v0.29 closes that gap with confirm-before-release — borrowed from the transporter: you are not disassembled at the origin until a living copy is confirmed at the destination.
What travels is the complete self: a serialized VmSnapshot in the USAV
format from persist.rs — the dictionary (including evolved SOL-*
antibodies), memory, goals, fitness, and code_strings. What does not travel is
the binary and the prelude: every coordinate already has them. The receiving
unit process is the transporter pad. This is why a self-state blob, not a
process image, crosses the wire — and why it rides a length-prefixed TCP frame
(UTPT), reusing spawn.rs's framing style, never the UDP gossip wire.
The protocol:
origin destination
| capture self (USAV) |
| -- UTPT frame (len-prefixed USAV) --> |
| | validate, admission gate? (margin + rate window; fail closed)
| | deserialize the complete self
| <------ UTPC confirm (accept|refuse) -|
| release ONLY on Accepted |
The asymmetry is the whole point. The origin releases solely on
Ok(Accepted). A refused confirm, a timeout, a dropped connection, or a garbled
reply all map to Err, and on any Err the origin stays alive exactly as it
was. No unit is ever lost in transit. The frame encode/decode and the
destination handler are pure functions over byte buffers (resources injected),
so the invariant is pinned by deterministic tests, not by hoping the network
behaves.
To choose a destination, a unit needs to know which peers have room. Peers
already gossip their unit count in the heartbeat; v0.29 appends one more byte —
advertised headroom (0..=100) — kept at the tail so older peers that omit it
read as 0 (fail closed again). Bounded-k gossip stays bounded.
choose_destination() is two-tier: it prefers the emptiest
abundantly-free peer (advertised headroom ≥ 50%), breaking ties among
equals with a per-node random pick so concurrent senders decorrelate, and
falls back to the first merely-sufficient peer in view order when no one
is abundant. The receiver-side admission gate (margin below the ceiling,
plus a rate window of at most 8 accepts per 5s) is what actually prevents
a thundering herd from overshooting a popular target.
A coordinate knows it should consider leaving when it is mislocated, and the
honest trigger is local pressure: is_mislocated == !has_headroom(). There is
no separate "mislocation score" to invent or tune.
Crucially, relocation is not a host-driven scheduler. The host offers the
capability; the TRANSPORT Forth word is something a unit chooses to call,
GP-mutable exactly like COURT or SAY!. Whether and when units flee local
pressure is therefore an evolvable behavior, selected for or against by the
same metabolism as everything else — not a policy imposed from above. Calling
TRANSPORT senses mislocation, picks a destination (emptiest-abundant first, sufficient as fallback), and
relocates with confirm-before-release; if it is not mislocated or no destination
fits, it is a safe no-op and the unit stays.
It costs energy. TRANSPORT_COST = 150 sits in the same heavy class as
SPAWN_COST (just below it — no binary travels), charged with the same
no-op-on-starve semantics as SAY!. A starving unit cannot flee. That is
not a limitation to work around; it is correct. Fleeing is metabolic work, and a
unit with nothing left to spend has no business replicating itself across the
network.
Placement trusts a peer's advertised headroom. Nothing verifies it. If a peer
lies — advertises room it doesn't have — the consequence is contained entirely
by the existing machinery: the destination refuses at the transport layer
(the admission gate refuses there too), send_transport returns Err, and the
origin stays put. There is no detection, no flag, no blacklist, no reputation.
The transport simply doesn't complete. Whether honest advertisement is the
evolutionarily stable strategy is an empirical question the system is built to
ask, not one it answers by policing.
- The host is an idea; the mesh is real. A "coordinate" is wherever a unit process runs. Units relocate between coordinates; the colony is the set of living copies across the mesh, not anything centrally held.
- Minimum-sufficient is emergent. Local rule (demand ∧ headroom) plus energy metabolism, with no coordinator, quorum, or population target.
- 80% is a refusal wall, not a target. The colony never grows toward it.
- A unit with no work runs its
LIVEword. Idleness routes to the dictionary-resident life loop (prelude default:GP-EVOLVE), not to sitting still or to being culled. The host provides the tick; the genome decides what living is — so life strategies are heritable, shareable, and mutable. - An unsustainable life is mortal. A unit pinned at the energy hard floor
(a runaway
LIVE, not ordinary GP debt) for 30 consecutive ticks dies. Its final act is a death-cry: itsSOL-*antibodies go to local siblings and to the mesh, trust-gated on receipt (immune-memory words only, never overwriting) so a forged cry can't install behavior. - The complete self transports — antibodies included. Evolved
SOL-*words ride along; the binary and prelude don't, because every coordinate has them. - Confirm before release. No unit is ever lost in transit.
- Honesty is selected, not enforced. Lying isn't policed; it just doesn't pay.
- Fail closed. A coordinate that can't measure itself neither replicates nor accepts a transport.
- Zero new dependencies. The whole surface is hand-rolled;
Cargo.lockstill contains only theunitcrate.
Everything above shipped in v0.29 as unit-tested mechanism. v0.30 added the
persistent run loop (unit --multi-unit N --port P --peers ... is now a living
node, not a 5-second discovery demo) and put the whole arc on real hardware for
the first time. This section is what was observed, not claimed.
Setup. Three DigitalOcean droplets, SFO3, 512 MB RAM each, Ubuntu 24.04, built from source, peered into one mesh.
What happened. A 2000-unit colony was started on one box. Its periodic
resource line read 86.4% memory utilization — OVER-CEILING. The node sensed
itself MISLOCATED (!has_headroom() on a real /proc reading, not a
fail-closed guess), and from its gossiped peer view chose peers advertising
sufficient headroom (~73% free). It then transported units one per tick,
with confirm-before-release holding across real UDP gossip + TCP transport: the
origin slot was retired only after the destination confirmed a live copy, so the
unit count drained incrementally — 2000 → 1987 → … — toward the two peers,
never dropping a unit in transit.
The arrivals were alive. The receiving box's unit count rose (e.g. 3 → 8) and the transported units resumed evolving on landing — the complete self (dictionary, evolved antibodies, fitness, code_strings) travelled and ran, not an inert blob.
Honesty was selected, not policed. As the overloaded box shed load its advertised headroom fell — gossiped honestly all the way down to 14% — and the other nodes simply stopped choosing it as a destination. No detection, no blacklist; an over-full box just isn't sufficient-first anymore.
The bug only a real network could surface. Multi-machine testing immediately
exposed that the mesh UDP socket and the transport TCP listener were bound to
127.0.0.1. On a single host that "works" — and worse, --peers seed entries
populate the peer table at startup and survived the old demo's 5-second window
(shorter than the 15-second peer timeout), so cross-machine discovery looked
fine. It wasn't: a loopback-bound socket never receives datagrams destined for
the host's routable IP. The fix was to bind both peer-traffic sockets to
0.0.0.0 (the HTTP bridge stays localhost-only by design). A single-host test
could not have caught this; the persistent loop on three boxes caught it in the
first minute.
The v0.30 soak proved the 80% ceiling holds as a hard refusal wall, but it also
showed a receiver still under the ceiling could be pushed over it by a burst:
several senders act on the same stale "has room" gossip and all transport within
one window, so the wall holds but overshoots for a tick before the next frame is
refused. v0.31 added the inbound admission margin — accept inbound only while
utilization is below CEILING - ADMISSION_MARGIN (0.05), so a receiver refuses
before a burst can push it onto the wall. This section is what was observed.
Setup. Three DigitalOcean droplets, SFO3, 512 MB RAM each, Ubuntu 25.10, built from source, peered into one mesh.
What happened. A receiver was parked at 76.7–79.2% utilization — under
the 80% ceiling, but inside the admission margin (above the 75% admission gate).
A single over-ceiling sender tried to transport to it and was refused with
destination refused (no headroom): the receiver declined inbound while it
was still under its own ceiling, exactly because the margin reserves that last
slack for in-flight units a fresh measure can't yet see. Under a 2-sender
burst the receiver held — utilization never crossed 80% — where pre-margin
admission would have let both frames land in one window and overshoot the wall
transiently before clawing back.
Admission stays separate from replication. The receiver refusing inbound at
~78% did not stop it tending its own units; only the decision to accept more
uses the stricter has_admission_headroom, while the host's own replication and
mislocation decisions still use the full-ceiling has_headroom. A box can be
content to keep what it has yet decline to take on more.
- No central coordinator. This is the load-bearing one. In v0.30 the
persistent node does run the local rule on a tick — it is not purely
unit-invoked anymore — but each node evaluates that rule against its own
gossiped view and its own measured pressure, deciding independently. There
is no global scheduler, no quorum, no authority placing units across the mesh.
The three-droplet run drained one box while two others independently judged
themselves sufficient and accepted arrivals; nothing orchestrated that. The
TRANSPORTForth word remains for genuinely unit-invoked, GP-evolvable relocation; the tick loop is the always-on per-node driver layered beside it. - No reclaim or cull. Surplus resolves through starvation, not through a reaper.
- No liar detection. Honesty is selected, not policed — observed live as the full box's advertised headroom fell and peers simply stopped choosing it.
| Concern | Where |
|---|---|
Host sensor, binding constraint, ceiling, has_headroom (fail closed) |
src/resources.rs |
| Spawn guard + ceiling refusal | src/spawn.rs (can_spawn_within) |
| Emergent local rule, demand sense, evolve-when-unworked | src/multi_unit.rs (MultiUnitHost) |
| Confirm-before-release transport, USAV framing | src/transport.rs |
| Gossiped headroom advertisement | src/mesh.rs (heartbeat, peer_resource_view) |
| Abundant-emptiest-first placement, mislocation, relocate-with-release | src/multi_unit.rs (MultiUnitNode) + src/transport.rs |
TRANSPORT word, energy cost |
src/words/persistence.rs (prim_transport), src/vm/mod.rs, src/energy.rs |
The rebalancing claim — the colony rebalances toward boxes with room — was validated for one polite sender in v0.30. Drill scenario S8 tests it under SIMULTANEOUS pressure with only local rules: three 96 MiB nodes boot 520 units each (~82%, hard over-ceiling) beside two 128 MiB receivers, and one receiver is blackholed (network partition — packets dropped, no RST) mid-shed. Two real failures fell out before the claim held:
-
Shedding didn't relieve pressure. Dropping a transported-out unit frees its heap to the ALLOCATOR, but glibc retains the pages — cgroup
memory.currentnever fell, the local rule saw no relief, and a sender shed 312 of 520 units while still measuring 84%: an over-ceiling node would bleed its whole population without ever converging. Fix:malloc_trim(0)after any tick that releases units (raw libc FFI, no new dependency) — the release is now physically real. -
A blackholed destination starved the tick loop.
TcpStream::connecthas no timeout of its own; against a partitioned peer still in the placement view it blocked on the OS SYN retry schedule. S8 measured 5 ticks in 25 seconds. Fix:connect_timeout(2s) — an unreachable destination now costs one bounded attempt, and the established- connection handshake timeout is unchanged.
What S8 now proves, from the machine-readable (node-status …) chronicle
alone: all three senders shed toward capacity and every survivor keeps
ticking under sustained pressure (the stronger static-equilibrium claim —
everyone under the wall, quiescent — was later RETIRED: landed units grow
evolution state, so the wall is an attractor with self-correcting
transients, and per-unit memory growth is the named open problem);
ticking continues at full rate through the blackhole window; and the colony's unit count balances to
the initial total exactly, with loss bounded by the in-flight window
swallowed by the partition and duplication bounded the same way (the
documented fail-toward-duplication was observed live: one unit landed on
the blackholed receiver, its confirm died in the partition, and both
copies were retained — never silent loss).