Skip to content

Architecture

Monorepo Layout

.
├── apps/
│   ├── web/                 # Frontend (React + Vite → Cloudflare Pages)
│   ├── api/                 # Backend API (oRPC → Cloudflare Worker)
│   └── docs/                # Documentation site (VitePress → Cloudflare Pages)
├── packages/
│   ├── db/                  # Drizzle ORM + Neon Postgres schema + migrations
│   ├── auth/                # better-auth configuration + helpers
│   ├── email/               # Resend email sender
│   ├── payment/             # Doku payment gateway integration
│   └── shared/              # Shared types, utilities, constants
├── pnpm-workspace.yaml      # Workspace definitions + dependency catalogs
├── package.json
├── turbo.json               # Build orchestration
├── biome.json               # Formatter + linter config
└── lefthook.yml             # Pre-commit hooks

Namespaces

DirectoryPackage NamespaceExample
apps/*@apps/*@apps/web, @apps/api
packages/*@packages/*@packages/db, @packages/auth

Data Flow

User → Cloudflare Pages (apps/web)

      oRPC API (apps/api)

      Neon Postgres (packages/db)

      Auth (packages/auth) → better-auth + Google OAuth + Email/Password
      Email (packages/email) → Resend
      Payment (packages/payment) → Doku

Cron (*/5 min) → scheduled() handler (apps/api)

      reconcilePendingPayments() — overlap lock (SESSION_KV), chunked
      Promise.allSettled (≤8 concurrent), routes each paid tx through
      grantEntitlementForTransaction; grace window on createdAt

SyncPayments (client-triggered) → SyncPaymentsMixin.syncPayments()

      routes each paid tx through grantEntitlementForTransaction

Package Boundaries

  • @packages/db exports schema, migrations, and a createDb(env) factory.
  • @packages/auth exports auth instance and getSession helpers.
  • @packages/email exports sendEmail function.
  • @packages/payment exports Doku client and checkout helpers.
  • @packages/shared exports TypeScript types and utilities used by both frontend and backend.

API Architecture (oRPC)

Contract (apps/api/src/contract/)    →  Route definitions, input/output schemas
Middleware (apps/api/src/middleware/) →  Auth chain, role checks, context builders
Routes (apps/api/src/routes/)        →  Route handlers (guard + governance + service call)
Services (apps/api/src/services/)    →  Pure business logic, no auth awareness

The API entrypoint is a pure Cloudflare Workers fetch handler. No additional HTTP framework (like Hono) is used — oRPC's OpenAPIHandler from @orpc/openapi/fetch handles request routing directly.

The same worker also exports a scheduled() handler (Cloudflare Cron Trigger, */5 * * * *) that runs the payment reconciler — see Deployment: Cron Triggers.

Durable Workflows

Payment webhook processing runs as a Cloudflare Durable Workflow (PaymentWebhookWorkflow). The HTTP receiver verifies the Doku HMAC signature and enqueues the workflow immediately. All side effects (plan upgrade, entitlement grant, email receipt) execute inside the workflow with per-step retries.

Entitlement Grant Funnel

grantEntitlementForTransaction (apps/api/src/services/payment/grant-entitlement.ts) is the single co-transactional funnel for every entitlement side-effect. All four payment paths route through it:

PathEntry point
WebhookPaymentWebhookWorkflow step 4
Status checkCheckStatusMixin.checkPaymentStatus
Cron reconcilerreconcilePendingPayments
Client syncSyncPaymentsMixin.syncPayments step 2

Inside a single Drizzle db.transaction, it performs atomically:

  1. CAS stampUPDATE transactions SET entitlement_granted_at = now() WHERE id = ? AND entitlement_granted_at IS NULL (exactly-once guard)
  2. Never-downgrade plan upgrade — upgrades user.subscription_plan only when the product tier is higher than current
  3. Token credit — credits deep-insights token + free analysis slot (bundle product only)
  4. DI-single unlock — marks deep_insights_purchases paid + unlocks compatibility_results (single-purchase product only)

Re-running any path over an already-granted transaction is a CAS no-op — no double credit.

base44.com Migration

  • base44.com was previously the backend (functions + entities).
  • All secrets previously stored in frontend now live in apps/api env bindings.
  • "Whoami" and non-AI data endpoints move from base44 to apps/api.
  • AI features may still use base44.com directly; everything else goes through the new API.

Deployment Model

  • apps/web deploys to Cloudflare Pages.
  • apps/api deploys to Cloudflare Workers.
  • apps/docs deploys to Cloudflare Pages (separate site from apps/web).
  • apps/web and apps/api share the same Neon Postgres database via Hyperdrive binding per environment.
  • apps/docs is a static site with no backend dependency.

Workers vs Pages

AppPlatformWrangler Config
apps/apiWorkersSupports env.development and env.production keys
apps/webPagesNo env keys — env vars dashboard-managed or via --branch CLI
apps/docsPagesNo env keys — static site

Documentation Architecture

  • Human docs: apps/docs (VitePress) — overview, tech stack, architecture, user flows.
  • API docs: Auto-generated OpenAPI spec served by apps/api at /api/openapi.json with Scalar UI at /api/docs.
  • AI rules: .agents/ knowledge system — hard rules and conventions for code generation.
  • No duplication: API endpoint docs live only in OpenAPI. Human docs link to it.
  • Hot deploy: apps/docs deploys on every push to main. Keep it in sync with codebase changes.

README.md

The root README.md is minimal. It contains:

  • One-line project description
  • Link to apps/docs/index.md for full documentation
  • Link to /api/docs for API reference
  • Quick setup instructions (pnpm install, dev commands)

Do NOT duplicate apps/docs content in README.md.

apps/docs/

The documentation site is built with VitePress and deployed to Cloudflare Pages. It is divided into two sections:

  • overview/ — Non-technical docs: project goals, product features, getting started.
  • technical/ — Technical docs: architecture, tech stack, auth, database, deployment, user flows.

API endpoint documentation is NOT in VitePress. It is auto-generated from the oRPC contract and served by apps/api at /api/openapi.json with Scalar UI at /api/docs. The docs site only links to it.

For docs conventions and update rules, see .agents/rules/docs.md.