Skip to content

Roles & Permissions ​

Studio uses a two-tier role system: workspace roles determine organizational access, and project roles control content operations.

Workspace Roles ​

RoleDescriptionAccess Level
OwnerCreator of the workspace. Full control.Implicit access to all projects. Can manage billing, members, settings.
AdminTrusted team lead. Nearly full control.Implicit access to all projects. Can manage members and settings. Cannot transfer ownership or delete workspace.
MemberStandard team member.No implicit project access. Needs explicit project_members assignment.

Workspace Role Hierarchy ​

Owner > Admin > Member
  • A workspace has exactly one owner (transferable)
  • Admins and members are added via invitation (email)
  • Invited members must accept before gaining access

Project Roles ​

Project roles are assigned per project to workspace members:

RoleDescriptionContent Access
EditorContent creator.Can create, update, delete content and media. Cannot merge/reject branches.
ReviewerContent approver.Can read all content, merge/reject branches, approve/reject form submissions. Cannot create content.
ViewerRead-only observer.Can read content, models, and submissions. Cannot modify anything.

Role Resolution

Workspace Owner and Admin get all project permissions regardless of project_members assignment. Only workspace Members need explicit project roles.

The UI follows the role

Write controls are only shown to roles that may use them, so a Viewer or Reviewer is not offered an Edit button they could only learn was refused at save time. Content editing (and vocabulary editing) is shown to callers who have the save_content tool, and for members limited to specific models only on the models they are allowed. Model management (add or delete a model, title field), form and comment settings, and moderation are shown to workspace Owners and Admins. The server routes remain the authority and check the same permissions.

Tool Access Matrix ​

This matrix shows which roles can use each agent tool:

ToolViewerReviewerEditorAdminOwner
Read Operations
list_modelsYesYesYesYesYes
get_contentYesYesYesYesYes
brain_queryYesYesYesYesYes
brain_searchYesYesYesYesYes
brain_analyzeYesYesYesYesYes
validateYesYesYesYesYes
validate_schemaYesYesYesYesYes
list_branchesYesYesYesYesYes
branch_healthYesYesYesYesYes
relation_expandYesYesYesYesYes
search_mediaYesYesYesYesYes
get_mediaYesYesYesYesYes
list_submissionsYesYesYesYesYes
list_commentsYesYesYesYesYes
Content Operations
save_content----YesYesYes
replace_in_field----YesYesYes
delete_content----YesYesYes
copy_locale----YesYesYes
update_status----Yes*YesYes
vocabulary----YesYesYes
upload_media----YesYesYes
update_media----YesYesYes
delete_media----YesYesYes
Review Operations
merge_branch--Yes--YesYes
reject_branch--Yes--YesYes
request_changes--Yes--YesYes
approve_submission--Yes--YesYes
reject_submission--Yes--YesYes
approve_comment--Yes--YesYes
reject_comment--Yes--YesYes
reply_comment--Yes--YesYes
Admin Operations
save_model------YesYes
delete_model------YesYes
init_project------YesYes
add_locale------YesYes

* An Editor's update_status moves entries to draft or archived only. Setting published -- with update_status, or a save_content that asks for published -- is limited to Owners and Admins, the same rule as the status badge and the remote contentrain_content_status tool. On a plan without review, an Editor's chat saves stay drafts instead of being published automatically.

Permission Resolution ​

ts
async function resolveAgentPermissions(
  userId: string,
  workspaceId: string,
  projectId: string,
  accessToken: string,
): Promise<AgentPermissions>

Resolution Flow ​

1. Get workspace member role
   |
   +-- Owner/Admin? → Full access (all tools)
   |
   +-- Member? → Continue to step 2
   |
2. Get project member record
   |
   +-- No record? → No access (empty tools list)
   |
   +-- Has role? → Continue to step 3
   |
3. Normalize role (EE bridge gate)
   |
   +-- EE bridge present? → a stored reviewer/viewer role is kept as-is on
   |                        every plan, including after a downgrade (assigning
   |                        the role is what the plan gates). specificModels
   |                        is honored only when the plan grants it (Pro+).
   |
   +-- No EE bridge (Community Edition)? → reviewer/viewer downgrade to
   |                                       editor, specificModels forced false
   |
