Access for AI agents

An agent should read no more than it is allowed to.

Give an AI assistant access to your engineering record and it can quote anything it finds. Muninark filters by the caller’s scopes before results reach the model, and logs each tool call.

  1. Requestfrom an agent or a person
  2. Scope filteridentity, scopes, roles
  3. Evidenceonly what the caller may read
  4. Answerand a log entry
The problem

Search results end up in a prompt.

Whatever a retrieval layer returns, the model can repeat. If that layer ignores who is asking, one agent with broad access can leak a confidential record to a person who could never have opened it.

  1. An exampleAn assistant with broad access is asked about next quarter’s release plan.
  2. The catchThe plan sits in a repository the person asking cannot open.
  3. Without a filterSearch returns the plan, and the model quotes it.
How it works

Filter first, answer second.

Access is decided before retrieval results are ranked, assembled or shown to a model. Nothing relies on the model to hold anything back.

01

The caller is identified

Over HTTP, each request carries its own validated token. The token maps to scopes such as one repository or one wiki, and a policy maps scopes to roles. With no identity, or an identity with no roles, nothing is returned.

02

Records are filtered first

A record is readable only if one role covers both its sensitivity level and one of its scopes. Everything else is dropped before ranking, before evidence is assembled and before any model sees it.

03

The answer is built from what is left

The model receives only evidence the caller may read. That does not guarantee that every statement it generates is supported by that evidence.

04

The call is logged

Each tool call, answered or refused, adds a record to a tamper-evident log: who called, which tool, the outcome, the roles in force and the evidence cited. If a record cannot be written, the call is refused. Authentication failures are logged separately.

What else holds

Checks beyond search filtering.

Filtering search results is the easy part. These cover other paths to the same records.

A refusal looks the same as an empty result

When nothing matches and when a match is withheld, the response is byte for byte identical across the 22 tools that use it. Tests cover that one property of the response. They do not establish resistance to every way of probing for what exists, and timing is not covered in general.

Derived knowledge never widens access

When the build merges source records into a summary or a decision, it never combines records with different scopes. A derived item is therefore never readable by more people than its sources.

Older revisions follow the same rule

The archive reader checks access to both the archived copy and the current revision of the record. Point-in-time search is currently switched off.

A test on a public repository

Four callers, 54 questions each.

Run on the public microsoft/semantic-kernel repository, split into three scopes: source code, pull requests and wiki. Muninark is not affiliated with Microsoft.

0answers citing evidence outside the caller’s scopes
4 × 54caller identities: one per scope, and one holding all three, each asking the same 54 questions in two authority modes
4 runs28 September to 1 October 2026, the last through a real identity provider

Each cited reference was resolved to its record and compared with the caller’s scopes. One shared scope is enough to pass, and any evidence with none counts as a leak.

The test also counted over-refusals: questions the caller should have been able to answer, returned without that evidence. For the caller holding all three scopes this was 13 to 16 of its 54 questions, and for single-scope callers between 1 and 5. These are expected-visible questions that came back without evidence for the expected scope. The run report ties some of the changing results to the retrieval relevance threshold, not to the access check.

The test checks the evidence references, not the wording of the answers. It covers one tool, search, on a public corpus with no private data, and it does not test timing or hostile queries. Ten subjects that span several scopes were left out of the question bank.

Limits

What this does not do.

These limits depend on how a deployment is configured and on the records it holds.

Security and deployment
  • An agent gets the grant of its own client, whoever asked it. Only a client presenting the user’s own token gets that user’s grant. Give an agent no more than the narrowest audience it serves.
  • Scopes are stamped at ingestion from the deployment’s configuration. They are not a live mirror of permissions in the source system, and a revoked right takes effect as fast as token lifetimes and policy reloads allow.
  • Records are withheld whole. Muninark does not redact parts of a record, and finer zones inside a record are reserved but enforce nothing.
  • The audit log records the caller, tool, outcome and evidence, not the question text, and it stores the caller’s user id.
  • Operators of the console see more than any agent does: the graph and memory views show every node to an authenticated reader, and the audit log is shown whole.
  • This is access control, not protection against prompt injection. Source text is passed to the model as data in separate messages, and the access boundary limits what an injected instruction could reach, but no model is claimed to ignore it.

Who needs to be kept out, and of what?

Tell us which teams and sources must stay separate, and we can walk through how the scopes would be set.

Discuss your use case