Finalizes the docs section after an adversarial audit (code review + per-page fact-check + coherence critic) and two content requests. - Accuracy: fixed 10 invented cross-link slugs, the admin-vs-board gating claims the verifiers caught (permissions matrix, /settings, invites, onboarding, connect-payments, proposal withdraw, Stripe reconciliation, member fields, door.operate), and 2 issues in the new pages (project area-at-creation, forum thread-delete role). - Coverage: added 5 how-to pages so every module is documented (recurring Stripe dues, tasks & chores, projects, comms & forum, ops knowledge base + secrets vault). 34 pages total. - Screenshots: 12 captioned app screenshots under public/docs-media/, embedded as figures. Each tutorial now has 2-3 step screenshots. - Style: removed all em-dashes/en-dashes/ellipses from the docs (natural ASCII punctuation). - Handoff (docs/HANDOFF-audit-cleanup.md) updated with the current state. typecheck 0 · lint 0 errors · build ok (all 34 /docs pages prerendered)
5.1 KiB
A member is one person's record in your space, stored in the space_members table. This page lists every member status and tier, the fields on the member record, and the settings that control who can see the directory. Manage members at /members; members edit their own record at /me.
Statuses
status is the member_status enum. It defaults to unverified. It governs whether a member can act in the app and whether they hold their role's privileges.
| Status | Can use the app | Holds role privileges | Shown on |
|---|---|---|---|
current |
Yes | Yes | All members |
late |
Yes | Yes | All members (dues lapsed) |
unverified |
Yes | No, pending approval | Pending Approval tab |
inactive |
No, blocked from all actions | No | Inactive tab |
Notes:
- The active set is
current,unverified, andlate. Only these three are loaded as a member's active membership;inactivemembers are blocked from every server action. lateis a dues state, not an authorization downgrade, a late member keeps their role and privileges.unverifiedis the approval gate. In arequire_approvalspace, a new member staysunverifiedand holds no privileged capability until an admin approves them, even if they redeemed a role-bearing invite. Approving setsstatustocurrentandapprovedtotrue.- The /members page tabs are All, Payment Issues, Pending Approval (
unverified), and Inactive. The All tab hidesinactivemembers.
Tiers
tier is the member_tier enum. It defaults to basic and records the membership level. It is set by an admin when adding or editing a member; it does not by itself grant permissions (roles do that).
| Tier |
|---|
plus |
basic |
associate |
Record fields
Key columns on space_members. user_id is nullable on purpose: admins can add "offline" members who have no auth account.
| Field | Type | Notes |
|---|---|---|
id |
uuid | Primary key |
space_id |
uuid | Owning space |
user_id |
uuid, nullable | Auth account; null for offline members |
role |
member_role |
Default member; see roles |
tier |
member_tier |
Default basic |
tier_id |
uuid, nullable | FK to space_tiers; canonical custom-tier reference |
status |
member_status |
Default unverified |
approved |
boolean | Default true; set true on approval |
display_name |
text | Self- or admin-editable |
handle |
text | Self- or admin-editable |
email |
text | Unique per space when present |
phone |
text | |
bio |
text | Self-editable |
avatar_url |
text | |
has_card_access |
boolean | Door access flag |
payment_status |
text | Non-current value drives the Payment Issues tab |
payment_note |
text | |
last_payment_at / last_paid_at |
timestamptz | |
dues_paid_until |
timestamptz | Dues coverage end |
joined_at |
timestamptz | Sort key on /members |
stripe_customer_id |
text | |
skills / interests / willing_to / affiliations |
text[] | Self-editable profile arrays |
coi_last_disclosed_at |
timestamptz | Set when affiliations are disclosed |
onboarding_completed_at |
timestamptz, nullable | Set when onboarding is finished or skipped |
onboarding_progress |
jsonb | Default {}; tracks completed onboarding step IDs |
created_at / updated_at |
timestamptz |
Constraints: unique on (space_id, user_id), and a partial unique index on (space_id, email) when email is present.
Who edits what
- Self (at /me):
display_name,handle,phone,bio,skills,interests,willing_to, andaffiliations. Changing affiliations re-runs conflict-of-interest disclosure and updatescoi_last_disclosed_at. - Admin (at /members):
email,display_name,phone,handle,role,tier,status,has_card_access,payment_status, andpayment_note.joined_atis only set when adding a member;updateMemberdoes not accept it.
On the /members page, some per-member panels are gated by capability rather than role: the access-cards panel (card UID) requires door.manage, the certifications panel requires certifications.grant, and the submitted-forms panel requires forms.manage.
Directory visibility
Who can see the member directory is set per space by member_directory_visibility (the directory_visibility enum) under Member directory visibility in /settings. It defaults to members_visible.
| Value | Who sees the directory |
|---|---|
members_visible |
Any authenticated member (default) |
public_members_visible |
Any authenticated member; behaves identically to members_visible (there is no public, unauthenticated directory route) |
board_only |
Elevated roles only (admin, board, treasurer) |
member_count_visible |
Elevated roles only; treated identically to board_only, non-elevated members get the same restricted notice (no count is shown) |
When a member is not permitted to see the directory, /members shows a "restricted in this space" notice instead of the table.
