Secrets and backups
what is encrypted, and how not to lose it.
Keys you store in the console are encrypted on disk with one secret key. Keep that key safe and backed up separately from the data folder: without it, stored secrets cannot be read. This page explains the key, what happens if it is lost or changed, and what to back up.
01What is protected, and how
The commands on this page are written for the shell of macOS and Linux (bash or zsh). On Windows, run them in WSL (Windows Subsystem for Linux, Ubuntu) or Git Bash: commands such as cp, openssl, rm -rf and multi-line curl with \ do not work as written in PowerShell or cmd. The node and npm commands work in any of them.
| What | Stored as | Needs the secret key to be read |
|---|---|---|
| Integration secrets: Anthropic key, Telegram bot token, Solana RPC URL, and for live trading the Solana wallet key; with the optional modules the TypeSafe key (jev) and the Hyperliquid API wallet key and account address (perps) | Encrypted in data/admin.sqlite | Yes |
| Notifications → webhook signing secret | Encrypted in data/admin.sqlite | Yes |
| Each member's two-factor (authenticator) secret | Encrypted in data/admin.sqlite | Yes |
| Passwords | scrypt hash with a per-user salt (cannot be turned back into the password) | No |
| Sessions, printed setup codes, two-factor recovery codes | Only a SHA-256 hash is stored | No |
ADMIN_SETUP_TOKEN (creates the first owner) | Not stored: read from the environment and compared in constant time. Remove it once the owner exists. | No |
| Normal settings, history, audit log | Plain values in data/admin.sqlite | No |
Algorithm (packages/admin-kit/server/crypto.ts): AES-256-GCM with a fresh random 12-byte IV for every value and an authentication tag, and the field's name bound as associated data, so an encrypted value copied onto another field fails to decrypt instead of being misused.
Secrets are write-only in the console: an owner can set, replace or clear them, a manager can test them, and nobody can read them back. A secret set as an environment variable (ANTHROPIC_API_KEY, TELEGRAM_BOT_TOKEN, TYPESAFE_API_KEY, SOLANA_PRIVATE_KEY, HL_API_WALLET_KEY, HL_ACCOUNT_ADDRESS) is not stored at all: it wins and locks the console field.
02The secret key
The console uses one 32-byte key (src/admin/index.ts, secretKey(); compiled to dist/ by npm run build):
- ADMIN_SECRET_KEY, if set64 hex characters, or base64 of exactly 32 bytes. Generate one:
Put it inopenssl rand -hex 32.envor your process manager's environment (how), never in git. - Otherwise, a generated fileOn the first start without
ADMIN_SECRET_KEY, the desk writes a random key todata/.admin-secret-key(file mode 600) and prints:
Later starts read the same file.Admin: ADMIN_SECRET_KEY not set — generated <path>/data/.admin-secret-key. Back it up with the data dir; without it stored secrets cannot be read.
The file holds the key as 64 hex characters. Set ADMIN_SECRET_KEY to exactly that value and everything keeps decrypting; you can then remove the file. Do this before you move the data folder to a new server.
A key in the wrong format stops the console at start (the desk prints Admin console failed to start: createAdminKit: secretKey must be 32 bytes as 64 hex chars or base64. Generate one with: openssl rand -hex 32) and the desk then runs without the console, so the dashboard has no sign-in. Fix the key before you expose the server.
03If the key is lost or changed
The key cannot be recovered from the data folder, and there is no master key. With a different key:
- Stored secrets stop working. The console logs, once per read,
[admin] secret "<field>" cannot be decrypted: was the secretKey changed?and the desk behaves as if that key were not set: no Claude scoring or briefing, no Telegram, the public RPC, and live mode refuses to start for lack of keys. - Authenticator codes stop working. The log shows
[admin] two-factor secret of <email> cannot be decrypted: was the secretKey changed?. Recovery codes still work, because they are stored as hashes. - Everything else is fine: accounts and passwords, settings, history, the audit log, the journal and the desk state are not encrypted with this key.
04Recovery path
- Try the old key firstRestore the old
ADMIN_SECRET_KEYordata/.admin-secret-keyfrom your backup and restart. If it is the right key, nothing else is needed. - Otherwise, start with a new keySet a new
ADMIN_SECRET_KEY(or deletedata/.admin-secret-keyso a new one is generated) and restart. - Re-enter every secretSign in as owner, open Integrations (and Notifications for the webhook secret) and set each key again. A replaced value is encrypted with the new key. The Status page shows whether the RPC and the Telegram token answer.
- Fix two-factorEach member with two-factor signs in with a recovery code, then turns two-factor off in Account (password and a recovery code) and on again with the authenticator app. If the owner requires two-factor for that role, it cannot be turned off; another owner removes the account in Team and adds it again.
There is no built-in command that re-encrypts stored secrets under a new key. To rotate the key on purpose, follow the same steps: note which secrets are set, change the key, restart and enter them again. Rotating also makes sense after anyone who had server access leaves.
If you think the server or the key file was exposed, treat the Solana wallet key and the Hyperliquid API wallet as compromised: move the funds to a new wallet and revoke the API wallet on Hyperliquid, then enter the new keys.
05What to back up
| What | Why |
|---|---|
data/journal.jsonl, data/state.json | The track record and the desk's state. The journal is hash-chained; node src/cli.mjs verify checks a restored copy. |
data/admin.sqlite with admin.sqlite-wal and admin.sqlite-shm | Console accounts, settings, history, audit log and the encrypted secrets. Copy all three while the desk is stopped. |
ADMIN_SECRET_KEY or data/.admin-secret-key | Without it the encrypted secrets in the backup cannot be read. Keep a copy separate from the data backup (a password manager is a good place), so one stolen backup does not hold both. |
config.json, .env | Your settings and keys. .env holds secrets in plain text: protect it like the key. |
data/pump-events/, data/gecko-trades/, wallet and research files | Recordings and wallet books. Large; keep them if you want to replay or research later. |
Stop the desk first (Ctrl+C, or stop your service, PM2 process or container), then, in the install folder:
tar czf outrider-backup-$(date +%F).tgz --exclude data/.admin-secret-key data config.json
# store data/.admin-secret-key (or your ADMIN_SECRET_KEY) somewhere else
Restore by stopping the desk, unpacking the archive into the install folder and starting again with the same secret key. In Docker, data lives in the outrider-data volume (Docker).