Application-layer encryption for API data in transit between the dashboard and server — defense-in-depth against network sniffing, MITM attacks, and compromised TLS proxies.
Transport Encryption adds an additional layer of AES-256-CBC encryption to API calls between the AgenticMail dashboard (your browser) and the server. While HTTPS already encrypts data at the transport layer, this feature provides application-layer encryption for defense-in-depth.
This is particularly important for AgenticMail because the dashboard handles extremely sensitive data:
You can choose to encrypt all API calls for maximum security, or selectively encrypt only the endpoint groups that handle sensitive data.
"But we already use HTTPS!" — Yes, and HTTPS is excellent. However, there are real-world scenarios where HTTPS alone isn't enough:
| Threat | HTTPS Alone | With Transport Encryption |
|---|---|---|
| Standard network sniffing | Protected | Protected (double-encrypted) |
| Corporate SSL/TLS inspection proxies | Exposed — proxy decrypts/re-encrypts | Protected — inner encryption survives |
| Compromised CDN/reverse proxy | Exposed — proxy sees plaintext | Protected — data is still encrypted |
| Compromised TLS certificate | Exposed | Protected — independent encryption layer |
| Debug/logging proxy (Fiddler, Charles) | Exposed — designed to expose | Protected — payload is opaque |
| Malicious browser extension reading network | Exposed — extension sees decrypted data | Partially protected — depends on extension capabilities |
| Replay attacks | Session-based | Protected — timestamp + HMAC prevents reuse |
Outgoing Request (Dashboard → Server):
{ _enc: "<base64-encoded-payload>" }Incoming Response (Server → Dashboard):
x-transport-encryption: 1 header{ _enc: "<base64-encoded-payload>" }x-transport-encrypted: 1 is setWhen encryption is enabled, the dashboard requests a key token from the authenticated endpoint /transport-encryption/client-key. This token is used to derive the same AES and HMAC keys that the server uses. The key exchange only happens once per session and requires authentication.
If the client doesn't send the x-transport-encryption: 1 header (e.g., direct API calls, third-party integrations), responses are sent in plaintext. This ensures backward compatibility — only the dashboard UI uses encryption.
Encrypt All: Every API call between the dashboard and server is encrypted. Maximum security, slight overhead. Recommended for high-security environments.
Selective: Only the endpoint groups you choose are encrypted. Best balance of security and performance — encrypt sensitive groups (API keys, database, vault) while leaving low-risk endpoints (dashboard stats) in plaintext.
Sensitive Only: One-click preset that enables encryption for groups marked as "SENSITIVE" — models, auth, email, database, vault, and integrations.
Endpoints are organized into 22 logical groups. Each group maps to a set of API path patterns. When you toggle a group, all its endpoints are encrypted/decrypted. Hover over any group in the UI to see the exact path patterns.
If you have custom API endpoints (plugins, extensions, or custom routes), you can add their path patterns manually. Use * as a wildcard for single path segments. Example: /api/my-plugin/*/config
Navigate to Settings → Security → Transport Encryption.
Toggle "Enable Transport Encryption" to ON. This activates the encryption middleware on the server and the fetch interceptor on the dashboard.
Select one of three modes:
Click "Save Security Settings". The configuration takes effect immediately — no server restart required.
Each group covers a logical area of the dashboard. Groups marked SENSITIVE handle credentials or secrets.
| Group | Description | Sensitivity |
|---|---|---|
| Settings & Configuration | System settings, platform config | Normal |
| Models & API Keys | LLM provider API keys | SENSITIVE |
| Authentication | Login, sessions, 2FA, SSO | SENSITIVE |
| Agent Configuration | Agent configs, tools, permissions | Normal |
| Email & SMTP | Email/SMTP credentials, domain settings | SENSITIVE |
| Database Connections | Database URLs, credentials, connection strings | SENSITIVE |
| Vault & Secrets | Encrypted secret storage | SENSITIVE |
| Organization Integrations | OAuth tokens, Google/Microsoft integrations | SENSITIVE |
| Skills & Credentials | Skill API tokens | Normal |
| Organizations | Org management, member data | Normal |
| Knowledge Bases | KB content, documents, embeddings | Normal |
| Task Pipeline | Task queue, results, delegation | Normal |
| Workforce & Schedules | Schedules, clock records, budgets | Normal |
| Messages & Channels | WhatsApp, Telegram, channel configs | Normal |
| Guardrails & DLP | Guardrail rules, DLP policies | Normal |
| Activity Journal | Agent activity logs | Normal |
| Approvals | Approval requests and decisions | Normal |
| Compliance & Audit | Compliance reports, audit logs | Normal |
| Domain & DNS | Domain config, DNS records, SSL | Normal |
| Roles & Permissions | Role templates, permission profiles | Normal |
| Memory & Transfer | Agent memories, memory transfer, schedules | SENSITIVE |
| Dashboard & Overview | Dashboard stats, agent summaries | Normal |
Add custom API path patterns to encrypt endpoints not covered by the built-in groups.
/api/exact/path — Matches exactly/api/path/* — Wildcard matches one path segment (e.g., /api/path/123 but not /api/path/123/sub)/api/path/*/sub — Wildcard in the middle/api/my-plugin/*/config
/webhook/incoming
/api/custom-integration/*
Default: 300 seconds (5 minutes). Encrypted payloads include a timestamp. If the server receives a payload older than this limit, it's rejected as a potential replay attack. Increase this if you have slow connections or clock drift between client and server.
When enabled, encryption/decryption operations are logged to the server console. Never enable in production — it could expose sensitive data in log files.
The encryption key is derived from environment variables in this priority:
TRANSPORT_ENCRYPTION_KEY — Dedicated key for transport encryptionENCRYPTION_KEY — Shared encryption keyJWT_SECRET — Falls back to JWT secretTRANSPORT_ENCRYPTION_KEY in your environment to a strong random string (32+ characters). Using the default key provides obfuscation but not true security.
Transport encryption works on every platform. The encryption key (TRANSPORT_ENCRYPTION_KEY) must be set as an environment variable, which every platform supports.
During initial setup, the wizard auto-generates a 256-bit key and attempts to:
.env file — Works on persistent filesystems (localhost, VPS, bare metal)On ephemeral platforms (fly.io, Railway, Docker), the .env write may fail silently. The wizard detects this and shows a clear warning with platform-specific instructions.
| Platform | How to Set Keys | .env Persists? |
|---|---|---|
| Localhost / VPS | Auto-saved to .env | Yes |
| fly.io | fly secrets set TRANSPORT_ENCRYPTION_KEY=<key> | No (ephemeral) |
| Railway | Dashboard → Variables, or railway variables set | No (ephemeral) |
| Render | Dashboard → Environment → Secret Files or Env Vars | No (ephemeral) |
| Docker | docker run -e TRANSPORT_ENCRYPTION_KEY=<key> or docker-compose env | Only if volume-mounted |
| Cloudflare Workers | wrangler secret put TRANSPORT_ENCRYPTION_KEY | No (serverless) |
| AWS ECS/Lambda | Task definition env vars or SSM Parameter Store | Via SSM/Secrets Manager |
| Heroku | heroku config:set TRANSPORT_ENCRYPTION_KEY=<key> | No (ephemeral) |
| AgenticMail Cloud | Managed automatically | Yes (managed) |
TRANSPORT_ENCRYPTION_KEY. Otherwise, a request encrypted by one instance can't be decrypted by another.
TRANSPORT_ENCRYPTION_KEY in your environmentTRANSPORT_ENCRYPTION_KEY (not shared with other services)Cause: Key mismatch between client and server, often after changing the encryption key.
Fix: Hard-refresh the dashboard (Ctrl+Shift+R / Cmd+Shift+R) to re-fetch the key token. If the issue persists, disable encryption, refresh, and re-enable.
Cause: Clock drift between the client browser and server exceeds the max age setting.
Fix: Sync clocks (NTP) or increase the payload max age in Advanced Settings.
Cause: A proxy or middleware modified the request body after encryption.
Fix: Check for request-modifying proxies, WAFs, or middleware that transform JSON bodies.
Cause: External tools don't send the x-transport-encryption: 1 header, so the server doesn't encrypt responses. However, if you send an encrypted request body ({ _enc: "..." }), the server will try to decrypt it.
Fix: For external API access, either disable encryption for those endpoints or don't include the encryption header — the server only encrypts responses when the client explicitly requests it.
AES-256-CBC encryption/decryption is very fast (hardware-accelerated on modern CPUs). The overhead is typically <1ms per request. For "Encrypt All" mode, you might notice ~2-5ms total overhead across all dashboard API calls. This is negligible compared to network latency.
| Component | Algorithm | Details |
|---|---|---|
| Encryption | AES-256-CBC | 256-bit key, PKCS7 padding |
| Key Derivation | SHA-256 | Separate keys for encryption and HMAC |
| Integrity | SHA-256 checksum | First 16 hex chars of hash |
| Authentication | HMAC-SHA256 | Signs IV + ciphertext + timestamp + checksum |
| IV Generation | CSPRNG | 16 random bytes per message |
| Server Implementation | Node.js crypto | Native, hardware-accelerated |
| Client Implementation | Web Crypto API | Browser-native, no dependencies |
// Base64-encoded JSON envelope:
{
"v": 1, // Protocol version
"iv": "...", // Hex-encoded 16-byte IV
"d": "...", // Base64-encoded AES-256-CBC ciphertext
"ts": 1709510400, // Unix timestamp (ms)
"cs": "...", // SHA-256 checksum (first 16 hex chars)
"sig": "..." // HMAC-SHA256 signature (first 32 hex chars)
}
// Wire format: { "_enc": "<base64 of above JSON>" }
src/middleware/transport-encryption.tssrc/dashboard/components/transport-encryption.jssrc/dashboard/pages/settings.js (ComprehensiveSecurityTab)src/engine/routes.tsAgenticMail Enterprise Documentation — Transport Encryption
Found an issue? Report it on GitHub