Skip to content

feat: make --keyless an opt-in flag on clerk init - #268

Merged
rafa-thayto merged 1 commit into
mainfrom
remove-keyless-mode
May 8, 2026
Merged

feat: make --keyless an opt-in flag on clerk init#268
rafa-thayto merged 1 commit into
mainfrom
remove-keyless-mode

Conversation

@rafa-thayto

@rafa-thayto rafa-thayto commented May 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • clerk init now 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.
  • --keyless is the opt-in flag for the keyless flow. On a non-keyless framework it exits with a usage error.
  • -y no longer bypasses authentication — it skips y/n confirmations only. Without --keyless, an unauthenticated user is still prompted to log in via the browser.
  • 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.
  • Auth-related I/O (isAuthenticated, resolveProfile) is gated behind !optsKeyless so --keyless skips disk reads it doesn't need.
  • Test fixtures DRYed via shared KEYLESS_CTX constant + mockBootstrapTo / mockExistingProject / mockMiddlewareScaffold helpers — collapses ~140 lines of repeated setup.

Test plan

  • bun run format clean
  • bun run lint clean
  • bun run typecheck clean
  • bun run test — 97/97 pass (47 in init/index.test.ts)
  • bun run build succeeds
  • bun run build:compile succeeds; compiled dist/clerk init --help shows --keyless flag and example
  • Manual: run clerk init in a fresh Next.js project with no credentials → should prompt browser login
  • Manual: run clerk init --keyless in a fresh Next.js project → should generate dev keys without prompting login
  • Manual: run clerk init --keyless in a Vue project → should exit with usage error
  • Manual: run clerk init -y in a fresh Next.js project with no credentials → should still prompt browser login (not skip auth)

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-bot

changeset-bot Bot commented May 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 98c6b4d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
clerk Minor

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

@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR restructures how clerk init handles authentication and keyless development keys on keyless-capable frameworks. The new --keyless flag makes keyless mode explicit opt-in; by default, the command authenticates and links a real Clerk application. The implementation introduces a strategy-based execution model that selects between "authenticate", "keyless", or "manual" paths based on authentication state, framework support, and user options. All scenarios—interactive and agent-mode runs—are covered by expanded test assertions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary change: making --keyless an opt-in flag on clerk init, which matches the main objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly relates to the changeset, explaining the new --keyless opt-in flag, authentication behavior changes, and internal refactoring with specific implementation details.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto
rafa-thayto requested a review from djgould May 8, 2026 18:50

@djgould djgould left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@rafa-thayto
rafa-thayto merged commit dcc2073 into main May 8, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the remove-keyless-mode branch May 8, 2026 19:27
rafa-thayto added a commit that referenced this pull request Jul 24, 2026
…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.
rafa-thayto added a commit that referenced this pull request Jul 30, 2026
…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.
rafa-thayto added a commit that referenced this pull request Jul 31, 2026
…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.
rafa-thayto added a commit that referenced this pull request Jul 31, 2026
…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.
rafa-thayto added a commit that referenced this pull request Jul 31, 2026
…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.
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.

2 participants