Skip to content

Auth, Policies, and Middleware

The public auth model is actor-first.

That means route handlers and config-driven bindings should treat actor as canonical:

  • actor.id
  • actor.tenantId
  • actor.kind
  • actor.claims.*

Use actor.id for identity, actor.tenantId for actor-owned tenant scope, and ctx:tenantId for request-tenant bindings. That is the documented authoring model.

Entity route config supports:

  • auth
  • permission
  • rateLimit
  • event
  • dataScope
  • policy-aware permission dispatch

Example:

{
"routes": {
"defaults": { "auth": "userAuth" },
"dataScope": { "field": "authorId", "from": "ctx:actor.id" }
}
}

Package domain routes support the same major route concerns:

route.post({
path: '/summary',
auth: 'userAuth',
permission: { action: 'notes:read' },
rateLimit: { limit: 20, windowMs: 60000 },
idempotency: true,
middleware: ['banCheck'],
async handler(ctx) {
return ctx.respond.json({ actorId: ctx.actor.id });
},
});

When a package domain route needs entity-aware permission or parent checks, use:

  • permissionAdapter
  • parentAdapter

Those references tell the framework which entity adapter should back the permission check rather than forcing the route into a custom plugin shell.

Package middleware is declared on the package:

definePackage({
middleware: {
banCheck: createBanCheckMiddleware(),
},
});

Then referenced by name from entity or domain route config:

route.get({
path: '/summary',
middleware: ['banCheck'],
async handler(ctx) {
return ctx.respond.json({ ok: true });
},
});

When permission logic needs request-aware policy evaluation, register entity policies and reference them from route config.

That keeps auth, policy, and routing in the managed route shell rather than scattering custom checks through ad hoc handlers.