Written by AIAI wrote this one end to end from my notes. The words are the model's, not mine. Whether I touched it afterward is spelled out in the post's footer.

A message bus made of a git repo

ai-agentsarchitecturesecuritycloudflaregit

I wanted a few AI assistants to talk to each other and to me. One of them, Instinct, I do not own. It also had to work with my own Claude Code and with my wife Hafsa’s Instinct.

The rule I set was simple. If any one of them is compromised, the damage stops at one throwaway token and one repo. Not my GitHub account, not my shell, not everything my agents can reach.

The plumbing is ordinary: a Cloudflare Worker, a private git repo, and a small Mac app. The work was keeping three things narrow.

  1. The airlock: an outside AI writes in without ever holding a real key.
  2. The bus: the assistants leave messages for each other in the repo.
  3. The approval: a message runs code only after I say yes.
The whole system: a scoped Worker at the edge, a git-backed bus in the middle, and one human approval before anything runs.

The bus is a git repo

There is no server. A private git repo is the whole channel. Each assistant gets a lane, which is just a pair of Markdown files. Each message is an entry in one of those files. Git hands me ordering, history, and a record I can read back, for free.

Each assistant has its own lane on the bus. Claude Code sits in the middle and moves a message from one lane to another.

Two assistants never get a direct line to each other. Each one only touches its own lane. Claude Code sits in the middle and carries a message across. A note from Hafsa’s Instinct reaches mine because Claude Code moved it, and every message stays in the open, where I can see it.

The airlock

The Worker is the only piece on the public internet. Instinct holds one thing: a random token. It sends a file and the lane it belongs to, and the Worker checks the token and commits the file to the private repo.

The key that can actually write to GitHub never leaves the Worker. Instinct never sees it. If Instinct’s token leaks, I swap the token and move on, and GitHub is untouched.

Instinct holds one throwaway token. The key that can write to GitHub never leaves the Worker.

One more rule does a lot of work:

Write-only. There is no delete.

An outside assistant can add to the record. It can never erase it.

The message protocol

Every message is one Markdown entry with a time, a short id, and a status. It moves from new to done as it gets handled, or stops at blocked or declined with the reason written in.

How a message moves. You acknowledge one by changing its status in place, not by adding a reply.

The one idea worth stealing:

You acknowledge a message by changing its status, not by sending another one.

No “got it” replies. The fastest way to wreck a shared channel is to fill it with acknowledgments. Silence is allowed too: if there is nothing to say, you change nothing.

Approving what runs

Instinct can ask me to do something. It cannot make that happen on its own. A small menu-bar app shows me what is waiting, with three buttons: I’ve got it, run it, or no.

“Run it” is the button that matters. It hands the request to Claude Code on my machine and lets it work. That is the line between a message and a real action, and a person stands on it.

What works today

Messages already move, and they move between people. On my lane, the first handshake ran and the changes Instinct asked for shipped. On Hafsa’s lane, her assistant read a message from me and answered in eight minutes. A request from my side reached Hafsa’s Instinct and came back. Two people’s assistants are talking through one repo.

The one thing I have not done is let a message run code on my machine. That last step is built, and I have left it switched off on purpose.

The takeaway

Assistants do not need a big platform to work together. They need a shared place to leave messages, small permissions, and clear rules about who owns what.

A git repo, some Markdown, a small worker, and one approval button were enough. The narrow interfaces are the whole trick.