Problem
Environment variable authentication in gh currently has no concept of a "trusted" host for enterprise tokens. When GH_ENTERPRISE_TOKEN is set, gh will send that token to any host that isn't github.com; including URLs passed as command-line arguments (e.g., gh pr view https://unintentional-host.example/org/repo/pull/1).
This is by design today: GH_TOKEN/GITHUB_TOKEN is hard-coded to github.com (and GHEC tenants), but GH_ENTERPRISE_TOKEN has no equivalent host-binding mechanism.
But this is also an ergonomic gap: there is no way to provide distinct tokens for multiple GHES instances via environment variables. Users must either write to ~/.config/gh/hosts.yml at runtime or juggle GH_HOST + GH_ENTERPRISE_TOKEN between commands.
Proposed Solution
Inspired by Terraform's TF_TOKEN_* environment variable credentials (shipped in Terraform v1.2.0), gh should support host-specific token environment variables using the naming convention:
GH_TOKEN_<encoded_hostname>
where the hostname is encoded by replacing dots (.) with underscores (_) and hyphens (-) with double underscores (__).
Examples
| Environment Variable |
Resolves to Host |
GH_TOKEN_github_com |
github.com |
GH_TOKEN_ghes_example_com |
ghes.example.com |
GH_TOKEN_git_corp__internal_io |
git.corp-internal.io |
Credential Resolution Priority (proposed)
GH_TOKEN_<host> - most specific, wins for the matching host
GH_TOKEN / GITHUB_TOKEN - github.com only (existing behavior, unchanged)
GH_ENTERPRISE_TOKEN / GITHUB_ENTERPRISE_TOKEN - fallback for any GHES host (existing behavior, unchanged)
hosts.yml config file - stored credentials from gh auth login
This is fully backward-compatible: existing env vars retain their current semantics, and the new per-host variables simply slot in at a higher priority.
Eventually, given adoption, we could mark GH_ENTERPRISE_TOKEN as deprecated for GHES authentication. But that'd be a separate decision and step aside from this new feature.
Use Cases
Secure Token-to-Host Trust Relationships
Per-host env vars establish the trust mapping mechanism. Each token is explicitly bound to exactly one hostname, so the CLI will never send it elsewhere. This directly addresses the GH_ENTERPRISE_TOKEN trust gap - today, a user who sets that variable and runs gh pr view <some-url> will send their enterprise token to an arbitrary host. With GH_TOKEN_<host>, tokens are only sent to the host encoded in the variable name.
This is particularly important because the CLI intentionally trusts URLs provided by the user on the command line, but env var tokens don't carry any host affinity to validate this as premeditated and intentional trust.
Multi-host CI/CD pipelines
env:
GH_TOKEN_github_com: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN_ghes1_corp_com: ${{ secrets.GHES1_TOKEN }}
GH_TOKEN_ghes2_corp_com: ${{ secrets.GHES2_TOKEN }}
steps:
- run: gh pr list -R org/repo # token for github.com
- run: gh pr view https://ghes1.corp.com/org/repo/pull/42 # token for ghes1.corp.com
- run: gh api repos/org/repo/issues --hostname ghes2.corp.com # token for ghes2.corp.com
Prior Art
- Terraform CLI (
TF_TOKEN_*): docs - shipped v1.2.0, widely adopted in CI/CD
gh existing env vars: GH_TOKEN, GH_ENTERPRISE_TOKEN - proves the pattern works, just needs per-host granularity
Additional Context
- Non-ASCII hostnames should be converted to their punycode equivalent before encoding (matching Terraform's approach)
gh auth status could surface which env-var source was resolved per host for debuggability
- The
gh auth token --hostname <host> command should respect this priority as well
Related
Problem
Environment variable authentication in
ghcurrently has no concept of a "trusted" host for enterprise tokens. WhenGH_ENTERPRISE_TOKENis set,ghwill send that token to any host that isn't github.com; including URLs passed as command-line arguments (e.g.,gh pr view https://unintentional-host.example/org/repo/pull/1).This is by design today:
GH_TOKEN/GITHUB_TOKENis hard-coded to github.com (and GHEC tenants), butGH_ENTERPRISE_TOKENhas no equivalent host-binding mechanism.But this is also an ergonomic gap: there is no way to provide distinct tokens for multiple GHES instances via environment variables. Users must either write to
~/.config/gh/hosts.ymlat runtime or juggleGH_HOST+GH_ENTERPRISE_TOKENbetween commands.Proposed Solution
Inspired by Terraform's
TF_TOKEN_*environment variable credentials (shipped in Terraform v1.2.0),ghshould support host-specific token environment variables using the naming convention:where the hostname is encoded by replacing dots (
.) with underscores (_) and hyphens (-) with double underscores (__).Examples
GH_TOKEN_github_comgithub.comGH_TOKEN_ghes_example_comghes.example.comGH_TOKEN_git_corp__internal_iogit.corp-internal.ioCredential Resolution Priority (proposed)
GH_TOKEN_<host>- most specific, wins for the matching hostGH_TOKEN/GITHUB_TOKEN- github.com only (existing behavior, unchanged)GH_ENTERPRISE_TOKEN/GITHUB_ENTERPRISE_TOKEN- fallback for any GHES host (existing behavior, unchanged)hosts.ymlconfig file - stored credentials fromgh auth loginThis is fully backward-compatible: existing env vars retain their current semantics, and the new per-host variables simply slot in at a higher priority.
Eventually, given adoption, we could mark
GH_ENTERPRISE_TOKENas deprecated for GHES authentication. But that'd be a separate decision and step aside from this new feature.Use Cases
Secure Token-to-Host Trust Relationships
Per-host env vars establish the trust mapping mechanism. Each token is explicitly bound to exactly one hostname, so the CLI will never send it elsewhere. This directly addresses the
GH_ENTERPRISE_TOKENtrust gap - today, a user who sets that variable and runsgh pr view <some-url>will send their enterprise token to an arbitrary host. WithGH_TOKEN_<host>, tokens are only sent to the host encoded in the variable name.This is particularly important because the CLI intentionally trusts URLs provided by the user on the command line, but env var tokens don't carry any host affinity to validate this as premeditated and intentional trust.
Multi-host CI/CD pipelines
Prior Art
TF_TOKEN_*): docs - shipped v1.2.0, widely adopted in CI/CDghexisting env vars:GH_TOKEN,GH_ENTERPRISE_TOKEN- proves the pattern works, just needs per-host granularityAdditional Context
gh auth statuscould surface which env-var source was resolved per host for debuggabilitygh auth token --hostname <host>command should respect this priority as wellRelated