Quickback Docs

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. Zero-false-positive checks fail the compile (v0.47+) with an explicit authz.analyzer.allow override surface; heuristic checks warn, with a QUICKBACK_AUTHZ_STRICT opt-in that promotes warnings too.

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.
  override: authz: { analyzer: { allow: [{ check: 'AUTHZ_EMPTY_PERMISSION', target: 'event:locked', reason: '<why this is acceptable>' }] } }

The first line stays greppable ([quickback:<channel>] CODE: message); the source location, fix hint, and — for promoted errors — the exact override recipe render on continuation lines.

Errors vs warnings (v0.47+)

The checks split into two tiers:

  • Promoted (compile errors). A trip is a definite misconfiguration — never a legitimate config. These fail the build by default. Each one can be explicitly accepted per finding via authz.analyzer.allow.
  • Heuristic (warnings) and reports (info). Advisory findings where a legitimate config could trip the detector. These print and the compile continues. Set QUICKBACK_AUTHZ_STRICT=true to promote these warnings to errors as well (overridden findings stay warnings even under strict — an override is an explicit, reasoned acceptance).

The analyzer remains compile-time only — zero runtime blast radius. A defect in the analyzer itself is caught and downgraded to a single warning, so an analyzer bug can never take down an otherwise-valid compile. Only real findings fail builds.

Authz codes

These read the permission graph projected from the policy IR and run on every compile.

CodeTierWhat it catches
AUTHZ_EMPTY_PERMISSIONerrorA permission that can never be satisfied — allOf(a, not(a)), a fact and its exact negation at the same AND-level. The detector is conservative (exact-leaf contradictions only), so a trip is structurally unsatisfiable.
AUTHZ_CAPABILITY_CEILINGerrorA 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. The M0.2 pillar guarantee.
AUTHZ_SHADOWED_ARMwarnAn 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_ROLEwarnA scoped role declared under authz.scopes with no grant profile — it can authorize no read, action, or channel. Dead config, deny-safe.
AUTHZ_UNREACHABLE_VIEWwarnA grant's read names a resource:view whose resource has no firewall — no role can reach that view. (A reachability proxy — heuristic by design.)
AUTHZ_NOT_OVER_RESOURCEwarnA not whose operand resolves to a resource (SQL) siteNOT EXISTS over a nullable join can silently pass (the null-trap). The site resolution is conservative, so this stays advisory.
AUTHZ_PUSHDOWN_COSTinfoPer-permission report: how many SQL subqueries the permission lowers to, plus the max recursive-CTE depth. Advisory, never fails a build.

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 check 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 if a resource grants a scope role access the profile doesn't cover. This is what makes the grant profile the single auditable manifest of what an external principal can see.

Authn codes

Five structural authentication checks — proving properties over the generated route + credential surface rather than the authz policy. All are promoted (compile errors):

CodeTierWhat it catches
AUTHN_ROUTE_COVERAGEerrorA route that reaches data with an unclassifiable auth gate and isn't declared PUBLIC — the signup / flag-gap bug class (data delivered to an unauthenticated ctx). Fires only on a gate the compiler cannot classify; zero-FP by construction.
AUTHN_SURFACE_INVARIANTerrorA surface contract breach: an /mcp or agent route not gated bearer-only, or an INTERNAL-gated route mounted on an HTTP-reachable surface.
AUTHN_SECRET_CONSISTENCYerrorA 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_DELIVERYerrorA 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_PROVENANCEerrorAn identity ctx field written from request input rather than a credential verifier — client-asserted identity, which is forbidden. Unprovable provenance also fails (fail closed).

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 severity contract is locked in now: they fail builds the moment they can fire. (The emitted route surface is separately guarded today by the IR's unbound-route invariant — a hard error that predates this promotion and owns that surface; one subject, one error.)

The override surface

Every promoted finding can be explicitly accepted — per finding, with a required reason:

authz: {
  analyzer: {
    allow: [
      {
        check: 'AUTHZ_CAPABILITY_CEILING',
        target: 'sessions/scope:event:organizer',
        reason: 'organizer grant profile lands with the v2 rollout (TICKET-123)',
      },
    ],
  },
}

An entry downgrades the exact (check, target) finding back to a warning that cites the reason. The rendered error prints the exact recipe — copy the check and target from it. Target formats:

Checktarget format
AUTHZ_EMPTY_PERMISSIONthe permission name — event:locked
AUTHZ_CAPABILITY_CEILING<resource>/scope:<kind>:<role>sessions/scope:event:organizer
AUTHN_ROUTE_COVERAGE, AUTHN_SURFACE_INVARIANT<METHOD> <path>GET /mcp
AUTHN_SECRET_CONSISTENCY, AUTHN_CREDENTIAL_DELIVERYthe credential kind — ws-ticket
AUTHN_CLAIM_PROVENANCEthe ctx field — ctx.userId

Guard rails, all compile errors:

  • check must be a promoted id — unknown ids and keep-warn ids (there is nothing to override) are rejected at config validation.
  • reason is required and non-empty: overrides are self-documenting.
  • An allow entry that matches no current finding is a dead override and fails the compile — fix or remove the stale entry. Override lists can never silently outlive the problems they suppressed.

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 / not DSL 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.

On this page