Quickback Docs

Security Overview

Rows, columns, fields, and who — the four questions the security pillars answer, and which page owns each one.

Quickback's security is not one mechanism. It is four different questions, each answered by a different pillar, each enforced at a different point in the request. This page is the map; every mechanism has its own page.

The four questions

The questionPillarWhere it runs
Which rows may this caller ever see?FirewallA WHERE clause added to every query
Who may perform this operation?AccessMiddleware, before the handler
Which columns come back on a read?ViewsA read projection
Which fields may be written?GuardsWrite validation, before the DB
What is redacted in the response?MaskingA response transform
How often may they ask?Rate limitsEdge, before routing

Views and Guards are documented under Reads and Writes rather than under Security, because that is where you are when you need them: a view is a shape you return, a guard is a rule about a write. They are security mechanisms either way.

Firewall — which rows

The firewall is tenant isolation. It rewrites every query so a caller can only ever address rows inside their own scope. It is not a permission check that can be granted away — it is the boundary of the addressable data set.

Isolation columns are auto-detected from the schema:

  • organizationId — plus five other accepted spellings, see below — scoped to ctx.activeOrgId
  • teamId — scoped to ctx.activeTeamId
  • ownerId — scoped to ctx.userId

The owner-column rule

This is the rule that is most often stated backwards, so state it once and link it:

ownerId is auto-detected as the owner column: the compiler firewalls reads with WHERE ownerId = ctx.userId and auto-stamps the column on insert.

userId is not auto-detected on feature tables. It is a Better Auth convention on Better Auth's own tables (session, account, member, passkey), and on a feature table it usually means a user this row references — a member, a contact, an invitee — rather than the row's owner.

If userId is the only candidate column on the table, the compiler raises an error. Alongside a detected isolation column it raises a warning.

Two escapes: rename the column to ownerId, or declare firewall: { owner: { column: "userId" } } explicitly.

Six spellings of the organization column

Organization auto-detection accepts six column names, American and British, in this precedence order. The first one present in the schema source wins:

organizationId
organisationId
orgId
organization
organisation
org

Access — who

Access is the role check. It runs as middleware, before your handler, per operation — read, create, update, delete, and each action independently.

Two things it is not:

  • It is not row filtering. A caller who passes the access check still only sees the rows the firewall admits.
  • It is not a substitute for the firewall. A scoped-role gate is an access decision; the row-scope-column boundary is a firewall decision. Mixing them produces policies that read as security but enforce nothing.

Beyond plain role lists, Access carries record conditions, context substitution, relationship-based roles, and the SESSION / SCOPED / AUTHENTICATED pseudo-role tiers. See Access.

Columns and fields

ReadsViews are named column-level projections with their own access control. A view is how you return a narrower shape to a broader audience.

WritesGuards declare which fields can be set on create (createable), modified on update (updatable), changed only through an action (protected), or never changed after creation (immutable). A protected field is omitted from the generated request schemas entirely, so a client cannot even send it.

RedactionMasking redacts values in the response based on the caller's role. Use it for data the caller may legitimately address but should not read in full.

Advanced authorization

When roles are not enough — when access depends on a relationship between the caller and the row — the model extends:

  • Permissions — named capabilities instead of raw role strings
  • Scopes — sessionless scope-token principals
  • Arrows — relationship traversal
  • FGA — fine-grained authorization tuples
  • Diagnostics — what the compiler decided, and why

Encryption

Encryption and the sealed protocol cover data at rest. They are orthogonal to the pillars above: encryption governs what the stored bytes are, the pillars govern who reaches them.

Where this is enforced

Everything on this page is a compile-time decision. The pillars are declared in your feature files, checked for coherence when you run quickback compile, and emitted as generated middleware and query builders. There is no runtime policy engine to misconfigure and no rules to keep in sync by hand — if a policy is incoherent, the compile fails.

On this page