Skip to content
  • System
  • Light
  • Dark
  • High contrast

Architecture

Inntrig is a monorepo with three deployable apps (web, api, docs-dev) and two shared packages (db, shared). All three apps ship to Railway independently.

Browser → apps/web (React Router v7, SSR)
↓ server-side fetch
apps/api (Hono)
packages/db (Drizzle + PostgreSQL)

React Router v7 in framework mode (SSR). All rendering is server-side by default; React hydration enhances the experience progressively. Forms must work without JavaScript — this is an accessibility tool.

Auth is handled server-side: form actions POST to the API, receive a session token, and set an HttpOnly cookie. The web app never stores credentials client-side.

The /auth/* route is a transparent proxy to the API’s Better Auth handler, allowing the browser to interact with auth endpoints on the same origin.

Hono API. Handles:

  • /health — health check
  • /auth/* — Better Auth handler (sign-up, sign-in, sign-out, session)
  • /projects, /audits, /reports, /results — CRUD for domain entities
  • /standards — read-only access to audit standards and criteria
  • /admin/* — staff-only user, workspace, and standard management
  • /docs — interactive OpenAPI documentation (Scalar UI)
  • /mcp — MCP server for AI tool integration

Single source of truth for the database. Exports:

  • db — Drizzle client (postgres.js driver)
  • schema — all table definitions
  • auth — Better Auth instance (shared between API and any future server-side code)

Zod schemas and TypeScript types shared between web and api. Ensures request/response shapes are validated and typed end-to-end.

UI components come from @eekodigital/raster, an external design system package. The web app applies a sand-palette product theme over Raster’s tokens via apps/web/app/theme.css.

Better Auth with email/password. The auth instance lives in @inntrig/db so it shares the same Drizzle client and schema as the rest of the application.

Session token is stored in an HttpOnly cookie (better-auth.session_token). The web app’s server-side loaders read this cookie and validate the session against the API before rendering protected routes.

Both apps deploy to Railway via git push. Railway provides managed Postgres in production — the same DATABASE_URL environment variable works for both local Docker and Railway.