Authentication
APM works without tokens for public packages on github.com. Authentication is needed for private repositories, enterprise hosts (*.ghe.com, GHES), and Azure DevOps.
How APM resolves authentication
Section titled “How APM resolves authentication”APM resolves tokens per (host, org) pair. For each dependency, it walks a resolution chain until it finds a token:
- Per-org env var —
GITHUB_APM_PAT_{ORG}(GitHub-like hosts — not ADO) - Global env vars —
GITHUB_APM_PAT→GITHUB_TOKEN→GH_TOKEN(any host) - Git credential helper —
git credential fill(any host except ADO)
If the global token doesn’t work for the target host, APM automatically retries with git credential helpers. If nothing matches, APM attempts unauthenticated access (works for public repos on github.com).
Results are cached per-process — the same (host, org) pair is resolved once.
All token-bearing requests use HTTPS. Tokens are never sent over unencrypted connections.
Token lookup
Section titled “Token lookup”| Priority | Variable | Scope | Notes |
|---|---|---|---|
| 1 | GITHUB_APM_PAT_{ORG} | Per-org, GitHub-like hosts | Org name uppercased, hyphens → underscores |
| 2 | GITHUB_APM_PAT | Any host | Falls back to git credential helpers if rejected |
| 3 | GITHUB_TOKEN | Any host | Shared with GitHub Actions |
| 4 | GH_TOKEN | Any host | Set by gh auth login |
| 5 | git credential fill | Per-host | System credential manager, gh auth, OS keychain |
For Azure DevOps, the only token source is ADO_APM_PAT.
For Artifactory registry proxies, use PROXY_REGISTRY_TOKEN. See Registry proxy (Artifactory) below.
For runtime features (GITHUB_COPILOT_PAT), see Agent Workflows.
Configuration variables
Section titled “Configuration variables”| Variable | Purpose |
|---|---|
APM_GIT_CREDENTIAL_TIMEOUT | Timeout in seconds for git credential fill (default: 60, max: 180) |
GITHUB_HOST | Default host for bare package names (e.g., GHES hostname) |
Multi-org setup
Section titled “Multi-org setup”When your manifest pulls from multiple GitHub organizations, use per-org env vars:
export GITHUB_APM_PAT_CONTOSO=ghp_token_for_contosoexport GITHUB_APM_PAT_FABRIKAM=ghp_token_for_fabrikamThe org name comes from the dependency reference — contoso/my-package checks GITHUB_APM_PAT_CONTOSO. Naming rules:
- Uppercase the org name
- Replace hyphens with underscores
contoso-microsoft→GITHUB_APM_PAT_CONTOSO_MICROSOFT
Per-org tokens take priority over global tokens. Use this when different orgs require different PATs (e.g., separate SSO authorizations).
Fine-grained PAT setup
Section titled “Fine-grained PAT setup”Fine-grained PATs (github_pat_) are scoped to a single resource owner — either a user account or an organization. A user-scoped fine-grained PAT cannot access repos owned by an organization, even if you are a member of that org.
To access org packages, create the PAT with the org as the resource owner at github.com/settings/personal-access-tokens/new.
Required permissions:
| Permission | Level | Purpose |
|---|---|---|
| Metadata | Read | Validation and discovery |
| Contents | Read | Downloading package files |
Set Repository access to “All repositories” or select the specific repos your manifest references.
Alternatives that skip scoping entirely:
gh auth login— produces an OAuth token that inherits your full org membership. Easiest zero-config path.- Classic PATs (
ghp_) — inherit the user’s membership across all orgs. GitHub is deprecating these in favor of fine-grained PATs.
Enterprise Managed Users (EMU)
Section titled “Enterprise Managed Users (EMU)”EMU orgs can live on github.com (e.g., contoso-microsoft) or on GHE Cloud Data Residency (*.ghe.com). EMU tokens are standard PATs (ghp_ classic or github_pat_ fine-grained) — there is no special prefix. They are scoped to the enterprise and cannot access public repos on github.com.
Fine-grained PATs for EMU orgs must use the EMU org as the resource owner — a user-scoped fine-grained PAT will not work. See Fine-grained PAT setup.
If your manifest mixes enterprise and public packages, use separate tokens:
export GITHUB_APM_PAT_CONTOSO_MICROSOFT=github_pat_enterprise_token # EMU orgPublic repos on github.com work without authentication. Set GITHUB_APM_PAT only if you need to access private repos or avoid rate limits.
GHE Cloud Data Residency (*.ghe.com)
Section titled “GHE Cloud Data Residency (*.ghe.com)”*.ghe.com hosts are always auth-required — there are no public repos. APM skips the unauthenticated attempt entirely for these hosts:
export GITHUB_APM_PAT_MYENTERPRISE=ghp_enterprise_tokenapm install myenterprise.ghe.com/platform/standardsGitHub Enterprise Server (GHES)
Section titled “GitHub Enterprise Server (GHES)”Set GITHUB_HOST to your GHES instance. Bare package names resolve against this host:
export GITHUB_HOST=github.company.comexport GITHUB_APM_PAT_MYORG=ghp_ghes_tokenapm install myorg/internal-package # → github.company.com/myorg/internal-packageUse full hostnames for packages on other hosts:
dependencies: apm: - team/internal-package # → GITHUB_HOST - github.com/public/open-source-package # → github.comSetting GITHUB_HOST makes bare package names (without explicit host) resolve against your GHES instance. Alternatively, skip env vars and configure git credential fill for your GHES host.
Azure DevOps
Section titled “Azure DevOps”export ADO_APM_PAT=your_ado_patapm install dev.azure.com/myorg/myproject/myrepoADO is always auth-required. Uses 3-segment paths (org/project/repo). No ADO_HOST equivalent - always use FQDN syntax:
apm install dev.azure.com/myorg/myproject/myrepo#mainapm install mycompany.visualstudio.com/org/project/repo # legacy URLIf your ADO project or repository name contains spaces, URL-encode them as %20:
apm install dev.azure.com/myorg/My%20Project/_git/My%20Repo%20NameCreate the PAT at https://dev.azure.com/{org}/_usersSettings/tokens with Code (Read) permission.
Package source behavior
Section titled “Package source behavior”| Package source | Host | Auth behavior | Fallback |
|---|---|---|---|
org/repo (bare) | default_host() | Global env vars → credential fill | Unauth for public repos |
github.com/org/repo | github.com | Global env vars → credential fill | Unauth for public repos |
contoso.ghe.com/org/repo | *.ghe.com | Global env vars → credential fill | Auth-only (no public repos) |
GHES via GITHUB_HOST | ghes.company.com | Global env vars → credential fill | Unauth for public repos |
dev.azure.com/org/proj/repo | ADO | ADO_APM_PAT only | Auth-only |
| Artifactory registry proxy | custom FQDN | PROXY_REGISTRY_TOKEN | Error if PROXY_REGISTRY_ONLY=1 |
Registry proxy (Artifactory)
Section titled “Registry proxy (Artifactory)”Air-gapped environments route all VCS traffic through a JFrog Artifactory proxy. APM supports this via three env vars:
| Variable | Purpose |
|---|---|
PROXY_REGISTRY_URL | Full proxy base URL, e.g. https://art.example.com/artifactory/github |
PROXY_REGISTRY_TOKEN | Bearer token for the proxy |
PROXY_REGISTRY_ONLY | Set to 1 to block all direct VCS access — only proxy downloads allowed |
export PROXY_REGISTRY_URL=https://art.example.com/artifactory/githubexport PROXY_REGISTRY_TOKEN=your_bearer_tokenexport PROXY_REGISTRY_ONLY=1 # optional -- enforces proxy-only mode
apm installWhen PROXY_REGISTRY_URL is set, APM rewrites download URLs to go through the proxy and sends PROXY_REGISTRY_TOKEN as the Authorization: Bearer header instead of the GitHub PAT.
Lockfile and reproducibility
Section titled “Lockfile and reproducibility”After a successful proxy install, apm.lock.yaml records the proxy host and path prefix as separate fields:
dependencies: - repo_url: owner/repo host: art.example.com # pure FQDN -- no path registry_prefix: artifactory/github # path prefix resolved_commit: abc123def456Subsequent apm install runs (without --update) read these fields to reconstruct the proxy URL and route auth to PROXY_REGISTRY_TOKEN, ensuring byte-for-byte reproducibility without needing the original env vars to be set identically.
Proxy-only enforcement
Section titled “Proxy-only enforcement”With PROXY_REGISTRY_ONLY=1, APM will:
- Validate the existing
apm.lock.yamlat startup and exit with an error if any entry is locked to a direct VCS source (noregistry_prefix) - Skip the download cache for entries that have no
registry_prefix(forcing a fresh proxy download) - Raise an error for any package reference that does not route through the configured proxy
Deprecated Artifactory env vars
Section titled “Deprecated Artifactory env vars”The following env vars still work but emit a DeprecationWarning. Migrate to the PROXY_REGISTRY_* equivalents:
| Deprecated | Replacement |
|---|---|
ARTIFACTORY_BASE_URL | PROXY_REGISTRY_URL |
ARTIFACTORY_APM_TOKEN | PROXY_REGISTRY_TOKEN |
ARTIFACTORY_ONLY | PROXY_REGISTRY_ONLY |
Troubleshooting
Section titled “Troubleshooting”Rate limits on github.com
Section titled “Rate limits on github.com”APM tries unauthenticated access first for public repos to conserve rate limits during validation (e.g., checking if a repo exists). For downloads, authenticated requests are preferred — with unauthenticated fallback for public repos on github.com. If you hit rate limits, set any token:
export GITHUB_TOKEN=ghp_any_valid_tokenSSO-protected organizations
Section titled “SSO-protected organizations”Authorize your PAT for SSO at github.com/settings/tokens — click Configure SSO next to the token.
EMU token can’t access public repos
Section titled “EMU token can’t access public repos”EMU PATs use standard prefixes (ghp_, github_pat_) — there is no EMU-specific prefix. They are enterprise-scoped and cannot access public github.com repos. Use a standard PAT for public repos alongside your EMU PAT — see Enterprise Managed Users (EMU) above.
Fine-grained PAT can’t access org repos
Section titled “Fine-grained PAT can’t access org repos”Fine-grained PATs are scoped to one resource owner. If you created the PAT under your user account, it cannot access repos owned by an organization — even if you are an org member. Recreate the PAT with the org as the resource owner. Classic PATs (ghp_) and gh auth login OAuth tokens do not have this limitation. See Fine-grained PAT setup.
Diagnosing auth failures
Section titled “Diagnosing auth failures”Run with --verbose to see the full resolution chain:
apm install --verbose your-org/packageThe output shows which env var matched (or none), the detected token type (fine-grained, classic, oauth, github-app), and the host classification (github, ghe_cloud, ghes, ado, generic).
The full resolution and fallback flow:
flowchart TD
A[Dependency Reference] --> B{Per-org env var?}
B -->|GITHUB_APM_PAT_ORG| C[Use per-org token]
B -->|Not set| D{Global env var?}
D -->|GITHUB_APM_PAT / GITHUB_TOKEN / GH_TOKEN| E[Use global token]
D -->|Not set| F{Git credential fill?}
F -->|Found| G[Use credential]
F -->|Not found| H[No token]
E --> I{try_with_fallback}
C --> I
G --> I
H --> I
I -->|Token works| J[Success]
I -->|Token fails| K{Credential-fill fallback}
K -->|Found credential| J
K -->|No credential| L{Host has public repos?}
L -->|Yes| M[Try unauthenticated]
L -->|No| N[Auth error with actionable message]
Git credential helper not found
Section titled “Git credential helper not found”APM calls git credential fill as a fallback (60s timeout). If your credential helper needs more time (e.g., Windows account picker), set APM_GIT_CREDENTIAL_TIMEOUT (seconds, max 180):
export APM_GIT_CREDENTIAL_TIMEOUT=120Ensure a credential helper is configured:
git config credential.helper # check current helpergit config --global credential.helper osxkeychain # macOSgh auth login # GitHub CLI