mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-28 08:02:43 +00:00
* 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>
67 lines
4.2 KiB
Markdown
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`.
|