Ten minutes: install two binaries, scaffold a team folder, connect one agent,
and watch work flow through the journal, tasks and queues. Everything below is
plain files — at any step you can cat what just happened.
Want full worked scenarios instead of a walkthrough? See recipes.
Three things, two of them one npm package:
hubd— the daemon: an MCP server your agents talk to (stdio, zero deps).hub— the CLI: the same data for you, no LLM required.- data — a folder you own (
HUBD_DIR, default~/.hubd): markdown + JSONL. Code and data never mix; upgrading the code never touches the data.
npm i -g @bzdos/hubd
hub doctorThe first run creates ~/.hubd and materialises HUBD.md — the agent-facing
protocol, regenerated from the installed version (never edit it by hand).
hub doctor also prints an environment: section when something outside the
code needs attention — it will tell you about HUBD_AGENT below.
One-off, without installing: npx -p @bzdos/hubd hub doctor.
hub initThis creates, in the current directory (or hub init <path>):
AGENTS.md— your team constitution: roles, policy, decision rights. Yours to write; hubd never touches it.INBOX.md— the human-readable handoff journal (newest on top).queues/— per-role message queues (files appear on first send).specs/SPEC_template.md— an assignment template for delegated work.
Rule of thumb: AGENTS.md is law you write, HUBD.md is the manual the tool
maintains.
claude mcp add --scope user hubd --env HUBD_AGENT=dev-myproject -- npx -y @bzdos/hubdSet HUBD_AGENT on day one. Every write names its author — journal entries,
tasks, queue messages — and the field is required. The floor catches calls
that forget it: they get attributed to dev-myproject plus a short per-session
suffix instead of failing. Name the function, not the model (dev-hubd,
reviewer-api) — model and client names (claude, cursor) are refused,
because many sessions share them and the journal is forever.
Other clients: any MCP client that can run npx -y @bzdos/hubd over stdio
works the same. No MCP at all? Paste the matching block from
prompts/ — every model that can read and write files can join.
Verify: restart the client, then ask the agent to call hub_onboarding —
it returns the protocol. That call is the whole onboarding.
In a project folder, tell the agent something like:
Sync this project into the hub: call hub_context with your cwd, then hub_sync with a digest of where the project stands.
The agent calls hub_sync {path, digest, agent}; hubd collects the git facts
itself (branch, commits since last sync, dirty count) — agents never retype
what git already knows. You check the result with your own eyes:
hub status # every project, one line each
hub get myproject # one project in depth: card + journal + locksThe channel table is the one thing worth memorising (it is the #1 mistake):
| you want to say | channel | lives |
|---|---|---|
| "I'm working on X — don't clobber" | hub claim |
expires (TTL) |
| "this needs doing" | hub task add |
until closed |
| "this is now true / decided / shipped" | hub report |
forever |
| "agent, do this" | hub queue send |
until consumed |
| "starting / still going" | nothing | — |
A session ends with ONE structured report — prefix-tagged lines that fan into the project card's sections:
hub report -p myproject --agent dev-myproject <<EOF
DECIDE: ship 0.2 without SSO | demand unproven, two asks total
FACT: the registry JWT expires in minutes, not hours
NEXT: redeploy staging under 0.2.0
DONE: 42, 43
EOFDONE: closes tasks by id, no confirmation — an id that matches nothing comes
back as doneMissed, check it. "What changed" is read from git, never listed
by hand.
hub brief # morning brief: tasks by deadline, journal, locks, queues
hub inbox # only what needs a DECISION: blocked/overdue/unassigned
hub serve # read-only kanban on localhost:7777The board's only button is ⚙ Rules, and it opens AGENTS.md. Cards move because agents move them; you manage the rules, not the agents.
hub queue send worker "run the release checklist for 0.2" --from owner-alexA waiting session picks it up and goes back to waiting — no polling you:
hub_queue_wait(worker) -> task? do it -> hub_report -> hub_heartbeat -> wait again
One live waiter per role by default: a message goes to exactly one reader, so
two sessions on one role split work instead of duplicating it. A role listed in
<team>/subscriber-roles.json broadcasts instead — every waiting session gets
its own cursor and sees every message. Decisions only a human can make go to an
owner queue (list those role names in HUB/owner-roles.json) and hub brief
rolls them up as "N buttons waiting".
Your data is a folder, so sync it like one:
cd ~/.hubd && git init && git add -A && git commit -m "hub"
git remote add origin ssh://you@yourhost/~/hub.git && git push -u origin mainOn the other machine: install the package from npm, clone the data, done. Every
log is per-host and append-only (journal.<node>.jsonl, tasks.<node>.events.jsonl,
queues/<role>.<node>.queue.md), so two machines never conflict on one file —
a plain pull/push loop (cron it) is a working mesh. No GitHub required.
npm i -g @bzdos/hubd@latestThe next hub run refreshes HUBD.md to match. When an upgrade needs
something outside the code — a config variable, a role declaration, a protocol
section worth re-reading — hubd tells the agents itself: hub_whatsnew returns
an environment list with what is wrong, the remedy, and who can fix it.
hub doctor shows you the same list. Nothing blocks, nothing needs
acknowledging: an item disappears when its condition does.
- Recipes — complete scenarios: a standing worker, an orchestrator fleet, owner buttons, harvesting a chat, infra topology.
hubd-company/(repo only) — a ready org template: constitution, role onboardings, recipes, a weekly chronicle.- Self-hosting — one shared hub for a team over HTTP.
- Interop — reading the hub with grep, Obsidian, anything.