- Get Started

Multi-Tenancy

OpenFlows supports multiple independent teams on a single Coder deployment. Each tenant is a logical boundary composed of a Coder user, a repository binding, a dedicated NEXUS workspace, and a namespaced Redis keyspace. Multi-tenancy is not achieved by running separate Coder servers; it is achieved by isolating identity, state, and workspaces within one shared Coder runtime. This guide explains the tenant model, how to use Coder RBAC, and how Redis keyspace prefixes enforce isolation.

Coder is the only runtime. OpenFlows is a thin orchestration brain that provisions ephemeral Coder workspaces and stores state in Redis. In a multi-tenant deployment, every tenant gets its own brain (NEXUS workspace) and its own memory slice (Redis keyspace), but they all share the same underlying Coder control plane. The security boundary is therefore Coder's RBAC plus Redis keyspace prefixes, not physical separation.

Manage tenants with the CLI

See the CLI reference guide for commands to add, remove, and isolate tenants.

Tenant Model

A tenant is a five-tuple. All five pieces must be in place for a tenant to operate:

ComponentDescription
Coder userA normal Coder user account that belongs to the team. The tenant acts as that user for workspace and GitHub operations.
Repository bindingA GitHub repository (e.g. owner/repo) the tenant is authorized to work on. Multiple repos per tenant are allowed.
NEXUS workspaceA dedicated Coder workspace named openflows-nexus-{"{tenant}"} that hosts the orchestrator controller for that tenant.
Redis keyspace prefixAll SharedStore keys are prefixed with ns:{"{tenant}"}: to isolate state, queues, and worker slots from other tenants.
Coder RBACRole-based access controls inside Coder determine which tenant users can create workspaces, view the tenant, or manage external auth.

One Coder server, many teams

Multi-tenancy in OpenFlows is strictly one Coder server hosting many tenants. Running a separate Coder server per tenant is not supported and would defeat the purpose of the model. If you need physical isolation, run separate OpenFlows deployments instead.

Coder RBAC

Coder provides the identity layer. Each tenant maps to a Coder user (or a service account for NEXUS) and a Coder group for the team. Coder's built-in RBAC controls who can:

OpenFlows does not implement its own user management. It relies on Coder groups, roles, and template permissions. Assign tenant membership by adding Coder users to the tenant's group, and assign admin rights by granting the Owner role on that group.

RolePermissions
OwnerTenant admin. Can add/remove repos, manage Coder external auth, and operate the NEXUS workspace.
MemberTeam member. Can view tenant status, create worker workspaces, and open tickets.
ViewerRead-only. Can view the TUI dashboard and logs but cannot trigger or modify work.
NEXUS service accountA non-human Coder user that owns the openflows-nexus-{"{tenant}"} workspace and has limited, scoped permissions.
bash
# Create a Coder group for the tenant
coder groups create my-team --name "My Team"

# Add users to the group
coder groups add-members my-team alice bob charlie

# Grant the group access to the OpenFlows workspace template
coder template update --acl-group my-team:use openflows-nexus

NEXUS is a service account, not a human user

The NEXUS workspace for a tenant must be owned by a dedicated Coder service account, not by a human engineer. If a human user leaves the organization, their workspaces are typically decommissioned. A service account ensures the orchestrator keeps running. Create a service account with a long-lived API token and store it in the orchestrator's environment under CODER_NEXUS_TOKEN.

Redis Keyspace Prefixes

SharedStore is the central state machine. In production, it runs on Redis. Without namespacing, all tenants would share the same keys and collide on ticket IDs, worker IDs, and event rings. OpenFlows enforces a mandatory prefix of ns:{tenant}: on every Redis key. The tenant slug is the canonical namespace identifier.

The prefix is applied automatically by the SharedStore implementation. Application code and operators do not need to remember to include it. However, when you inspect Redis directly with redis-cli, you must include the prefix to see the tenant's data.

Key PatternPurpose
ns:{"{tenant}"}:ticketsMap of ticket IDs to ticket status for the tenant.
ns:{"{tenant}"}:worker_slotsMap of worker IDs to worker status for the tenant.
ns:{"{tenant}"}:pending_prsList of PRs waiting for VESSEL to merge.
ns:{"{tenant}"}:flow_recoveryRecovery state and inconsistencies detected by NEXUS.
ns:{"{tenant}"}:event_ringRing buffer of the last 1000 events for the tenant TUI.
bash
# Inspect all keys for a tenant
redis-cli --scan --pattern 'ns:my-team:*'

# Look at a specific ticket
redis-cli HGET ns:my-team:tickets ISSUE-123

# Count the tenant's worker slots
redis-cli HLEN ns:my-team:worker_slots

Keyspace isolation is not authorization

Redis key prefixes prevent accidental key collisions, but they do not encrypt or hide data. Use Redis ACLs or separate Redis databases if your compliance requirements demand stricter isolation. OpenFlows supports Redis password authentication and TLS, but it does not implement row-level encryption.

Per-Tenant NEXUS Workspace

Each tenant runs its own NEXUS controller inside a dedicated Coder workspace. See the NEXUS workspace guide for details on startup, fault isolation, and cost attribution.