Software Factories on AWS

Self-hosted platform for standing up and running software factories on AWS: complete DevSecOps environments with secure delivery pipelines, supply-chain controls, compliance evidence and multi-tenant governance — in minutes, not months.

Permission Contract

This file is maintained BY HAND. There is no regenerate flag, and that is the point.

Highlights

Overview

Every route’s authorization is also recorded in frontend/src/generated/route-table/<service>.json, which is generated from the routes themselves. That makes permission weakening invisible: relax a gate and the generated table changes to match, so the diff looks like whatever the change intended. Nothing in review says “this route got easier to reach”.

This file is the human-stated counterpart, and it exists for reviewers as much as for authors. If you are looking for what a permission means rather than which route demands it, see Permissions.

How it works

  1. Each service’s route-coverage test generates its table from the routes, and rewrites it on demand with UPDATE_ROUTE_TABLES=1.
  2. frontend/test/permission-contract.test.ts renders each generated route into one canonical authorization string, using the tokens in Notation below.
  3. It parses every data row of this file into the same shape.
  4. It fails on any disagreement — a gated route missing from this file, a row for a route that no longer exists, or a row that states something different from what the route now enforces.
  5. The failure names the exact line to edit. So a relaxed gate cannot land until someone states the new requirement here, by hand, in the same commit.

A further assertion checks that every notation token appearing in a row is explained above the table — a row whose rendering uses an unexplained token is unreviewable.

Maintaining it

  1. Change the route’s gate in the service as usual.
  2. Regenerate that service’s route table (UPDATE_ROUTE_TABLES=1 on its route-coverage test).
  3. Run the contract test. It will fail and name the route plus what the route now requires.
  4. Edit the row here by hand to state the new requirement. Copy the routeRequires string from the failure — it is already in canonical form.
  5. If you introduced a new token, add it to the Notation table too, or the token assertion fails.
  6. Check the UI side. Look at KNOWN_UI_GATE_MISMATCHES in frontend/test/route-permissions.test.tsx; if the control still checks the old gate, the dashboard is now stricter than the API.
  7. Say so in the commit message when the change is a weakening, and expect a security review.

Review rule

A diff that touches this file is a permission change. Review it as a security change: for each altered row, ask what a caller can now reach that they could not before. sysadmin → a permission, all(...) → any(...), a dropped aal2/step-up(...)/system-org, a widened internal(...) caller list, or an added +svc are all weakenings, whatever the commit message says.

Removing a row means the route is gone. Adding one means a newly gated route — check it is gated at the right level, not merely gated.

The UI side of the same question is pinned separately, in frontend/test/route-permissions.test.tsx: every write route here is either mapped to a dashboard control whose gate that suite proves by rendering it with and without the permission, or categorized there with the caller that drives it. Its KNOWN_UI_GATE_MISMATCHES list names the routes whose control checks a different gate from the row below — read it alongside this file when a row changes, because relaxing a gate here can leave the UI as the only thing still enforcing the old one.

Area notes

These explain why particular families of rows look the way they do.

Plugin ecosystem governance routes

Every /plugins/ecosystem/* row carries system-org + ... + aal2: only the system org decides anything in the plugin ecosystem, and only from an MFA-grade session (see Plugin Publishing). A row losing system-org or aal2, or gaining a tenant permission, is a governance weakening.

The request-decision rows (/requests/:id/approve, /second-approve, /reject) show no step-up(...) because the step-up is demanded per request KIND inside the chain — yank, transfer, claim, profile change, Verified application, moderation actions. Two-person approval and separation of duties are enforced by the service and pinned by api/plugin/test/ecosystem-moderation.test.ts.

The tenant /plugins/publisher* and /plugins/publish-requests* rows only submit requests or narrow the caller’s own reach, which api/plugin/test/ecosystem-governance.test.ts enforces.

Org-local install routes

The install rows (/plugins/catalog, /plugins/listings/*/install-state, /plugins/installs*, /plugins/install-policy, /plugins/shadowing) act only on the caller’s own org.

plugins:install may install, upgrade and uninstall — but an install of an approval-required tier made without plugin_installs:manage only becomes a pending request, and moving such an install across a major or breaking version needs plugin_installs:manage too. Both are decided inside the service, so they don’t show in the rows.

Loosening PUT /plugins/install-policy below plugin_installs:manage + step-up(any) lets any member widen what the org’s pipelines may run, which is a weakening.

Plugin security notification routes

The rows under /plugins/security-notifications* decide where the org’s blocked-build (N30) and rescan (N31) notices go — including an outbound webhook and an external address. Loosening the PUT or the test send below org:settings would let any member redirect security notices or aim the webhook, which is a weakening.

The anonymous POST /public/plugin-security-notifications/confirm carries no caller identity — its single-use emailed token is the authority — so it has no row here. Its waiver is pinned in api/plugin/test/route-coverage.test.ts.

Plugin reviews

