Data Isolation

chevron-rightRelevant source fileshashtag

Purpose and Scope

Data isolation ensures that resources belonging to one workspace cannot be accessed by users from another workspace. Every database record that represents user-created content is scoped to a workspace_id, and all API queries automatically filter by the authenticated user's workspace. This prevents workspace spoofing, unauthorized cross-workspace access, and data leaks between tenants.

For information about how workspaces are resolved and assigned to users, see Workspace Management. For details on the authentication flow that establishes the RequestContext, see Authentication Flow.


RequestContext as the Isolation Boundary

Every API endpoint receives a RequestContext from the get_request_context_hybrid authentication dependency. This context contains the resolved workspace_id and UserContext, which together define the isolation boundary for that request.

spinner

Sources: orchestrator/core/auth/hybrid.py:283-399, orchestrator/core/auth/dependencies.py

The RequestContext is constructed by get_request_context_hybrid after resolving the workspace through multiple strategies:

  1. Explicit workspace ID from x-workspace-id header or workspace_id query parameter

  2. User's workspace from Clerk organization or personal workspace

  3. Auto-provisioned workspace for first-time Clerk users

  4. Environment default from WORKSPACE_ID or DEFAULT_WORKSPACE_ID

Once resolved, the workspace_id is immutable for the duration of the request and serves as the filter for all database operations.

Sources: orchestrator/core/auth/hybrid.py:29-68, orchestrator/core/auth/hybrid.py:190-254


Database Query Filtering Patterns

All workspace-scoped resources are filtered by workspace_id in their database queries. The pattern is consistent across all API routers.

Standard Query Pattern

spinner

Sources: orchestrator/api/agents.py:437-476, orchestrator/api/skills.py:418-501, orchestrator/api/patterns.py:15-41

Agent Queries Example

The agents API demonstrates the filtering pattern in all CRUD operations:

Operation
Query Filter
Line Reference

list_agents

.filter(Agent.workspace_id == ctx.workspace_id)

get_agent

.filter(Agent.id == agent_id, Agent.workspace_id == ctx.workspace_id)

create_agent

agent = Agent(..., workspace_id=ctx.workspace_id)

update_agent

.filter(Agent.id == agent_id, Agent.workspace_id == ctx.workspace_id)

delete_agent

.filter(Agent.id == agent_id, Agent.workspace_id == ctx.workspace_id)

get_agent_stats

.filter(Agent.workspace_id == ctx.workspace_id)

Sources: orchestrator/api/agents.py:437-741

Create Operations with Workspace Assignment

When creating new resources, the workspace_id is explicitly assigned from the RequestContext:

spinner

Sources: orchestrator/api/agents.py:359-428, orchestrator/api/patterns.py:44-86

Example from agent creation:

Sources: orchestrator/api/agents.py:374-383


Workspace Access Verification

The hybrid authentication system includes access verification to prevent workspace spoofing. When a client sends an explicit x-workspace-id header, the system verifies the authenticated user actually has access to that workspace.

spinner

Sources: orchestrator/core/auth/hybrid.py:71-82, orchestrator/core/auth/hybrid.py:84-107, orchestrator/core/auth/hybrid.py:283-350

Access Check Implementation

The _user_has_workspace_access function queries both workspace ownership and workspace membership:

A user has access to a workspace if:

  • They own the workspace (workspaces.owner_id = users.id), OR

  • They are an active member (workspace_members.is_active = true)

Sources: orchestrator/core/auth/hybrid.py:84-107

Spoofing Prevention

If a user attempts to access a workspace they don't belong to by manipulating the x-workspace-id header, the system falls back to resolving their default workspace instead of blocking the request entirely. This prevents breaking the UI while maintaining security:

Sources: orchestrator/core/auth/hybrid.py:316-323


Workspace-Scoped Database Models

The following database models include a workspace_id foreign key and are filtered by workspace in all queries:

Model
Table
Workspace Field
Primary API Router

Agent

agents

workspace_id

/api/agents

Skill

skills

(not workspace-scoped)

/api/v1/skills

Pattern

patterns

workspace_id

/api/patterns

Workflow

workflows

workspace_id

/api/workflows

WorkflowRecipe

workflow_recipes

workspace_id

/api/workflow-recipes

MarketplacePlugin

marketplace_plugins

owner_type + owner_id

/api/marketplace

WorkspaceEnabledPlugin

workspace_enabled_plugins

workspace_id

/api/workspaces/{id}/plugins

