From 529829bfce37aaa88d3b3c043106264541309c5a Mon Sep 17 00:00:00 2001 From: Adil Date: Tue, 27 Oct 2026 22:59:14 +0500 Subject: [PATCH 1/7] hives: every README held to its code, and what was broken fixed A sweep of each hive's README against what its code does, which turned up code that was broken outright; that is fixed here too, each pinned by a test. humd - A connection is heard for nothing but its hello until a breath admits it; before, a connection that never said hello could prompt. - A sid's tones reach only the connections that prompted or attached under it, not every idle bee. - A bloom whose peer leaves the ensemble fails with a retryable `nurse-gone` instead of hanging until it expires. - A refused tool call no longer hangs its cell; per-sid state and sigil claims are freed; a second connection on one key closes the first. hum-nest - `wallClockMs` is enforced; cleanup and eviction act only on their own cell, so a race no longer kills a replacement. hives - grpc listens on 127.0.0.1 by default (it served a bash builder to every interface, unauthenticated). - twilio-sms verifies Twilio's signature. - paid-oracle refuses pending or reverted payments and redeems each once. - gsm-modem, ollama-server, vercel-ai and wifi-p2p start (or build). - anthropic-server, ollama-server and vercel-ai keep history, carry tool calls through, and cancel on disconnect. - Foragers name no model by default, so any nurse with room grows. - Fixes in openai-server, web2, grpc, twilio-sms, dwarfstar, claude-repl. docs - Every hive README, hives/README.md and THRUM.md describe what the code does; foragers.md goes. - hum bee's help names --role. --- THRUM.md | 47 +- ensemble/src/lib.rs | 21 +- hives/README.md | 113 +- hives/anthropic-server/README.md | 303 +- hives/anthropic-server/package.json | 6 +- hives/anthropic-server/pnpm-lock.yaml | 998 ++++++ hives/anthropic-server/pnpm-workspace.yaml | 4 + hives/anthropic-server/src/conversation.ts | 266 ++ hives/anthropic-server/src/forager.ts | 98 + hives/anthropic-server/src/growth.ts | 211 ++ hives/anthropic-server/src/http.ts | 142 + hives/anthropic-server/src/index.ts | 591 +--- hives/anthropic-server/src/messages.ts | 129 + hives/anthropic-server/src/tools.ts | 41 + .../test/conversation.test.ts | 116 + hives/anthropic-server/test/e2e.test.ts | 275 ++ hives/anthropic-server/test/hum.ts | 121 + hives/anthropic-server/test/nurse-bee.ts | 94 + hives/anthropic-server/test/tools.test.ts | 30 + hives/bash/README.md | 40 +- hives/bp7/README.md | 94 +- hives/bp7/src/hum.rs | 41 +- hives/bp7/src/main.rs | 42 +- hives/bp7/tests/common/mod.rs | 25 +- hives/bp7/tests/dtn.rs | 41 + hives/bp7/tests/grower_gone.rs | 9 +- hives/claude-repl/Cargo.lock | 1 + hives/claude-repl/Cargo.toml | 3 + hives/claude-repl/src/lib.rs | 35 +- hives/dwarfstar/README.md | 163 +- hives/dwarfstar/src/ds4_bridge.c | 6 +- hives/dwarfstar/src/ds4_stub.c | 4 +- hives/dwarfstar/src/main.c | 4 +- hives/foragers.md | 8 - hives/grpc/Cargo.lock | 2695 +++++++++++++++- hives/grpc/Cargo.toml | 4 + hives/grpc/Orchfile | 9 + hives/grpc/README.md | 189 +- hives/grpc/build.rs | 5 +- hives/grpc/src/main.rs | 466 ++- hives/grpc/tests/common/mod.rs | 311 ++ hives/grpc/tests/outlive.rs | 29 + hives/grpc/tests/stream.rs | 99 + hives/gsm-modem/Orchfile | 15 + hives/gsm-modem/README.md | 192 +- hives/gsm-modem/src/gsm.rs | 153 + hives/gsm-modem/src/hum.rs | 152 + hives/gsm-modem/src/main.rs | 514 ++-- hives/gsm-modem/src/modem.rs | 151 + hives/ollama-server/Cargo.lock | 2481 +++++++++++++-- hives/ollama-server/Cargo.toml | 7 + hives/ollama-server/Orchfile | 26 +- hives/ollama-server/README.md | 334 +- hives/ollama-server/install | 10 +- hives/ollama-server/src/conversation.rs | 408 +++ hives/ollama-server/src/growth.rs | 180 ++ hives/ollama-server/src/hop.rs | 10 +- hives/ollama-server/src/main.rs | 744 +++-- hives/ollama-server/src/nurse/main.rs | 14 +- hives/ollama-server/src/nurse/ollama.rs | 292 +- hives/ollama-server/src/requester.rs | 231 ++ hives/ollama-server/tests/common/mod.rs | 331 ++ hives/ollama-server/tests/forager.rs | 178 ++ hives/ollama-server/tests/live.rs | 39 + hives/ollama-server/tests/nurse.rs | 119 + hives/openai-server/README.md | 207 +- hives/openai-server/src/config.ts | 2 +- hives/openai-server/src/growth.ts | 1 + hives/openai-server/src/keys.ts | 15 +- hives/openai-server/src/requester.ts | 85 +- hives/openai-server/src/responses.ts | 37 +- hives/openai-server/src/scent.ts | 12 +- hives/openai-server/test/cancel-nurse.ts | 10 + hives/openai-server/test/hello.test.ts | 38 + hives/openai-server/test/holds.test.ts | 58 + hives/openai-server/test/keys.test.ts | 49 + hives/openai-server/test/requester.test.ts | 141 + hives/openai-server/test/responses.test.ts | 45 + hives/paid-oracle/Cargo.lock | 2696 ++++++++++++++++- hives/paid-oracle/Cargo.toml | 14 +- hives/paid-oracle/Orchfile | 22 + hives/paid-oracle/README.md | 290 +- hives/paid-oracle/buyer/buy.ts | 48 +- hives/paid-oracle/src/main.rs | 630 ++-- hives/paid-oracle/src/tests.rs | 278 ++ hives/paid-oracle/tests/chain/mod.rs | 172 ++ hives/paid-oracle/tests/reach.rs | 201 ++ hives/twilio-sms/Orchfile | 22 + hives/twilio-sms/README.md | 172 +- hives/twilio-sms/main.go | 498 ++- hives/twilio-sms/main_test.go | 491 +++ hives/vercel-ai/Orchfile | 11 +- hives/vercel-ai/README.md | 246 +- hives/vercel-ai/build | 7 +- hives/vercel-ai/install | 21 - hives/vercel-ai/package.json | 7 +- hives/vercel-ai/pnpm-lock.yaml | 1034 +++++++ hives/vercel-ai/src/conversation.ts | 217 ++ hives/vercel-ai/src/index.ts | 3 + hives/vercel-ai/src/provider.ts | 244 +- hives/vercel-ai/src/thrum.ts | 195 +- hives/vercel-ai/src/tools.ts | 31 + hives/vercel-ai/src/transform.ts | 109 +- hives/vercel-ai/test/conversation.test.ts | 99 + hives/vercel-ai/test/done.mjs | 10 + hives/vercel-ai/test/e2e.test.ts | 276 ++ hives/vercel-ai/test/hum.ts | 99 + hives/vercel-ai/test/lab.ts | 160 + hives/web2/README.md | 202 +- hives/web2/examples/mock_mcp.rs | 12 +- hives/web2/src/auth.rs | 9 +- hives/web2/src/config.rs | 27 +- hives/web2/src/engine.rs | 40 +- hives/web2/src/import.rs | 101 +- hives/web2/src/main.rs | 65 +- hives/web2/src/mcp.rs | 21 +- hives/web2/tests/wrepl.rs | 88 +- hives/wifi-p2p/.gitignore | 3 +- hives/wifi-p2p/Orchfile | 4 +- hives/wifi-p2p/README.md | 336 +- hives/wifi-p2p/app/build.gradle.kts | 39 +- .../wifi-p2p/app/src/main/AndroidManifest.xml | 49 +- .../src/main/java/hum/hive/wifip2p/Forage.kt | 131 + .../java/hum/hive/wifip2p/IdentityStore.kt | 74 +- .../main/java/hum/hive/wifip2p/P2pManager.kt | 463 ++- .../main/java/hum/hive/wifip2p/Settings.kt | 38 + .../src/main/java/hum/hive/wifip2p/Stream.kt | 40 + .../main/java/hum/hive/wifip2p/ThrumClient.kt | 349 ++- .../src/main/java/hum/hive/wifip2p/Tone.kt | 241 +- .../hum/hive/wifip2p/WifiP2pBeeService.kt | 420 +-- .../main/res/xml/data_extraction_rules.xml | 16 + .../test/java/hum/hive/wifip2p/FakeHumd.kt | 80 + .../test/java/hum/hive/wifip2p/ForageTest.kt | 197 ++ .../test/java/hum/hive/wifip2p/HumdTest.kt | 54 + .../java/hum/hive/wifip2p/IdentityTest.kt | 85 + .../java/hum/hive/wifip2p/SettingsTest.kt | 35 + .../test/java/hum/hive/wifip2p/StreamTest.kt | 52 + .../java/hum/hive/wifip2p/ThrumClientTest.kt | 129 + .../test/java/hum/hive/wifip2p/ThrumTest.kt | 80 + .../test/java/hum/hive/wifip2p/ToneTest.kt | 72 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 47505 bytes .../gradle/wrapper/gradle-wrapper.properties | 10 + hives/wifi-p2p/gradlew | 248 ++ hives/wifi-p2p/gradlew.bat | 82 + hives/wifi-p2p/settings.gradle.kts | 4 + hum-nest/src/limits.rs | 100 +- hum-nest/src/serve.rs | 288 +- hum-paths/src/lib.rs | 16 +- hum-paths/tests/env.rs | 23 + hum.thrum | 2 +- hum/src/bees.rs | 2 +- hum/src/main.rs | 6 +- humd/src/lib.rs | 192 +- humd/src/thrumd.rs | 65 +- humd/src/thrumd/conn.rs | 31 +- humd/src/thrumd/registry.rs | 39 +- humd/tests/common/mod.rs | 282 ++ humd/tests/sids.rs | 197 ++ humd/tests/socket.rs | 77 + sim/tests/chi_scoping.rs | 14 +- thrum-core/src/wane.rs | 14 + 161 files changed, 24373 insertions(+), 4384 deletions(-) create mode 100644 hives/anthropic-server/pnpm-lock.yaml create mode 100644 hives/anthropic-server/pnpm-workspace.yaml create mode 100644 hives/anthropic-server/src/conversation.ts create mode 100644 hives/anthropic-server/src/forager.ts create mode 100644 hives/anthropic-server/src/growth.ts create mode 100644 hives/anthropic-server/src/http.ts create mode 100644 hives/anthropic-server/src/messages.ts create mode 100644 hives/anthropic-server/src/tools.ts create mode 100644 hives/anthropic-server/test/conversation.test.ts create mode 100644 hives/anthropic-server/test/e2e.test.ts create mode 100644 hives/anthropic-server/test/hum.ts create mode 100644 hives/anthropic-server/test/nurse-bee.ts create mode 100644 hives/anthropic-server/test/tools.test.ts delete mode 100644 hives/foragers.md create mode 100644 hives/grpc/tests/common/mod.rs create mode 100644 hives/grpc/tests/outlive.rs create mode 100644 hives/grpc/tests/stream.rs create mode 100644 hives/gsm-modem/src/gsm.rs create mode 100644 hives/gsm-modem/src/hum.rs create mode 100644 hives/gsm-modem/src/modem.rs create mode 100644 hives/ollama-server/src/conversation.rs create mode 100644 hives/ollama-server/src/growth.rs create mode 100644 hives/ollama-server/src/requester.rs create mode 100644 hives/ollama-server/tests/common/mod.rs create mode 100644 hives/ollama-server/tests/forager.rs create mode 100644 hives/ollama-server/tests/live.rs create mode 100644 hives/ollama-server/tests/nurse.rs create mode 100644 hives/openai-server/test/cancel-nurse.ts create mode 100644 hives/openai-server/test/hello.test.ts create mode 100644 hives/openai-server/test/holds.test.ts create mode 100644 hives/openai-server/test/keys.test.ts create mode 100644 hives/openai-server/test/requester.test.ts create mode 100644 hives/openai-server/test/responses.test.ts create mode 100644 hives/paid-oracle/src/tests.rs create mode 100644 hives/paid-oracle/tests/chain/mod.rs create mode 100644 hives/paid-oracle/tests/reach.rs create mode 100644 hives/twilio-sms/main_test.go delete mode 100755 hives/vercel-ai/install create mode 100644 hives/vercel-ai/pnpm-lock.yaml create mode 100644 hives/vercel-ai/src/conversation.ts create mode 100644 hives/vercel-ai/src/tools.ts create mode 100644 hives/vercel-ai/test/conversation.test.ts create mode 100644 hives/vercel-ai/test/done.mjs create mode 100644 hives/vercel-ai/test/e2e.test.ts create mode 100644 hives/vercel-ai/test/hum.ts create mode 100644 hives/vercel-ai/test/lab.ts create mode 100644 hives/wifi-p2p/app/src/main/java/hum/hive/wifip2p/Forage.kt create mode 100644 hives/wifi-p2p/app/src/main/java/hum/hive/wifip2p/Settings.kt create mode 100644 hives/wifi-p2p/app/src/main/java/hum/hive/wifip2p/Stream.kt create mode 100644 hives/wifi-p2p/app/src/main/res/xml/data_extraction_rules.xml create mode 100644 hives/wifi-p2p/app/src/test/java/hum/hive/wifip2p/FakeHumd.kt create mode 100644 hives/wifi-p2p/app/src/test/java/hum/hive/wifip2p/ForageTest.kt create mode 100644 hives/wifi-p2p/app/src/test/java/hum/hive/wifip2p/HumdTest.kt create mode 100644 hives/wifi-p2p/app/src/test/java/hum/hive/wifip2p/IdentityTest.kt create mode 100644 hives/wifi-p2p/app/src/test/java/hum/hive/wifip2p/SettingsTest.kt create mode 100644 hives/wifi-p2p/app/src/test/java/hum/hive/wifip2p/StreamTest.kt create mode 100644 hives/wifi-p2p/app/src/test/java/hum/hive/wifip2p/ThrumClientTest.kt create mode 100644 hives/wifi-p2p/app/src/test/java/hum/hive/wifip2p/ThrumTest.kt create mode 100644 hives/wifi-p2p/app/src/test/java/hum/hive/wifip2p/ToneTest.kt create mode 100644 hives/wifi-p2p/gradle/wrapper/gradle-wrapper.jar create mode 100644 hives/wifi-p2p/gradle/wrapper/gradle-wrapper.properties create mode 100755 hives/wifi-p2p/gradlew create mode 100644 hives/wifi-p2p/gradlew.bat create mode 100644 hum-paths/tests/env.rs create mode 100644 humd/tests/common/mod.rs create mode 100644 humd/tests/sids.rs create mode 100644 humd/tests/socket.rs diff --git a/THRUM.md b/THRUM.md index 5ee100af..7deb4466 100644 --- a/THRUM.md +++ b/THRUM.md @@ -345,7 +345,7 @@ the bloom broke off | field | type | meaning | |---|---|---| | `message` | `string` | what broke | -| `code` | `string?` | no-nurse when no nurse here or on a trusted peer grows the model; cannot-grow-on from a nurse that grows only from new words; empty-prompt for a prompt with neither words nor a graft | +| `code` | `string?` | no-nurse when no nurse here or on a trusted peer grows the model; nurse-gone from humd when the peer growing it left the ensemble mid-bloom, so it may be grown again; cannot-grow-on from a nurse that grows only from new words; empty-prompt for a prompt with neither words nor a graft | | `subtype` | `string?` | the cell's own word for what broke | | `nectar` | `any?` | what the bloom took before it broke off | @@ -755,14 +755,20 @@ a bee only through its hive, and knows it only by its key. The ensemble layer (peer humds, gossip) and the on-chain layer (`HumdRegistry`) are additive opt-ins on top. +Until a breath admits it, a connection is heard for nothing but its +`hello`: any other tone is answered with an `echo` whose `ok` is false +under its rid, and an `echo` or a `log` is dropped. One key is one bee: +when a second connection signed by the same key is admitted, humd closes +the first, and frees what it held as a closing connection does. + The handshake doesn't distinguish where the bytes came from. A hello arriving on humd's local Unix socket and a hello bridged through the ensemble from a peer humd hit the same code path. Local-dev runs, local-prod-with-systemd setups, and distributed mesh deployments all -exercise the same `chi:"hello"` shape — see -[hives/foragers.md](../hives/) for the three deployment -paradigms (local-dev / local-prod / distributed) that share this -single protocol. +exercise the same `chi:"hello"` shape. See +[hives, Run a hive](./hives/README.md#run-a-hive) for the three +deployment shapes (local dev, the ensemble, a managed service) that +share this single protocol. ```json { @@ -858,6 +864,14 @@ with no `modelId` goes to any nurse with room, which grows it with a model of its own, and one no nurse grows is refused with `code: "no-nurse"`. +A bloom's tones go to the connections that prompted or attached under its +sid, and to no one else; a sid no connection holds reaches no one. A +`cleanup` frees what humd keeps for the sid, and so does the close of the +last connection that held it, so a forager that takes a sid up again on a +new connection `attach`es it first, and names its `cwd` again. When the +peer growing a bloom leaves the ensemble mid-bloom, humd breaks the bloom +off with `code: "nurse-gone"`, and its forager may prompt it again. + #### Graft A prompt's `graft` is the blooms of its sigil before it, oldest first, as @@ -916,7 +930,11 @@ humd → nurse: chi:"tendril-result" { sid, callId, output, isError } A reach no bee can serve is answered by humd itself, with a `tendril-result` whose `isError` is true and whose `output` says why, so -the cell does not wait for an answer that cannot come. +the cell does not wait for an answer that cannot come. A bee that cannot +or will not grasp a reach answers it the same way. An `error` sent under +a reach's `callId` reaches the cell as that failed grasp too, its +`message` as the `output`, its `code` and `subtype` as the `result`, and +its `ext` whole. #### `bash` @@ -1214,8 +1232,9 @@ interrupts and terminations: its life is the pipe's. A tone can also travel whole, as one message, with no line to split. hum's gRPC hive carries it as `Tone { chi, sid, rid, body }`: the JSON of -the tone as `body` bytes, with `chi`, `sid` and `rid` lifted out so the -bridge can multiplex without parsing a body. Inside one humd, the ensemble +the tone as `body` bytes, with `chi`, `sid` and `rid` lifted out so a +client can match what comes back without parsing a body; `body` is +authoritative, and the bridge reads it. Inside one humd, the ensemble hands tones to the kernel as values, and `peer-add` and `peer-remove` travel only that way. Either way the tone is the same object, and a medium that carries it whole has no framing to specify. @@ -1276,10 +1295,10 @@ link, or the wire itself. The wire, as hum's client speaks it: - [`hum.thrum`](./hum.thrum): the thrum itself, its envelope, range, shapes, kinds, patterns and advisories. The file wins on what a tone is. - [GLOSSARY.md](./GLOSSARY.md): what each word means. -- [`thrum-core`](../thrum-core): the Rust reading of it (chi enum, views, helpers), held to the file. -- [`hum-thrum`](../hum-thrum): the client, sans-IO at its core, and the link. -- [`thrum-clients`](../thrum-clients): the generated client for TS, Python, Go and C. -- [`codegen`](../codegen): what reads the file out, into the clients and into this document. -- [`ensemble/README.md`](../ensemble/README.md): inter-humd routing. -- [`hives/foragers.md`](../hives/foragers.md): typology and propensity axes. +- [`thrum-core`](./thrum-core): the Rust reading of it (chi enum, views, helpers), held to the file. +- [`hum-thrum`](./hum-thrum): the client, sans-IO at its core, and the link. +- [`thrum-clients`](./thrum-clients): the generated client for TS, Python, Go and C. +- [`codegen`](./codegen): what reads the file out, into the clients and into this document. +- [`ensemble/README.md`](./ensemble/README.md): inter-humd routing. +- [`hives/README.md`](./hives/README.md): roles, propensity axes, and how to build and run a hive. - [adiled.github.io/hum](https://adiled.github.io/hum/): the docs site. diff --git a/ensemble/src/lib.rs b/ensemble/src/lib.rs index 887f2c6d..553a2059 100644 --- a/ensemble/src/lib.rs +++ b/ensemble/src/lib.rs @@ -350,8 +350,10 @@ impl Ensemble { pub fn remove_peer(&self, id: &Hid) { self.kad.forget(id); - if let Some(p) = self.peers.write().remove(id) { + let gone = self.peers.write().remove(id); + if let Some(p) = gone { p.conn.close(); + self.inbox.publish(peer_remove_tone(&self.me, id)); } } @@ -1789,4 +1791,21 @@ mod invariants { "a dead peer must not route" ); } + + #[tokio::test] + async fn a_removed_peer_is_told_to_the_kernel_once() { + let me = Hid::random_humd(); + let peer = Hid::random_humd(); + let ensemble = Ensemble::new(me); + let (mine, _theirs) = InMemoryEndpoint::pair_concrete(me, caps(), peer, caps()); + ensemble.add_peer(mine); + let mut inbox = ensemble.subscribe(); + + ensemble.remove_peer(&peer); + ensemble.remove_peer(&peer); + let told = inbox.try_recv().expect("the kernel hears the peer is gone"); + assert_eq!(told["chi"], "peer-remove"); + assert_eq!(told["humd_id"], hex::encode(peer.as_bytes())); + assert!(inbox.try_recv().is_err(), "a peer already gone is not told again"); + } } diff --git a/hives/README.md b/hives/README.md index 77b5d719..778b9c52 100644 --- a/hives/README.md +++ b/hives/README.md @@ -9,7 +9,7 @@ description: "how to build a hive, install it on a humd, and the bees that humd ## The vocabulary, once -A humd is a coordination node: it admits bees, routes between them and gossips with its peers, and hosts no cells. A **nest** is the cells a hive hosts, which it turns on by using [`hum-nest`](../hum-nest). A **hive** is the kind, such as `claude-cli`, `openai-server`, or `paid-oracle`: a directory with an `Orchfile`. It does not run on its own. A **bee** is a participant: a key its hive entered, and whatever signs a hello with that key: a process on this machine, a runtime hosting many bees, a service on a phone. A bee does one job, its **role**, set when its hive enters it. A **forager** prompts: it brings work in from an outside wire, whether that wire is HTTP, SMS, a smart contract, or a paid API, and carries the bloom back. A **nurse** grows blooms: it accepts `chi:"prompt"` and unfurls the bloom in its cells until it wilts. A **builder** lends its host: it runs the thrum's `bash` for the blooms that build there. An **attendant** serves the fixed, named tools it lists. A hive that wants two jobs enters two bees. While a bee is mid-handshake it is a **nestler**, and once registered it is **nestled**. +A humd is a coordination node: it admits bees, routes between them and gossips with its peers, and hosts no cells. A **nest** is the cells a hive hosts, which it turns on by using [`hum-nest`](../hum-nest). A **hive** is the kind, such as `claude-cli`, `openai-server`, or `paid-oracle`: a directory with an `Orchfile`. It does not run on its own. A **bee** is a participant: a key its hive entered, and whatever signs a hello with that key: a process on this machine, a runtime hosting many bees, a service on a phone. A bee does one job, its **role**, set when its hive enters it. A **forager** prompts: it brings work in from an outside wire, whether that wire is HTTP, gRPC, SMS or a DTN bundle, and carries the bloom back. A **nurse** grows blooms: it accepts `chi:"prompt"` and unfurls the bloom in its cells until it wilts. A **builder** lends its host: it runs the thrum's `bash` for the blooms that build there. An **attendant** serves the fixed, named tools it lists, and the scents of the models it names. A hive that wants two jobs enters two bees. While a bee is mid-handshake it is a **nestler**, and once admitted it is **nestled**. A honeybee on the plant pot by your bed is still a bee; you just need to know what kind it is to understand it. So it goes here. A humd understands a bee only through its hive, and **installing** a hive on a humd is what teaches the humd that kind. The hive then **enters** bees, signing them in one key each, and a bee nestles by signing its hello with its key, wherever it lands from. humd refuses a hello that is unsigned, from a key never entered, or naming a hive it was never taught. humd never links a hive in at build time and hum never starts a bee; bees attach at runtime, in any language, run however their hive likes. @@ -18,31 +18,34 @@ A honeybee on the plant pot by your bed is still a bee; you just need to know wh | hive | role | binary | wire it speaks | |---|---|---|---| | [`claude-cli`](claude-cli) | nurse | `claude-cli-nurse` | `claude -p` stream-json over a pipe | -| [`claude-repl`](claude-repl) | nurse | `claude-repl-nurse` | claude REPL over a PTY | -| [`openai-server`](openai-server) | forager, nurse, attendant | `openai-server` | OpenAI `/v1/chat/completions`, `/v1/responses`, `/v1/completions`, `/v1/embeddings` | -| [`anthropic-server`](anthropic-server) | forager | `anthropic-server` | Anthropic `/v1/messages` | -| [`ollama-server`](ollama-server) | forager, nurse | `ollama-server`, `ollama-nurse` | Ollama `/api/chat`, both ways | -| [`vercel-ai`](vercel-ai) | forager | `vercel-ai` | Vercel AI SDK `LanguageModelV3` | -| [`bash`](bash) | builder | `bash-builder` | a bash shell per sigil, on its own host | -| [`paid-oracle`](paid-oracle) | attendant | `paid-oracle` | x402 over a tendril, USDC per call | -| [`web2`](web2) | attendant | `web2` | OpenAPI and MCP, as one command-line tendril, `wrepl` | -| [`grpc`](grpc) | forager | `grpc-forager` | gRPC bidi stream | +| [`claude-repl`](claude-repl) | nurse | `claude-repl-nurse` | the `claude` REPL over a PTY | +| [`dwarfstar`](dwarfstar) | nurse, one bee per model | `dwarfstar-bee` | the DwarfStar engine (`antirez/ds4`) linked into the bee, on Metal | +| [`ollama-server`](ollama-server) | forager, nurse | `ollama-server`, `ollama-nurse` | Ollama `/api/chat` and `/api/generate`, both ways | +| [`openai-server`](openai-server) | forager, nurse, attendant | `node dist/index.js` | OpenAI `/v1/chat/completions`, `/v1/responses`, `/v1/completions`, `/v1/embeddings` in front, an OpenAI-compatible API behind | +| [`anthropic-server`](anthropic-server) | forager | `node dist/index.js` | Anthropic `/v1/messages`, streamed or not | +| [`vercel-ai`](vercel-ai) | forager | `createHum`, in the app's own process | Vercel AI SDK `LanguageModelV3` | +| [`grpc`](grpc) | forager | `grpc-forager` | gRPC bidi stream, tones carried whole | | [`twilio-sms`](twilio-sms) | forager | `twilio-sms` | Twilio Messaging webhook | | [`gsm-modem`](gsm-modem) | forager | `gsm-modem` | GSM AT-command serial SMS | -| [`bp7`](bp7) | forager | `bp7-forager` | Bundle Protocol v7 (DTN) | -| [`hum-nest`](../hum-nest) | library | n/a | `serve_nurse`: the nest, and the MCP its cells reach tools through | +| [`bp7`](bp7) | forager | `bp7-forager` | Bundle Protocol v7 (DTN) over UDP | +| [`wifi-p2p`](wifi-p2p) | forager | `WifiP2pBeeService`, an Android app | the thrum's stream over WiFi Direct TCP | +| [`bash`](bash) | builder | `bash-builder` | a bash shell per sigil, on its own host | +| [`paid-oracle`](paid-oracle) | attendant | `paid-oracle` | x402 over a tendril, USDC per call | +| [`web2`](web2) | attendant | `web2 serve` | OpenAPI and MCP, as one command-line tendril, `wrepl` | -Listing here is editorial, reserved for exemplars the maintainers keep. Your hive works the moment it handshakes a humd, whether or not it lives in this repo. +Listing here is editorial, reserved for exemplars the maintainers keep. Your hive works once a humd has it installed and its bee nestles there, whether or not it lives in this repo. [`hum-nest`](../hum-nest) is not a hive: it is the library a Rust nurse hosts its cells with (`serve_nurse`), and the MCP its cells reach tools through. ## Propensity, the three axes of a bee -Every bee sits somewhere on three orthogonal axes, declared in its hello's `propensity`. They decide which `chi` it can keep. +Every bee sits somewhere on three orthogonal axes, declared in its hello's `propensity`. They describe which `chi` it can keep. humd carries them in the bee's manifest and gossips them with it, and routes on none of them. The first axis is **statefulness**. A `stateful` bee holds its own session graph. A `convention-stateful` bee keeps no server state but speaks a protocol that implies continuation. A `stateless` bee treats each call as a fresh sid. A `transport-only` bee is pure bytes in and bytes out. The second axis is **richness**. A `rich` bee hands humd cwd, MCP configs, and the blooms before its prompt as its `graft`. A `medium` bee hands tools, system, and content. A `lean` bee hands only content and system. An `opaque` bee passes JSON straight through. -The third axis is **wire**, the outside contract the bee translates to, for example `openai/chat`, `twilio/sms`, `x402/tendril`, or `grpc/bidi`. +The third axis is **wire**, the outside contract the bee translates to, for example `openai/api`, `twilio/sms-webhook`, `x402/tendril`, or `grpc/bidi`. + +The values are conventions, not a closed set: `serve_nurse` declares its statefulness as `stateful_session`, `stateless_per_call` or `ephemeral_per_call`, and its hive's name as its wire. A forager picks a contract that already exists in the wild and hides the rest of thrum behind it. A lean bee drops the tones it cannot express, while a rich one forwards everything. @@ -50,9 +53,9 @@ A forager picks a contract that already exists in the wild and hides the rest of ### 0. Be a hive -Put an `Orchfile` at the root of your hive's directory. That is what makes it a hive, and the directory's name is the hive's name. hum recognises one unit in it, `hive-install`, and nothing else. `hum hive install` records the hive on the humd and then runs that unit in the foreground, with `HUM_SRC` set to your directory, `HUM_HIVE` to your hive's name, `HUM_HIVE_STATE` to your hive's state directory, and `hum` on its `PATH`; if the unit fails, the hive is not installed. The installed Orchfile carries the same three as `ARG`s, so a unit can say `${HUM_HIVE_STATE}`. +Put an `Orchfile` at the root of your hive's directory. That is what makes it a hive, and the directory's name is the hive's name, a plain name of letters, digits, `-`, `_` and `.` that does not start with `.`. hum recognises one unit in it, `hive-install`, and nothing else. `hum hive install` records the hive on the humd and then runs that unit's `RUN` with `sh -c` in the foreground, in the unit's `WORKDIR` or else your directory, with the unit's `ENV`, `HUM_SRC` set to your directory, `HUM_HIVE` to your hive's name, `HUM_HIVE_STATE` to your hive's state directory, and `hum` on its `PATH`; if the unit fails, the hive is not installed. An `ARG` your Orchfile declares takes its value from the environment of `hum hive install`, which the unit inherits. The copy hum records carries `HUM_SRC`, `HUM_HIVE` and `HUM_HIVE_STATE` as `ARG`s. A supervisor that reads your own Orchfile sees an `ARG` only as you pass it: the bundled `install` passes orchd every `ARG` the Orchfile declares that is set in its environment, which is how a unit there can say `${HUM_HIVE_STATE}`. -What the unit does is yours: build, fetch a model, start processes and keep them alive with whatever supervisor you like. The bundled hives use one small `install` script each that builds and runs their process under [orchd](https://github.com/adiled/orchd) in their own namespace; the process enters its own bee when it starts. Every other unit in the Orchfile is only what your own tooling makes of it. +What the unit does is yours: build, fetch a model, start processes and keep them alive with whatever supervisor you like. The bundled hives that run a process use one small `install` script each, which runs the hive's `build` and then grows the hive's units under [orchd](https://github.com/adiled/orchd), in the namespace `hum-` with its state in `${HUM_HIVE_STATE}/orch`: the unit named after the hive, or every unit for a hive with several (dwarfstar, one per model; ollama-server, its forager and its nurse). The process enters its own bee when it starts. A unit's settings are empty `ARG`s passed on as its `ENV`, as in `bp7/Orchfile`, so `BP7_ROUTE=router.local:4556 hum hive bp7 install` sets one, and an empty one keeps the process's own default. vercel-ai, whose bee lives in the app that imports it, only builds. Every other unit in the Orchfile is only what your own tooling makes of it. ``` ARG HUM_SRC= @@ -66,105 +69,111 @@ RUN ${HUM_SRC}/target/release/my-bee RESTART always ``` -A hive whose bees live elsewhere (a phone, another machine) can have an Orchfile with no units at all; installing it still teaches the humd the kind, and its bees are entered on the humd they land on. +A hive whose bees live elsewhere (a phone, another machine) can have an Orchfile with no units at all; installing it still teaches the humd the kind. Its bees are entered on the humd they land on, and each key goes to wherever its bee runs, as `wifi-p2p`'s goes to the phone. -A hive keeps its state in `$XDG_STATE_HOME/hum/hives//` (`${HUM_HIVE_STATE}`) and its config in `$XDG_CONFIG_HOME/hum/hives/.json`; anything else it writes under a hum root is moved to that root's `tmp/` while humd runs. +A hive keeps its state in `$XDG_STATE_HOME/hum/hives//` (`${HUM_HIVE_STATE}`) and its config in `$XDG_CONFIG_HOME/hum/hives/.json`. Anything else it writes under a hum root is moved to that root's `tmp/` while humd runs (in the config root, once it has sat untouched for ten minutes), and on to `~/.hum-archive/` after a week there. ### 1. Speak thrum -Use hum's client; a hive writes only what its bee does. In Rust that is `hum_thrum::Bee::link`, or `hum_nest::serve_nurse`, which runs a nurse on it, so you implement one trait method and ship a `main.rs`. In TypeScript, Python, Go and C it is the client in [`thrum-clients/`](../thrum-clients), which runs `hum bee link` for you. Either way the client enters the bee, signs its hello, reads humd's verdict, reconnects when humd restarts, and checks every tone against the thrum, the same in every language ([THRUM.md, The client](../THRUM.md#the-client)). +Use hum's client; a hive writes only what its bee does. In Rust that is `hum_thrum::Bee::link`, or `hum_nest::serve_nurse`, which runs a nurse on it, so you implement `Nurse` (`raise`, which turns an `Egg` into a `Cell`, and `ephemeral`) and ship a `main.rs`. A Rust hive is a crate of its own, with its own `[workspace]`, that takes hum's crates as dependencies; humd never builds it. In TypeScript, Python, Go and C it is the client in [`thrum-clients/`](../thrum-clients), which runs `hum bee link` for you and finds `hum` at `$HUM_CLI`, else on `PATH`, else at `~/.local/bin/hum`. Either way the client enters the bee, signs its hello, reads humd's verdict, reconnects when humd restarts, and checks every tone against the thrum, the same in every language ([THRUM.md, The client](../THRUM.md#the-client)). A refusal is final: the client stops, and the bee then exits non-zero, so that whatever supervises it decides what comes next. A bee that cannot run hum's client, such as `wifi-p2p` on a phone, speaks the wire itself ([THRUM.md, Speaking the wire yourself](../THRUM.md#speaking-the-wire-yourself)). ### 2. Handshake, the hello contract -Give the client your role and your hello's own fields. It adds `hive`, `role`, `protoVersion` and the signature itself. +Give the client your role and your hello's own fields. It adds `chi`, `rid`, `hive`, `role`, `protoVersion` and the signature itself, and ignores any value you give for them with the `client-owned` advisory. | field | meaning | |---|---| | `hive` | the hive you belong to, for example `"openai-server"`; it must be installed on the humd you land on; the client sets it | | `role` | the one job you do: `forager`, `nurse`, `builder` or `attendant`, the one your hive entered you for; the client sets it | -| `hid`, `pubkey`, `signedAt`, `signature` | who you are, proven: the id your hive entered you as, and a signature by its key (below); the client sets them | -| `models` | model ids a nurse serves | -| `tools` | tool defs an attendant serves, each `{name, description, inputSchema}` | -| `builds` | a forager's blooms build where it is: a prompt of its with no `cwd` builds at its humd's home | +| `hid`, `from`, `pubkey`, `signedAt`, `signature` | who you are, proven: the id your hive entered you as, and a signature by its key (below); the client sets them | +| `models` | a nurse: the model ids it grows; an attendant: the models it distils a `scent` by | +| `tools` | an attendant: the tool defs it serves, each `{name, description, inputSchema}`; humd ignores them from any other role | +| `builds` | a forager: its blooms build where it is, so a prompt of its with no `cwd` builds at its humd's home | +| `headroom` | a nurse: `{freeSlots, totalSlots}`, how many more cells it can take and how many it holds at most | +| `propensity` | `{statefulness, richness, wire}` (above) | +| `chis` | the chi values you speak; humd sends a bee that lists them only those among the tones it fans out to a sigil, and a bee that lists none every one | | `version`, `protoVersion` | your release, and the `THRUM_VERSION` the client speaks, which it sets | -| `chis` | the chi values you speak; advisory | | `source` | URL to your hive's repo; informational, for humans only | +| `bind` | an address the bee may be reached at, `{host, port, scheme}`, carried in its manifest | > ### A bee signs its hello with the key its hive entered > -> A bee is entered with `hum bee enter --hive --role --as