feat: guest login credentials, stale-key handling, and sign-in link refresh - #306
Merged
Merged
Conversation
Store guest_user_id, attest guest credentials on cli-auth login, and refresh guest sign-in URLs on listen start and via a TUI ticker. Co-authored-by: Cursor <cursoragent@cursor.com>
Add claim_guest, login, and create_new intents with TTY prompt, Cobra flags, and auth_intent on POST /cli-auth. MCP login defaults to claim_guest with guest credentials preserved on stale 401. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Add guest-tagged acceptance tests and CI matrix slice for auth_intent on POST /cli-auth. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Guest profiles always claim the Console sandbox on login. Use hookdeck logout then hookdeck login to attach the CLI to an existing Platform account. Remove the TTY three-way prompt. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Guest profile presence alone selects claim_guest; drop Options and intent flags from the login command surface. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
When guest_url is set, a valid API key no longer short-circuits login. Preserve stale guest key on validate 401 and send guest credentials on POST /cli-auth. Remove auth_intent from StartLogin payload. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Add unit test for guest profile with valid key reaching signup claim flow. Update guest acceptance test to assert guest_user_id/guest_api_key on POST after validate 401 without auth_intent. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Cover profile update on success, fallback to saved URL on API error, and no-op when guest profile fields are missing. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Guard empty link responses, defer refresh to async TUI path, unify persisting refresh through login.RefreshGuestSigninLink, and fix guest API error formatting. Co-authored-by: Cursor <cursoragent@cursor.com>
Redact guest_api_key from PerformRequest debug bodies and Authorization from debug headers. Write hookdeck login --local config as 0600. Co-authored-by: Cursor <cursoragent@cursor.com>
Product onboarding keys must configure the CLI even when a guest Console profile is still on disk; use a dedicated validate-and-save path instead of falling through to POST /cli-auth with mismatched guest credentials. Also document CLI authentication keys, expose login --cli-key in help, and include guest tests in acceptance slice 0. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
alexbouchardd
approved these changes
Jul 3, 2026
Refresh calls POST /cli/guest with link_context only; empty device_name was rejected by server Joi validation (422). Add omitempty and assert the key is absent in unit and guest acceptance tests. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Merge hookdeck-cli #308 (EnsureUserAssociatedCredentials, CI login UX, ListProjects header fix) while preserving guest claim flow on login. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…ixes Bring #308 handling into simplify guest flow: CI keys fail fast on project list/use, login rejects headless CI keys, keep waitForGuestUpgrade for active guest profiles. Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Collaborator
Author
|
@alexbouchardd — Your simplify guest-login stack (#309) has been merged into this branch ( |
Resolve const-block conflict in pkg/listen/tui/model.go: keep the guest-URL refresh interval (this branch) alongside main's copy-feature constants (detailsInstructions, copyStatusTimeout). Union merge; no logic change. go build ./... and go vet pass.
Resolves the test-acceptance.yml conflict in favour of main's reusable workflow (#328), and carries the branch's `guest` build tag into the reusable acceptance.yml so guest_login_acceptance_test.go keeps running. Co-Authored-By: Claude <noreply@anthropic.com>
social4hyq
pushed a commit
to social4hyq/homebrew-core
that referenced
this pull request
Sep 20, 2026
hookdeck 2.5.0 Created-by: HarmonybrewBot Commit-by: HarmonybrewBot Merged-by: HarmonybrewBot Description: Created by `brew bump` --- Created with `brew bump-formula-pr`.<details> <summary>release notes</summary> <pre>## Summary This release makes the CLI safe to run where there is no human: CI, Docker, `nohup`, and AI agents. Nine fixes, most sharing one failure shape — the CLI would silently do something other than what you asked, and the first symptom was missing traffic rather than an error. `hookdeck listen` now works headlessly and honours `HOOKDECK_API_KEY`, `--local` no longer touches your global config, and empty secrets are rejected instead of quietly producing a source that verifies nothing. It also ships the guest login improvements merged after v2.4.0. ## New features - **`hookdeck listen` reads `HOOKDECK_API_KEY`.** Running the CLI in CI no longer needs a separate `hookdeck ci` step — export a Project API key and `listen` exchanges it for CLI credentials, saves them, and connects to your project: ```sh export HOOKDECK_API_KEY="your-project-api-key" hookdeck listen 3000 shopify orders ``` Precedence is `--cli-key`, then your stored login, then `HOOKDECK_API_KEY`. An existing login is never repointed by the environment — but a temporary guest profile is, so a machine that once ran `listen` without credentials still picks up your project when the variable is set. Replacing a guest profile prints a notice with the sandbox URL rather than swapping accounts silently. - **`hookdeck listen` detects when there is no terminal** and uses compact output automatically, so it works under CI, Docker, `nohup`, and coding agents with no flags. Pass `--output compact` or `--output quiet` to get the same behaviour on a machine that does have a terminal. ## Fixes Three of these turn a silent success into a clear failure. Nothing was removed or renamed, but if you have automation that depended on the old behaviour, these are the ones to check. - **`hookdeck listen` no longer dies at startup without a terminal.** It defaulted to a full-screen UI that opens `/dev/tty`; anywhere without one it failed with `could not open a new TTY` and **exited 0 having forwarded nothing** — no tunnel, no error, nothing to attribute the failure to. ([#333](hookdeck/hookdeck-cli#333), [#339](hookdeck/hookdeck-cli#339)) - **`hookdeck listen` no longer silently creates a guest account when `HOOKDECK_API_KEY` is set.** Events still arrived on your machine, so it looked like it worked — but they went to a throwaway account with no connection, delivery history, retries, or issue triggers, not the project you configured. ([#334](hookdeck/hookdeck-cli#334)) - **`hookdeck ci --local` and `hookdeck login --local` no longer rewrite your global config.** `--local` added a second write rather than redirecting the first, so it silently switched the active project for every other `hookdeck` command on the machine — the opposite of what the flag is for. ([#332](hookdeck/hookdeck-cli#332)) - **Empty secret and identity flags are now rejected.** An unexported shell variable expands to an empty string, and the CLI accepted it: `hookdeck gateway source create --type STRIPE --webhook-secret "$UNSET_VAR"` produced a source with **no verification at all** while looking configured, so it rejected every genuinely signed request. `--webhook-secret ""`, `--source-webhook-secret ""`, `--name ""` and similar now error with a message naming the likely cause. To clear verification deliberately, use the JSON escape hatch: `--config '{"auth": null}'`. ([#335](hookdeck/hookdeck-cli#335)) - **`delete` and `dismiss` no longer exit 0 without doing anything.** Without a terminal these commands skipped the confirmation prompt, printed `Deletion cancelled.` and exited 0 — so a CI job deleted nothing and reported success. They now exit non-zero and tell you to pass `--force`. ([#338](hookdeck/hookdeck-cli#338)) - **Unauthenticated commands no longer hang for four minutes.** Any command run without credentials dropped into interactive sign-in, blocking on a prompt and opening a browser even on a machine with no display. Without a terminal it now exits immediately with instructions for `hookdeck ci`, `HOOKDECK_API_KEY` and `hookdeck login --cli-key`. ([#337](hookdeck/hookdeck-cli#337)) ## Improvements / behavior changes - **Guest login improvements** (merged after v2.4.0): guest credentials are now persisted and attested on login, guest sign-in links refresh while `listen` runs, and a stale key is preserved rather than discarded when validation returns 401. Note one behaviour change: with a guest profile, `hookdeck login` now always opens the browser to claim your sandbox instead of short-circuiting on a valid key. To attach the CLI to an existing Hookdeck account, run `hookdeck logout` then `hookdeck login`. ([#306](hookdeck/hookdeck-cli#306)) - README and `hookdeck listen --help` now describe running in CI accurately. The previous CI example showed a full-screen UI as the expected output while documenting an environment variable `listen` ignored — the exact combination the two bugs above disprove. ## Internal / reliability / infrastructure - The root `--cli-key` flag is now hidden, matching `--api-key` beside it. Authentication is command-specific — `hookdeck login --cli-key`, `hookdeck listen --cli-key`, `hookdeck ci --api-key` — and those remain documented. The global flag still works for anyone already passing it; it is simply no longer advertised as a global option. It was added in #306 and never shipped in a release, so nothing that worked in v2.4.0 changes. - Dropped `go-github` v28, whose only use was a single unauthenticated release check and whose only other effect was linking `golang.org/x/crypto/openpgp` — the unmaintained package flagged by GO-2026-5932, for which no fixed version exists. Replaced with a direct `net/http` call, which is now covered by tests for the first time. ([#331](hookdeck/hookdeck-cli#331)) - Added a `govulncheck` job to CI, so a clean scan is enforced rather than asserted once. - Fixed the release skill's CI gate, which read the legacy commit-status API that GitHub Actions never writes and so reported `pending` unconditionally, blocking every release. ([#336](hookdeck/hookdeck-cli#336)) **Full Changelog**: hookdeck/hookdeck-cli@v2.4.0...v2.5.0 </pre> <p>View the full release notes at <a href="https://github.com/hookdeck/hookdeck-cli/releases/tag/v2.5.0">https://github.com/hookdeck/hookdeck-cli/releases/tag/v2.5.0</a>.</p> </details> <hr> See merge request: Harmonybrew/homebrew-core!16506
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.
Companion PR
Coordinate with hookdeck/core #5233 — that PR owns API, Dashboard CliAuth, Console analytics, and L3 journeys. Merge core first, then this PR.
Why we're doing this
Same motivations as core #5233 — primarily guest sandbox claim (Journey A) and better tracking. Most CLI behaviour already existed; this PR wires guest attestation on
POST /cli-auth, hardens stale-key handling, refreshes expiring listen links, and adds acceptance tests so claim-path changes do not break the alternate login paths.hookdeck loginafterlistenclaims and upgrades the same guest sandbox (signupGuest), so the developer keeps their Console work. The CLI sends guest attestation onPOST /cli-auth(guest_user_id+guest_api_key) so the server can prove the caller is that guest.listensessions must not show an expired Console sign-in link; refresh the 1h/signin/guest?token=…URL automatically.Measurement note: only Journey A gets a clean, linked conversion. Journeys B and C are existing flows — we regression-test them (acceptance + core L3) and rely on core #5233 for new instrumentation (
origin_guest_user_id, abandon event, PostHog identity sync).CLI behaviour (no flags — follows CLI profile)
Behaviour is driven by what's stored in the profile after
listen/logout. There is noauth_intentflag and no TTY prompt.Scope of this PR: changes target Journey A and supporting infrastructure (guest attestation, stale keys, link refresh). Journeys B and C are existing paths — covered by acceptance/L3 tests, not new user-facing behaviour.
POST /cli-authbodyguest_urlset)guest_user_id+guest_api_key(guest attestation)hookdeck logout(guest fields cleared)device_nameonlyCLI Guest Sandbox Abandonedon core — existing path; regression-testedPath B note:
logout→loginto attach the CLI to an existing Platform account is not new — it is how the CLI has always worked once guest credentials are cleared. This PR adds tests to ensure claim-path work does not break it. Withouthookdeck logoutfirst, the CLI still has guest credentials and the default sends you to signup (path A).hookdeck login --cli-key(product onboarding)Dashboard onboarding and Console CLI destination copy
hookdeck login --cli-key <key>for claimed product keys (user + project set at creation).ConfigureFromClaimedCliKey— validate viaGET /cli-auth/validate, save config, clear stale guest profile fields. Does not start device login or guest claim.--cli-keyfell through toLogin(), overwroteapi_keywith the onboarding key, thenPOST /cli-authsent mismatchedguest_user_id/guest_api_key→ 401.hookdeck login(no--cli-key) still opens the browser for Journey A.--cli-keyis for attaching claimed product keys (e.g. after browser signup + EG onboarding), not for replacing the guest claim flow.Claim path details (A)
StartLoginso the user can complete signup /signupGuest.guest_user_id+guest_api_keyso the server can prove the caller is that guest).Existing Platform account (B) — regression coverage only
hookdeck logoutclearsguest_user_id,guest_url, and the stored API key (guest attestation is not sent on the next login).hookdeck loginsends device name only → Dashboard signin URL. No new CLI behaviour — acceptance tests assert the POST body with vs without guest profile.Listen guest URL (separate from
hookdeck login)The TUI link to open Console in a browser (
/signin/guest?token=…). This is not thehookdeck loginclaim flow.RefreshGuestSigninLinkon listen start and ~50m TUI tick →POST /cli/guest/signin-link(core endpoint).MCP
hookdeck_loginfollows the same guest vs logged-out rules.Code changes
pkg/login/client_login.go— guest credentials on POST; stale-key handling; logout clears guest fieldspkg/login/guest_link.go— refresh helper + listen integrationpkg/login/guest_link_test.go— refresh success, API error fallback, missing profile no-oppkg/login/claimed_cli_key.go— explicit--cli-keyvalidate-and-save (guest profile safe)pkg/cmd/login.go— visiblelogin --cli-key; routes claimed keys to dedicated pathpkg/hookdeck/request_log_redact.go— redact guest attestation in debug logsREADME.md,AGENTS.md— CLI authentication keys reference + onboarding behaviourtest/acceptance/guest_login_acceptance_test.go— guest profile POST body vs after logoutTest plan
go test ./...go test -tags=guest ./test/acceptance/...— guest profile POST body vs after logoutgo test ./test/acceptance/...— stale validate 401 → browser flow (login_auth_acceptance_test.go,basictag)pkg/login/claimed_cli_key_test.go— guest profile + onboarding--cli-keypkg/login/guest_link_test.goDepends on
core #5233:
origin_guest_user_id, guest attestation (guest_user_id+guest_api_keyonPOST /cli-auth),POST /cli/guest/signin-link, CliAuth guest-claim completion,flow_mismatchintent API, PostHog identity sync.