Skip to main content

Introducing session-bus

· 3 min read

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:read and/or bus: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 inbox every 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 with kill -9 and reads the message back after restart.
  • On a real tailnet: the server behind tailscale serve with 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.