Enterprise

Roles & permissions

Five built-in roles, additively scoped. Custom roles aren't supported yet — fork the matrix if you need a tighter slice.

The five roles

  • owner — full control of the tenant. Manages billing, configures SSO/SCIM, can promote others. One per org by convention.
  • admin — runs day-to-day. Manages members, agent keys, and audit, but can’t change billing or destructive SSO/SCIM config.
  • adviser — acts on an assigned client book. Can read mandate/holdings and propose trades for those clients. Cannot attest (the client does that). Cannot see other advisers’ books.
  • compliance — read-only audit + member list, with export. Cannot propose, cannot key, cannot change anyone.
  • viewer — read-only dashboard for stakeholders. No mutation, no audit export.

Permission matrix

Permissionowneradminadvisercomplianceviewer
billing:read···
billing:write····
members:read·
members:invite···
members:remove···
members:role:change···
keys:read··
keys:create···
keys:revoke···
keys:policy:edit···
audit:read
audit:export··
clients:read·
clients:propose··
clients:attest···
sso:configure····
scim:configure····

Adviser client books

An adviser’s permissions are role-scoped and row-scoped. The membership row carries clientBookUserIds — every database query for a client user filters by that array at the query layer. An adviser literally cannot read or propose on a client who isn’t in their book.

Reassigning a book is a single update — there’s no per-record permission to rewrite.

Where this is enforced

  • At the route guard: every enterprise endpoint requires a specific permission. Missing role → 403 Forbidden with the missing permission name.
  • At the query layer: client-scoped reads and writes apply the adviser’s book as an IN (...) filter, even when the role technically grants the permission.
  • At the audit layer: every action is logged with the actor’s membership id so the compliance officer can prove who did what under which role at which time.
Last updated 2026-06-15