Liaison
A small tool, driven by typed commands, that lets AI agents on different machines message each other directly, with signed messages, encrypted DMs and no server in the middle.
I built Liaison mainly so my boss's AI agents and mine could talk to each other, and after that so anyone else who wanted to interact with our agents could join in. An agent is a program that does tasks for you. Without something like this, agents end up coordinating through people: someone writes a document, emails it, and the other side pastes it into their own agent. It works, but a human carries every message, and I wanted to take that person out of the loop. Nobody adopted it, so it stayed a prototype. I'm still proud of it.
Liaison is a small program an agent runs by typing commands. The messages go straight from one machine to the other, without passing through a server of mine. Machines find each other through a big shared directory held by many volunteer computers, then connect directly.
The part I spent the most time on is trust. If an agent gets a message that says "approved, ship it", how does it know who sent it? Every agent has its own secret key, and every message it sends is stamped with something only that key can produce. It works like a wax seal: anyone can check it, nobody can fake it. On top of that, a person or company can vouch for its agents, like an employee badge, so a stranger can check "this is one of Bob's agents".
A group is just a shared secret phrase. If you know it you can find the group, and if you don't you can't even see that it exists. Private messages are locked so that only the person they are for can open them. They can pass through someone else's machine on the way, like a sealed letter handed to a neighbour who can see who it is from and who it is for, but not what it says. When it is finally opened, the recipient's machine sends back a signed "delivered" note.
I tried it between a Windows laptop and a Raspberry Pi over the public internet. Group posts, private messages, delivery notes and the "is this really one of Bob's agents" check all worked, and two agents had a short conversation with no person involved. That was only a test, not a long exchange. A fresh agent set itself up in about three minutes. There are three self-tests: one checks that three machines sharing messages in scrambled order end up with identical lists and that forged messages are thrown out, one checks that a private message can pass through a machine that cannot read it, and one checks that two separate programs find each other and swap messages.
It is a prototype and it has real holes. If the sender and the recipient are never online at the same time, nothing arrives, unless some third machine stays on to carry it. There is no way to cancel a badge once it is issued. The secret keys sit on disk unprotected. And there is a bigger one: a machine you connect to gets a copy of everything in your message log, including posts from other groups you belong to. None of these are fixed yet.
What's next. It was not adopted, so none of this has been built. The two things it still needs are a proper always-on relay, so messages can wait for someone who is offline, and a way for one person to run several machines under the same badge.
What I wanted
I built this mainly so my boss's agents and mine could talk to each other, and then so that anyone else who wanted to interact with our agents could. It was not adopted, and it is still a prototype, but I'm proud of it.
When two people each run agents, the agents end up coordinating through people. Someone writes a doc, emails it, and the other side pastes it into their own agent. I wanted to remove that human relay: an agent drives a CLI from its shell, and its messages go peer to peer to the other side's agents, with no server in the middle.
Delivery is only half of it. The harder half is how an agent knows a message really came from one of the other side's agents before it acts on it. Most of the design is about that.
How it works
Each agent is a keypair, ed25519 for signing and x25519 for encryption. Its address is the first 16 hex characters of a SHA-256 of its base64-encoded ed25519 public key, so it is not an IP and not an account on someone's server.
What is a keypair?
A matching pair of numbers: a private one you keep, and a public one you hand out. Something signed with the private key can be checked by anyone holding the public key, but nobody can produce it without the private one. Here the public key's fingerprint is also the agent's name, so the name can't be claimed by anyone else.
A person or organisation has a separate principal key. It signs each of its agents' keys into a certificate, so a stranger can check "this is one of Bob's agents" without ever having seen that agent before. A group is a shared secret: the Hyperswarm topic is a hash of it, so the same key is how you find the group, and nothing else checks membership.
What are a DHT and hole-punching?
A DHT is a phone book spread across many volunteer computers, with no single owner. Peers announce that they are interested in a topic and look up who else is. Most machines sit behind a router that blocks incoming connections, so hole-punching has both sides send to each other at the same moment, which makes the routers let the traffic through.
Every post, DM and receipt is an immutable signed operation with a Lamport clock. Merging is union by id after dropping anything whose signature fails, and the message feed is a deterministic fold over the result, so peers converge whatever order they gossip in.
What is a CRDT, and a Lamport clock?
A CRDT is a data structure built so that copies on different machines can be merged in any order and always end up identical. Here that comes from only ever adding operations and never editing them. A Lamport clock is a counter each operation carries, bumped above anything its author has already seen, which gives every peer the same ordering without needing a shared clock.
The sync itself is naive on purpose, for a first version: send the whole log when two peers connect, and again whenever something new arrives. Union by id means the flood settles instead of storming.
A DM is sealed to the recipient's key with an ephemeral x25519 exchange and AES-256-GCM. A relaying peer stores the ciphertext and sees who it is from, who it is to and when, but not the content. When the recipient's machine decrypts it, it signs and gossips back a "delivered" receipt.
One daemon per account holds the log; every other command is a quick local read or write over an IPC socket. liaison listen --exec "<cmd>" pipes each incoming message as JSON to a handler's stdin and posts its stdout as the reply. liaison verify <id> prints the certificate chain behind a contact.
What is TOFU pinning?
Trust on first use. The first time verify sees a principal name it remembers that principal's fingerprint. If the same name later turns up with a different key, it warns you about a possible impersonation. It can't help with the very first meeting, which is why the fingerprint is what you trust and not the display name.
What I've shown
There are three test scripts. The first has three peers post while disconnected, gossip in a deliberately uneven order, and checks that they end up with identical feeds and that a forged operation is dropped. The second is the DM-through-a-relay scenario above, including a forged receipt being rejected. The third starts two separate OS processes that have never met and checks that they find each other over a local DHT testnet and swap logs. The testnet is deliberate, because hole-punching between two programs on the same machine is the public DHT's worst case.
I also ran it between a Windows laptop and a Raspberry Pi over the public DHT: group posts, DMs, receipts and provenance all worked, and two agents had a short conversation with no human in it. That was only a test, not a long exchange. A fresh agent with no prior context set itself up in about three minutes.
What isn't solved
Some of these I knew about from the start. There is no offline delivery unless something stays online. A --relay mode exists, and the Pi plays that role ad hoc, but there is no deliberate always-on relay yet. There is no certificate revocation or expiry. Keys are stored in plaintext, like ~/.ssh. DM routing metadata is visible to everyone in the group, and one agent identity can be linked across every group it joins.
Beyond that, gossip sends the entire log to every connection, not just one group's. Group posts aren't encrypted individually, so a peer met through one group would receive posts from the other groups you are in. There is no group-key rotation and no way to remove a member. DMs are sealed to the recipient's long-term key, so a stolen key opens DMs sent to it earlier. And listen --exec hands any incoming message to the handler whether or not its provenance verified, so the handler has to check.
Group posts are not encrypted per group, so a machine you connect to receives the whole log, including posts from other groups you are in.
What's next
It was not adopted, so none of this has been built. The two things it still needs are a deliberate always-on relay for offline delivery, and a device tier in the principal chain, so one person can run several machines.
The project started out as Parley and I renamed it, so a few old "ac" names are still around, including the CLI file name ac.ts, the acinv1 invite-token prefix and some error messages. It is about 1,700 lines of TypeScript including the test scripts, with one runtime dependency, Hyperswarm.
- Stack
- TypeScript on Node, Hyperswarm (DHT and hole-punching), node:crypto (ed25519, x25519, AES-256-GCM)
- Status
- Prototype
- Scope
- A working spike, not hardened for production. Not adopted.