The review writes (/plugins/listings/*/reviews, /plugins/reviews/*) are gated on plugins:read on purpose: every member who can browse the catalog may rate and review (see Permissions). The aal1 is what makes them a PERSON’s action — requireAssurance({ minAssurance: 1 }) refuses service accounts and exchanged access keys with HUMAN_SESSION_REQUIRED. Dropping the aal1 lets automation write, vote on and report reviews, which is a weakening.

Who may act on WHICH review is decided in the service and pinned by api/plugin/test/ecosystem-reviews.test.ts: the author edits and deletes; nobody from the publisher’s own org reviews or votes; only the listing publisher’s own managers reply.

The /plugins/ecosystem/reviews* moderation rows follow the governance rule above. Like the reserved names, they carry no step-up — hiding or restoring user content needs none.

Deliberately read-gated writes

One write is gated on a read permission on purpose: message POST /messages/support.

Contacting support is self-service — every member who can open the Messages page (messages:read) must be able to file a request, including a read-only one who holds no messages:write. The route is safe at that floor because it takes no recipient: the server forces every message to the support desk on the reserved support channel and discards any recipient the body carries. POST /messages stays on messages:write.

Notation

Token Meaning
any(a\|b) Caller holds at least one of these permissions
all(a\|b) Caller holds every one of these permissions
+svc A service principal also passes, without holding the permission
sysadmin Platform administrator only (isSuperAdmin)
system-org The caller’s active org must be the system org (requireSystemOrg, the plugin-ecosystem governance boundary). Dropping it from a route gated on plugins:moderate / publishers:verify is a weakening — and fails api-core’s findSystemOrgGuardViolations too
service-principal Any signed service token
internal(a,b) Peer services only, and only these callers — refuses every user token
feature(f) Paid entitlement f required
scope(s) Machine-credential scope s required
aalN Authenticator assurance level ≥ N
aalN(except c) …of every caller except the named carve-out c. Today the only one is bootstrap-setup: a fresh install’s single administrator, who has no second factor to be MFA-grade with yet, on the setup calls init-platform.sh makes. Step-up still applies, the reach stays limited to the bootstrap allowlist, and a live re-read bounds it to BOOTSTRAP_SETUP_WINDOW_MS (default 24 h) after the install and to an exception that has not yet closed
step-up(m,…) Recent re-authentication with one of these methods
org-admin-assurance The org’s “administrative actions require MFA” policy applies
authenticated Signed in, nothing more
Service Method Path Required authorization
ask POST /ask any(pipelines:read|plugins:read|templates:read) + feature(ai_generation)
ask POST /ask/agent/stream any(pipelines:read|plugins:read|templates:read) + feature(ai_generation)
ask GET /ask/availability any(pipelines:read|plugins:read|templates:read) + feature(ai_generation)
ask POST /ask/feedback any(pipelines:read|plugins:read|templates:read)
ask GET /ask/providers any(pipelines:read|plugins:read|templates:read) + feature(ai_generation)
ask POST /ask/stream any(pipelines:read|plugins:read|templates:read) + feature(ai_generation)
billing GET /billing/admin/discounts sysadmin
billing POST /billing/admin/discounts sysadmin
billing DELETE /billing/admin/discounts/:id sysadmin
billing GET /billing/admin/discounts/:id sysadmin
billing PUT /billing/admin/discounts/:id sysadmin
billing POST /billing/admin/discounts/:id/apply sysadmin
billing POST /billing/admin/discounts/:id/preview sysadmin
billing POST /billing/admin/discounts/:id/token sysadmin
billing GET /billing/admin/events sysadmin
billing GET /billing/admin/promotions sysadmin
billing POST /billing/admin/promotions sysadmin
billing DELETE /billing/admin/promotions/:id sysadmin
billing GET /billing/admin/promotions/:id sysadmin
billing PUT /billing/admin/promotions/:id sysadmin
billing POST /billing/admin/promotions/:id/activate sysadmin
billing POST /billing/admin/promotions/:id/grant sysadmin
billing POST /billing/admin/promotions/:id/preview sysadmin
billing GET /billing/admin/promotions/:id/spend sysadmin
billing GET /billing/admin/subscriptions sysadmin
billing PUT /billing/admin/subscriptions/:id sysadmin + step-up(any)
billing GET /billing/admin/summary sysadmin
billing GET /billing/bundles any(billing:read)
billing GET /billing/events any(billing:read)
billing GET /billing/invoices any(billing:read)
billing POST /billing/marketplace/claim any(billing:manage) + org-admin-assurance
billing GET /billing/marketplace/entitlements any(billing:read)
billing POST /billing/portal any(billing:manage) + org-admin-assurance
billing GET /billing/subscriptions any(billing:read)
billing POST /billing/subscriptions any(billing:manage) + org-admin-assurance
billing PUT /billing/subscriptions/:id any(billing:manage) + org-admin-assurance
billing POST /billing/subscriptions/:id/addons any(billing:manage) + org-admin-assurance
billing DELETE /billing/subscriptions/:id/addons/:bundleId any(billing:manage) + org-admin-assurance
billing POST /billing/subscriptions/:id/addons/preview any(billing:read)
billing POST /billing/subscriptions/:id/cancel any(billing:manage) + step-up(any) + org-admin-assurance
billing POST /billing/subscriptions/:id/discounts any(billing:manage) + org-admin-assurance
billing DELETE /billing/subscriptions/:id/discounts/:discountId any(billing:manage) + org-admin-assurance
billing POST /billing/subscriptions/:id/discounts/preview any(billing:read)
billing POST /billing/subscriptions/:id/reactivate any(billing:manage) + org-admin-assurance
billing DELETE /billing/subscriptions/by-org/:orgId sysadmin
billing GET /billing/subscriptions/by-org/:orgId/billable sysadmin
billing POST /billing/subscriptions/checkout any(billing:manage) + org-admin-assurance
billing GET /billing/summary any(billing:read)
billing GET /billing/summary/allocation any(billing:read)
billing GET /billing/summary/usage-by-team any(billing:read) + feature(team_usage_analytics)
billing GET /billing/usage any(billing:read)
compliance GET /compliance/audit any(compliance:read)
compliance GET /compliance/entitlements/:orgId service-principal + internal(billing)
compliance PUT /compliance/entitlements/:orgId service-principal + internal(billing)
compliance POST /compliance/events/entity service-principal + internal(pipeline,plugin)
compliance GET /compliance/exemptions any(compliance:read)
compliance POST /compliance/exemptions any(compliance:read)
compliance DELETE /compliance/exemptions/:id any(compliance:write)
compliance PUT /compliance/exemptions/:id/review any(compliance:write)
compliance POST /compliance/exemptions/bulk any(compliance:read)
compliance GET /compliance/notification-preferences any(compliance:read)
compliance PUT /compliance/notification-preferences any(compliance:write)
compliance GET /compliance/posture service-principal + internal(reporting)
compliance GET /compliance/policies any(compliance:read)
compliance POST /compliance/policies any(compliance:write)
compliance DELETE /compliance/policies/:id any(compliance:write)
compliance GET /compliance/policies/:id any(compliance:read)
compliance PUT /compliance/policies/:id any(compliance:write)
compliance POST /compliance/policies/:id/purge any(compliance:write) + step-up(any)
compliance POST /compliance/policies/:id/restore any(compliance:write) + step-up(any)
compliance GET /compliance/policies/deleted any(compliance:read)
compliance GET /compliance/published-rules any(compliance:read)
compliance GET /compliance/rules any(compliance:read)
compliance POST /compliance/rules any(compliance:write)
compliance DELETE /compliance/rules/:id any(compliance:write)
compliance GET /compliance/rules/:id any(compliance:read)
compliance PUT /compliance/rules/:id any(compliance:write)
compliance GET /compliance/rules/:id/history any(compliance:read)
compliance POST /compliance/rules/:id/purge any(compliance:write) + step-up(any)
compliance POST /compliance/rules/:id/restore any(compliance:write) + step-up(any)
compliance GET /compliance/rules/deleted any(compliance:read)
compliance GET /compliance/scan-schedules any(compliance:read)
compliance POST /compliance/scan-schedules any(compliance:write)
compliance DELETE /compliance/scan-schedules/:id any(compliance:write)
compliance PUT /compliance/scan-schedules/:id any(compliance:write)
compliance PATCH /compliance/scan-schedules/:id/active any(compliance:write)
compliance GET /compliance/scans any(compliance:read)
compliance POST /compliance/scans any(compliance:write)
compliance GET /compliance/scans/:id any(compliance:read)
compliance POST /compliance/scans/:id/cancel any(compliance:write)
compliance GET /compliance/subscriptions any(compliance:read)
compliance POST /compliance/subscriptions any(compliance:read)
compliance DELETE /compliance/subscriptions/:ruleId any(compliance:write)
compliance PATCH /compliance/subscriptions/:ruleId any(compliance:read)
compliance DELETE /compliance/subscriptions/:ruleId/pin any(compliance:write)
compliance POST /compliance/subscriptions/:ruleId/pin any(compliance:write)
compliance POST /compliance/subscriptions/auto-subscribe service-principal + internal(platform)
compliance POST /compliance/subscriptions/bulk any(compliance:read)
compliance POST /compliance/subscriptions/clone any(compliance:write)
compliance GET /compliance/subscriptions/enforced any(compliance:read)
compliance POST /compliance/subscriptions/preview any(compliance:read)
compliance POST /compliance/subscriptions/preview/impact any(compliance:read)
compliance GET /compliance/templates any(compliance:read)
compliance POST /compliance/templates/apply any(compliance:write)
compliance POST /compliance/validate/pipeline any(compliance:read)+svc
compliance POST /compliance/validate/pipeline/dry-run any(compliance:read)+svc
compliance POST /compliance/validate/plugin any(compliance:read)+svc
compliance POST /compliance/validate/plugin/dry-run any(compliance:read)+svc
image-registry POST /api/admin/gc any(registry:write)
image-registry GET /api/admin/storage/:prefix any(registry:read)
image-registry GET /api/images any(registry:read)
image-registry DELETE /api/images/:name any(registry:write)
image-registry GET /api/images/:name/blobs/:digest any(registry:read)
image-registry DELETE /api/images/:name/manifests/:reference any(registry:write)
image-registry GET /api/images/:name/manifests/:reference any(registry:read)
image-registry GET /api/images/:name/tags any(registry:read)
image-registry POST /api/images/copy all(registry:read|registry:write)
image-registry POST /internal/plugin-signatures service-principal + internal(plugin)
image-registry POST /internal/plugin-publications service-principal + internal(plugin)
image-registry POST /internal/plugin-publications/resign service-principal + internal(plugin)
image-registry DELETE /internal/quarantine/:submissionId service-principal + internal(plugin)
image-registry POST /internal/quarantine/:submissionId/credential service-principal + internal(plugin)
image-registry POST /internal/plugin-publications/yank service-principal + internal(plugin)
image-registry POST /internal/plugin-publications/retag service-principal + internal(plugin)
image-registry POST /internal/plugin-publications/gc service-principal + internal(plugin)
image-registry GET /internal/plugin-publications/verify service-principal + internal(plugin)
image-registry POST /internal/plugin-publications/verify-cache/invalidate service-principal + internal(plugin)
message GET /messages any(messages:read)
message POST /messages any(messages:write)
message DELETE /messages/:id any(messages:write)
message GET /messages/:id any(messages:read)
message PATCH /messages/:id any(messages:write)
message GET /messages/:id/attachments any(messages:read)
message POST /messages/:id/purge any(messages:write) + step-up(any)
message PUT /messages/:id/read any(messages:read)
message POST /messages/:id/reply any(messages:write)
message POST /messages/:id/restore any(messages:write) + step-up(any)
message GET /messages/:id/thread any(messages:read)
message PUT /messages/:id/thread/read any(messages:read)
message GET /messages/announcements any(messages:read)
message POST /messages/attachments any(messages:write)
message GET /messages/attachments/:id any(messages:read)
message GET /messages/conversations any(messages:read)
message GET /messages/deleted any(messages:read)
message POST /messages/internal/notify service-principal + internal(billing,compliance,platform)
message DELETE /messages/internal/org/:orgId/attachments service-principal + internal(platform)
message POST /messages/notifications/ticket any(messages:read)
message GET /messages/recipients/orgs any(messages:write)
message POST /messages/support any(messages:read)
message GET /messages/unread/count any(messages:read)
pipeline GET /pipeline-templates any(templates:read)
pipeline POST /pipeline-templates any(templates:write)
pipeline DELETE /pipeline-templates/:id any(templates:write)
pipeline GET /pipeline-templates/:id any(templates:read)
pipeline PUT /pipeline-templates/:id any(templates:write)
pipeline POST /pipeline-templates/:id/instantiate any(templates:read)
pipeline POST /pipeline-templates/:id/purge any(templates:write) + step-up(any)
pipeline POST /pipeline-templates/:id/restore any(templates:write) + step-up(any)
pipeline GET /pipeline-templates/deleted any(templates:read)
pipeline GET /pipelines any(pipelines:read)
pipeline POST /pipelines any(pipelines:write)
pipeline DELETE /pipelines/:id any(pipelines:write)
pipeline GET /pipelines/:id any(pipelines:read)
pipeline PUT /pipelines/:id any(pipelines:write)
pipeline POST /pipelines/:id/purge any(pipelines:write) + step-up(any)
pipeline POST /pipelines/:id/restore any(pipelines:write) + step-up(any)
pipeline GET /pipelines/:id/scorecard any(pipelines:read) + feature(advanced_reporting)
pipeline POST /pipelines/:pipelineId/executions any(pipelines:write)
pipeline POST /pipelines/:pipelineId/executions/:executionId/stop any(pipelines:write)
pipeline POST /pipelines/bulk/create any(pipelines:write) + feature(bulk_operations)
pipeline POST /pipelines/bulk/delete any(pipelines:write) + feature(bulk_operations)
pipeline PUT /pipelines/bulk/update any(pipelines:write) + feature(bulk_operations)
pipeline GET /pipelines/deleted any(pipelines:read)
pipeline GET /pipelines/find any(pipelines:read)
pipeline POST /pipelines/generate any(pipelines:write) + feature(ai_generation)
pipeline POST /pipelines/generate/from-url any(pipelines:write) + feature(ai_generation)
pipeline POST /pipelines/generate/from-url/stream any(pipelines:write) + feature(ai_generation)
pipeline POST /pipelines/generate/stream any(pipelines:write) + feature(ai_generation)
pipeline GET /pipelines/providers any(pipelines:read) + feature(ai_generation)
pipeline GET /pipelines/registry any(pipelines:read)
pipeline POST /pipelines/registry any(pipelines:write)
pipeline DELETE /pipelines/registry/:id any(pipelines:write)
pipeline GET /pipelines/scorecard any(pipelines:read) + feature(advanced_reporting)
pipeline POST /pipelines/validate any(pipelines:write)
platform GET /admin/org-idp any(org:idp)
platform DELETE /admin/org-idp/:orgId any(org:idp) + aal2 + step-up(totp,webauthn)
platform GET /admin/org-idp/:orgId any(org:idp)
platform PATCH /admin/org-idp/:orgId any(org:idp) + aal2 + step-up(totp,webauthn)
platform PUT /admin/org-idp/:orgId any(org:idp) + aal2 + step-up(totp,webauthn)
platform GET /admin/orgs/:orgId/k8s-namespace.yaml sysadmin + step-up(any)
platform DELETE /admin/orgs/:orgId/kms-config any(org:kms) + aal2 + step-up(totp,webauthn)
platform GET /admin/orgs/:orgId/kms-config any(org:kms)
platform PUT /admin/orgs/:orgId/kms-config any(org:kms) + aal2 + step-up(totp,webauthn)
platform POST /admin/orgs/:orgId/kms-config/test any(org:kms)
platform GET /admin/console-check sysadmin + aal2
platform GET /admin/summary sysadmin
platform DELETE /admin/users/:id/grants sysadmin + aal2 + step-up(totp,webauthn)
platform POST /admin/users/:id/grants sysadmin + aal2 + step-up(totp,webauthn)
platform POST /admin/users/:id/mfa-reset sysadmin + aal2 + step-up(totp,webauthn)
platform POST /audit/events service-principal + internal(ask,billing,compliance,image-registry,message,pipeline,plugin,quota,reporting)
platform GET /audit/verify sysadmin
platform GET /dashboards any(dashboards:read)
platform POST /dashboards any(dashboards:write)
platform GET /dashboards/:id any(dashboards:read)
platform POST /dashboards/:id/clone any(dashboards:write)
platform GET /dashboards/deleted any(dashboards:read)
platform GET /internal/ecosystem/approvers service-principal + internal(plugin)
platform GET /internal/ecosystem/publisher-eligibility/:orgId service-principal + internal(plugin)
platform POST /internal/notify-email service-principal + internal(compliance,plugin,reporting)
platform GET /internal/reporting/access-posture/:orgId service-principal + internal(reporting)
platform GET /internal/reporting/recipient-check/:orgId service-principal + internal(reporting)
platform GET /internal/reporting/report-authority/:orgId/:userId service-principal + internal(reporting)
platform GET /internal/notify-email/status service-principal + internal(ask,plugin,reporting)
platform GET /invitation any(invitations:manage)
platform DELETE /invitation/:invitationId any(invitations:manage) + org-admin-assurance
platform POST /invitation/:invitationId/resend any(invitations:manage) + org-admin-assurance
platform POST /invitation/send any(invitations:manage) + org-admin-assurance
platform GET /observability/alert-destinations any(observability:read)
platform POST /observability/alert-destinations any(observability:write)
platform DELETE /observability/alert-destinations/:id any(observability:write)
platform PUT /observability/alert-destinations/:id any(observability:write)
platform POST /observability/alert-destinations/:id/purge any(observability:write) + step-up(any)
platform POST /observability/alert-destinations/:id/restore any(observability:write) + step-up(any)
platform POST /observability/alert-destinations/:id/test any(observability:write)
platform GET /observability/alert-destinations/all sysadmin
platform GET /observability/alert-destinations/deleted any(observability:read)
platform GET /observability/alert-rules any(observability:read)
platform POST /observability/alert-rules any(observability:write)
platform DELETE /observability/alert-rules/:id any(observability:write)
platform PUT /observability/alert-rules/:id any(observability:write)
platform POST /observability/alert-rules/:id/purge any(observability:write) + step-up(any)
platform POST /observability/alert-rules/:id/restore any(observability:write) + step-up(any)
platform GET /observability/alert-rules/deleted any(observability:read)
platform GET /observability/alert-rules/materialized.yml sysadmin
platform GET /observability/alerts any(observability:read)
platform GET /observability/audit-query any(observability:read)
platform GET /observability/catalog any(observability:read)
platform GET /observability/logs any(observability:read)
platform GET /observability/logs/context any(observability:read)
platform GET /observability/logs/export any(logs:export) + org-admin-assurance
platform GET /observability/logs/raw any(observability:read)
platform GET /observability/logs/volume any(observability:read)
platform GET /observability/query any(observability:read)
platform GET /observability/silences any(observability:read)
platform POST /observability/silences any(observability:write)
platform DELETE /observability/silences/:id any(observability:write)
platform POST /organization any(org:settings)
platform POST /organization/names service-principal
platform DELETE /organization/:id sysadmin + step-up(any)
platform PUT /organization/:id sysadmin + step-up(any)
platform GET /organization/:id/authenticator-policy any(org:settings)
platform PATCH /organization/:id/authenticator-policy any(org:settings) + step-up(any)
platform GET /organization/:id/domains any(org:settings)
platform POST /organization/:id/domains any(org:settings)
platform DELETE /organization/:id/domains/:domainId any(org:settings)
platform PATCH /organization/:id/domains/:domainId any(org:settings)
platform POST /organization/:id/domains/:domainId/verify any(org:settings)
platform GET /organization/:id/export any(org:settings)
platform PATCH /organization/:id/identity any(org:settings)
platform DELETE /organization/:id/idp any(org:idp) + aal2 + step-up(totp,webauthn)
platform GET /organization/:id/idp any(org:idp)
platform PATCH /organization/:id/idp any(org:idp) + aal2 + step-up(totp,webauthn)
platform PUT /organization/:id/idp any(org:idp) + aal2 + step-up(totp,webauthn)
platform GET /organization/:id/idp/group-mappings any(roles:manage)
platform POST /organization/:id/idp/group-mappings any(roles:manage) + org-admin-assurance
platform DELETE /organization/:id/idp/group-mappings/:mappingId any(roles:manage) + org-admin-assurance
platform PUT /organization/:id/idp/group-mappings/:mappingId any(roles:manage) + org-admin-assurance
platform POST /organization/:id/idp/metadata/import any(org:idp)
platform GET /organization/:id/idp/sp-info any(org:idp)
platform POST /organization/:id/idp/test any(org:idp)
platform POST /organization/:id/idp/test/complete any(org:idp)
platform GET /organization/:id/impersonation-policy any(org:impersonation)
platform PATCH /organization/:id/impersonation-policy any(org:impersonation) + step-up(any)
platform GET /organization/:id/join-requests any(org:settings)
platform POST /organization/:id/join-requests/:reqId/:decision any(org:settings)
platform POST /organization/:id/members any(members:manage) + org-admin-assurance
platform DELETE /organization/:id/members/:userId any(members:manage) + org-admin-assurance
platform PATCH /organization/:id/members/:userId/activate any(members:manage) + org-admin-assurance
platform PATCH /organization/:id/members/:userId/deactivate any(members:manage) + org-admin-assurance
platform POST /organization/:id/members/bulk-add any(members:manage) + org-admin-assurance
platform GET /organization/:id/mfa-policy any(org:settings)
platform PATCH /organization/:id/mfa-policy any(org:settings) + step-up(any)
platform GET /organization/:id/mfa-resets any(members:manage)
platform POST /organization/:id/mfa-resets any(members:manage) + aal2 + step-up(any)
platform POST /organization/:id/mfa-resets/:requestId/approve any(members:manage) + aal2 + step-up(totp,webauthn)
platform POST /organization/:id/mfa-resets/:requestId/deny any(members:manage)
platform POST /organization/:id/move sysadmin + step-up(any)
platform GET /organization/:id/password-policy any(org:settings)
platform PATCH /organization/:id/password-policy any(org:settings) + step-up(any)
platform POST /organization/:id/restore any(org:settings) + step-up(any)
platform POST /organization/:id/roles any(roles:manage) + org-admin-assurance
platform DELETE /organization/:id/roles/:roleId any(roles:manage) + org-admin-assurance
platform PUT /organization/:id/roles/:roleId any(roles:manage) + org-admin-assurance
platform POST /organization/:id/roles/:roleId/members any(roles:manage) + org-admin-assurance
platform DELETE /organization/:id/roles/:roleId/members/:userId any(roles:manage) + org-admin-assurance
platform GET /organization/:id/service-accounts any(service_accounts:manage)
platform POST /organization/:id/service-accounts any(service_accounts:manage) + aal2(except bootstrap-setup) + step-up(any)
platform DELETE /organization/:id/service-accounts/:accountId any(service_accounts:manage) + step-up(any)
platform GET /organization/:id/service-accounts/:accountId any(service_accounts:manage)
platform PATCH /organization/:id/service-accounts/:accountId any(service_accounts:manage) + step-up(any)
platform POST /organization/:id/service-accounts/:accountId/keys any(service_accounts:manage) + aal2(except bootstrap-setup) + step-up(any)
platform DELETE /organization/:id/service-accounts/:accountId/keys/:keyId any(service_accounts:manage)
platform DELETE /organization/:id/teams/:teamId any(org:settings) + step-up(any)
platform GET /organization/:id/teams/deleted any(org:settings)
platform PATCH /organization/:id/tier sysadmin + step-up(any)
platform PATCH /organization/:id/transfer-owner any(org:settings) + aal2 + step-up(any)
platform PUT /organization/ai-config any(org:settings) + step-up(any)
platform GET /organizations sysadmin
platform GET /users any(members:manage)
platform POST /users any(members:manage)
platform DELETE /users/:id any(members:manage) + aal2 + step-up(any)
platform GET /users/:id any(members:manage)
platform PUT /users/:id any(members:manage) + aal2 + step-up(any)
platform PUT /users/:id/features any(members:manage) + aal2 + step-up(any)
platform POST /users/bulk-delete any(members:manage) + aal2 + step-up(any)
plugin GET /internal/plugins/public-names service-principal + internal(image-registry)
plugin GET /plugins any(plugins:read)
plugin POST /plugins any(plugins:write)
plugin DELETE /plugins/:id any(plugins:write)
plugin GET /plugins/:id any(plugins:read)
plugin GET /plugins/:id/sbom any(plugins:read)
plugin PUT /plugins/:id any(plugins:write)
plugin POST /plugins/:id/deprecate any(plugins:write)
plugin POST /plugins/:id/purge any(plugins:write) + step-up(any)
plugin POST /plugins/:id/restore any(plugins:write) + step-up(any)
plugin POST /plugins/:id/yank any(plugins:write)
plugin POST /plugins/bulk/delete any(plugins:write) + feature(bulk_operations)
plugin PUT /plugins/bulk/update any(plugins:write) + feature(bulk_operations)
plugin GET /plugins/catalog any(plugins:read)
plugin GET /plugins/deleted any(plugins:read)
plugin POST /plugins/deploy-generated any(plugins:write)
plugin GET /plugins/ecosystem/advisories system-org + any(plugins:moderate) + aal2
plugin POST /plugins/ecosystem/advisories system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin PATCH /plugins/ecosystem/advisories/:id system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin POST /plugins/ecosystem/advisories/:id/withdraw system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin GET /plugins/ecosystem/listings system-org + any(plugins:moderate) + aal2
plugin POST /plugins/ecosystem/listings/:id/state system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin POST /plugins/ecosystem/listings/:id/versions/:version/deprecate system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin POST /plugins/ecosystem/listings/:id/versions/:version/unyank system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin POST /plugins/ecosystem/listings/:id/versions/:version/yank system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin GET /plugins/ecosystem/overview system-org + any(plugins:moderate|publishers:verify) + aal2
plugin GET /plugins/ecosystem/publishers system-org + any(plugins:moderate|publishers:verify) + aal2
plugin POST /plugins/ecosystem/publishers/:id/suspend system-org + any(publishers:verify) + aal2 + step-up(any)
plugin POST /plugins/ecosystem/publishers/:id/tier system-org + any(publishers:verify) + aal2 + step-up(any)
plugin POST /plugins/ecosystem/publishers/:id/unsuspend system-org + any(publishers:verify) + aal2 + step-up(any)
plugin GET /plugins/ecosystem/requests system-org + any(plugins:moderate|publishers:verify) + aal2
plugin GET /plugins/ecosystem/requests/:id system-org + any(plugins:moderate|publishers:verify) + aal2
plugin GET /plugins/ecosystem/requests/:id/submission-sbom system-org + any(plugins:moderate) + aal2
plugin GET /plugins/ecosystem/requests/:id/submission-scan system-org + any(plugins:moderate) + aal2
plugin POST /plugins/ecosystem/requests/:id/approve system-org + any(plugins:moderate|publishers:verify) + aal2
plugin POST /plugins/ecosystem/requests/:id/reject system-org + any(plugins:moderate|publishers:verify) + aal2
plugin POST /plugins/ecosystem/requests/:id/second-approve system-org + any(plugins:moderate|publishers:verify) + aal2
plugin POST /plugins/ecosystem/resign system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin GET /plugins/ecosystem/reviews system-org + any(plugins:moderate) + aal2
plugin POST /plugins/ecosystem/reviews/:id/hold system-org + any(plugins:moderate) + aal2
plugin POST /plugins/ecosystem/reviews/:id/release system-org + any(plugins:moderate) + aal2
plugin POST /plugins/ecosystem/reviews/:id/remove system-org + any(plugins:moderate) + aal2
plugin POST /plugins/ecosystem/reviews/:id/remove-reply system-org + any(plugins:moderate) + aal2
plugin GET /plugins/ecosystem/reserved-names system-org + any(plugins:moderate) + aal2
plugin DELETE /plugins/ecosystem/reserved-names/:name system-org + any(plugins:moderate) + aal2
plugin PUT /plugins/ecosystem/reserved-names/:name system-org + any(plugins:moderate) + aal2
plugin GET /plugins/ecosystem/rules system-org + any(plugins:moderate) + aal2
plugin POST /plugins/ecosystem/rules system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin DELETE /plugins/ecosystem/rules/:id system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin PATCH /plugins/ecosystem/rules/:id system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin POST /plugins/ecosystem/rules/:id/approve-change system-org + any(plugins:moderate) + aal2 + step-up(any)
plugin GET /plugins/publish-requests any(plugins:read)
plugin POST /plugins/publish-requests any(plugins:publish|publishers:manage)
plugin POST /plugins/publish-requests/:id/transfer-response any(publishers:manage) + step-up(any)
plugin POST /plugins/publish-requests/:id/withdraw any(plugins:publish|publishers:manage)
plugin GET /plugins/publish-requests/draft any(plugins:publish)
plugin GET /plugins/publisher any(plugins:read)
plugin PATCH /plugins/publisher any(publishers:manage)
plugin POST /plugins/publisher any(publishers:manage)
plugin GET /plugins/publisher/advisories any(plugins:read)
plugin GET /plugins/publisher/incoming-transfers any(publishers:manage)
plugin GET /plugins/publisher/insights any(plugins:read)
plugin GET /plugins/publisher/listings any(plugins:read)
plugin POST /plugins/publisher/listings/:listingId/deprecate any(plugins:publish)
plugin POST /plugins/publisher/listings/:listingId/pause any(plugins:publish)
plugin POST /plugins/publisher/terms any(publishers:manage)
plugin GET /plugins/find any(plugins:read)
plugin POST /plugins/generate any(plugins:write) + feature(ai_generation)
plugin POST /plugins/generate/stream any(plugins:write) + feature(ai_generation)
plugin POST /plugins/inspect any(plugins:write)
plugin GET /plugins/install-policy any(plugins:read)
plugin PUT /plugins/install-policy any(plugin_installs:manage) + step-up(any)
plugin GET /plugins/installs any(plugins:read)
plugin POST /plugins/installs any(plugins:install)
plugin DELETE /plugins/installs/:id any(plugins:install)
plugin PATCH /plugins/installs/:id any(plugins:install)
plugin POST /plugins/installs/:id/approve any(plugin_installs:manage)
plugin GET /plugins/installs/change-requests any(plugin_installs:manage)
plugin POST /plugins/installs/:id/change-requests any(plugins:install)
plugin POST /plugins/installs/:id/change-requests/approve any(plugin_installs:manage)
plugin POST /plugins/installs/:id/change-requests/reject any(plugin_installs:manage)
plugin POST /plugins/installs/:id/deny any(plugin_installs:manage)
plugin GET /plugins/listings/:publisher/:name/install-state any(plugins:read)
plugin GET /plugins/listings/:publisher/:name/review-state any(plugins:read)
plugin POST /plugins/listings/:publisher/:name/reviews any(plugins:read) + aal1
plugin DELETE /plugins/reviews/:id any(plugins:read) + aal1
plugin PATCH /plugins/reviews/:id any(plugins:read) + aal1
plugin DELETE /plugins/reviews/:id/helpful any(plugins:read) + aal1
plugin PUT /plugins/reviews/:id/helpful any(plugins:read) + aal1
plugin DELETE /plugins/reviews/:id/reply any(publishers:manage) + aal1
plugin PUT /plugins/reviews/:id/reply any(publishers:manage) + aal1
plugin POST /plugins/reviews/:id/report any(plugins:read) + aal1
plugin POST /plugins/lookup any(plugins:read)
plugin GET /plugins/plugin-usage any(plugins:read)
plugin GET /plugins/providers any(plugins:read) + feature(ai_generation)
plugin GET /plugins/security-notifications any(plugins:read)
plugin PUT /plugins/security-notifications any(org:settings)
plugin POST /plugins/security-notifications/test any(org:settings)
plugin GET /plugins/shadowing any(plugins:read)
plugin DELETE /plugins/queue/dlq sysadmin
plugin GET /plugins/queue/dlq any(plugins:write)
plugin POST /plugins/queue/dlq/:jobId/replay any(plugins:write)
plugin GET /plugins/queue/failed any(plugins:write)
plugin POST /plugins/queue/failed/:jobId/retry any(plugins:write)
plugin GET /plugins/queue/status sysadmin
plugin GET /plugins/queue/triage any(plugins:write)
plugin GET /plugins/vuln-waivers any(plugins:read)
quota GET /quotas any(quotas:read)
quota DELETE /quotas/:orgId sysadmin + step-up(any)
quota GET /quotas/:orgId any(quotas:read)+svc
quota PUT /quotas/:orgId sysadmin
quota GET /quotas/:orgId/:quotaType any(quotas:read)+svc
quota GET /quotas/:orgId/at-risk any(quotas:read)
quota POST /quotas/:orgId/decrement service-principal + internal(ask,billing,compliance,image-registry,message,pipeline,platform,plugin,quota,reporting)
quota POST /quotas/:orgId/increment service-principal + internal(ask,billing,compliance,image-registry,message,pipeline,platform,plugin,quota,reporting)
quota POST /quotas/:orgId/reset sysadmin + step-up(any)
quota GET /quotas/all sysadmin
quota GET /quotas/at-risk sysadmin
reporting POST /reports/deployments/:executionId/outcome any(pipelines:write) + feature(advanced_reporting)
reporting GET /reports/execution/action-failures any(reports:read)
reporting GET /reports/execution/build-health any(reports:read)
reporting GET /reports/execution/count any(reports:read)
reporting GET /reports/execution/dora any(reports:read) + feature(advanced_reporting)
reporting GET /reports/execution/dora/trend any(reports:read) + feature(advanced_reporting)
reporting GET /reports/execution/duration any(reports:read)
reporting GET /reports/execution/environments any(reports:read) + feature(advanced_reporting)
reporting GET /reports/execution/errors sysadmin + any(reports:read)
reporting GET /reports/execution/list any(reports:read)
reporting GET /reports/execution/stage-bottlenecks any(reports:read)
reporting GET /reports/execution/stage-failures any(reports:read)
reporting POST /reports/execution/stream/ticket any(reports:read)
reporting GET /reports/execution/success-rate any(reports:read)
reporting GET /reports/incidents any(reports:read) + feature(advanced_reporting)
reporting POST /reports/incidents/test any(reports:read) + feature(advanced_reporting)
reporting GET /reports/ingest-health any(reports:read)
reporting GET /reports/plugins/build-duration any(reports:read)
reporting GET /reports/plugins/build-failures sysadmin + any(reports:read)
reporting GET /reports/plugins/build-success-rate any(reports:read)
reporting GET /reports/plugins/distribution any(reports:read)
reporting GET /reports/plugins/runtime-duration any(reports:read)
reporting GET /reports/plugins/runtime-success-rate any(reports:read)
reporting GET /reports/plugins/summary any(reports:read)
reporting GET /reports/plugins/versions any(reports:read)
reporting GET /reports/retention any(reports:read)
reporting PUT /reports/retention-sync/:orgId service-principal + internal(billing)
reporting PUT /reports/stakeholder-sync/:orgId service-principal + internal(billing)
reporting GET /reports/retention-sync/:orgId service-principal + internal(billing)
reporting GET /reports/settings/incidents any(reports:read) + feature(advanced_reporting)
reporting PUT /reports/settings/incidents any(reports:read) + any(org:settings) + feature(advanced_reporting)
reporting POST /reports/stakeholder-internal/owner-left/:orgId/:userId service-principal + internal(platform)
reporting GET /reports/stakeholder/definitions any(reports:read) + feature(stakeholder_reports)
reporting POST /reports/stakeholder/definitions any(reports:author) + feature(stakeholder_reports)
reporting DELETE /reports/stakeholder/definitions/:id any(reports:author) + feature(stakeholder_reports)
reporting GET /reports/stakeholder/definitions/:id any(reports:read) + feature(stakeholder_reports)
reporting PUT /reports/stakeholder/definitions/:id any(reports:author) + feature(stakeholder_reports)
reporting GET /reports/stakeholder/definitions/:id/runs any(reports:read) + feature(stakeholder_reports)
reporting POST /reports/stakeholder/definitions/:id/runs any(reports:author) + feature(stakeholder_reports)
reporting POST /reports/stakeholder/definitions/:id/transfer any(reports:author) + feature(stakeholder_reports)
reporting DELETE /reports/stakeholder/links/:id any(reports:share) + feature(stakeholder_reports)
reporting GET /reports/stakeholder/delivery-status any(reports:read) + feature(stakeholder_reports)
reporting GET /reports/stakeholder-preview any(reports:read)
reporting POST /reports/stakeholder-preview any(reports:author)
reporting GET /reports/stakeholder/policy any(reports:read) + feature(stakeholder_reports)
reporting PUT /reports/stakeholder/policy any(org:settings) + feature(stakeholder_reports)
reporting GET /reports/stakeholder/recipients any(reports:read) + feature(stakeholder_reports)
reporting POST /reports/stakeholder/recipients any(reports:author) + feature(stakeholder_reports)
reporting DELETE /reports/stakeholder/recipients/:id any(reports:author) + feature(stakeholder_reports)
reporting POST /reports/stakeholder/recipients/:id/resend any(reports:author) + feature(stakeholder_reports)
reporting GET /reports/stakeholder/runs/:id any(reports:read) + feature(stakeholder_reports)
reporting GET /reports/stakeholder/runs/:id/links any(reports:read) + feature(stakeholder_reports)
reporting GET /reports/stakeholder/runs/:id/pdf any(reports:read) + feature(stakeholder_reports)
reporting POST /reports/stakeholder/runs/:id/links any(reports:share) + feature(stakeholder_reports)
reporting PUT /reports/stakeholder/runs/:id/notes any(reports:author) + feature(stakeholder_reports)
reporting POST /reports/stakeholder/runs/:id/summary any(reports:author) + feature(stakeholder_reports)
reporting POST /reports/stakeholder/runs/:id/publish any(reports:share) + feature(stakeholder_reports)