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 โ the sales velocity is not served there โ, competing sellers, a sale price that slides โ see docs/market.md), wholesale (a wholesale marketplace: category sales ranks behind a delay gate, competing sellers and repricers, lead times, stock on hand and outstanding โ see docs/wholesale.md), amazon (the same market seen from a seller's seat: the marketplace's fee card, inbound routes, the seller's own prices, customer returns, held seller balances and disbursements โ see The marketplace) 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_AMAZON_CONFIG |
โ | The amazon engine's configuration: a JSON file overriding any field of its configuration, the fee schedule included (see The marketplace); part of the market identity like the engine itself โ to change the fees of a running market schedule an amendment instead |
XGM_MARKET_INTERVALS |
โ | Session lengths in seconds per market (m15=6;m45=18;h2=48;h10=240) for local test runs only: with a fresh XGM_DB_PATH, an XGM_GENESIS close to now and a small XGM_HISTORY_DAYS |
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 docs/market.md |
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.
The continuous marketplace (XGM_KERNEL=amazon). One market, us, on a marketplace clock: XGM_TIME_SCALE
marketplace seconds per real second (default 24: a tick, one marketplace hour, every 150 real seconds; it must divide
3600), part of the market's frozen identity like the engine configuration (XGM_AMAZON_CONFIG). A fresh database
records the history from XGM_HISTORY_DAYS real days before now, which is XGM_HISTORY_DAYS ร 86,400 /
(3600 / XGM_TIME_SCALE) marketplace hours; more than 20,000 of them is refused at start unless
XGM_ALLOW_LONG_BACKFILL=1 (16 real days at the default scale = 9,216 hours = 384 marketplace days). The start
logs the open tick and its marketplace date. xgames amazon report [--week W --sessions N] (N marketplace weeks from
week W) and xgames amazon calibrate [--every n] read the continuous engine when XGM_KERNEL=amazon (--engine
weekly for the weekly-session one). Fee amendments (below) take effect from the first marketplace hour opening at or
after their time.
Fee amendments. The fee card of a running market changes only forward: xgames amazon fees amend '<json>'|@file
--effective 2026-12-01T00:00:00Z [--note โฆ] [--markets us] [--dry-run] schedules a partial fee schedule from the
first marketplace hour opening at or after that time, on every market (the same version number everywhere); it needs at least
30 days' notice (--min-notice-days; --force skips the notice, and is refused once anybody holds an account).
xgames amazon fees list lists the amendments, xgames amazon fees show [--market us] [--tick N | --version V] the
schedule in force, xgames amazon fees cancel [--version V] withdraws the latest pending one. The amendment is
published at once in every market's rules.pending_amendments; the admin interface lists the schedule in force per
market and every amendment (/fees, read-only). xgames amazon report and xgames amazon calibrate print what a
configuration produces and how its public series compare with a real marketplace's.
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://v2.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://v2.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.