Namespace-scoped permissions
Combining Flux's namespace primitive with RBAC — patterns for tenant-restricted roles and the gaps where namespaces don't isolate.
Flux permissions are colon-delimited strings with * wildcards. For workflows, the second segment is the namespace: workflow:{namespace}:{name}:{verb}. That gives you a place to anchor tenant or environment isolation in custom roles. This page collects the patterns that work, plus the resource families where the namespace segment is absent and a single role accidentally crosses tenants.
What namespaces do scope
Workflow permissions are the one family that carries {namespace} as a real segment. Routes under /workflows/{namespace}/{workflow_name}/... build the required permission from the request path (see flux/server.py):
required = f"workflow:{wf.namespace}:*:register"
required = f"workflow:{namespace}:{workflow_name}:read"
required = f"workflow:{namespace}:{workflow_name}:run"
AuthService._collect_required_permissions does the same for nested workflows and task-level execute checks. A role granting workflow:tenant-a:*:* covers register, read, run, and task execution inside tenant-a — and only there.
Patterns
Tenant-restricted developer
Registers, runs, and reads any workflow inside their namespace, plus schedules:
flux roles create dev-tenant-a \
--permissions "workflow:tenant-a:*:*" \
--permissions "schedule:*:manage" \
--permissions "schedule:*:read"
workflow:tenant-a:*:* covers all task-level execute checks too because the terminal * matches any remaining segments (see _wildcard_match in flux/security/identity.py).
Tenant-restricted operator
Run, read, and cancel workflows in one namespace; observe executions across the cluster:
flux roles create op-tenant-a \
--permissions "workflow:tenant-a:*:run" \
--permissions "workflow:tenant-a:*:read" \
--permissions "execution:*:read"
The execution:*:read line is the catch — see What namespaces do not scope.
Cross-tenant viewer
Read everywhere, write nowhere. The built-in viewer role already covers this (workflow:*:*:read, execution:*:read, schedule:*:read, plus agent / config / service reads). Clone it if you need a custom variant.
What namespaces do not scope
Workflows are the only family that route the namespace into the required permission string. Everything else uses fixed top-level shapes:
| Family | Permission shape | Namespace segment? |
|---|---|---|
workflow | workflow:{ns}:{name}:{verb} | Yes |
schedule | schedule:*:read, schedule:*:manage | No |
execution | execution:*:read | No |
agent | agent:*:{verb} | No |
config | config:*:read, config:*:manage | No |
service | service:*:{verb} | No |
worker | worker:*:* | No |
admin | admin:{area}:{verb} (roles, principals, secrets, metrics) | No |
Consequences:
- Executions, schedules, and services are cluster-wide reads.
execution:*:readis the only execution permission the server checks;GET /executionstakes anamespacequery parameter but does not enforce it against the caller’s permissions. A principal withexecution:*:readsees every namespace by passing or omitting the filter. If you need hard isolation here, put a gateway in front and rewrite or reject queries by tenant claim. - Secrets are global.
SecretModelinflux/models.pyhasnameas its only primary key — there is no namespace column.admin:secrets:readreads every secret, regardless of which namespace’s workflow asked for it. - Configs are global.
ConfigModelhas the same shape:nameis the primary key, no namespace.config:*:readreads them all. - Workers are a global fleet.
worker:*:*is the only worker permission and is reserved for the worker principal itself. Per-tenant worker pools are a labels-and-affinity story, not a permissions story. admin:*crosses tenants. Anyadmin:roles:manageoradmin:principals:manageholder can grant themselves another tenant’s namespace. Do not includeadmin:*in tenant-restricted roles.
Provisioning a tenant-restricted user
End-to-end, for an API-key principal:
# 1. Custom role.
flux roles create dev-tenant-a --permissions "workflow:tenant-a:*:*"
# 2. Service-account principal.
flux principals create \
--type service \
--subject dev-tenant-a-bot \
--external-issuer flux \
--display-name "tenant-a dev bot" \
--role dev-tenant-a
# 3. API key.
flux principals api-key create <principal-id> --name ci
# 4. Distribute the key as FLUX_API_KEY.
For OIDC principals, attach the role through your IdP claim mapping or via [flux.security.oidc] default_user_roles so new sign-ins land on the namespace-scoped role instead of viewer.
Failure modes
- Wildcard too wide.
workflow:*:*:runinstead ofworkflow:tenant-a:*:runquietly grants every namespace. Audit custom roles for non-terminal*in the namespace slot. - Tenant role mixed with
admin:*. Grants self-elevation. Keepadmin:*on a separate, non-tenant role. - Assuming secrets are namespaced. A workflow under
tenant-areadingdb_passwordreads the same row astenant-b’s workflow. Prefix secret names by tenant (tenant-a/db_password) and verify the SECRET_REQUESTS strings before relying on isolation.
The wildcard string is the whole isolation primitive. If a resource family does not put the namespace in its permission shape, namespaces will not isolate it.