Expand description
The GitHub gateway.
This crate holds every line of code in the product that talks to GitHub, and the crate root holds the one client all of it goes through.
device_flow— the OAuth 2.0 Device Authorization Grant, which is the only way this product ever obtains a credential (D3, D16).AuthenticatedClient— the sharedapi.github.comclient. Every request in this crate is built by it, which is what makes “setsX-GitHub-Api-Versionand an explicitAccept” a property of the design rather than of each call site, and what lets the authentication-failure taxonomy be implemented exactly once.rest,demand,jit— typed adapters owned byc3andc4, built onAuthenticatedClient.
§Three properties this crate is required to keep
It holds no client secret, and it renews without one. This paragraph used to say the opposite — that the App opts out of user-token expiration, that no renewal token is ever issued, and that renewing would require a client secret a public client cannot hold. All three were wrong, and a test in this crate enforced the error by forbidding the word “refresh token”.
The published App has user-token expiration on: a device-flow exchange
returns expires_in: 28800 and a refresh_token. GitHub requires the
client secret to refresh “unless the user access token was generated using
the device flow”, and every one of this product’s is. So the credential
renews itself, no server appears in the design, and the eight-hour life of
an access token is invisible to a daemon that runs for months. See
AuthenticatedClient::renew_once, and
docs/spikes/token-expiry-and-renewal.md for the two renewals that
confirmed it on real hosts.
AuthenticatedClient::revalidate is what still happens on a 401 for a
credential with no refresh half — every one issued before 0.1.11.
It persists nothing. device_flow::DeviceFlow::complete returns the
token; it never writes it anywhere. The machine-scoped secret store is d2
and the wiring is f1. That boundary is why this crate has no dependency on
runner-manager-platform and performs no filesystem write outside its own
tests — and it is what lets the whole gateway be tested with no platform
dependency at all.
It never renders a secret. The device code, the user access token, and
every header carrying either are absent from Debug, from Display, from
errors, and from tracing output. Every type here that holds one wraps it in
secrecy::SecretString and implements fmt::Debug by hand, because a
#[derive(Debug)] added later to a struct with a plain String field is
precisely how this control is lost. tests/no_secret_reaches_the_logs.rs
drives a whole login and an authenticated round trip through a capturing
tracing subscriber and fails if any of the three appears.
That scan is a separate test binary, and deliberately so. As a unit test
it silently stopped working: tracing caches each callsite’s Interest
process-wide while with_default installs a subscriber on one thread, and
run concurrently with the crate’s other unit tests the scan captured
only its own handful of events — passing with a real device-code leak on the
live path. A binary holding one test has no concurrency to be poisoned by.
The word “concurrently” is load-bearing and was measured;
tests/no_secret_reaches_the_logs.rs records the numbers and what they rule
out.
Modules§
- demand
- How much work is waiting for a runner that does not exist yet.
- device_
flow - The OAuth 2.0 Device Authorization Grant against the published GitHub App.
- jit
- Just-in-time runner registration: the one call in this product that returns a secret.
- rest
- This gateway is deliberately client-secret-free, as D3 requires.
Structs§
- ApiRequest
- One
api.github.comrequest, before authentication headers are applied. - ApiResponse
- One buffered
api.github.comresponse. - AppRegistration
- The published GitHub App this product authenticates as (D3, D16).
- Authenticated
Client - The one client every
api.github.comrequest in this crate goes through. - Endpoints
- Where GitHub is.
- Header
Map - Re-exported because
GithubError::headersandApiResponse::headersreturn one, and a consumer cannot name a type it has no path to. - Installation
- One installation of the published App, and what it can actually reach.
- Rate
Limit Evidence - What GitHub said about its own rate limit on a response that failed.
- Reachable
Targets - Everything the stored credential can reach.
- Renewal
- What a token needs in order to replace itself without a person.
- Tokio
Sleeper - The production adapter.
- User
Access Token - A user access token obtained from the device flow.
Enums§
- Config
Error - A configuration value this crate refuses to start with.
- Github
Error - Everything
AuthenticatedClientcan fail with. - Installation
Account - Whose account an installation sits on.
- Installation
Discovery - What
auth statusandauth loginshow after a successful sign-in. - Repository
Selection - Whether an installation can reach every repository on its account, or only the ones the user picked.
- Revalidation
- What a single re-validation of the held credential concluded.
Constants§
- DEFAULT_
LOCKOUT_ BACKOFF - How long a lockout backs off for when GitHub sends no
retry-after. - DEFAULT_
REQUEST_ TIMEOUT - Per-request ceiling, so one wedged connection cannot stall the agent’s reconciliation loop forever.
- DEVICE_
VERIFICATION_ PATH - The canonical page a user types their code into.
- GITHUB_
ACCEPT - The media type every request asks for, stated rather than defaulted.
- GITHUB_
API_ BASE - Production
api.github.com. - GITHUB_
API_ VERSION - The REST API version every request pins.
- GITHUB_
WEB_ BASE - Production
github.com, which hosts the device-flow endpoints. They are on the web host, not the API host. - MAX_
LOCKOUT_ BACKOFF - The longest a lockout may silence this client, whatever
Retry-Aftersaid. - MAX_
PAGES - The most pages either pagination loop follows before giving up.
- REVALIDATION_
PATH - What a
401re-validates the held credential against. - USER_
AGENT - The
User-AgentGitHub requires on every API request.
Traits§
- Credential
Renewal - How a credential replaces itself.
- Credential
Source - Where a client can go to find out that the stored credential changed under it.
- Sleeper
- The one way anything in this crate waits.