Darna Docs

Architecture

How the darna-stack monorepo fits together.

A pnpm monorepo with three apps. Each one ships independently to Cloudflare Workers. Auth crosses every boundary — WorkOS issues access tokens, the backend verifies them.

Layout

darna-stack/
├── apps/
│   ├── backend/   Hono + Effect API on :4000
│   ├── admin/     Next.js 16 admin panel on :3000
│   └── docs/      Fumadocs site on :3001 (this app)
├── infra/         deploy notes, env templates
├── package.json   workspace root
└── pnpm-workspace.yaml

Request flow

        ┌─────────────────┐
        │     WorkOS      │  hosted sign-in, JWKS
        └────┬───────┬────┘
   OAuth     │       │ JWT verify
   callback  ▼       ▼
   ┌──────────────┐  ┌──────────────┐
   │    admin     │  │   backend    │
   │   :3000      │──▶  :4000       │   Bearer <accessToken>
   │  Next 16     │  │  Hono+Effect │
   │  AuthKit     │  │  hono-openapi│
   └──────────────┘  └──────────────┘


                     /openapi  /docs

The admin obtains a WorkOS access token via AuthKit, forwards it as Authorization: Bearer … to the backend, and the backend's adminMiddleware verifies the JWT against the WorkOS JWKS for the same WORKOS_CLIENT_ID.

Apps

Conventions

  • Package names@darna/backend, @darna/admin, @darna/docs. Workspace filters use these (pnpm --filter @darna/backend …).
  • Ports — backend 4000, admin 3000, docs 3001. Hardcoded in each app's dev script so pnpm dev (parallel) doesn't collide.
  • Wrangler — every deployable app has its own wrangler.jsonc. compatibility_date is the same across all three.
  • Env files.env (Node dev), .env.local (Next.js dev), .dev.vars (wrangler dev), all gitignored. Each app has a .example next to it. Production secrets go through wrangler secret put.
  • Effect runtime — backend code provides services through one ManagedRuntime (src/lib/effect/runtime.ts). HTTP handlers go through runRoute(span, c, effect) which adds an OTel span and renders RenderableError failures.

Every change to a Next.js app needs turbopack.root pinned to the monorepo root — pnpm's symlink farm under node_modules/.pnpm lives outside the app directory, and Turbopack refuses to compile anything outside its root. See Deploy for the exact config.

Top-level scripts

CommandWhat it does
pnpm devAll three apps in parallel
pnpm dev:backend / dev:admin / dev:docsOne app at a time
pnpm typecheckRecursive tsc --noEmit
pnpm deployBackend → admin → docs to Cloudflare Workers
pnpm deps:checkpnpm outdated -r
pnpm deps:updateInteractive pnpm update -r -i --latest

On this page