Skip to content

Configuration reference

Every environment variable read by the code, with its default and purpose, taken from the source. Where DEPLOY.md disagrees, the code wins, and the difference is noted.

Using deploy/docker-compose.prod.yml?

That compose file loads all of deploy/.env into the server container (env_file: .env), so every variable below can be set there. A few are fixed by the compose file and win over .env (DATA_DIR, TRUST_PROXY, DEEP_ENGINE). See Deploy to a VPS.

Server: network and paths

variabledefaultpurposeread in
PORT8080listen portindex.js
HOST0.0.0.0bind addressindex.js
PUBLIC_URLhttp://localhost:$PORTbase for sign-in, confirmation, unsubscribe and puzzle links in email. Must match the live URL exactly, scheme included. A trailing / is removedindex.js
TRUST_PROXYunset1 makes rate limiting use the left-most X-Forwarded-For entry. Set it behind any proxy, and never without one. See Rate limitingindex.js
DATA_DIRrepository rootparent directory for records, database, caches, annotations and the server keyindex.js
RECORDS_DIR$DATA_DIR/recordssigned match recordsindex.js
RECORDS_GZIPon0 writes records as plain .json instead of .json.gzrecords.js
DB_DIR$DATA_DIRdirectory of gammonchain.dbindex.js
KEY_DIR$DATA_DIRdirectory of server-key.jsonindex.js
SERVER_KEY_PKCS8unsetthe server's Ed25519 private key as hex PKCS#8. Takes precedence over server-key.json, for platforms with secrets but no diskidentity.js
ANNOTATIONS_DIR$DATA_DIR/annotationsstored match analysesindex.js
CACHE_DIR$DATA_DIR/analysis-cacheposition-level analysis cacheindex.js
NODE_ENVdevelopmentonly used as the environment in error reports. The Docker image sets productionerrors.js

Bots and analysis

variabledefaultpurposeread in
BOT_ENGINEautoengine for the hard bot: auto tries BGBlitz, then gnubg, then built-in. bgblitz or gnubg try only that one before falling back to built-in. Any other value, such as builtin, uses built-inopponent.js
DEEP_ENGINEbgblitz/de.bgblitz.ai.TachiAIengine id that deep-analysis jobs are queued for. Must equal the id the workers report: gnubg-1.07 for gnubg. The Dockerfile and prod compose file set gnubg-1.07. (DEPLOY.md shortens the default to bgblitz/…)index.js
GNUBG_CMD/usr/games/gnubggnubg binaryadapters/gnubg.js
BGBLITZ_CPbuildJava classpath for the bridge, including your licensed BGBlitz jaradapters/bgblitz.js
BGBLITZ_CLASSunsetthe TachiAI engine class to bind by reflection. auto only tries BGBlitz when this is setadapters/bgblitz.js, opponent.js
JAVA_CMDjavaJava binary for the BGBlitz bridgeadapters/bgblitz.js
ENGINE_DEBUGunsetany value copies engine subprocess stderr to the server's stderranalysis/process.js
GAMMONET_KEYunsetshared key volunteer workers must send as x-gammonet-key. Unset: the worker endpoints return 503 and deep analysis is unavailableindex.js
STRICT_ANALYSISunset1 requires two different workers to agree before a result is cached. See Strict modeindex.js

Email

variabledefaultpurposeread in
MAIL_PROVIDERconsoleconsole, mailjet, resend, brevo, mailgun or webhookmail.js
MAIL_API_KEYunsetprovider key. For Mailjet, the public key. For webhook, optional, sent as a Bearer tokenmail.js
MAIL_API_SECRETunsetMailjet only: the private key. Both are requiredmail.js
MAIL_DOMAINunsetMailgun sending domainmail.js
MAIL_WEBHOOKunsetURL for the webhook providermail.js
MAIL_FROMgammonchain <no-reply@localhost>sender. Must use the authenticated domainmail.js
MAIL_HOUR8UTC hour the daily puzzle goes outindex.js
MAIL_DAILY_CAP500most addresses mailed in one daily run. Keep it under your provider's free tier (committed configs use 200)index.js
CRON_KEYunsetrequired to call POST /api/cron/daily?key=…. Unset means the endpoint always answers 403index.js

