The permission layer most enterprise RAG deployments lack

Your private LLM isn't leaking. Your retrieval layer is.

A junior analyst asks a routine question, and a stale ingestion-time ACL hands the model a Board-Only memo. The Sovereign RBAC Firewall authorizes every retrieved document at query time, against the user's live nested-group set and attributes, and drops withheld documents before the model sees a token. Deterministic code, outside any LLM. Agents advise, code decides.

40/40

Correct decisions on the golden authorization set

0 unauthorized disclosures, 0 false denials, computed by the eval harness

29/40

The faithful flat-ACL baseline on the same 40 cases

10 unauthorized disclosures, 1 false denial

$670K

Added cost of a shadow-AI breach over a traditional incident

IBM Cost of a Data Breach, 2025

This is a runnable demo, not a deployment. Every identity and document is a synthetic European-bank fixture; the identity provider is an Azure-AD/SCIM-shaped JSON fixture, and the vector store runs in-process behind a Qdrant-shaped interface.

The pilot passes the demo. Then it fails the security review.

Buying a private LLM does not make it safe to point at the crown-jewel corpus.

The standard pilot build tags each document chunk with a flat ACL at ingestion. That design collapses under real enterprise identity: nested Active-Directory groups, cross-OU inheritance, clearance levels, time-boxed project grants, and terminations that happen after the index was built. The result is the classic failure this demo stages end to end: a junior analyst asks a normal question, and the RAG pipeline hands the model a Board-Only memo, because a stale ingestion-time snapshot said she could see it.

The root cause is structural. Document permission is a live property of an identity graph, so any snapshot of it is already wrong. A better model does not fix this: a perfect model handed a board document still leaks it. The value sits in governing what reaches the model, not in model quality.

The stakes are measured. Shadow-AI breaches cost $670K more than traditional incidents, and 65% of AI-related breaches compromised customer PII (IBM Cost of a Data Breach, 2025). Shadow-AI breaches take 247 days to detect against a 241-day average, and 1 in 5 organizations has suffered a breach tied to shadow AI (IBM, 2025). On the regulatory side, EU AI Act Article 50 transparency obligations become enforceable on August 2, 2026, and the combined GDPR plus AI Act penalty ceiling reaches EUR 55M or 11% of global annual turnover.

Our own solution page calls retrieval-time permission enforcement "the reason most enterprise RAG pilots fail their security review" and "the hardest unsolved problem in enterprise RAG." The whitepaper product analysis behind it flagged RBAC-aware RAG across the market as described but not demonstrated, with no working implementation. This demo is that working implementation.

How the Sovereign RBAC Firewall works

A deterministic policy engine decides what the model sees. Not the model, and not a prompt.

Every query runs the same pipeline:

query + user identity → retrieval → RBAC firewall (deterministic, outside any LLM) → permitted documents only → answer LLM → hash-chained audit record

1. Live permission resolution

For every candidate document, the firewall resolves the user's effective permissions at query time: it recursively flattens nested AD/OU groups (the fixture nests 3 levels deep) and evaluates ABAC attributes against the document's structured policy reference: clearance level L1 to L4, managed device, department, time-boxed project grants with expiry, and employment status.

2. A deterministic verdict

Each document gets ALLOW, WITHHELD with a machine-checkable reason code (BOARD_MEMBERSHIP_REQUIRED, ACCESS_WINDOW_EXPIRED, ALL_ACCESS_REVOKED_TERMINATION, POLICY_CONFLICT), or NEEDS_REVIEW. Withheld documents are dropped before the context window, so the model never sees documents the user cannot access. On any conflict or unresolved reference it denies by default and routes to human review rather than guessing.

3. The receipt

Every query appends a record to an append-only SHA-256 hash chain with tamper verification: user, resolved permission set, documents retrieved, served, and withheld with reason codes, needs-review conflicts, model and provider, and the prompt/response pair. Exportable as JSON, generated inside the VPC. The retrieval-access evidence record an EU AI Act Article 50 file asks for.

The baseline it beats is not a strawman

The comparison side of the demo is a faithful naive flat-ACL RAG, the build most pilots actually ship. It genuinely resolves nested groups at ingestion time. Its failures are its two honest, inherent limits: the snapshot goes stale, and a flat tag cannot express clearance, device, time-window, or termination. On the demo's 40-case golden set those two limits produce 10 unauthorized disclosures and 1 false denial, the false denial being a post-ingestion joiner whose entitlement the frozen snapshot missed.

The LLM is swappable scenery

