feat(forms): schema + RLS for custom forms and waivers (Phase 1)

Adds the forms and form_submissions tables behind a custom form/waiver
builder. Phase 1 is schema + RLS only; server actions, builder UI, the
public /f/[slug] page, onboarding integration, and retro-linking follow
in later phases.

- scripts/026_forms.sql (idempotent), mirrored as schema.sql Section 17
- forms: globally-unique slug, kind (form/waiver), visibility
  (public_anon/public_auth/members), status, jsonb field schema,
  legal_text, version
- form_submissions: per-row snapshot of schema/legal-text/version for
  immutable waiver records, plus ip/user_agent for e-sign defensibility
- RLS additive + default-deny: forms write gated by forms.manage via
  user_has_permission; form_submissions has no write policy, so it is
  service-client-only and immutable; anon gets no grant on forms
- new forms.manage permission seeded to board + backfilled
- types/database.ts and DATABASE_SCHEMA/DB_SCHEMA_MAP/ARCHITECTURE/HANDOFF
  updated
This commit is contained in:
Moheeb Zara
2026-05-16 14:38:28 -07:00
parent 3ace1afa17
commit 3a2d3ca13f
8 changed files with 302 additions and 5 deletions
+19
View File
@@ -415,6 +415,25 @@ Integration credentials stored in `integrations.config` (JSONB):
| 010 | Fixed channel trigger (removed description column) |
| 011 | Fixed enum values (active→current, pending→unverified) |
Migrations 012-026 are tracked authoritatively in `docs/DATABASE_SCHEMA.md`
(governance kernel, areas, forum/tiers/roles/invites, configurable onboarding,
customizable permissions + Ops ACLs, self-change hardening, and `026` custom
forms + waivers).
### Forms & waivers (migration 026 — Phase 1: schema + RLS)
`forms` and `form_submissions` back a custom form/waiver builder. Phase 1 is
schema + RLS only; server actions, the builder UI, the public `/f/[slug]` page,
onboarding integration, and retro-linking are later phases. Security shape:
all submissions (anonymous, public-authenticated, or members) are written by a
single validated server action using the service client AFTER server-side
schema validation and snapshotting (same pattern as `finishOnboarding`); the
`form_submissions` table has no INSERT/UPDATE/DELETE policy, so RLS hard-denies
every non-service client and submissions are immutable. The public fill page is
served by a service-client server action, so the `anon` Postgres role gets no
grant on `forms`. Form management is gated by the new additive `forms.manage`
permission via `user_has_permission`.
---
## 13. Known Limitations