Skip to content

feat(setup): setup wizard with browser-based Chat key handoff#5911

Open
TheodoreSpeaks wants to merge 16 commits into
stagingfrom
feat/cli-auth-key
Open

feat(setup): setup wizard with browser-based Chat key handoff#5911
TheodoreSpeaks wants to merge 16 commits into
stagingfrom
feat/cli-auth-key

Conversation

@TheodoreSpeaks

@TheodoreSpeaks TheodoreSpeaks commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

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 via the OAuth device-authorization flow. The CLI generates a request id + poll secret, opens /cli/auth, and polls /api/cli/auth/poll over 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
  • A pairing code shown in both terminal and browser is the anti-phishing compare — the poll secret binds the key to the CLI process, but only the code tells the user the page is theirs
  • Approvals live in Redis (120s TTL, hashed key, single-use via verify-then-atomic-claim); poll verifies the secret before deleting, so an observer of the semi-public request id can neither mint nor cancel
  • Doctor understands both env layouts (compose writes a single root .env; dev writes the three per-app files) — a healthy compose install no longer reports false failures
  • Setup reuses an existing managed Postgres/Redis container (reads port + password back from Docker) instead of colliding on the name; k8s deploys pin the validated kube-context and pipe secrets to helm on stdin, never argv
  • Signup now honours callbackUrl like signin, so a new account returns to the CLI flow

Type of Change

  • New feature

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

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

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.
@TheodoreSpeaks
TheodoreSpeaks requested a review from a team as a code owner July 24, 2026 00:25
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jul 24, 2026 9:19pm

Request Review

@cursor

cursor Bot commented Jul 24, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Introduces Redis-backed CLI authentication that mints Chat API keys, plus broad setup/doctor changes to secrets and deploy paths—security-sensitive auth and credential handling with substantial new surface area.

Overview
Adds bun run setup (wizard for compose / local dev / Helm k8s) and bun run doctor to validate or autofix env files, with README self-hosting collapsed to a single bun run setup path. The wizard can obtain a Chat API key via a device-style CLI flow: terminal opens /cli/auth with a pairing code, the user approves while signed in, and the CLI polls /api/cli/auth/poll; approvals sit in Redis (hashed keys, verifier challenge, mint lock) and keys are minted server-side—never through the browser.

Auth now keeps a validated callbackUrl across login ↔ signup and email verify (fixes signup → verify races and supports returning to /cli/auth after creating an account). Chat keys move off workspace settings to a hosted-only account page at account/settings/chat-keys; copilot key CRUD is centralized in lib/copilot/server/api-keys for the UI and CLI poll route.

Compose files optionally load a root .env; setup adds sha256Base64Url for PKCE-style challenges.

Reviewed by Cursor Bugbot for commit a393319. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread scripts/setup/redis.ts
Comment thread scripts/setup/cli-auth.ts
@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds an interactive setup and diagnostics workflow with browser-based Chat key authorization.

  • Introduces setup modes for Docker Compose, local development, and Kubernetes.
  • Adds a device-style CLI authorization flow backed by short-lived Redis approvals.
  • Adds account-level Chat key management and preserves post-auth callback destinations.
  • Updates environment handling, dependency checks, container reuse, and installation documentation.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failures remain within the scope of the previous review threads.

No blocking failure remains.

Important Files Changed

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
Loading

Reviews (7): Last reviewed commit: "fix(setup): quote kube-context in the he..." | Re-trigger Greptile

Comment thread scripts/setup/modes/k8s.ts Outdated
Comment thread scripts/setup/steps.ts Outdated
Comment thread scripts/setup/modes/k8s.ts Outdated
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.
Comment thread apps/sim/app/(auth)/verify/use-verification.ts
…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
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread scripts/setup/checks.ts
Comment thread scripts/setup/checks.ts
Comment thread apps/sim/app/(auth)/login/login-form.tsx
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.
Comment thread apps/sim/app/api/cli/auth/poll/route.ts
# Conflicts:
#	package.json
#	scripts/check-api-validation-contracts.ts
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread scripts/setup/modes/k8s.ts Outdated
Comment thread scripts/setup/steps.ts
Comment thread scripts/setup/redis.ts
@blacksmith-sh

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
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/cli/auth/poll/route.ts
Comment thread scripts/setup/checks.ts
Comment thread scripts/setup/cli-auth.ts
Comment thread scripts/setup/detect.ts
Comment thread scripts/setup/modes/k8s.ts
…, 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
Comment thread scripts/setup/steps.ts
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.
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread scripts/setup/checks.ts
Comment thread scripts/setup/detect.ts Outdated
Comment thread scripts/setup/modes/k8s.ts Outdated
…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.
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread scripts/setup/modes/k8s.ts
Comment thread scripts/setup/modes/k8s.ts Outdated
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).
@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@greptile

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant