1. The three ways to add a user
Settings → User Management exposes a single Add User menu with three options. Pick the one that matches the situation:
The role catalog presented in all three flows comes from
ASSIGNABLE_TENANT_ROLES in src/shared/lib/valid-roles.ts. Legacy
module-specific roles (e.g. hr_admin, fm_technician) are intentionally
filtered out of this primary flow; they will be retired in a future release
(see Phase C of the user-management modernization plan).
2. Invitation lifecycle
pf_user_invitations table tracks every transition. Rows are never
hard-deleted; this preserves the audit trail.
Audit columns
3. Revoking a pending invitation
- Open Settings → User Management → Invitations tab.
- Click Revoke on the row.
- Optionally enter a reason (recommended for compliance — examples: “sent to wrong address”, “role change”, “no longer needed”).
- Confirm.
revoke_invitation(_invitation_id, _reason) RPC.
The RPC is a SECURITY DEFINER function and only succeeds when the caller
has admin access to the invitation’s organization (enforced by RLS on the
underlying tables).
Revoked invitations remain visible in admin queries but cannot be accepted —
the magic-link flow rejects any token whose row is not in pending state.
4. Resending an invitation
- Open Settings → User Management → Invitations tab.
- Click Resend on the row.
resend-invitation-email Edge Function:
- Verifies the caller has org admin access.
- Refreshes
expires_at(rolling 7-day window from the resend time). - Increments
resend_countand stampsresent_at. - Re-sends the branded email through the shared
invitation-mailer.ts.
Rate limits
These thresholds are applied inside the Edge Function and do not require
any client-side bookkeeping.
5. Permissions reference
Phase B of the modernization plan introduces dedicated permission keys
(
pf.users.invite, pf.users.create_direct, pf.users.revoke) so admins
can delegate individual capabilities without granting full org admin.
6. Related code
7. Roadmap
The following improvements are tracked under the user-management modernization plan:- Phase B (Medium-Term): unified Add People dialog, server-side
pf_assignable_rolestable, role-permission preview, hardened route guards on/settings/users. - Phase C (Long-Term): Postgres state machine
(
pf_transition_invitation_status), hourlyexpire-invitationscron, fullpf_audit_logsintegration, domain events (user.invited,user.invitation_revoked,user.invitation_accepted,user.created), retirement of legacy module-roles frompf_user_role_assignments.system_role, HMAC-signed invitation tokens.