● Ship · Custody model
Security
who holds what.
The server never holds a trading key and never signs an order. This page lists what it does hold and how each piece is protected, so you can explain it to your users.
01Custody
- Arena orders are signed in your runner tab by an agent key generated there (sessionStorage only), approved once by your master wallet. The agent can trade and cancel, cannot withdraw, and expires.
- Ride orders are signed in the viewer's tab with the viewer's own agent key.
- Paper leagues involve no keys and no signing at all.
- Payments go to your Stripe account or your wallet; the server only verifies them.
02What the server holds
| Item | Protection |
|---|---|
| Model, Stripe, Telegram and RPC keys | Environment, or the console's store encrypted with AES-256-GCM under ADMIN_SECRET_KEY. Write-only; never sent to browsers. Model keys go only to the provider's own host unless ALLOW_CUSTOM_LLM_BASE_URL=1. |
| Console accounts | scrypt passwords of 12+ characters, lockout after 5 failures per email, optional or required two-factor, 12 h httpOnly SameSite=Strict cookie, a CSRF header on every write, append-only audit log with masked addresses. |
| Operator token | Compared in constant time; wrong tokens are slowed and then refused per client IP. |
| Viewer sessions and members' webhook secrets | Signed and encrypted with PIT_SECRET. Member webhooks are resolved, validated and pinned to their IP on every send (no private addresses unless ALERTS_ALLOW_PRIVATE=1). |
| Viewer sign-in | Sign-In with Ethereum messages are bound to your domain (PUBLIC_URL when set). Plain wallet signatures are checked offline; smart-contract wallets only through the read-only isValidSignature call on the RPC you list in SIWE_RPC_URLS. |
| Country and client IP | Trusted only from a proxy that sends TRUSTED_PROXY_SECRET. |
The server sends a Content-Security-Policy and never serves source maps (*.map).
03What you must protect
- Treat
OPERATOR_TOKEN,ADMIN_SECRET_KEY,PIT_SECRET,TRUSTED_PROXY_SECRETand every provider key as passwords. - Never commit
.env,data/or an export file fromnpm run admin:export-legacy. - Back up
ADMIN_SECRET_KEYwithadmin.sqlite. - Keep the runner browser clean: it signs real orders while it runs.