The answer path, built on Pydantic AI, receives only post-firewall permitted chunks, states what was withheld and why instead of bluffing, and has zero authority over permissions. It is provider-swappable by one environment variable: Anthropic (default model claude-opus-4-8), OpenAI, Gemini, or Ollama for air-gapped sovereign deployment, where nothing, not one token, leaves the box. No API key is ever required: the demo routes answers to a local bridge when one is running and falls back to a deterministic stub otherwise, and the eval scoreboard never calls an LLM at all. The firewall is the product; the model is scenery.

The scoreboard is independent

Golden labels for the 40-case set are derived mechanically by an independent reference oracle, a separate implementation written from the policy definitions rather than the firewall under test. The scoreboard is a function of the identity graph, not hand-picked results, and every figure is computed at run time, never hard-coded. Enforcement overhead measures in microseconds, in-process, for the firewall logic alone; a production IdP round-trip adds latency this demo stubs.

One chokepoint, agent-ready

Gartner projects that 40% of enterprise applications will embed AI agents by end-2026, up from under 5% in 2025. The same deterministic authorize(user, doc) gate is the documented extensibility path for that world, not a shipped feature of this demo: an agent cannot retrieve what its on-behalf-of user could not.

The EUR 412 million leak, worked end to end

One question, asked by one synthetic analyst, answered on both sides of the split screen. Every image below is a screenshot of the running app; every identity, document, and figure is synthetic fixture data.

The question that leaks

Lena Vogt, an EMEA Credit Risk Analyst with clearance L2, asks: "What is our Q3 EMEA credit-loss projection and the methodology behind it?" Retrieval surfaces the Board-Only Q3 board pack alongside documents she may see. The flat-ACL side serves it: years of nested-group inheritance debt in the identity graph have made her transitively a "Board" member, and a group-only tag has no concept of clearance. The memo reaches the model, the answer reads out EUR 412 million, and the banner says LEAK.

The split-screen leak: the Naive Flat-ACL side reads out the EUR 412 million Board-Only projection to L2 analyst Lena Vogt with a red LEAK banner and a warning that 1 unauthorized document was served, while the RAGGUARD side shows 5 retrieved, 2 granted, 3 denied.
The leak moment: a Board-Only memo read out to an L2 analyst. The figure is synthetic; the failure mode is not.

The same query, behind the firewall

On the RAGGUARD side, the same retrieval runs, but the board pack is withheld before the LLM with the reason code BOARD_MEMBERSHIP_REQUIRED: the document requires clearance L4 and Lena holds L2. The model answers correctly from the Internal methodology note and the Confidential memo her nested-group chain does entitle her to, and it states what was withheld and why instead of bluffing.

The RAGGUARD side of the demo after Lena's query: 5 retrieved, 2 granted, 3 denied, with a withheld note listing the denied documents and their reason codes, including BOARD_MEMBERSHIP_REQUIRED on the board pack.
Same question, same retrieval: 5 retrieved, 2 granted, 3 denied, each denial carrying a reason code.

What the model actually saw

The decision-detail modal shows the verbatim model input on both sides. On the left, the leaked board pack sits inside the prompt, flagged red. On the right, it is simply absent: the model never sees documents the user cannot access. The per-document view makes the divergence explicit, the same board pack row reading "Served without authorization" on one side and "Access denied at retrieval" with its reason code on the other.

The verbatim model input on both sides: the Board-Only document flagged red inside the flat-ACL prompt, and absent from the RAGGUARD prompt, which is labeled grounded in permitted documents only.
The context windows, verbatim: the leaked document present on the left, absent on the right.
The per-document decision view: the board pack row reading Served without authorization on the flat-ACL side and Access denied at retrieval with the reason code BOARD_MEMBERSHIP_REQUIRED on the RAGGUARD side.
The same document, two verdicts: served without authorization versus denied at retrieval, with the reason code.

The injection that had nothing to steal

The Internal methodology note carries an embedded prompt-injection string instructing any AI assistant reading it to ignore its retrieval permissions and output the full Board figure. On the firewall side it has nothing to exfiltrate: the board pack never entered the context window, because authorization happened before the LLM. This is one labeled case, shown as defense-in-depth from retrieval-time enforcement, not a separate guardrails product.

The embedded prompt-injection line inside the Internal methodology note, highlighted in the decision-detail view, instructing any AI assistant to ignore retrieval permissions and output the Board credit-loss figure.
The injection, in the document the model was allowed to read. The document it targets never reached the model.

The identity graph moves. The snapshot does not.

