Sportsbook current limitations
What the current Openfront Sportsbook source does not do, including the closed customer API outside sandbox mode, sandbox-only providers, and lists with no writer.
This page describes source boundaries. It is not legal advice or a complete checklist for operating a sportsbook in any jurisdiction.
No regulatory status is supplied
The repository does not license an operator, certify adapters, approve a jurisdiction policy, or authorize real-money wagering. Onboarding records the legal entity and operator jurisdiction as pending with placeholder references, the jurisdiction policy as a draft, and the LICENSING and CERTIFICATION stages as BLOCKED. Licence references, certification status, and authority citations are operator-entered records.
The customer API is closed outside sandbox mode
- Every customer route under
/api/sandbox/sportsbook/*callsassertSandboxRuntime. It returnsSANDBOX_DISABLEDunlessSPORTSBOOK_SANDBOX=true. - In production, sandbox mode also requires
SPORTSBOOK_DEPLOYMENT_MODE=sandbox, anhttps://*.localSANDBOX_PUBLIC_ORIGIN, and a database URL whose name containsephemeral,sandbox,validation, ortest. - So a normal production deployment has no working sign-up, bet slip, wallet, or bet history. The storefront renders published sports and events but shows "Betting closed".
- There is no non-sandbox customer API in the source.
Providers are sandbox only
- The only adapter implementation is
SandboxAdapter. Payments refuse any adapter other thansandbox-payments. Verification, location, AML, prices, and results also use sandbox adapters. - Sandbox adapter results live in process memory, so a lookup after a restart reports
PENDING. - There is no real odds feed, results feed, identity or geolocation provider, payment processor, tax engine, or reporting channel.
Missing or partial workflows
- Lists with no writer:
Team,AccessReview,AffiliateAttribution,DataCorrectionCase,LegalHold,ResettlementCase, andWagerStatusEventexist in the schema, but no mutation or worker writes them. Corrections use newSettlementversions instead ofResettlementCase. - Regions: competitions show a region, but
createCatalogCompetitiontakes no region, so new competitions cannot be assigned one from the dashboard. - Market grading:
gradeMarketalways refuses; grading is per selection withgradeMarketSelection. - Read-only pages: account closures and notifications have list pages but no operator actions.
- Object storage: no list uses file or image storage, so the shared
S3_*variables are not read. - MCP: only generic schema tools exist; there are no sportsbook-specific agent tools.
- Starter tables:
RetainedStarterTodo,RetainedStarterTodoImage, andRetainedStarterTodoImageLinkremain in the Prisma schema from the starter template. They are not Keystone lists, are not exposed through GraphQL or the dashboard, and are unused.
Operator access
- Every custom operator action requires a verified two-factor session. Operators without MFA can sign in but cannot act.
- The first admin is created without
canApproveSensitiveActions. Dual-control actions need a second operator, created throughbootstrapIndependentApprover. - Approval requests expire after 24 hours. The code marks this lifetime as an owner decision.
- The operator sign-up page is closed unless
PUBLIC_SIGNUPS_ALLOWED=true.
Tenancy and scale
The schema and GraphQL requests are tenant-scoped, and the migrations add row-level security policies. /api/ready only reports row-level security as approved when the database role does not bypass it. The storefront resolves a single operator brand per host, so one storefront host serves one tenant. Settlement, delivery, recovery, and anchoring depend on the separate worker process; if it is not running, those queues do not drain.
Deployment responsibilities
Before any real deployment, the operator must supply licences, counsel review, certified provider adapters, real customer API exposure, backups, monitoring, secret management, audit anchoring, a non-bypass database role, and incident procedures. Passing builds and rendering pages show only that the exercised code runs.