← Back to Dashboard

Transport Encryption

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.

Table of Contents

Overview

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.

Why Transport Encryption?

"But we already use HTTPS!" — Yes, and HTTPS is excellent. However, there are real-world scenarios where HTTPS alone isn't enough:

Threat Scenarios

ThreatHTTPS AloneWith Transport Encryption
Standard network sniffingProtectedProtected (double-encrypted)
Corporate SSL/TLS inspection proxiesExposed — proxy decrypts/re-encryptsProtected — inner encryption survives
Compromised CDN/reverse proxyExposed — proxy sees plaintextProtected — data is still encrypted
Compromised TLS certificateExposedProtected — independent encryption layer
Debug/logging proxy (Fiddler, Charles)Exposed — designed to exposeProtected — payload is opaque
Malicious browser extension reading networkExposed — extension sees decrypted dataPartially protected — depends on extension capabilities
Replay attacksSession-basedProtected — timestamp + HMAC prevents reuse
When to enable: If your AgenticMail instance is behind a corporate proxy, CDN (Cloudflare, AWS CloudFront), or accessed over untrusted networks. Also recommended if you manage high-value API keys or sensitive client data.

How It Works

Encryption Flow

Outgoing Request (Dashboard → Server):

  1. Dashboard intercepts the fetch call for a sensitive endpoint
  2. Request body is serialized to JSON
  3. A random 16-byte IV is generated
  4. Data is encrypted with AES-256-CBC using the shared key
  5. SHA-256 checksum is computed for integrity
  6. HMAC-SHA256 signature is computed over IV + ciphertext + timestamp + checksum
  7. Payload is wrapped as { _enc: "<base64-encoded-payload>" }
  8. Server receives, verifies HMAC, verifies timestamp, decrypts, verifies checksum
  9. Handler sees the original plaintext JSON

Incoming Response (Server → Dashboard):

  1. Server handler returns normal JSON response
  2. Middleware checks if client sent x-transport-encryption: 1 header
  3. If yes, response JSON is encrypted with the same algorithm
  4. Response is wrapped as { _enc: "<base64-encoded-payload>" }
  5. Response header x-transport-encrypted: 1 is set
  6. Dashboard fetch interceptor detects the header, decrypts the response
  7. Application code receives the original plaintext JSON

Key Exchange

When 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.

Graceful Degradation

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.

Key Concepts

Encrypt All vs Selective

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.

Endpoint Groups

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.

Custom Endpoints

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

Configuration

Navigate to Settings → Security → Transport Encryption.

Step 1: Enable Transport Encryption

Toggle "Enable Transport Encryption" to ON. This activates the encryption middleware on the server and the fetch interceptor on the dashboard.

Step 2: Choose Encryption Scope

Select one of three modes:

Step 3: Save

Click "Save Security Settings". The configuration takes effect immediately — no server restart required.

No restart needed: Changes to transport encryption take effect immediately. The middleware reads the updated config on the next request.

Endpoint Groups

Each group covers a logical area of the dashboard. Groups marked SENSITIVE handle credentials or secrets.

GroupDescriptionSensitivity
Settings & ConfigurationSystem settings, platform configNormal
Models & API KeysLLM provider API keysSENSITIVE
AuthenticationLogin, sessions, 2FA, SSOSENSITIVE
Agent ConfigurationAgent configs, tools, permissionsNormal
Email & SMTPEmail/SMTP credentials, domain settingsSENSITIVE
Database ConnectionsDatabase URLs, credentials, connection stringsSENSITIVE
Vault & SecretsEncrypted secret storageSENSITIVE
Organization IntegrationsOAuth tokens, Google/Microsoft integrationsSENSITIVE
Skills & CredentialsSkill API tokensNormal
OrganizationsOrg management, member dataNormal
Knowledge BasesKB content, documents, embeddingsNormal
Task PipelineTask queue, results, delegationNormal
Workforce & SchedulesSchedules, clock records, budgetsNormal
Messages & ChannelsWhatsApp, Telegram, channel configsNormal
Guardrails & DLPGuardrail rules, DLP policiesNormal
Activity JournalAgent activity logsNormal
ApprovalsApproval requests and decisionsNormal
Compliance & AuditCompliance reports, audit logsNormal
Domain & DNSDomain config, DNS records, SSLNormal
Roles & PermissionsRole templates, permission profilesNormal
Memory & TransferAgent memories, memory transfer, schedulesSENSITIVE
Dashboard & OverviewDashboard stats, agent summariesNormal

