Diagnostics
The compile-time authz / authn analyzer. Reads the lowered policy IR and flags empty or shadowed permissions, unreachable roles and views, capability-ceiling breaches, the not-over-resource null-trap, and pushdown cost — warn-by-default so upgrades never break, with a QUICKBACK_AUTHZ_STRICT opt-in that turns every warning into a hard compile error.
Quickback compiles your authorization model into a single normalized policy IR — relations, permissions, arrows, scopes, grants, and per-resource firewalls — and then runs a set of static checks over it. The analyzer never changes what's emitted; it reports problems it can prove from the policy graph: a permission that can never be satisfied, a scoped role no grant reaches, a resource that hands a scope role more than its profile allows, a negation that can silently pass.
The console channel
Diagnostics render to the existing [quickback:authz] / [quickback:authn]
console channel — the same prefix the compiler has always used for security
warnings, so any CI grep you already key on keeps working. Each line carries
a stable, greppable code:
[quickback:authz] AUTHZ_EMPTY_PERMISSION: permission "event:locked" can never be satisfied — it requires organizerOf AND its negation simultaneously.
at resource=sessions authz.permissions["event:locked"]
hint: Remove the contradictory arm, or split into separate permissions.The first line stays greppable ([quickback:<channel>] CODE: message); the
source location and fix hint render on continuation lines.
Warn by default, opt-in strict
The analyzer is warn-by-default, compile-continues. This is deliberate:
the analyzer runs on the shared compiler that serves every project, so a new
check must never break an existing build on upgrade. In the default path
every diagnostic — including the one check authored at error severity
(the capability ceiling) — is rendered as a warning line and the compile
proceeds.
Set QUICKBACK_AUTHZ_STRICT=true to promote every warn to error.
Under strict mode the compile fails (throws an aggregate error listing the
codes) if any diagnostic is — after promotion — an error. info lines
(the pushdown-cost report) stay informational and never fail a build.
# warn-only (default): prints diagnostics, compile succeeds
quickback compile
# strict: any warn or error fails the compile
QUICKBACK_AUTHZ_STRICT=true quickback compileThe analyzer is compile-time only — zero runtime blast radius. A defect in the analyzer itself is caught and downgraded to a single warning in the default path, so an analyzer bug can never take down an otherwise-valid compile. (Strict mode re-raises, because there the throw is the intended hard failure.)
Authz codes
These read the permission graph projected from the policy IR. All are warn
severity (so info for the pushdown report) except the capability ceiling,
which is authored as error.
| Code | Severity | What it catches |
|---|---|---|
AUTHZ_EMPTY_PERMISSION | warn | A permission that can never be satisfied — e.g. allOf(a, not(a)), which requires a fact and its negation at the same AND-level. |
AUTHZ_SHADOWED_ARM | warn | An anyOf arm subsumed by a broader arm: a leaf X and an allOf(X, …) in the same union — the narrower arm can never widen the result. |
AUTHZ_UNREACHABLE_ROLE | warn | A scoped role declared under authz.scopes with no grant profile — it can authorize no read, action, or channel, so it's dead config. |
AUTHZ_UNREACHABLE_VIEW | warn | A grant's read names a resource:view whose resource has no firewall — no role can reach that view. |
AUTHZ_PUSHDOWN_COST | info | Per-permission report: how many SQL subqueries the permission lowers to, plus the max recursive-CTE depth (from recursive arrows). Advisory, never fails a build. |
AUTHZ_CAPABILITY_CEILING | error | A resource's firewall grants a scope:<kind>:<role> role read access beyond its capability profile — either the role has no profile at all, or the profile doesn't list this resource. |
AUTHZ_NOT_OVER_RESOURCE | warn | A not whose operand resolves to a resource (SQL) site — NOT EXISTS over a nullable join can silently pass (the null-trap). The analyzer resolves the operand's site from its leaves, so it fires even when the lowering left the site unset. |
A few codes are reserved in the diagnostic enum for the milestones that emit
them and don't fire on a shipped config today: AUTHZ_UNBOUNDED_RECURSION
(an unbounded recursive arrow with no claim path — currently a hard compile
error raised at config validation, before the analyzer runs),
AUTHZ_UNENFORCEABLE_FACT, and AUTHZ_CAVEAT_SITE_MISMATCH (M3 caveats).
The capability ceiling
The one error-severity authz check. It is the M0.2 deliverable that backs
scope grant profiles: a scoped role is
deny-by-default and may touch only what its grants profile lists. The
analyzer walks every resource firewall, follows each { permission } arm
into the permission graph to find the scope roles it references, and fails the
build (in strict mode) if a resource grants a scope role access the profile
doesn't cover. In the default path it warns loudly. This is what makes the
grant profile the single auditable manifest of what an external principal can
see.
Authn codes
Four structural authentication checks — proving properties over the generated route + credential surface rather than the authz policy:
| Code | Severity | What it catches |
|---|---|---|
AUTHN_ROUTE_COVERAGE | warn | A route that reaches data with an unclassified auth gate and isn't declared PUBLIC — the signup / flag-gap bug class (data delivered to an unauthenticated ctx). |
AUTHN_SECRET_CONSISTENCY | error | A credential's mint site and verify site don't share key / issuer / audience — every minted token would fail verification (the ws-ticket "401 on every upgrade" trap). |
AUTHN_CREDENTIAL_DELIVERY | error | A fire-and-forget credential send (the OTP-email shape) not anchored to waitUntil / await — the Worker may drop the promise and never deliver (the OTP-500 bug class). |
AUTHN_CLAIM_PROVENANCE | error | An identity ctx field written from request input rather than a credential verifier — client-asserted identity, which is forbidden. |
AUTHN_SURFACE_INVARIANT | warn / error | A surface contract breach: an /mcp or agent route not gated bearer-only (warn), or an INTERNAL-gated route mounted on an HTTP-reachable surface (error). |
These check definitions are wired and fully unit-tested, but their inputs
— the per-route and per-credential metadata (RouteNode / MintSite /
VerifySite / ClaimWrite) — are not yet emitted by the route, middleware,
and credential generators. Until those generators emit that metadata the
analyzer runs the authn checks against empty route/credential graphs, so
they are effectively no-ops on a real compile. The check logic needs no change
when the metadata lands — the route/credential coverage activates the moment
the generators emit it. The authz / permission-graph checks above run
unconditionally today, because the policy IR is always present.
Reading the pushdown-cost report
AUTHZ_PUSHDOWN_COST is an info line per permission that pushes down to
SQL — it never fails a build, but it surfaces the query cost of an
authorization rule before you ship it:
[quickback:authz] AUTHZ_PUSHDOWN_COST: permission "section:inTree" lowers to 1 SQL subquery, recursive CTE depth 6 (sites: resource).A high subquery count or a deep recursive CTE (from a recursive arrow) is a signal to reconsider the rule — not an error, but worth a look.
See also
- Permissions — the
anyOf/allOf/notDSL the empty / shadowed / not-over-resource checks analyze. - Arrows — recursive-CTE depth feeds the pushdown-cost report; the unbounded-recursion guard is a hard error.
- Scopes & capability grants — the grant profiles the capability-ceiling check enforces.
- Firewall — the
{ permission }arms the ceiling check follows into the permission graph.
Arrows
Traverse a foreign key to inherit authority from a parent object — "you can see this row because you administer the organization it belongs to", or "because you control an ancestor in the tree". Declared once under authz.arrows, referenced from a permission, lowered to an FK-hop subquery or a bounded WITH RECURSIVE CTE.
Access - Role & Condition-Based Access Control
Define who can perform read and write operations and under what conditions. Configure role-based and condition-based access control for your API endpoints.