Security, Governance and Platform · Principal
A semantic cache served tenant A's private answer to tenant B. What now?
The question
Interview question
A shared semantic cache returns an answer grounded in tenant A's private document to a user in tenant B. The two queries have similar embeddings. Contain the leak, find its scope, and redesign the cache. The cache uses a similarity threshold and a TTL.
Take a few minutes to form your approach. Then open a worked answer and compare the decisions.
Reveal a worked answer
I would turn off serving from the affected cache path immediately. Do not wait for the TTL. Preserve the records needed to understand the incident under restricted access, then invalidate suspect entries and rotate or disable the route that can repopulate them. The response already shown to tenant B cannot be recalled. We need to establish what text was exposed, to whom, and for how long, then follow the organization's incident process. I would not tell a customer “only one answer” before checking the access logs.
The first trace I want is one logical request end to end: tenant and principal at ingress, embedding query, vector index filter, cache candidate IDs and scores, metadata returned, answer selected, source documents behind it, and what authorization check ran after the hit. A similarity threshold answers “is this query like a cached query?” It says nothing about whether the cached answer is safe for this principal. Two tenants may ask identical questions about entirely different private contracts.
Scope investigation needs both directions. Which tenant A entries were visible to other tenants? Which other tenants had their answers cached in the same namespace? Were hit logs complete? Were there unauthenticated previews, CDN layers, prompt logs, or response caches after the semantic cache? If raw response bodies were not logged, reconstruct candidates from stored IDs and source lineage without unnecessarily copying private text into a new investigation system. Bound the time window by deployment and index history, not just the current cache contents. Expired entries may have been served before they expired.
There are several plausible faults: a missing tenant filter on vector lookup, a filter applied after selecting the nearest global neighbor, a shared key built from embedding alone, a metadata parser defaulting tenant to “any,” or a final answer cache with a broader scope than the sources it used. I would find the first boundary where the request's tenant and principal were dropped. Redis's semantic cache documentation shows tenant scoping in the vector query as one possible implementation. The existence of that feature would not save an application that failed to use it consistently.
I would redesign the cache around what is reusable. Public, non-personal answers based on stable public sources may be answer-cached across tenants if the content and policy actually allow it. For private retrieval, cache candidate source IDs, embeddings, or computation inside a tenant partition where appropriate, and recheck the current principal's authorization before fetching source text or serving any answer. A private answer may also depend on account state, product version, source revision, prompt policy, and model behavior. If we cannot name and invalidate all those dependencies, skip answer-level semantic caching for that class.
The interviewer says the two documents have similar embeddings. That is exactly why a global nearest-neighbor lookup is unsafe. The tenant and permitted corpus restriction must apply before a candidate can become a hit. Even a tenant-scoped cache is not enough when two users in the same tenant have different document permissions. At serve time, verify the source IDs and versions behind the entry under the current principal. If a cache item contains private answer text, do not load or emit that text into an unauthorized context while “checking” it. Store the dependency metadata separately from the protected payload.
They suggest adding the tenant ID to the key and keeping the TTL at one hour. That fixes one possible collision but not a stale permission grant, a document deletion, or a wrong principal inside the tenant. A TTL bounds age. It does not substitute for event invalidation and authorization. Test a revoked grant while the cache is warm, a document update that changes the answer, identical queries across tenants, and a query paraphrase that selects an old neighbor. A security test should assert that the protected text never reaches the model, reranker, or final response on the denied path.
The release gate is a demonstrated tenant and principal boundary through every cache read and write, plus a credible way to enumerate and invalidate entries by source and policy version. Until then, accept the extra latency and cost of a cache miss. A fast cross-tenant answer is not an optimization.
Read beyond the question
Explore more security, governance and platform
Follow another question in this area, or return to the full Interview Prep index.
Browse this area →