Files
windmill/docs/workspace-roles.md
T
hugocasaandClaude Opus 5.5 e5dedd147e feat: let flow builder operators build AI agents (#11898)
* feat: let flow builder operators build AI agents

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: check linked agents' bound tools and type, validate on the write's transaction

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: bound the linked-agent walk by sorting bound tool ids

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: let builders bind only the agent tool inputs left to the model

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: check every agent tool sharing a bound id

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: show builders the agent's own tool inputs read-only and name agents in the setting

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: show the agent's own tool inputs with the read-only step viewer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: give the agent-set inputs heading the input label style

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: label the agent's fixed tool inputs static inputs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: address cubic review on agent builder rights

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: check agent writes against the role's flows capability

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: name the flows capability as the agent builder right

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: gate agent evals on the role's code capability

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: move the composed-flow check into windmill-api-auth

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:05:20 +02:00

67 lines
4.2 KiB
Markdown

# Workspace roles
What a non-admin member may author, manage and see is its **role**. `developer` and `operator` are
the default roles; a licensed EE instance defines its own list in the `workspace_roles` global
setting (at least one role), and any workspace may override a role's capabilities in
`workspace_settings.role_overrides`, or, when licensed, add roles of its own there. A lapsed
license freezes them rather than dropping them (`custom_roles_enabled`): the stored list and the
workspace-only roles keep applying, writes that change them are refused, and resetting the list to
the defaults or removing a workspace-only role stays open. Falling back to the defaults instead
would hand a restricted `developer` everything. Workspace admin is not a
role: it is the `is_admin` flag on top of whichever role a member holds, and holds everything. The
code is `windmill_common::roles`.
## Capabilities
| Capability | Values | Enforced |
|---|---|---|
| `code` | bool | yes: scripts, previews, dependency jobs, dynamic selects, resolving jobs, data table migrations, eval datasets |
| `flows` | `none` / `compose` / `full` | yes; also AI agents, whose tools are checked as composed steps |
| `apps` | `none` / `compose` / `full` | yes; low-code apps and worker-built apps need `full` |
| `manage_schedules`, `manage_triggers` | bool | yes, see `docs/operator-write-rights.md` |
| `visibility.*` | bool per page | no, UI only |
`compose` is the former operator builder right: the composition-only checks of
`docs/operator-builder-rights.md` run for it and are skipped only at `full`. `full` flows or apps
without `code` is refused at write, since both carry inline code.
Stored values are **patches**: an unset key falls through to the layer below, so a key added later
reads as "never configured". The layers, top first: the workspace override, the instance
definition, then `RoleCapabilities::base_for` (`FULL` for `developer`, the legacy operator for any
other name). A role name no longer defined resolves to `NONE`, so deleting a role fails closed;
deleting one that a member, invite, auto-invite or instance group mapping still names is refused,
and so is deleting an instance role that a workspace still overrides (the override would fall onto
`base_for` instead of the definition it was written against).
## Who is restricted
`ApiAuthed::is_operator` means "this member's role is restricted", computed at authentication from
the role, never from `usr.operator` alone. Every gate fast-paths on it (`caller_capabilities`
returns `FULL` for an unrestricted or admin caller without reading the role), so it must stay
derived from the role: a stale flag lets a restricted member through. The authentication cache
holds it for up to 120s.
`usr.operator` is kept as "this role cannot write code", which is what clients that predate roles
read it as. A member's role is `usr.workspace_role`, or, while that is NULL, `operator` /
`developer` from `usr.operator`: rows written by paths that know nothing of roles stay meaningful.
The old `usr.role` column is unrelated and holds legacy display strings.
## Seats
A role that writes or composes anything costs a full seat; anything else half. Billing SQL cannot
merge capability patches, so `role_seat` materializes the author flag per role
(`workspace_id = ''` for the instance, a row per workspace override), rewritten by
`sync_role_seats` after every write to either setting. Every count reads it through
`AUTHOR_SEAT_SQL`, and usage telemetry reports builders (authors anywhere) against non-builders the
same way. A change that adds seats under an offline license is priced inside the transaction that
writes it, by `check_seat_cap_for_role_change`.
## Legacy `operator_settings`
`/operator_settings` still works: it is applied as an override of the `operator` role, dropping any
key equal to what the instance gives that role, and the payload is mirrored into the
`operator_settings` column for git sync and older servers. The migration moved every key a
workspace had changed from its default into `role_overrides.operator` the same way. The workspace
list still returns an `operator_settings` object for a restricted member, synthesized from its
role, alongside `role` and `capabilities`.