Permissions & Scopes
What are permission scopes?​
A permission scope is a fine-grained string that controls access to a single action. Instead of checking a user's role, the system checks "does this user have scene_block:video:generate?". This lets admins grant or revoke individual capabilities without changing a user's overall role.
Scopes follow the pattern resource:action or resource:sub-resource:action.
Why fine-grained scopes?​
- A user can be given a subset of capabilities without making them Admin.
- An individual user can be given elevated access (e.g.
episode:assign) without a role change.
All available scopes​
Admin​
| Scope | Description |
|---|---|
admin:dashboard | View the admin dashboard |
admin:logs:view | View system logs |
admin:logs:clear | Clear system logs |
admin:permissions:view | View permission definitions |
admin:permissions:edit | Edit permission definitions |
admin:user:permissions:manage | Manage individual user permission overrides |
admin:roles:view | View roles list |
admin:roles:create | Create new roles |
admin:roles:edit | Edit role names |
admin:role:permissions:manage | Assign/remove permissions from a role |
admin:roles:delete | Delete roles |
admin:analytics:edit | Edit analytics settings |
admin:prompt_settings:edit | Edit LLM prompt settings |
admin:preprocess_prompt_settings:edit | Edit prompt preprocessing settings |
admin:users:view | View users list |
admin:users:create | Create users |
admin:users:edit | Edit user profiles |
admin:users:delete | Delete users |
Templates​
| Scope | Description |
|---|---|
template:create | Create content templates |
template:edit | Edit content templates |
template:delete | Delete content templates |
Projects​
| Scope | Description |
|---|---|
project:create | Create projects |
project:edit | Edit project details |
project:delete | Delete projects |
Episodes​
| Scope | Description |
|---|---|
episode:create | Create episodes |
episode:edit | Edit episode metadata |
episode:delete | Delete episodes |
episode:import | Import an episode from another source |
episode:assign | Manage creator assignments (roles → users) |
episode:preview:upload | Upload episode preview image |
episode:preview:delete | Delete episode preview image |
episode:history:revert | Revert episode to a previous state |
Scenes​
| Scope | Description |
|---|---|
scene:create | Create scenes in an episode |
scene:edit | Edit scene metadata and content |
scene:delete | Delete scenes |
scene:emotional_arc:generate | Generate emotional arc analysis |
scene:location:assign | Assign a location to a scene |
scene:history:revert | Revert a scene to a previous state |
Locations​
| Scope | Description |
|---|---|
location:create | Add locations to the library |
location:edit | Edit location metadata |
location:delete | Delete locations |
location:image:generate | AI-generate a location image |
location:image:upload | Upload a location image manually |
location:image:delete | Delete a location image |
Scene Blocks​
| Scope | Description |
|---|---|
scene_block:create | Add blocks (dialog/action) to a scene |
scene_block:edit | Edit block text, timing, emotion |
scene_block:delete | Delete blocks |
scene_block:frame:generate | Generate a start or end frame image |
scene_block:frame:delete | Delete a generated frame |
scene_block:video:generate | Generate a video from frames |
scene_block:video:delete | Delete a generated video |
scene_block:media:attach | Attach media files to a block |
scene_block:media:delete | Remove attached media from a block |
scene_block:history:revert | Revert a block to a previous state |
Characters​
| Scope | Description |
|---|---|
character:create | Add characters |
character:edit | Edit character details |
character:delete | Delete characters |
character:image:generate | AI-generate a character image |
character:image:upload | Upload a character image manually |
character:image:delete | Delete a character image |
Comments​
| Scope | Description |
|---|---|
comment:create | Post comments on scene blocks |
comment:resolve | Mark comments as resolved |
comment:delete | Delete comments |
Sounds​
| Scope | Description |
|---|---|
sound:create | Generate and upload sound effects |
sound:edit | Rename sound effects |
sound:delete | Delete sound effects |
Voiceover​
| Scope | Description |
|---|---|
voiceover:generate | Generate voiceover audio for a dialog block |
voiceover:delete | Delete a saved voiceover |
Emotions​
| Scope | Description |
|---|---|
emotion:create | Create emotions |
emotion:edit | Edit emotions |
emotion:delete | Delete emotions |
emotion:category:create | Create emotion categories |
emotion:category:edit | Edit emotion categories |
emotion:category:delete | Delete emotion categories |
LLM​
| Scope | Description |
|---|---|
llm:generate | Trigger LLM text generation |
llm:analyze | Trigger LLM analysis (e.g. emotional arc) |
llm:approve | Approve LLM-generated suggestions |
World Rules​
| Scope | Description |
|---|---|
world_rule:create | Create world rules |
world_rule:edit | Edit world rules |
world_rule:delete | Delete world rules |
How permissions are checked​
Backend​
The requirePermission(scope) middleware reads the user's permissions (role permissions + individual overrides) from the JWT payload and returns 403 if the scope is missing.
router.put('/assignment-template', requireAuth, requirePermission('template:edit'), controller.save);
Frontend​
The <PermissionGate> component hides UI elements the user cannot access:
<PermissionGate permission={permissions.SCENE_BLOCK_VIDEO_GENERATE}>
<button onClick={handleGenerate}>Generate Video</button>
</PermissionGate>
It also supports mode="any" (union) and mode="all" (intersection) for multiple scopes:
// Show if user has EITHER permission
<PermissionGate permission={[permissions.VOICEOVER_GENERATE, permissions.VOICEOVER_DELETE]} mode="any">
// Show only if user has ALL permissions
<PermissionGate permission={[permissions.VOICEOVER_GENERATE, permissions.VOICEOVER_DELETE]} mode="all">
The usePermission(scope) hook returns a boolean for programmatic checks:
const canEdit = usePermission(permissions.SCENE_BLOCK_EDIT);