Docs

Auth is the quiet center of gravity in the Dark Avian Labs stack. These notes explain how it thinks about users, cookies, and the boundary between “central truth” and “app-specific behaviour” - without pretending to replace reading the code.

The contract other apps rely on

Integrating applications - today Armory and Codex - do not each implement password hashing, email verification flows, and twelve slightly different session formats. They implement one integration pattern:

  • Send the user to Auth when they need to authenticate.
  • After login, return them to the calling app with a session that makes sense on a shared cookie domain.
  • Ask Auth (or the central database, depending on the route) whether the session is valid and whether the user may use a given app id.

That contract is why BASE_DOMAIN, BASE_PROTOCOL, and AUTH_SUBDOMAIN show up so early in server/config.ts. They are not trivia; they are how the browser knows it is still talking to the same family of sites.

APP_LIST: the bouncer’s clipboard

APP_LIST is a comma-separated allowlist of application identifiers. It is normalized to lowercase and de-duplicated at startup. If you add a new product subdomain, you will almost certainly add a new app id here and teach the downstream app how to identify itself (APP_ID in Codex, Armory’s public URL wiring, and so on).

Think of it as the bouncer’s clipboard: if your app is not on the list, it should not expect friendly CORS behaviour or first-class support in cross-app redirects.

Example shape (exact values depend on your deployment)
APP_LIST=armory,codex

Cookies: shared domain, shared responsibility

Auth sets the tone for cookie behaviour across the stack. In production you want secure cookies and a real session secret - no surprises there. What is easy to overlook is coordination:

  • COOKIE_DOMAIN defaults to . so subdomains can participate in the same session story.
  • AUTH_COOKIE_DOMAIN exists because sometimes you need a narrower or wider scope without rewriting every consumer.

If Ops and Web disagree here, users see “randomly logged out” bugs that are miserable to reproduce. Fix the domain model before you fix React.

Central SQLite: single writer, clear expectations

The central database path is configurable via CENTRAL_DB_PATH. In production you will often mount a persistent volume and point every service at the same file.

SQLite is not magic multi-master storage. The README in sibling projects already warns about WAL and single-writer boundaries for schema changes; Auth is the service most likely to perform authoritative writes to user tables. Treat migrations like surgery: scheduled, backed up, and announced.

Rate limiting and abuse-shaped thinking

Auth exposes HTTP APIs that attackers love: login-adjacent routes, session checks, anything that can be spammed to learn whether an account exists. Optional AUTH_API_RATE_LIMIT_* variables exist to tune window and max request counts.

This is not “security through annoyance.” It is buying time - for humans to notice spikes, for captchas to be added later if needed, and for your database to stay upright when someone’s botnet gets curious.

Session secret hygiene

Production requires SESSION_SECRET. The README recommends 32+ characters; in practice, generate something from a cryptographically sound source and store it in your vault. Rotation is a policy question, but the implementation detail that matters in code reviews is: never reuse a dev secret in prod “just to get unblocked.”

Mental model diagram

Client environment split

Vite injects VITE_* variables at build time for the Auth UI. Server variables stay server-side. If you see “works on my machine” bugs after deploy, compare built client env with runtime server env; they are not the same file.

Tests and confidence

The repository ships Vitest coverage. For Auth, tests are especially valuable around edge cases in URL parsing, cookie configuration, and permission checks - places where “looks fine in the UI” hides a logic bug that only fires for one app id.

pnpm run test
pnpm run test:coverage