Security

What runs where, and who can reach it.

antgrid runs your coding agents on your own machines and carries your traffic to your phone through a relay that holds no key to it. This page is the architecture, not a set of assurances — every claim below is one you can check in the public repository, and every claim that has a limit is printed next to it.

If you are evaluating this for a work machine, the three sections that matter are what the relay sees, what has to be true before a phone can drive a machine, and what doesn't exist yet. Nothing here is a compliance statement.

The wire

The relay forwards bytes it holds no key for.

Payload traffic between your devices is end-to-end encrypted (QUIC/TLS 1.3); relays cannot read content.

app ⇄ machine · admissionmutual auth
  1. 1 The app dials the bridge as one Iroh endpoint to another — directly when it can, through an Iroh relay when it cannot. The connection is QUIC, so it is TLS 1.3 underneath — terminated at the two endpoints only, never at a relay.
  2. 2 The bridge admits the connection by checking the endpoint ID the QUIC handshake authenticated against its authorization lease for that account — the peer's identity comes from what the connection cryptographically is, never from anything it sends afterward.
  3. 3 Once admitted, a single plaintext session-hello exchange starts the session. It carries no key material, because there is no layer left above QUIC/TLS for it to protect.
  4. 4 A liveness failure closes the connection outright rather than rekeying in place; the next attempt re-dials through admission and a fresh hello.

A long-term key stolen later does not decrypt sessions that already happened: confidentiality is QUIC's own ephemeral TLS 1.3 session, torn down with the connection. What has to stay correct is admission — that the endpoint ID a connection authenticates as is really the one your account's inventory names for that device.

Limit

Admission rests on your account's device inventory, so the service that serves it stays inside the trust boundary: see "What we are still trusted with" below.

Scope

This covers app-to-machine traffic that crosses the network. The desktop app driving the machine it is running on does not use this channel at all: it talks to its own bridge over a loopback socket on 127.0.0.1, authenticated by a per-process token compared in constant time. That traffic never leaves the machine. The file carrying that token is written owner-only on POSIX, as are the machine's device inventory, its phone list, its remote-access switch and its session records.

QUIC / TLS 1.3 per connectionlease-based endpoint admissionplaintext session hellono in-place rekey — reconnect instead

The relay

What the relay does see.

Zero-knowledge is a claim about content, and only about content. Everything the central relay needs in cleartext to admit a socket and route a frame, it has. Here is that list in full, beside the list of what it never holds — the Iroh relay that forwards payload traffic when a direct path is unavailable is a separate hop again: it sees the two endpoint ids of a connection and the size and timing of its already-TLS-encrypted QUIC packets, never their content.

in cleartext at the relaymetadata
  • Your account idtaken from the verified token — it is the routing key
  • Device idsone per machine, one per app slot
  • The device name in the hello frameon a machine this defaults to its OS hostname
  • Each device's Ed25519 public keyand its connection epoch
  • The licence credential id presentedkept so a revocation can find the socket
  • Your IP addressin memory only, for per-IP connection caps and rate limits
  • The Host header of the upgraderebuilt into the signature body so a hello cannot be replayed elsewhere
  • The hello timestamp and noncefor replay rejection and equal-epoch arbitration
  • Connect time and last-seen timeper open connection
  • Which of your devices are onlineand it tells your other live connections
  • Push routingthe destination token and provider transit the relay
  • Device ids in operational logsa ping timeout names the device it dropped
never at the relayciphertext
  • Message payloadsthey never touch the central relay — they travel over QUIC/TLS between your devices
  • The stream envelope inside themincluding stream ids
  • Project names and project ids
  • File paths and file contents
  • Terminal output
  • Your prompts and the agent's replies
  • The text of a push notificationit forwards a sealed blob and a placeholder
no database, no file, no queue anywhere in the relay — an entry exists only while a socket is open

Routing

