Enforcing Access in an AI-first Architecture

Organizations are building shared context layers. The pattern is consistent: take a set of systems that were never designed to talk to each other, unify their contents into a single retrieval layer, and put a language model in front of it so that anyone can ask a question in plain language and get an answer.

Consider a private equity firm with twenty portfolio companies. Each one runs a different ERP system. Each one classifies revenue slightly differently. The firm wants a single real-time view instead of spreadsheets emailed back and forth at the end of every quarter. The data engineering required to do this is well understood and entirely solvable.

The part that is not solvable with data engineering appears the moment the unified layer exists. A finance lead at one portfolio company can now ask a question about another. Before unification, that was impossible for a reason nobody had written down: the systems were separate, held separate credentials, and had no network path between them. The isolation was real, and it was structural, and it was undocumented. Unification removes it, and because it was never written down as a policy, nothing in the new architecture was built to replace it.

This is the general shape of the problem. The industry borrowed retrieval from a mature discipline. It did not borrow the authorization model that came with it.

Where Permissions Were Enforced

Every mature data layer treats authorization as a guarantee provided by the engine rather than by the code calling it. PostgreSQL offers row-level security, a feature that attaches an access policy to a table so the database itself filters rows before returning them. A developer who forgets to write the restricting condition still receives the correct result set, because the restriction was never the developer's job. Directory services work the same way. Group membership is evaluated before the resource is handed over. In both cases the guarantee holds even when the application above it is written badly.

Retrieval-augmented generation is usually built without that guarantee. In most implementations, documents and their embeddings live in a vector store while the record of who may see what lives in a separate system entirely. The system retrieves the closest matches first and applies the permission check afterward, in application code. The check still happens, but it is no longer part of the storage engine, which means it now depends on every query being written correctly by every engineer who touches the retrieval path.

It is worth being precise about what the vector database vendors have and have not solved. Separating one customer from another is handled well. Weaviate assigns each tenant a dedicated shard, which is a self-contained storage and query unit, and its documentation notes that no filter is required to isolate a query to that shard. Milvus routes data to tenant-specific partitions using a partition key. That is a genuine engine-level guarantee, and it is the right model.

What remains in application code is the finer-grained case: not which customer, but which individual employee may see which individual document. That is typically implemented by storing an access list alongside each vector and rewriting every query to include a matching condition. The academic literature treats this as unfinished work. A 2026 paper by Yalamarthi and Pappachan of Portland State University, presented at a SIGMOD workshop, sets out to make fine-grained access control a first-class concern in vector databases and observes that existing metadata filtering strategies are not well suited to the purpose. The authors describe the difficulty as a three-way tension between enforcing the policy correctly, keeping search recall high, and keeping query latency low. That tension is why this cannot be solved by adding a filter and moving on. Two research prototypes, Curator and HONEYBEE, exist to address the same gap. None of this is shipping product. When researchers are still proposing that authorization become first-class, it is not first-class.

The gap can be closed, and the fix is architectural. The pgvector extension stores vectors inside PostgreSQL rather than in a separate system, which means it inherits row-level security automatically. Access control and vector search then run in the same execution path, and the database enforces the restriction rather than the application. That solves enforcement, though not retrieval, and the two are easy to confuse, for reasons the third section returns to.

One distinction matters throughout the rest of this discussion. Pre-filtering applies the permission condition during the search, so the engine never ranks content the person cannot see. Post-filtering runs the search first and discards unauthorized results afterward. The difference is not cosmetic, and it is the first thing to check in any existing implementation.

The consequences of getting this wrong are already documented at enterprise scale. Microsoft 365 Copilot does not bypass SharePoint permissions; it honors them exactly. What changed is discoverability. Traditional enterprise search required a user to guess a query and hope something came back. Copilot summarizes everything a user can already reach, which means a decade of accumulated oversharing became available conversationally and immediately. Varonis, analyzing 15 billion files across more than 300 organizations, found that almost half of all files shared with every user in an organization contain sensitive information. Microsoft's own deployment guidance is organized around remediating that oversharing before the product is switched on. One of the recommended controls, Restricted Content Discovery, blocks a site from Copilot regardless of the permissions on that site, and it explicitly does not change those permissions. That is the current state of practice across the industry. Hide the data from the assistant rather than fix who can see it.

