WardenAuthAgent Security
PricingDocsCompareBlogLearnChangelog
Sign inGet started free
All posts
Deep Dive
August 7, 2025
11 min read

Designing a Permission Schema: Resources, Actions, Wildcards, and Deny-Wins

Your permission naming convention is an API you will live with for years. A practical guide to resource:action naming, choosing granularity, using wildcards safely, and applying deny-wins semantics — with a reference schema you can copy.


Your permission naming convention is an API. Once services and customers depend on invoice:read, renaming it is a breaking change. It is worth getting right early. This is a practical guide to designing a permission schema that stays legible as your product grows.

The resource:action Convention

Adopt a consistent, two-part naming scheme: a resource (the noun) and an action (the verb). Keep resources singular and actions in a small, controlled vocabulary.

text
invoice:read      invoice:create    invoice:update    invoice:delete
report:export     user:invite       billing:manage    apikey:rotate

Choosing Granularity

Too coarse (billing:manage covers everything) and you cannot express least privilege. Too fine (invoice:update-due-date) and you drown in permissions. A good rule: model a distinct action when a real role would plausibly need one without the other. Start coarser; split when a use case demands it.

Wildcards

Wildcards collapse families of permissions. Support them on both halves of the pair, and reserve the full wildcard for genuine superadmins:

typescript
{ resource: 'invoice:*', action: 'read' }   // read any invoice sub-resource
{ resource: 'report',    action: '*'    }   // any action on reports
{ resource: '*',         action: '*'    }   // superadmin — grant sparingly

See wildcard permissions for matching semantics.

Deny-Wins Semantics

Decide your conflict-resolution rule up front. Deny-wins — an explicit deny overrides any allow — is the safest default and enables broad grants with precise carve-outs:

typescript
// Grant broad read, but hard-block a sensitive resource:
{ resource: '*',              action: 'read', effect: 'allow' }
{ resource: 'salary-record',  action: 'read', effect: 'deny'  } // wins

Resource Hierarchies

Encode hierarchy in the resource segment so wildcards can express containment: project:acme:invoice lets project:acme:* grant everything under one project without touching another. Keep the separator consistent across your whole schema.

A Reference Schema

ResourceActionsNotes
invoiceread, create, update, deleteCore CRUD; delete often deny-carved for non-admins
reportread, exportExport is higher-privilege than read
userread, invite, removeInvite/remove are admin-tier
billingread, manageCoarse by design; split if needed
apikeyread, create, rotate, revokeCredential lifecycle actions

How WardenAuth Implements This

WardenAuth uses resource + action permissions with wildcard matching and deny-wins resolution natively — the exact primitives above. Try it free and model your schema in the dashboard.


Back to blogTry WardenAuth free →
© 2026 ecarrizo. All rights reserved.
PricingDocsCompareBlogLearnChangelogStatusGlossaryContactTermsPrivacy