The three entities
Memberships is the join: one document per (user, organisation) pair, carrying the role. A user with no membership in an organisation cannot see anything inside it.
Roles
Three roles, defined as aLiteral in apps/api/app/models/schemas.py:
Permission matrix
Role checks are explicit
HTTPException raises inside the handlers, not FastAPI dependencies. A valid token with the wrong role gets 403; a missing or invalid token gets 401.The active organisation
A JWT carries three claims that matter:
Handlers read
org_id from the token, never from the request body, so a caller cannot reach another tenant’s data by changing a payload field.
Signing uses HS256 with SECRET_KEY, and tokens expire after ACCESS_TOKEN_EXPIRE_MINUTES (default 30).
Switching organisations
GET /api/v1/users/organisations.
Signing up and inviting
Signup always creates an organisation and makes the signer itssuper_admin. There is no seeded default account — see Generated secrets and defaults.
Check whether an address is already known before inviting:
Machine access
The runtime and other services are not users, so they do not log in. They exchange the shared internal key for a short-lived, organisation-scoped token:
The returned token carries role
admin in the requested organisation. An unknown org_id returns 404.
How scoping is enforced
- The token is verified and decoded (
get_current_user). org_idcomes from the token.- Queries filter on that
org_id. - Role-restricted handlers compare the
roleclaim and raise 403 on mismatch.
Related
- Provider credentials — how secrets are stored and masked
- Agents — what a member can configure
- REST API — auth column for every route
- Security hardening