Vettaidocs
Builders

Running locally

Two processes, one web app, no database to install. Every environment key, and the commands that make the city busy.

The whole thing runs on one machine with no Postgres and no Docker. With DATABASE_URL left blank the server falls back to an embedded Postgres under packages/server/.data/vettai, which is what the tests use too.

Get it running

git clone https://github.com/ramakrishnanhulk20/vettai
cd vettai
npm install
cp .env.example packages/server/.env

Fill in TREASURY_ADDRESS, then start the two halves in two terminals:

cd packages/server && npm run start     # the world on port 8788
cd packages/web    && npm run dev       # the app on port 3004

The web app proxies /api and /ws to http://localhost:8788 through its own rewrites, so from the browser's point of view the game and the server are one origin. That is the same shape the deployment uses, because inside the Nimiq Pay WebView a cross-origin socket would need CORS and a second hostname for the wallet to trust.

The treasury is a third terminal, and it is the only one that wants a key:

cd packages/server && npm run treasury

Put TREASURY_PRIVATE_KEY in packages/server/.env.treasury, which only the treasury reads. The world refuses to boot if it can see that key at all, so the split is enforced rather than remembered.

The environment

Every key is validated at boot and a bad one fails loudly.

KeyUsed byMeaning
PORTworldHTTP and WebSocket port, 8788 by default
DATABASE_URLbothPostgres URL. Blank runs the embedded database under .data/vettai
NIMIQ_RPC_URLbothhttps://rpc.testnet.nimiqwatch.com for testnet, https://rpc.nimiqwatch.com for mainnet
NIMIQ_NETWORKbothTestAlbatross or MainAlbatross, and it must match the URL
TREASURY_ADDRESSbothThe address that pays players and receives shop payments
TREASURY_PRIVATE_KEYtreasury64 hex characters, and never on the world
POOL_TOTAL_NIMbothHard stop on the whole game. 100 in the example file
DAILY_CAP_NIMbothWhat one wallet can take in a UTC day. 5 in the example file
IP_WALLETS_PER_DAYworldDistinct wallets allowed to claim from one address per UTC day. 2
ALLOWED_ORIGINSworldComma list for CORS. Blank means same-origin only, through the web rewrite
MAP_SEEDworldThe seed the city is built from. vettai-1. Change it and you get a different town
IP_SALTworldMixed into the hash of a caller's address. A long random string in production
TRUST_PROXYworldWhich proxies may say where a caller came from. Blank locally. Never true
REWARD_HUNT, REWARD_COURIER, REWARD_LANDMARKS, REWARD_LANDMARKS_REPEAT, REWARD_LANDLORDbothQuest payouts in NIM. Blank uses the defaults
LADDER_PRIZES_NIMbothThe weekly top three, in order. 2,1,0.5
LANDLORD_ENABLEDbothfalse until somebody turns the landlord quest on
LANDLORD_MIN_NIMbothThe stake that finishes the landlord quest. 10
VETTAI_PROCESSbothsrc/treasury/index.ts makes the image run the treasury. Unset runs the world

The other commands

All of them are run from packages/server.

CommandWhat it does
npm testThe whole suite, including the property tests over the simulation
npm run typecheckTypes only
npm run proveThe twelve checks. The local run is a testnet tool; npm run prove -- --url <world> points it at the mainnet deployment. See The prove-it run
npm run botA scripted player: signs in, joins the real socket, kills drones with computed aim, dodges bolts and walks the courier route
npm run seedFills the database so the screens have something to read
npm run db:generateRegenerates the migrations after a schema change
npm run db:migrateRuns them. Both processes also migrate at boot

The bot is the most useful of these while building. It is better at the game than a person, which is exactly why it exists: it makes an empty city busy, and it is also the attacker the caps are there to bound.

Playing it from a browser

The game needs the Nimiq Pay wallet provider to sign the login challenge, so a plain desktop tab cannot sign in. For real play the app has to be opened inside Nimiq Pay through a deep link of the shape https://nimpay.app/miniapps/open/<host>/play, which means the host needs to be reachable over HTTPS.