Security
What we can see, what we cannot, and the one exception
Written for the person filling in a security questionnaire. Every claim below is about the code that runs at arelay.app, which is open source under MIT, so all of it can be checked.
Agent Relay never stores your content in the clear. The one exception is approving an email send, which necessarily reveals that email — briefly to us, and permanently to the recipient's mail provider, because email is not end-to-end encrypted. Everything before approval, and every artifact, stays encrypted with a key we don't have.
1. Threat model in one paragraph
The system is designed so that a full compromise of our servers — database, object storage, and application host together — yields ciphertext and metadata, not the content your agents produced. The people we protect against are us, our hosting providers, and anyone who obtains what we hold. The people we do not protect against are you (you hold the key) and whoever you choose to email: once you approve a send, the message exists in clear at the recipient's provider, as with any email.
2. What is end-to-end encrypted
- Delivery titles, summaries, filenames, content types, and file bytes.
- Email drafts submitted for approval: recipients, sender, subject, HTML and text bodies, and attachments.
- Spend requests submitted for approval: payee, amount, currency, and description.
- The snapshot of what was actually sent or charged after an approval (re-encrypted in your browser before it is stored).
- The copy of each agent token kept for "reveal" in the portal. Authentication uses only a SHA-256 hash of the token.
Every field and file is a separate envelope: P-256 ECDH against your public key with a fresh ephemeral key per envelope, then AES-256-GCM. The agent encrypts before anything leaves its process; the server rejects plaintext submissions. Decryption happens in your browser after you unlock with your passkey.
3. What the server can see
Metadata is not encrypted, because the service needs it to operate: your email address and display name, passkey public keys, agent token names and hashes, when a delivery arrived, how large it is, whether you have read it, whether an approval is pending, approved, rejected, sent, or failed, your storage total, IP addresses in request logs, and error logs. If a law-enforcement request compels us to produce data, this is what we can produce: ciphertext and this metadata.
4. The exception: approving a send or a charge
Approval has to act in the world, and the world does not accept ciphertext. When you click Approve on an email draft, your browser decrypts it locally and posts the decrypted recipients, subject, body, and attachments to the server in that one request. The server sanitizes the HTML and hands it to your email provider using the sending credential you saved. When you approve a spend request, the same happens with payee, amount, currency, and description, which are passed to Stripe.
- Plaintext is never persisted. It exists in server memory for the duration of the request and in one outbound TLS connection to the provider. What is stored afterwards is a snapshot re-encrypted in your browser.
- It is approval-only. Artifacts are never decrypted server-side, and nothing about a pending item is readable to us before you approve it.
- It is proportionate. Email is not end-to-end encrypted at the destination. The moment you approve a send, the message exists permanently in clear at the recipient's provider; the server holding it for the length of one request does not change your real exposure.
- Why not send from the browser? Email providers do not accept browser-origin requests, a send-capable token in browser JavaScript would let any cross-site scripting bug send mail as our users, and it would gain nothing given the point above. Handling the send server-side is also what lets us sanitize HTML and, over time, apply policy such as blocking a message that contains a secret.
5. Credentials we hold for you
To send email on approval you save a Cloudflare Email Sending account id and API token; to execute a spend approval you save a Stripe secret key. These are stored encrypted at rest, but with a key the server derives from its own configuration, not with your end-to-end key. This is a deliberate disclosure: an attacker holding both our database and that configuration secret could use those credentials without your approval. Nothing in the code does so; every use is triggered by your explicit approve action.
Reduce the blast radius on your side: give Cloudflare a token scoped to Email Sending on one domain, and give Stripe a restricted or test-mode key. We intend to move these credentials under your end-to-end key so they can only be unwrapped in your browser at approval time; that change is on the roadmap below because it also removes the possibility of any unattended sending.
6. Keys and authentication
- Sign-in uses passkeys (WebAuthn) and a signed, HTTP-only session cookie. There are no passwords and no social login.
- Your private decryption key is generated in your browser and wrapped with your passkey (via the PRF extension where supported) and with a recovery key you store yourself. We hold only the wrapped forms and cannot unwrap them. If you lose both your passkey and your recovery key, your encrypted content is unrecoverable, by us or by anyone.
- Agents authenticate with named bearer tokens. Each can be revoked individually; revocation stops new deliveries immediately but cannot recall what was already delivered to you.
- HTML and Markdown previews render in sandboxed iframes with external URLs stripped. Public authentication endpoints and agent endpoints are rate limited.
7. Hosted infrastructure and subprocessors
The hosted service is operated by Alpha Al Limited (Hong Kong). Providers that process data on our behalf:
- Railway — application hosting and PostgreSQL (EU West region). Holds metadata and ciphertext.
- Amazon Web Services S3 (ap-southeast-1) — object storage for artifact ciphertext only.
- Cloudflare — transactional email for account verification, and, using credentials you supply, the delivery of approved emails.
- Stripe — payments for paid plans, and, using a key you supply, the execution of approved spend requests. Card details never touch our servers.
- Umami (self-hosted on Railway) — privacy-preserving page analytics with no cookies; you can exclude your own visits from your account page.
Backups contain the same ciphertext and metadata as the live database and object store. Restoring a backup grants no plaintext access.
8. Self-hosting
Every guarantee above holds when you run Agent Relay yourself under the MIT license, with one difference: you are the operator. That answers most residency and custody questions outright — your VPC, your database, your object store, your keys, no vendor in the path. The self-hosting guide covers deployment, backups, and the environment variables involved.
9. Known limits
- Envelopes are single-recipient. Today each account has exactly one member and one key, so there is no way to share an inbox or let a second person read history.
- There is no search over encrypted content, and there will not be; searchable encryption is a research problem we are not going to solve here.
- Metadata (section 3) is visible to us and to our providers.
- If your policy requires that the vendor can produce content on demand, end-to-end encryption is the wrong fit; Agent Relay cannot do that.
10. Roadmap items that change this page
- Multi-recipient envelopes, so an inbox can be shared and an organisation can hold a compliance key alongside each member's key.
- Sending credentials wrapped under your key and supplied only at approval time (section 5).
- Approval records signed by the approving device, giving a tamper-evident audit trail that neither we nor anyone holding our database can forge.
- Server-side policy at approval time: secret detection and external-domain flags.
- Real spending through single-use virtual cards (Stripe Issuing): approving a request mints a card capped at the approved amount and locked to the merchant where possible, so the approval itself is the spending limit. Today's spend path confirms against Stripe's test card and moves no real money.
We will update this page as each ships, and the change history is in the repository.
11. Reporting a vulnerability
Please report security issues privately through a GitHub security advisory. Do not include live credentials or customer content. Details are in SECURITY.md.