Appearance
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 hooksNamespaces
| Directory | Package Namespace | Example |
|---|---|---|
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 R2Package Boundaries
@packages/apiexportscreateApiHandler(params)factory,router, schemas, middleware, and services. Accepts pre-initialized dependencies (db,authInstance,storage,env).@packages/dbexports schema, migrations, and acreateDb(connectionString)factory.@packages/authexportscreateAuth(db, env)factory andgetSessionhelpers.@packages/emailexportscreateEmailClient,sendMagicLinkEmail, andsendNotificationEmail.@packages/storageexports R2 client and upload helpers with validation.@packages/sharedexports 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 awarenessThe API is mounted in apps/web/src/hooks.server.ts:
traceHandlerfrom telemetry-js wraps every request/api/auth/*requests go to better-auth handler/api/*requests go to oRPCOpenAPIHandlerviacreateApiHandler()- 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/webdeploys to Cloudflare Worker with ASSETS binding.apps/docsdeploys to Cloudflare Pages (separate site fromapps/web).apps/webuses@sveltejs/adapter-cloudflarewhich generatesdist/index.jsas the Worker entrypoint.
Worker with ASSETS Binding
| App | Platform | Adapter | Wrangler Config |
|---|---|---|---|
apps/web | Worker + ASSETS | @sveltejs/adapter-cloudflare | wrangler.jsonc with assets object |
apps/docs | Pages | None (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.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 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.