Rate limits

All values are per client address. Burst is capacity, rate is refill per minute. See Rate limiting.

variabledefaultbucket
LIMIT_CONNECT_BURST / LIMIT_CONNECT_RATE120 / 240opening WebSockets
LIMIT_READ_BURST / LIMIT_READ_RATE120 / 240GET on /api/* and /records/*
LIMIT_ANALYSIS_BURST / LIMIT_ANALYSIS_RATE15 / 30/api/analysis/*
LIMIT_WRITE_BURST / LIMIT_WRITE_RATE30 / 60non-GET requests

Error reporting

variabledefaultpurposeread in
ERROR_PROVIDERconsoleconsole, sentry or webhookerrors.js
SENTRY_DSNunsethttps://<key>@<host>/<project>, for sentryerrors.js
ERROR_WEBHOOKunsetDiscord or Slack webhook URL, for webhookerrors.js
RELEASEdevrelease tag attached to reportserrors.js

Backups

variabledefaultpurposeread in
BACKUP_S3_ENDPOINTunsete.g. https://<account>.r2.cloudflarestorage.com. Backups run only when endpoint, bucket, key and secret are all setbackup.js
BACKUP_S3_BUCKETunsetbucket namebackup.js
BACKUP_S3_KEY / BACKUP_S3_SECRETunsetS3 credentialsbackup.js
BACKUP_S3_REGIONautoauto for R2, a real region for B2 or AWSbackup.js
BACKUP_INTERVAL_H6hours between snapshots (minimum 1)backup.js
BACKUP_PREFIXgammonchainobject key prefix. Not listed in DEPLOY.mdbackup.js

gammonet worker (bin/gammonet.js)

variabledefaultpurpose
GAMMONET_SERVERhttp://localhost:8080server to pull work from (--server overrides)
GAMMONET_KEYunsetworker key (--key overrides). Prefer the env var: argv shows up in ps and docker inspect
GAMMONET_NAMErandom anon-xxxxxxxx per processworker id sent on both acquire and submit (--name overrides). Distinct ids are what let STRICT_ANALYSIS count two workers as independent

DNS setup script (bin/setup-dns.js)

variablepurpose
MAILJET_API_KEY / MAILJET_API_SECRETMailjet key pair (default provider)
RESEND_API_KEYfor --provider resend. Needs domain access
CLOUDFLARE_API_TOKENscope Zone → DNS → Edit, this zone only. Not needed with --dry-run

Deploy scripts

variablescriptdefaultpurpose
APP_USERsetup.shgammonunprivileged user to create
REPO_URLsetup.shunsetclone this repo into /opt/gammonchain instead of waiting for push.sh
APP_DIRpush.sh/opt/gammonchaintarget directory on the server
SSH_KEYpush.shunsetprivate key to use, with IdentitiesOnly=yes
CONFfirewall.shports.confport list to build the firewall from

Load test (test/load.js)

variabledefaultpurpose
LOAD_MATCH_LENGTH1match length for the simulated matches
LOAD_PORT8233port for the server the test starts

Hard-coded values worth knowing

These are constants in the code, not configuration:

valuewhere
reconnect grace before forfeit: 90 sDEFAULT_GRACE_MS, actor.js
idle match expiry: 30 minIDLE_LIMIT_MS, actor.js
handshake timeout: 60 sHANDSHAKE_LIMIT_MS, actor.js
WebSocket ping every 25 s, terminated after 2 missed pingsindex.js
sign-in token lifetime: 30 min, single usedb.js
emails per key/address: 5 per hourindex.js
reveal chain length: 4000identity.js, actor.js
deep analysis wait per position: 20 sindex.js

Rules engine, fairness protocol, verifier, analysis and worker: MIT. Server and client: AGPL-3.0-or-later.