grill-me
Interview the user relentlessly about a plan or design until reaching shared understanding,…
Schema-based permission system for API features. Use this skill when implementing authorization in use cases, defining permission schemas with createPermissionSchema, creating injectable permissions via createPermissionsAbstraction/createPermissionsFeature, checking
$ npx -y skills add webiny/webiny-js --skill permissions --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/permissionsContext preview
The summary Claude sees to decide when to auto-load this skill.
Schema-based permission system for API features. Use this skill when implementing authorization in use cases, defining permission schemas with createPermissionSchema, creating injectable permissions via createPermissionsAbstraction/createPermissionsFeature, checking
name: webiny-api-permissions description: > Schema-based permission system for API features. Use this skill when implementing authorization in use cases, defining permission schemas with createPermissionSchema, creating injectable permissions via createPermissionsAbstraction/createPermissionsFeature, checking read/write/delete/publish permissions, handling own-record scoping, or testing permission scenarios. Covers the full pattern from schema definition to use case integration to test matrices.
Permissions follow two layers: **domain** (schema) and **features** (DI abstractions + feature registration). Each package declares a permission schema and gets a typed `Permissions` abstraction injectable into use cases via DI. Methods like `canRead`, `canEdit`, `canDelete`, `canPublish`, `onlyOwnRecords` replace manual `identityContext.getPermission()` calls.
Define the schema in `src/domain/permissionsSchema.ts`:
import { createPermissionSchema } from "webiny/api/security";
export const SM_PERMISSIONS_SCHEMA = createPermissionSchema({
prefix: "sm",
fullAccess: true,
entities: [
{
id: "product",
permission: "sm.product",
scopes: ["full", "own"],
actions: [{ name: "rwd" }, { name: "pw" }]
},
{
id: "settings",
permission: "sm.settings",
scopes: ["full"]
}
]
});The schema MUST use `as const` inference (handled by `createPermissionSchema`) for TypeScript to narrow entity IDs in method signatures.
| Field | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `prefix` | Namespaces the DI abstraction: `${prefix}:Permissions` | | `fullAccess` | `true` for standard full access. Pass an object with custom boolean flags for full-access extras (e.g., `{ canForceUnlock: true }`). | | `entities[].id` | Entity identifier used in method calls: `canRead("product")` | | `entities[].permission` | Permission name matched against identity permissions | | `entities[].scopes` | `["full"]` or `["full", "own"]` — determines if own-scope supported | | `entities[].actions` | Action definitions — built-in: `"rwd"`, `"pw"`; custom: boolean flags |
Omit `entities` for binary full/no access:
export const MA_PERMISSIONS_SCHEMA = createPermissionSchema({
prefix: "ma",
fullAccess: true
});---
import { createPermissionsAbstraction } from "webiny/api/security";
import type { Permissions } from "webiny/api/security";
import { SM_PERMISSIONS_SCHEMA } from "~/domain/permissionsSchema.js";
export const SmPermissions = createPermissionsAbstraction(SM_PERMISSIONS_SCHEMA);
export namespace SmPermissions {
export type Interface = Permissions<typeof SM_PERMISSIONS_SCHEMA>;
}import { createPermissionsFeature } from "webiny/api/security";
import { SM_PERMISSIONS_SCHEMA } from "~/domain/permissionsSchema.js";
import { SmPermissions } from "./abstractions.js";
export const SmPermissionsFeature = createPermissionsFeature(SM_PERMISSIONS_SCHEMA, SmPermissions);Register the feature in your context plugin:
import { SmPermissionsFeature } from "~/features/permissions/feature.js";
// In createContext:
SmPermissionsFeature.register(container);---
src/ ├── domain/ │ └── permissionsSchema.ts # createPermissionSchema() ├── features/ │ └── permissions/ │ ├── abstractions.ts # createPermissionsAbstraction() + namespace type │ └── feature.ts # createPermissionsFeature() └── index.ts # SmPermissionsFeature.register(container)
---
All methods follow a 3-tier bypass:
1. `identityContext.hasFullAccess()` → `name: "*"` permission (super admin) 2. `hasFullSchemaAccess()` → wildcard permission (e.g. `"sm.*"`) 3. Entity-level permission check
| Method | Purpose | Item-aware | Notes | | --------------------------- | --------------------- | ---------- | --------------------------------------------------------------------------------------------- | | `canAccess(entity, item?)` | General access check | Yes | Without item: checks entity permission exists. With item + `own: true`: checks `createdBy.id` | | `onlyOwnRecords(entity)` | List filter flag | No | Returns `true` when ALL permissions have `own: true` | | `canRead(entity)` | Read permission | No | Checks `rwd` includes `"r"` (or no `rwd` = unrestricted) | | `canCreate(entity)` | Create permission | No | Checks `rwd` includes `"w"`
Open-source content platform. Self-hosted on AWS serverless. Built as a TypeScript framework you extend with code, not a closed product you configure through a UI. Runs on Lambda, DynamoDB, S3, and CloudFront inside your own AWS account. Scales automatically.
Repo: webiny/webiny-js
Interview the user relentlessly about a plan or design until reaching shared understanding,…
Turn a PRD into a multi-phase implementation plan using tracer-bullet vertical slices, saved…
Webiny-only. Run all checks required before packages are ready for publish: deps, build,…
Use when running tests. Shows how to run tests for a single package, including OpenSearch…
Generate, refresh, and maintain Webiny MCP server skills from source documentation and…
Create a PRD through user interview, codebase exploration, and module design, then submit as…