Skip to content

Permissions

A permission is a dotted string naming an action, e.g. admin.users.create. Permissions are namespaced by the unit that owns them (an app’s permissions live under <app>.*), so units cannot collide.

Apps never make authorization decisions themselves. A route declares the permission it requires:

@app.page("/users", permission="admin.users.read")
def users(ctx): ...

The core’s request adapter enforces permission= before the handler runs, calling the authorization service’s has_permission(user, required). This keeps the decision in one place and consistent across every app.

A grant is what a role holds. A grant may be a concrete permission (admin.users.create) or a dotted-prefix wildcard:

  • admin.users.* — every permission under admin.users.
  • admin.* — every permission in the admin namespace.
  • * — everything.

Wildcards are grants only — they are never used as the required permission on a route (a route always requires a concrete permission). A required permission is satisfied if any of the user’s grants matches it.

has_permission resolves the user’s effective grants through the role chain (see Roles) and returns whether any grant matches the required permission. A super_user bypasses the check entirely — they hold everything without enumerating grants.