Multi-Tenancy¶
Hierarchy¶
Organization
├── Team (with roles: admin, member, viewer)
│ └── Workspace
│ └── Project
│ ├── Tasks
│ └── Workflow Instances
├── Skills (org-scoped)
├── Configs (org-scoped)
└── Secrets (org/team/workspace-scoped)
Default bootstrap creates a single Organization + Team. Operators add complexity only when they need it.
How Scoping Works¶
TenantMiddleware resolves request.organization and request.project from:
X-Organization/X-Projectheaders (API clients)- Session (
active_organization_id/active_project_id) - First membership (fallback)
Views filter queries by request.organization to prevent cross-tenant data leaks.
Missing filters leak data
Every query that should be tenant-scoped must include .filter(organization=request.organization). A missed filter is a cross-tenant data leak. Audit all routes for this pattern.
Scoping Rules¶
| Model | Scoped to | Notes |
|---|---|---|
| Task | Project | project_id FK |
| WorkflowInstance | Project | project_id FK |
| Skill | Organization | Shared across all teams in the org |
| Config | Organization | Same as skills |
| Secret | Org / Team / Workspace | Narrowest scope applies |
| DispatcherInstance | Global | Not tenant-scoped |
| InfraService | Global | Registered once, available to all |
| AgentDefinition | Global | Shared image catalogue |
Auth Stack¶
- django-guardian: object-level RBAC
- django-oauth-toolkit: OAuth2 for external integrations
- Scoped API keys: per-key scopes, expiry, dispatcher binding
- HMAC callbacks: signed with
CALLBACK_SIGNING_KEY - Hashed tokens: callback tokens stored as SHA-256, never plaintext
API Authentication¶
| Client | Auth method |
|---|---|
| External orchestrators | OAuth2 client credentials |
| Operator tools | Token auth (DRF) |
| Dispatcher | Long-lived API key (X-API-Key header) |
| Agents | Per-task key (X-Agent-Key header) |
Isolation Layers¶
Multi-tenancy is handled at multiple layers:
Runtime Isolation¶
Agents are namespaced at the runtime level:
- K8s -- Separate namespaces per environment. Agents run in dedicated
agentsnamespace. - ECS -- Separate clusters or security groups. Dedicated task role with no AWS API permissions by default.
Controller Isolation¶
Django RBAC provides org/team isolation at the workflow and registry level. Skills, configs, briefs, and workflows are scoped to their owner.
Dispatcher Isolation¶
The Dispatcher is tenancy-agnostic -- it executes tasks without enforcing tenancy. Isolation is the runtime's and Controller's responsibility.
Secrets Scoping¶
Secrets scope hierarchically with the narrowest scope winning:
| Scope | Available to |
|---|---|
| Organization | All teams, workspaces, projects |
| Team | All workspaces in the team |
| Workspace | All projects in the workspace |
A workspace-level secret with the same name as an org-level secret overrides it at brief assembly time.