Repository navigation
Support Workload Identity in mix hex.publish - #1253
Merged
Merged
Conversation
yordis
commented
Oct 5, 2026
Contributor
- Follow-up to Add Trusted Publishing hexpm#1785, so CI can publish without storing a long-lived Hex API key.
… not hit Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
…ecoder Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
ericmj
reviewed
Oct 5, 2026
ericmj
left a comment
Member
There was a problem hiding this comment.
Most of lib/hex/trusted_publisher.ex should move to hex_core so rebar3_hex can share it:
exchange!/2andHex.API.OAuth.jwt_bearer_token/2becomehex_api_oauth:jwt_bearer_token(Config, Assertion, Scope), next toclient_credentials_token.audience!/0andlib/hex/api/oidc.exbecomehex_api_oauth:oidc_audience/1or a smallhex_api_oidcmodule.github_oidc_request/0,put_audience/2andgithub_oidc_token!/3go in a new hex_core module that detects the CI provider and fetches its OIDC token throughhex_http:request/5, so the request uses the build tool's HTTP adapter (proxy settings, CA certs) instead of callingHex.HTTPdirectly.- hex_core supports OTP 24+ and has no JSON decoder.
decode_json/1can usejson:decode/1when it's loaded and otherwise takevaluewith a match limited to the JWT alphabet (base64url and.). A compact JWT can't contain quotes or JSON escapes, and a bad extraction fails hexpm's signature check. That removes the OTP 27 / Elixir 1.18 requirement for both mix and rebar3. configured_credentials?/0can be deleted, sincehex_cli_auth:resolve_api_auth/2already knows whether credentials resolve. The entry point would be something likehex_cli_auth:trusted_publisher_auth(Config, Scope), returning{ok, <<"Bearer ", Token/binary>>},nonewhen credentials exist or no CI provider is detected, or{error, Reason}. Mix calls it once per publish and passes the key to both the release and docs requests, aspublish_auth/3does now. Doing it insidewith_apiwould mint a token for each request.describe_error/1and theMix.raisemessages stay in hex, and hex_core returns tagged errors such as{error, {oidc_token_request_failed, Status}}.
What stays in hex is the call site in hex.publish.ex (skip on --dry-run, run after the prompt and the docs build, print the info line), so lib/hex/trusted_publisher.ex and lib/hex/api/oidc.ex go away. The organization-level repository:<org> exchange planned in hexpm/hexpm#1785 (comment) needs the same CI token fetch from do_resolve_repo_auth in hex_cli_auth, next to exchange_for_oauth_token.
Once this ships, the GitHub Actions example in the hexpm trusted publishers docs should switch from the curl exchange and HEX_API_KEY="Bearer ..." to id-token: write plus mix hex.publish --yes, with the minimum Hex version.
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Hex.TrustedPublisher is now Hex.WorkloadIdentity, and the publish task's docs, messages and changelog entry use the new name. The docs mention that the feature is sometimes also known as Trusted Publishing.
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
…-publishing Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com> # Conflicts: # src/mix_hex_advisory.erl # src/mix_hex_api.erl # src/mix_hex_api_auth.erl # src/mix_hex_api_key.erl # src/mix_hex_api_oauth.erl # src/mix_hex_api_organization.erl # src/mix_hex_api_organization_member.erl # src/mix_hex_api_package.erl # src/mix_hex_api_package_owner.erl # src/mix_hex_api_release.erl # src/mix_hex_api_short_url.erl # src/mix_hex_api_user.erl # src/mix_hex_cli_auth.erl # src/mix_hex_core.erl # src/mix_hex_core.hrl # src/mix_hex_deflate.erl # src/mix_hex_erl_tar.erl # src/mix_hex_erl_tar.hrl # src/mix_hex_http.erl # src/mix_hex_http_httpc.erl # src/mix_hex_licenses.erl # src/mix_hex_pb_names.erl # src/mix_hex_pb_package.erl # src/mix_hex_pb_policy.erl # src/mix_hex_pb_signed.erl # src/mix_hex_pb_versions.erl # src/mix_hex_registry.erl # src/mix_hex_repo.erl # src/mix_hex_safe_binary_to_term.erl # src/mix_hex_tarball.erl # src/mix_safe_erl_term.xrl
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
…SON decoder Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Since hexpm/hexpm@a746876, a package in an organization's repository uses the organization's workload identities, and one with the write role can also create packages. The mix hex.publish docs and the CHANGELOG entry said every package needs its own workload identity and must already exist.
A CI log didn't show that a publish used a workload identity once the exchange moved into hex_core.
The previous test only checked where Hex.State loaded api_key from. The new one loads api_key from the environment with Hex.State.init/1, as Hex does at startup, and publishes with the GitHub Actions variables set, without reading the user's Hex config.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.