Encrypted Outputs
Oxid automatically encrypts sensitive output values at rest using AES-256 via PostgreSQL's pgcrypto extension. Zero configuration required.
Overview
When using the PostgreSQL backend, any output marked with sensitive = true is automatically encrypted before being written to the database. This protects secrets like database passwords, API keys, and connection strings from exposure through direct database access.
output "db_password" {
value = aws_db_instance.main.password
sensitive = true # This output will be encrypted at rest
}
output "vpc_id" {
value = aws_vpc.main.id
# Not sensitive - stored in plaintext
}How It Works
Encryption
When Oxid persists a sensitive output during apply:
- The output value is serialized to JSON
- An encryption key is derived from the SHA-256 hash of your
OXID_DATABASE_URL - The value is encrypted using
pgp_sym_encrypt(AES-256) via pgcrypto - The encrypted blob is stored in the
outputstable
Decryption
When you read a sensitive output (via oxid output or oxid query), Oxid transparently decrypts it using the same key derived from your connection string. No extra flags or configuration needed.
Key derivation
Key = SHA-256(OXID_DATABASE_URL)
The encryption key is derived deterministically from your database connection URL. This means:
- Anyone with the same connection URL can decrypt the outputs
- If you change the connection URL, existing encrypted outputs become unreadable
- The key is never stored - it is derived at runtime
Zero Configuration
Encryption is enabled automatically when all three conditions are met:
OXID_DATABASE_URLis set (PostgreSQL backend)- The
pgcryptoextension is available - The output is marked
sensitive = true
No flags, no config files, no key management. It just works.
CREATE EXTENSION IF NOT EXISTS pgcrypto;SQLite Behavior
The SQLite backend does not support encryption. Sensitive outputs are stored in plaintext in the local .oxid/oxid.db file.
.oxid/ directory has appropriate file permissions and is not committed to version control.# Secure your local state file chmod 600 .oxid/oxid.db echo ".oxid/" >> .gitignore
What Gets Encrypted
Only output values with sensitive = true are encrypted. Specifically:
- The
valuecolumn in theoutputstable
The following are NOT encrypted:
- Resource attributes (even if they contain secrets)
- Non-sensitive outputs
- Resource history entries
- Run metadata
Verifying Encryption
You can verify that outputs are encrypted by querying the database directly:
-- Direct database query (shows encrypted blob) SELECT name, value FROM outputs WHERE sensitive = true; -- value column contains binary pgcrypto ciphertext -- Through Oxid (transparent decryption) oxid output --json -- Shows the actual decrypted values
Rotating the Encryption Key
If you need to rotate the encryption key (e.g., changing the database URL), you must re-apply to re-encrypt all outputs with the new key:
# 1. Read current outputs while you still have the old URL oxid output --json > /tmp/outputs-backup.json # 2. Update the connection URL export OXID_DATABASE_URL="postgresql://user:new-pass@new-host:5432/oxid_state" # 3. Re-apply to re-encrypt outputs with the new key oxid apply