feat(setup): setup wizard with browser-based Chat key handoff#5911
feat(setup): setup wizard with browser-based Chat key handoff#5911TheodoreSpeaks wants to merge 16 commits into
Conversation
Adds `bun run setup` and `bun run doctor` for local installs, and replaces the wizard's paste-your-Chat-key step with a browser handoff that never puts the key in a URL.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryHigh Risk Overview Auth now keeps a validated Compose files optionally load a root Reviewed by Cursor Bugbot for commit a393319. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryAdds an interactive setup and diagnostics workflow with browser-based Chat key authorization.
Confidence Score: 5/5The PR appears safe to merge because no blocking failures remain within the scope of the previous review threads. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| scripts/setup/modes/k8s.ts | Pins Helm and kubectl operations to the validated context, transfers secrets through Helm stdin values, and safely quotes copyable context arguments. |
| scripts/setup/cli-auth.ts | Implements browser launch, pairing-code display, and polling for the approved Chat key. |
| apps/sim/lib/cli-auth/approval-store.ts | Implements short-lived Redis approval records with verifier checking and single-mint coordination. |
| apps/sim/app/api/cli/auth/poll/route.ts | Polls and claims approved requests, generates a Chat key, and consumes the approval after successful minting. |
| scripts/setup/steps.ts | Collects and validates setup secrets and integrates browser-based Chat key acquisition. |
Sequence Diagram
sequenceDiagram
participant CLI as Setup CLI
participant Browser
participant Sim as Sim Web App
participant Redis
participant Keys as Chat Key Service
CLI->>CLI: Generate request, verifier, and pairing code
CLI->>Browser: Open /cli/auth with request and challenge
Browser->>Sim: Authenticate and approve request
Sim->>Redis: Store short-lived approval
loop Until approved or expired
CLI->>Sim: Poll with request and verifier
Sim->>Redis: Verify secret and claim approval
end
Sim->>Keys: Generate Chat API key
Keys-->>Sim: Return generated key
Sim->>Redis: Consume approval
Sim-->>CLI: Return key over polling response
Reviews (7): Last reviewed commit: "fix(setup): quote kube-context in the he..." | Re-trigger Greptile
The browser handoff is now the only path — the wizard waits on a spinner instead of racing a paste prompt. Consent card leads with "Connect your terminal" and moves the match-the-code disclaimer into the description.
…ed keys Review findings from #5911: - helm/kubectl now run against the validated context instead of the ambient one - helm values are piped on stdin rather than passed as --set arguments - ENCRYPTION_KEY/API_ENCRYPTION_KEY are checked for the 64-hex format the app requires, not just length, so an unusable key is replaced rather than kept - the managed Redis container's published port is read back instead of assumed
|
@cursor review |
list/generate/delete each repeated the same /api/validate-key envelope in their route. They now share callValidateKey in lib/copilot/server/api-keys.ts, which also keeps the display masking server-side so the full key can only ever leave at creation.
…ad code - PKCE verifier/state/pairing code now use generateSecureToken, generateRandomHex and generateShortId instead of hand-rolled randomBytes; the pairing loop's modulo was unbiased only because 256 % 32 == 0 - new sha256Base64Url in @sim/security/hash so both sides of the PKCE exchange derive the challenge from one implementation - isUsableSecret moved beside SECRET_KEYS so setup and doctor apply the same rule; doctor previously passed a key setup would replace - isTruthy narrowed to true/1, matching the app it claims to mirror — it accepted yes/on, so a flag could read on in doctor and off in the app - checkLive runs its five probes concurrently (~17s serial worst case) - detection overlaps the banner animation instead of queueing behind it - glyph.fail/glyph.warn at 13 sites that bypassed the constant; removed unused prompter exports, a dead ENV_PATHS re-export, and an unused export keyword
Compose writes a single root .env (what docker-compose reads via env_file) but the checks required the three per-app files, so a successful compose install was followed by doctor printing three failures and exiting 1 — and the whole coherence catalog was skipped because it keyed off apps/sim/.env existing. Layout is now derived from what's on disk and every check consults it: file and schema checks iterate the layout's targets, consistency reports skip when there's only one file to mirror, and coherence/live read the layout's primary file. The wizard's existing-config detection counts root for the same reason — a compose install used to read as unconfigured and re-run from scratch.
…tener The CLI no longer binds a local port. It generates a request id + poll secret, opens /cli/auth, and polls /api/cli/auth/poll over TLS while the user approves in the browser — so the flow works over SSH and inside containers, where the browser and terminal don't share a machine. - approve stores the approval keyed by request id (session-authed, userId from the session only); poll verifies the secret before an atomic claim, so an observer of the semi-public request id can neither mint nor cancel it - pairing code stays as the anti-phishing compare; no key ever crosses the browser; done page just confirms - removes the loopback listener, /token exchange, buildCliHandoffUrl, and validateCliCallbackUrl (+ its tests) — nothing hands a key to a URL anymore
…olliding A running sim-postgres fell through to `docker run --name sim-postgres` and died on the name conflict; a stopped one failed with "no DATABASE_URL to reach it" because the generated password only lived in the env files a fresh clone lacks. Both facts are recoverable from Docker: the ladder now reads the published port and password back via `docker inspect` and reuses the container (starting it if stopped). A container that won't answer prompts before recreating, and never drops the data volume silently.
Each run mode now names who it's for — compose for self-hosting/evaluating, dev for contributing to Sim, k8s for rehearsing a production deploy — with the live detection state (Docker/kube/VM) appended.
# Conflicts: # package.json # scripts/check-api-validation-contracts.ts
|
@cursor review |
This comment has been minimized.
This comment has been minimized.
…s + k8s Review findings from #5911: - poll now reserves the mint with an atomic NX lock instead of deleting the approval up front, so a failed mint (e.g. mothership blip) is retried by the next poll instead of forcing a fresh browser approval; the lock still prevents a double-mint and its TTL frees the slot if the caller dies - setup reuses/recreates an unhealthy managed sim-redis instead of colliding on the name (Redis has no data volume, so it removes and recreates without a prompt) - k8s failure-path hints carry --context, matching the success-path hints, so a changed ambient context can't send diagnostics to the wrong cluster - compose port-free waits for a killed port to actually release before re-checking; SIGKILL is async, so the immediate re-check re-saw the port
|
@cursor review |
…, helm cwd Review findings from #5911: - a post-mint completeApproval failure no longer routes into releaseMint — the mint lock now outlives the approval (shared TTL), so a cleanup blip can't leave a re-mintable window and orphan a key; cleanup is best-effort after the key ships - compose doctor --fix writes the feature-flag twin to the layout's primary env (root .env on a compose install), not always apps/sim/.env - Windows opens the browser via `cmd /c start "" <url>` — `start` is a shell builtin, so spawning it directly ENOENT'd and the handoff never opened - managed-container detection filters loosely and pins the exact name in code; Docker's `name=^x$` anchor matches the internal `/x` form and often missed, skipping the reuse branch - the shared helm/kind run helper pins cwd to the repo root, matching helm test, so `helm upgrade --install ./helm/sim` works from any working directory
…esh README - Add /account/settings/chat-keys — a linkable page to view, create, and revoke Chat API keys - Remove Chat keys from the settings sidebar (account + unified nav) and its render branches - README: replace Docker Compose + Manual Setup with the bun run setup wizard; drop the manual COPILOT_API_KEY step, point to the manage page
Cursor: the warn hardcoded '64-character hex key', but only ENCRYPTION_KEY/API_ENCRYPTION_KEY require that — BETTER_AUTH_SECRET/INTERNAL_API_SECRET only need length >= 32. Use the existing secretRequirement(key) helper so each replaced key reports its actual requirement.
|
@cursor review |
…uoted context hints
- Doctor: for the compose (root) env layout, require only the secrets compose has no interpolation default for (BETTER_AUTH_SECRET/ENCRYPTION_KEY/INTERNAL_API_SECRET). DATABASE_URL/BETTER_AUTH_URL/NEXT_PUBLIC_APP_URL come from docker-compose ${VAR:-default}, so a healthy compose install no longer fails doctor.
- Binary detection: use Bun.which instead of which (which is absent on Windows), so kubectl/helm/kind/docker resolve cross-platform.
- k8s diagnostic hints: POSIX-quote the kube-context so a context with whitespace/metacharacters can't break or inject into a copied command.
|
@cursor review |
The tear-down hint used --kube-context ${context} raw while the sibling kubectl hints already used shq(); a context with whitespace/metacharacters could break or inject into the copied command. All copyable k8s hints now go through shq(context).
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a393319. Configure here.
Summary
bun run setup— interactive first-run wizard (--quick,--mode compose|dev|k8s);bun run doctor [--fix] [--json]validates an existing install. Run-mode picker names who each mode is for: compose (self-host/evaluate), dev (contribute), k8s (rehearse a prod deploy)/cli/auth, and polls/api/cli/auth/pollover TLS while the user approves in the browser. No loopback listener, so it works over SSH and inside containers. No key ever crosses the browser.env; dev writes the three per-app files) — a healthy compose install no longer reports false failurescallbackUrllike signin, so a new account returns to the CLI flowType of Change
Testing
Device-flow poll verified end-to-end against a local server + Redis (pending → approve →
consume-once; wrong-secret returns pending and does not delete the approval). Unit tests cover
the approval store and both routes; the container-reuse recovery and doctor layout were run
against a live Docker/env.
bun run lint,check:api-validation:strict,check:react-query,check:client-boundary,type-check, and the unit suites pass. The wizard's mode flows(compose/dev/k8s) aren't exercised by automated checks.
Checklist