Permissions and Roles
What is this
How identity and access work in ComStack: three layers of identity, project roles, and application in MCP tools and REST endpoints.
How it works
Identity layers
There are three independent identity layers in parallel:
| Layer | Purpose |
|---|---|
| User | Global identity. One per real person, identified by Firebase Auth UID. |
| Account | Legal and billing entity. Owns projects. |
| Project Member | Access per project with an assigned role. |
| Platform Admin | Global access for the ComStack team: bypasses all project-level checks. |
A single user can be a member of multiple projects across various accounts. Account membership and project membership are independent: being the account owner does not grant automatic project access.
Project roles
Three project roles stored in the member document:
| Role | Description |
|---|---|
manager | Full project access: content, settings, members, and all destructive operations. |
editor | Intended for content editing without destructive privileges. Currently not enforced: behaves the same as a generic member (see Implementation Gap below). |
viewer | Intended for read-only access. Currently not enforced: behaves the same as a generic member. |
MCP tool capabilities matrix
| Capability | manager | Any member | Platform Admin |
|---|---|---|---|
get-project-state, get-page-content, list-pages | ✅ | ✅ | ✅ |
create-page, update-page | ✅ | ✅ | ✅ |
delete-page, update-theme | ✅ | ❌ | ✅ |
publish, publish-confirm | ✅ | ❌ | ✅ |
connect-github, pull-and-publish, revert-publish | ✅ | ❌ | ✅ |
| Template CRUD tools | ❌ | ❌ | ✅ |
whoami, list-my-projects, create-project, get-guide | ✅ | ✅ | ✅ |
tools/list is filtered based on the caller: those who are not manager do not see exclusive management tools. The filter result is cached for 30 seconds per user.
Implementation gap: editor/viewer not enforced
The API accepts editor and viewer role values, but the authentication layer only distinguishes between manager and “any member document exists.” Editor and viewer behave identically: both get non-destructive read/write access, just like any other member.
Do not rely on distinctions between editor and viewer for security.
When to use it
- Check what a caller can do with a given role before creating a workflow.
- Diagnose a 403 error in an MCP tool call (is the caller a manager?).
- Understand what
tools/listreturns for different role levels.
Parameters / fields / inputs
How users are created:
- First OAuth login: the user document is created automatically.
- Added to a project by email: if a Firebase Auth user does not exist for that email, one is created.
How projects are created: the create-project MCP tool is the only way. The creator is automatically added as a manager.
How members are added: POST /api/projects/:projectId/members (requires manager). Accepts email, role (manager, editor, or viewer), and an optional display_name.
Example
A caller with the manager role calls publish: it succeeds. A caller with the editor role calls publish: it returns 403 Forbidden.
A caller without project membership calls list-my-projects: returns an empty list. create-project: succeeds (any registered user can create a project). get-project-state for a specific project: returns 403 (not a member).
Common errors
403 on publish or delete-page: the caller has the editor or viewer role, not manager. Update the member role to manager via PATCH /api/projects/:projectId/members/:uid.
403 on get-project-state: the caller is not a project member. Add them via POST /api/projects/:projectId/members.
403 on Template CRUD tools: these require Platform Admin access, not just project manager. Contact the ComStack team.
Related
- Authentication — how roles are checked in the middleware.
- MCP Server —
tools/listfiltering and the capabilities matrix in context. - Plans and Quotas — quota enforcement works alongside role checks.