Secrets
How Modulo handles secrets and credentials.
Encryption keys
- Fernet encryption is keyed from the
FERNET_KEYenvironment variable FERNET_KEYis separate fromSECRET_KEY(used exclusively for JWT signing) - two distinct keys, two distinct cryptographic purposes- Key rotation for connector credentials must not require JWT session invalidation, and vice versa
- Startup refuses to start if either key is absent or fewer than 32 bytes
Never logged or returned
Secrets are never logged and never returned in API responses.
Credential-in-state rule
Decrypted connector credentials and model backend API keys must never enter LangGraph state, checkpoint blobs, OpenTelemetry span attributes, or log output. Connectors receive the decrypted credential in-process only (via a transient context object), use it for the API call, and do not serialise it.
At-rest encryption
Modulo encrypts connector credentials at rest with Fernet symmetric encryption. Credentials are decrypted once at run-start into a run-scoped context object and never written to LangGraph state, checkpoint blobs, logs, or OpenTelemetry spans.
Pluggable backends
A pluggable SecretsBackend interface (Vault, AWS Secrets Manager) is planned for v2.
DOM sensitive data rule
Sensitive field values (API key secrets, connector credentials, webhook secrets, model backend API keys) must never exist in the DOM in plaintext unless the user has explicitly completed a server-authenticated reveal action.
- Sensitive values render as
●●●●●●●in the DOM by default - A reveal action calls the server; the server verifies authentication and returns the value in the API response body
- The frontend injects it into the DOM for a time-limited display window (default 30 seconds)