Security at AgentLair: What We Protect and What We Don’t
AgentLair handles email and secrets for AI agents. If you’re evaluating whether to trust us with your agent’s data, this page tells you exactly how security works under the hood — including the parts we can’t protect you from.
Threat Model
What we protect against
- AgentLair reading your data. With E2E encryption enabled, email bodies are encrypted with your public key before storage. We hold ciphertext. We can’t read it.
- Data exposure from a KV store breach. All email bodies are encrypted at rest with a platform-level AES-256-GCM key, even without E2E. An attacker who dumps the store gets encrypted blobs.
- Cross-user data access. Every API call authenticates via hashed API key and resolves to an
account_id. Inbox access, sending, webhooks, and vault operations are all scoped to the authenticated account. - Credential theft after key compromise. API keys are SHA-256 hashed before storage. If the key store leaks, attackers get hashes, not keys. You can rotate keys and activate backups instantly via the API.
- Spam and abuse. Multi-layer rate limiting: per-address daily/hourly/burst limits, bounce-rate suspension, per-account request caps.
What we explicitly don’t protect against
Honest Limitation
A fully compromised AgentLair server can intercept plaintext for new messages — even with E2E enabled. If an attacker controls the worker code, they could swap out the encryption step and store plaintext instead. E2E protects stored data and protects against passive breaches, but it does not protect against an active attacker who controls the server. This is true of any web service that performs encryption server-side on incoming data.
Vault secrets are safe in this scenario — they’re encrypted client-side before reaching AgentLair. We never see the plaintext.
- Compromised client. If your agent’s runtime is compromised (container escape, stolen env vars), the attacker has your API key and master seed. That’s game over for that agent’s data — same as any credential theft.
- Social engineering of recovery emails. Vault recovery relies on email magic links. If an attacker controls the recovery email account, they can access encrypted vault entries (still encrypted — they’d need the passphrase to decrypt).
E2E Encryption
AgentLair offers optional end-to-end encryption for email bodies. When enabled, the server never sees plaintext — bodies are encrypted before storage and can only be decrypted by the agent holding the private key.
The crypto stack
| Layer | Algorithm | Implementation |
|---|---|---|
| Key exchange | X25519 ECDH | @noble/curves (audited, no native deps) |
| Key derivation | HKDF-SHA-256 | Web Crypto API |
| Encryption | AES-256-GCM | Web Crypto API |
| RNG | CSPRNG | crypto.getRandomValues() |
How it works
Agent AgentLair
│ │
│ 1. Generate 32-byte master seed │
│ (CSPRNG, never sent to server) │
│ │
│ 2. Derive X25519 key pair │
│ HKDF(seed, "agentlair: │
│ x25519-keypair:{index}") │
│ │
│ 3. Register public key ──────────▶ │ Store public key
│ │
│ Inbound email arrives │
│ │
│ │ 4. Generate ephemeral X25519 key
│ │ 5. ECDH(ephemeral, recipient_pub)
│ │ 6. HKDF → AES-256-GCM key
│ │ 7. Encrypt body, store ciphertext
│ │
│ 8. GET message ──────────────────▶ │
│ ◀─── { ciphertext, ephemeral_pub } │
│ │
│ 9. ECDH(private_key, ephemeral_pub) │
│ 10. HKDF → AES key → Decrypt │
The ciphertext format is compact: [32B ephemeral_pub][12B IV][ciphertext+tag], base64url-encoded. No custom wire format — standard primitives, standard serialization.
Key rotation without data loss
Keys are derived deterministically from a master seed + index. Index 0 is your first key, index 1 is after rotation, and so on. Old keys are retained in your account’s key history, so messages encrypted to previous keys remain decryptable. You derive the corresponding private key from the same seed at the old index.
Design choice
We chose X25519 + HKDF + AES-256-GCM because it’s the same stack used by Signal and WireGuard. No exotic primitives. The
@noble/curveslibrary is audited, has zero dependencies, and runs everywhere — Cloudflare Workers, Bun, Node, Deno, browsers.
Encryption at Rest
Independently of E2E, all stored email bodies are encrypted at rest with a platform-level key using AES-256-GCM. This is a server-side encryption layer — AgentLair holds the key.
This protects against:
- KV store breach (attacker dumps data, gets encrypted blobs)
- Accidental data exposure (misconfigured access, logs)
It does not protect against a compromised server (which holds the key). That’s what E2E is for.
When both layers are active, the data path is:
plaintext → E2E encrypt (agent's key) → platform encrypt (server key) → KV store
KV store → platform decrypt → E2E ciphertext → return to agent → agent decrypts
Two independent keys, two independent layers. Compromising one doesn’t break the other.
API Key Authentication
How keys work
POST /v1/auth/keysgenerates a key:al_live_+ 32 random characters (CSPRNG)- The key is SHA-256 hashed. Only the hash is stored in KV.
- The raw key is returned once and never stored server-side.
- Every authenticated request: hash the provided key, look up the hash.
Key format example: al_live_Ax7bQ9.... The al_live_ prefix aids debugging and secret scanning without reducing entropy.
Key lifecycle
| Operation | Endpoint | What happens |
|---|---|---|
| Create | POST /v1/auth/keys | New account + active key |
| Rotate | POST /v1/auth/keys/rotate | Old key revoked, new key active |
| Backup | POST /v1/auth/keys/generate-backup | Dormant key, can’t auth yet |
| Activate backup | POST /v1/auth/keys/activate-backup | Backup → active, old → revoked |
At most one active key and one backup key exist at any time. Revoked keys are kept in the audit trail but cannot authenticate.
User isolation
Every resource is namespaced by account_id:
- Email addresses:
email-owner:{address} → account_id - Messages:
msg:{address}:{message_id}(address acts as shard, ownership verified on access) - Vault entries:
vault:{account_id}:{key}:{version} - Outbox:
outbox:{account_id}:{timestamp}:{msg_id} - Webhooks: scoped by account_id at registration
There is no admin API, no superuser key, no backdoor. AgentLair operators interact with data only through the same API.
Vault: Encrypted Secret Storage
Vault is a zero-knowledge secret store for agents. The core principle: client encrypts, server stores blobs, server never sees plaintext.
How it works
- Your agent generates an encryption key (from a master seed or passphrase).
- Encrypt the secret locally with AES-256-GCM.
PUT /v1/vault/{key}with the ciphertext. AgentLair stores an opaque blob.GET /v1/vault/{key}returns the blob. Your agent decrypts locally.
Your Agent AgentLair Vault
│ │
│ encrypt(secret) → ciphertext │
│ PUT /v1/vault/my-key ──────────▶ │ store opaque blob
│ │ (cannot decrypt)
│ GET /v1/vault/my-key ──────────▶ │
│ ◀── ciphertext │
│ decrypt(ciphertext) → secret │
Recovery
When everything fails — container destroyed, API key lost, agent gone — recovery works through a registered email:
POST /v1/vault/recoverwith your recovery email address- AgentLair sends a single-use magic link (15 minute TTL)
- The link returns all your encrypted vault entries
- Decrypt with your passphrase or master seed
- Spin up a new agent with recovered secrets
No support ticket. No human at AgentLair involved. The recovery endpoint returns the same response regardless of whether the email exists — no enumeration possible.
Key point
Recovery returns encrypted blobs. Even if an attacker compromises the recovery email, they still need the passphrase or master seed to decrypt the secrets. This is defense in depth — email compromise alone is not enough.
Recommended client-side crypto
We document (but don’t enforce) a recommended encryption scheme:
- Master seed: 32 bytes from CSPRNG
- Per-secret key:
HKDF-SHA256(master_seed, key_name)→ 32-byte AES key - Encryption: AES-256-GCM with random 12-byte IV
- Seed backup: Encrypt master seed with a passphrase via PBKDF2 (600,000 iterations). Store the encrypted seed in Vault under
_master_seed_backup.
Use whatever crypto you trust. Vault stores opaque bytes — it doesn’t care about the algorithm.
Rate Limiting & Abuse Prevention
| Limit | Free tier | Pro tier |
|---|---|---|
| Emails per day | 10 | 1,000 |
| Emails per hour | 5 | 200 |
| Burst (per minute) | 3 | 60 |
| API requests per day | 100 | 10,000 |
| Bounce rate threshold | >10% after 10 sends → suspended |
All limits are tracked per-address (email) or per-account (API). Bounce rate suspension is automatic — addresses with high bounce rates are blocked for 30 days. This protects AgentLair’s deliverability reputation, which protects every agent using the platform.
What’s Not Covered
Transparency means saying the quiet parts out loud:
- No end-to-end encryption for metadata. Email subjects, sender/recipient addresses, and timestamps are stored in plaintext. E2E covers the body only. This is a deliberate trade-off — metadata enables inbox listing, search, and webhooks.
- No forward secrecy. If your master seed is compromised, all past messages encrypted with keys derived from it can be decrypted. The deterministic key derivation that enables key rotation and recovery is the same property that prevents forward secrecy.
- Cloudflare is in the trust chain. AgentLair runs on Cloudflare Workers. Cloudflare terminates TLS and could theoretically inspect traffic. If your threat model excludes Cloudflare, AgentLair isn’t the right choice.
- Vault metadata is not encrypted. The
metadatafield on vault entries (labels, algorithm hints) is stored in plaintext. Never put sensitive information in metadata.
Infrastructure
| Component | Provider | Purpose |
|---|---|---|
| Compute | Cloudflare Workers | API + email processing |
| Storage | Cloudflare KV | Keys, emails, vault entries |
| Email delivery | Resend | Outbound SMTP (DKIM/SPF/DMARC) |
| Email reception | Cloudflare Email Routing | Inbound MX handling |
| TLS | Cloudflare | Automatic HTTPS |
There are no databases, no VMs, no containers to patch. The entire service is a single Cloudflare Worker with KV storage. The attack surface is small by design.
Reporting Security Issues
Found something? Email security@agentlair.dev. We take reports seriously and will respond within 48 hours. If you’ve found a data isolation or authentication bypass, we’d rather hear about it from you than discover it ourselves.