How TREK Handles Encryption Key Rotation for Production Deployments
TREK handles encryption key rotation through a Node.js migration script (server/scripts/migrate-encryption.ts) that decrypts all AES-256-GCM protected secrets with the old key and re-encrypts them with a new key while creating a timestamped database backup, allowing production operators to rotate keys without downtime.
TREK, an open-source travel management application maintained in the mauriceboe/TREK repository, protects sensitive configuration values—such as API keys, OAuth secrets, and MFA TOTP seeds—using AES-256-GCM encryption. Understanding how to perform encryption key rotation in production deployments is critical for maintaining security compliance, as the system relies on a 32-byte hex key derived from the ENCRYPTION_KEY environment variable or persistent file storage. The encryption architecture is designed to support zero-downtime rotation workflows that re-encrypt data in place without affecting unencrypted user records.
Understanding TREK's Encryption Key Resolution
Before rotating keys, you must understand how TREK resolves the active encryption key at startup. According to the source code in mauriceboe/TREK, the system checks four locations in strict precedence order:
Production Key Precedence Order
ENCRYPTION_KEYenvironment variable – The highest priority source. When set, TREK also persists this value to./data/.encryption_keyto ensure container restarts survive the loss of the environment variable../data/.encryption_keyfile – Used when the environment variable is absent. Created automatically on first start if no other key source exists../data/.jwt_secretfile – A legacy fallback for pre-v2 installations. On startup, this value is copied to.encryption_keyand future JWT rotations no longer impact encryption.- Auto-generated key – On fresh installations with no key source, TREK generates a random 32-byte hex key and writes it to
./data/.encryption_key.
How Encryption Key Rotation Works in TREK
TREK provides a dedicated migration utility rather than manual database edits. The script at server/scripts/migrate-encryption.ts performs an atomic re-encryption of every stored secret by decrypting with the old key and encrypting with the new key.
The Migration Script Process
When executed, the script performs the following operations:
- Prompts for the old
ENCRYPTION_KEYvalue (hidden input) - Prompts for the new
ENCRYPTION_KEYvalue (hidden input) - Requires manual confirmation before proceeding
- Creates a timestamped backup of the SQLite database named
travel.db.backup-<epoch> - Iterates through all encrypted tables and re-encrypts values in place
- Reports counts of migrated, skipped, already-migrated, and errored entries
- Aborts safely if any value cannot be decrypted, leaving the original database backup untouched
Performing a Zero-Downtime Key Rotation
Use the following workflow to rotate keys in production without service interruption:
1. Generate a New Encryption Key
Create a cryptographically secure 32-byte hex key:
export NEW_ENCRYPTION_KEY=$(openssl rand -hex 32)
echo "New key generated: $NEW_ENCRYPTION_KEY"
2. Execute the Migration Script
Run the migration from the host or inside a Docker container:
Host execution:
cd server
node --import tsx scripts/migrate-encryption.ts
Docker execution:
docker exec -it trek node --import tsx scripts/migrate-encryption.ts
The script will interactively prompt for the old key, new key, and confirmation. You can also pipe inputs for automation:
node --import tsx scripts/migrate-encryption.ts <<EOF
$OLD_ENCRYPTION_KEY
$NEW_ENCRYPTION_KEY
yes
EOF
3. Update the Runtime Environment
Persist the new key for subsequent restarts:
# Option A: Environment variable (recommended for containers)
export ENCRYPTION_KEY=$NEW_ENCRYPTION_KEY
# Option B: Persistent file
echo "$NEW_ENCRYPTION_KEY" > ./data/.encryption_key
4. Restart TREK
Restart the service to load the new key:
docker restart trek
Backup and Recovery Considerations
The regular backup ZIP created by TREK does not contain the encryption key. Production operators must store the key separately in a password manager or secrets manager (e.g., HashiCorp Vault, AWS Secrets Manager) and synchronize it with backup restoration procedures.
If the encryption key is lost entirely, all encrypted settings become unreadable. TREK cannot decrypt them, and you must manually re-enter those secrets via the admin UI. Unencrypted data—including trips, users, and places—remains unaffected.
Summary
- Key resolution follows a strict four-tier order: environment variable →
./data/.encryption_key→ legacy./data/.jwt_secret→ auto-generation. - Zero-downtime rotation is achieved via
server/scripts/migrate-encryption.ts, which creates a database backup before re-encrypting all secrets. - Backup isolation requires separate storage of the encryption key outside the TREK backup ZIP.
- Legacy migration automatically upgrades old installations using
.jwt_secretto the modern.encryption_keysystem on startup.
Frequently Asked Questions
Where does TREK store the production encryption key?
TREK stores the active encryption key in the ENCRYPTION_KEY environment variable, which takes precedence over all other sources. If the environment variable is not set, TREK falls back to the ./data/.encryption_key file. When the environment variable is present, TREK writes its value to this file to ensure persistence across container restarts.
What happens if I lose the encryption key?
If the encryption key is lost, TREK cannot decrypt any AES-256-GCM protected values, including API keys and OAuth secrets. You must re-enter these sensitive configuration values manually through the admin UI. Unencrypted database records—such as user accounts, trip data, and location information—remain fully accessible and unaffected by key loss.
Does TREK support zero-downtime encryption key rotation?
Yes. TREK supports zero-downtime rotation through the server/scripts/migrate-encryption.ts migration script. The script re-encrypts all stored secrets in place while the application is running, creates a timestamped backup of the SQLite database, and only requires a standard service restart to activate the new key after migration completes.
How do I rotate the encryption key when running TREK in Docker?
Execute the migration script inside the running container using docker exec -it trek node --import tsx scripts/migrate-encryption.ts, then update the ENCRYPTION_KEY environment variable in your docker-compose.yml or orchestration platform, and finally restart the container with docker restart trek. The script handles the re-encryption atomically before the new key is activated.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →