.ai/principles/distilled/permissions-rest-gpat.md
Prerequisite: If you haven't already, also read .ai/principles/distilled/permissions-fundamentals.md - it contains foundational rules that apply to all permissions work.
bin/permission command rather than creating them manually.config/authz/permissions/<resource>/<action>.yml; DO NOT add extra directories between the base path and the filename.force_delete_ai_catalog_item), pass -a <action> -r <resource> flags to bin/permission to override the default split; otherwise the command places the file under the wrong resource directory.DELETE /projects/:id/jobs/:job_id/artifacts → delete_job_artifact).read_<resource> permission for both list and show (GET) operations on the same resource.create_pipeline_schedule_variable).update_<resource> permission covering all attribute updates; DO NOT create per-attribute permissions such as update_issue_title.feature_category in the resource .metadata.yml to a valid entry from config/feature_categories.yml; look at existing endpoints in the same API file for the correct value.bundle exec rake gitlab:permissions:validate (or rely on the Lefthook pre-push hook) to catch naming and structure violations before pushing.config/authz/permission_groups/assignable_permissions/<category>/<resource>/<action>.yml; DO NOT add extra directories.permissions array already exists as a raw permission definition file before referencing it.available_for to granular_access_token, role, or both; an assignable permission declaring granular_access_token must have at least one of its raw permissions referenced by a REST authorization decorator or GraphQL granular scope directive.boundaries based only on the organizational levels the bundled raw permissions actually support; DO NOT include boundaries where the permissions do not apply.project in boundaries when raw permissions cover /projects/:id/... endpoints; include group for /groups/:id/... endpoints; include user for /users/:id/... or personal-namespace operations; use instance sparingly and only for admin-facing permissions..metadata.yml only when titleization produces an incorrect display name (e.g., ci_cd → "CI/CD"); DO NOT create one when the folder name titleizes correctly..metadata.yml only when the resource name contains an acronym, brand name, or awkward pluralization; DO NOT create one when the directory name titleizes and pluralizes correctly.<actions> interpolation in a resource .metadata.yml description so the action list stays in sync automatically.rename_granular_scope_permission batched background migration, mark the old permission deprecated: true, finalize the migration in a later milestone, then delete the deprecated file using bundle exec rake gitlab:permissions:assignable:cleanup_deprecated.boundary_type between project and group is safe; changing to or from user or instance is a breaking change requiring token holders to recreate scopes.route_setting :authorization immediately before every HTTP method definition (get, post, put, delete), even when multiple endpoints share the same permission.boundary_type: :project | :group | :user | :instance for single-boundary endpoints; use the boundaries array for endpoints that support multiple boundary types.boundary option (a callable returning the boundary object) only when the boundary cannot be determined through standard parameter lookup.boundary_param when the request parameter containing the boundary identifier is not the default :id.boundaries array, include a boundary_type key in each entry and optionally a boundary_param; the system evaluates boundaries in priority order project > group > user > instance and uses the first resolvable boundary.skip_granular_token_authorization: :<reason> (a symbol naming the reason, e.g., :public_endpoint) only for endpoints that are publicly accessible, authenticate by means other than PATs, or where authentication is optional; DO NOT use it to bypass permission checks on authenticated endpoints, and DO NOT pass true — the reason must be a key defined in lib/tasks/gitlab/permissions/routes/skip_reasons.rb (add a new key with a human-readable label if no existing reason fits).todo: '<issue-link-or-reason>' (a non-empty string) to defer granular token authorization when you have not yet decided how it should work for an endpoint; granular PATs receive 403 Forbidden while legacy PATs are unaffected. Replace todo with permissions + boundary_type (or skip_granular_token_authorization) once the decision is made. DO NOT leave todo blank — the validation task fails on a blank value.config/authz/roles/public_anonymous.yml under the matching project: or group: boundary so that granular PATs without an explicit scope can access them on public resources; DO NOT add user or instance boundary permissions to this file.'authorizing granular token permissions' shared example for every endpoint, providing boundary_object, user, and request let-bindings.boundary_object to match the boundary_type: project → project, group → group, :user → :user, :instance → :instance.user is a member of the namespace (project or group) when the boundary object is a project or group.expected_success_status: as a keyword argument to the shared example when the success response is not :success (e.g., :created, :accepted, :no_content, :redirect).legacy_token_scopes: as a keyword argument when the endpoint requires legacy token scopes other than the default %w[api].request block supplies valid params and that any resource the request path references exists, so the "granting access" assertion receives a real success response.bundle exec rake gitlab:permissions:routes:compile_docs after adding or changing REST API endpoint authorization; DO NOT edit doc/auth/tokens/fine_grained_access_tokens_rest.md by hand.@gitlab-com/gl-security/product-security/appsec for security review before merging any new job token permission.lib/ci/job_token/policies.rb, app/validators/json_schemas/ci_job_token_policies.json, and app/assets/javascripts/token_access/constants.js.route_setting :authentication, job_token_allowed: true and route_setting :authorization, job_token_policies: <policy> to each endpoint that should accept job token authentication.:read_* policies for GET (read) operations and :admin_* policies for POST/PUT/DELETE (write/delete) operations on job token endpoints.allow_public_access_for_enabled_project_features parameter on route_setting :authorization to allow job token access based on project feature visibility, providing backward compatibility.'enforcing job token policies' shared RSpec example to test job token authorization, passing the required policy and optionally allow_public_access_for_enabled_project_features and expected_success_status.bundle exec rake ci:job_tokens:compile_docs.For the full picture, see: