Introducing session-bus
session-bus lets Claude Code sessions on different machines send each other messages over a Tailscale tailnet. This post covers why it exists, how it works, and what has and has not been verified.
The problem
I run several Claude Code sessions at once: a few on a laptop, some on a server. They cannot see each other. To ask the server session whether it touched the migrations, I switch terminals and type the question myself. To hand it a task, I copy text across.
What I wanted was small: a session should be able to address another session by name, leave it a message, and have that message still be there later.
The design in brief
- One Node 24 process runs on one machine in the tailnet. It stores mail in SQLite (WAL mode) and exposes 7 MCP tools over HTTP:
register,list_sessions,send,broadcast,inbox,ack,unregister. - It listens on loopback and is exposed with
tailscale serve, or binds directly to its own tailnet address. It refuses any other bind address. - Every request needs a bearer token. Tokens belong to principals, carry
bus:readand/orbus:write, and last at most 90 days. Scope checks happen before any tool code runs. - Delivery is at-least-once. Mail stays until the recipient acks it. Unacked mail is never pruned.
- Pull is the contract. Any session can call
inbox. - Push is an accelerator. An optional per-session shim polls
inboxevery 5 seconds and pushes new mail into an idle session through Claude Code channels. Channels are a research preview, so push is kept optional: if it breaks, pull still delivers.
Message bodies are treated as untrusted input. There is no permission relay, and the shim pushes only messages from allowlisted principals.
What is verified
- The test gate (
scripts/gate.sh 6): typecheck, build, the unit and integration tests, a smoke test, and an end-to-end test that kills the server withkill -9and reads the message back after restart. - On a real tailnet: the server behind
tailscale servewith HTTPS, unauthenticated requests refused, a message round trip between two Macs, and a push into an idle session on the bus host.
What is not
- Push across machines has not been run. Push to an idle session was seen on one machine.
- The systemd unit renders and passes static checks but has not run on Linux.
- Whether HTTP MCP servers can act as channels (which would remove the shim) is untested.
- There is no load test or benchmark.
The verification log lists each item.
Trying it
The getting started page runs a bus on loopback and sends a message between two local sessions in about five minutes. Spreading it across machines takes a tailnet, an ACL grant and one token per machine; the guides cover each step.