PATTERN Cited by 1 source
Three-layer agent control (permissions / policies / guardrails)¶
Pattern¶
Compose three non-overlapping runtime control surfaces between an agent and the resources it acts on, each keyed on a different decision input, each with a different granularity:
- Permissions — keyed on identity. Who can call what. Granularity: per-user-per-resource. Mechanism: data permissions inherited via OBO token passing. Failure mode if absent: agent acts with shared service-account credentials, blast radius unbounded.
- Per-call policies — keyed on tool name + args + caller identity. Whether this specific tool call should proceed in the context of this request. Granularity: per-tool-call. Mechanism: Service Policies as UC functions attached to MCP servers, returning allow/deny/consent. Failure mode if absent: any authorised caller can invoke any tool with any args.
- Content guardrails — keyed on request/response payload. What content flows in and out. Granularity: per-request payload. Mechanism: Guardrails inline on the inference path scanning inputs (PII, jailbreak) and outputs (hallucination, sensitive content), fail-closed. Failure mode if absent: authorised tool calls leak unsafe content.
All three layers run; none subsumes the others.
Canonical statement on the wiki¶
"In practice, these three layers work together: permissions control who can call what. Service Policies control whether a specific tool call should proceed in the context of a given request. Guardrails control what content flows in and out." — Source: sources/2026-05-20-databricks-governing-ai-agents-at-scale-with-unity-catalog
Why three layers (and not one or two)¶
The post's structural argument: each layer's decision input is strictly weaker than what's needed to make the runtime decision the next layer makes:
- Permissions ignore runtime context. "Knowing that an agent is allowed to call GitHub doesn't tell you whether it should delete a file or merge a pull request." Per-call policies fill the gap.
- Per-call policies ignore content. A Service Policy approves a tool call based on the synthesised tool name + args; it can't see what the LLM is about to generate in the response, or whether the prompt contains adversarial content. Content guardrails fill the gap.
- Content guardrails ignore identity and tool. The classifier sees a payload and decides if it's safe; it doesn't know which user is calling, which tool is being invoked, or what the policy says.
Composing all three is the cheapest way to cover the union of failure modes that any single layer leaves open.
Architectural shape¶
agent request
│
▼
┌──────────────────────────────────────────────────┐
│ Layer 1: Permissions (OBO) │
│ identity flows end-to-end → UC ABAC │
│ if user can't access table, agent can't either │
└──────────────────────────────────────────────────┘
│ allowed
▼
┌──────────────────────────────────────────────────┐
│ Layer 2: Service Policies │
│ UC function bound to MCP │
│ evaluates(tool_name, args, identity) │
│ → allow │ deny │ consent │
│ fail-closed on deny │
└──────────────────────────────────────────────────┘
│ allowed
▼
┌──────────────────────────────────────────────────┐
│ Layer 3: Inline content guardrails │
│ scan input: PII, jailbreak │
│ scan output: hallucination, sensitive content │
│ inline, per-request, fail-closed │
└──────────────────────────────────────────────────┘
│ passes
▼
to user
Every layer audit-logs its decision; rejected requests are recorded as such (per audit-trail).
Decision-input matrix¶
| Layer | Sees identity | Sees resource | Sees tool name + args | Sees request body | Sees response body |
|---|---|---|---|---|---|
| Permissions | ✓ | ✓ | — | — | — |
| Per-call policies | ✓ | ✓ | ✓ | (partially — synthesised args) | — |
| Content guardrails | — | — | — | ✓ | ✓ |
The complementary structure: layer 3 sees what layers 1+2 don't (content), layers 1+2 see what layer 3 doesn't (identity, resource, tool semantics). The composition is defense-in-depth via decision-input diversity.
Concrete instance (Databricks)¶
Per sources/2026-05-20-databricks-governing-ai-agents-at-scale-with-unity-catalog:
| Layer | Mechanism | System |
|---|---|---|
| 1. Permissions | OBO token passing into UC tables | systems/unity-catalog + patterns/on-behalf-of-agent-authorization |
| 2. Per-call policies | UC functions attached to registered MCPs | systems/uc-service-policies |
| 3. Content guardrails | Inline gateway-side scanning, fail-closed | systems/unity-ai-gateway-guardrails |
All three layers are mediated by Unity AI Gateway as the central proxy choke point. The composition only works if the gateway is the only path — agents that bypass the gateway escape all three layers.
Sibling shapes¶
- Single-layer permissions only. The pre-agent baseline: identity-based access control through enterprise IAM. Insufficient for agents because runtime tool-call context is invisible to identity policy.
- Dynamic content filtering in MCP pipeline (Redpanda Connect, 2025-04-03) — collapses layers 2 + 3 into one programmable policy point at the MCP boundary using Bloblang / Starlark / Python extensions. The trade-off: more programming-language flexibility, less explicit separation between tool-call admission and content scanning.
- Two-layer (permissions + content), no per-call policy. Common in 2024-2025 agent products that bolted Bedrock-Guardrails-style content filtering onto IAM. Misses the runtime-tool-context layer the post argues is necessary.
Generalisation¶
The pattern doesn't require Databricks substrates. The structural property is:
Any three control surfaces with non-overlapping decision inputs at identity / tool-call / content granularity, each fail-closed, each mediated by a single gateway.
A non-Databricks shape could substitute:
- IAM provider (any) for layer 1.
- OPA / custom policy engine for layer 2.
- Bedrock Guardrails / Azure Content Safety / custom classifier for layer 3.
The pattern is the composition discipline, not the specific tools.
When to use this pattern¶
- Enterprise agent deployment with mixed-trust agents (developer-built, vendor-supplied, third-party-integrated) calling shared resources.
- Regulatory environments where content-leakage (Pillar 3) is a separate compliance concern from access-control (Pillar 1).
- Multi-framework agent populations where governance must be framework-agnostic (governance travels with resources).
When this pattern is overkill¶
- Single-team agent deployment with a small known tool surface.
- Internal-only agents in a trusted environment where the cost of three control layers exceeds the threat-model.
- Demo / prototype agents where the audit substrate doesn't yet exist.
Seen in¶
- sources/2026-05-20-databricks-governing-ai-agents-at-scale-with-unity-catalog — canonical first wiki disclosure (2026-05-20), framed as Pillar 1 of the four pillars.
Source¶
- Originating post: https://www.databricks.com/blog/governing-ai-agents-scale-unity-catalog
Related¶
- patterns/on-behalf-of-agent-authorization — Layer 1 mechanism.
- patterns/policy-as-uc-function-attached-to-mcp — Layer 2 mechanism.
- concepts/inline-llm-content-guardrail — Layer 3 concept.
- patterns/central-proxy-choke-point — the discipline that makes all three layers reachable.
- concepts/four-pillars-of-agent-governance — Pillar 1 names this pattern.
- concepts/governed-agent-data-access — sibling Redpanda framing.
- patterns/dynamic-content-filtering-in-mcp-pipeline — sibling pattern collapsing layers 2 + 3.