Configuration
environment, arena file, console.
Three places: environment variables for secrets and the server, arena.config.json for the arena, and the console for brand, fees, payments and keys. The console edits the arena file for you; hand edits hot-reload.
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, $(…) and multi-line commands ending in \ do not work as written in PowerShell or cmd. The npm and node commands work in any of them.
01Three places
| Where | What goes there | Change needs |
|---|---|---|
.env / host secrets | Secrets, server and proxy settings | A restart |
arena.config.json | Season, competitors, league, paper league, community, membership plans, Ride, prompts | Nothing: the server validates and reloads it about a second after you save. An invalid file is refused and the previous config stays. |
Console (admin.sqlite) | Brand, disclaimer, site and SEO, banner, builder fee, payment switches, API and payment keys | Save. While the console runs these override the same fields in the file. |
GET /api/config serves it to every visitor, except prompts, provider settings and disabled competitors. Addresses and style prompts are public by design. Never put a secret in arena.config.json.
02Environment variables
The model keys, Stripe keys and the USDC RPC URL below are your own provider accounts. Model calls are billed to you by the AI provider for every decision (the paper league's daily LLM budget caps them); Stripe deducts its fees from each payment; a private RPC plan above its free tier is billed to you by the provider. Details: Costs not included.
| Variable | Default | Purpose |
|---|---|---|
PORT, HOST | 8787 (8080 in the Docker image); 0.0.0.0 in production, else 127.0.0.1 | Listen port and bind address |
NODE_ENV | unset (production in npm start and the image) | Production serves dist/, binds 0.0.0.0 and marks the viewer cookie Secure |
ADMIN_SECRET_KEY Required | unset → console off (503) | Turns on /admin, encrypts its secrets. 32 bytes: openssl rand -hex 32. Back it up. |
ADMIN_SETUP_TOKEN | unset → the log prints a one-time setup code | One-time token that creates the console owner. 24+ characters: openssl rand -hex 24. Remove it once the owner exists; it is never accepted again. |
ADMIN_DB | admin.sqlite next to PIT_DB | Console database |
PIT_SECRET Required | unset (Status warns) | Signs viewer sessions, encrypts members' webhook secrets. openssl rand -base64 32. Keep stable. |
OPERATOR_TOKEN | unset → runner endpoints 503 | Password of the runner tab (/api/decide and execution reports). A long random string, 24+ characters. |
ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY / GEMINI_API_KEY, XAI_API_KEY, DEEPSEEK_API_KEY | unset | Model keys; each wins over and locks its console field |
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, TELEGRAM_BOT_TOKEN, USDC_RPC_URL | unset | Payment and alert secrets; usually entered in the console |
PUBLIC_URL | unset → each request's own host (Status warns) | Your public address, e.g. https://arena.example.com. Fixes the domain viewers sign in to, and the links in share cards, alerts and Stripe returns. Must be https:// in production. Set it on every public host. |
SIWE_RPC_URLS | unset → plain wallets only | Sign-in for smart-contract wallets (Safe, Argent, deployed smart accounts; EIP-1271). One RPC per chain as chainId=url, comma separated, e.g. 1=https://…,8453=https://…. The server calls the wallet contract's isValidSignature read-only over that RPC. Plain wallets never use it.
Wallets not yet deployed on that chain (ERC-6492) are not supported. |
PIT_DOCS_URL | unset → no Docs link | Where the console's Docs link points: your own copy of this documentation |
TRUSTED_PROXY_SECRET | unset → country and IP headers ignored | Value your CDN injects as x-pit-proxy-secret. Needed before Ride can allow anyone. |
RIDE_COUNTRY_HEADER, CLIENT_IP_HEADER | cf-ipcountry, cf-connecting-ip | The headers trusted from the verified proxy |
RIDE_ENABLED, RIDE_BLOCKED_COUNTRIES | .env.example sets false and US,CA,GB,SG,HK,MY | Override Ride and its country list |
PIT_CONFIG, PIT_DB, PIT_DIST | ./arena.config.json, ./data/pit.db, ./dist | File locations |
ADMIN_COOKIE_SECURE | on in production | false only for a plain-http local test |
CSP_CONNECT_EXTRA | unset | Extra connect-src origins for the security policy |
ALLOW_CUSTOM_LLM_BASE_URL | unset | 1 lets a provider base URL on another host (a gateway) receive the key |
POLYMARKET_GAMMA_URL | Polymarket Gamma | League market source |
ALERTS_ALLOW_PRIVATE, PIT_SILENT, PIT_OG_FONTS, PIT_BACKUP_KEEP | unset, unset, server/og/fonts, 14 | 1 lets member webhooks reach private addresses (local tests only); 1 silences logs (tests); share-card font folder; backups kept by deploy/backup.mjs |
ADMIN_PASSWORD | unset | Optional and not a login (the console has its own accounts). Read only while PIT_SECRET is unset, to derive the key that encrypts members' webhook secrets. Set PIT_SECRET instead. |
03Arena file: season and competitors
| Field | Limits | Meaning |
|---|---|---|
brand.name, brand.tagline | 1–40, ≤200 | Site name and hero line (console → Brand overrides) |
season.id, season.name | slug ≤40, 1–80 | Stored with every decision |
season.network | testnet | mainnet | Where real-money competitors trade |
season.startsAt, season.endsAt | ISO time with a zone; end or null | Season window for PnL |
season.coins | 1–50 perp names | Coins the models may trade |
season.decisionIntervalSec | 10–86400 | Seconds between real-money decisions |
season.risk.* | maxLeverage (≤50), maxPositionPctOfEquity (0–1), maxOpenPositions, minStopDistance, maxStopDistance, slippageBps | |
competitors[] | up to 20 | id, name, model.provider + model.model (exact provider id), color, address (sub-account or null), optional style (public prompt), enabled, initials, maxLeverage, decisionIntervalSec |
builder | null or {address, feeTenthsBps 0–100} | Ignored while the console runs; use console → Builder fee |
The example competitors use claude-opus-5-5, gpt-6-sol, gemini-3.8-flash, grok-4.7 and deepseek-v4-pro. Providers retire ids: check them before every season. Each decision stores the id the provider says answered (servedModel).
04Arena file: paper league
The optional paper block; every field falls back to a default.
| Field | Default | Limits |
|---|---|---|
enabled | true | |
startEquity | 10,000 (paper USD) | 100 – 100,000,000 |
decisionIntervalSec | 900 | 60 – 86,400 |
activeVariantIds | ["baseline"] | Built-in variants: Baseline, Monk, Situational, Max-lev |
takerFeeBps, slippageBps | 4.5, 5 | 0–100, 0–500 |
dailyBudgetUsd | 10 | 0 – 100,000. A hard stop for estimated LLM spend of all paper leagues; 0 = no model calls. |
minTradesForRank | 10 | Below this a standing is flagged "small sample" |
pricing | built-in price table | Per-model input/output price per million tokens, used for the budget estimate |
05Arena file: community, league, money, Ride
| Block | Main fields (defaults) |
|---|---|
community | enabled (true), Sign-In with Ethereum (on), picks: season winner and daily duels (on, 3 duels a day, 10 points a win, streak bonus 2, 100 for the season winner, winner pick locks 48 h after start), agents: on, moderation off, 1 free slot, strategy ≤1,200 characters, decides every 900 s, allowedModels (empty = none listed) |
league | enabled, marketCount (0–100), maxDaysToClose, minVolume24h, reforecastHours, optional price band, pinned and excluded markets |
money | Absent = everything off.
plans[] (up to 10: price, period days, features: realtime, alerts, archive, export, API, agent slots; billing subscription or one-time), freeDelayMinutes, stripe.enabled, usdc (chain id, token, receiver, confirmations), telegram (bot username), affiliates[], publicApi.rateLimitPerMinute |
ride, rideCaps | enabled (false), blockedCountries; rider maxima and optional starting values |
site | Accent, headline, SEO, disclaimer, banner, theme (usually from the console); footer and social links (Site links page) |
prompts, providerSettings | Custom trade and forecast templates; per-provider base URL, timeout, max tokens, temperature. Never sent to the public. |
The full schema is in shared/admin.ts (configSchema) with the blocks in shared/paper-schema.ts, shared/community-schema.ts and shared/money-schema.ts.