Auth model
GitHubApiRefStore authenticates every request with a token sent as Authorization: Bearer <token>. This page describes where tokens come
from, what scopes they require, and how the store treats them at rest
and in transit.
It is the auth companion to the GitHub API RefStore design . For the end-user how-to, see Authenticating to GitHub .
Token sources, in priority order
The CredentialProvider indirection resolves a token from the first
source that succeeds. Native and WASM walk distinct lists.
Native
- Explicit option —
Config.refstore.github.credentials = .{ .explicit = "..." }. Wins over everything. Intended for tests and short-lived scripts. GITHUB_TOKENenvironment variable. Standard CI signal; matches GitHub Actions defaults.hosts.tomlwritten bysideshow gh auth login. Per-user store at$XDG_CONFIG_HOME/sideshowdb/hosts.toml(mode0600, parent dir0700). The CLI's auth subcommands own this file; rewrites are atomic and re-chmoded on every replace, and reads refuse to surface a token when the file's mode bits are group- or world-readable.gh auth tokenshell-out. When theghCLI is installed and the user is logged in, SideshowDB invokesgh auth tokenonce per process to capture a fresh token. Behind--credential-helper gh(default ifghis onPATH).- Keychain helper. macOS Keychain, Linux
secret-tool, Windowscred.exe. Behind--credential-helper system. git credential fillshell-out. Honors the user's existing git credential helper. Behind--credential-helper git.
Browser, Chrome extension
- Explicit option —
loadSideshowDbClient({ refstore: { kind: 'github', credentials: { kind: 'explicit', token } } }). Demos and integration tests. hostCapabilities.credentials— host-supplied resolver. Extension implementations read fromchrome.storage; web pages read from a session token store, an OAuth-device-flow handler, or a user prompt.
The CLI default order is explicit > env > hosts.toml > gh auth token > keychain > git helper. The browser default order is explicit > host capabilities.
Required scopes
| Operation | Classic PAT | Fine-grained PAT |
|---|---|---|
get, list, history (public repo) | none | none |
get, list, history (private repo) | repo | Contents: read |
put, delete (public or private) | repo | Contents: write |
CLI help text and the loadSideshowDbClient JSDoc surface these
requirements at the source so misconfigured tokens fail with the right
hint.
CLI surface
The native CLI exposes a small auth family that owns the per-user
hosts file. All of these commands defer the token-on-disk path through
the same atomic write/chmod plumbing, so users only ever interact
with redacted previews.
| Command | Purpose |
|---|---|
sideshow gh auth login [--with-token] [--skip-verify] | Capture a PAT (interactive /dev/tty prompt with ECHO off, or --with-token from stdin) and persist it under [hosts."github.com"]. |
sideshow gh auth status | Print github.com's status line; exit code 1 if not signed in. |
sideshow gh auth logout | Remove the github.com entry. |
sideshow auth status [--json] | List every authenticated host with its source and a redacted token preview. |
sideshow auth logout [--host <h>] | Remove a single host or, with no flag, every entry. |
Tokens are surfaced only as gho_****…last4-style previews on stdout
and stderr. --json auth status exposes a token_preview field but
never the raw token. Whitespace-bearing tokens are rejected before any
disk I/O. The full security model — what the CLI defends against and
what it explicitly does not — lives in Authenticating to GitHub .
Token handling guarantees
- The Authorization header is never logged at any severity. Errors that reference upstream failures redact the header.
- Tokens flow through allocators and are zeroed where practical. When a
token is held for the lifetime of a
GitHubApiRefStore, it is held in exactly one place; rotation requires constructing a new store. - SideshowDB persists tokens to disk only when the user runs
sideshow gh auth login. That command writes$XDG_CONFIG_HOME/sideshowdb/hosts.tomlwith mode0600(parent dir0700) via an atomic temp-file +rename, and re-applies the mode bits on every replace. Reads refuse to surface a token when the file is group- or world-readable. No other code path persists tokens; any other store-on-disk behaviour comes from the user's chosen external credential helper. - The token is sent only to the configured
api_base(defaulthttps://api.github.com). The store does not follow redirects to other hosts.
Failure mapping
| Upstream signal | Returned error |
|---|---|
| 401 (no creds, expired, malformed) | AuthInvalid |
| 403 with body indicating insufficient scope | InsufficientScope |
403 with X-RateLimit-Remaining: 0 | RateLimited(reset_at) |
404 on /git/blobs POST against valid repo | RepoNotFoundOrUnauthorized (GitHub masks both as 404) |
AuthMissing is returned synchronously, before any HTTP request, when
the configured CredentialProvider produces no token at all.
Rate-limit posture
Successful responses expose X-RateLimit-Remaining and X-RateLimit-Reset to the caller via the operation result. The store
itself does not auto-wait when limits are exhausted; it returns RateLimited carrying the reset timestamp so observability tooling and
batch CLIs can decide what to do. A future RateLimitPolicy.WaitUntilReset mode is filed as a separate ticket.
If-None-Match (ETag) is used on ref reads where supported by the
upstream; 304 responses do not count against the rate-limit
budget. See Caching for details.
Audit and observability
- Every request emits a structured event with method, path, status, rate-limit headers, and request ID. The Authorization header is always omitted.
- The store exposes an optional
on_requesthook for callers wiring the events into a metrics pipeline.