Web Worker (apps/web)
- TanStack Start with SSR, served by Vite via the
@cloudflare/vite-pluginlocally and deployed as a Cloudflare Worker - Public Showcase Site routes (landing, docs, blog, FAQ, pricing, changelog)
- Reference Application routes (
/workspaces,/workspaces/$workspaceSlug,/workspaces/$workspaceSlug/settings,/admin) - Better Auth catchall at
/api/auth/$, with CloudflareRATE_LIMITER_AUTH_READ,RATE_LIMITER_AUTH_WRITE, andRATE_LIMITER_AUTH_SIGN_INbindings enforcing per-IP limits viaapps/web/src/lib/rate-limit.ts - Server functions resolve capabilities through the shared runtime in
packages/capabilities, which selects the Live layer when theDBbinding is present (wired in wrangler/alchemy) and the in-memory Seed layer when it is not
The web Worker can call @b2b-saas-starter/capabilities directly. It does not call the API Worker for its own UI — that would add a round trip without value.
API Worker (apps/api)
- Serves the
StarterApiHttpApi contract frompackages/apidirectly viaHttpRouter.toWebHandler— routing, schema decoding, OpenAPI, and the Scalar UI all derive from the contract, with no parallel route table (ADR 0039). Hono is intentionally avoided. - Workspace-scoped API Tokens:
Authorization: Bearer …parsing,ApiTokenRegistry.verifyBearerToken(token)followed by the route's permission check, andApiTokenRegistry.create/revokeroutes (both requiringWorkspaceContext) withapi_token.created/.revokedAudit Events - Per-bucket Cloudflare
RateLimitbindings (RATE_LIMITER_REST,RATE_LIMITER_REST_WRITE,RATE_LIMITER_INVITATIONS,RATE_LIMITER_ASSISTANT,RATE_LIMITER_MCP) - MCP discovery skeleton at
/mcp; tool dispatch lands on top of the same capabilities - OpenAPI document at
/openapi.jsonand Scalar reference UI at/reference
External clients hit the API Worker. Internal UI hits its own server functions. Both paths share @b2b-saas-starter/capabilities so business behavior cannot drift.
Background Worker (apps/background)
- Queue consumer for outbound Webhook Endpoint delivery: queue
b2b-saas-starter-webhookswithmaxRetries: 6,batchSize: 25,maxConcurrency: 4,retryDelay: 30s, and dead-letter queueb2b-saas-starter-webhooks-dlq. Failures callmessage.retry({ delaySeconds })so Cloudflare schedules the next attempt. - Wide-event emission for every delivery attempt via
packages/logger
The background Worker writes delivery attempt history to D1 through WebhookEndpoints.recordDeliveryAttempt. Terminal outcomes are audited: failed_permanent and dead_lettered statuses batch an audit event (webhook.delivery_failed / webhook.delivery_dead_lettered) with the attempt row inside the same D1 batch, scoped by the workspace id from the queue message.