Namespaces

Flux's namespace primitive — what it isolates, what it doesn't, and how to use namespaces for team, environment, or tenant separation.

A namespace in Flux is a short label attached to a workflow definition. Set it on the decorator:

from flux import workflow, ExecutionContext

@workflow.with_options(namespace="billing")
async def invoice(ctx: ExecutionContext[dict]):
    ...

The decorator runs the value through validate_namespace (flux/_namespace.py:12), which lowercases-only, allows a-z0-9_-, caps the length at 64, and substitutes "default" when the argument is None or "". Every workflow ends up with a namespace; the only question is whether you picked one.

The namespace travels with the workflow through three tables. WorkflowModel keys uniqueness on (namespace, name, version) (flux/models.py:478). ExecutionContextModel.workflow_namespace (flux/models.py:527) is copied onto every execution, and ScheduleModel.workflow_namespace (flux/models.py:688) pins each schedule to the workflow it triggers. The qualified name on the wire is always namespace/name (flux/workflow.py:94).

Operational uses

Three patterns make sense in practice:

Per-team isolation. Pick team-a, team-b. Each team owns its namespace and the RBAC rules that grant access to it. Names can collide across namespaces (billing/invoice and analytics/invoice are different workflows), which is the main reason to separate teams this way.

Environment isolation. You can run prod and staging namespaces in one Flux deployment, but separate deployments are usually cleaner. Namespaces don’t isolate the database, worker fleet, or encryption key (see below), so a staging workflow on the same server still competes for the same workers and reads from the same secrets store as prod. If you need real blast-radius separation, run two servers.

Per-customer isolation. tenant-acme, tenant-xyz. Works for the catalog and the audit trail (every execution row records workflow_namespace), but it does not get you tenant-isolated secrets, storage, or workers. Treat it as a logical grouping for queries and permissions, not as a security boundary.

Listing namespaces

There is no top-level flux namespace command group in 0.56.0. Namespaces are implicit: they are created the moment a workflow registers with one and disappear when the last workflow in them is deleted. The discovery surface is on the workflow group (flux/cli.py:109-142):

flux workflow list-namespaces                    # all namespaces + workflow counts
flux workflow list --namespace billing           # workflows in one namespace

The REST equivalents are GET /namespaces (flux/api/workflow_routes.py:112) and GET /workflows?namespace=billing (flux/api/workflow_routes.py:135). Both honor the caller’s workflow:{ns}:{name}:read permissions and elide entries the caller cannot see.

To create a namespace, register a workflow with that namespace value — that is the whole operation. To delete one, delete every workflow in it.

What namespaces isolate

What namespaces do NOT isolate

Cross-namespace references

Yes — they work. The call() task (flux/tasks/call.py) accepts either a workflow object or a string ref. A string "analytics/report" is parsed by resolve_workflow_ref (flux/catalogs.py:169) into (analytics, report) and POSTed to /workflows/analytics/report/run/{mode}. From a workflow in billing, you can call into analytics provided the caller’s identity holds workflow:analytics:report:run. Namespaces are an addressing convention, not a wall.

Failure modes

Worker label drift. A worker meant for tenant-acme starts without --label tenant=acme and begins claiming work for every tenant. Workflows that omit affinity={...} will land on whichever worker grabs them first. Fix: declare affinity on every multi-tenant workflow, and gate worker startup on the label being present.

Secret name collision. Two tenants both declare secret_requests=["DB_PASSWORD"]. There is one DB_PASSWORD in the secrets store — whichever tenant set it last wins, and the other reads the wrong value. Fix: prefix secret names by tenant (acme_db_password, xyz_db_password) and update each workflow’s secret_requests, or run a separate Flux deployment per tenant when the prefix discipline is not workable.

Audit “per tenant” assumed to be cheap. The event log is global, so any tenant report is a WHERE workflow_namespace = ? scan. Index that column if you find yourself filtering it under load — ix_workflow_namespace_name exists on the workflows table but not on execution_events.