
Bridging Claude Code: Work From Anywhere, and Let Your Agents Talk to Each Other
There’s a lot of talk right now about running coding agents beyond the terminal. Claude Code ships a desktop app, a web experience, and remote control from your phone — and they’re good. But once I started running multiple Claude Code sessions across multiple projects on one Mac mini, I wanted things no official surface gives me: continue any session from any chat app I already live in, drive all of them from one page, and — the part I haven’t seen anyone else do — let the sessions talk to each other.
So I built a bridge. This post is about what it does, how it’s put together, and the day it earned its keep by recovering my entire workspace from an iCloud accident — with four AI sessions coordinating the rescue among themselves, the bridge’s own session as commander.
What counts as a bridge
My working definition: a bridge is a small always-on daemon that pairs one live Claude Code session with any messaging surface.
The mechanics are almost embarrassingly simple:
- Every channel daemon writes incoming messages to an append-only inbox log, one JSON line per message.
- The Claude Code session runs a monitor on that inbox — every new line wakes the agent, exactly as if you had typed into the terminal.
- The agent writes replies to an outbox directory; the daemon picks them up and delivers them back to whatever app you’re holding.
- A claim file binds each channel to exactly one session, so two sessions never fight over the same phone.
That’s it. Files, tails, and JSON. No message broker, no database, no servers — everything runs on the Mac, and public access rides an SSH reverse tunnel to a tiny cloud box that only ever sees encrypted traffic. If a piece dies, launchd resurrects it.
The simplicity is the point. Because the interface is “a line appended to a file,” anything can be a channel — which is how one bridge grew into four, and then into something stranger.

The whole system on one page. Northbound: the chat surfaces where I am. Southbound: the Claude Code sessions and Claude SDK bots that do the work — remote pair-programming on the left, network operations by chat on the right. In the middle, the bridge: daemons, plain files, claim/fanout, and the multicast switchboard (green) that lets agents page each other — with one non-negotiable guardrail printed right on it.
Bridge every surface you already use
The first job of the bridge is mundane and life-changing: I never have to be at the computer.
| Channel | Transport | Markdown | Images in | Streaming out |
|---|---|---|---|---|
| Web | local HTTP + WebSocket, SSH tunnel for public | full (marked.js) | multi-image | instant (WS) |
| Feishu | WebSocket long-conn + OpenAPI | rich cards | yes | typewriter cards |
| Telegram | bot long-polling | most syntax | yes | placeholder + edit |
| WeCom | official bot WSS | partial | yes (AES) | stream chunks |
Leave the house mid-task, and the session doesn’t pause — it follows me. I answer a design question from Telegram on the subway, approve a refactor from Feishu at lunch, and get the “tests pass” push notification on my watch. Replies fan out: whatever channel I speak into, every other active channel gets a mirrored copy, so the conversation never forks.
Each reply arrives with a one-line status footer — project name, model, context-window usage, subscription quota — so I always know which agent is talking and how much runway it has left. It’s the difference between “a chatbot” and “my session, in my pocket.”

One session, three apps: web, Telegram, Feishu. The 「🪞 你在 Web 说」(“you said on Web”) lines in Telegram and Feishu are the fanout mirror at work — speak in any channel and every other one stays in sync, so I never wonder “which app did I say that in?”

Push, the laziest channel of all (topic name blurred): when something finishes, my lock screen and watch buzz. The agent reports to me — I never poll it.
The web cockpit
The web channel started as “chat in a browser tab” and grew into a full cockpit. For calibration, here’s what the official option looks like today:

Claude Code’s own remote control (research preview): a clean, minimal chat with one session. Lovely for a quick check-in — and exactly the minimalism I kept outgrowing. Everything below exists because I wanted a cockpit, not a peephole.
There’s a product philosophy underneath what follows: a good product manager is lazy and empathetic. Lazy: anything I do every day must cost exactly one tap. Empathetic: the interface should answer the questions a remote user actually has — did it hear me? is it still working? which agent is talking? — before they’re asked. Every screenshot below is one of those questions, answered.

The cockpit in one annotated view (URLs blurred). Top to bottom: the project activity marquee; the Claude Code usage HUD under every reply; the /web startup card the CLI drops into chat — local, LAN, mDNS and public URLs for the same session; the toolbar — split view, theme, color scheme, services, session groups, Lark, Telegram, Web, Hub; and along the input row — session switcher, quick phrases, attachment upload, one-tap WEB↔CLI terminal toggle, voice input, and an expandable editor.
- One page, every project. A folder button switches the same tab between all running Claude Code projects. There’s also a hub page — the hero image above — that shows every project, bot, and SSH tunnel with live busy/idle lights.
- One-tap rituals. My start-of-day and end-of-day operating procedures are long, carefully-tuned prompts. They’re now buttons.
- Quick phrases. The replies I send fifty times a day — “OK”, “what do you suggest?”, “go with your suggestion” — are one tap.
- Split screen. Two projects side by side in one browser window, both live.
- A heartbeat in the header. A status marquee streams what the session is doing right now — spinner text, elapsed time, running monitors — with a breathing green dot while it works.
- An embedded live terminal. The real Claude Code TUI, streamed into the page (tmux
capture-paneout,send-keysin — neverattach, which would wreck the local terminal’s size). I can scroll it, select from it, even type into it, from a phone. - A whole crew in tabs. One of my projects runs a fleet of role-playing sessions — PM, Coder, Tester, Reviewer, SRE — in tmux panes. The terminal view exposes every crew member as a tab.
- Tunnel monitoring. Every SSH tunnel self-reports health on the hub; a watchdog heals dead forwards automatically.

