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

LayerAlgorithmImplementation
Key exchangeX25519 ECDH@noble/curves (audited, no native deps)
Key derivationHKDF-SHA-256Web Crypto API
EncryptionAES-256-GCMWeb Crypto API
RNGCSPRNGcrypto.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/curves library 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

  1. POST /v1/auth/keys generates a key: al_live_ + 32 random characters (CSPRNG)
  2. The key is SHA-256 hashed. Only the hash is stored in KV.
  3. The raw key is returned once and never stored server-side.
  4. 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

OperationEndpointWhat happens
CreatePOST /v1/auth/keysNew account + active key
RotatePOST /v1/auth/keys/rotateOld key revoked, new key active
BackupPOST /v1/auth/keys/generate-backupDormant key, can’t auth yet
Activate backupPOST /v1/auth/keys/activate-backupBackup → 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

  1. Your agent generates an encryption key (from a master seed or passphrase).
  2. Encrypt the secret locally with AES-256-GCM.
  3. PUT /v1/vault/{key} with the ciphertext. AgentLair stores an opaque blob.
  4. 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:

  1. POST /v1/vault/recover with your recovery email address
  2. AgentLair sends a single-use magic link (15 minute TTL)
  3. The link returns all your encrypted vault entries
  4. Decrypt with your passphrase or master seed
  5. 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.

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

LimitFree tierPro tier
Emails per day101,000
Emails per hour5200
Burst (per minute)360
API requests per day10010,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 metadata field on vault entries (labels, algorithm hints) is stored in plaintext. Never put sensitive information in metadata.

Infrastructure

ComponentProviderPurpose
ComputeCloudflare WorkersAPI + email processing
StorageCloudflare KVKeys, emails, vault entries
Email deliveryResendOutbound SMTP (DKIM/SPF/DMARC)
Email receptionCloudflare Email RoutingInbound MX handling
TLSCloudflareAutomatic 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.