Skip to navigation

Tenancy strategies

A Tenancy Strategy tells Runtype whose data an agent or flow works with on each execution. It decides three things: which identity an execution must carry, how strongly that identity must be verified, and how Runtype scopes memory, records, and end-user secrets for the execution.

You save the strategy on a saved agent as config.tenancyStrategy, or on a flow’s config. A dispatch request cannot set or override it, because the caller would then choose its own isolation. Runtype rejects a tenancyStrategy in a dispatch body.

Presets

Runtype has three presets. The following table compares them:

PresetIsolates data byRequiresDefault assurance floor
internalNothing (product-wide)No identityNone
tenant-isolatedTenant (company)_tenantverified tenant
end-user-isolatedEnd user (person)_endUserverified end user

A resource without a strategy keeps its existing scoping and runs no identity check.

Choose tenant-isolated when the people in one customer company share data, such as a buyer organization’s order history or a team knowledge base. Choose end-user-isolated when each person’s data is private, such as personal preferences or a connected GitHub account. Choose internal for agents that serve your own staff or run without a customer identity.

The following agent config isolates by tenant:

Agent config
{
"config": {
"tenancyStrategy": {
"preset": "tenant-isolated"
}
}
}

Assurance levels

Every tenant and end-user identity on an execution carries an assurance level. Runtype sets the level from how the caller authenticated, never from the request body:

LevelMeaning
verifiedRuntype verified the identity. It came from an admitted oidc-jwt Identity Exchange proof, or from a backend request authenticated with your API key.
assertedA trusted source supplied the identity without cryptographic proof, such as a session or custom Identity Exchange integration.
anonymousNobody vouched for the identity. A browser caller that sends a client token or surface key without an admitted proof is anonymous.

A preset rejects an execution whose required identity is missing or below the floor. Runtype returns 403 with error: Tenancy lock-down and one of these codes: tenant_scope_required, tenant_assurance_below_floor, end_user_scope_required, or end_user_assurance_below_floor.

To accept asserted identities, lower the floor for the scope the preset requires:

Lower the floor
{
"tenancyStrategy": {
"preset": "end-user-isolated",
"assuranceFloor": { "endUser": "asserted" }
}
}

The floor accepts only asserted or verified. An override applies only to the scope that the preset requires. Lower a floor only when the source of the identity is one you trust, because an asserted identity is only as strong as that source.

What Runtype isolates

A strategy scopes the data that Runtype stores for the execution:

  • Memory. tenant-isolated keys memory by the Identity Exchange integration and the tenant. end-user-isolated keys it by the integration, the tenant when present, and the end user. internal keys it by the agent.
  • Records. Runtype stamps records that an execution writes with the tenant, and with the end user for end-user-isolated. Reads return only rows with the same stamp. An end user in a tenant also reads the tenant’s shared rows, which have no end user.
  • End-user secrets. Resolving a secret or connected account that belongs to an end user requires a verified end user.

Conversation history in an embedded widget is scoped separately, by the visitor and the verified end user. For details, see Conversation history in an embedded widget.

What your backend must still enforce

A strategy isolates only the data that Runtype stores. It does not control what your API or MCP server returns. When a tool calls your backend, the tool receives the identity, and your backend decides what that identity may read or change.

Pass the identity as server-controlled configuration by referencing it in a tool header, such as X-Tenant-Id: {{_tenant.id}}. Runtype fills _tenant and _endUser from the execution, and the model cannot set them. Also authenticate Runtype to your backend with a {{secret:NAME}} header, so your backend accepts the identity header only from Runtype. To let your backend verify the identity without a shared secret, send Authorization: Bearer {{_identity.token}} instead. For the template rules, see Tool template variables.

Filter every query in your backend by the identity that Runtype sends. If your API returns all orders when it receives no tenant header, a misconfigured tool leaks every customer’s data.

Next steps