Summary
Please add first-class support for GitHub App credentials that are fetched from an external secret manager at runtime, without requiring the app private key to be stored directly in GitHub Actions secrets.
Today gh-aw supports GitHub App authentication through github-app: frontmatter:
github-app:
app-id: ${{ vars.WORKFLOW_APP_ID }}
private-key: ${{ secrets.WORKFLOW_APP_PRIVATE_KEY }}
owner: my-org
repositories: ["*"]
This works when the app id/private key are directly available as GitHub Actions vars/secrets. It does not work cleanly when an organization requires GitHub App credentials to live in an external secret manager such as Conjur, Vault, Keeper, or a similar enterprise secret-management system.
Prior Art / Related Issues
I searched existing issues and did not find an exact duplicate for this secret-manager-backed GitHub App credential flow.
Related but different issues:
Use Case
Some deployments do not allow long-lived GitHub App private keys to be stored in GitHub Actions secrets. Instead:
- GitHub Actions stores only secret-manager bootstrap credentials.
- GitHub App credentials live in an external secret manager.
- Workflows fetch
WORKFLOW_APP_ID and WORKFLOW_APP_PRIVATE_KEY at runtime.
- gh-aw should then mint short-lived GitHub App installation tokens for checkout, GitHub tools, and safe outputs.
Current Behavior
A natural gh-aw configuration is to use a custom credential-fetching job and wire it through needs:
on:
workflow_dispatch:
needs: [secrets_fetcher]
jobs:
secrets_fetcher:
runs-on: ubuntu-latest
outputs:
app_id: ${{ steps.fetch.outputs.WORKFLOW_APP_ID }}
private_key: ${{ steps.fetch.outputs.WORKFLOW_APP_PRIVATE_KEY }}
steps:
- id: fetch
uses: some-secret-manager-action@v1
with:
# Fetch WORKFLOW_APP_ID and WORKFLOW_APP_PRIVATE_KEY from an external secret manager.
safe-outputs:
needs: [secrets_fetcher]
github-app:
app-id: ${{ needs.secrets_fetcher.outputs.app_id }}
private-key: ${{ needs.secrets_fetcher.outputs.private_key }}
This can compile, but it fails at runtime with many secret-manager actions because they mask retrieved values using ::add-mask:: / core.setSecret().
GitHub Actions then refuses to expose masked values as job outputs:
Skip output 'app_id' since it may contain secret.
Skip output 'private_key' since it may contain secret.
Then gh-aw's generated GitHub App token-minting step receives empty inputs and fails:
The 'client-id' (or deprecated 'app-id') input must be set to a non-empty string.
Root Cause
gh-aw correctly mints GitHub App installation tokens inside each generated job that needs them. That is the right security model.
The gap is that secret-manager-fetched credentials cannot safely cross job boundaries as needs.<job>.outputs.* once the secret-fetching action masks them.
The pattern works only when the secret-fetch step and actions/create-github-app-token run in the same job:
- id: get-secrets
uses: some-secret-manager-action@v1
- id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ steps.get-secrets.outputs.WORKFLOW_APP_ID }}
private-key: ${{ steps.get-secrets.outputs.WORKFLOW_APP_PRIVATE_KEY }}
But gh-aw owns the generated token-minting jobs/steps, so workflow authors cannot express: "run this credential-fetch step immediately before every generated GitHub App token mint."
Expected Behavior
gh-aw should provide a first-class way to fetch GitHub App credentials inside every generated job that needs to mint an app token, without passing masked secrets through job outputs.
The app token itself should still be generated by gh-aw using actions/create-github-app-token. The feature should only solve credential provisioning.
Proposed Design
Add support for credential-provider steps on github-app: configs.
Example syntax:
github-app:
credentials:
steps:
- name: Fetch GitHub App credentials
id: app-creds
uses: some-secret-manager-action@v1
with:
# Fetch WORKFLOW_APP_ID and WORKFLOW_APP_PRIVATE_KEY.
app-id: ${{ steps.app-creds.outputs.WORKFLOW_APP_ID }}
private-key: ${{ steps.app-creds.outputs.WORKFLOW_APP_PRIVATE_KEY }}
owner: my-org
repositories: ["*"]
The compiler would inject credentials.steps into each generated job immediately before any generated actions/create-github-app-token step for that github-app config.
This avoids cross-job masked output loss because steps.app-creds.outputs.* is consumed within the same job.
Scope
This should apply consistently to all gh-aw token-minting sites:
checkout.github-app
tools.github.github-app
safe-outputs.github-app
- top-level
github-app fallback
on.github-app / activation token minting
Implementation Plan
Please implement the following changes:
-
Extend GitHub App configuration
- Update
GitHubAppConfig in pkg/workflow/safe_outputs_app_config.go with an optional credential-provider config.
- Suggested shape:
credentials.steps, credentials.app-id, credentials.private-key.
- Preserve existing
app-id / client-id and private-key behavior for direct vars/secrets.
-
Update schema and validation
- Update frontmatter schema files to accept
github-app.credentials wherever github-app is valid.
- Validate that
credentials.steps is only emitted into generated jobs and is not interpreted as normal workflow-level steps.
- Validate that
credentials.app-id and credentials.private-key reference same-job step outputs, for example ${{ steps.app-creds.outputs.WORKFLOW_APP_ID }}.
- Reject or warn on
needs.*.outputs.* in credentials.app-id / credentials.private-key, because that recreates the masked-output problem.
- Follow the existing error-message style:
[what's wrong]. [what's expected]. [example].
-
Inject credential-provider steps before token minting
- Update generated token-minting paths so credential-provider steps appear immediately before
actions/create-github-app-token.
- Ensure this works for checkout token minting, GitHub MCP/tool token minting, safe-output token minting, and activation token minting.
- Ensure step IDs remain collision-safe if multiple GitHub App configs are used in one workflow.
-
Preserve existing security model
- gh-aw should still mint short-lived installation tokens with
actions/create-github-app-token.
- The agent job should still receive only the minted token where appropriate, not the raw private key.
- Safe outputs should keep existing least-privilege permission calculation.
- Existing
ignore-if-missing behavior should continue to work for direct GitHub Actions vars/secrets. Define and document whether it also applies to credential-provider outputs.
-
Add tests
- Add unit tests for parsing and validating
github-app.credentials.
- Add compiler-output tests showing credential steps emitted before every relevant token-minting step.
- Add regression tests proving that direct
github-app.app-id/private-key configurations compile unchanged.
- Add negative tests for invalid
credentials shapes and needs.*.outputs.* references in credential fields.
-
Update documentation
- Update
docs/src/content/docs/reference/auth.mdx with a section like "Secret-manager-backed GitHub App credentials".
- Mention why job outputs from masked secrets are not reliable across job boundaries.
- Provide a generic external secret-manager example.
- Cross-link from
docs/src/content/docs/reference/triggers.md and docs/src/content/docs/reference/safe-outputs.md where on.needs / safe-outputs.needs currently show secret-fetching examples, clarifying that masked credentials should use the new same-job credential-provider mechanism instead.
-
Run standard validation
- Run the project validation flow recommended in
CONTRIBUTING.md, including make agent-finish.
Acceptance Criteria
- A workflow can keep GitHub App id/private key in an external secret manager.
- GitHub Actions secrets only need secret-manager bootstrap credentials.
- gh-aw still mints GitHub App installation tokens itself.
- No app private key is passed through
needs.<job>.outputs.*.
- Generated workflows do not require manual lockfile patching.
- Existing
github-app behavior using vars.* / secrets.* remains unchanged.
- Documentation clearly explains when to use direct vars/secrets vs same-job credential-provider steps.
Why This Matters
This enables enterprise CentralRepoOps and cross-repository workflows where GitHub App credentials must remain in approved secret managers, while still using gh-aw's native GitHub App token model and safe-output permission boundaries.
Follow-up Validation
A throwaway validation confirmed that the workaround is viable when the GitHub App credentials are fetched from the external secret manager inside each job immediately before actions/create-github-app-token runs.
The gh-aw-generated token-minting sites that needed same-job credential access were:
- activation checkout token
- agent checkout token
- agent GitHub/MCP token
- safe_outputs token
- conclusion token
With that pattern, cross-repository checkout, agent execution, PR review submission, and label updates all completed successfully.
This reinforces the feature request: secret-manager-backed GitHub App credentials need to be injectable at every gh-aw-generated token-minting site, without requiring masked values to cross job boundaries.
Summary
Please add first-class support for GitHub App credentials that are fetched from an external secret manager at runtime, without requiring the app private key to be stored directly in GitHub Actions secrets.
Today gh-aw supports GitHub App authentication through
github-app:frontmatter:This works when the app id/private key are directly available as GitHub Actions vars/secrets. It does not work cleanly when an organization requires GitHub App credentials to live in an external secret manager such as Conjur, Vault, Keeper, or a similar enterprise secret-management system.
Prior Art / Related Issues
I searched existing issues and did not find an exact duplicate for this secret-manager-backed GitHub App credential flow.
Related but different issues:
on.needscustom jobs can produce invalid workflow syntax.needsgaps forengine.envexpressions..github/.agentscheckout withon.github-app#21188: cross-repo activation checkout withon.github-app.COPILOT_GITHUB_TOKENstill requires a PAT and is not this issue.Use Case
Some deployments do not allow long-lived GitHub App private keys to be stored in GitHub Actions secrets. Instead:
WORKFLOW_APP_IDandWORKFLOW_APP_PRIVATE_KEYat runtime.Current Behavior
A natural gh-aw configuration is to use a custom credential-fetching job and wire it through
needs:This can compile, but it fails at runtime with many secret-manager actions because they mask retrieved values using
::add-mask::/core.setSecret().GitHub Actions then refuses to expose masked values as job outputs:
Then gh-aw's generated GitHub App token-minting step receives empty inputs and fails:
Root Cause
gh-aw correctly mints GitHub App installation tokens inside each generated job that needs them. That is the right security model.
The gap is that secret-manager-fetched credentials cannot safely cross job boundaries as
needs.<job>.outputs.*once the secret-fetching action masks them.The pattern works only when the secret-fetch step and
actions/create-github-app-tokenrun in the same job:But gh-aw owns the generated token-minting jobs/steps, so workflow authors cannot express: "run this credential-fetch step immediately before every generated GitHub App token mint."
Expected Behavior
gh-aw should provide a first-class way to fetch GitHub App credentials inside every generated job that needs to mint an app token, without passing masked secrets through job outputs.
The app token itself should still be generated by gh-aw using
actions/create-github-app-token. The feature should only solve credential provisioning.Proposed Design
Add support for credential-provider steps on
github-app:configs.Example syntax:
The compiler would inject
credentials.stepsinto each generated job immediately before any generatedactions/create-github-app-tokenstep for thatgithub-appconfig.This avoids cross-job masked output loss because
steps.app-creds.outputs.*is consumed within the same job.Scope
This should apply consistently to all gh-aw token-minting sites:
checkout.github-apptools.github.github-appsafe-outputs.github-appgithub-appfallbackon.github-app/ activation token mintingImplementation Plan
Please implement the following changes:
Extend GitHub App configuration
GitHubAppConfiginpkg/workflow/safe_outputs_app_config.gowith an optional credential-provider config.credentials.steps,credentials.app-id,credentials.private-key.app-id/client-idandprivate-keybehavior for direct vars/secrets.Update schema and validation
github-app.credentialswherevergithub-appis valid.credentials.stepsis only emitted into generated jobs and is not interpreted as normal workflow-level steps.credentials.app-idandcredentials.private-keyreference same-job step outputs, for example${{ steps.app-creds.outputs.WORKFLOW_APP_ID }}.needs.*.outputs.*incredentials.app-id/credentials.private-key, because that recreates the masked-output problem.[what's wrong]. [what's expected]. [example].Inject credential-provider steps before token minting
actions/create-github-app-token.Preserve existing security model
actions/create-github-app-token.ignore-if-missingbehavior should continue to work for direct GitHub Actions vars/secrets. Define and document whether it also applies to credential-provider outputs.Add tests
github-app.credentials.github-app.app-id/private-keyconfigurations compile unchanged.credentialsshapes andneeds.*.outputs.*references in credential fields.Update documentation
docs/src/content/docs/reference/auth.mdxwith a section like "Secret-manager-backed GitHub App credentials".docs/src/content/docs/reference/triggers.mdanddocs/src/content/docs/reference/safe-outputs.mdwhereon.needs/safe-outputs.needscurrently show secret-fetching examples, clarifying that masked credentials should use the new same-job credential-provider mechanism instead.Run standard validation
CONTRIBUTING.md, includingmake agent-finish.Acceptance Criteria
needs.<job>.outputs.*.github-appbehavior usingvars.*/secrets.*remains unchanged.Why This Matters
This enables enterprise CentralRepoOps and cross-repository workflows where GitHub App credentials must remain in approved secret managers, while still using gh-aw's native GitHub App token model and safe-output permission boundaries.
Follow-up Validation
A throwaway validation confirmed that the workaround is viable when the GitHub App credentials are fetched from the external secret manager inside each job immediately before
actions/create-github-app-tokenruns.The gh-aw-generated token-minting sites that needed same-job credential access were:
With that pattern, cross-repository checkout, agent execution, PR review submission, and label updates all completed successfully.
This reinforces the feature request: secret-manager-backed GitHub App credentials need to be injectable at every gh-aw-generated token-minting site, without requiring masked values to cross job boundaries.