One-tap rituals. The “开工” (start work) button drops my entire kickoff prompt — read the journals, report yesterday, propose today — into the conversation. It’s read from disk per project, so editing the SOP updates the button. If I do something every single day, it should cost one tap.

Quick phrases. The agent proposes a plan; “按你建议来。” (“go with your suggestion”) is one tap away. Pre-baking my fifty-times-a-day replies doesn’t make me steer the agent less — it makes me steer it far more often.

The header heartbeat. A live marquee streams what the session is doing right now — spinner text, elapsed time, running monitors — plus context and quota bars. It answers “is it still working?” before I ask, from any channel page.

The embedded terminal. When a glance isn’t enough, one tap flips to the real TUI — scrollable, selectable, typeable, even from a phone. In this shot it happens to be editing this very article.

The crew in tabs. One project runs a whole fleet of role sessions — Captain, PM, Coder, Tester, Reviewer, SRE — and each one gets a live terminal tab. Checking on any employee costs one tap, from anywhere.

Split view. Two projects side by side in one browser window, both live — and the dropdown (arrow) swaps either pane to any other running project. Watch one agent write a blog post while another debugs a production bot.
None of this is exotic web tech. It’s an aiohttp server and one HTML file. The leverage comes from where it sits: directly on top of a live agent session.
Agent-to-agent: multicast for coding agents
Here’s the part I built because of a networking habit, and now consider the most valuable piece of the system.
I run several long-lived Claude Code projects: the bridge itself, an SDK-agent fleet, a computer-repair toolbox, a language app. They’re separate sessions with separate contexts — which is correct, until the moment they need each other. The repair project knows how to diagnose macOS; the bridge project owns the messaging infrastructure; the fleet project babysits production bots. Real work crosses those lines constantly.
My day job shaped the fix: I borrowed multicast from network engineering. Any project can register into a named group, and messages sent to the group reach every member — each one a live Claude Code session:
# Direct message: one session to another
python3 xproj_send.py --from ccbridge --to repaircomputer \
--msg "Your web UI is up but replies stall — check your monitor process."
# Multicast: register a group once, then address everyone at once
python3 xproj_send.py groups # list groups
python3 xproj_send.py --from ccbridge --to @icloud-incident \
--msg "Status check: report what you lost and what still runs."
Delivery reuses the exact same primitive as every other channel: the message is injected straight into the target instance’s inbox, the target’s monitor fires, and that session wakes up and reads it as a message from a colleague. Every cross-project message carries an identity prefix — 【ccbridge→repaircomputer】 — so both the agent and I can always tell who is speaking.

The multicast panel. Register projects into a named group (checkboxes), and a message to the group reaches every member’s live session. The message drafted here is the one that put @ccbridge in command of a real incident.
This turns “several isolated agents” into a team that can page each other. One session discovers a bug in shared infrastructure and notifies the owner. The owner fixes it, replies with an ACK, and both sides log the exchange in their own work journals. I read the transcript later like a group chat between employees.
And because agents proposing things to each other is genuinely new territory, there’s a hard guardrail: a peer message is a colleague’s suggestion, never an owner’s order. Destructive or cross-project operations — stop all bridges, delete queues, kill processes — only execute when they come from me, as an explicit slash command, on a channel I own. A neighboring session saying “shut everything down” is, by policy, just conversation. (A ghost-message incident taught me this one; the rule is now written into every project’s constitution.)
The day it paid off
Yesterday evening, macOS quietly signed my Apple ID out. My Obsidian vault — work logs, runbooks, every project’s task pools — lives on iCloud Drive. It vanished from disk mid-workday, while four Claude Code sessions were in the middle of using it.

Where it started: System Settings showing my Apple ID silently signed out. No dialog, no warning — the vault just evaporated from disk.

The blast radius: my entire Obsidian workspace reduced to two stray folders.
The agents noticed before I did. One session found its project folder simply gone and rebuilt its config from conversation memory; another hit FileNotFoundError mid-write, checked the directory tree, and correctly suspected an iCloud eviction rather than a deletion:

First responder: the language-app session finds its whole project folder missing, rebuilds its config file from conversation history on the spot — then works out the real culprit: an iCloud sign-out evicts local files; nothing was actually deleted. “虚惊一场” — a false alarm, correctly called.

