Bankroll40 product spec
Version: 0.7 · 2026-10-02 · revised in place; history on the versions page
An easy bankroll manager for poker players, live and online, that keeps good money management and tilt discipline running on autopilot. Logging a session takes seconds. The professional standard of 40 buy-ins is the default every pot starts from (ADR-0003); the app suggests the stake the roll can take, pauses the rebuy at the stop-loss, holds the next session through the cool-off, and records every override with its reason, so the player sees what it cost (ADR-0006). It tracks every cash session and tournament in any currency. Claude reads and writes all of it over MCP. One Expo codebase ships to iPhone, Android and the web; the API and the MCP endpoint run on Cloudflare. It is a standalone product, not a Neutron feature.
Four decisions this spec rests on
standalone product
Own repo, own domain, own MCP endpoint. Players install an app, never a Neutron instance. Neutron is a consumer of the MCP surface, not the host of it.
Expo, one codebase
React Native through Expo, with Expo Router serving the web build too. Phase 1 is reached from the phone's browser; phases 3 ships the same code to the App Store and Play Store through EAS. Native notifications and background timers are what the discipline engine runs on.
MCP-native
Every action the screens can do is a tool on /mcp. claude.ai connects with OAuth 2.1 and dynamic client registration; Claude Code and Neutron use scoped bearer tokens. A voice memo to Claude at the table becomes a logged session.
pots, not one balance
A bankroll is a set of pots, each in one currency, each under its own rule set. Vegas in USD, Macau in HKD, tournaments in their own pot. Every amount is stored in minor units with its ISO 4217 code, and the FX rate is frozen at the session date.
Who it is for and where it starts
The target is the player who treats poker as income, or wants to: cash and tournament, live or online. What separates a professional from a good recreational player is not the cards, it is money management and tilt control, and those are the two things a spreadsheet cannot enforce. The app holds everyone to one standard, the professional one: 40 buy-ins. A smaller roll only buys a faster climb at a higher risk of ruin, and a softer mode would be the setting people pick on a bad night. The beachhead is the circle you sit with in Manila, because they can be onboarded in person and their feedback arrives at the table. The stores open it to everyone else.
| Segment | What costs them money today | What the app does about it |
|---|---|---|
| Cash regular | Moves up on a heater, stays up through the downswing | Stake eligibility is computed from the pot; the move-down is automatic the moment the floor is breached |
| Tournament player | Fires re-entries past any budget, judges a month by one score | Monthly tournament cap and a per-event re-entry cap enforced per pot; multi-flight and multi-day events stay open until finished; ROI shown only with the sample size beside it |
| Player who tilts | Rebuys after a bad beat, plays twelve hours, chases late at night | Stop-loss lock, cool-off, session cap, chasing flags, pre-session check-in |
| Travelling player | Mixes USD, HKD, GBP and PHP in one head | Pots per currency, FX frozen at the session date, one home-currency total |
| Online grinder | Sessions across sites and currencies, judged in hands not hours, no record of the late-night rebuys | Online sessions logged by hand from phase 1 with site, hands and tables; bb/100 per site and stake; the same locks and cool-offs; hand-history import later |
| Player who uses Claude | Logs nothing, remembers wrong | Logs by voice, asks "how am I running at 2/5 this quarter", gets a post-session review |
What exists today
Every serious tracker already converts currencies. The gap is enforcement of a bankroll plan and an agent-readable surface.
| App | Multi-currency | Notable | Price | What this spec adds |
|---|---|---|---|---|
| Poler | Any currency, converted views, multiple bankrolls | Staking deals with per-backer breakdown, CSV import and export | Pro 2.99 USD per month or 19.99 USD per year | Rules engine, MCP, offline session timer |
| High Run Tracker | 16 built-in currencies plus custom, auto-converted to home | New entrant, iPhone only | Not checked | Android and web reach, rules, MCP |
| PROker | Real-time rate conversion to preferred currency | New entrant | Not checked | Frozen historical rates, pots per currency, MCP |
| Poker Analytics | Yes | Long-standing, reads RunGood exports, deep stats | Not checked | Simpler logging, rules, MCP |
| Pokerbase | Live exchange rates | Staking marketplace | Not checked | Rules, MCP; staking is phase 4 here |
None of the five advertise a rules engine that says hold, drop or move up, and none expose an API an agent can drive. Both are cheap to build and hard for an incumbent to bolt on.
Architecture
One Cloudflare Worker serves three doors: the Expo app over JSON, Claude over MCP with OAuth, and Neutron or scripts over MCP with a token. Nothing runs on the home lab.
| Component | Choice | Why this and not the alternative |
|---|---|---|
| App | Expo (React Native), Expo Router, TypeScript | One codebase for iOS, Android and web. Native notifications, haptics, biometrics and background timers come as Expo modules; the web target covers phase 1 without a second UI |
| Local store | expo-sqlite with Drizzle, same schema shape as the server, plus an outbox of pending writes |
Card rooms have bad signal. The live session, its buy-ins and the rule checks run locally and replay when online; full two-way sync is not needed in phase 1 |
| API | Hono on Cloudflare Workers | Workers run workerd, not Bun, so the Bun house rule does not apply here. Hono is what Neutron's API already uses, so the shape is familiar |
| Database | Cloudflare D1 (SQLite) with Drizzle for schema and migrations | Integer minor units and ISO codes need nothing Postgres-specific. Hyperdrive to a Postgres is the escape hatch if the app outgrows D1 |
| Identity | better-auth on D1 with Sign in with Apple, Google and passkeys | Runs inside the Worker, no external IdP to keep alive; Apple requires its own sign-in wherever Google's is offered |
| MCP server | McpAgent from Cloudflare's agents package on a Durable Object, Streamable HTTP on /mcp |
Cloudflare's first-party remote MCP runtime; claude.ai, Claude Desktop, Claude mobile and Claude Code all speak it |
| MCP auth | @cloudflare/workers-oauth-provider: OAuth 2.1 with PKCE, dynamic client registration and discovery, consent screen backed by the app's own login |
The exact flow claude.ai custom connectors expect, maintained by Cloudflare. Personal bearer tokens with scopes cover Claude Code and Neutron |
| FX | Workers Cron Trigger fetching Frankfurter, a free open-source API for the European Central Bank reference rates, daily into fx_rate; manual override per session |
Free, no key, covers PHP, HKD, USD, GBP. Not MOP, so Macau players pick HKD or type a rate |
| Media storage | Cloudflare R2, one bucket, keys prefixed by user id; aws4fetch in the Worker signs short-lived upload and read URLs |
S3-compatible, no egress fees, 10 GB free. Files never pass through the Worker, so a 50 MB clip costs no CPU time |
| Media on the phone | expo-image-manipulator resizes to 2,000 px and strips EXIF before upload; expo-camera for capture; expo-av for voice memos; clips capped at 60 s, voice memos at 5 min |
Receipts need no more than that, uploads stay small on card-room signal, and location never leaves the phone |
| Receipt reading | Workers AI vision model reads amount and time off a receipt into media.extracted; the player confirms or edits |
Optional, phase 2. Free allocation covers thousands of receipts a day; the player's confirmation stays the source of truth |
| Voice memos | Recorded on the phone, stored in R2 like any media, transcribed by Workers AI Whisper into media.extracted |
A note spoken in the car park is the honest one. The transcript makes it searchable and lets Claude read it in a post-session review |
| Push | Expo push service through expo-notifications |
One API for APNs and FCM; cool-off countdowns, session-cap nudges and the weekly report go out as local or push notifications |
| Builds and stores | EAS Build and EAS Submit, or local builds on the MacBook with Xcode | Signing, provisioning and store upload handled; the free tier's monthly builds are enough for one app |
The media pipeline
Data model
Money lives in pots. A pot has one currency and one rule set; sessions, tournaments and transactions all point at a pot. Rates are frozen per row at the time of play, so history never moves when the ECB does.
A tournament is an event, not a row with an entry count. Every bullet fired is its own entry with a flight, a cost and an outcome; every day at the table is its own day row with hours; the event stays open until the last day is played. A real case from Metro Poker Manila:
| What happened | Rows written |
|---|---|
| Fifteen bullets across several Day 1 flights | Fifteen tournament_entry rows, each with its flight, kind, cost and timestamp; thirteen with outcome busted |
| One flight cashed | That entry's outcome is bag paid out with the amount in payout_minor; it counts as cash on the event from that moment |
| One flight bagged for Day 2 | That entry's outcome is bagged; the event status stays open and Home shows it as money in play |
| Day 2 on Sunday | A tournament_day row with the hours, then the event is closed with finish, field size and final cash |
ROI is per event, total cash over total cost across every bullet, so re-entries are never hidden. Open events are excluded from closed ROI and shown as in play, so a pending Day 2 cannot flatter or punish last week. Each bullet leaves the pot when fired, a paid-out bag returns when paid, the final result lands when known. The same rows cover a smaller bag forfeited when you bag twice (forfeited), add-ons and rebuys (kind), a final-table deal (deal on the event), and a satellite seat (an entry with cost zero).
Rules that the schema enforces or the service layer must:
- Minor units only.
amount_minoris a 64-bit integer in the row's own currency. No floats, no decimals in storage; formatting happens at the edge using the currency's exponent (PHP and GBP 2, JPY 0, KWD 3). - Live and online share one session row. A live session has a venue; an online one has a site, a hand count and a table count, and its venue is empty. Win rate is bb/h for live and bb/100 hands for online, never mixed in one figure.
- Currency is on every money row. A session's currency defaults to the pot's and may differ, for example a HKD game paid from a USD pot. The conversion to the pot's currency and to the home currency are both frozen on the row.
- Transfers are two rows. Moving money between pots writes a withdrawal on one pot and a deposit on the other in one transaction, with
counter_pot_idlinking them; the FX applied is stored, so a transfer can show its spread. - Result is derived, never stored. Cash result equals cash out minus the sum of buy-ins minus tips. Tournament result equals cash minus entries times buy-in plus fee. Views compute these so a corrected buy-in cannot leave a stale total.
- Rule verdicts are computed from the pot. Buy-ins on hand equals pot balance divided by the stake's 100 bb buy-in in the pot's currency. The verdict is hold, drop or eligible to move up, with the reasons attached, the same logic the MCP tool returns.
- The journal is a view, not a table. Sessions, events, notes, voice memos, photos, rule breaks and milestones (moved up, 100 hours at a stake, a clean month) already carry timestamps; the journal reads them in order. A standalone entry with no session is a
mediaor note row whose target is the profile. - Media is a row that points at anything. A
mediarow names its target by kind and id: a buy-in, a cash-out, a tournament day, a transaction or the profile. The object lives in R2 under the user's prefix; deleting the row deletes the object. What the receipt reader extracted sits beside it as JSON until the player confirms it. - Every write carries an actor. An audit row records whether the app, a personal token or an OAuth client made the change, and which client.
Multi-currency, precisely
home currency
One per user, changeable. Totals across pots are shown in it. Changing it re-renders totals using each row's frozen rate chain; it never rewrites rows.
frozen rate
Every session and tournament stores fx_to_home at its date. Reports sum frozen values, so a month's result does not drift when the peso moves.
rate source
ECB reference rates by day from Frankfurter, cached in fx_rate. A missing pair or a currency ECB does not publish falls back to a manual rate the user types once and the app remembers per pair.
display
Native currency first, home currency as a secondary line. Symbols follow the currency, not the locale, so a HKD session shows HK$ in Las Vegas.
stakes in bb
Performance is measured in big blinds per hour within a stake and currency. Cross-currency comparisons use bb/h, never converted money, because rake and tips distort converted hourly rates.
casino chips and cages
Some rooms deal in a currency other than their country's (Macau tables in HKD, Manila high-stakes in USD). The session's currency is the table's; the venue only supplies a default.
The discipline engine
Every rule has a trigger the app can observe and an action the app takes without being asked. The rules are the same live and online; online, the lock is on logging the next buy-in and on starting the next session, and the cool-off notification lands on the phone in the player's pocket. Thresholds live in the pot's rule set and are editable, but never during a live session: rules are edited cold and applied hot. There is one standard, 40 buy-ins, with three to six months of living costs held outside the roll. The thresholds can be edited, cold, and every edit below the standard is recorded as a rule change so the weekly report can show what it cost.
| Rule | Trigger | What happens automatically |
|---|---|---|
| Stake eligibility | The current stake, and one arrow: red down when the roll is under 30 buy-ins at it, green up when the next stake has 50 buy-ins and 100 logged hours, nothing when it is fine | One tap moves the stake down or up, or keeps it; the thresholds stay in the rule set and never appear as jargon on screen. Staying under a red arrow writes a note in the record |
| Stop-loss lock | Buy-ins lost in the live session reach the limit (default 3) | The add-buy-in button locks. Continuing needs a typed reason, opens a flagged override segment shown in red, and counts as a rule break |
| Cool-off | Stop-loss reached, or an override used | The next session cannot start for a set period (default 12 h). Home shows the countdown. An early start is an override, logged |
| Session cap | Hours in the live session pass the cap (default 8 h) | A nudge at the cap and every hour after. Fatigue is tilt's twin and gets the same treatment |
| Loss streak | Three losing sessions in a row, or a day down more than a set number of buy-ins | Suggests a day off, proposes the lower stake for the next session, and shows the player's own numbers from earlier streaks |
| Tournament budget | Monthly tournament spend against the cap (default 5 percent of the cash pot) | Bullets past the cap need an override. The Pots screen shows spend against cap all month |
| Next stake | Always on, on Home | One line under the stake card: the next stake, the pot it opens at, the amount still to go and the hours logged. When both are there the arrow turns green and the line becomes the one-tap move up |
| Max buy-in today | Opening the Tournaments screen | The largest tournament buy-in the roll supports today: the smaller of the tournament pot divided by 100 and the monthly budget left spread over the bullets the player plans. One number with its reason |
| Re-entry cap | Bullets fired into one event pass the cap (default 2 re-entries) | The next entry needs a typed reason and counts as a break. Fourteen re-entries across the flights of one event is the chasing case this rule exists for |
| Chasing flag | A buy-in within 20 minutes of a buy-in, or a buy-in after midnight local time | The buy-in is tagged "chasing?" and asks for confirmation. Friction, not a block |
| Pre-session check-in | Starting a session | A ten-second check: slept, sober, mood 1 to 5, planned hours, planned stop. Low answers propose a lower stake or a skip. Check-ins are kept so they can be compared with results later |
| Weekly report | Sunday | Rule breaks against results: hours, bb/h, and what the sessions with breaks cost compared with clean ones |
Tilt is measured only through things the app can observe: breaks, overrides, chasing flags, check-in answers, session length and time of day. There is no invented tilt score. The weekly report puts those observations next to money, which is the argument that changes behaviour.
Sign-up: the questionnaire that sets the roll
Nobody should have to know the 40 buy-in rule to benefit from it. Sign-up asks eight questions, about two minutes, and turns them into a setup the player accepts in one tap: where they play, usual game and stake, poker money today in each currency, monthly take-home, monthly living costs, savings outside poker, what they can add to the roll each month, whether poker pays the bills, how losing nights affect them (1 to 5), and whether they play tournaments. The suggestion, computed by suggestSetup in the rules package, covers:
| Output | How it is derived |
|---|---|
| Reserve | Three months of living costs outside poker; six when poker pays the bills. If savings fall short, the gap comes out of the poker money before any stake is suggested |
| Safe roll | Poker money across currencies converted at today's rate to the currency of the usual stake, minus whatever had to move to the reserve |
| Starting stake | The usual stake if the safe roll clears the floor of 30 buy-ins there; otherwise the next stake down that does |
| Rules | The standard set; a tilt answer of 4 or 5 tightens the stop-loss to 2 buy-ins and the cool-off to 24 hours |
| Tournament budget | Five percent of the safe roll a month, and the max buy-in it allows for the bullets planned |
| Next stop | The pot size and hours that open the next stake, and how many months of top-ups reach it before any winnings |
A worked example: a 20,000 USD roll, 2/5 as the usual game, poker not the income, living costs of 3,500 a month and 15,000 in savings. The reserve is three months, 10,500, and the savings cover it, so the whole 20,000 is the safe roll. That is 40 buy-ins at 2/5, right at the standard, so 2/5 is confirmed and 5/10 opens at 50,000. Adding 1,000 a month reaches it in 30 months before any winnings. A roll held in another currency is converted at today's rate first.
Features by phase
| Phase | Feature | What it does |
|---|---|---|
| P1 | Live session | Start, add buy-ins, notes, end. Timer and buy-ins work offline and replay when the signal returns |
| P1 | Discipline engine | Stake eligibility, promotion bar, max buy-in today, stop-loss pause, cool-off, session cap, loss streak, tournament budget, chasing flag, pre-session check-in, weekly report |
| P1 | Sign-up questionnaire | Eight questions about play and money; suggested reserve, safe roll, stake, rules, tournament budget and next stop, accepted in one tap |
| P1 | Past session and tournament logging | Venue, game, stake, currency, amounts, hours, tips; tournaments as events with bullets per flight, days played and an open state until the last day |
| P1 | Online sessions | Site instead of venue, hands and tables, bb/100; same rules, same pots, same currencies |
| P1 | Pots and transactions | Deposit, withdraw, transfer with stored FX; home-currency total |
| P1 | Rules engine | Floor, standard, move-up and stop-loss thresholds per pot; hold, drop or move-up verdict with reasons; stop-loss warning during a live session |
| P1 | Stats | Hours, bb/h, hourly, standard deviation, sessions won, by stake, venue and month; bankroll line over time |
| P1 | Sign in and export | Apple, Google, passkey through better-auth; CSV export of everything |
| P1 | Profile, media and journal | Avatar, cover, handle, accent colour, headline numbers, badges, gallery; receipts, stack photos, 60 s clips and 5 min voice memos attached to buy-ins, cash-outs, tournament days, transactions or the journey itself; a journal timeline under Me; private by default, shared per item |
| P2 | Transcription | Voice memos transcribed on Workers AI, searchable, read by Claude in the post-session review |
| P2 | Receipt reading | Amount and time read off a receipt photo, confirmed by the player, checked against the logged buy-in |
| P2 | MCP tools | The twelve tools below, two resources, one prompt; OAuth 2.1 with DCR for claude.ai, personal tokens for Claude Code |
| P2 | Post-session review prompt | Claude reads the session and the rules and asks about the big hands, not the result |
| P3 | Store release | EAS builds for iOS and Android, biometric lock, share card of a session, store listings, TestFlight and Play internal tracks |
| P4 | Groups | Share a venue list and game schedule with the people you play with; leaderboard opt-in |
| P4 | Staking | Sell action on a session or tournament with per-backer splits, the Poler feature players ask for |
| P4 | Import | CSV from Poler and Poker Analytics so switching costs nothing |
| P4 | Hand-history import | A desktop companion watches the hand-history folders of PokerStars and other exporting sites and turns them into sessions automatically; online tournaments the same way |
Design tokens
Two themes, re-picked rather than inverted, every text-and-ground pair computed at 4.5:1 or better. Light is white ground with navy ink and accent, as the owner set it. Dark is navy-black with a pale blue accent. Three semantic families come from the product's own distinctions: ok (clean session, bag paid), warn (near stop-loss, under floor, event open), fail (stop-loss hit, cap passed, budget spent). The values live in spec/figures/tokens.json and generate the wireframes; the decision is ADR-0004.
| Token | Light | Dark | Used for |
|---|---|---|---|
| bg / surface / surface-2 | #ffffff / #f4f6fb / #e8edf6 | #0a0f1a / #111827 / #1a2336 | Ground, cards, inputs |
| ink / muted / line | #0b1a33 / #4a5a75 / #d3dbea | #e9eef8 / #9aa8c2 / #263149 | Text, secondary text, borders |
| accent | #143a7c on #e3ebfa | #8db4ff on #14264a | Primary actions, active tab, links |
| ok | #0f6b3c on #dff3e8 | #55d58f on #0e2a1c | Clean session, receipt matches, bag paid |
| warn | #8a4b00 on #fff0d6 | #f5b942 on #33240a | Near stop-loss, under floor, open event |
| fail | #b11f1f on #fde4e4 | #ff6f6f on #3b1518 | Stop-loss hit, cap passed, budget spent |
Wireframes
Fourteen screens cover phase 1. The bottom tab bar is Home, Log, Stats, Pots, Me; tournaments live under Log. Starting a session is the primary action on Home; ending it is the primary action on the live screen. These are wireframes, not the visual design: the design pass comes after the spec is agreed.
| Screen | Purpose | Key states |
|---|---|---|
| Home | Pot balance, the current stake with a red down or green up arrow and one-tap buttons to move or stay, the next-stake line, this month's line, recent results, Start session | No pot yet (onboarding), session already live (tap resumes), red arrow (the lower stake is one tap away, with the buy-ins that keep the current one open), green arrow (the next stake is one tap away) |
| Live session | Timer, money in play, add buy-in, note, stop-loss warning, End session | Offline (banner, writes queue), one buy-in from stop-loss (amber), stop-loss hit (red; buy-in locked, End session is the primary action, override asks for a typed reason), chasing flag on a quick rebuy |
| Log session | Live or Online toggle; venue or site, game, stake, currency with today's rate, amounts, hours or hands and tables, tips; result and bb/h or bb/100 computed as you type | Currency differs from pot (shows both conversions), rate missing (manual field), online (venue becomes site, hours become hands and tables) |
| Stats | Filter chips by stake and format; bankroll line; hours, bb/h, hourly, std dev, sessions; by venue | Under 100 h at a stake (caveat card), no data (empty state with the rules text) |
| Pots | One card per pot with currency, balance, home equivalent, rule set; transactions; total in home currency | Pot without rules (prompt to set), transfer with spread shown |
| Tournaments | Open and closed events, bullets and money in per event, bags paid and live, next day's date, monthly budget against cap, max buy-in today with its reason | Budget nearly spent (red bar), event open with a live bag (amber) |
| Event | Flights with bullets and outcome each, days played, money in and out, pending result, cap status, Fire bullet and Log day | Open (amber), closed, cap passed on this event (red card) |
| Fire a bullet | Flight, kind, cost, time; cap and budget checks | Past the re-entry cap (red card, reason required, amber confirm), past the monthly budget (same) |
| Day 2 and close | Day timer, start stack, players left; finish, field, cash, deal; event result with ROI | Day logged without closing (event stays open), closed (result lands in the pot) |
| Sign-up questions | Where you play, usual stake, poker money per currency, take-home, living costs, savings, monthly top-up, poker as income, losing nights 1 to 5, tournaments | Second currency added (another money row), poker is income (reserve becomes six months) |
| Suggested setup | Reserve, safe roll, stake, rules, tournament budget and max buy-in, next stop with months of top-ups; Adjust or Accept | Savings short of the reserve (amber card: move money out of the roll first), usual stake under the floor (lower stake suggested) |
| Profile | Avatar, cover, handle, accent colour, headline numbers, badges, gallery, journal timeline of sessions, voice memos, photos, breaks and milestones, privacy line | New account (placeholder avatar, prompt to add), shared item (badge on the thumbnail) |
| Session with media | Clip or photo hero, attachments by target, notes, receipts-match check, Share card, Ask Claude | Receipt amount differs from the logged buy-in (amber), no attachments (dashed add tile only) |
| Capture | Viewfinder or recorder, kind (receipt, stack, clip, voice), what the reader or transcriber extracted, privacy line, Gallery or Use | Reader unsure (fields empty, player types), offline (upload queued, thumbnail shows pending) |
The MCP surface
The tool list mirrors the screens one to one, so anything a player can do with a thumb Claude can do with a sentence. Writes are scoped; deletes stay in the app in phase 2.
| Tool | Scope | What it does |
|---|---|---|
session_start |
write | Opens a session: venue or site, game, stake, currency, first buy-in. Refuses if one is already live |
session_buyin |
write | Adds a buy-in to the live session. Refused past the stop-loss unless an override reason is given, which is logged as a break |
discipline_status |
read | Active locks, cool-off remaining, eligible stakes, breaks and overrides this month, last check-in |
session_end |
write | Closes it with cash out, tips, notes; returns result, bb/h and the rule verdict |
session_log |
write | One-shot past session, same fields as the form |
sessions_list |
read | Filter by pot, venue, stake, date range; paginated |
tournament_open |
write | Creates an event: venue, name, buy-in, fee, currency; returns the event id |
tournament_entry |
write | Fires a bullet into an event: flight, kind, cost, and later its outcome and payout. Refused past the re-entry cap unless an override reason is given |
tournament_day |
write | Logs a day at the table for an event: date, flight, hours, end stack |
tournament_close |
write | Closes an event with finish, field size, final cash and whether a deal was made |
tournaments_list |
read | Events by pot, venue, status; open events show bullets, cost so far and bags still live |
transaction_add |
write | Deposit, withdrawal or transfer between pots with the FX applied |
pot_status |
read | Balance, buy-ins on hand, thresholds, verdict with reasons |
stats |
read | Hours, bb/h or bb/100, hourly, std dev, sessions for a stake, venue, site or period |
rules_check |
read | Hold, drop or move up, and what would change the verdict |
venues_list, venue_create |
read, write | The venue book with default currency |
fx_quote |
read | Rate for a pair on a date, with source |
media_list |
read | Attachments for a session, event, day or transaction, as signed URLs that expire; Claude can look at a receipt when asked |
journal_list, journal_add |
read, write | The timeline in order, transcripts included; a text entry from Claude after a review, marked as written by the agent |
Resources: bankroll://plan returns the rule set as text so Claude can quote it, and bankroll://recent returns the last ten results. One prompt, post_session_review, loads the session and asks about decisions rather than outcomes.
The path to the App Store and Play Store
| Constraint | What it means here | Handled in |
|---|---|---|
| Apple guideline 4.2, minimum functionality | A native Expo app with notifications, biometrics and offline behaviour clears this; a webview shell would not | P3 |
| Apple guideline 4.8, Sign in with Apple | Required because Google sign-in is offered. better-auth supports it; needs an Apple Developer account and a Services ID | P1 setup, P3 review |
| Gambling policies, both stores | The app holds no wagers and moves no money. It is a ledger. Store listing says so; category is Finance or Utilities, not Casino | P3 listing |
| Web before the stores | The Expo web build is served from Cloudflare at the app's domain; no review needed. This is how the Manila circle gets it in phase 1, with TestFlight for the native build in phase 3 | P1 |
| Developer accounts | Apple 99 USD per year, Google 25 USD once. Apple approval can take days | Owner action, start now |
| Review cycles | TestFlight and Play internal testing first; expect one rejection round on 4.2 | P3 wall-clock |
| One build, two stores | One Expo codebase produces the iOS app, the Android app and the web app. Shared: every screen, the rules engine, the local store, notifications. Per platform: signing certificates, store listings and screenshots, the Apple Services ID for sign-in | P3 |
Running costs
Prices as published on 2026-10-01. At the scale of the first few hundred players the whole stack sits inside the fixed amounts below.
| Item | Price | What it covers | Source |
|---|---|---|---|
| Cloudflare Workers Paid | 5 USD per month | Workers, D1, Durable Objects, KV, Hyperdrive and cron on one plan: 10 million requests and 30 million CPU-ms a month included; D1 5 GB and 25 billion row reads a month included; Durable Objects 1 million requests and 400,000 GB-s a month included; metered above that, no egress charge | Workers pricing, Durable Objects pricing |
| Expo EAS Free | 0 USD | 15 iOS and 15 Android cloud builds a month, EAS Update to 1,000 monthly active users, push notifications. Local builds on the MacBook are unlimited and free | Expo plans |
| Expo EAS Starter | 19 USD per month | More build credit and priority queue, only if cloud builds become the bottleneck | Expo plans |
| Expo EAS Production | 99 USD per month | 50,000 monthly active users on EAS Update and two concurrent builds; not needed before the stores are live and the user base is in the thousands | Expo plans |
| Cloudflare R2 | 0 USD to start | 10 GB storage, 1 million writes and 10 million reads a month free, then 0.015 USD per GB-month; no egress charge. A thousand players at 200 MB each is 200 GB, about 3 USD a month | R2 pricing |
| Cloudflare Workers AI | 0 USD to start | 10,000 neurons a day free, then 0.011 USD per 1,000; receipt reading stays inside the free allocation at this scale | Workers AI pricing |
| Apple Developer Program | 99 USD per year | App Store, TestFlight, Sign in with Apple | Apple |
| Google Play Console | 25 USD once | Play Store listing and internal testing | |
| Domain | about 10 to 15 USD per year | The app's domain on Cloudflare Registrar | Cloudflare |
| Frankfurter, better-auth, Hono, Drizzle | 0 USD | Open source or free public API | — |
Fixed cost before the first paying user: 5 USD a month plus 99 USD a year plus 25 USD once. The stores take 15 to 30 percent of any subscription revenue on top.
Roadmap and effort
Effort is AI session time: hours of agent work in one sitting, most of it build, test and deploy iteration rather than typing. Wall-clock blockers are listed separately because they are the real schedule.
| Phase | Scope | AI session time | Wall-clock blockers |
|---|---|---|---|
| 0, now | This spec, owner decisions, repo and issue tracker created, name chosen | 2 h | Owner decisions below |
| 1, app and API | Schema on D1, Worker API, twelve Expo screens with the web target, discipline engine, tournaments as events, stats, local SQLite outbox, better-auth sign-in, FX cron, R2 media with signed uploads, profile, CSV export, deployed to Cloudflare at the domain | 38 to 48 h across 6 to 8 sessions: coding 14, build and test 22, verification against fixtures 10 | Apple Developer account for Sign in with Apple; domain choice |
| 2, MCP | Tools, resources, prompt, OAuth 2.1 server with DCR and discovery, personal tokens, audit, claude.ai connector verified end to end | 10 to 14 h across 2 sessions | None; verified against claude.ai directly |
| 3, stores | EAS build profiles, icons and screenshots, TestFlight and Play internal tracks, listing copy, one review round | 8 to 12 h across 2 sessions | Apple review 1 to 7 days per round; Google similar |
| 4, circle | Groups, staking, imports from Poler and Poker Analytics, whatever the first twenty users ask for | 16 to 24 h | Depends on feedback from the Manila circle |
Verification at each phase is the live product: phase 1 ends when you log a real session at Okada from your phone with the tunnel off and it syncs when you leave; phase 2 ends when claude.ai logs one for you from a sentence.
Open decisions for the owner
- Name and domain. Decided: Bankroll40, bankroll40.com, registered 2026-10-02. The number is the standard the app enforces.
- Where the repo lives. Decided:
gitlab.com/bizfoundry/bankroll40withdocs,siteandappprojects. - Price. Free for the circle through phase 3 is assumed. Poler charges 2.99 USD per month; the stores take 15 to 30 percent. A subscription needs StoreKit and Play Billing in phase 3, which is another 6 to 8 hours.
- Default thresholds. The standard (floor 30, standard 40, move up at 50; 3 buy-ins stop-loss, 12 h cool-off, 8 h cap, 5 percent tournament budget) comes from the bankroll plan and common practice. Confirm them or set your own; they ship as the template every new pot starts from.
- Which online sites first. Manual logging covers every site from phase 1. Automatic import in phase 4 needs one hand-history format at a time; PokerStars is the obvious first, and GGPoker cannot be imported at all.
Once the first two are answered the phase 1 repo can be created and the first session started.
Sources
- Poler on the App Store, multi-currency, staking, pricing
- High Run Tracker on the App Store, 16 currencies plus custom
- PROker on the App Store, real-time conversion
- Poker Analytics features and switching from RunGood
- Claude connectors, building authentication, OAuth 2.0 with dynamic client registration
- Claude support, custom connectors, SSE and Streamable HTTP
- Frankfurter, a free open-source API publishing the European Central Bank reference rates; PHP as a base currency confirmed against its API on 2026-10-01
- Poker Bankroll Plan, your Claude Doc of 2026-09-30, the rule set this spec enforces