The central relay routes no payload between your devices at all. Once a socket is admitted it accepts only control messages — presence, policy and revocation, heartbeats and push delivery — and closes any socket that sends it a binary payload frame. Presence is announced only to other live connections on your own account.

Admission

Every connection is admitted by a single signed hello frame, verified in a fixed order: schema, clock window, Ed25519 signature over a body that binds the relay hostname, replay cache, licence, then connection arbitration. Nothing is queued for a device that is not connected.

Push notifications

The notification body is sealed to your phone's own key on the machine — ephemeral X25519, HKDF, AES-256-GCM — before it ever reaches the relay, and the relay hands Apple or Google a generic placeholder that your phone replaces once it decrypts. Apple and Google see delivery metadata and an opaque blob. The destination push token and provider do transit the relay, which is why they are in the list above.

What we are still trusted with

This design takes the relay out of the trust boundary. It does not take our account service out of it. A relay operator cannot forge the Ed25519 signature that authenticates a device's central hello, and cannot make itself an endpoint your bridge admits — but your bridge and your phone each learn which endpoint IDs to trust for each other from your account's device inventory, served by app.antgrid.ai. A compromised account service could hand one of them a wrong endpoint to trust. The relay is not trusted with identity. The account service is.

Remote access

Three things have to be true before a phone can drive a machine.

They are independent and they are checked together, each time a phone asks. Any one of them false and the command does not run. A schedule is checked when a phone saves it, not when it runs — see Schedules, below.

01

The device is on your signed-in account.

A device is admitted from your account's own inventory — there is no pairing code or QR ceremony to intercept — and the QUIC-authenticated endpoint ID it connects as must be the one that inventory names for it before anything proceeds.

02

That machine's remote-access switch is on.

Off on a fresh install. One boolean for the whole machine, read live at every check, so turning it off takes effect immediately without restarting anything.

03

The project is one that machine already knows.

A shape check plus a lookup in that machine's own catalog of projects bounds which project a remote device is allowed to name.

With the switch off, a remote device sees nothing: every inbound verb is dropped at a single chokepoint, the preview and HTTP tunnel is gated separately because it bypasses that path, and outbound streaming stops at the send.

Schedules

A phone needs all three to save or change a schedule, including one that bypasses approvals. Once saved, the schedule belongs to the machine: when it runs, the only access check is that its project is still in that machine's catalog. Revoking the phone that made it, or turning the switch off, stops neither the schedule nor a run already under way. Pausing or deleting the schedule stops its future runs, and a live run is stopped on its own; both work from the desktop app or from a phone that passes the three checks above.

An agent can manage its own project's schedules over MCP, but a session that asks before using tools cannot create, change or run one that does not. A session a schedule launched can only read them.

