Skip to content

Technical paper8 min read

Tenant isolation when every customer shares the model

Cross-tenant leaks in AI products rarely come from model weights. They come from prompt caches, vector indexes filtered by a client-supplied field, memory, shared tool credentials and logs. Seven checks, why each matters, and the canary tests that prove them.

IntertraceSecurity engineering

Field guides from Intertrace security engineering for the people who run AI programs.

September 25, 2026 · 8 min read

Tenant IsolationMulti-TenancyRAG SecurityPrompt CachingSaaS
Contents
  1. 01To the provider, your whole platform is one customer
  2. 02Seven checks, with the reason for each
  3. 03Prove it with canaries on every build
  4. 04What a gateway decides, and what stays in your services
  5. 05The search to run on Monday

A tester at Brightdesk, a fictional helpdesk SaaS, changed one field in the request payload and got draft replies citing another customer's internal articles. Nothing in the model misbehaved. Every customer's articles lived in one vector index with a `tenant_id` field, the assistant's retrieval tool took `tenant_id` as an argument, and the orchestrator filled it from what the web client sent. The tenant boundary was whatever the caller said it was.

That is the usual shape of the problem. A SaaS product arrives with a sound tenancy model, with a tenant column on every table and row-level security behind it, and then the AI feature adds components that were never part of it: an index built once for everyone, a cache shared because hit rates are better, a support agent's tool with one API token for the whole platform. Each is a reasonable shortcut and each is a path between customers. Our position: resolve the tenant once, from the credential, enforce it inside every shared component, and treat isolation as untested until a canary suite in CI says otherwise. The model's weights are the least interesting part of the problem.

To the provider, your whole platform is one customer

Providers isolate their customers from each other, and that isolation stops at your account. OpenAI's prompt caching documentation, for example, states that caches are not shared across organizations [3]. If every tenant of your product is served through one provider organization, the provider sees one customer, and anything shared at that level is shared across all your tenants unless you divide it. The same holds for everything you build around the model. Each component needs its own answer to one question: which tenant is this, and who decided?

Shared components where tenants leakFig. 1
Tenant Acustomer workspaceTenant Bcustomer workspaceSHARED PLATFORM · ONE MODEL, MANY CUSTOMERSPrompt cacheLEAK POINTVector indexLEAK POINTConversation memoryLEAK POINTTool credentialsLEAK POINTLogs & evalsLEAK POINTthe model is rarely the leak · the components around it hold tenant data side by side

Two tenants feed one platform. The prompt cache, vector index, conversation memory, tool credentials, and logs and evals are each a leak point unless tenancy is enforced inside that component.

Seven checks, with the reason for each

1. The tenant comes from the credential, never from the request

Every other check depends on this one. Resolve the tenant at the edge from the session token, API key or runtime key, then carry it as server-side context to retrieval, cache, memory, the tool broker and logs. Ignore any tenant field in the body, a header or model-generated tool arguments for authorization. If a partner genuinely acts across several tenants, check the asserted value against the set the credential may act for and log any mismatch as a security event.

The model in particular must never choose the tenant. A tool schema with a `tenant_id` parameter is an invitation for an injected instruction to change it, which is exactly the Brightdesk bug.

2. The vector filter runs inside the search, and an empty filter is an error

Retrieval is the most common real leak. OWASP's LLM08:2025 Vector and Embedding Weaknesses names it: where several classes of users share one vector database, context can leak between users or queries. Its prevention guidance calls for fine-grained access controls, permission-aware vector stores and strict logical and access partitioning of datasets [4].

Tenant filter derived from the keyFig. 2
RequestBearer itr_rt_… keyAuthtenant = A, from the keyRetrievalserver-side filter: ATenant ApartitionTenant Bpartition{ "tenant": "B" } in the request bodyclient-supplied tenant field: ignoredtenancy comes from authentication, never from inputthe filter is applied by the server from the authenticated identity, so no prompt or parameter can widen it

The tenant is resolved from the runtime key during authentication and applied by the retrieval service, so results come only from Tenant A's partition. A tenant field supplied by the client is ignored.

Three details decide whether the filter holds. Apply it inside the search, not after: post-filtering a global top-k returns fewer results for small tenants and invites a "fall back to unfiltered" fix that leaks. Reject an empty filter instead of treating it as "match all". And give your most sensitive tenants their own index or namespace, so a missing filter finds nothing instead of everything.

3. Tenant data sits after the shared prompt prefix

Prompt caching reuses computation for a prefix seen before, so a cached prompt returns faster and response time depends on what other requests contained. Gu and colleagues built statistical audits for this, detected prompt caching in 8 of 17 production API providers and global cache sharing across users in 7 of them [1]. The attack relies on cache hits from prefix matches; the researchers disclosed to affected providers in October 2024, and at least five made changes [1]. Self-hosted serving has the same channel. Wu and colleagues showed that KV-cache sharing in frameworks such as SGLang and vLLM, which reuse cache across users with identical token sequences, lets one user reconstruct another's prompts, an attack they call PromptPeek [2].