AgentAssignedPlugin

agent_assigned_plugins

(via agent)

/api/agents/{id}/plugins

Note: Skills are currently global resources shared across all workspaces, as they are loaded from Git repositories. Workspace-specific skill customization is planned for future releases.

Sources: orchestrator/core/models/agents.py, orchestrator/core/models/workflows.py, orchestrator/core/models/marketplace_plugins.py


Frontend Workspace Context

The frontend maintains workspace context through the WorkspaceProvider React context, which fetches the current workspace from /api/workspaces/current and provides it to all child components.

spinner

Sources: frontend/components/providers.tsx:1-86, frontend/components/workspace-provider.tsx, orchestrator/api/workspaces.py:24-54

Workspace API Response

The /api/workspaces/current endpoint returns the authenticated user's workspace, including a flag for first-time users:

The is_new_workspace flag is true when the workspace has no agents yet, triggering the onboarding flow via FirstLoginGuard.

Sources: orchestrator/api/workspaces.py:24-54, frontend/components/onboarding/first-login-guard.tsx:1-35

API Client Workspace Headers

The frontend API client automatically includes the workspace ID in request headers:

Sources: frontend/lib/api-client.ts


Isolation Boundaries and Guarantees

What Is Isolated

The following resources are strictly isolated by workspace:

  • Agents: Each workspace has its own set of agents with unique configurations

  • Workflows and Recipes: Workflow definitions and execution history are workspace-scoped

  • Patterns: Custom patterns are private to each workspace

  • Plugin Enablement: Workspaces must explicitly enable marketplace plugins before assignment

  • Tool Connections: Composio app connections are workspace-specific

  • Execution History: Workflow execution records are isolated per workspace

What Is Not Isolated

The following resources are shared across workspaces:

  • Skills: Loaded from Git repositories, skills are global and read-only

  • Marketplace Plugins: The marketplace catalog is visible to all users

  • Marketplace Agents: Shared agent templates are visible to all workspaces

  • LLM Models: Model configurations are system-wide

  • Composio App Definitions: The catalog of available apps is global

Sources: orchestrator/api/agents.py, orchestrator/api/skills.py, orchestrator/api/marketplace.py


Testing Data Isolation

To verify data isolation works correctly, follow this testing procedure:

1. Create Two Test Workspaces

Sources: orchestrator/core/auth/hybrid.py:110-187

2. Create Resources in Workspace A

Sources: orchestrator/api/agents.py:359-428

3. Attempt Cross-Workspace Access

Sources: orchestrator/api/agents.py:534-552

4. Attempt Header Spoofing

Sources: orchestrator/core/auth/hybrid.py:316-323

5. Verify Workspace Member Access

Sources: orchestrator/core/auth/hybrid.py:84-107


Common Isolation Patterns

Pattern 1: List Resources

Sources: orchestrator/api/agents.py:437-476, orchestrator/api/patterns.py:15-41

Pattern 2: Get Single Resource

Sources: orchestrator/api/agents.py:534-552, orchestrator/api/patterns.py:88-117

Pattern 3: Create Resource

Sources: orchestrator/api/agents.py:359-428, orchestrator/api/patterns.py:43-86

Pattern 4: Update Resource

Sources: orchestrator/api/agents.py:605-695

Pattern 5: Delete Resource

Sources: orchestrator/api/agents.py:697-740, orchestrator/api/patterns.py:119-141


Isolation in Statistics and Aggregations

Statistics endpoints must also filter by workspace to prevent information leakage:

Sources: orchestrator/api/agents.py:266-294

Even aggregate queries that don't return specific records must be scoped to prevent counting resources from other workspaces.


Summary

Data isolation in Automatos AI is enforced through:

  1. Authentication Layer: get_request_context_hybrid resolves and validates workspace access

  2. Request Context: Every endpoint receives a RequestContext with immutable workspace_id

  3. Query Filtering: All database queries include WHERE workspace_id = ? filters

  4. Access Verification: Workspace membership is verified to prevent header spoofing

  5. Model Design: All user-created resources have a workspace_id foreign key

  6. Frontend Integration: React context and API client automatically include workspace headers

This multi-layered approach ensures complete data isolation between workspaces while maintaining a simple, consistent API pattern across all endpoints.

Sources: orchestrator/core/auth/hybrid.py:283-399, orchestrator/api/agents.py, orchestrator/api/patterns.py, frontend/components/workspace-provider.tsx


Last updated