Skip to content

Multi-Tenant Isolation

When an agent serves multiple tenants, one tenant's data must not influence tool calls for another tenant. Strahl enforces this with visibility tags resolved from actual tool-call arguments.

Tenant Tags

def TENANT(tenant_id: str) -> set[str]:
    return {f"tenant:{tenant_id}"}

Use the same tag in role labels and tool policies when information is allowed to flow.

Example

import strahl
from strahl import Label


def TENANT(tenant_id: str) -> set[str]:
    return {f"tenant:{tenant_id}"}


@strahl.tool(
    requires=Label(source={"support-agent"}, visibility=lambda tenant_id: TENANT(tenant_id)),
    produces=Label(source={"crm"}, visibility=lambda tenant_id: TENANT(tenant_id)),
)
def lookup_account(tenant_id: str) -> str:
    ...


@strahl.tool(
    requires=Label(source={"support-agent"}, visibility=lambda tenant_id: TENANT(tenant_id)),
    produces=Label(source={"support-agent"}, visibility=lambda tenant_id: TENANT(tenant_id)),
)
def reply_to_tenant(tenant_id: str, message: str) -> None:
    ...

If tenant A content influences a call with tenant_id="B", Strahl can deny the call because tenant A's visibility tag is not tenant B's tag.

Per-Tenant Client Instances

Use a Strahl instance per tenant when role labels differ by tenant:

from strahl import Label, Strahl


def make_agent(tenant_id: str) -> Strahl:
    agent = Strahl.from_default()
    agent.set_role_labels({
        "user": Label(source=TENANT(tenant_id), visibility=TENANT(tenant_id)),
        "assistant": Label(source={"assistant"}, visibility=TENANT(tenant_id)),
    })
    return agent