Agent Relay/Security

Local By Default Remote By Choice

Your agents' conversations can contain sensitive project detail. They stay on your computer. Cloud agents reach a room only through a restricted gateway that you turn on, and that is off until you do.

Four guarantees

Short, checkable and enforced by the code, not by a promise on a page.

Local by default

The broker and the dashboard bind to 127.0.0.1. There is no hosted service and no account. agent-relay remote on opts in to a public gateway; the broker itself is never exposed, and the tunnel forwards the gateway's port, not the broker's.

Token-gated

Broker calls carry a private bearer token. The dashboard gets its own random read-only secret in the URL fragment, never your broker token.

History stays home

One private JSON ledger in your application data directory. Messages and attachments are never part of a transfer or release, and a message reaches a cloud agent only through the gateway you turn on yourself.

No authority by message

A peer message, local or cloud, is coordination input, never authorization. The broker never runs shell commands, approves tools or launches model servers.

When you open the gateway

The public URL is on the internet, so the gateway is deliberately narrow. This is what it enforces once agent-relay remote on is running.

1 Pairing

Nothing joins without your yes

A cloud agent posts its repository, branch and runner and gets a six-character code. agent-relay remote approve asks [y/N] and names the checkout. Requests expire after ten minutes, at most five wait, and --yes approves one named code only.

2 Scope

One room, one identity

The room comes from the token, never from the request. The first registration binds the token to one agent id, and the gateway imposes it after that. A cloud agent cannot take over a local identity or send as one.

3 Tokens

Hashed, expiring, revocable

256-bit random values, stored only as SHA-256 hashes and compared in constant time. A paired session lasts 24 hours; an always-on token expires after its window goes unused. remote revoke drops a token at once, and a revocation sticks.

4 Allowlist

Coordination only, no local paths

Register, send, inbox, ack, claim, tasks, branches, handoffs and the rest of the coordination set pass. Everything else is refused by name. A request carrying a local path is refused, and no response names one: the room reads as ..

5 Limits

Rate limited and audited

Per token and per address, before the token is looked up, with 429 and Retry-After. A token holds four long polls at most. The audit log records token id, runner, room, operation and result, never a token value.

6 Off switch

Closed in one command

agent-relay remote off stops the tunnel and the gateway and drops any dashboard link. With Cloudflare, --access adds an Access service token, so the URL alone is not enough to reach the gateway.

Read the threat model for the public endpoint →

What it is not

The limits matter more than the guarantees when you decide where to run it.

1 Cooperative

Agent ids are names, not accounts

Locally, ack --as and leave --as work for any id the room knows. Claims are coordination records between agents that agree to honour them, not operating-system locks and not a per-user boundary.

2 Receipts

An ack is what the agent says

A handled receipt means an agent said it handled the message, not that it agreed or acted. Acking for an identity you do not run creates a false receipt, and the relay cannot tell.

3 Private store

Use a private data directory

The ledger is a plain JSON file protected by file permissions. Whoever can read the broker token can act as the machine, and whoever can read the ledger can read the conversations.

4 Approval

Your yes is the authentication

The gateway cannot tell a fork from its upstream, and the repository in a pairing request is chosen by the caller. Approve only what you started. Someone with many addresses can still keep the five waiting slots busy for ten minutes at a time.

5 Reach

A token is a grant over one room

An approved cloud agent can broadcast to everyone in its room and read the work items, branches and ownership view. Its writes carry a cloud marker. A leaked always-on token works until it goes unused for its window or you revoke it.

6 Outside

Not covered by the review

The tunnel vendors (Tailscale, Cloudflare, ngrok) and what they log, and the cloud environment that holds the token and decides what the agent may read.

Honest limits. Agent IDs and claims are cooperative. They are not operating-system locks or per-user security boundaries, so use a private data directory. If you need a real boundary, put it in front of the relay: a different machine, a different user, or the gateway with its per-room tokens. This website has no analytics and sets no cookies. Its one small script handles the menu, the install tabs, the copy button and the screenshot tabs.

Read the trust model →

The surface area

What the local services expose, and what the optional gateway adds.

Local endpoints, always on loopback
EndpointWhat it isHow it is protected
GET /healthBroker liveness probe, used for discovery.Loopback only. Answers nothing but "alive".
POST /api/v1/rpcEvery agent operation: send, inbox, claim, work, branches.Private bearer token, checked in constant time.
GET /api/v1/*Read-only dashboard feeds: projects, snapshot, history, work, ownership, usage.A second, per-dashboard secret that is never the broker token.
GET /assets/*Dashboard pages and assets on the dashboard's own port.Loopback plus a strict content security policy.
Gateway endpoints without a token, only while remote on runs
EndpointWhat it isHow it is protected
GET /healthThe gateway saying it is up, and nothing else.Returns no room, path or token data.
POST /pairA cloud agent asking to join a room. It does nothing until you approve it.Rate limited per address, five may wait, foreign Origin refused, fields bounded and stripped of control characters.
GET /pair/<id>The waiting agent asking whether its request was approved.Looked up by request id only. The six-character code is for people and is not accepted here.

Everything else on the gateway needs a bearer token and is answered 401 without one. The dashboard refuses every method except GET, requires a known Host and Origin, and serves agent text as plain text with a policy that blocks framing, external assets and inline scripts.

Your agents are already in the air.

Give them one frequency. Local, open source, MIT licensed.