feat(003): auth + control-plane API client (login, whoami, api module)#2
Merged
Conversation
Implement spec 003. `stagecraft login` performs the browser-assisted bearer-token handoff chosen in the spec's 2026-07-14 amendment: paste a control-plane session token (piped or at a prompt), validate it against GET /api/v1/auth/me, then store it in ~/.config/stagecraft/credentials.toml at mode 0600, keyed by base URL. `stagecraft whoami` GETs the same endpoint and renders id/email, exiting 1 when unauthenticated. The api module gives every later verb a typed, authenticated request path: a uniform error taxonomy (network, auth, api-4xx with server message, api-5xx), retry with jitter for idempotent GETs only, and a global --debug flag that dumps request/response metadata to stderr without credential material. reqwest is rustls-only (never native-tls); tokio drives the async verbs on a per-invocation runtime. Auth decision (recorded in the spec): the preferred RFC 8252 loopback OIDC flow needs control-plane additions that do not exist yet (a public rauthy client with a loopback redirect, and a token-return endpoint), so the paste fallback is the sanctioned v1. The credentials store and bearer replay are independent of acquisition, so the loopback flow drops in later with no change to the store or the api module. Testing: cargo fmt --check, clippy -D warnings, and 35 tests (credentials 0600 round-trip and permission tightening, GET-only retry, 401 to login hint, whoami happy/sad via httpmock, whoami JSON shape). Manual e2e against a live control plane is deferred per spec §1: the control plane is pre-code, so no plane is reachable; the spec stays implementation: in-progress with a dated Status note. Spine gates green (compile, index check, lint, couple). Spec-Drift-Waiver: spec 003 fills in the scaffold spec 002 established (002 §2 placed the src/ tree and login/whoami stubs "so later specs fill them in" and predicted "Async arrives with spec 003 (tokio + reqwest)"). The 002-owned paths here (Cargo.toml, Cargo.lock, src/cli.rs, src/commands.rs) plus the new src/api.rs and src/auth.rs modules are that fill-in; no 002 design is contradicted.
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.
Summary
Implements spec 003 (auth + control-plane API client): the
authandapimodules,stagecraft login(browser-assisted bearer-token handoff) andstagecraft whoami, a 0600 per-base-url credentials store, a uniform API error taxonomy, GET-only retry with jitter, and a global--debugflag.The auth mechanism is recorded in the spec: the preferred RFC 8252 loopback OIDC flow needs control-plane additions that do not exist yet (a public rauthy client with a loopback redirect, and a token-return endpoint), so the paste fallback is the sanctioned v1. The credentials store and bearer replay are independent of acquisition, so the loopback flow drops in later with no change to the store or the api module.
Testing
cargo fmt --check,cargo clippy --all-targets -- -D warnings,cargo test(36 tests: credentials 0600 round-trip and stale-temp tightening, GET-only retry, 401 to login hint, whoami happy/sad via httpmock, whoami JSON shape)spec-spine compile && spec-spine index check && spec-spine lint --fail-on-warn && spec-spine coupleManual e2e against a live control plane is deferred per spec section 1: the control plane is pre-code, so no plane is reachable. Spec 003 stays implementation: in-progress with a dated Status note until that check can run.
Spec-Drift-Waiver: spec 003 fills in the scaffold that spec 002 established. 002 section 2 placed the src/ tree and the login/whoami command stubs specifically "so later specs fill them in" and predicted "Async arrives with spec 003 (tokio + reqwest)". The 002-owned paths changed here (Cargo.toml, Cargo.lock, src/cli.rs, src/commands.rs) together with the new src/api.rs and src/auth.rs modules that 003 establishes are exactly that scaffold fill-in; no 002 design is contradicted, so this is a coupling waiver, not a coherence override.