Why Generalized Permissions
- By Alan James
Status: proposed design, not yet implemented. There is no
permission_grant table or GrantService in the codebase today - the
current auth model is the simple session-based AuthorizationGuard (see
Authorization). This page is also still an
outline, not finished narrative prose - both of those will be true until
this design actually gets built.
The Problem with Dedicated Relationship Tables
- One FK-constrained join table per relationship type
(
CompanyUser,TeamUser,CollectionTeam, ...) - Real DB integrity, but:
- schema proliferation - every new relationship = new table/migration/API/service/tests
- repeated APIs doing the same conceptual thing ("relate A to B with these scopes")
- risk of authorization logic drifting between relationship implementations over time
- Nothing stops a future contributor from bypassing a central service and querying/adding a raw table directly - the "one abstraction" is a convention, not a constraint
What the Generalized Grants Model Actually Does
- One
permission_granttable: subject, resource, one scope per row, soft-deleted (real audit trail - see below) - Governed by a typed relationship model (which subject/resource pairs are legal, which scopes are valid) - not an untyped bag of IDs
GrantServiceis the only thing that ever touches the table - two narrow methods (canPerform,getAccessQuery) are the entire surface every other service gets- This is a hard line, not a soft one - there's no raw table left to reach for if someone wants to bypass it
Why This Matters for a Small Team That Doesn't Know How It'll Grow
- Yew is young - the actual shape of future relationships isn't known today
- Committing to N dedicated tables optimizes for a stable domain model this project doesn't have yet
- One centralized, validated path is a stronger constraint than N typed tables + good intentions - same "platform enforces it, not team discipline" principle as Postgres/cookies/hash-routing, applied to the schema itself
The Tradeoff (named honestly, not hidden)
- No database-enforced foreign keys on either side of a relationship
- No automatic cascade deletes - has to be built by hand
- The plan: every resource-owning service's delete path explicitly
calls into
GrantServiceto purge anything referencing it - six known, enumerable call sites, not an open-ended risk - More validation work moves into the application layer, concentrated in one service instead of duplicated across many
The Broader Pattern
- Same shape as Postgres over MongoDB, cookies over JWT, hash routing over History API: give up some flexibility/elegance in exchange for the platform enforcing correctness instead of relying on people remembering to do the right thing
- One scope per grant row + soft delete also means every grant and revocation is independently auditable - a side effect that happens to set up V4's audit-log requirement for free