Soft device-level gate — the PIN is stored in this browser only. Real cross-device roles need a backend (on the roadmap). Don't rely on it as security.
Every game draws on the same wallet, so one leaking game drains all of them. Each load first runs the circuit breaker (economy_autopause): a game whose realised return runs above its own expected return beyond noise, or whose net payout exceeds a share of the coin supply, is paused — its money RPCs refuse spins and the feed hides it — then reads admin_economy. Pause / Resume here are the operator's switch (admin_pause_game).
Test before you publish: add a game (it starts as a Draft), hit ▶ Test to play it right here,
then flip it Live. Adding a game = drop its single .html into ads/ + a row here.
Payouts auto-balance — you only set cost & length. Changes save to this browser; Export → games.json makes them live for everyone.
Brand
Economy — cost & length are the per-game dials; payout auto-balances
Every regular game is auto-tuned so a spin nets about 22 coins — none out-earns the others. Locked worlds: any game with Access = Unlock takes no stake until the player pays its price in coins once (unlock_game); the shop is only the faster way to the coins. The server dials (daily cap, pity, packs) are pushed with npm run db:economy after Export.
Games
Backup & publish
Export → paste into ads/games.json and redeploy to make changes live for all players.
Balance & economy
Players — all
Engagement by game
Submissions — all developers
Games uploaded through dev.html. Publish seeds game_economy with the given ret and puts the game in everyone's feed in a sandboxed iframe; Reject sends a note back to the developer; Pause hides a live game without deleting it. Play any row in the feed via index.html?dev=<id>.
Experiments — A/B variants
Declared in games.json under experiments. The feed hashes each player's id into a variant and tags every event and game_stats row with it; admin_experiments compares the variants. Force variant on this device writes pintok_ab to this origin's localStorage — the same thing __pintok.ab(exp, v) does from the feed's console — and the feed must be reloaded to pick it up.
Shop — coin packs and purchases
A tap on a pack in the feed creates a pending intent and opens checkout. Coins land only when the payment webhook (functions/api/webhook/<provider>, service role) or Grant here confirms it — the browser never credits itself. Grant is for payments you verified by hand (a Ko-fi mail, a bank transfer, a refund reversed); Void closes an intent that will never be paid. Packs and dials come from games.json → economy.shop via npm run db:economy.
Developers — retention earnings
A third-party game earns by keeping players. score = dwell minutes + 10 × returning players, payout = score × rate. The rate is admin_config.dev_rate in the database — shown read-only here; change it with SQL.
Saves to this browser (localStorage). Pintok picks it up on next open.