Envelope Encryption Migration Guide
This guide is for operators running Mycelium with the global token_secret key
who need to migrate to the envelope encryption scheme (KEK/DEK per tenant).
Overview
| Before | After |
|---|---|
| All secrets encrypted with a single global key (direct KEK) | Each tenant has a random DEK, encrypted by the KEK and stored in the database |
| Rotating the KEK invalidates all encrypted data | Rotating the KEK re-encrypts only the DEKs — O(number of tenants), not O(number of records) |
| No ciphertext versioning | The v2: prefix identifies data in the new scheme; the legacy format (v1) continues to be read |
The new version is fully backward-compatible: data encrypted in the old format continues to be decrypted correctly. Migration is optional at first and can be done incrementally.
Prerequisites
- Mycelium API Gateway updated to the version with envelope encryption support
- Access to the PostgreSQL database
- Access to the configured
token_secret(via env, Vault, or config file) — do not change this value before completing the migration myc-cliavailable in PATH
Migration steps
1. Verify compatibility (no downtime required)
The new version supports reading both v1 (old format) and v2 (new format)
data simultaneously. It is safe to deploy the new version while the database is
still in v1 format.
# Confirm the installed version supports envelope encryption
myc-cli --version
2. Run the SQL migration
psql $DATABASE_URL < path/to/20260421_01_envelope_encryption.sql
This adds the encrypted_dek and kek_version columns to the tenant table.
Both are nullable — existing tenants will have NULL until the next step.
3. Simulate the migration (dry-run)
SETTINGS_PATH=settings/config.toml myc-cli migrate-dek --dry-run
Expected output: a list of tenants with the count of v1 fields to migrate.
No writes are performed.
4. Run the migration
SETTINGS_PATH=settings/config.toml myc-cli migrate-dek
The command is idempotent and resumable:
- Fields already in
v2format are skipped. - It can be interrupted at any point and re-run without duplicating work.
- To migrate only a specific tenant:
--tenant-id <uuid>
5. Validate completion
SETTINGS_PATH=settings/config.toml myc-cli migrate-dek --dry-run
Should report 0 v1 fields remaining across all tenants.
KEK rotation (optional, post-migration)
After completing the migration to v2, the KEK can be rotated without touching
encrypted data records:
# Increment kek_version in config and make the new key available
# Then run:
SETTINGS_PATH=settings/config.toml myc-cli rotate-kek \
--from-version 1 \
--to-version 2
This re-encrypts only the encrypted_dek of each tenant with the new KEK. The
data records (user.mfa, tenant.meta, webhook.secret) are not
modified.
After a successful rotation, the v1 KEK can be discarded.
Side-effect — connection strings are invalidated.
token_secretis also used as the HMAC key for connection-string signatures (UserAccountScope::sign_token). Rotating the KEK therefore invalidates every signature issued under the old secret. There is no re-signing path — treat all active connection strings as revoked and plan the rotation accordingly. See the Encryption Inventory for the full list oftoken_secretconsumers.
Rollback
If you need to roll back before the migration is complete:
- Roll back the deployment to the previous gateway version.
- Any
v2data already written is unreadable by the old version (which does not know thev2format).
Therefore: do not interrupt a migration mid-way in production. Use
--dry-runto validate first, and run in a maintenance window if in doubt.
If the migration is complete and you need to roll back the SQL schema:
ALTER TABLE tenant DROP COLUMN IF EXISTS encrypted_dek;
ALTER TABLE tenant DROP COLUMN IF EXISTS kek_version;
This is only safe if no v2 data was written. If v2 writes have already
occurred, rolling back the schema will cause loss of access to those records.
Frequently asked questions
Do I need downtime to migrate?
No. The new version reads both v1 and v2. Deploy first, then run
migrate-dek with the service running.
Can I keep v1 data indefinitely?
Yes, as long as token_secret does not change. If the global key is rotated,
v1 data becomes unreadable. Migrating to v2 protects against this.
What about Argon2 hashes (passwords)?
Argon2 hashes in identity_provider.password_hash are one-way — there is no
plaintext to re-encrypt. They are unaffected by this migration and continue to
work normally.
What happens to new tenants created after the deploy? Tenants created after the deploy receive a DEK automatically on first use. No manual action is required.
See Encryption Inventory for the complete field classification table.