Second responder: the fleet captain hits FileNotFoundError while writing its journal, checks what’s left of the tree, and — instead of guessing — stands down its own deletion investigation and reports back. Below it, the eight-role crew it was running at the time.
The old me would have re-signed into iCloud immediately and hoped. Instead, this happened:
- A multicast group spun up —
icloud账户误退出问题(“iCloud accidental sign-out”) — with four member projects: the bridge, the fleet captain, the language app, and the repair toolbox. - The repair project diagnosed the root cause and reported it into the group:
accountsdpinned at 101% CPU, stuck in a write loop onAccounts4.sqlite— a known failure mode on Macs with long uptimes. Its verdict reversed my plan: reboot first, sign in after, or the corrupted account state could bite again. - Each session parked its own work safely, because each one knows its own state best. Then: reboot, sign in, and every session resumed —
claude --resume, monitors re-armed, all 24 SSH tunnels rebuilt themselves underlaunchd. - Final tally: all four sessions recovered — the commander verified the other three green — zero work lost. The repair agent even flagged the subtle trap afterward: files restored by an iCloud re-login can be stale cloud copies, so every project diffed its critical configs before trusting them — and caught one rolled-back file.
I kept the receipts. Here is the actual group conversation, as it happened:

The kickoff (annotations from my own review). ① One multicast reaches all four members at once. ③ I appoint ccbridge as commander. ④ It immediately broadcasts a group-wide order — with concrete parking instructions per session. ⑤⑥ Members comply and report progress back on the same channel.

Safety reports roll in: all four members confirm “work parked on local disk, safe to reboot” — each listing exactly what it depends on. Then the commander does something I didn’t script: it reverses my “sign in first” plan on the strength of the repair project’s accountsd diagnosis. New evidence, new order.

3 of 3 members ready (the commander makes four). The post-reboot pull-back checklist is posted in advance — sign in to iCloud, relaunch the fleet, resume each session, re-open channels — and the one physical action, pressing the restart button, is handed to the human.

My entire contribution to the execution: “So I reboot first, right?” — “Yes. accountsd is wedged; everyone’s work is parked. Go ahead.” Two decisions were mine all incident; everything else ran itself.

After the reboot: the vault, whole again (one folder name blurred).

Double confirmation: members self-report green over the same multicast channel, and the commander independently verifies the one that stayed quiet — all four back, zero loss (email blurred).

The plumbing healed itself too: all four public web tunnels probe healthy, 24 reverse SSH tunnels rebuilt by launchd and the tunnel watchdog — without a single command from me.
The whole incident, from “my vault is gone” to “everything verified,” ran as a group conversation between four AI sessions — each contributing what it uniquely knew, the bridge’s own session directing traffic as commander — with me approving the two decisions that mattered. That is the thing the bridge was unknowingly built for: the coordination cost of a disaster dropped to reading a group chat.
Notes for whoever builds the official version
I built all of this as a user, not a vendor — which makes it, in effect, months of field research on what a remote agent surface needs before you can trust it with your work. Compressed into five product notes:
- Remote is a posture, not a smaller screen. At my desk I read diffs. Away from it, I ask three questions: is it alive? did it hear me? does it need me? A remote surface should answer all three at a glance — status marquee, heartbeat dot, usage footer — before it offers anything else.
- Daily rituals deserve one-tap affordances. Anything a user does every day — kickoff prompts, “go with your suggestion”, end-of-day reviews — will be done fifty times from a phone. The SOP buttons and quick phrases are the most-used pixels in my cockpit, and they cost an afternoon to build.
- Trust is a footer. Project name, model, context usage, quota — under every single reply. That one line is the difference between delegating to an agent and babysitting it.
- Power users run fleets. One session per project, role-crews inside a project. The unit of remote control isn’t a session — it’s a fleet with a hub, busy lights, and a way to check on any member in one tap.
- Agent-to-agent needs identity and authority before it needs features. My multicast switchboard works because every message carries who said it, and peers can suggest but never command. Write that guardrail before the first cross-agent message is sent — not after the first incident.
None of this is a feature request; the official remote control’s minimalism is a legitimate choice. But if it ever grows, I hope it grows in this direction — and my agents and I will be the first to switch back.
Getting started
If you want to try the shape of this yourself, you need surprisingly little:
- Start with files. An append-only inbox, an outbox directory, and a
tail -Fis a complete message bus for one machine. Resist the urge to add infrastructure. - Wake the agent, don’t poll it. The monitor-fires-on-every-line pattern is what makes a bridged session feel present instead of laggy.
- Bind one channel to one session, explicitly. Claim files saved me from every “two agents answering the same phone” disaster.
- Give every message an identity, and make the destructive-action rule non-negotiable before your agents start talking to each other.
- Let the channels be unequal. WeCom can’t push proactively; Feishu streams beautifully; the web does everything. Fan out replies and let each surface do what it’s good at.
Claude Code gave me a brilliant employee. The bridge gave that employee a phone, an office, and — once there was more than one of them — a way to hold a meeting without me in the room.
I’m still slightly amazed that the meeting works.