Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-2254Done

Permissions — a permission catalog, custom project roles, and permission-gated UI

Replace the fixed three-role model shipped by 6.4 Roles & permissions with a permission-based one: a named permission CATALOG the code enforces, built-in roles re-expressed as permission SETS, project admins able to author their own roles, and a UI that hides what the actor cannot reach instead of showing everything and refusing the write.

Where this starts from (rung 2 — verified on origin/main, 2026-08-05)

  • lib/projects/access.ts is already a pure policy module with 11 named predicatescanBrowse · canEdit · canComment · canModerateComments · canCreateAttachments · canDeleteAllAttachments · canManageWatchers · canManageProject · canSubmitToTriage · canUpvotePublicRequest · canCommentPublicRequest — each deciding over { accessLevel, workspaceRole, projectRole }. Those predicate NAMES are already a permission vocabulary; what is missing is the indirection that lets a ROLE choose its own set.
  • The enforcement surface is coarse and wide: git grep assertCan over lib + app returns 56 assertCanBrowse · 51 assertCanManage · 45 assertCanEdit call sites. assertCanManage alone conflates every administrative domain — members, workflow, board, fields, components, estimation, automation, repositories, AI planning, code access — into one boolean.
  • The UI gate exists but is wide open: lib/settings/projectSettingsNav.ts gives every settings entry an access predicate, and 10 of its 11 entries are browse-gated (only automation is manage-gated). Its own comment records the intent — "a member VIEWS every section". So a plain member does see Members, Code access, Workflow, Board, Fields, Components, AI planning; only the destination's writes are refused. The user's reading of the product is correct, and the mechanism to fix it is already in place — it is the POLICY that is missing.
  • app/(authed)/_components/ProjectAccessProvider.tsx carries exactly two booleans into the client tree — { canEdit, canManage }. Every client-side affordance decision the product makes today is made from those two bits.

The model (rung 1 — CHECKED against the mirror + the field, not asserted)

What this epic therefore builds: permissions are a code-owned catalog, never user-authored rows — a permission the code does not enforce would be a lie on a settings screen. Built-in roles stay immutable and are seeded to reproduce today's behaviour exactly. Custom roles are project-scoped, authored by a project admin, and start from a built-in BASE. Access level (public/open/limited/private) is unchanged and keeps deciding who is in the project at all; the role decides what they may do once in.

Scope boundary

In: the permission catalog and its totality guards; built-in roles as permission sets; the lib/projects/access.ts re-expression; splitting project:administer into per-domain administrative permissions; project-scoped custom roles (schema, service, API, editor UI, assignment, delete-with-reassign); the actor's resolved permission set delivered to the client; the hide-versus-disable visibility rule and the surface sweep that applies it; the amendment of the 2026-06-09 gating directive recorded in design/projects/design-notes.md.

Out (named, with their owners): WORKSPACE/org-scoped custom roles — workspace MemberRole (owner/admin/member) is untouched by this epic, and the always-pass workspace-manager rail survives intact; API-token scopes (lib/mcp/scopes.ts) remain a separate, deliberately separate vocabulary that NARROWS a role and is not merged into the catalog; issue-level security schemes (Jira's per-issue security) — not planned, not needed by any shipped surface; permission INHERITANCE across projects, which Plane offers and Motir's flat workspace→project shape does not need.

Repo: every deliverable in this epic ships in motir-core — the policy module, the services, the settings surfaces and the tests all live there. Nothing here touches motir-ai or the gateway.