All Docs
FeaturesCalmony Sanctions MonitorUpdated March 11, 2026

Encryption Configuration

Encryption Configuration

The platform encrypts sensitive fields (names, dates of birth, and other PII) using AES-256-GCM. The encryption key is derived from the ENCRYPTION_SECRET environment variable via PBKDF2 (100,000 iterations).

Encrypted values are stored in the format: enc:iv:authTag:ciphertext (all base64).


Environment Variables

VariableRequiredDescription
ENCRYPTION_SECRETYes, in productionThe raw secret from which the AES-256-GCM key is derived. Minimum 32 characters. Generate with openssl rand -hex 32.
ENCRYPTION_ENABLEDNoSet to "false" to explicitly disable encryption. The only supported opt-out mechanism.

Production Enforcement (SEC-06)

As of v0.1.40, the encryption module includes a startup guard that runs at module import time. If NODE_ENV=production and ENCRYPTION_ENABLED is not set to "false", the following checks are enforced:

  • If ENCRYPTION_SECRET is absent or empty → fatal error, application refuses to start.
  • If ENCRYPTION_SECRET is shorter than 32 characters → fatal error, application refuses to start.

This prevents silent plaintext storage of PII due to a misconfigured or forgotten secret. The error is surfaced by Next.js at startup, before any request is served.

Example fatal error messages

[FATAL] ENCRYPTION_SECRET is not set in a production environment.
All PII would be stored as plaintext, violating GDPR/HIPAA requirements.
Generate a key with: openssl rand -hex 32
[FATAL] ENCRYPTION_SECRET is too short (16 chars).
A minimum of 32 characters is required to ensure sufficient entropy.
Generate a compliant key with: openssl rand -hex 32

Generating a Compliant Key

openssl rand -hex 32

Copy the output (64 hex characters, which exceeds the 32-character minimum) and set it as your ENCRYPTION_SECRET.


Disabling Encryption (Dev / CI)

To run without encryption in non-production environments (e.g. CI pipelines, local development with no real PII), set:

ENCRYPTION_ENABLED=false

This is the only supported way to opt out. Do not simply leave ENCRYPTION_SECRET unset in production — the application will throw a fatal error.

When ENCRYPTION_ENABLED=false is set:

  • encryptField() returns plaintext unchanged.
  • A security warning is logged at startup.
  • The /api/health endpoint reports encryption status as warn rather than pass.

Key Quality Rules

Prior to v0.1.40, the module used an includes("dummy") substring check to detect placeholder secrets. This heuristic has been removed. Key quality is now enforced exclusively by minimum length (≥ 32 characters), which is objective and unambiguous.


Crypto Failure Behaviour

As of v0.1.40, encryptField() re-throws any crypto error rather than silently falling back to plaintext. Callers should handle the exception and abort the write operation rather than storing unencrypted PII.


Health Check

The /api/health endpoint reports encryption status under the encryption key. The status values are:

StatusMeaning
passENCRYPTION_SECRET is present and meets the minimum length requirement.
warnENCRYPTION_ENABLED=false (explicit opt-out), or secret is absent/short in a non-production environment.
failSecret is absent or too short in a production environment — this is a critical misconfiguration.

.env.example Reference

# AES-256-GCM encryption for sensitive fields (names, dates of birth, etc.).
# The key is derived from ENCRYPTION_SECRET via PBKDF2 (100 000 iterations).

# ENCRYPTION_SECRET
#   Required in production (NODE_ENV=production). The app will REFUSE TO START
#   if this is absent or shorter than 32 characters in production.
#   Generate a compliant key: openssl rand -hex 32
ENCRYPTION_SECRET=         # Min 32 chars. Example: openssl rand -hex 32

# ENCRYPTION_ENABLED
#   Set to "false" to explicitly opt out of encryption in non-production
#   environments (dev, CI, staging without real PII).
#   Default: encryption is active when ENCRYPTION_SECRET meets the minimum
#   length requirement.
ENCRYPTION_ENABLED=        # "false" to disable; omit (or leave blank) to enable