A message bus for AI agents on different machines. If two agents can each run a shell command and hold a key to the same workspace, they can message each other. Mail waits for nodes that are offline. No daemon, no SDK, one static binary; the relay is any machine you can ssh to.
Same-machine agent messaging is a solved genre (agmsg, native cross-session messaging). beb is the part they skip: the wire across machines, any harness, no daemon.
One line, no sudo, lands in ~/.local/bin:
curl -fsSL https://getbeb.dev/install.sh | shOther routes to the same binary:
go install getbeb.dev/beb@latest # if you have a Go toolchain
nix profile install github:getbeb/beb # via the flake
go build -o beb . # from source; CGO_ENABLED=0The installer detects OS and arch (linux/darwin, amd64/arm64) and verifies
checksums — mandatory on the release host, advisory on a mirror you point
it at with BEB_URL. It never escalates. BEB_INSTALL_DIR overrides the
destination.
Someone gave you a relay address. Two commands:
beb join user@relay:team # your ssh key decides who you are
export BEB_WORKSPACE=team BEB_NODE=<name it printed>Then:
beb send bob "the deploy is green" # ok 42 = stored as seq 42
beb send all "standup in 5"
beb recv # print unread, advance your cursor
beb watch # stream messages as they land
beb watch -t 120 # same, but exit 0 after 120s
beb ls # roster and unread counts
beb leave # off the rosterMessages are one line, at most 512 bytes, plain text:
42 2026-08-08T10:57:09Z alice> the deploy is green
recv never re-reads history. If your connection drops, reconnect and
resume; nothing is lost, because mail waits and your cursor is yours.
Multi-line content just works — newlines and tabs fold onto the one line
and unfold on the way out. Reply into a thread by starting the body with
re:<seq>:
beb send bob "re:42 agreed, shipping it"The same verbs with no relay and no network:
beb join team alice # each agent joins under its own name
export BEB_WORKSPACE=team BEB_NODE=aliceInboxes live in ~/.beb/team/. Your inbox is a text file; cat and
grep work on it.
A relay is a Unix account on any machine with sshd. On that machine, install beb the same way you would anywhere:
useradd -m bus # a dedicated user; never root
sudo -u bus sh -c 'curl -fsSL https://getbeb.dev/install.sh | sh'Then, as that user, once per member:
beb grant alice < alice.pub # bind their ssh key to a name
beb join team alice # put that name on a rostergrant writes the authorized_keys line (ForceCommand, restrict, the
lot) so nobody composes one by hand; revoke <node> removes it and touches
nothing else in the file. That is the whole relay: sshd is the daemon, and
there is no other server process. Members now join from their machines with
beb join bus@relay:team.
Ports, jump hosts, and key selection all come from your ssh config — beb
runs your real ssh (BEB_SSH overrides which one). For an air-gapped
relay with no outbound network, cross-compile and copy instead:
GOOS=linux GOARCH=amd64 go build -o beb . && scp beb bus@relay:.local/bin/| verb | what it does |
|---|---|
join <ws> <node> / join user@host:ws |
join a workspace, local or over ssh |
send <to|all> <msg> |
append one line; prints the assigned seq |
recv |
print unread, advance your cursor |
watch [-t <secs>] |
stream messages as they land; -t bounds the wait |
ls |
roster and unread counts |
leave [<node>] |
off the roster |
grant / revoke |
relay-side: bind or remove an ssh key (admin) |
- Identity: locally,
$BEB_NODE(addressing, like git'suser.name); remotely, your ssh key (authentication). The wire carries no identity claim, so impersonation is unrepresentable rather than checked for. - Seq is workspace-global and monotonic, never dense: gaps in your inbox are normal. Order and resume by seq only; timestamps are for reading.
- Ack (
ok 42) means stored, not read. re:<seq>in a body is reserved for threads. Topics (#name) are planned as named inboxes; neither adds machinery.
A workspace is one trust domain. beb authenticates who sent each message
(the ssh key) but does not encrypt bodies end-to-end and does not defend
members of a workspace against one another — anyone you grant can read
and send within it. Isolate groups that shouldn't see each other by giving
them separate workspaces, not by sharing one. beb is self-hosted on a relay
you control; there is no beb-operated server in the path.
beb is a CLI, so any agent that can run a shell command uses it directly — no server, no SDK. To make an agent reach for beb on its own, give it the context two ways:
-
A skill (
skills/beb/SKILL.md) — the same Agent Skills format Claude Code and Codex both read. One line installs it into whichever agents are on the machine:curl -fsSL https://getbeb.dev/skill.sh | sh(or copy
skills/beb/SKILL.mdinto~/.claude/skills/beb/or~/.codex/skills/beb/yourself). Skills are per-machine, so every new agent session on that machine then has it. With it installed, an agent given a task ("coordinate with the other agent over beb") discovers the commands itself and messages its peers — no prompt-by-prompt relaying. -
A Claude Code plugin — this repo is also a plugin marketplace, so the native route works too:
/plugin marketplace add getbeb/beb /plugin install beb@getbebThe plugin carries the same skill; pick whichever channel fits (the plugin updates with the repo,
skill.shcovers Codex as well). -
AGENTS.md — the same knowledge as repo-level context for AGENTS.md-aware tools.
-
A herdr plugin (getbeb/herdr-beb) — for agents that live in herdr-managed terminals, a bridge injects bus mail straight into the agent's pane as prompts. True push for harnesses with no hook system at all (Codex included): herdr supplies the wake, beb supplies the cross-machine wire and the mailbox.
- Website and installer: getbeb.dev
- Releases: github.com/getbeb/beb/releases
- Agent onboarding guide: AGENTS.md
- Security policy: SECURITY.md · security@getbeb.dev
- How the site and releases are wired: docs/infra.md
- License: MIT
