Users and organisations
An application user is a member of organisations. Organisations own guilds, secrets, connector logins, model keys, and usage. How a request becomes a user depends on AUTH_MODE (apps/orchestrator/src/http/identity.ts).
Identity
A user has an id, a globally unique name, an optional email, an optional image path, an optional IANA timezone, and an optional identity color (a 6-digit lowercase hex). Names match ^[a-z][a-z0-9-]{0,62}$. The name user is reserved so @user stays the group-chat tag for every operator of a guild. GET /api/auth/me returns the user with platform_admin, which is true for the instance admin. PATCH /api/auth/me takes name, timezone, color, or any of them (timezone and color may be null; a field left out keeps its value), from the Settings page; a name another user holds is 409. The Routine tab pre-fills random windows with the timezone, falling back to the browser's. The colour, returned on /api/auth/me and the organisation's member list, tints the person's messages and avatar.
Core implements two modes, and its configuration accepts no other. Before anyone is identified the app reads GET /api/auth/config, which names the mode. An edition built on core may add a mode of its own by supplying an identity provider (Extension points); a server in a mode with no provider signs no one in (401).
AUTH_MODE | Proof | Typical box |
|---|---|---|
compat-cookie | X-Guilds-User or guilds_user | Local Compose; the default |
trusted-header | Proxy email header (TRUSTED_HEADER_EMAIL, default X-Forwarded-Email) + X-Guilds-Proxy-Secret | Behind the operator's own authenticating proxy |
compat-cookie: whoever reaches the port is the operator. One user and no header selects that user; several users and no header is 400; an unknown id is 404. The UI boots from GET /api/users and remembers the chosen id in localStorage.
The user directory is GET /api/users, POST /api/users (a name and an optional email; creates the user plus a personal organisation), PATCH /api/users/:id (sets the email, or clears it with null), and DELETE /api/users/:id. On compat-cookie it takes no current-user header and answers whoever reaches the box. On trusted-header it answers the instance admin and is 404 to anyone else. A mode an edition adds has no directory. An email is stored trimmed and in lower case, and two users cannot hold the same one (409).
trusted-header: the reverse proxy exclusively determines the email, and the request's user is the one who holds it; an email no user holds is 401, as is a header without TRUSTED_PROXY_SECRET. Boot gives the seeded operator the address in OPERATOR_EMAIL, which the mode does not start without, and refuses to start when another user holds it. The operator gives anyone else an account through the user directory (Remote access).
Organisations
instance → organisation → guild → agentOrganisation is the ownership root. org_members carries an owner, admin, or member role. A new user gets a personal organisation with themselves as owner; it stays visually implicit while it is the user's only membership. Guild names are unique within an organisation; agent names are globally unique.
The selected organisation is X-Guilds-Org or guilds_org. One membership and no header selects that organisation; several memberships and no header is 400. Lists return that organisation's visible guilds (org-visible plus the private guilds the current user operates), secrets, and connector logins. Cross-organisation reads and writes return 404. Every member lists every secret of the organisation (metadata, never a value); owners and admins manage any secret and decide who gets it, and a member manages only secrets granted inside guilds they can see (Secrets and access).
Organisation routes: GET /api/orgs, POST /api/orgs (name, slug; anyone on compat-cookie, the instance admin otherwise), GET /api/orgs/:id, PATCH /api/orgs/:id (name, slug), members (GET /api/orgs/:id/members, POST /api/orgs/:id/members with user_id and role, PATCH/DELETE /api/orgs/:id/members/:user_id; owners change owners, admins the rest), usage (GET /api/usage: the current organisation's guilds the caller may see, by guild and agent), and model keys (GET /api/orgs/:id/model-providers for every member, PUT/DELETE /api/orgs/:id/model-providers/:provider for owners and admins; Tools and connectors).
An organisation's limits (how many agents, browser agents, coding agents, and guilds it may hold, how many generations run at once, and a monthly spend) come from the organisation policy. Core's policy sets none of them; the instance-wide ceiling on concurrent generations is MAX_CONCURRENT_GENERATIONS (Configure). An edition may supply a policy of its own (Extension points).
Instance admin is who administers the instance: the connector catalog, instance-level access, and new organisations. Core's rule is an owner of the seed organisation org_home; an edition may replace the rule. The instance admin reaches /api/admin/mcp/* (the connector catalog), GET /api/platform (the instance-level access view), and the Admin entry in the account menu (Operator app).
Group chat snapshots the current user's name on each post; agents see it as the post's author. A guild's operators (Guilds and agents) are tagged by name, and @user tags all of them. Neither selects an agent.
A user can be removed from an organisation, or deleted, only while every guild they administer has another admin.
Seed identity
On a box with no user at all, boot (seedOperator) inserts one user row and the organisation it owns:
| Id | Name | Image | |
|---|---|---|---|
user_operator | operator | none, or OPERATOR_EMAIL when it is set | none |
Organisation org_home (name Home, slug home): operator is the owner and has no second personal organisation. The seed user cannot be deleted. Once the box has a user the seed never runs again, so a renamed operator or organisation keeps its name. An edition that seeds its own first user before core looks gets no operator.
A user has no portrait upload path; the account button shows initials.
Delete
DELETE /api/users/:id refuses a seed user, and refuses a user who is the last owner of an organisation that still has guilds. The UI has no delete control.
Deleting a user does not cascade organisation-owned data. The instance usage view lists every organisation's lifetime totals; an organisation's counter drops when that organisation is deleted, and the global total stays.