v0.1.0 · Apache 2.0

Search docs...

Multi-Tenancy

Namespace-per-team isolation, RBAC roles, resource scoping, and secret propagation in Recif.

5 min read

Overview

Recif achieves multi-tenancy through Kubernetes namespaces. Each team gets its own namespace with dedicated agents, secrets, and resource isolation. The platform manages team membership and role-based access control through its API.

Namespace-Per-Team Model

Every team maps to a Kubernetes namespace following the naming convention team-{slug}:

Platform
  |
  +-- team-default (namespace)
  |     +-- support-agent (Deployment)
  |     +-- research-agent (Deployment)
  |     +-- agent-env (Secret)
  |
  +-- team-engineering (namespace)
  |     +-- code-reviewer (Deployment)
  |     +-- agent-env (Secret)
  |
  +-- team-data-science (namespace)
        +-- ml-assistant (Deployment)
        +-- agent-env (Secret)

The default namespace is team-default, configured via global.teamNamespace in the Helm chart.

Namespace derivation

Team IDs are converted to namespace names:

Team IDNamespace
tk_defaultteam-default
tk_ENGINEERINGteam-engineering
tk_DataScienceteam-datascience
(empty)team-default

Team Management

Create a team

curl -X POST http://localhost:8080/api/v1/teams \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Engineering",
    "description": "Backend engineering team"
  }'

Response:

{
  "data": {
    "id": "tk_01J...",
    "name": "Engineering",
    "slug": "engineering",
    "description": "Backend engineering team",
    "namespace": "team-engineering",
    "member_count": 0,
    "agent_count": 0,
    "created_at": "2026-04-03T10:00:00Z"
  }
}

Add a member

curl -X POST http://localhost:8080/api/v1/teams/tk_01J.../members \
  -H "Content-Type: application/json" \
  -d '{
    "email": "developer@company.com",
    "role": "developer"
  }'

Update a member's role

curl -X PATCH http://localhost:8080/api/v1/teams/tk_01J.../members/user_01J... \
  -H "Content-Type: application/json" \
  -d '{
    "role": "admin"
  }'

Remove a member

curl -X DELETE http://localhost:8080/api/v1/teams/tk_01J.../members/user_01J...

List teams

curl http://localhost:8080/api/v1/teams

Get team details

curl http://localhost:8080/api/v1/teams/tk_01J...

Delete a team

curl -X DELETE http://localhost:8080/api/v1/teams/tk_01J...

RBAC Roles

Recif defines four roles with increasing privilege levels:

RoleScopeCapabilities
platform_adminGlobalFull access to all teams, agents, and platform configuration
adminTeamManage team members, create/delete agents, configure governance
developerTeamCreate agents, deploy, trigger evaluations, view scorecards
viewerTeamRead-only access to agents, evaluations, and dashboards

Role permissions matrix

Actionplatform_adminadmindeveloperviewer
Create teamYesNoNoNo
Delete teamYesNoNoNo
Add/remove membersYesYesNoNo
Change member rolesYesYesNoNo
Create agentYesYesYesNo
Delete agentYesYesNoNo
Deploy agentYesYesYesNo
Update agent configYesYesYesNo
Trigger evaluationYesYesYesNo
View agentsYesYesYesYes
View evaluationsYesYesYesYes
View scorecardsYesYesYesYes
Manage governance policiesYesYesNoNo
Platform configYesNoNoNo

Resource Isolation

Each team namespace is independently managed:

  • Agents are scoped to a single team namespace
  • Secrets are namespace-local (an agent in team-engineering cannot access secrets in team-default)
  • Network -- agents in different namespaces are isolated (configurable with Istio network policies)
  • Resource quotas can be applied per namespace using standard Kubernetes ResourceQuota

Example resource quota

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: team-engineering
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "20"

Secret Propagation

Agents load API keys and credentials from Kubernetes Secrets in their namespace.

Default secret: agent-env

The Helm chart creates an agent-env Secret in each team namespace containing LLM provider API keys:

apiVersion: v1
kind: Secret
metadata:
  name: agent-env
  namespace: team-default
type: Opaque
data:
  OPENAI_API_KEY: <base64>
  ANTHROPIC_API_KEY: <base64>
  GOOGLE_API_KEY: <base64>

Custom secrets

Agents can reference additional secrets via the envSecrets field:

spec:
  envSecrets:
    - agent-env           # Default LLM keys
    - custom-api-keys     # Team-specific secrets

All key-value pairs from referenced Secrets are injected as environment variables into the agent pod.

Credential files

For GCP service accounts and similar file-based credentials, use credentialSecret:

spec:
  credentialSecret: gcp-adc

This mounts the Secret as a file and sets GOOGLE_APPLICATION_CREDENTIALS to its path.

How Agents Are Scoped to Teams

When a request arrives at the Recif API, the middleware extracts the team context from the authentication token or request headers. All subsequent operations are scoped to that team's namespace:

  1. Agent listing returns only agents in the team's namespace
  2. Agent creation places the Deployment in the team's namespace
  3. Evaluations are tagged with the team ID
  4. Releases include the team namespace in the artifact's deployment config
# In recif-state artifact
deployment:
  namespace: team-engineering
  environment: development

Note

In development mode (AUTH_ENABLED:"false"), all requests default toteam-default. Enable authentication for real multi-tenant isolation.

Tip

Use theplatform_adminrole sparingly. Most day-to-day operations should be done byadminordeveloperrole users within their team scope.