Custom Endpoints

Add custom API path patterns to encrypt endpoints not covered by the built-in groups.

Pattern Syntax

Examples

/api/my-plugin/*/config
/webhook/incoming
/api/custom-integration/*

Advanced Settings

Payload Max Age

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.

Debug Logging

When enabled, encryption/decryption operations are logged to the server console. Never enable in production — it could expose sensitive data in log files.

Encryption Key

The encryption key is derived from environment variables in this priority:

  1. TRANSPORT_ENCRYPTION_KEY — Dedicated key for transport encryption
  2. ENCRYPTION_KEY — Shared encryption key
  3. JWT_SECRET — Falls back to JWT secret
  4. Built-in default (not recommended for production)
Production recommendation: Set TRANSPORT_ENCRYPTION_KEY in your environment to a strong random string (32+ characters). Using the default key provides obfuscation but not true security.

Deployment Platforms

Transport encryption works on every platform. The encryption key (TRANSPORT_ENCRYPTION_KEY) must be set as an environment variable, which every platform supports.

Key Generation & Storage

During initial setup, the wizard auto-generates a 256-bit key and attempts to:

  1. Write to .env file — Works on persistent filesystems (localhost, VPS, bare metal)
  2. Store in database — Backup copy in settings table (always works)
  3. Display to user — For manual platform configuration (always shown)

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.

PlatformHow to Set Keys.env Persists?
Localhost / VPSAuto-saved to .envYes
fly.iofly secrets set TRANSPORT_ENCRYPTION_KEY=<key>No (ephemeral)
RailwayDashboard → Variables, or railway variables setNo (ephemeral)
RenderDashboard → Environment → Secret Files or Env VarsNo (ephemeral)
Dockerdocker run -e TRANSPORT_ENCRYPTION_KEY=<key> or docker-compose envOnly if volume-mounted
Cloudflare Workerswrangler secret put TRANSPORT_ENCRYPTION_KEYNo (serverless)
AWS ECS/LambdaTask definition env vars or SSM Parameter StoreVia SSM/Secrets Manager
Herokuheroku config:set TRANSPORT_ENCRYPTION_KEY=<key>No (ephemeral)
AgenticMail CloudManaged automaticallyYes (managed)
Key must be identical across all instances. If you run multiple replicas (load-balanced), every instance must have the same TRANSPORT_ENCRYPTION_KEY. Otherwise, a request encrypted by one instance can't be decrypted by another.

Best Practices

For Most Deployments

For High-Security Environments

For Development

Troubleshooting

Dashboard shows errors after enabling encryption

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.

"Encrypted payload expired" errors

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.

"Invalid signature — payload tampered" errors

Cause: A proxy or middleware modified the request body after encryption.

Fix: Check for request-modifying proxies, WAFs, or middleware that transform JSON bodies.

API calls from external tools (curl, Postman) fail

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.

Performance impact

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.

Technical Details

Cryptographic Specifications

ComponentAlgorithmDetails
EncryptionAES-256-CBC256-bit key, PKCS7 padding
Key DerivationSHA-256Separate keys for encryption and HMAC
IntegritySHA-256 checksumFirst 16 hex chars of hash
AuthenticationHMAC-SHA256Signs IV + ciphertext + timestamp + checksum
IV GenerationCSPRNG16 random bytes per message
Server ImplementationNode.js cryptoNative, hardware-accelerated
Client ImplementationWeb Crypto APIBrowser-native, no dependencies

Encrypted Payload Format

// 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>" }

Source Files


AgenticMail Enterprise Documentation — Transport Encryption
Found an issue? Report it on GitHub