Database
Neon Postgres, ap-southeast-2.
Migrations
Forward-only, numbered, applied by scripts/migrate.mjs. Never edit an applied
migration — write another. A migration that has run on production is history.
Run as neondb_owner (which has BYPASSRLS); the application runs as
sanctumboard_app (which does not).
Row-level security
Every table with a board_id has RLS ENABLE and FORCE. ENABLE alone
does not apply to the table owner, which is how a table can look protected and
not be.
New tenant tables must opt in. Write it as a loop over table names rather than one statement per table — twice, a table has been added and forgotten.
The policy
An unset app.board_id is unrestricted, so migrations and platform jobs work.
That is safe only because the application role cannot bypass RLS and always
sets the board. Both halves are required.
Append-only tables
audit_log, decision_seal. UPDATE is refused unconditionally; DELETE only
under a declared purge (app.purge_board). Tamper-resistance and
undeletability are different properties, and only the first makes a record
evidence.