Skip to content

Architecture

Monorepo Layout

.
├── apps/
│   ├── web/                 # SvelteKit app + API worker (SSR → Cloudflare Worker with ASSETS)
│   └── docs/                # Documentation site (VitePress → Cloudflare Pages)
├── packages/
│   ├── api/                 # oRPC API routes, services, middleware
│   ├── db/                  # Drizzle ORM + Postgres schema + migrations
│   ├── auth/                # better-auth configuration + helpers
│   ├── email/               # Resend email sender
│   ├── storage/             # R2 client for photo evidence & payment proofs
│   └── shared/              # Shared types, utilities, constants
├── pnpm-workspace.yaml      # Workspace definitions + dependency catalogs
├── package.json
├── turbo.json               # Build orchestration
├── biome.json               # Formatter + linter config
├── .prettierrc              # Prettier config (Svelte files)
└── lefthook.yml             # Pre-commit hooks

Namespaces

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

Data Flow

User → Cloudflare Worker (apps/web)
            ├── SvelteKit SSR (file-based routing + hooks.server.ts)
            │       ├── Page requests → +page.server.ts load → server-rendered HTML
            │       └── API requests → hooks.server.ts routes to:
            │               ├── /api/auth/* → better-auth handler
            │               └── /api/* → oRPC OpenAPIHandler (@packages/api)
            └── Static assets served via ASSETS binding

            Neon Postgres Database (packages/db)

            Auth (packages/auth) → better-auth + Google OAuth + Email/Password + Magic Link + Bearer + JWT
            Email (packages/email) → Resend
            Storage (packages/storage) → Cloudflare R2

Package Boundaries

  • @packages/api exports createApiHandler(params) factory, router, schemas, middleware, and services. Accepts pre-initialized dependencies (db, authInstance, storage, env).
  • @packages/db exports schema, migrations, and a createDb(connectionString) factory.
  • @packages/auth exports createAuth(db, env) factory and getSession helpers.
  • @packages/email exports createEmailClient, sendMagicLinkEmail, and sendNotificationEmail.
  • @packages/storage exports R2 client and upload helpers with validation.
  • @packages/shared exports TypeScript types and utilities used by both frontend and backend.

API Architecture (oRPC)

API code lives in @packages/api:

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

The API is mounted in apps/web/src/hooks.server.ts:

  1. traceHandler from telemetry-js wraps every request
  2. /api/auth/* requests go to better-auth handler
  3. /api/* requests go to oRPC OpenAPIHandler via createApiHandler()
  4. All other requests fall through to SvelteKit page rendering

No additional HTTP framework is used — oRPC's OpenAPIHandler from @orpc/openapi/fetch handles request routing directly.

SvelteKit Architecture

src/hooks.server.ts          → Telemetry + auth + API mounting
src/routes/+layout.svelte    → Root shell (header, nav, footer)
src/routes/+layout.server.ts → Session loading for all pages
src/routes/+page.svelte      → Login page
src/routes/admin/*           → Admin backoffice (SSR + server loads)
src/routes/teacher/*         → Teacher dashboard (SSR + server loads)
src/routes/profile/*         → User profile (SSR + server loads)

Server-side role guards live in +layout.server.ts:

  • /admin/* → redirects non-admin/non-owner users
  • /teacher/* → redirects non-teacher users

Deployment Model

  • apps/web deploys to Cloudflare Worker with ASSETS binding.
  • apps/docs deploys to Cloudflare Pages (separate site from apps/web).
  • apps/web uses @sveltejs/adapter-cloudflare which generates dist/index.js as the Worker entrypoint.

Worker with ASSETS Binding

AppPlatformAdapterWrangler Config
apps/webWorker + ASSETS@sveltejs/adapter-cloudflarewrangler.jsonc with assets object
apps/docsPagesNone (static)wrangler.jsonc with pages_build_output_dir

Documentation Architecture

  • Human docs: apps/docs (VitePress) — overview, tech stack, architecture, user flows.
  • API docs: Auto-generated OpenAPI spec served 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 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.

Released under the MIT License.