Skip to main content

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.