Token key rotation
The relay encrypts credit tokens at rest with TOKEN_ENCRYPTION_KEY_PRIMARY. This
rotates that key with no downtime and no lost credits.
Balances aren't tied to the key — only restore is (handing a token back to someone who reinstalled). During a rotation the relay runs the new key as primary and the old key as a decrypt fallback, while a sweep re-encrypts every wallet onto the new key. The old key is dropped only once every wallet is on the new one.
The just infra token-key-* commands resolve keys and the database URL themselves and
call the rotate-key tool for you — you never set environment variables or run the
binary directly. Each step guards the one before it.
Rehearse on staging first, and take a database snapshot before step 4. Set the target:
export ENV=staging # or production
1. Deploy this feature
just infra deploy $ENV
Ships the two-key support and the new Terraform outputs the rest of this relies on. Skip if it's already deployed.
2. Check the starting state
just infra token-key-status $ENV
Fails if an earlier rotation left wallets on an old key. Resolve that before continuing.
3. Deploy the new key
just infra token-key-replace $ENV
It prints the current key and the exact .env line to add as the secondary, then stops.
Add that line, re-run, and it regenerates the primary — leaving the relay on new primary
- old secondary, every wallet still readable.
4. Re-encrypt every wallet
just infra token-key-sweep $ENV
Shows a dry-run count, asks for confirmation, re-encrypts every wallet onto the new key, then verifies the drain. Safe to re-run — it resumes where it stopped.
Undecryptable wallets. If the sweep reports wallets it couldn't decrypt, they were
encrypted under a key you didn't supply — usually an older key from before rotation was in
place. Their credits still work, but the stored token can't be restored until it's
re-encrypted with the key it was minted under. They aren't under the key you're retiring,
so the rest of the rotation is safe to finish; track down the original key and re-run the
sweep with it, or handle those wallets manually. token-key-status will keep listing them
until they're resolved.
5. Drop the old key
just infra token-key-retire $ENV
It stops until you remove the secondary line from .env, then redeploys. The old key is
now gone. Rotation complete.