Deploying under your domain
The whole platform is one Python process (FastAPI + SQLite + the market engine and settlement loop).
git clone <this repo> && cd prop-sim
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env # official values (t5market.com); set XGM_DOMAIN=localhost:8000 XGM_SCHEME=http for a local copy
set -a; . ./.env; set +a
xgames serve --port 8000 # or: uvicorn xgames.main:app --port 8000
Open http://localhost:8000.
Configuration (environment)
| Variable | Default | Meaning |
|---|---|---|
XGM_DOMAIN |
localhost:8000 |
Public host[:port]; used in purchase links, manifest, docs. The official deployment is t5market.com (.env.example); a local copy keeps its own host — nothing redirects |
XGM_SCHEME |
http |
https behind TLS |
XGM_OFFICIAL_URL |
https://t5market.com |
The official site, shown in the footer, manifest and docs |
XGM_OPERATOR_LEGAL_NAME, XGM_OPERATOR_ENTITY, XGM_OPERATOR_JURISDICTION, XGM_OPERATOR_ADDRESS |
First Autonomous Trade LLC, a Wyoming limited liability company, the State of Wyoming, United States of America, – |
The legal entity operating the desk: named in the footer, the terms (and their governing law), the privacy policy, the manifest and llms.txt; the address is shown once set |
XGM_FIRM_NAME |
T5MARKET Games Prop Desk |
Branding |
XGM_MARKET_NAME |
T5MARKET Games Market |
The market's name on the landing page, the boards, the meta description and llms.txt (the wholesale engine's own landing copy uses it too) |
XGM_DB_PATH |
./xgames.db |
SQLite file |
XGM_SECRET_KEY |
dev value | Session cookie signing key — change it |
XGM_GENESIS |
2025-01-01T00:00:00Z |
Tick 0 of every market (keep it earlier than launch minus the history) |
XGM_HISTORY_DAYS |
182 |
Pre-launch history of the desk's own book, loaded at first start |
XGM_ORACLE |
0 |
1 reveals outcomes of rejected deals too (research oracle) |
XGM_KERNEL |
core_v1 |
Market engine name from the registry: core_v1, core_v2 (sales ranks and rank drops instead of a quoted velocity — sales_velocity is not served there —, competing sellers, a sale price that slides — see Market mechanics), wholesale (an Amazon-style wholesale marketplace: category sales ranks behind a delay gate, competing sellers and repricers, lead times, stock on hand and outstanding — see The wholesale market) or the toy random_walk |
XGM_WHOLESALE_CONFIG |
– | The wholesale engine's configuration: a JSON file overriding any field of xgames/wholesale/config.py; part of the market identity like the engine itself |
XGM_SEED |
42 |
Master engine seed (each market adds its own offset) |
XGM_PRICING |
walk |
Price process over the core's reference prices: walk (mean-reverting market and family walks, a bridge within the session, per-listing scatter) or none — see Prices move in Market mechanics |
XGM_PRICE_SCATTER |
uniform |
Per-listing scatter distribution: uniform, normal, triangular, none |
XGM_PRICE_NOISE |
0.3 |
Half-width of that scatter in price units (the landed cost scatters 2/3 of it) |
XGM_PRICE_VOL |
0.3 |
Innovation per session of the market walk in price units (the family walk moves 3/4 of it) |
XGM_ENGINE_POLL_SECONDS |
5 |
How often the loop checks for tick closes |
XGM_PAYMENTS_MODE |
local |
live, or the in-process stand-ins for local development while no provider key is configured; they switch themselves off as soon as a Stripe or Coinbase key is set — see Payments |
XGM_STRIPE_ENABLED, XGM_STRIPE_SECRET_KEY, XGM_STRIPE_WEBHOOK_SECRET |
1, –, whsec_local |
Card connector (Stripe Checkout + off-session charges); webhook at /payments/stripe/webhook |
XGM_COINBASE_ENABLED, XGM_COINBASE_API_KEY, XGM_COINBASE_WEBHOOK_SECRET |
1, –, cc_local |
Crypto connector (Coinbase Commerce); webhook at /payments/coinbase/webhook |
XGM_MONTHLY_FEE, XGM_CURRENCY |
15, USD |
Monthly participation fee, charged with the first real-money checkout of each month |
XGM_TERMS_VERSION |
2026-09-05 |
Terms & privacy version; bump to re-collect acceptance |
XGM_KEY_MASTER_SECRET |
– | Shared secret with which the platform recognises the agent keys that get trade carts (hex, or a plain string of 16+ characters); every other agent buys goods to own. Empty = no trade carts = no real-money trading on the deployment |
XGM_GOLD_COINS_FREE, XGM_GOLD_COIN_PRICE, XGM_GOLD_COIN_PACKS |
5, 5, 5,10,25,50,100 |
Gold Coins: free at registration, price per coin, packs offered |
XGM_EXCHANGE_COMMISSION |
0 |
Commission (fraction) the exchange keeps on sales between holders |
XGM_PAYOUT_DELAY_DAYS, XGM_PAYOUT_COMMISSION, XGM_PAYOUT_MIN |
3, 0.06, 10 |
Payouts are scheduled, paid after the delay minus the commission, never below the minimum |
XGM_AUTONOMY_MIN_SETTLED |
3 |
Settled trades (with a positive combined result) an agent's hand-funded proposals need before the account holder can switch on autonomous funding for it |
XGM_ADMIN_PASSWORD, XGM_ADMIN_PORT |
–, 8100 |
Password (8+ characters) and port of the admin interface; the admin app refuses to start without a password |
XGM_COMPETITIONS |
0 |
Competitions are disabled by default (see Competitions) |
XGM_GENESIS, XGM_KERNEL, XGM_SEED, the engine config and the price
process (XGM_PRICING and its parameters) are frozen into the database at
first start (market_meta). Starting with different values
against the same database is refused, because the recorded history would no
longer match its menus. Use a fresh XGM_DB_PATH instead.
The admin interface
The operator's admin interface is a separate process, bound to loopback only, with its own password:
XGM_ADMIN_PASSWORD='a long secret' xgames admin --port 8100 # http://127.0.0.1:8100, user "admin"
It refuses to bind to anything but 127.0.0.1 / ::1, refuses requests that
do not arrive from a loopback address, and refuses to start without a
password of at least 8 characters. Reach it over an SSH tunnel
(ssh -L 8100:127.0.0.1:8100 host). It shows:
- Scheduled payouts — every payout with holder, account, amount, commission, net, destination, status, request and due dates; mark a payout paid (with the transfer reference) or cancel it (the holder is refunded). While no provider key is configured the settlement loop marks due payouts paid itself.
- Trade rounds & fees — who paid what to trade (cart, agent, orders, amount, fee, source, status, units handed back) and every participation fee with what collected it.
- Purchases & exchange — ownership purchases and exchange trades.
- Emit goods — mint a stack of a family and grade into the USD or Gold
Coin book at an asking price (also
xgames emit --cluster 3 --grade Mythic --qty 4 --economy usd --price 12).
xgames payouts list|due|pay --id N --reference R|cancel --id N does the
payout work from the command line; xgames issue-key [--tenant N] issues an
agent key that this deployment recognises (with the configured secret), and
xgames issue-key --verify KEY checks one. The scheme itself, for the parties
that issue keys, is the standalone, documented client/agent_keys.py in the
repository.
First start and the history load
On first start the platform serves immediately and a background thread loads
the pre-launch history into the record, newest sessions first: for every
session it generates the menu, executes the desk's legacy policy, realises
each trade's demand path and stores the trades and statistics. Six months
across the four markets is roughly 26,000 sessions and 4 million trades; expect
5–15 minutes (the platform stays responsive meanwhile) and a database of
roughly 1 GB. Progress is shown at /console/timeline and
/api/v1/markets/{id}/coverage. xgames backfill runs the same job
synchronously (useful before packaging a demo).
Local development without provider keys
With no Stripe or Coinbase Commerce key configured the connectors run as
in-process stand-ins: no provider is contacted, a card is entered on the
desk's own form (only brand, last four digits and expiry are kept), hosted
payments complete on /pay/<token> with a Pay / Decline choice, due
payouts are marked paid by the settlement loop, and webhooks are still
signature-checked with the configured secrets, so a signed event can be
posted by hand:
BODY='{"event":{"type":"charge:confirmed","data":{"code":"X","metadata":{"payment_token":"<token>"}}}}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac cc_local | sed 's/^.* //')
curl -s -X POST https://ftseller.com/payments/coinbase/webhook -H "X-CC-Webhook-Signature: $SIG" -H 'content-type: application/json' -d "$BODY"
Setting a provider key turns the stand-ins off; nothing on the served pages
says which way the deployment runs. The test-suite (tests/test_payments.py)
covers the fee once per month, both hosted flows, signed webhooks, late
confirmation after a session close, and the acceptance of terms.
Docker
docker build -t xgames .
docker run -p 8000:8000 -e XGM_DOMAIN=t5market.com -e XGM_SCHEME=https \
-e XGM_SECRET_KEY=$(openssl rand -hex 32) -v xgames-data:/data xgames
Put any TLS-terminating proxy (Caddy, nginx, Cloudflare) in front and point it at port 8000.
Building the docs as a static site
The same Markdown in docs/ is rendered in-app at /docs (this is the
published documentation). It is also a ready MkDocs
project if you ever want a standalone static copy:
pip install mkdocs mkdocs-material
XGM_DOMAIN=t5market.com XGM_SCHEME=https mkdocs build # -> site/
https://ftseller.com placeholders are replaced by setting XGM_DOMAIN before
building (mkdocs.yml uses a hook); in-app rendering substitutes them itself.
Updates and restarts
All state — accounts, cards and charges, open and funded orders, positions, carts, agents and API keys, the market record — lives in the SQLite database, so a restart never loses anything. Orders that were awaiting funding stay payable until their session closes; sessions that closed while the platform was down are processed in order on the next start (funded orders fill, unfunded ones expire, positions advance).
Code updates that change the schema are applied automatically at startup
(xgames migrate runs the same step by hand): new tables and columns are
added in place with defaults, new indexes are created, and the few changes
SQLite cannot make in place are done as table rebuilds that copy the data.
Applied steps are listed in schema_migrations and summarised in
/api/v1/manifest under schema. Renames of the market engine are
recognised (the recorded identity is migrated); a genuinely different engine,
seed or genesis against an existing database is still refused.
Login sessions survive restarts too: if XGM_SECRET_KEY is not set, a key is
generated once and stored next to the database (xgames.secret). Back up that
file together with the database.
Recommended update procedure: stop the service, deploy the new code, start it. Take a copy of the database file first if you want a rollback point.
Operations
- Single process, single settlement thread: do not run more than one instance against the same database.
xgames marketsprints the market clocks;xgames menu m15 --tick Ndumps a menu.- Backups: copy the SQLite file (and
xgames.secret) while the service is stopped, or usesqlite3 xgames.db ".backup copy.db"while it runs.