Nenjo Docs
API

Scopes

Complete reference for Nenjo's scope system covering API keys, platform scopes, and mode escalation.

Scopes

Scopes control what actions an API key, agent role, or mode session can perform. They apply consistently across the MCP server, worker execution, and mode system.

Scope Format

Scopes follow the pattern resource:permission:

tickets:read
tickets:write
projects:write

Each resource group has a :read and :write scope (except chat and graph, which are read-only).

Complete Scope Reference

ScopeDescription
tickets:readList and get tickets
tickets:writeCreate, update, and delete tickets
projects:readList and get projects
projects:writeCreate and update projects
documents:readList documents and read content
documents:writeDelete documents
pipelines:readList and get pipelines, steps, and edges
pipelines:writeCreate, update, and delete pipelines, steps, and edges
executions:readList and get execution runs
executions:writeCreate execution runs and send commands
agents:readList and get agents and agent roles
agents:writeCreate, update, and delete agents
councils:readList and get councils
councils:writeCreate, update, and delete councils
chat:readList chat sessions and messages
graph:readGet dependency graphs and execution order

Total: 16 scopes across 8 resource groups.

Scope Rules

Write Implies Read

A :write scope automatically grants the corresponding :read scope for the same resource. If a key has tickets:write, it can also perform tickets:read operations without explicitly listing both.

Empty Scopes = Full Access

An API key or role with an empty scopes array has unrestricted access to all resources. This is the default when creating a key without specifying scopes.

Scope Checking Logic

The scope check follows this logic:

  1. If the scopes array is empty, access is granted (full access).
  2. If the exact scope is present, access is granted.
  3. If the requested scope is :read and the corresponding :write scope is present, access is granted.
  4. Otherwise, access is denied.

Where Scopes Apply

Scopes are used in three contexts with consistent behavior:

API Key Scopes

When you create an API key, the scopes field restricts which MCP tools are accessible. The tools/list response only includes tools whose scope is satisfied by the key's scopes.

{
  "name": "Read-only Key",
  "scopes": ["tickets:read", "projects:read", "executions:read"]
}

This key can call tickets_list, tickets_get, projects_list, etc., but cannot call tickets_create or any write tool.

Platform Scopes (Agent Roles)

Each agent role has a platform_scopes field (TEXT[] in the database) that controls which MCP tools the worker makes available when building the agent's tool set. These scopes are checked at agent build time and work identically to API key scopes.

Platform scopes are configured per role and are seeded with sensible defaults for system roles.

Mode Additional Scopes

Modes can temporarily escalate an agent's platform scopes for the duration of a mode session through the additional_scopes field in the mode's ModeToolConfig:

{
  "additional_scopes": ["tickets:write", "projects:write"]
}

When a mode session is active, the worker merges these additional scopes with the role's base platform_scopes before filtering MCP tools. This allows modes like /the-creator to grant write access that the role does not normally have.

Scope Inference on the Worker

The worker's MCP client infers required scopes from tool names when filtering tools for a role:

Tool Name PatternInferred Scope
list_*, get_*resource:read
create_*, update_*, delete_*resource:write

The resource name is derived from the tool name prefix (e.g., tickets_list infers tickets:read). This inference is a fallback -- the canonical scope is always the one defined in the tool's ToolDef.scope field in the registry.

Examples

Minimal read-only key

curl -X POST \
  -H "Authorization: Bearer <clerk-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Dashboard Reader",
    "scopes": ["tickets:read", "projects:read", "executions:read"]
  }' \
  https://your-instance/api/v1/api-keys

Full-access key (no scopes)

curl -X POST \
  -H "Authorization: Bearer <clerk-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Worker Full Access"
  }' \
  https://your-instance/api/v1/api-keys

Write-only for a specific resource

curl -X POST \
  -H "Authorization: Bearer <clerk-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ticket Manager",
    "scopes": ["tickets:write"]
  }' \
  https://your-instance/api/v1/api-keys

This key can read and write tickets (:write implies :read) but has no access to any other resource.

On this page