The preconditions are specific, which is what makes the risk manageable: a cache shared with the victim, a long cacheable prefix that contains tenant data, the ability to submit guessed prefixes, and fine-grained latency measurement. So put the common system prompt first and tenant content after it, and include the tenant ID in the cache key, or run separate pools, wherever you control the cache. Do not mistake routing hints for a boundary: OpenAI documents `prompt_cache_key` as influencing routing, without pinning requests to a machine or guaranteeing a hit [3]. Brightdesk's review found the two platform-side preconditions in place: its system prompt opened with the customer's name and internal escalation policy, and its self-hosted serving stack shared KV cache across all requests. Reordering the prompt and adding the tenant ID to the cache key closed both. Coarser latency reporting raises the cost of measurement without closing the channel, and many requests from one tenant that share a long prefix and vary only a short suffix are the shape of a probe worth an alert.

4. Memory is keyed by tenant and user together

Long-term memory summarizes past conversations and retrieves them later. Keyed by user ID alone, where user IDs are not globally unique, or shared as "team memory" across an organization boundary, it surfaces one tenant's facts in another's session. Use a composite key and the same server-side filter as retrieval.

5. No model is tuned on pooled tenant data

A model fine-tuned on several customers' data is a shared store with no access control, and anything it memorized can be produced for any tenant that prompts well. Tune per tenant, or only on data every tenant has agreed may be pooled, after de-identification.

6. Tools carry per-tenant credentials

An agent that calls a CRM, ticketing system or storage bucket with one platform-wide token can reach every tenant's records, scoped only by the model choosing the right parameters. A prompt injection in one tenant's document then becomes a query against another's data. Issue per-tenant credentials, or per-tenant scoped tokens from a broker, so the downstream system holds the boundary when the agent is wrong. Brightdesk swapped its platform-wide ticketing token for broker-issued per-tenant tokens, so a wrong tool call fails at the ticketing API.

7. Logs, eval sets and rate limits are per tenant

Logs and eval datasets mix tenants by default. A support engineer debugging tenant A sees tenant B's prompts in the same trace view; an eval set built from production traffic goes to a vendor or gets replayed into another tenant's test environment. Tag every record with the tenant, restrict access by it, and build eval sets per tenant or from de-identified data. Capacity counts too: one tenant's runaway agent can exhaust a shared rate limit or token budget for everyone, so enforce per-tenant limits ahead of the shared provider quota.

Prove it with canaries on every build

Isolation that is not tested regresses with the next refactor. Create at least two test tenants, seed each with canary documents, memory entries, tickets and log lines carrying unique random strings, and run the suite on every change to retrieval, caching, memory or tool code. Any canary that crosses fails the build.

The canary suite: one test per leak surface
Leak surfaceControlIsolation test
Prompt or KV cache [1][2]Tenant data after shared prefix; per-tenant cache keysSeed a long canary prefix as B, then compare latency for it and a random prefix as A; a consistent gap means the cache crosses tenants
Vector index [4]Server-side filter from credential; per-tenant namespaceQuery for B's canary while authenticated as A, directly and through the assistant
Conversation memoryComposite tenant and user keyRecall B's canary fact from an A session
Pooled fine-tunePer-tenant tuning or consented, de-identified poolsPrompt for canary strings planted in training data
Tool credentialsPer-tenant or brokered scoped credentialsAsk the agent to fetch a B record as A, through each tool
Logs and evalsTenant tags, access by tenant, per-tenant datasetsSearch A's views, traces, exports and eval sets for B's canary
Rate limitsPer-tenant limits and budgetsLoad one tenant and watch another's error rate
Every internal serviceTenant from the credential onlySend forged tenant fields in the body, headers and tool arguments, and a request with no tenant context; expect results only from the authenticated tenant, and a rejection rather than a global answer when context is missing

What a gateway decides, and what stays in your services

Intertrace's gateway derives tenancy from the `itr_rt_*` runtime key during authentication, never from the request body, and that tenant scopes every LLM, MCP, service and agent-to-agent call it decides. It applies access rules and DLP to tool-call arguments, so a call can be held or blocked before it reaches a downstream system, and records each decision with the rule and layer that made it. It does not partition your caches, indexes or memory stores. Those boundaries live in your services, and the canaries are how you prove them.

The search to run on Monday

After its fix, Brightdesk replayed the tester's forged request, confirmed it returned only the tester's own articles, then searched 90 days of retrieval logs for responses whose cited documents belonged to a different tenant than the requester. Run that search on your own logs. If you cannot, because records do not carry both the authenticated tenant and the tenant of every retrieved document, that is the first thing to fix: without it, you cannot tell whether the boundary has ever held.

Sources

Reporting, research and specifications this piece draws on. Links go to third-party sources; analysis and recommendations are Intertrace’s own.

  1. 01Gu, Li, Kuditipudi, Liang and Hashimoto, Auditing Prompt Caching in Language Model APIs (ICML 2025) · arXiv
  2. 02Wu et al., I Know What You Asked: Prompt Leakage via KV-Cache Sharing in Multi-Tenant LLM Serving (NDSS 2025) · NDSS Symposium
  3. 03Prompt caching guide · OpenAI
  4. 04LLM08:2025 Vector and Embedding Weaknesses · OWASP GenAI Security Project

About this desk

Intertrace

Security engineering

Field guides from Intertrace security engineering for the people who run AI programs.

All 22 pieces by Intertrace

Dated September 25, 2026 · added to intertrace.ai October 19, 2026

+ Runtime enforcement

See these controls on your own agent traffic.

Intertrace sits inline between your agents and the models, tools and MCP servers they call, and decides every call before it runs.

Keep reading

All insights →