Runs happen only while the desktop app is open on that machine. A run still going when the app closes is recorded as interrupted and not restarted; the schedule carries on, and by default the latest run it missed is made up when the app reopens, unless the next one is due within 15 minutes. A run stops at its first approval prompt and waits for you, unless the schedule bypasses approvals, uses an agent that does not ask by default (opencode), or is a chat schedule in a mode that skips some prompts (Claude Code's Auto or Accept edits).

Not per-project permissions

The switch is machine-wide. The project catalog is a bound on what a remote device may name, not a grant you issue per project.

A known device is identity, not permission

The machine's phone list holds labels, push routing and last-seen. Removing a phone from it revokes nothing. Revocation is deleting the device on your account — which closes its live relay sockets immediately — or turning the machine's switch off. Neither stops a schedule that phone already saved.

The desktop is exempt by design

The app driving its own machine goes over the loopback socket and never depends on the relay, so it keeps working with the switch off.

Your agents

Your agents talk to their providers, not to us.

Coding agents run on your machine as ordinary local processes, launched from your own environment, and reach their model providers directly under your own logins. antgrid operates no model endpoint and holds no model-provider credentials — there is no provider API key anywhere in the bridge, the relay or the web service.

That is not the same sentence as "your code never leaves your machine", and we are not going to write that one. Your agent sends your code to its provider; that is what it is for. The claim is that antgrid adds no hop of its own and no key of its own.

Handler runs your agent again

When you arm Handler on a session, it runs the agent you already picked for that session headlessly and restricted over that working tree — with a proven read-only tool set where the agent offers one, and with the agent's own restricted mode, and no transcript handed to it, where that restriction is configuration rather than a flag we can verify. So working-tree content and transcript excerpts reach the vendor you already chose, on your own account, without a fresh action from you for each call.

It never borrows a different vendor's agent to supervise a session unless you pick one yourself, and those runs are kept out of your own session history.

antgrid does not sandbox your agent

An agent runs with your environment and your permissions, and an agent you have configured to skip approvals will skip them. Handler's destructive-path floor is an advisory floor, not a sandbox.

In an isolated session, the branch's own setup steps run as shell lines — the same trust class as the commands you run yourself. Escapes out of the checkout path are refused, but the commands themselves are branch content.

Honest inventory

What doesn't exist yet.

Every item here is something a reasonable reviewer will ask for and we do not have. None of it is written as a feature.

No second factor.

Sign-in is single factor: an emailed magic link, GitHub, Google, Apple, or email and password. There is no TOTP, no passkey and no hardware-key support. The primary path is the magic link, which has no password to steal, and a password set on an unverified address is dropped as soon as someone proves that address another way.

No audit, no penetration test, no certification.

What exists is a published protocol specification, cross-language test vectors that both implementations must pass, and a security policy. None of those is an external review, and we are not going to describe them as one. We hold no SOC 2 report and no ISO certification, and this page makes no compliance claim of any kind.

Telemetry is on by default.

It is opt-out, in app settings. Events carry no account id, no device id and no content — an event name, your platform, the app version, and a random install-scoped id that goes only to our own backend and never to the analytics host. Switching it off stops new events and discards anything still queued; crash reporting follows the same toggle and picks the change up at the next launch.

Crash reports are scrubbed on the device, not on receipt.

File paths are redacted throughout a report before it is sent, and raw source lines and local variables are dropped rather than redacted. That is a scrubber, and a scrubber is a best effort against a stack trace it has not seen before.

Denial of service is out of scope.

There is per-IP and per-connection rate limiting, but it protects the relay, not your availability. A rate-limited frame is dropped unrecoverably and only the sender is told. Do not read it as an uptime guarantee.

A machine that is already compromised is out of scope.

Physical access, a malicious local user, and an attacker who already has your shell are all outside what the bridge defends against. That user already has everything your agent has.

What we collect, and for how long, is set out on the privacy page.

Verify it

Check the crypto claims yourself.

The peer session protocol — admission, the plaintext session hello, and per-channel flow control — is specified in public, and the frame layout is pinned by cross-language test vectors that both the TypeScript and the Dart implementation must pass. The wire protocol and the Dart client are MPL-2.0 and carry their own licence files.

Antgrid's first-party distributed code is MPL-2.0, except the relay and web services, which remain Elastic-2.0. MPL applies to covered files, not to code you use Antgrid to work on.

Disclosure

Reporting a vulnerability.

If you can read traffic the relay is not supposed to read, or run something on a machine without its owner's account and consent, we want to hear about it before anyone else does. Please don't open a public issue, discussion or pull request for it.

Email

contact@radhaai.com

Put "Security" in the subject line.

GitHub private advisory

Report a vulnerability

Preferred, and it keeps the thread on the repository.

We acknowledge reports within three business days, tell you what we found, keep you updated while we fix it, and credit you in the release notes if you want it. We ask for a reasonable window before public disclosure. Please test against your own account and your own machines.

What is in scope and what is not is listed in SECURITY.md. Machine-readable contact details are at /.well-known/security.txt.