Reference
Operating a deployment
The variables a deployment reads, its health and readiness routes, where the schema lives, and where to ask for help.
Variables#
The database is the Postgres that DATABASE_URL points at. In production it is the Supabase project's transaction pooler, which is why the app never uses prepared statements that carry a name. The identity project is the canonical Wisent Supabase project at NEXT_PUBLIC_SUPABASE_URL; account sign-in and organization membership are verified against it and nothing else.
| Variable | What it is |
|---|---|
DATABASE_URL | Postgres for every row; in production the Supabase project's transaction pooler URL, written by the product catalogue, never by hand |
PREFERENCES_OPERATOR_TOKEN | server-only bearer for the operator routes |
NEXT_PUBLIC_SUPABASE_URL | the canonical Wisent identity project that verifies account sign-in and organization membership |
NEXT_PUBLIC_SUPABASE_ANON_KEY | that project's public anon key; not a secret |
NEXT_PUBLIC_BASE_URL | public origin of this deployment |
PREFERENCES_COOKIE_SECURE | false only for http://localhost development |
Health, readiness and schema#
GET /api/healthz answers status ok as soon as the process is serving and says nothing about the database. Readiness is GET /api/readyz: it runs select 1 and answers status ready, or 503 with the database's own message. Point uptime checks and deploy gates at readiness.
The schema is not owned by the app. Migrations live in wisent-supabase-preferences and are the source of truth; the app only reads and writes rows. Row-level security is on for every table with no policies, so the PostgREST roles read nothing and the app connects as the database owner through DATABASE_URL.
DATABASE_URL is never set by hand. The database is the Stado database preferences, and its coordinates and credentials sit in the Skarbiec item preferences-database, field pooler_url. Stado delivers that value to the deployment: once the app runs as a Stado service, stado service secret-sync puts DATABASE_URL into the unit's environment, so nobody copies the credential by hand and no second copy of it exists.
Concurrent climb advances#
Store reads made while a transaction is active use its existing connection, including the ranking read by climb advance. Concurrent advances therefore do not reserve a second pool connection while holding the climb's row lock. Outside a transaction the same readers use the shared pool, whose lifecycle settings come from pg. This is shared server behavior for the CLI and the climb management screen, not a client-specific queue.
An idle connection error is logged with its database cause. If a transaction fails and rollback also fails, both causes reach the caller and the connection is discarded instead of returned for another request. A readiness response alone does not prove that advances work under concurrency.
Declare PREFERENCES_URL and PREFERENCES_TOKEN_ROLE for an isolated test arena on the deployed candidate. PREFERENCES_TEST_CONCURRENT_ADVANCES supplies the simultaneous request count; choose the deployed pool's capacity or more to cover connection exhaustion. The journey opens a real climb round, advances it simultaneously through the CLI with no votes, confirms every response remains not ready and that the stored incumbent and round are unchanged, then removes the arena and confirms the climb is gone. Source revision, commands, exit statuses and final state are retained beneath .next/tests/. A failed or unavailable deployment is not a passing test.
node --test tests/climb/concurrent.test.mjsHelp and defects#
Defects go to the GitHub issues of wisent-ai/preferences. Help is on the Wisent Discord. There is no dedicated security policy for the repository yet: report anything sensitive privately to a Wisent maintainer on Discord rather than in an issue.