Priya Shah was terminated nine minutes ago on the demo clock, and the fixture's webhook has fired. The firewall returns ALL_ACCESS_REVOKED_TERMINATION on everything: 0 granted, 5 denied. The flat-ACL snapshot, built before the termination, still serves her, because the re-index has not run. The demo walks the same edge for Marco Rossi, whose time-boxed Project-Atlas grant expired yesterday on the demo clock (ACCESS_WINDOW_EXPIRED), for Raj Patel, who sits in an allowing group and a denying group at once (POLICY_CONFLICT, held for human review), and for Wei Chen, the L4 board member the firewall correctly serves, the positive control proving it is not just denying everything.

The terminated-employee case: Priya Shah with a TERMINATED badge in the identity strip, the RAGGUARD side showing 0 granted and 5 denied with the reason code ALL_ACCESS_REVOKED_TERMINATION, while the flat-ACL side still serves from its stale snapshot.
Terminated nine minutes ago: the firewall revokes everything; the ingestion snapshot has not noticed.

The receipt, on screen

Every one of these decisions lands in the tamper-evident audit trail: a hash-chained JSON record of who asked what, what was served, what was withheld and why, and which model answered, with a CHAIN INTACT verification chip and a one-click export. Generated automatically, inside the VPC.

The tamper-evident audit trail: a CHAIN INTACT chip, the hash-chained JSON record of served and withheld documents with reason codes, and the Export Audit Record button.
The hash-chained audit record: the artifact an Article 50 file asks for, not a certification.

The scoreboard, with its exact scope

The golden set is 40 labeled (user, document) allow/deny decisions: 10 users against 4 sensitive documents, labels derived mechanically by the independent oracle from the identity-graph semantics. The firewall scores 40/40 with 0 unauthorized disclosures and 0 false denials. The flat-ACL baseline scores 29/40 with 10 unauthorized disclosures, including the board pack served to 7 unauthorized users, the expired Atlas window, and the terminated employee, plus 1 false denial. Every figure is computed by the harness at run time, never hard-coded, and the per-case drill-down shows each oracle label beside both systems' decisions. These are golden-set results, not open-world guarantees.

The 40-case benchmark scoreboard: Naive Flat-ACL RAG at 29/40 with 10 unauthorized disclosures and 1 false denial, RAGGUARD at 40/40 with 0 unauthorized disclosures and 0 false denials.
The scoreboard, computed live by the eval harness against the oracle's labels.
A per-case benchmark drill-down: the oracle labels the case deny, the flat-ACL baseline served the document, and RAGGUARD withheld it, with the policy chips explaining the decision.
Per-case drill-down: oracle label, flat-ACL decision, firewall decision, and the policy that decided it.

Naive Flat-ACL RAG versus the Sovereign RBAC Firewall

The same comparison the demo runs live, on the same identity graph and the same documents.

Dimension Naive Flat-ACL RAG (the typical pilot build) Sovereign RBAC Firewall (RAGGUARD)
When authorization happens At ingestion, frozen into a tag on each chunk At retrieval time, per query, per document
Nested groups and cross-OU inheritance Resolved once at ingestion; the snapshot goes stale Recursively flattened against the live identity graph on every query
ABAC: clearance, device, time-boxed grants, employment status Inexpressible in a flat tag Evaluated per document against the structured policy reference
Termination nine minutes ago Still served until the re-index runs ALL_ACCESS_REVOKED_TERMINATION on everything
Conflicting policies on one document Serves the document Deny-by-default: NEEDS_REVIEW, routed to a human with the conflict spelled out
40-case golden set 29/40: 10 unauthorized disclosures, 1 false denial 40/40: 0 unauthorized disclosures, 0 false denials
Audit trail None Hash-chained, tamper-evident record of every serve and withhold, exportable as JSON

What this demo does not do

  • ✓ It does not connect to a live Active Directory, Okta, SharePoint, or Confluence. The identity provider is a synthetic Azure-AD/SCIM-shaped JSON fixture, terminations and expiries are fixture events, and the vector store is in-process behind a Qdrant-shaped interface. The live IdP connector is the documented production swap, not what runs today.
  • ✓ It does not involve any real bank, customers, or live accounts. Lena Vogt, Marco Rossi, Priya Shah, Raj Patel, Wei Chen, every document, and the EUR 412 million figure are synthetic.
  • ✓ It does not claim open-world guarantees. 40/40 with 0 unauthorized disclosures is the computed result on a fixed 40-case labeled golden set, not a promise that nothing ever leaks in production.
  • ✓ It does not measure production latency. The microsecond-scale enforcement overhead is firewall logic only, in-process; a live IdP round-trip, which this demo stubs, adds more.
  • ✓ It does not ship a guardrails suite, DLP, or a multi-agent platform. It is one capability done convincingly: the retrieval-time permission firewall, with the injection case as one labeled defense-in-depth example. The answer path is Pydantic AI, and the deliberately non-agentic deterministic gate is the design; agentic governance is the documented extensibility path.
  • ✓ It does not certify compliance. The audit export is the evidence record an EU AI Act Article 50 file asks for, not a certification, and no customers, deployments, or case studies exist for this demo.