4. Filter tools by effective role

AgentPermissions Object ​

ts
interface AgentPermissions {
  workspaceRole: 'owner' | 'admin' | 'member'
  projectRole: 'editor' | 'reviewer' | 'viewer' | null
  specificModels: boolean       // Model-level restriction active?
  allowedModels: string[]       // Restricted model IDs
  allowedLocales: string[]      // Restricted locale codes
  availableTools: string[]      // Tools this user can invoke
}

Model-Specific Access ​

On Pro and Enterprise plans, project members can be restricted to specific content models:

ts
{
  role: 'editor',
  specificModels: true,
  allowedModels: ['blog-post', 'team-members']
}

When specificModels is true:

  • The agent only allows tools on models in allowedModels
  • Content reads for other models return access denied errors
  • The system prompt informs the agent of the restriction

Pro feature, EE-gated

Model-specific access requires the roles.specific_models feature flag, granted on Pro and Enterprise plans. The flag is also requires_ee, so it only functions with the Enterprise Edition bridge present. On Free and Starter plans, and in a self-hosted Community Edition deployment, specificModels is always treated as false (full model access).

Workflow Interaction ​

Permissions interact with the workflow configuration:

Auto-Merge Workflow ​

All write operations (from any permitted role) are automatically merged through the branch pipeline.

Review Workflow ​

Under the review workflow, whether a write merges itself is decided from what the change does, not who made it. The agent's write is classified by risk (for example low-risk content, bulk content, or a destructive schema change) and by scope, and the project's approval policy (.contentrain/approval-policies.json) decides how many approvals it needs. A project without a policy file uses the default policy: every write above read-only is held on its cr/* branch for one approval, and the author may give it.

RoleWrite Behavior
EditorWrites create cr/* branches. They merge once the approvals the policy asks for are collected.
ReviewerCan merge or reject branches and give approvals in the branch review panel (anyone with the merge_branch tool can; a policy may narrow which roles count).
Owner / AdminHold every tool, so they can merge, reject and approve too. Their role does not exempt their own writes from the policy.

Calling merge_branch is not an approval: on a review-workflow project the branch must first collect the approvals its policy asks for, otherwise the merge is refused and the branch stays pending.

API Route Enforcement ​

Permissions are enforced at multiple levels:

Server Middleware ​

The auth middleware (01.auth.ts) validates session tokens on every request. Public endpoints are explicitly allowlisted.

Route-Level Checks ​

ts
// In a route handler
const session = requireAuth(event)
const db = useDatabaseProvider()

// Verify workspace membership with required role
await db.requireWorkspaceRole(
  session.accessToken,
  session.user.id,
  workspaceId,
  ['owner', 'admin'],
)

Agent-Level Enforcement ​

The agent system filters available tools based on resolved permissions before sending them to the LLM:

ts
const permissions = await resolveAgentPermissions(userId, workspaceId, projectId, accessToken)
const tools = filterToolsByPermissions(STUDIO_TOOLS, permissions.availableTools)
const phaseTools = filterToolsByPhase(tools, phase)

Billing Enforcement ​

The billing middleware (03.billing.ts) checks workspace billing state and blocks requests when subscriptions are locked:

Billing StateEffect
subscribedFull access
trial_activeFull access
past_dueFull access (payment overdue warning)
trial_expired402 blocked
grace_expired402 blocked
canceled_expired402 blocked

Plan Limits ​

Permissions are further constrained by plan-based numeric limits:

LimitFreeStarterProEnterprise
Team Members1325Unlimited
AI Credits/Month03001,600Unlimited
Media Storage—5 GB25 GB100 GB
CDN API Keys0325Unlimited
CDN Bandwidth03 GB60 GBUnlimited
Form Models0115Unlimited
Form Submissions/Month01003,000Unlimited
Outbound Webhooks0325Unlimited
Conversation API Keys0015Unlimited
API Credits/Month00450Unlimited
MCP Cloud Keys0115Unlimited
MCP Cloud Calls/Month05,000150,000Unlimited

Released under the AGPL-3.0 License. Privacy