Skip to content

What is encrypted

Badlen uses two keys, for unrelated jobs. One seals three columns in the database; the other seals a file you downloaded. Neither can stand in for the other.

SECRET_ENCRYPTION_KEY lives in your .env and nowhere else. It is 32 bytes, base64, and the instance refuses to start without it. Everything it encrypts is encrypted with AES-256-GCM, at rest, in the database.

It seals exactly three things:

  • Your two-factor secret, written when you enrol an authenticator app and opened again on every code check.
  • Your provider API keys, today the Moralis key, used to find the tokens held at a wallet address.
  • The opening code of your relay, when you chose one. The relay seals the letter without you, so the instance has to be able to read it. That weakens nothing: whoever holds the database and the .env already reads your accounts in the clear.

It seals nothing else, so those three are all a leaked key would expose.

Everything else is stored in the clear: your accounts, balances, operations, holdings, lots, prices, daily net worth points, categories, rules, preferences and achievements. So are your wallet addresses, stored with the account they belong to: an address is public by construction and the wallet sync reads it on every run. Your password is neither in the clear nor encrypted: it is hashed with argon2id, which is one way and has no key.

The passphrase you type when exporting seals the downloaded file itself, and is used for nothing else. The key is derived from it with argon2id, over 64 MiB of memory, three passes and a fresh salt per file. The salt and those parameters travel inside the file, so it opens on any Badlen without you writing anything down.

It never touches SECRET_ENCRYPTION_KEY, on purpose: a backup whose readability depended on a key in a .env would stop opening the day that key was regenerated. A sealed export opens on a machine that never saw the original instance, and the secrets it carries are re-encrypted under the key of the instance doing the restoring.

Export your data is where you choose between a sealed file and a plain one, and where each one’s contents are listed.

A pg_dump carries the ciphertext of those three columns and none of the key, so a restore into an instance holding a different key gives every figure back but none of the sealed values. Restoring under another key covers that case and the way out of it. It is why SECRET_ENCRYPTION_KEY belongs beside the dump, in a different place, and why a passphrase-sealed JSON export has none of the problem.