Hello,
SwiftPM currently supports authentication when fetching binary artifacts and registry packages via the macOS Keychain and .netrc files, as established in SE-0378. While these mechanisms work well for local development, they are not ideal for working with CI/CD environments. This pitch proposes adding environment variable-based credential support for both package registry and source control authentication.
Motivation
In CI/CD (Continuous Integration/Continuous Deployment) environments the existing SwiftPM authentication mechanisms have notable shortcomings:
- macOS Keychain is unavailable on Linux and headless macOS environments (e.g., Docker containers, ephemeral CI runners). Even when available, populating the Keychain non-interactively is difficult requires additional tooling and permissions.
.netrcfiles require writing credentials to disk, which introduces security concerns. Managing.netrcfile creation and cleanup adds complexity to CI scripts, and there is a risk of accidental credential leakage.
Environment variables are the de facto standard for providing secrets in CI/CD systems. Services like GitHub Actions, GitLab CI, CircleCI, and Jenkins all provide first-class support for injecting secrets as environment variables. Nearly every other package manager ecosystem supports this approach:
- npm/Yarn:
NPM_TOKEN,NPM_CONFIG__AUTH - pip/PyPI:
TWINE_USERNAME,TWINE_PASSWORD - Maven/Gradle:
MAVEN_USERNAME,MAVEN_PASSWORD - Docker:
DOCKER_USERNAME,DOCKER_PASSWORD - Cargo:
CARGO_REGISTRY_TOKEN - NuGet:
NUGET_API_KEY
SwiftPM should offer the same convenience and security model that developers expect from modern package management tooling.
Proposed solution
We propose introducing five environment variables that SwiftPM will recognize as credential sources, along with two new internal types (EnvironmentAuthorizationProvider and InMemoryNetrcAuthorizationProvider) to read and apply these credentials. Environment variable credentials take the highest precedence, overriding both Keychain and .netrc file credentials when set.
No changes to the swift package-registry subcommands or the registry service specification are required. The environment variables are read-only inputs consumed by SwiftPM's existing authorization provider infrastructure.
Detailed design
Environment variables
The following environment variables are introduced:
| Variable | Purpose |
|---|---|
SWIFTPM_REGISTRY_TOKEN |
Bearer token for all package registries |
SWIFTPM_REGISTRY_LOGIN |
Username for Basic auth to all package registries |
SWIFTPM_REGISTRY_PASSWORD |
Password for Basic auth to all package registries |
SWIFTPM_SOURCE_CONTROL_TOKEN |
Token for HTTP downloads of binary artifacts and prebuilts; does not affect git operations |
SWIFTPM_NETRC_DATA |
Inline netrc-formatted content for per-host credentials |
These variables are divided into two categories: all-host variables that apply credentials to every host without discrimination, and a per-host variable that supports targeting specific hosts using the existing netrc format.
All-host registry credentials
SWIFTPM_REGISTRY_TOKEN provides a bearer token applied to all registry API requests. When set, SwiftPM constructs an Authorization: Bearer <token> header for every registry request, regardless of host.
SWIFTPM_REGISTRY_LOGIN and SWIFTPM_REGISTRY_PASSWORD provide Basic authentication credentials. Both must be set for either to take effect; setting only one is treated as if neither is set. When both are present, SwiftPM constructs an Authorization: Basic <base64(login:password)> header.
If SWIFTPM_REGISTRY_TOKEN is set alongside SWIFTPM_REGISTRY_LOGIN/SWIFTPM_REGISTRY_PASSWORD, the token takes precedence and the login/password pair is ignored.
Empty string values ("") are treated as unset.
Example: token authentication in CI
export SWIFTPM_REGISTRY_TOKEN="$CI_REGISTRY_TOKEN"
swift build
Example: basic authentication in CI
export SWIFTPM_REGISTRY_LOGIN="$CI_USERNAME"
export SWIFTPM_REGISTRY_PASSWORD="$CI_PASSWORD"
swift build
All-host source control credentials
SWIFTPM_SOURCE_CONTROL_TOKEN provides a bearer token for HTTP downloads that SwiftPM makes outside of the registry API. Today this covers two cases:
- Binary artifact downloads -- when a
Package.swiftdeclares a.binaryTarget(url:), SwiftPM fetches the archive from the specified URL. These URLs point to arbitrary HTTP hosts (e.g., a GitHub release asset, an Artifactory endpoint, or an S3 bucket), not to a package registry. - Prebuilt downloads -- SwiftPM can download prebuilt Swift syntax libraries to speed up macro compilation. These are fetched from a configurable HTTP endpoint.
Important: This variable does not affect git clone or git fetch operations. SwiftPM shells out to git directly for repository cloning, and git uses its own credential system (credential helpers, SSH keys, git's own .netrc reading, etc.). SWIFTPM_SOURCE_CONTROL_TOKEN applies only to HTTP requests that SwiftPM itself makes to download artifacts from URLs declared in package manifests or SwiftPM configuration.
export SWIFTPM_SOURCE_CONTROL_TOKEN="ghp_xxxxxxxxxxxx"
swift build
Per-host credentials via inline netrc
SWIFTPM_NETRC_DATA contains netrc-formatted content that is parsed in-memory using SwiftPM's existing NetrcParser. This supports the standard netrc machine, login, password, and default directives:
export SWIFTPM_NETRC_DATA="machine registry1.example.com login user1 password pass1
machine registry2.example.com login user2 password pass2
machine github.com login token password ghp_abc123"
swift build
The default machine entry is supported as a fallback for hosts not explicitly listed:
export SWIFTPM_NETRC_DATA="machine private.registry.com login deploy password s3cret
default login token password fallback-token"
SWIFTPM_NETRC_DATA is used for both registry and source control authentication paths. It is functionally equivalent to a .netrc file, but without requiring any file system writes.
Credential precedence
When multiple credential sources are available, SwiftPM resolves them in a defined precedence order. The first source that provides credentials for a given URL wins.
Registry operations:
SWIFTPM_REGISTRY_TOKENorSWIFTPM_REGISTRY_LOGIN+SWIFTPM_REGISTRY_PASSWORD(all-host, highest precedence)SWIFTPM_NETRC_DATA(per-host)- Keychain (macOS only)
~/.netrcfile
Binary artifact and prebuilt downloads:
SWIFTPM_SOURCE_CONTROL_TOKEN(all-host, highest precedence)SWIFTPM_NETRC_DATA(per-host)~/.netrcfile- Keychain (macOS only)
Note that the precedence order between Keychain and .netrc differs between registry and source control paths. This reflects the existing behaviour established in SE-0378, where registry authentication prefers the Keychain while source control authentication prefers .netrc. Environment variables are simply inserted at the top of each existing chain.
Authentication type inference
The existing registry authentication flow requires an explicit type entry ("basic" or "token") in registries.json for each registry host. This was necessary because credentials stored in Keychain or .netrc are ambiguous -- a login of "token" is a convention, not a guarantee.
When credentials come from environment variables, the authentication type can be inferred reliably:
SWIFTPM_REGISTRY_TOKENalways produces Bearer authentication (the user field is the sentinel value"token")SWIFTPM_REGISTRY_LOGIN+SWIFTPM_REGISTRY_PASSWORDalways produces Basic authenticationSWIFTPM_NETRC_DATAentries withlogin tokenproduce Bearer authentication; all others produce Basic authentication
This inference is applied only when no explicit type is configured in registries.json for the target host. If a type is configured, it is respected as before. This allows environment variable authentication to work without requiring swift package-registry login to be run first, which is the primary use case for CI environments.
Security
Environment variables are a well-established mechanism for secret injection in CI/CD systems. This proposal aligns SwiftPM with industry practice and offers several security benefits over alternatives:
- No file system persistence. Unlike
.netrcfiles, environment variables exist only in process memory and are not written to disk by SwiftPM. This eliminates the risk of credentials being left behind on ephemeral CI runners or accidentally committed to version control. - Platform-native secret management. CI/CD platforms provide encrypted secret storage with access controls, audit logging, and automatic masking in build logs. Environment variables are the standard interface for surfacing these secrets to build processes.
- Cache key exclusion. All five environment keys are added to the
nonCachableset, ensuring credentials never leak into build cache keys or fingerprints. - No privilege escalation. Environment variables are scoped to the current process and its children. They do not require elevated permissions or system-level credential store access, unlike Keychain operations which may trigger authorization dialogs.
The SWIFTPM_NETRC_DATA variable deserves specific attention: while it contains credentials in a structured format, it is parsed in-memory and never written to the file system. The parsed Netrc object is held only for the duration of the SwiftPM process.
Impact on existing packages
This proposal is purely additive. No existing behavior is changed when the new environment variables are unset:
- The
swift package-registry loginandswift package-registry logoutsubcommands continue to work as before. - Keychain and
.netrcfile credentials continue to function unchanged. - The
registries.jsonconfiguration file format is unchanged. - No existing commands or options are removed or modified.
Packages that already use Keychain or .netrc authentication will see no behavioral difference. The environment variable path is opt-in and only activates when the relevant variables are set to non-empty values.
Alternatives considered
Per-host environment variables (e.g., SWIFTPM_REGISTRY_TOKEN_<HOST>)
We considered supporting per-host environment variables like SWIFTPM_REGISTRY_TOKEN_EXAMPLE_COM with host names encoded into variable names. This approach is used by some tools (e.g., Cargo's CARGO_REGISTRIES_<NAME>_TOKEN), but has drawbacks:
- Host-to-variable-name encoding is error-prone (dots, hyphens, ports)
- The number of required variables grows with the number of registries
- It is difficult to discover which variables are expected
Instead, SWIFTPM_NETRC_DATA provides per-host support using the well-known netrc format, which is already familiar to SwiftPM users and reuses existing parsing infrastructure.
A configuration file path variable (e.g., SWIFTPM_NETRC_FILE)
We considered adding a variable that points to a netrc file on disk rather than containing inline content. However, SWIFTPM_NETRC_DATA has the advantage that it can be populated from a file (export SWIFTPM_NETRC_DATA=$(cat /path/to/.netrc)), from a CI secret, or from a script, and it avoids requiring any file to exist on the runner's file system. Users who prefer a file-based approach can already use the existing --netrc-file option or the default ~/.netrc path.
OAuth / OIDC token exchange
Some CI environments support identity federation, where the CI runner can obtain short-lived tokens via OIDC. In many package ecosystems this is known as "Trusted Publishing". While this is a good solution for zero-secret CI authentication, it requires registry-side support for token exchange protocols. The environment variable approach proposed here is complementary and does not preclude future OIDC support. OIDC token exchange could be added as a separate proposal when registry services are ready to support it.
Future directions
Diagnostic logging for credential source
SwiftPM currently logs which credential source provided authentication (e.g., "credentials for found in netrc file at "). The implementation includes logging for the new environment variable providers ("credentials for found in environment variables" and "credentials for found in SWIFTPM_NETRC_DATA environment variable"). This could be expanded to include a --verbose or --debug flag that shows the credential resolution chain for troubleshooting authentication failures in CI.
Credential helper protocol
Some systems (notably Git) support pluggable credential helpers that can retrieve credentials from arbitrary sources. A future proposal could introduce a credential helper protocol for SwiftPM, where the environment variable approach would be one built-in helper among many possible external helpers.
OIDC / workload identity
As mentioned in alternatives considered, direct OIDC token exchange with registry services would eliminate the need for static secrets entirely. This could be explored in a future proposal as registries adopt standardized token exchange endpoints.