Skip to content

Encryption at Rest

Configure encryption for sensitive data stored in the Mantis database.

Mantis encrypts sensitive data stored in PostgreSQL:

Data TypeEncryptionStorage
CredentialsAES-256-GCMDatabase
SecretsAES-256-GCMDatabase
API keysArgon2 hashDatabase
OAuth client secretsAES-256-GCMDatabase
Variable valuesAES-256-GCM (if marked sensitive)Database
Terminal window
# Generate 256-bit master key
openssl rand -base64 32
# Example output:
# K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=

The encryption configuration is a single base64-encoded 32-byte key under the [encryption] table. There is no enabled toggle or algorithm selector — encryption is always AES-256-GCM, and supplying a valid key is what enables credential encryption.

[encryption]
key = "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols="

Store the key as an environment variable instead of in the config file:

Terminal window
export MANTIS_ENCRYPTION_KEY="K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols="

Mantis uses the configured 32-byte key directly as the AES-256-GCM key — there is no key-derivation (HKDF) step and no per-record or per-data-type key hierarchy. A fresh random 96-bit nonce is generated for each encryption operation, so the same key safely encrypts many records. Rotating the key re-encrypts all existing records (see Key Rotation).

Mantis uses AES-256-GCM (Galois/Counter Mode):

PropertyValue
AlgorithmAES-256
ModeGCM (authenticated encryption)
Key size256 bits
IV size96 bits (12 bytes)
Tag size128 bits (16 bytes)

Benefits:

  • Authenticated encryption (integrity + confidentiality)
  • Parallelizable encryption/decryption
  • Hardware acceleration support (AES-NI)

Encrypted values are stored as:

Base64(IV || Ciphertext || Tag)
ComponentSizePurpose
IV12 bytesInitialization vector
CiphertextVariableEncrypted data
Tag16 bytesAuthentication tag

Mark a variable as sensitive when creating it through the REST API (or the Lens UI). A variable created with variable_type: "sensitive" is encrypted at rest and its value is masked on read:

Terminal window
curl -X POST "https://api.mantis.example.com/api/v1/variable-sets/$SET_ID/variables" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "DB_PASSWORD", "value": "supersecret123", "variable_type": "sensitive"}'
┌─────────────────────────────────────────────────────────────┐
│ Create Variable │
├─────────────────────────────────────────────────────────────┤
│ │
│ Name * │
│ [DB_PASSWORD ] │
│ │
│ Value * │
│ [•••••••••••••••• ] │
│ │
│ ☑ Sensitive (encrypt value at rest) │
│ │
│ Scope: │
│ ○ Global ● Environment ○ Target │
│ │
│ [Cancel] [Create Variable] │
│ │
└─────────────────────────────────────────────────────────────┘

Storage backend credentials are always encrypted:

[storage.s3]
access_key = "AKIAIOSFODNN7EXAMPLE"
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# secret_key is automatically encrypted when stored

In addition to application-level encryption, consider:

LevelMethodProtection
ApplicationAES-256-GCMData at rest in DB
Databasepgcrypto extensionAdditional layer
StorageLUKS/dm-cryptDisk encryption
CloudAWS RDS encryptionManaged encryption

Enable for additional database-level encryption:

CREATE EXTENSION IF NOT EXISTS pgcrypto;

For cloud deployments:

ProviderFeatureConfiguration
AWS RDSEncryption at restEnable in RDS console
AzureTDEEnable in Azure portal
GCPCloud SQL encryptionEnabled by default

API keys are hashed with Argon2 before storage, using the library’s default Argon2 parameters. These parameters are not configurable — there is no [api_keys] configuration section.

Rotate the master key annually or if compromised:

Terminal window
# Generate new master key
NEW_KEY=$(openssl rand -base64 32)
# Run key rotation (re-encrypts all data)
mantisctl key rotate \
--old-key "$OLD_KEY" \
--new-key "$NEW_KEY"
# Update configuration
export MANTIS_ENCRYPTION_KEY="$NEW_KEY"
# Restart services
systemctl restart mandible thorax

See Key Rotation for detailed procedures.

Database backups contain encrypted data:

Backup TypeEncryption Status
pg_dumpData remains encrypted
File backupData remains encrypted
SnapshotData remains encrypted

Backup the master key securely:

  • Store in separate secure location
  • Use secrets management (Vault, AWS Secrets Manager)
  • Document key recovery procedure
  • Test key recovery periodically

For disaster recovery, you need:

  1. Database backup
  2. Master key
  3. Configuration files
Terminal window
# Restore database
pg_restore -d mantis mantis_backup.dump
# Set master key (from secure backup)
export MANTIS_ENCRYPTION_KEY="backed-up-key"
# Start services
systemctl start mandible thorax

Problem: Decryption failed: authentication error (wrong key, AAD, or tampered data)

Causes:

  • Wrong master key
  • Corrupted data
  • Data modified in database

Solution:

Terminal window
# Verify that all encrypted records decrypt with the configured key
# (reads MANTIS_ENCRYPTION_KEY, or pass --key explicitly)
mantisctl key verify

Problem: SECURITY ERROR: Encryption key is not configured (missing key) or SECURITY ERROR: Encryption key must be exactly 32 bytes (256 bits), got N bytes (wrong length) or SECURITY ERROR: Encryption key is not valid base64 (encoding error).

Diagnosis:

Terminal window
# Check key format (should be base64)
echo "$MANTIS_ENCRYPTION_KEY" | base64 -d | wc -c
# Should output: 32

Solution:

Terminal window
# Ensure key is properly formatted
export MANTIS_ENCRYPTION_KEY="$(cat /path/to/key | tr -d '\n')"

Encryption adds minimal overhead:

OperationOverhead
Encrypt~1ms per record
Decrypt~1ms per record
Batch operationsParallelized

If performance issues occur:

Terminal window
# Check for hardware acceleration
grep -m1 'aes' /proc/cpuinfo
# Should show: aes (AES-NI support)

Confirm that every encrypted record can be decrypted with the configured key. This is the primary integrity check after a key change or restore:

Terminal window
# Uses MANTIS_ENCRYPTION_KEY, or pass --key "<base64 key>"
mantisctl key verify

To generate a fresh key (for example before a rotation), use:

Terminal window
mantisctl key generate
Terminal window
# Good: Environment variable
export MANTIS_ENCRYPTION_KEY="..."
# Bad: Configuration file
# [encryption]
# key = "..." # Don't do this!

Use a secrets manager:

ToolIntegration
HashiCorp VaultKV secrets engine
AWS Secrets ManagerIAM integration
Azure Key VaultManaged identity
GCP Secret ManagerService account

Example with Vault:

Terminal window
export MANTIS_ENCRYPTION_KEY=$(vault kv get -field=key secret/mantis/encryption)
Key TypeRotation Period
Master keyAnnually
After compromiseImmediately
After personnel changeAs needed

Enable multiple layers:

[encryption]
key = "<base64 32-byte key>" # Application-level AES-256-GCM
[database]
url = "postgres://...?sslmode=verify-full" # TLS in transit
# Plus: Disk encryption on database server
# Plus: Cloud provider encryption (RDS, etc.)

Maintain documentation for:

  • Key storage location
  • Key recovery procedure
  • Rotation schedule
  • Emergency contacts