There is a second problem underneath, which is that roles are frequently the wrong unit. Take a law firm handling large-scale class action matters, where an assistant drafts responses to claimant inquiries. The correct retrieval scope is the matter, not the job title. Two associates with identical titles and identical seniority may be permitted entirely different documents, and the boundary between them changes when a case is staffed or closed rather than when anyone is promoted.

The legal profession has had a name for this for decades. The American Bar Association Model Rules define screening as isolating a person from participation in a matter through procedures imposed within the firm. Model Rule 1.10 governs imputation, which is the default assumption that if one lawyer is disqualified from a matter, everyone at the firm is disqualified with them, and it sets out when a screen allows a firm to keep the matter anyway. The Model Rules are recommendations, and each state adopts, modifies, or rejects them independently, so the specific obligations vary by jurisdiction.

Microsoft ships the nearest equivalent as Purview Information Barriers, documented explicitly for ethical wall and conflict-of-interest scenarios. It is worth noting what it actually does, because it illustrates the point rather than resolving it. Information Barriers works by placing users into segments defined by directory attributes such as department, then blocking communication and collaboration between segments. That is an attribute-based control. It handles a research team separated from a review committee. It does not handle a document boundary that follows a case number and changes on a Tuesday when staffing changes.

What Changes When the Agent Acts

Everything above concerns the read path, which is the tractable half of the problem. Authorization becomes much harder when the system stops retrieving and starts acting on the user's behalf.

The Model Context Protocol, the emerging standard for connecting language models to external tools and data sources, has made real progress on part of this. Under the current revision, an MCP server is defined as a resource server rather than an authorization server, meaning it validates credentials issued elsewhere rather than issuing them itself. It must publish a metadata document telling clients where the correct authorization server lives. It must verify that any token presented to it was issued specifically for it, which is what prevents a credential minted for one tool from being replayed against another. Passing a token through from one component to the next without validation is forbidden outright. These are sound requirements and they should be adopted.

But read the scope of what has been standardized. The protocol settles who is calling and which system the credential is good for. It leaves the server to enforce permissions internally, by whatever means the implementer chooses. The protocol specifies the plumbing, not which records a given person may see.

What that gap looks like in production has a CVE number. EchoLeak, tracked as CVE-2025-32711, was disclosed by Aim Security in June 2025 as a zero-click vulnerability in Microsoft 365 Copilot, rated 9.3 out of 10 on the industry severity scale. An attacker could send a crafted email containing hidden instructions. When Copilot later retrieved that email as part of assembling context for an ordinary user request, it followed the attacker's instructions, gathered internal files, and transmitted them outward. The user clicked nothing. Microsoft patched it before disclosure and reported no exploitation in the wild, so the vulnerability itself is closed.

The lasting lesson is not the bug but the language used to describe it. The failure was characterized as an LLM scope violation, meaning the system blended trusted internal sources with untrusted external content without enforcing a boundary between them. Scope violation is an authorization term, and it describes a permission failing inside a model rather than inside a query planner, a category of failure no existing control was designed to catch.

The same phrase now appears in survey data. A Cloud Security Alliance study published in April 2026, commissioned by the vendor Zenity, found that 53 percent of organizations have had AI agents exceed their intended permissions, and that only 8 percent said their agents never do. A second Cloud Security Alliance survey published days later, commissioned by Token Security and based on 418 responses, found that 65 percent of organizations had experienced an AI agent incident in the preceding twelve months, with data exposure the most common consequence. Both studies were sponsored by companies selling controls for the problem they measure, and the two incident rates differ because the questions differ, so treat the exact figures with appropriate caution. The direction is not in dispute. Agents exceeding their intended scope is an ordinary operating condition rather than an edge case.

Simon Willison's lethal trifecta framing, introduced in June 2025, is useful here for reasons that have little to do with security research. It identifies three conditions that together make exploitation possible: access to private data, exposure to untrusted content, and a path to send data outward. Any two are manageable. All three together mean an attacker who can plant text anywhere the system reads can extract anything the system can reach. The framing is valuable because it declines to ask whether the model can be trusted, and asks instead what the model is permitted to do once it has been fooled. That is a question about entitlements, which means it can be settled in the design rather than in the prompt.

