Deploy on Cloudflare Workers
Run the TinyShip-based application with D1, Better Auth, an upstream provider, and a separate static documentation Worker.
Architecture
| Location | Responsibility |
|---|---|
apps/tanstack-app | Main website, console, playground, API routes, and scheduled cleanup. |
libs/system-one | Native transport/observational validation, exact JSON identity, keys, provider adaptation, limits and usage. |
libs/auth, libs/database | Existing Better Auth sessions and shared D1 data model. |
libs/credits, libs/payment | Existing credit balance, ledger, orders, and verified payment fulfillment. |
config | Product limits, plans, and shared capability configuration. |
apps/docs-app | This Fumadocs / Next.js static export, deployed independently. |
Only the TanStack main app and docs app are maintained in this distribution. Shared business logic stays in TinyShip’s modules and the routes stay thin. There is no custom replacement language model or production mock inference. An unconfigured provider returns 503 provider_not_configured without a new charge.
Run locally
Use Node.js 22 or later and the project’s pinned pnpm 9.4.0. From the repository root:
npx --yes pnpm@9.4.0 install --frozen-lockfile
npx --yes pnpm@9.4.0 setup:local
npx --yes pnpm@9.4.0 db:migrate:local
npx --yes pnpm@9.4.0 devThe main app runs at http://localhost:7001. setup:local creates a root .env and apps/tanstack-app/.dev.vars without overwriting existing files. It generates a local Better Auth secret. These files contain secrets and stay excluded from version control.
Add real upstream credentials to the local configuration when you are ready to call a provider. Keep root .env and Worker .dev.vars consistent when both are used. Local setup allows unverified sign-in for development; it does not grant the verified-email welcome bonus to an unverified account. Production should require email verification and a configured email delivery service.
Create and migrate production D1
Authenticate Wrangler with your Cloudflare account, then create the database:
npx --yes pnpm@9.4.0 --filter @system-one/web exec wrangler login
npx --yes pnpm@9.4.0 --filter @system-one/web exec wrangler d1 create system-oneIn apps/tanstack-app/wrangler.jsonc, replace the placeholder database_id with the returned ID. Keep the binding name DB and the migrations directory ../../libs/database/drizzle-sqlite. Apply the entire migration sequence, including the existing TinyShip tables and 0006_system_one.sql:
npx --yes pnpm@9.4.0 db:migrate:remoteMigrate the intended remote database before serving the new application. Do not replace the shared auth, order, or credit tables with a separate decision-only database.
Configure the main Worker
Set these non-secret values in the main Worker’s Wrangler vars:
| Variable | Value |
|---|---|
DB_DIALECT | d1 |
APP_BASE_URL | Exact public main application origin, such as https://app.example.com. |
BETTER_AUTH_URL | The same main application origin. |
AUTH_REQUIRE_EMAIL_VERIFICATION | true for production. |
EMAIL_PROVIDER | resend by default; cloudflare is an optional existing email integration. |
EMAIL_DEFAULT_FROM | Sender address on your configured email domain. |
SYSTEM_ONE_PROVIDER | typesafe or openrouter. |
SYSTEM_ONE_MODEL | Default used only when a request omits model; otherwise jev-latest. Set a supported alias/version. Explicit TypeSafe request models take priority. |
SYSTEM_ONE_RATE_LIMIT | 120 by default, shared per account. |
SYSTEM_ONE_SIGNUP_CREDITS | 100 by default; 0 disables welcome credits. |
SYSTEM_ONE_DOCS_URL | Public docs destination, such as https://docs.example.com/zh-CN/docs. |
Do not deploy the example origin or placeholder database ID. Session-origin checks use APP_BASE_URL; cookies and email links need the corresponding Better Auth URL. Preserve the main Worker’s nodejs_compat setting and cleanup cron */15 * * * *.
Add credentials as Worker secrets, entering values at the prompt rather than placing them in the command:
npx --yes pnpm@9.4.0 --filter @system-one/web exec wrangler secret put BETTER_AUTH_SECRET
npx --yes pnpm@9.4.0 --filter @system-one/web exec wrangler secret put SYSTEM_ONE_UPSTREAM_API_KEY
npx --yes pnpm@9.4.0 --filter @system-one/web exec wrangler secret put RESEND_API_KEYUse a new strong production auth secret, not the local development value. The email configuration shown uses TinyShip’s Resend integration. Additional enabled authentication providers need their corresponding existing TinyShip settings.
Select the upstream
| Provider | Default upstream base URL | Upstream model |
|---|---|---|
typesafe | https://api.typesafe.ai/v1 | jev-latest |
openrouter | https://openrouter.ai/api/alpha | ~typesafe/jev-latest |
SYSTEM_ONE_UPSTREAM_URL is an optional HTTPS override without credentials, query or fragment. SYSTEM_ONE_UPSTREAM_API_KEY must match the selected service. Native TypeSafe uses bounded Fetch and X-System-One-Response-Mode: typesafe-native: explicit request model/text and successful response text/status/extensions are preserved after validation. Omitted model defaults are inserted into root JSON without rounding existing numbers. Nullable state is allowed but required as a field, instructions may be omitted/null, and Noul criteria may be null; the provider decides final acceptance.
OpenRouter is an explicit adapter inside the Worker. Matching jev-* request names map to ~typesafe/jev-* and the adapter builds /decisions. Responses retain provider usage/cost and metadata with openrouter-adapted, not TypeSafe byte fidelity; unsupported top-level request extensions are rejected. Clients still point at the platform /v1 without a direct-provider adapter. Native GET /v1/models requires a platform key and returns real TypeSafe { models }; OpenRouter returns 501 model_listing_not_supported.
The Worker forwards state and questions to that provider. Review data handling before exposing a deployment to users. Upstream availability and measured performance must be verified in your own environment.
Compatibility upgrade
Contract typesafe-2026-09-18 separates Choice's 255-option maximum from Score's 10-level maximum; request size remains 65,536 bytes, questions 32 and depth 32. Client validation imports shared configuration. Duplicate JSON keys are rejected, exact numeric tokens distinguish idempotent requests, and aliases/versions are not restricted to a local latest-only list.
Migrate consumers from successful body billing/IDs to X-System-One-Credits, X-Request-Id, X-Upstream-Request-Id and X-Idempotency-Replayed. The gateway adds no default usage/warnings/rounding. Native Choice/Score distributions and confidence are required, as is Score legend. Project SDK 0.3.0 may normalize/default its own result; the official SDK 0.6.0 supplies an independent compatibility check.
Upstream JSON errors retain 4xx/5xx status and Retry-After, including 401/403 and 429/529; stable UI codes and source come from headers. Credential-bearing errors receive safe bodies, while unsafe successes, malformed or oversized responses and redirects are rejected. The shared upstream timeout is 20 seconds with no retries. Refund reconciliation failure is 503 with no charge header. Review errors before changing client recovery logic.
No schema migration is needed for replay storage. New versioned records retain text/status/allowed headers; unversioned records are read as legacy-cache, with old platform billing/request-id/replayed additions removed and their existing 24-hour expiry unchanged. Previously synthesized warnings/rounding cannot be reliably undone. Back up application data before routine deployment changes; do not clear the account or credit ledger to apply this contract.
Enable Stripe credit packs
Set STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET using wrangler secret put. Set STRIPE_PUBLIC_KEY to the corresponding publishable key in the payment configuration. Use a consistent Stripe mode for all keys and webhook endpoints.
Register the webhook URL https://YOUR_MAIN_ORIGIN/api/payment/webhook/stripe for checkout.session.completed and checkout.session.async_payment_succeeded. Use that endpoint’s signing secret. The handler validates the signed event and the server-owned credit order; browser redirects cannot grant credits.
config/plans.ts defines Starter, Builder, and Scale as one-time USD credit packs. The integration creates inline Stripe price data from those plans; it does not require invented Stripe price-ID environment variables. Test checkout and signed fulfillment in Stripe test mode before accepting live payments. This documentation does not imply that real Stripe purchases have been tested on your deployment.
Build, check, and deploy the main app
npx --yes pnpm@9.4.0 typecheck:tanstack
npx --yes pnpm@9.4.0 build:tanstack
npx --yes pnpm@9.4.0 deploy:check
npx --yes pnpm@9.4.0 deploydeploy:check builds the Cloudflare target and performs a Wrangler dry run. deploy publishes the main Worker. Local production preview is available through pnpm preview. These commands do not prove real provider credentials, email delivery, payment fulfillment, or decision quality.
The final build reported 39,422.77 KiB uncompressed and 7,350.28 KiB gzip for the main Worker on September 18, 2026. Check Wrangler’s uncompressed Total Upload against your account’s enforced limits. The official Worker size rules, checked on that date, specify 64 MiB uncompressed for both Free and Paid, with no compressed limit. The gzip value alone does not establish whether a paid plan is required; CPU and other quotas still matter.
Those figures are a historical build measurement, not a measurement of the current compatibility changes.
After deployment, verify /health, /v1/models using a platform Bearer key, registration/email verification, key creation/revocation, both client paths, editable Playground models, usage, replay headers and signed payment fulfillment. Run offline native-contract/D1 tests before rollout; they compare actual fetch arguments and same-call response text. pnpm test:contract:live is a separately invoked, billable check, not part of building these docs. It compares one call's actual upstream text to the gateway response, not numerical equality between independent model evaluations. A real inference consumes provider usage and platform credits.
Deploy the documentation separately
The docs app exports all English and Chinese pages and a static search index to apps/docs-app/out. It uses system fonts and needs no runtime database or secrets. Set the public build-time SYSTEM_ONE_DOCS_URL to your documentation origin so canonical URLs and social metadata use the correct domain.
npx --yes pnpm@9.4.0 --filter @system-one/docs typecheck
npx --yes pnpm@9.4.0 build:docs
npx --yes pnpm@9.4.0 --filter @system-one/web exec wrangler deploy --config ../docs-app/wrangler.jsonc --dry-run
npx --yes pnpm@9.4.0 --filter @system-one/web exec wrangler deploy --config ../docs-app/wrangler.jsoncThe docs config names the Worker system-one-docs, serves the out directory as static assets, and uses the exported 404 page for unknown paths. Preview locally with:
npx --yes pnpm@9.4.0 --filter @system-one/web exec wrangler dev --config ../docs-app/wrangler.jsonc --port 3001Do not run next start for this static export. A documentation-only preview does not provide /v1/systemone. Keep the main Worker’s SYSTEM_ONE_DOCS_URL pointing to the published documentation after assigning custom domains.
The repository operator guide is docs/deployment.md. Framework details: Cloudflare static assets, D1 migrations, and Next.js static export.