CONCEPT Cited by 1 source
Security model is the data model¶
A design principle for AI data agents: the agent has no authority of its own — it runs as the calling user, inheriting exactly that user's permissions on the underlying data. There is no service-account elevation, no agent-side privilege grant, no "the agent can read more than the human can" shape. Permission decisions follow the calling identity, not the agent identity.
The principle is canonicalised by Cloudflare Skipper in the 2026-05-28 Town Lake / Skipper launch post.
Canonical statement¶
"Everything Skipper does runs as the calling user. If you don't have access to a table, Skipper can't query it for you. If you ask for PII, your permissions are checked. If a query you save is shared with a teammate, their access is checked at view time, not at save time, because group membership changes."
The shape encodes four properties:
- No agent authority — Skipper is a tool, not a principal. It can only do what the user could do directly.
- PII redaction follows the user — the opt-in PII redaction path is checked against the calling user's authorisation, not Skipper's.
- View-time permission checks for shared artifacts — when a saved query / dashboard is shared, the viewer's permissions are checked at the moment they view it, not the creator's at the moment they shared.
- Group membership changes are honoured — the post explicitly names this as the rationale for view-time checking. If an employee changes teams, their access changes immediately on every shared artifact, without anyone re-saving anything.
Why view-time, not save-time, permission checks¶
The naive design is "on save, materialise the result; serve the
materialised result to viewers." That's broken under group
membership change: if Alice saved a dashboard while in team-finance
and later moved to team-marketing, anyone she shared the
dashboard with would still see finance data — even after Alice
herself can no longer access the underlying tables.
The Skipper design instead defers the permission check to the moment the viewer opens the dashboard:
- The dashboard stores the query, not the result.
- On view, the viewer's identity is checked against the underlying tables.
- If they have access: query runs, current data returned.
- If they don't: the access-denied flow kicks in (see patterns/error-message-as-self-serve-permission-request).
This costs query execution per view, but eliminates entire classes of permission-leak bugs.
Sibling at Databricks: governance travels with resources¶
concepts/governance-travels-with-resources is the Databricks-side framing of the same shape — "governance follows the data, not the resource that creates it." The Skipper phrasing is more agent-specific (the agent's authority is the caller's), the Databricks phrasing is more catalog-centric (the ABAC tags + row filters travel with the data into any consumer). Both are expressions of the same architectural commitment: access decisions are made at the data, not at the agent / resource / dashboard / share link.
Why this matters for AI data agents specifically¶
Agents are tempting privilege-escalation surfaces. "The agent needs access to all the data so it can answer any question" is a seductive but disastrous shape — it makes the agent the universal authority, with all of its prompt-injection / hallucination / leakage risks now load-bearing for the organisation's data security.
The opposite design — agent runs as calling user — has two structural benefits:
- Bounded blast radius — a misuse of the agent can leak no more data than the user could have leaked manually.
- Audit clarity — "who saw this PII" maps to "which user ran a query that touched it", regardless of whether the access was direct SQL or via the agent.
Composes with the rest of Town Lake's governance¶
The agent-level "runs as the calling user" property only works if the underlying data platform exposes per-user access controls the agent can ride on. Town Lake's Lifeguard is the substrate that makes this real:
- Lifeguard renders a per-user JSON policy that Trino reads.
- Skipper queries Lifeguard before generating SQL (front-door check).
- Skipper's generated SQL runs through Trino with the same per-user policy.
- The agent has no Lifeguard policy of its own — only callers do.
The combination is the architectural shape: default-closed governance + per-user access policy + agent-runs-as-user = the agent inherits exactly the same authority surface as a direct SQL user, no more, no less.
Sibling pattern: on-behalf-of agent authorisation¶
patterns/on-behalf-of-agent-authorization is the Databricks- side pattern — the agent acts "on behalf of" the user via delegation tokens. The Skipper / Town Lake shape is stronger: not just on-behalf-of, but identical-to the calling user. There is no agent identity in the policy at all.
Seen in¶
- sources/2026-05-28-cloudflare-how-we-built-cloudflares-data-platform-and-an-ai-agent-on-top-of-it — canonical wiki instance. The "everything Skipper does runs as the calling user" statement and the view-time-permission- checking rationale.
Related¶
- systems/cloudflare-skipper — canonical wiki agent.
- systems/cloudflare-town-lake — the platform.
- systems/cloudflare-lifeguard — the per-user policy substrate.
- concepts/governed-agent-data-access — sibling Databricks framing.
- concepts/default-closed-table-allowlist — the broader governance posture.
- concepts/opt-in-pii-redaction-per-session — composes with this concept on the column-level access.
- concepts/audit-trail — the accountability substrate.
- concepts/governance-travels-with-resources — sibling architectural commitment.
- patterns/on-behalf-of-agent-authorization — sibling pattern from Databricks.