diff --git a/docs/src/content/docs/reference/frontmatter-full.md b/docs/src/content/docs/reference/frontmatter-full.md index 8874bae7ee5..b1f2fcdfa1a 100644 --- a/docs/src/content/docs/reference/frontmatter-full.md +++ b/docs/src/content/docs/reference/frontmatter-full.md @@ -1784,7 +1784,10 @@ cache: [] # Safe output processing configuration that automatically creates GitHub issues, # comments, and pull requests from AI workflow output without requiring write -# permissions in the main job +# permissions in the main job. When using GitHub App tokens (app:), permissions +# are automatically narrowed per-job to match only what's needed, and tokens are +# auto-revoked at job end. Multiple safe outputs in the same workflow receive the +# union of their required permissions. # (optional) safe-outputs: # List of allowed domains for URI filtering in AI workflow output. URLs from other @@ -3412,8 +3415,10 @@ safe-outputs: github-token: "${{ secrets.GITHUB_TOKEN }}" # GitHub App credentials for minting installation access tokens. When configured, - # a token will be generated using the app credentials and used for all safe output - # operations. + # tokens are automatically minted per-job with permissions narrowed to match the + # job's permissions block. Tokens are auto-revoked at job end. This enables safe + # use of a broadly-permissioned GitHub App because each job only receives the + # specific permissions it needs. # (optional) app: # GitHub App ID. Should reference a variable (e.g., ${{ vars.APP_ID }}). diff --git a/docs/src/content/docs/reference/safe-outputs.md b/docs/src/content/docs/reference/safe-outputs.md index cf066f68b3c..beb763ed669 100644 --- a/docs/src/content/docs/reference/safe-outputs.md +++ b/docs/src/content/docs/reference/safe-outputs.md @@ -628,7 +628,7 @@ Exposes outputs: `status-update-id`, `project-id`, `status`. ### Pull Request Creation (`create-pull-request:`) -Creates PRs with code changes. Falls back to issue if creation fails (e.g., org settings block it). `expires` field (same-repo only) auto-closes after period: integers (days) or `2h`, `7d`, `2w`, `1m`, `1y` (hours < 24 treated as 1 day). +Creates PRs with code changes. By default, falls back to creating an issue if PR creation fails (e.g., org settings block it). Set `fallback-as-issue: false` to disable this fallback and avoid requiring `issues: write` permission. `expires` field (same-repo only) auto-closes after period: integers (days) or `2h`, `7d`, `2w`, `1m`, `1y` (hours < 24 treated as 1 day). ```yaml wrap safe-outputs: @@ -641,6 +641,7 @@ safe-outputs: if-no-changes: "warn" # "warn" (default), "error", or "ignore" target-repo: "owner/repo" # cross-repository base-branch: "vnext" # target branch for PR (default: github.ref_name) + fallback-as-issue: false # disable issue fallback (default: true) ``` The `base-branch` field specifies which branch the pull request should target. This is particularly useful for cross-repository PRs where you need to target non-default branches (e.g., `vnext`, `release/v1.0`, `staging`). When not specified, defaults to the workflow's branch (`github.ref_name`). @@ -656,7 +657,7 @@ safe-outputs: ``` > [!NOTE] -> PR creation may fail if "Allow GitHub Actions to create and approve pull requests" is disabled in Organization Settings. Fallback creates issue with branch link. +> PR creation may fail if "Allow GitHub Actions to create and approve pull requests" is disabled in Organization Settings. By default (`fallback-as-issue: true`), fallback creates an issue with branch link and requires `issues: write` permission. Set `fallback-as-issue: false` to disable fallback and only require `contents: write` + `pull-requests: write`. ### Close Pull Request (`close-pull-request:`) @@ -1336,6 +1337,20 @@ safe-outputs: create-issue: ``` +#### How GitHub App Tokens Work + +When you configure `app:` for safe outputs, tokens are **automatically managed per-job** for enhanced security: + +1. **Per-job token minting**: Each safe output job automatically mints its own token via `actions/create-github-app-token` with permissions explicitly scoped to that job's needs +2. **Permission narrowing**: Token permissions are narrowed to match the job's `permissions:` block - only the permissions required for the safe outputs in that job are granted +3. **Automatic revocation**: Tokens are explicitly revoked at job end via `DELETE /installation/token`, even if the job fails +4. **Safe shared configuration**: A broadly-permissioned GitHub App can be safely shared across workflows because tokens are narrowed per-job + +> [!TIP] +> **Why this matters**: You can configure a single GitHub App at the organization level with broad permissions (e.g., `contents: write`, `issues: write`, `pull-requests: write`), and each workflow job will only receive the specific subset of permissions it needs. This provides least-privilege access without requiring per-workflow App configuration. + +**Example**: If your workflow only uses `create-issue:`, the minted token will have `contents: read` + `issues: write`, even if your GitHub App has broader permissions configured. + ### Maximum Patch Size (`max-patch-size:`) Limits git patch size for PR operations (1-10,240 KB, default: 1024 KB): diff --git a/docs/src/content/docs/reference/tokens.mdx b/docs/src/content/docs/reference/tokens.mdx index 737e014be0d..84191adecaa 100644 --- a/docs/src/content/docs/reference/tokens.mdx +++ b/docs/src/content/docs/reference/tokens.mdx @@ -232,7 +232,9 @@ gh aw secrets set APP_PRIVATE_KEY --value "$(cat path/to/private-key.pem)" **How it works**: -At workflow start, a token is automatically minted with permissions matching your agent job's `permissions:` field. The token is passed to the GitHub MCP server and automatically revoked at workflow end (even on failure). +At workflow start, a token is automatically minted with **permissions matching your job's `permissions:` field**. The token is passed to the GitHub MCP server and automatically revoked at workflow end (even on failure). + +This is the same per-job narrowing behavior used by `safe-outputs.app:` - see the [Safe Outputs documentation](/gh-aw/reference/safe-outputs/#github-app-token-app) for a detailed explanation of how GitHub App tokens are narrowed per-job. **Token precedence and fallback**: @@ -243,6 +245,7 @@ At workflow start, a token is automatically minted with permissions matching you - The compiler automatically sets `GITHUB_MCP_SERVER_TOKEN` and passes it as `GITHUB_PERSONAL_ACCESS_TOKEN` (local/Docker) or an `Authorization: Bearer` header (remote). - In most cases, you do not need to set this token separately. Use `GH_AW_GITHUB_TOKEN` instead. - **GitHub App advantages**: Short-lived tokens (auto-revoked at workflow end), no credential rotation needed, automatic permission calculation, better auditability. +- **Per-job permission narrowing**: When using a GitHub App (via `tools.github.app:` or `safe-outputs.app:`), tokens are automatically narrowed to match the job's `permissions:` block. This means you can safely configure a broadly-permissioned GitHub App at the organization level, and each job will only receive the specific permissions it needs. - If using a GitHub App for both safe-outputs and GitHub MCP server, you can configure them independently for different permission levels. - Set the resource owner to the organization when the repository is organization-owned. - `GITHUB_TOKEN` is not supported in remote mode. Use `GH_AW_GITHUB_TOKEN` or a GitHub App instead.