Repository navigation
65 commits since v3.0.0 (2026-05-22). The theme is consolidation: the repo became a real monorepo, the live umbrel box was reconciled back into git, and the swarm's public surface learned to tell the truth about itself.
The repo is now a packaged monorepo
apps/ holds deployables, packages/ holds installable shared libraries. Services import shared code as normal packages — there are no sys.path inserts reaching across the tree any more.
packages/swarm-core— committee deliberation, BEAM-SCoT, the {7,3} hyperbolic kernel, lore vault, agent registry, session store, schedulers. Absorbs the oldpython/,kernel/,core/andllm/.packages/swarm-tg— Telegram formatting and the swarm task client, shared by all four bots. Wasshared/.apps/<name>— one deployable each;services/is retired.
This closes a long-standing failure mode. The repo used to keep hand-synced duplicates — tg_fmt.py in four bots, swarm_heartbeat.py in four services, an entire forked python/ under smolting. They drifted, and the forks quietly became the newer side.
Honest status: GET /api/swarm
The public feed previously answered one question — which of four agents is alive — which left a deliberately dormant app indistinguishable from a crashed one.
/api/swarm separates the two ideas that were conflated. state is declared (active / asleep / retired) and comes from a checked-in registry; online is observed from the mesh. Keeping them apart is what makes state: "active", online: false expressible — precisely the alarm condition, and previously unrepresentable.
Also new: doors (a heartbeat says a loop ran; a door says a named capability is being asserted), lineage between agents, a declared offers registry, and a query envelope that reports what it withheld — silent truncation is how a status feed starts lying.
Every field is declared or observed, and the distinction is the safety argument: declared fields already render publicly, while observed fields are coarsened to buckets and booleans, never numbers, because cadence is fingerprintable. The endpoint never 500s — with the mesh unreachable it still serves the declared roster with observation degraded. /status is unchanged and kept as an alias.
Inference goes through one privacy proxy
All five LLM services now route through apps/proxy, an OpenAI-compatible privacy proxy with a content-aware auto-router (model: "auto") and a free-first provider cascade. It strips fingerprinting headers, scrubs PII, stores no prompt or response content by default, and does per-project usage accounting. Upstream egress can leave over a SOCKS5 tunnel so requests never carry the swarm's own address.
Mesh reliability
- Inter-agent coordination wired end to end, with the chan → Hermes result round-trip completed via
reply_key. - Two schedulers were silently parked with
next_run_time=None— the Hermes inbox poller andoracle_post. The poller had been fanning broadcasts at dead agents, rotting the queues; both are unpaused and the queues self-heal. - Heartbeat parsing is shared rather than copied, with alias resolution so
redactedinternandredactedbuilderresolve to their canonical agents.
Security and correctness
apps/runtimenow binds uvicorn to loopback, so the sub-agent service is reachable only from the node it runs on.- Every agent-facing website endpoint is served cross-origin correctly.
- The proxy stops three known-wasted upstream hops and emits a daily error rollup.
- Admin Telegram no longer receives
status_reportspam.
CI
A Python checks workflow now imports every swarm_core and swarm_tg module, byte-compiles the tree, and asserts the layout invariants. This is the check that would have caught every layout outage this repo has had — a moved directory used to break imports at runtime, in production, rather than in CI. Its first real run immediately earned its keep, surfacing an undeclared numpy dependency in the hyperbolic kernel.
Known issues
Validate Agentsis red:agents/default.character.jsonlacks the top-levelidandgroupsits siblings have. Pre-existing, and unresolved because whether that file is an agent or a template is a call for a maintainer, not a guess.apps/website/build.pyexits early on a missingapps/terminal/system.prompt.md, so the generated roster does not regenerate end to end.