The harder problem is the one nobody has solved. When one agent calls another agent, whose permissions apply. Role-based access control has no answer, and no protocol currently in production has one either. Anyone claiming otherwise is describing a roadmap.

The consequences are already visible in adoption data. Gartner predicted in May 2026 that by 2027, 40 percent of enterprises will demote or decommission autonomous AI agents because governance gaps are discovered only after production incidents occur. The second half of that sentence matters more than the number, because it means the gaps are found after something has already gone wrong in production, which is the most expensive moment to find them.

Gartner locates the underlying failure precisely, and it is worth quoting the diagnosis rather than the number. Failures are most likely where organizations do not distinguish between an agent's ability to act and the scope of access it has been granted. Those are two different permissions that most architectures express as one. The result, in the words of Gartner analyst Shiva Varma, is that enterprises treat agent governance as binary, either locked down or fully trusted. That is what the absence of an entitlement model looks like in operation. With no way to express which records this agent may retrieve on whose behalf, the only two available settings are off and everything.

What to Enforce

This is the work Forte Group does, and it belongs in the architecture phase rather than in a security review at the end.

Start with what role-based access control can and cannot do here. Roles remain the right foundation. They are how most enterprises already describe who does what, they already have an owner, and discarding them means rebuilding an authorization model that exists and works. What a role cannot express by itself is the resource boundary. A role establishes that someone is a claims analyst. It does not establish which claims. The working model in production is a role that determines what kind of operation a person may perform, combined with a resource scope drawn from the source system that determines which records that operation may touch. Most implementations propagate the first and lose the second.

Compliance is an input to that design rather than a gate applied afterward. The HIPAA minimum necessary standard, at 45 CFR 164.502(b), requires covered entities to make reasonable efforts to limit protected health information to the minimum necessary to accomplish the intended purpose of a use, disclosure, or request, subject to defined exceptions including disclosures for treatment and disclosures to the patient. Set the statutory language aside and read what it describes. It is a retrieval scoping requirement, written into federal regulation in 2000, years before anyone built a RAG pipeline. GDPR data minimization and purpose limitation impose a structurally similar constraint on a different class of data.

