| name | pikku-permissions |
| description | Use when adding authorization checks to Pikku functions or routes — pikkuPermission, pikkuAuth, per-function permissions, pattern-based permissions, or understanding OR/AND permission logic. TRIGGER when: user wants to restrict who can call a function, check resource ownership, add role-based access, or understand where permission checks belong. DO NOT TRIGGER when: user asks about middleware or request interception (use pikku-middleware), authentication strategies (use pikku-security), or session management. |
| installGroups | ["core"] |
Pikku Permissions
The Rule
ALWAYS put authorization checks in the permissions field of pikkuFunc or pikkuSessionlessFunc — NEVER inside the func body.
This includes: org access checks, repo access checks, role checks, resource ownership, and any other authorization logic. The permissions field runs before func, is visible to the inspector, and is the only place Pikku enforces authorization.
export const deleteBook = pikkuFunc({
func: async ({ db }, { bookId }) => {
await db.deleteBook(bookId)
},
permissions: {
owner: isBookOwner,
},
})
export const deleteBook = pikkuFunc({
func: async ({ db }, { bookId }, { session }) => {
if (!session) throw new UnauthorizedError()
await db.deleteBook(bookId)
},
})
Agent Operating Procedure
- Discover before editing. Run
pikku info permissions --verbose and pikku info functions --verbose to understand what permissions are already defined and applied.
- Define permission checkers in a
src/permissions.ts or domain-specific src/lib/*-permissions.ts file.
- Apply them via the
permissions field on the function, or via addHTTPPermission / addPermission for pattern/tag-based application.
- Validate: run
pikku all --tsc to confirm permission checker signatures are correct.
Permission Factories
pikkuAuth(fn) — Session-Only Checks
Use for checks that only need the session — no request data required.
import { pikkuAuth } from '#pikku'
export const isAuthenticated = pikkuAuth(
async (_services, session) => !!session
)
export const isAdmin = pikkuAuth(
async (_services, session) => session?.role === 'admin'
)
pikkuPermission(fn) — Data-Aware Checks
Use when authorization depends on the actual request data (e.g., resource ownership).
import { pikkuPermission } from '#pikku'
export const isBookOwner = pikkuPermission(
async ({ db }, { bookId }, { session }) => {
const book = await db.getBook(bookId)
return book?.authorId === session?.userId
}
)
export const hasBookAccess = pikkuPermission(
async ({ db }, { bookId }, { session }) => {
return await db.hasAccess(session?.userId, bookId)
}
)
OR / AND Logic
permissions: {
admin: isAdmin,
owner: isBookOwner,
reviewer: [isAuthenticated, hasBookAccess],
}
Groups are OR'd. Entries within a group array are AND'd.
Where to Apply Permissions
Per-Function (preferred)
export const deleteBook = pikkuFunc({
func: async ({ db }, { bookId }) => {
await db.deleteBook(bookId)
},
permissions: {
admin: isAdmin,
owner: isBookOwner,
},
})
Pattern-Based (addHTTPPermission)
import { addHTTPPermission } from '@pikku/core/http'
addHTTPPermission('/admin/*', { admin: isAdmin })
Tag-Based (addPermission)
import { addPermission } from '.pikku/pikku-types.gen.js'
addPermission('internal', { machine: isMachineAgent })
Complete Example
import { pikkuAuth, pikkuPermission } from '#pikku'
export const isAuthenticated = pikkuAuth(
async (_services, session) => !!session
)
export const isAdmin = pikkuAuth(
async (_services, session) => session?.role === 'admin'
)
export const isOrgMember = pikkuPermission(
async ({ db }, { orgId }, { session }) => {
return await db.isMember(session?.userId, orgId)
}
)
export const deleteOrg = pikkuFunc({
func: async ({ db }, { orgId }) => {
await db.deleteOrg(orgId)
},
permissions: {
admin: isAdmin,
owner: [isAuthenticated, isOrgMember],
},
})
After Changes
pikku all
pikku all --tsc