Backend contract container for runtime, API, auth/session validation, database, migrations, workers, telemetry, and logging rules.

Sections

  • Runtime: backend service, workers, domain services
  • API: routes, OpenAPI, request/response shape, errors, pagination
  • Auth/session: session issuer, validation seam
  • Database: schema, identity, migrations, naming

Rules

  1. [locked] Better Auth issues the session/JWT; protected routes in services/api validate the Better Auth session and never issue it. The auth handler is mounted at the route /api/auth/* (the ROUTE is locked; hosting may be a Cloudflare Pages Function in apps/web OR a dedicated Worker), backed by Neon through Better Auth's Drizzle adapter and @neondatabase/serverless using a SOPS-managed DATABASE_URL. Auth DDL stays Alembic-owned; no Better Auth migrator, Drizzle migrations, Kysely patching, pg, or Hyperdrive binding is used for this auth path. Auth event logs carry only provider/result/status — no email, user_id, or other PII in trace or metric attributes.
  2. [locked] The FastAPI app in services/api is the single OpenAPI source of truth; its schema is exported to a committed artifact services/api/openapi.json. The generation+diff gate (governance/scripts/check-openapi.mjs) exports the live schema and fails when code and the committed artifact drift.
  3. [locked] Identity is a minted asset_id. source_uid (and any hash-keyed identity) is forbidden in the new repo.
  4. [locked] Migrations are clean, forward-only SQL authored for this repo. Do NOT copy legacy migration history from writing-system.
  5. [locked] Alembic (with SQLAlchemy) is the single migration authority over the entire Neon DB, owned by services/api. All DDL — including Better Auth's user/session/account/verification tables — is hand-authored as Alembic migrations. Better Auth's own migrator is disabled; BA only reads and writes tables Alembic created. The schema reference Alembic matches is captured connection-free via Better Auth's getAuthTables() API (checked in at apps/web/better-auth-schema.reference.md); better-auth generate (CLI) requires a live DB and emits a diff, so it is used only optionally against the Neon dev branch to verify applied SQL.
  6. [locked] Better Auth's internal table IDs (user/session/account/verification) are auth-framework plumbing, NOT domain-asset identity. The identity rule (minted asset_id, no source_uid) governs domain assets only and does not apply to these auth tables.

Enforced by

contract tests / OpenAPI diff + schema lint + CI — derived from these rules

  • Endpoint rule (pending) — routes follow the agreed router/path layout.
  • Error-envelope rule (pending) — all error responses match one shape.
  • Auth rule — protected routes validate the Better Auth session/JWT via services/api/app/auth.py.
  • Identity rule — flags any source_uid usage or hash-keyed identity in schema/code.
  • Migration rule (CI) — rejects imported legacy migration history.
  • Migration-authority rule (CI, pending) — fails if Better Auth's migrator runs, or if any DDL reaches Neon outside an Alembic migration.

Open items

  • Endpoint definitions: router layout + path conventions — define and lock.
  • Error envelope: the single error response shape — define and lock.
  • Pagination: the standard page/cursor contract — define and lock.
  • Telemetry & logging: backend logger + trace/span conventions — define and lock.
  • Naming conventions: table / column / constraint naming — define and lock.
  • Migration safety specifics: idempotent/reversible, explicit policies, no blocking locks — confirm and lock.

On this page