ShipKit docs

Architecture

How the monorepo is laid out — apps, packages, and what touches what.

Layout

apps/
  web/      # Next.js 16 — marketing + auth screens
  admin/    # Next.js 16 — auth-gated dashboard
  api/      # Elysia.js — REST API on Bun
  worker/   # Bun process — BullMQ drainer
  mcp/      # MCP server — generated from OpenAPI
  docs/     # Fumadocs site (this app)
  e2e/      # Playwright tests

packages/
  auth/     # Better Auth (server + client + admin plugin, RBAC)
  db/       # Drizzle ORM, schema, migrations, audit log helper
  cache/    # ioredis client + CacheService
  jobs/     # BullMQ queue + worker registration + dispatch helper
  ai/       # Vercel AI SDK chat + RAG (pgvector)
  storage/  # Cloudflare R2 / S3 via Bun.S3Client
  email/    # React Email templates + render
  analytics/ # PostHog browser provider + consent
  ui/       # shadcn/ui primitives + Ladle stories
  entities/ # Shared zod schemas + react-query hooks
  env/      # Type-safe env (@t3-oss/env-core / nextjs)
  logger/   # LogTape category loggers
  eslint-config/
  typescript-config/

Dependency rules

  • App code reaches into packages, never sideways into other apps.
  • packages/db is the only place that talks to Postgres.
  • packages/cache is the only place that talks to Redis.
  • packages/storage is the only place that talks to R2.
  • packages/auth owns session shape; everything else reads it through auth-context.ts.

User scoping

The base product is single-user: every resource is owned by the user who created it. Resource tables each carry an owning-user column — posts.author_id, files.owner_id, subscriptions.user_id, embeddings.user_id, webhook_endpoints.user_id. Route handlers scope queries to the authenticated user's id; any route that reads across users (e.g. /search) is gated by admin RBAC, not by a userId filter alone.

There is no organization / multi-tenancy layer in the base. It ships as the restorable multitenancy skill (.claude/skills/multitenancy), which reintroduces org/member/invitation tables, session.activeOrganizationId, and org-scoped resources.

On this page