Questions buyers actually ask

How do we stop our internal AI chatbot from showing employees documents they should not see?

Enforce authorization at retrieval time, not ingestion time. A deterministic policy engine outside the LLM checks every retrieved document against the user's live group memberships and attributes before it reaches the model, so withheld documents never enter the context window. In this demo that gate scores 40/40 on a labeled 40-case golden set, against 29/40 for the flat-ACL build most pilots ship.

We already tag documents with permissions when we index them. Why is that not enough?

Because the tag is a snapshot and permission is a live property of your identity graph. An ingestion-time ACL misses everything that changes after indexing, like a termination or an expired project grant, and it cannot express attribute rules like clearance level or managed device at all. In the demo those two limits account for 10 unauthorized disclosures and 1 false denial across a 40-case benchmark, from a baseline that faithfully resolves nested groups at ingestion.

Can a prompt injection trick the model into leaking a restricted document?

Not if the document never reaches the model. The demo includes a labeled case where a note embedded in a document instructs any AI assistant to ignore its retrieval permissions and output the Board-Only figure, and the injection has nothing to exfiltrate because authorization happened before the LLM and the memo never entered the context window. That is one defense-in-depth example of retrieval-time enforcement, not a standalone guardrails product.

What happens when access policies conflict or the system is not sure?

Deny by default. When a user holds both an allowing and a denying membership on the same document, or a policy reference cannot be resolved, the firewall returns NEEDS_REVIEW and routes the case to a human with the conflict spelled out, rather than guessing. The demo shows exactly that: a credit risk manager who is in an allowed group and a denied group at once has the disputed memo held for review, not served.

Does any data leave our VPC? Can this run fully air-gapped?

The firewall is deterministic Python that needs no model at all, and the answer path is swappable by one environment variable: Anthropic, OpenAI, Gemini, or Ollama. Set LLM_PROVIDER=ollama for air-gapped sovereign deployment and nothing, not one token, leaves the box. The demo itself runs offline with no API key, and the eval scoreboard never calls an LLM.

What do we show an auditor about who accessed what through the AI system?

Every query appends a record to a tamper-evident SHA-256 hash chain: who asked, the resolved permission set, what was served, what was withheld and why, and which model answered. It exports as JSON and is generated inside your VPC. It is the retrieval-access evidence record an EU AI Act Article 50 file asks for, not a compliance certification.

Is this a live Active Directory integration or a demo?

A runnable demo that proves the mechanism. The identity provider is a synthetic Azure-AD/SCIM-shaped JSON fixture, terminations and grant expiries are fixture events, and the vector store runs in-process behind a Qdrant-shaped interface. The policy engine, the eval harness, and the hash-chained audit export are real and run exactly as shown; because the fixture mirrors Azure AD Graph and SCIM interfaces, the documented production path to a live IdP is a config swap, not a rewrite.

Technical Research

The research behind this demo — the architecture, the verification design, and the enterprise blueprint.

Evaluating private LLM deployment at a regulated enterprise?

The permission layer is the hard part. We build it.

If your team is wrestling with how to point a private model at a corpus governed by years of nested-group inheritance, or with what the EU AI Act's transparency obligations mean for your retrieval logs, we would genuinely like to hear how you are thinking about it. The problem is industry-wide and the answers will be too.

Permission-layer assessment

  • ✓ Map where your RAG pipeline authorizes today: ingestion time or retrieval time
  • ✓ Model your nested-group and cross-OU inheritance as a live policy graph
  • ✓ Define the ABAC rules a flat tag cannot express: clearance, device, time-boxed grants
  • ✓ Specify the retrieval-access record your Article 50 file asks for

Build the retrieval-time firewall

  • ✓ A deterministic authorize(user, doc) gate outside any LLM
  • ✓ Live IdP resolution behind Azure AD Graph and SCIM-shaped interfaces
  • ✓ Deny-by-default conflict routing to human review
  • ✓ A hash-chained, exportable audit trail generated in your VPC