Skip to content

Architecture decisions

This page summarises the decisions that shape the codebase. The authoritative, evolving source is the Linear document V1 Architecture Decisions; this page is the in-repo reflection of it.

  • Package manager: pnpm, exclusively. Enforced with engines + engine-strict.
  • Monorepo: pnpm workspaces — apps/* (and packages/* as shared code emerges).
  • Lint + format: Biome, configured once at the root. CI runs biome check as a blocking gate.

TanStack Start (Router + Query) fits an app built around large in-memory graph state and optimistic editing better than Next.js or Remix. The data API is built from server functions (createServerFn), not REST routes.

Supply-chain hardening: all @tanstack/* packages are pinned to exact versions, CI runs with --frozen-lockfile, and Socket.dev monitors dependencies.

Database & auth: Supabase (PostgreSQL), EU region

Section titled “Database & auth: Supabase (PostgreSQL), EU region”

One Supabase instance, two schemas — app and network. Row-Level Security is on for every table. Data must stay in the EU (some cohort members work in politically sensitive contexts) — this is a one-way door at provisioning.

Critical rule: no cross-schema SQL joins, ever. Any query needing data from both app and network is written as two queries assembled in application code. This keeps a future migration of network to its own database clean.

Graph database: PostgreSQL (network schema)

Section titled “Graph database: PostgreSQL (network schema)”

At our scale (~180k nodes, ~400k edges across all maps) Postgres with good indexing is sufficient. Centrality and layout are computed client-side. Apache AGE is the escalation path before a standalone graph DB, if ever needed.

Configuration-as-code via render.yaml (a web service for apps/app, a static site for apps/docs). No manual dashboard setup; the same pattern translates to a docker-compose.yml for self-hosters.

Visualisation: two renderers, one data layer

Section titled “Visualisation: two renderers, one data layer”

@xyflow/react (React Flow v12) for editing mode; react-force-graph-2d for visualisation mode. Both consume the same GraphData contract — the coupling point between modes. Mode switching is a state change, not a data reload.

This Starlight site lives in the repo and ships with the code. See Adding a feature for the docs-currency rule.