OUTRIDER docs
v1.1.0
Live demo Get help
● Ship · Keys at rest

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.

AES-256-GCMADMIN_SECRET_KEY

01What is protected, and how

Which terminal

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.

WhatStored asNeeds 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.sqliteYes
Notifications → webhook signing secretEncrypted in data/admin.sqliteYes
Each member's two-factor (authenticator) secretEncrypted in data/admin.sqliteYes
Passwordsscrypt hash with a per-user salt (cannot be turned back into the password)No
Sessions, printed setup codes, two-factor recovery codesOnly a SHA-256 hash is storedNo
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 logPlain values in data/admin.sqliteNo

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):

  1. ADMIN_SECRET_KEY, if set64 hex characters, or base64 of exactly 32 bytes. Generate one:
    openssl rand -hex 32
    Put it in .env or your process manager's environment (how), never in git.
  2. Otherwise, a generated fileOn the first start without ADMIN_SECRET_KEY, the desk writes a random key to data/.admin-secret-key (file mode 600) and prints:
    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.
    Later starts read the same file.
Moving from the file to the environment

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

  1. Try the old key firstRestore the old ADMIN_SECRET_KEY or data/.admin-secret-key from your backup and restart. If it is the right key, nothing else is needed.
  2. Otherwise, start with a new keySet a new ADMIN_SECRET_KEY (or delete data/.admin-secret-key so a new one is generated) and restart.
  3. 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.
  4. 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.

Trading keys

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

WhatWhy
data/journal.jsonl, data/state.jsonThe 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-shmConsole accounts, settings, history, audit log and the encrypted secrets. Copy all three while the desk is stopped.
ADMIN_SECRET_KEY or data/.admin-secret-keyWithout 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, .envYour settings and keys. .env holds secrets in plain text: protect it like the key.
data/pump-events/, data/gecko-trades/, wallet and research filesRecordings 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).