Here is how an AI reporting rollout becomes an HR incident. For years your permissions worked, and they worked in two places at once: the BI tool decided who saw which dashboards, and the data layer decided who could read which rows, both driven off the same metadata about who has access to what. A branch manager opened the revenue dashboard and saw their branch, every time, without anyone thinking about it. Then a new AI tool arrives. It connects to the warehouse through its own service account, drafts a genuinely good report, and mails it to forty managers. Every one of them sees every branch, because the tool never consulted the metadata both permission layers were built on. The query was correct. The audience was wrong. And the person who notices is not an engineer reading logs. It is a manager reading a colleague's numbers.
This is the third piece in a series about putting AI on top of the data you already have. The first argued that the reporting queue is dying. The second covered what the model may read and what its query may do, and ended on a promise: who gets to see the result deserves its own article. Before Dittah I led enterprise reporting and then data strategy, and in every shop I ran, entitlements worked exactly the way I just described. Metadata-driven, enforced at two layers, and invisible right up until something bypassed them. AI does not have to be that something.
The reassuring headline comes first, because it is the part most coverage of "AI and permissions" gets wrong: if AI stays at config time, almost nothing about your permissions has to change. This article walks through why, and through the parts that do need attention: the two identities in every workflow, what restriction means when a model is involved, and the audit trail, which now has two halves. Key takeaways at the end, as usual.
Two identities, not one
Every AI workflow has two people in it, and most permission mistakes come from designing for only one of them.
The builder, at design time. The builder's permissions scope what the model may see. When an analyst describes a report and the platform assembles the drafting context, the catalog checks the analyst's own access against the metadata, the same entries that already say who may see what, and offers the model only what that person is cleared for. An analyst who cannot read the salary column cannot build a workflow that leaks salaries, and not because a filter catches it on the way out. The model drafting on their behalf never learned the column exists. Least privilege applies to the machine because it applies to the person driving it.
The viewer, at run time. The viewer's permissions scope what the frozen query returns. The drafted query carries an entitlement predicate, the reviewer confirms it, and the execution engine binds the viewer's identity on every run. One frozen report serves the whole company: a branch manager gets 40 rows, the group CFO gets 4,000, from the same code, on the same schedule. That is not a workaround. It is the design, and it is what the diagram at the top of this page shows lane by lane.
Almost nothing changes at runtime
Now the claim that makes this article worth a CIO's time. The industry panic about AI and access control is really about one architecture: the model at run time. If a model writes a fresh query for every question, you have to re-solve security for every answer. Did the improvised join respect row-level security? Did the prompt leak data from a table this user cannot see? Is the answer cached somewhere a different user can reach? Every response becomes its own small security review, forever, and that is why those deployments stall in committee.
Freeze the code and the problem collapses back into one your database team solved years ago. At run time there is no model. The thing touching your data layer is plain, reviewed code, executing the way reports have executed for two decades, under the same row-level security, the same roles, the same masking policies your DBAs already maintain. AI participates at config time only: it drafts the workflow, a person reviews and freezes it, and from then on runtime is the runtime you have always governed. Your permission model does not need to be reinvented for AI. It needs to not be bypassed by AI.
Which brings up the one real trap. This guarantee holds only if the frozen code executes with the viewer's identity in hand. Plenty of older reporting stacks ran everything through a single all-powerful service account and enforced visibility only in the BI tool. Recreate that pattern with an AI-built workflow and the data layer never gets its say, which is exactly the failure in the opening story. So the rule has two clauses: runtime unchanged, and no bypass. The viewer's identity flows through to the data layer on every run, so both of your historical enforcement points stay live.
The mechanisms, driven off the same metadata
None of the enforcement machinery is new, which is the point. What changes is where the policy lives and who reads it.
| Mechanism | What it protects | How the frozen workflow enforces it |
|---|---|---|
| Row-level security | Which rows a viewer sees | The query carries WHERE region = :viewer_region, and the engine binds the viewer's identity on every run |
| Column masking | Which fields a viewer sees | The same report returns salary_band to an analyst and salary to HR, decided by policy, not by maintaining two copies |
| Tenant isolation | One customer's data from another's | A wall, not a WHERE clause: the connection itself is scoped to the tenant, so no query can cross it, however it is written |
The policy lives in the catalog, next to the PII tags and the metric definitions from the previous article. That placement does double work. People and BI tools keep reading it, as they always did. And the AI reads it at design time, so drafted queries arrive already carrying the right predicates instead of having them bolted on in review. The pre-freeze validator adds the guarantee on top: a query that touches a table with no entitlement policy does not reach review at all. The reviewer confirms the predicate is right. The machine guarantees one exists.
Restriction: what never enters the context
Access says what you may see. Restriction says what does not exist for you, and with a model in the loop it deserves its own discipline, because the cheapest data to protect is data that never entered the context window.
A restricted dataset in this design is not "visible but blocked." It is invisible. It is not offered to the model, it appears in no schema listing, no sample, no crosswalk, and a draft cannot reference it, because from the model's point of view it was never there. Deny by default does the heavy lifting: an unclassified table, an un-policied column, a dataset without an owner, none of it is exposable until someone makes a deliberate decision. The builder's own clearance caps the ceiling, and the sample masking covered last time handles what does pass through.
Restriction also has to follow the data out the far side. A masked column stays masked in the PDF, the spreadsheet export, and the API response, because the delivery step renders from the entitlement-filtered result, not from a privileged copy. A report inherits the viewer's entitlements in every format it is delivered in, or the restriction is theater.
Audit: two trails now, not one
The first trail is the one you already run. Every execution logs who asked, which code version ran, what parameters were bound, and what came back, down to row counts. When an examiner asks who saw a given customer's data last quarter, the answer comes out of a log query, not out of interviews. We walked that machinery end to end in a regulated report, automated end to end, and it is the audit half that deterministic runtime gives you almost for free, because the same input always produces the same, explainable output.
The second trail is genuinely new, and it exists because there is now a model to account for. Who built this workflow. What schema, metadata, and masked samples entered the drafting context. What the model proposed, what the reviewer changed, and who approved the freeze. That is construction lineage, and the old world never had it, because construction lived in an analyst's head and a stack of undocumented decisions. When a regulator asks how a report was built and who authorized it, this trail answers with a record instead of a reconstruction. Our compliance checklist covers where each regime asks for exactly this.
When entitlements live in many places
The honest complication: your warehouse has row-level security, your SaaS platforms have their own role models, and the file share has ACLs from another era, and they do not agree with each other. The workable design is one entitlement model in the platform, mapped to each source, with the frozen workflow enforcing the intersection: a viewer sees a row only if every source involved would have shown it to them. What I will not pretend is that the mapping is easy. Translating one system's idea of a role into another's is slow, political, unglamorous work, and it is where multi-source projects actually sink. It deserves its own article, and it will get one.
The lookup that never names the customer
The previous article deferred one scenario, and it belongs here because it is where access, restriction, and audit meet in a single request: someone asks an ad-hoc question about one specific person. "Show me transactions for Priya Sharma." The name is PII, and it must not enter a prompt. The answer is a token vault. The identity is swapped for a token before any model sees the request, the model drafts against a placeholder, and resolution happens inside your boundary at execution, where the engine also asks the entitlement question: is this viewer allowed to see this customer at all? The resolution and the viewer both land in the audit log.
Where to start, without a two-year program
As with curation, this is discovery work more than construction work, because most of what you need already exists. It just is not wired to the AI path yet.
- Inventory where entitlements are enforced today: BI layer, data layer, or both, and which metadata drives each. That metadata is the asset everything else builds on.
- Pick the single source of truth for viewer identity, usually your identity provider, and make the execution engine carry it through to the data layer on every run. Retire the god-mode service account, or at minimum take it off anything AI-built.
- Move access policies into the catalog next to your PII tags and metric definitions, so one set of metadata governs people, BI tools, and the drafting model alike.
- Make un-policied data invisible: no entitlement policy means no exposure to the model and no freeze. Deny by default only works if the default is real.
- Turn on both audit trails and rehearse the examiner's question against them: who saw customer X's data last quarter, and who approved the workflow that showed it to them?
Key takeaways
- Two identities, two scopes. The builder's access decides what the model may see at design time. The viewer's access decides what the report returns at run time. Design for both or leak through one.
- Runtime permissions do not change. With AI at config time and deterministic code at run time, the thing touching your data is plain code under the same row-level security and roles you already govern. The panic belongs to runtime-model architectures, which re-solve access control per answer.
- The rule has two clauses. Runtime unchanged, and no bypass: the viewer's identity must flow through to the data layer. A god-mode service account silently disables the enforcement you are counting on.
- Metadata was always the driver. The same who-has-access-to-what metadata that ran your BI and data layers now also scopes the model's context and the drafted predicates. One policy, read by people, code, and AI.
- Restriction means never in the context. Restricted data is not blocked on the way out. It never enters the model's world, and masking follows the data into every delivery format.
- Audit now has two trails. Who saw what at run time, and who built and approved what at design time. The second trail is new, and it is the one that turns "how was this report constructed" into a record instead of a reconstruction.
The quiet conclusion is that entitlements are the strongest argument for the whole design-time architecture. Every mechanism in this article, the row-level security, the masking, the metadata, already existed in your stack and already had owners. Keeping AI at config time means all of it keeps working, unchanged, with one new reader of the same old metadata. Letting a model loose at run time means rebuilding all of it, per answer, forever. That is not a close call.
If you want to see viewer-scoped results from a frozen workflow on your own data, build your first workflow, or download the free Community edition and run it inside your own network, against your own permissions.