GhEx.new/1 accepts three credential forms via :auth.

Token

A classic or fine-grained personal access token, or an OAuth token:

client = GhEx.new(auth: {:token, token})

GitHub App

A GitHub App authenticates as itself with a short-lived RS256 JWT, minted from the app's private key with OTP crypto (no JOSE dependency):

app = GhEx.new(auth: {:app, client_id_or_app_id, File.read!("app-private-key.pem")})

The JWT is minted per request inside GhEx.Auth.resolve/1. A malformed key returns {:error, :invalid_pem} rather than raising.

Installation

To act as an installation, use a client that mints and caches the installation access token (valid one hour) and refreshes it before expiry. This needs a running token cache. Add the default ETS cache to your supervision tree:

children = [
  GhEx.TokenCache.ETS
  # or, to run more than one or pick the name:
  # {GhEx.TokenCache.ETS, name: MyApp.GitHubTokens}
]

Then build an installation client:

inst = GhEx.App.installation(app, installation_id, cache: GhEx.TokenCache.ETS)
GhEx.REST.get(inst, "/installation/repositories")

Construction does no I/O; the token is minted on the first request and reused until it nears expiry.

Custom cache backend

GhEx.TokenCache is a behaviour. Back it with your own module (for example Nebulex or Redis) to share tokens across a cluster, without gh_ex depending on a cache library. Pass {module, ref} as the :cache to name a specific instance.

Stateless primitives

To own the token lifecycle yourself:

# a token-auth client scoped to the installation, plus its expiry
{:ok, inst, _expires_at} = GhEx.App.installation_client(app, installation_id)

# or the raw token body, optionally scoped to repositories/permissions
{:ok, body} =
  GhEx.App.installation_token(app, installation_id, json: %{repositories: ["gh_ex"]})