Eight controls follow. Several have no equivalent in conventional application security, which is where most implementations go wrong. Teams apply the controls they already know and assume the rest carries over.

  1. Classify the index at the same level as the source data. An embedding is not a one-way hash. Morris and colleagues at Cornell demonstrated in 2023 that a multi-step inversion method recovers 92 percent of 32-token inputs exactly, and recovered full names from a corpus of clinical notes. Their conclusion was that embeddings should be treated with the same precautions as the raw text. Three consequences follow. The vector store inherits the classification, residency requirement, and retention schedule of whatever was embedded. Deleting a source record does not delete its embedding, so an erasure request has to reach the index explicitly. And a leaked vector store is a breach of the underlying documents rather than of a harmless derived artifact.

  2. Expect the entitlement filter to be the case the index handles worst, and test for it. This is the failure that surprises teams. A permission filter is high-selectivity by nature, because any given person is entitled to a small fraction of the corpus, and high selectivity is the known weak point of approximate nearest neighbor indexes. Restrictive filters fragment the graph that an HNSW index traverses, so the walk runs out of edges to follow and terminates early. Filtering after the search returns fewer results than were requested. The pgvector documentation states the arithmetic plainly: with a condition matching 10 percent of rows and the default search parameter, roughly four rows match out of a requested ten. The failure is silent. Nobody sees an error, the results simply get thinner, and the symptom presents as a relevance problem, so a retrieval engineer picks it up and the tempting fix is to loosen the filter. Concretely: enable iterative index scans in pgvector and keep the ordering clause and the permission clause at the same query level, or select an engine built for filter-aware traversal, such as Weaviate's pre-filtering implementation with its brute-force cutoff or Qdrant's filterable index. Then measure recall at the selectivity a real person's entitlements produce rather than on an unfiltered benchmark.

  3. Make the boundary that must never fail a separate index rather than a condition inside one. This has a security rationale and a retrieval rationale, and the second is what justifies the operational cost. Separating one portfolio company, one client matter, or one regulated entity into its own tenant, partition, or schema removes the selectivity problem described above, because the boundary becomes the index instead of a predicate evaluated inside it. Weaviate tenants, Milvus partition keys, and separate PostgreSQL schemas all produce a boundary that does not depend on any individual query being written correctly. Use filters for the finer distinctions underneath it.

  4. Give every derived artifact an access list, and key every cache by entitlement. Chunks, summaries, extracted entities, knowledge graph nodes, and cached responses are new objects that no source system holds permissions for. A summary drawn from ten documents inherits from ten parents. The safe rule is intersection rather than union: a derived object is visible only to those entitled to every input, and anything whose parent entitlements cannot be computed does not get created. The same logic governs caching. A semantic cache keyed on question text alone will hand one person an answer assembled from another person's documents, and it will do so correctly according to its own logic.

  5. Carry the end-user identity to every hop, and check it at the tool rather than only at retrieval. In conventional software the set of code paths is known in advance, so a checkpoint can be placed on each one. An agent composes its own sequence at runtime, which means there is no complete list of paths to guard and the check has to live at each resource. In practice: the retrieval service receives a credential whose subject is the person, obtained through token exchange or an on-behalf-of flow, and rejects anything arriving with only a service account. Under the Model Context Protocol that credential must be bound to the specific server receiving it, and forwarding it untouched to a downstream service is not permitted. Every tool enforces its own check at execution, because reading a customer record and updating one are different permissions, and a system that checks only at retrieval will let an agent write wherever its credentials reach. Default to deny, list which tools each role may invoke, and require human approval for anything irreversible.

  6. Decide where entitlements are resolved, and write down a staleness budget in minutes. Copying source access lists into the index at ingestion is fast at query time and goes stale between synchronizations. Evaluating entitlements live against the source system or a policy store is current and adds latency. Most production systems need both: copied lists to narrow the candidate set, and a live check before anything reaches the user. Whichever combination is chosen, state the maximum tolerable staleness as a number, trigger re-indexing from permission change events rather than a nightly batch, and test the number rather than assuming it.

  7. Break one leg of the trifecta on purpose. When a system has access to private data and also processes content originating outside the organization, remove the outbound path. No tools that make arbitrary network requests, no rendering of externally supplied links or images, no automatic sending. This is the control that would have stopped EchoLeak, and it is a configuration decision rather than an open research question.

  8. Log the retrieval set, and treat revocation as an event. Persist the identifiers of the documents returned, the identity that triggered the request, the policy version in effect, and the tool calls that followed. A conversation transcript is not an audit record, because the output of a non-deterministic system cannot be reproduced from the question alone. When access is withdrawn, that change should reach the index, the caches, and any persistent agent memory as an event, bounded by the staleness budget from the sixth control, and a cache lifetime longer than that budget is a gap in the design. This is the least implemented control on the list: in the Cloud Security Alliance survey cited earlier, only 21 percent of organizations reported having a formal process for decommissioning an agent, which means credentials and permissions routinely outlive the systems they were issued for. Fine-tuning cannot be undone, so restricted data belongs in the retrieval path, where entitlements still apply, and not in model weights, where they do not.

None of this is theoretical for us. We have built the unified reporting layer for a private equity firm across a portfolio of operating companies, where the boundary between companies had to be specified before the first connector was written. We have built matter-scoped retrieval for a law firm handling large-scale class actions. We work inside the back office platforms that serve tier one and tier two banks, and inside the systems of a retirement and health benefits administrator, where the audit trail is examined by someone whose job is to find the gap in it.

Two limitations are worth stating plainly. All eight controls in place will not prevent a model from producing a wrong answer, which is a quality problem and a separate discipline with its own techniques. And none of this is inexpensive in organizations whose source-system permissions were never clean, which is most of them. The oversharing has to be remediated before the context layer is built, because the context layer opens the existing mess to everyone at once, in plain language.

Shared organizational context is an authorization problem rather than a retrieval problem. The question was never how to filter a vector search, but which layer enforces the rule, and whether that enforcement holds when an agent begins acting on someone else's behalf.

About the author

Lucas Hendrich
CTO at Forte Group

You may also like

Transform AI into a Scalable Delivery Capability

83% faster delivery. Under 10% rework. See exactly how Xceptor got there.