Appearance
Authorization matrix (Dispatch Phase 1) โ
Row-level security is enforced in Postgres (Supabase) โ see supabase/migrations/20260616010000_rls_policies.sql. This is the source of truth the Go API (WS3) and Angular guards (WS1/WS4) must agree with.
Roles โ
| Role | In this doc | Notes |
|---|---|---|
| Member Partner | Member | Default role on first sign-in. |
| Managing Partner | Manager | Back-office: validates skills, manages all entities. |
| Admin | Manager | Same access as Managing Partner. |
public.is_manager() (SECURITY DEFINER) returns true for Managing Partner and Admin. The Board role was removed. Anonymous (anon) users have no table access โ every policy is scoped to authenticated.
public.is_member() (migration 20260715000100_open_signup.sql) is a restrictive "member gate" ANDed onto every member-data table: being authenticated no longer implies membership โ an open-signup Pending user sees only their own public.users row and nothing else, whatever the permissive policies below would otherwise allow.
Two principals bypass RLS entirely:
- service_role โ the Go API; performs validated server-side writes.
- superuser โ migrations and the non-prod seed (
supabase/seed.sql).
Matrix โ
Legend: โ allowed ยท โช own rows only ยท โ denied.
| Entity | Read (Member) | Read (Manager) | Write (Member) | Write (Manager) |
|---|---|---|---|---|
users | โช own profile | โ all | โช own full_name / function only | โ all |
organizations | โ all (catalog) | โ all | โ | โ all |
mandates | โช assigned to me or Available | โ all | โ | โ all |
tasks | โช assigned to me | โ all | โ | โ all |
skills | โ all (catalog) | โ all | โ | โ all |
user_skills | โช own | โ all | โช insert/update own self_score only | โ
all (incl. validated_score) |
votes | โ all | โ all | โ | โ all |
vote_ballots | โช own | โ all | โช cast own ballot | โ all |
notifications | โช own (recipient) | โ all | โช mark own read | โ all (create/fan-out) |
Column-level rule: skill validation โ
RLS governs rows, not columns. The invariant "only a Manager sets validated_score / coefficient / status" is enforced by the user_skills_enforce_validation trigger:
- Member INSERT must be
validated_score IS NULL,status = 'pending',coefficient = 0โ i.e. self-registration only. - Member UPDATE may change
self_scorebut notvalidated_score,coefficient, orstatus(compared against the prior row). - Manager / service_role / superuser are unrestricted.
This is what lets a member self-assess a skill at registration while the Managing Partner sets the validated score and scheduling coefficient later, in the validation queue.
Column-level rule: member self-update โ
The users Member-write cell is column-scoped. A member may update only their own full_name and function; the users_enforce_self_update trigger (20260617000000_member_self_update.sql) rejects any change to id, email, membership_role, status, or gacx_balance by a non-manager. The Go API's PUT /api/v1/me enforces the same restriction independently (it only ever writes those two columns for the caller's own uid). Managers, service_role, and the superuser are unrestricted.
Verification โ
Policy tests run against the local stack on every supabase db reset (see the WS2.2 commit). They assert: a Member reads only their own tasks; a Manager reads all; a Member cannot set or pre-insert a validated_score; a Manager can; anon has no table privilege.