Appearance
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 hooksNamespaces
| Directory | Package Namespace | Example |
|---|---|---|
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 grantEntitlementForTransactionPackage Boundaries
@packages/dbexports schema, migrations, and acreateDb(env)factory.@packages/authexportsauthinstance andgetSessionhelpers.@packages/emailexportssendEmailfunction.@packages/paymentexports Doku client and checkout helpers.@packages/sharedexports 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 awarenessThe 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:
| Path | Entry point |
|---|---|
| Webhook | PaymentWebhookWorkflow step 4 |
| Status check | CheckStatusMixin.checkPaymentStatus |
| Cron reconciler | reconcilePendingPayments |
| Client sync | SyncPaymentsMixin.syncPayments step 2 |
Inside a single Drizzle db.transaction, it performs atomically:
- CAS stamp —
UPDATE transactions SET entitlement_granted_at = now() WHERE id = ? AND entitlement_granted_at IS NULL(exactly-once guard) - Never-downgrade plan upgrade — upgrades
user.subscription_planonly when the product tier is higher than current - Token credit — credits
deep-insightstoken + free analysis slot (bundle product only) - DI-single unlock — marks
deep_insights_purchasespaid + unlockscompatibility_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.comwas previously the backend (functions + entities).- All secrets previously stored in frontend now live in
apps/apienv 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/webdeploys to Cloudflare Pages.apps/apideploys to Cloudflare Workers.apps/docsdeploys to Cloudflare Pages (separate site fromapps/web).apps/webandapps/apishare the same Neon Postgres database via Hyperdrive binding per environment.apps/docsis a static site with no backend dependency.
Workers vs Pages
| App | Platform | Wrangler Config |
|---|---|---|
apps/api | Workers | Supports env.development and env.production keys |
apps/web | Pages | No env keys — env vars dashboard-managed or via --branch CLI |
apps/docs | Pages | No 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/apiat/api/openapi.jsonwith 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/docsdeploys 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.mdfor full documentation - Link to
/api/docsfor 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.