feat: make --keyless an opt-in flag on clerk init - #268
Conversation
Previously, `clerk init` silently used keyless mode (auto-generated development keys) when run unauthenticated on a keyless-capable framework. The default now always authenticates and links a real Clerk application; `--keyless` is required to opt into auto-generated dev keys. - `--keyless` on a non-keyless framework exits with a usage error - `-y` skips y/n confirmations but no longer bypasses authentication - agent mode without `--keyless` or `--app` prints manual setup guidance instead of silently generating dev keys (agents can't run interactive OAuth) Internal: the `keyless` + `manualSetup` boolean pair is replaced by an `InitStrategy` union (`"keyless" | "manual" | "authenticate"`) so the post-scaffold dispatch is exhaustive. Test fixtures are DRYed via shared `KEYLESS_CTX` + `mockBootstrapTo` / `mockExistingProject` / `mockMiddlewareScaffold` helpers.
🦋 Changeset detectedLatest commit: 98c6b4d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughThis PR restructures how Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…e a keyless instance Restores keyless mode as the default for unauthenticated `clerk init` (reversing PR #268), and extends keyless so an agent can bootstrap and configure Clerk end to end without a Clerk account. Init: - Unauthenticated runs on keyless-capable frameworks use keyless again — human bootstrap and all agent runs. Existing-project human runs still log in. - `--login` forces the authenticated flow; `--keyless` now forces keyless even when signed in. `--keyless --login`, `--keyless --app`, and agent `--login` while signed out are usage errors. - `--template <b2b-saas|b2c-saas|native|waitlist>` pre-configures the keyless application at creation. Operating a keyless instance (Backend API, instance secret key only): - `clerk config pull` and `clerk config patch` cover seven resource groups. - `clerk enable/disable orgs`, `clerk whoami`, and `clerk env pull` work with no account. Billing stays account-only and says why. - Keys are also discovered from `.clerk/.tmp/keyless.json`, so an application a Clerk SDK minted for itself is reachable from the CLI. Account credentials are deliberately not part of the keyless decision: only `--app` or a linked profile selects the account path, so these commands work with or without a platform API key or login session.
…e a keyless instance Restores keyless mode as the default for unauthenticated `clerk init` (reversing PR #268), and extends keyless so an agent can bootstrap and configure Clerk end to end without a Clerk account. Init: - Unauthenticated runs on keyless-capable frameworks use keyless again — human bootstrap and all agent runs. Existing-project human runs still log in. - `--login` forces the authenticated flow; `--keyless` now forces keyless even when signed in. `--keyless --login`, `--keyless --app`, and agent `--login` while signed out are usage errors. - `--template <b2b-saas|b2c-saas|native|waitlist>` pre-configures the keyless application at creation. Operating a keyless instance (Backend API, instance secret key only): - `clerk config pull` and `clerk config patch` cover seven resource groups. - `clerk enable/disable orgs`, `clerk whoami`, and `clerk env pull` work with no account. Billing stays account-only and says why. - Keys are also discovered from `.clerk/.tmp/keyless.json`, so an application a Clerk SDK minted for itself is reachable from the CLI. Account credentials are deliberately not part of the keyless decision: only `--app` or a linked profile selects the account path, so these commands work with or without a platform API key or login session.
…e a keyless instance Restores keyless mode as the default for unauthenticated `clerk init` (reversing PR #268), and extends keyless so an agent can bootstrap and configure Clerk end to end without a Clerk account. Init: - Unauthenticated runs on keyless-capable frameworks use keyless again — human bootstrap and all agent runs. Existing-project human runs still log in. - `--login` forces the authenticated flow; `--keyless` now forces keyless even when signed in. `--keyless --login`, `--keyless --app`, and agent `--login` while signed out are usage errors. - `--template <b2b-saas|b2c-saas|native|waitlist>` pre-configures the keyless application at creation. Operating a keyless instance (Backend API, instance secret key only): - `clerk config pull` and `clerk config patch` cover seven resource groups. - `clerk enable/disable orgs`, `clerk whoami`, and `clerk env pull` work with no account. Billing stays account-only and says why. - Keys are also discovered from `.clerk/.tmp/keyless.json`, so an application a Clerk SDK minted for itself is reachable from the CLI. Account credentials are deliberately not part of the keyless decision: only `--app` or a linked profile selects the account path, so these commands work with or without a platform API key or login session.
…e a keyless instance Restores keyless mode as the default for unauthenticated `clerk init` (reversing PR #268), and extends keyless so an agent can bootstrap and configure Clerk end to end without a Clerk account. Init: - Unauthenticated runs on keyless-capable frameworks use keyless again — human bootstrap and all agent runs. Existing-project human runs still log in. - `--login` forces the authenticated flow; `--keyless` now forces keyless even when signed in. `--keyless --login`, `--keyless --app`, and agent `--login` while signed out are usage errors. - `--template <b2b-saas|b2c-saas|native|waitlist>` pre-configures the keyless application at creation. Operating a keyless instance (Backend API, instance secret key only): - `clerk config pull` and `clerk config patch` cover seven resource groups. - `clerk enable/disable orgs`, `clerk whoami`, and `clerk env pull` work with no account. Billing stays account-only and says why. - Keys are also discovered from `.clerk/.tmp/keyless.json`, so an application a Clerk SDK minted for itself is reachable from the CLI. Account credentials are deliberately not part of the keyless decision: only `--app` or a linked profile selects the account path, so these commands work with or without a platform API key or login session.
…yless instance (#395) * feat(keyless): make keyless the default again and let agents configure a keyless instance Restores keyless mode as the default for unauthenticated `clerk init` (reversing PR #268), and extends keyless so an agent can bootstrap and configure Clerk end to end without a Clerk account. Init: - Unauthenticated runs on keyless-capable frameworks use keyless again — human bootstrap and all agent runs. Existing-project human runs still log in. - `--login` forces the authenticated flow; `--keyless` now forces keyless even when signed in. `--keyless --login`, `--keyless --app`, and agent `--login` while signed out are usage errors. - `--template <b2b-saas|b2c-saas|native|waitlist>` pre-configures the keyless application at creation. Operating a keyless instance (Backend API, instance secret key only): - `clerk config pull` and `clerk config patch` cover seven resource groups. - `clerk enable/disable orgs`, `clerk whoami`, and `clerk env pull` work with no account. Billing stays account-only and says why. - Keys are also discovered from `.clerk/.tmp/keyless.json`, so an application a Clerk SDK minted for itself is reachable from the CLI. Account credentials are deliberately not part of the keyless decision: only `--app` or a linked profile selects the account path, so these commands work with or without a platform API key or login session. * feat(keyless): configure an unclaimed application end to end without an account Close the gaps between "keyless is the default" and "keyless actually works with no human intervention": - config patch validates instance fields against the Backend API's own schema before sending — PATCH /v1/instance answers 204 and silently discards unknown fields, so a typo or an account-only setting (password, social, second_factors, ...) used to print "Config pushed successfully" and change nothing. The refusal names the accepted fields and, for settings BAPI has no route for at all, says claiming is the only path. - config patch no longer prints a stale eventually-consistent re-read as the result of a write; bodyless groups are reported as unconfirmed. - allowlist_identifiers/blocklist_identifiers point at `clerk api` (which reaches them keyless) instead of demanding a login. - users open explains that an unclaimed app has no dashboard page and offers `clerk api /users/<id>`, instead of blaming "not linked" and suggesting remedies that need an application ID that doesn't exist yet. - whoami/env pull detect publishable/secret keys that belong to different applications; env pull refuses to write a mismatched pair. - doctor, open, users, api, env pull, whoami, enable/disable orgs and templates verified end to end on a live unclaimed instance, all under --mode agent with non-TTY stdin: no hangs, no prompts, no browser. docs/keyless-demos: before/after screenshots proving CLI writes reach the live hosted UI pre-claim (orgs step inserted into the sign-in flow, allowlist enforced then lifted), with a README on the FAPI /v1/environment oracle and its limits. * fix(keyless): address CodeRabbit findings on secret handling and error classification - demo-env.sh: bound the accountless-application curl request with connect/max timeouts, and stop echoing the raw minting response (it can contain a live secret_key when only one of the two fields fails to parse). - 09-health-and-users.tape: redact the claim token before it's recorded by piping `clerk open --print` through sed. - open/README.md: add a language to the fenced usage-error example (MD040). - bapi-command.ts: describeBapiTarget now checks options.secretKey first, mirroring resolveBapiSecretKey's actual precedence. - credential-store.ts: exclude 429 from isUnrecoverableRefreshFailure so a rate-limited token refresh isn't reported as an expired session. * chore(keyless): remove demo assets from the repo * fix(keyless): address CodeRabbit findings on doctor side effects, claim-url trust, and env-file key priority * fix(keyless): address review feedback on key precedence, init guard, and doctor diagnostics - Restore CLERK_SECRET_KEY's precedence: the env var wins over a linked profile again, and --instance next to it stays a no-op instead of a usage error. Routing it through resolveKeylessTarget had silently changed both, breaking invocations that work on main; real env-var-path tests pin it now. - init's keep-the-existing-app guard also recognizes an application the SDK minted for itself (.clerk/.tmp/keyless.json), instead of silently orphaning it with a replacement. - doctor reports a malformed local secret key as one named failing check with a remedy, instead of five anonymous 'Check crashed' lines. - whoami: keyless next-steps no longer point at clerk link (which cannot work pre-claim), and an unlinked directory holding a local secret key is called out, since clerk api/users will use that key's instance, not the account. - Smaller review items: billing asserts on an already-resolved target, config schema resolves its target once, apply-patch dry-run messages reuse the verb, claim-url validation inlined, keylessTargetStubs covers every export for module mocks, positive test for the non-429 refresh refusal, keyless disable-orgs test, README corrections, changeset trimmed. * refactor(keyless): gather user-facing keyless copy into lib/copy.ts One function per sentence with typed parameters, so a future i18n layer can swap the module's bodies without touching call sites; pluralisation and agreement rules move with the prose. No visible string changes. * docs(changeset): take the keyless-default release to major Signed-out `clerk init` changes behaviour — it now mints a keyless application instead of launching the browser login — so the release is not purely additive even with CLERK_SECRET_KEY precedence restored. * test(env): restore the iOS test's closing brace lost in a rebase conflict seam * fix(keyless): surface malformed local keys and exported key overrides Resolve the local secret key error in checkLoggedIn even when a stored account token exists, so a broken local CLERK_SECRET_KEY isn't hidden behind "Logged in" — other commands still prefer that key over the account session. whoami now also detects an exported CLERK_SECRET_KEY when a directory is linked, matching resolveBapiSecretKey's precedence: that env var overrides even a linked profile, so whoami's answer was wrong for the commands that honor it. Also list a `clerk api /<path>` example for every key, not just the first, when telling the user which keys are already reachable on an unclaimed application. Addresses CodeRabbit review feedback on PR #395.
Summary
clerk initnow always authenticates and links a real Clerk application by default. The previous silent fallback to auto-generated dev keys (when run unauthenticated on a keyless-capable framework) is removed.--keylessis the opt-in flag for the keyless flow. On a non-keyless framework it exits with a usage error.-yno longer bypasses authentication — it skips y/n confirmations only. Without--keyless, an unauthenticated user is still prompted to log in via the browser.--keylessor--appprints manual setup guidance instead of silently generating dev keys (agents can't run interactive OAuth).Internal
keyless+manualSetupboolean pair is replaced by anInitStrategyunion ("keyless" | "manual" | "authenticate") so the post-scaffold dispatch is exhaustive.isAuthenticated,resolveProfile) is gated behind!optsKeylessso--keylessskips disk reads it doesn't need.KEYLESS_CTXconstant +mockBootstrapTo/mockExistingProject/mockMiddlewareScaffoldhelpers — collapses ~140 lines of repeated setup.Test plan
bun run formatcleanbun run lintcleanbun run typecheckcleanbun run test— 97/97 pass (47 ininit/index.test.ts)bun run buildsucceedsbun run build:compilesucceeds; compileddist/clerk init --helpshows--keylessflag and exampleclerk initin a fresh Next.js project with no credentials → should prompt browser loginclerk init --keylessin a fresh Next.js project → should generate dev keys without prompting loginclerk init --keylessin a Vue project → should exit with usage errorclerk init -yin a fresh Next.js project with no credentials → should still prompt browser login (not skip auth)