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 on [AuthenticatedClient].
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.