Skip to main content

Context

GitHub Actions workflows receive a GITHUB_TOKEN by default with limited permissions: it only operates on the repo where it runs, it cannot create PRs if the org has that restriction active, and -- critically -- events it triggers do not execute other workflows (by design, to prevent infinite loops). This breaks many useful patterns: release bots that need to trigger downstream pipelines, cross-repo automation, or something as simple as /adr in a Discussion opening a PR that a validation workflow picks up.

The historical workaround has been a Personal Access Token (PAT) from some human with broad permissions. It works, but couples automation to that person, the tokens are long-lived, and scopes are granular only at the operation-type level, not per repo. Before we start building pipelines (package releases, ADR bootstrapping, cross-repo sync), it makes sense to establish a default policy so that individual decisions do not contradict each other.

Decision

When a pipeline or automation needs a token with permissions beyond the default GITHUB_TOKEN (for example: creating PRs, triggering downstream workflows, writing to other repos in the org, managing Discussions), the default approach is to issue the token from a GitHub App installed in the eigenoid org. Personal Access Tokens (PATs) are reserved for one-off, temporary cases.

Consequences

  • Fine-grained permissions: each App explicitly declares what scopes it needs and on which repos; principle of least privilege.
  • Short-lived tokens: the installation token expires in one hour; reduces impact if a key leaks.
  • Clear identity: actions show up signed as the App (eigenoid-bot[bot]), not as a person. Better auditability.
  • No coupling to a person: the token does not die if the owner rotates their PAT, leaves the org, or has their credentials rotated.
  • PRs created by the App DO trigger downstream workflows (unlike those created with GITHUB_TOKEN), eliminating a frequent gotcha.
  • Initial setup cost: the App must be created, a private key generated, the App installed on each repo, and APP_ID + PRIVATE_KEY secrets configured in each workflow.
  • One more layer to maintain: periodic rotation of the private key, managing who is the human owner of the App, monitoring which repos use it.

Alternatives considered

  • PAT from a human user: simple to set up but couples automation to that person; if they leave or their permissions are rotated, the entire pipeline breaks. Tokens are long-lived by default.
  • PAT from a dedicated bot account: improves ownership, but GitHub charges for the additional seat, PATs are still long-lived, and permissions remain coarse-grained (broad scopes).
  • Always use GITHUB_TOKEN: free and automatic, but permissions are limited to the repo where the workflow runs and, critically, events triggered by its actions do not execute other workflows (breaks chained automations).
  • Deploy keys or ephemeral tokens via OIDC to another provider: valid for specific integrations (e.g. cloud providers), not as a general mechanism within GitHub.

References