Authorization & permissions
Centralized, unit-tested authorization: org roles, project/repo roles, visibility grants, custom roles, teams, and site admins.
Authorization is centralized in packages/permissions, a dependency-free TypeScript package unit-tested independently of the NestJS app. Every controller resolves a permission set through it before performing a mutation or returning a resource — there is no ad hoc "is this user special-cased" branching elsewhere in the codebase.
Organization roles#
| Role | ORG_VIEW | ORG_CREATE_PROJECT | ORG_MANAGE_MEMBERS | ORG_MANAGE_TEAMS | ORG_MANAGE_SETTINGS | ORG_MANAGE_SECRETS | ORG_VIEW_AUDIT_LOG | ORG_DELETE |
|---|---|---|---|---|---|---|---|---|
| GUEST | ✓ | |||||||
| MEMBER | ✓ | ✓ | ||||||
| ADMIN | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
| OWNER | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Project / repository roles#
Repository access is governed by the project role ladder (ProjectRole), since a project's repositories share reviewers and contributors by default:
| Role | REPO_READ/CLONE | REPO_PUSH | CREATE_BRANCH | CREATE_PR | MERGE | MANAGE | MANAGE_WEBHOOKS | MANAGE_CI | MANAGE_COLLABORATORS |
|---|---|---|---|---|---|---|---|---|---|
| GUEST | |||||||||
| REPORTER | ✓ | ||||||||
| DEVELOPER | ✓ | ✓ | ✓ | ✓ | |||||
| MAINTAINER | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Roles are ranked (GUEST < REPORTER < DEVELOPER < MAINTAINER) and a user's effective role on a repository is the highest of: a direct repository-level role, their project-level role, or — critically — an automatic MAINTAINER floor if they hold OWNER or ADMIN at the organization level. Organization admins are never locked out of a repository by an unrelated repository-level grant.
Visibility grants permissions too#
Independent of any role, resolveRepositoryPermissions also grants REPO_READ/REPO_CLONE from visibility alone: PUBLIC repositories grant read to unauthenticated requests, INTERNAL repositories grant read to any authenticated user regardless of organization membership. Role-derived permissions and visibility-derived permissions are unioned, not chosen between — a GUEST on a PUBLIC repository can still read it.
Custom roles#
An organization can define a custom role: an explicit RepoPermission[] set that replaces the ladder position a ProjectRole would otherwise imply for a specific repository grant, rather than adding to it. Every other grant path (project-wide role, team grants, the org admin/owner MAINTAINER floor) still unions in on top of a custom role normally — a custom role narrows or reshapes one specific grant, it doesn't cap what a user can otherwise reach through a different path.
Teams#
A Team groups organization members and can be granted a project or repository role once, applied to every member of the team. Team membership changes take effect immediately for every resource the team has been granted access to — there's no separate propagation step, because permission resolution happens live, at request time, not via a materialized ACL.
Site administrators#
A user with isAdmin: true bypasses every check above entirely: resolveRepositoryPermissions/resolveOrganizationPermissions short-circuit to the full permission set. This is intentionally the only "god mode" bit in the system — it's a single boolean on User, toggled by another site admin under Admin → Users, and every use of it (including simply viewing a private repository as an admin) is available for audit via impersonation logging when acting as another user (see Instance administration).
