nest
the place inside humd where bees gather and cells live — defines the
WorkerBee+ForagerBeetraits, theCellruntime, and theNestpool
A nest is the space inside a humd where two kinds of inhabitants meet: bees (the askers / producers, connected over thrum) and cells (the live LLM subprocesses, spawned by worker bees). The nest itself isn’t a process or a model — it’s the meeting place. One humd, one nest.
This crate defines what lives in the nest and how:
WorkerBeetrait — produces compute. Owns the spawn, the stdin pipe, the parsed-event stream, and the cell lifecycle.ForagerBeetrait — translates outside wires (OpenAI, Anthropic, custom HTTP) into thrum. Stub-only today; concrete impls live inhives/.Cellstruct — one live LLM subprocess. The compute itself.Listenertrait — what humd binds to a cell to receive parsed events for a particular sid.Neststruct — the pool runtime. Owns the cells keyed by pool_key, dispatches stdin writes, evicts on idle, enforcesmax_procs.
humd today is a router: it doesn’t link WorkerBee impls in at build
time. Instead, worker-bee binaries (under hives/) attach
to humd over thrum and announce themselves with chi:"hello" carrying
bee: ["worker"]. The Nest pool above is the Rust SDK for in-process
embeddings — the canonical wire-attached path is the helper in
hives/common/ (nest_common::serve_worker).
The three things — clearly
There are exactly three things this crate is about. They get confused constantly. The README will spell them out and then never let them blur:
| word | what it is | example |
|---|---|---|
| nest | the space inside humd. Not a process, not a model. Where bees and cells coexist. | one per humd |
| cell | one live LLM subprocess that lives in the nest. The actual compute. | the claude-cli child process |
| WorkerBee (Rust trait) | the kind of compute — how to spawn it, what it speaks, when it’s ephemeral. Implementation. | claude-cli, claude-repl, future openai-api |
And the cohabitants from the other side of the thrum:
| word | what it is |
|---|---|
| hive | the kind/contract a bee conforms to (defined in hives/ or hives/). Doesn’t run; doesn’t send anything. |
| bee | the running instance. Declares its kinds on hello: bee: ["worker"], bee: ["forager"], or both. |
| nestler | the bee, in the act of joining. Pre-acceptance. Awaiting the breath. |
| nestled | the bee, after joining. Same actor, registered, has a nestledId. Keeps asking throughout the connection. |
Seven words, seven different referents. nestler → nestled is a state transition (one actor, two lifecycle stages); both are askers.
Where the nest fits
bees (outside) │ thrum tones (NDJSON over Unix socket) ▼humd │ ├── ensemble ← cross-humd routing │ └── nest ← THE SPACE (this crate) │ ├── nestled<1> ← bees, post-handshake ├── nestled<2> │ └── cells ← live LLM subprocesses │ ├── cell<A> spawned by claude-cli WorkerBee ├── cell<B> spawned by claude-repl WorkerBee └── cell<C> spawned by openai-api WorkerBee (hypothetical future)The nest is BELOW ensemble (ensemble routes between humds; nest is
local to one humd) and INSIDE humd. Nestleds and cells share it.
chi traffic crosses between them: a nestled’s chi:"prompt" is routed
to a worker bee’s cell; that cell’s chunks flow back to the nestled.
The WorkerBee trait
#[async_trait]pub trait WorkerBee: Send + Sync { fn ephemeral(&self) -> bool; async fn spawn(&self, spec: SpawnSpec) -> Result<Cell>;}A WorkerBee is a recipe for a kind of cell. Two methods:
ephemeral()— does the pool evict this cell after eachresult? (PTY/REPL-style harnesses say yes; pipe-mode say no.)spawn(spec)— turn a high-levelSpawnSpec(sid, modelId, cwd, system prompt, MCP url, …) into a runningCell.
The trait says nothing about what the cell is. A WorkerBee impl
might fork a local Claude binary, open an HTTPS connection to OpenAI’s
API, load a llama.cpp model into the same address space, or return a
canned-response mock for tests. The wire never sees the difference —
all the worker has to do is produce a Cell whose stdin → events
behaves correctly.
Cell
pub struct Cell { pub pid: Option<u32>, pub stdin: mpsc::Sender<String>, // push raw NDJSON to the cell pub events: Arc<Mutex<mpsc::Receiver<Value>>>, // pull parsed JSON back pub exited: tokio::sync::oneshot::Receiver<i32>, pub ephemeral: bool, pub kill: Arc<dyn Fn() + Send + Sync>,}One live LLM subprocess seen from the humd side. The stdin and
events channels are the only contract — whatever’s behind them is
the WorkerBee impl’s business.
The Nest pool
let nest = Nest::new( NestConfig { max_procs: 8, idle_timeout: Duration::from_secs(300) }, pipe_worker, // Arc<dyn WorkerBee> — long-lived pipe-mode cells pty_worker, // Arc<dyn WorkerBee> — ephemeral PTY/REPL cells);Two worker bees by convention: a pipe-mode one and a PTY-mode one. A pool that wants to colocate compute keeps these in-process. humd today doesn’t — it routes prompts to thrum-attached worker bees that live in their own processes.
Operations the daemon calls on the pool when it’s in use:
| call | what happens |
|---|---|
nest.murmur(spec, prompt, listener) | spawn-if-needed + write chi:"prompt" to stdin + bind listener for the sid |
nest.reply(sid, tool_use_id, result) | route a chi:"tool-result" reply to the right cell’s stdin |
nest.interrupt(sid, request_id) | inject control_cancel_request mid-turn |
nest.fell(sid) | tear the cell down — host called chi:"cleanup" |
Why a trait, not a hard-coded path
hum is LLM-agnostic by design. The protocol (thrum), the mesh
(ensemble), the drone, the bees — none of them know which model is
behind the nest. WorkerBee is the seam where that decision lives,
and where it gets swapped.
| kind of cell | what its worker spawns | when to use |
|---|---|---|
claude-cli | claude -p with stream-json over pipe | normal model-CLI usage |
claude-repl | claude-cli in interactive REPL mode over a PTY | non-stream-json fallbacks, debugging |
(future) openai-api | HTTPS to OpenAI’s API; chunks streamed via SSE | use GPT-4 from your humd |
(future) ollama-local | local LLM via Ollama’s CLI or HTTP | run open-weights models on a laptop |
A new model harness adds one WorkerBee impl. No code in humd,
thrum-core, ensemble, drone, or the wire needs to change.
What this crate is NOT
- Not “compute” by itself. The compute is the cell. This crate is the space the cell lives in and the traits that define what kind of bee can produce / translate. Same nest word, distinct referents — nest is the room, cell is who lives there.
- Not a thrum endpoint. Cells don’t speak thrum directly. humd
reads cell events via
Listenerand writes the thrum side. Worker bees running as standalone processes write thrum themselves viahives/common/serve_worker. - Not a router.
Nestpicks the right cell for a sid; it doesn’t decide which humd handles which conversation. Ensemble owns that.
Layout
nest/├── src/│ ├── lib.rs # SpawnSpec, Cell, WorkerBee + ForagerBee traits,│ │ # Listener trait, encode_* helpers│ ├── pool.rs # Nest — the cell pool runtime│ └── mock.rs # MockWorkerBee for sim tests└── README.mdConcrete WorkerBee impls live under hives/:
hives/claude-cli/—claude -pstream-json over pipehives/claude-repl/— PTY-mode interactive fallbackhives/common/— shared building blocks across worker impls (e.g. theserve_workerhelper + the regexClassifierfor drone’s context-loss detection)
See also
- WIRE.md — the thrum protocol; see “The nest model” section for what the wire sees (and what it doesn’t) about nests and cells.
drone/— the sentinel that observes the chunks coming out of a cell and scores channel health.humd/— the daemon that owns theNestinstance.- VOCABULARY — the canonical glossary; entries for nest, cell, hive, bee, worker, forager, nestler, nestled.