Self-service AWS CodePipeline platform — developers ship compliant CI/CD pipelines in minutes via dashboard, CLI, CDK, or AI prompt, while platform teams enforce policy-as-code guardrails, governance, and per-team isolation.
Access control in Pipeline Builder is permission-based and single-source. A user’s effective permissions are the union of the Roles assigned to them — there is no hidden role-derived baseline. Everything below is scoped to an organization (or team); platform-operator powers live behind the global Super Admin flag, not a per-org permission.
This reference documents Pipeline Builder’s per-org, permission-based access control: the fine-grained resource:action catalog, the built-in and custom Roles that bundle those permissions, and how they’re enforced. It’s for admins managing Roles and developers gating routes. A user’s effective permissions are the deduplicated union of their assigned Roles (a Super Admin short-circuits to all), sourced from permissions.ts and enforced by the middleware in auth.ts; a startup backfill keeps built-in Roles synced to the current catalog. Read The model first, then the permission catalog, enforcement middleware, and the API for managing Roles.
resource:action permissions
(e.g. a “Billing Manager” role granting billing:read + billing:manage).
Stored in the roles / role_assignments collections.resolveUserPermissions(assignedPermissions, isSuperAdmin)
returns the deduplicated union of every assigned Role’s permissions. No Role
assigned → no permissions (a Super Admin short-circuits to all).roles:manage can author additional Roles from
the org-assignable catalog (see the carve-out below), bounded by a permission
ceiling: an author may only grant permissions they themselves hold, so a
roles:manage holder cannot mint a Role carrying capabilities they lack (a
Super Admin bypasses the ceiling).owner / admin / member label on a membership is
display and authority only (ownership transfer, seat accounting). It is
derived, and it does not grant permissions — those come only from Roles.| Category | Permissions | Notes |
|---|---|---|
| Pipelines | pipelines:read, pipelines:write, pipelines:publish |
:publish allows setting a pipeline public |
| Plugins | plugins:read, plugins:write, plugins:publish |
:publish allows setting a plugin public |
| Compliance | compliance:read, compliance:write |
|
| Members & access | members:manage, roles:manage, invitations:manage |
|
| Observability | dashboards:read, dashboards:write, observability:read, observability:write |
|
| Insights | reports:read, reports:rollup |
:rollup allows including descendant teams in reports |
| Messaging | messages:read, messages:write |
|
| Billing & quotas | billing:read, billing:manage, quotas:read |
|
| Registry | registry:read, registry:write |
Super Admin only — never grantable to a custom Role |
| Org settings | org:settings |
General org settings + AI provider config |
| SSO / IdP | org:idp |
Per-org SSO/IdP (OIDC) configuration — sensitive (controls login); split out of org:settings |
| KMS | org:kms |
Customer-managed KMS key configuration — sensitive (controls encryption); split out of org:settings |
:read permissions are enforced. Withholding quotas:read / reports:read /
billing:read / messages:read from a custom Role actually blocks that read
(backend routes and the frontend nav/page guards check it). Built-in Admin and
Member both include all reads, so only bespoke custom Roles that drop a read are
affected.
Registry carve-out. registry:read / registry:write are in
SUPERADMIN_ONLY_PERMISSIONS: they’re in no built-in Role bundle and can’t be
requested by a custom Role (sanitizePermissions strips them), so the only holder
is a Super Admin via implicit-all.
pipelines:* (read/write),
plugins:* (read/write), compliance:read, dashboards:read,
observability:read, reports:read, messages:read/write, billing:read,
quotas:read. No :publish, no management, no :rollup.pipelines:publish,
plugins:publish, and reports:rollup.registry:*.Routes gate writes with permission middleware; a denied state-changing
request also emits an authz.denied audit event.
Not the same as the mesh. This page covers application authorization — per-org capability checks on API routes. Beneath it, the Istio ambient service mesh enforces network authorization (L4, by service identity): which services may reach which services at all. A request must pass both — the mesh
AuthorizationPolicy(cansa/pipelinereachplatform:3000?) and thenrequirePermission(does this user hold the capability?). Don’t conflate a mesh 403 (identity not allow-listed) with an app 403 (missing permission).
| Middleware | Semantics |
|---|---|
requirePermission(a, b, …) |
passes if the caller holds any of the listed permissions (Super Admin passes via implicit-all) |
requireAllPermissions(a, b, …) |
passes only if the caller holds all listed permissions |
requirePermissionOrService(a, …) |
like requirePermission, but ALSO admits an internal service:* principal — used on READ routes that both users and service-to-service callers hit (a service token carries no permission claims and would otherwise be wrongly denied) |
Public-visibility is permission-based too: resolveAccessModifier grants public
only to a caller holding the resource’s :publish permission — so a custom Role
can be granted publish rights instead of being forced private by its coarse label.
Downward report roll-up (?includeDescendants) requires reports:rollup.
Access tokens are short-lived (15 min) and carry a tokenVersion. On any
privilege change (role edit, permission change, superadmin grant/revoke), platform
bumps the user’s tokenVersion and publishes it to a Redis-backed revocation
store; requireAuth rejects a token whose version is behind (fail-open if Redis
is unavailable — auth then degrades to natural token expiry, never a lockout).
GET|POST /api/organization/:id/roles # list / create Roles
PUT|DELETE /api/organization/:id/roles/:roleId # update / delete a custom Role
POST|DELETE /api/organization/:id/roles/:roleId/members/:uid # add / remove a Role member
POST / PUT / DELETE require roles:manage. Custom-Role authoring validates
the requested permissions against the org-assignable set (the registry carve-out
is rejected) and against the author’s own permissions (the permission ceiling
above) — a request granting a permission the author lacks is rejected 403.
A parent-org admin/owner can administer its teams (members, rules, quotas) without a separate membership — fine-grained delegation applies within the team’s own tenancy boundary, and team-local Roles still bind. See Org → Team Hierarchy.