Skip to main content
Goal: for the scopes the warehouse can’t enforce (embedded users, sources without per-member auth, narrowing beyond the floor), governed .tql surfaces that fail closed — no scope, no rows.

3.1 · The anatomy of a guard

A guard is a governed query surface that requires a trusted scope and refuses to run without it. The scope arrives as a runtime client attribute — set by a trusted backend (an embedded app’s server, an API integration), never typed by the end user:
Prompt
You’ll see: a query surface whose WHERE clause is built from the required scope, with an explicit error path when the scope is absent. That error path is the point: fail closed means the failure mode of a broken integration is “no data,” never “all data.”

3.2 · The rules that keep guards trustworthy

  • Scope comes from a trusted backend — a server your team controls sets the attribute. Anything a browser or end user can set is a preference, not a boundary.
  • Never expose the entitlement field as a caller-controlled override — no “optional region parameter” that quietly widens scope.
  • Centralize shared guards — one reviewed module that other surfaces import, not a copy-pasted WHERE clause per file (copies drift; one of them will be wrong).
  • Interactive members are scoped differently — for humans in chat, the boundary is their warehouse identity (Module 1) or a role-scoped connector; guards + client attributes are the pattern for embedded and API traffic. Don’t build a client-attribute guard and assume it scopes your analysts.

3.3 · Wire it to the personas

Point each persona’s behavioral context (the Context Stack personas) at exactly these governed surfaces — the persona’s “allowed data surfaces” list and the enforced surface set should be the same list. Persona says should, guard says can, and they agree.

✅ Checkpoint

  • At least one fail-closed guard exists as governed .tql, and you inspected its rendered SQL
  • Removing the scope attribute produces an error, not unscoped rows — tested
  • Shared guard logic lives in one reviewed module, not copies
  • You can say which traffic guards protect (embedded/API) vs what protects interactive members