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
- [locked] Better Auth issues the session/JWT; protected routes in
services/apivalidate 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 inapps/webOR a dedicated Worker), backed by Neon through Better Auth's Drizzle adapter and@neondatabase/serverlessusing a SOPS-managedDATABASE_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 onlyprovider/result/status— no email, user_id, or other PII in trace or metric attributes. - [locked] The FastAPI app in
services/apiis the single OpenAPI source of truth; its schema is exported to a committed artifactservices/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. - [locked] Identity is a minted
asset_id.source_uid(and any hash-keyed identity) is forbidden in the new repo. - [locked] Migrations are clean, forward-only SQL authored for this repo. Do NOT copy legacy migration history from
writing-system. - [locked] Alembic (with SQLAlchemy) is the single migration authority over the entire Neon DB, owned by
services/api. All DDL — including Better Auth'suser/session/account/verificationtables — 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'sgetAuthTables()API (checked in atapps/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. - [locked] Better Auth's internal table IDs (
user/session/account/verification) are auth-framework plumbing, NOT domain-asset identity. Theidentityrule (mintedasset_id, nosource_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_uidusage 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.