Accounts
Overview
The accounts module manages the full account lifecycle: creation (with automatic JWT re-issue), name editing, account dissolution with confirmation, and leaving accounts as a non-owner member. Lumio supports multi-account switching -- a user can own multiple accounts and be a member of others.
Architecture
Dashboard UI / ID App
|
v
Next.js API Proxy (/api/accounts)
|
+---> REST API (POST/GET/DELETE /v1/accounts, POST /v1/accounts/{id}/leave)
| |
| +--> JWT re-issue on create/dissolve
|
+---> GraphQL (AccountQuery / AccountMutation)
|
v
db::overlays, db::auth, db::members, db::roles (PostgreSQL)
Account Creation Flow
- User calls
POST /v1/accountswith an optionalplan(defaults tofree). - The account is created with the user's display name and the specified plan.
- Default roles (Owner, Administrator, Moderator, Viewer) are created via
db::roles::create_default_roles. - The user is added as a member with the Owner role.
- All active sessions for the user are switched to the new account (
active_account_idupdated). - A fresh JWT is issued containing the new
account_idand returned alongside the account data.
Account Dissolution Flow
- User (must be owner) calls
DELETE /v1/accounts/{id}ordissolveAccountmutation withconfirm_namematching the account name exactly. - All sessions referencing this account have
active_account_idset toNULL. - The account is deleted (CASCADE handles related tables: memberships, roles, overlays, etc.).
- A fresh JWT without
account_idis returned so the user stays authenticated but can select another account.
Leave Account Flow
- Non-owner members can call
POST /v1/accounts/{id}/leaveorleaveAccountmutation. - Ownership check prevents the owner from leaving (they must dissolve instead).
- The user's membership row is deleted.
API
REST Endpoints
| Method | Path | Description | Auth |
|---|---|---|---|
POST | /v1/accounts | Create a new account | Authenticated |
GET | /v1/accounts/{id} | Get account details | Active account must match |
DELETE | /v1/accounts/{id} | Dissolve account (owner only) | Owner + confirm_name |
POST | /v1/accounts/{id}/leave | Leave an account | Authenticated member |
POST /v1/accounts
{
"plan": "free"
}
Response (201):
{
"data": { "id": "...", "name": "...", "plan": "free", ... },
"token": "lm_eyJ..."
}
DELETE /v1/accounts/{id}
{
"confirm_name": "My Account Name"
}
GraphQL Queries
| Query | Args | Returns | Permission |
|---|---|---|---|
account | id: UUID | Account? | account:read |
Users can only query their own active account.
GraphQL Mutations
| Mutation | Args | Returns | Permission |
|---|---|---|---|
createAccount | input: CreateAccountInput! | CreateAccountResult! | AuthGuard (any authenticated) |
updateAccount | id: UUID!, name: String! | Account! | account:edit |
dissolveAccount | confirmName: String! | DissolveAccountResult! | account:delete |
leaveAccount | -- | LeaveAccountResult! | AuthGuard (any authenticated) |
updateAccountvalidates that the name is not empty and the target account matches the active account.dissolveAccountrequires exact name confirmation and owner status.leaveAccounthas no permission guard beyond authentication -- any member can leave.
GraphQL Types
type Account {
id: UUID!
ownerId: UUID
name: String!
plan: String!
createdAt: String!
updatedAt: String!
}
type DissolveAccountResult {
success: Boolean!
}
type LeaveAccountResult {
success: Boolean!
}
Permissions
| Permission | Description |
|---|---|
account:read | View account details |
account:edit | Edit account name |
account:delete | Dissolve account (also requires owner check) |
Included in: Owner role has all three. Administrator has account:read and account:edit. The leaveAccount mutation bypasses permission guards (only requires authentication).
Database
Table: accounts
| Column | Type | Description |
|---|---|---|
id | UUID (PK) | Account ID |
owner_id | UUID (FK) | Account owner user ID |
name | TEXT | Account display name |
plan | TEXT | Plan tier (free, pro, enterprise) |
created_at | TIMESTAMPTZ | Creation timestamp |
updated_at | TIMESTAMPTZ | Last update timestamp |
Related tables (CASCADE delete):
account_memberships-- User-account membership with roleaccount_roles-- Custom roles for the accountaccount_role_permissions-- Permissions assigned to rolesoverlays,channel_rewards,se_tokens,integration_configs, etc.
Key Files
| File | Purpose |
|---|---|
apps/api/src/graphql/accounts.rs | GraphQL queries and mutations |
apps/api/src/routes/accounts.rs | REST endpoints with JWT re-issue |
apps/api/src/db/auth.rs | User lookup, session management |
apps/api/src/db/roles.rs | Default role creation |
apps/api/src/db/members.rs | Membership management |
crates/lo-auth/src/rbac.rs | Permission constants (account:read/edit/delete) |