Skip to content

feat: delegate keyless mode to SDK, autoclaim on authenticated init#267

Open
rafa-thayto wants to merge 1 commit intomainfrom
keyless-bootstrap-keys
Open

feat: delegate keyless mode to SDK, autoclaim on authenticated init#267
rafa-thayto wants to merge 1 commit intomainfrom
keyless-bootstrap-keys

Conversation

@rafa-thayto
Copy link
Copy Markdown
Contributor

Summary

  • Stop writing keys during keyless bootstrap: clerk init for unauthenticated users no longer calls createAccountlessApp() or writes temporary keys to .env.local. The @clerk/nextjs SDK handles keyless mode natively at runtime — when no keys are present, the SDK creates an accountless app and shows a "Configure your application" widget.

  • Read SDK breadcrumbs: The CLI now reads the SDK's keyless breadcrumb at .clerk/.tmp/keyless.json (in addition to the legacy CLI breadcrumb at .clerk/keyless.json). A unified readAnyKeylessBreadcrumb() reader tries the SDK path first and normalizes both formats to { claimToken, source }.

  • Autoclaim during clerk init: When an authenticated user runs clerk init without --app, the CLI checks for an SDK keyless breadcrumb before falling through to authenticateAndLink. If found, it claims the temporary app, links it, and pulls real keys — all in one step.

Test plan

  • bun run typecheck passes
  • bun run lint passes
  • bun run format:check passes
  • bun test packages/cli-core/src/lib/keyless.test.ts — 28 tests pass (17 new SDK breadcrumb + unified reader tests)
  • bun test packages/cli-core/src/lib/autoclaim.test.ts — 11 tests pass (updated for dual breadcrumb clearing)
  • bun test packages/cli-core/src/commands/init/index.test.ts — 46 tests pass (5 new autoclaim-in-init tests)
  • Manual E2E: run clerk init --starter --framework next --yes unauthenticated → app scaffolds without keys → run the app → SDK enters keyless mode and writes .clerk/.tmp/keyless.json → authenticate with clerk auth login → re-run clerk init → autoclaim kicks in, real keys are pulled

…crumb on init

clerk init no longer calls createAccountlessApp() for unauthenticated users —
the SDK handles keyless mode natively at runtime. When an authenticated user
re-runs clerk init (without --app), the CLI reads the SDK's breadcrumb at
.clerk/.tmp/keyless.json, claims the temporary app, links it, and pulls real
keys — eliminating the need for a separate clerk auth login step.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 7, 2026

⚠️ No Changeset found

Latest commit: 9eb1eb2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR extends the Clerk CLI's keyless authentication flow to support breadcrumbs created by the Clerk SDK at .clerk/.tmp/keyless.json alongside the existing CLI breadcrumb at .clerk/keyless.json. A new unified breadcrumb reader checks the SDK location first and falls back to the CLI location. The init command now attempts to claim and link the temporary keyless app automatically before prompting for manual authentication, and it skips interactive app selection when autoclaim succeeds. The change is reflected across the keyless/autoclaim libraries, integration with the init command, and comprehensive test coverage for both sources and various claim outcomes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 accurately and concisely summarizes the main changes: delegating keyless mode to SDK and adding autoclaim on authenticated init.
Description check ✅ Passed The description is well-organized and directly related to the changeset, covering the three key objectives: stopping key writing in keyless bootstrap, reading SDK breadcrumbs, and autoclaiming during init.
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.

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli-core/src/commands/init/index.ts`:
- Around line 104-116: tryInitAutoclaim currently returns a result object but
the code treats any status === "claimed" as fully done; change the flow to
inspect the full result from tryInitAutoclaim/attemptAutoclaim and only skip
authenticateAndLink()/pull() when result.status === "claimed" AND
result.envPulled === true (set autoclaimPulled = true only in that case); if
envPulled is false (or missing) treat it as not fully claimed and proceed to
call authenticateAndLink(ctx.cwd, ...) and later pull() so local linking/envs
are completed; apply the same adjustment to the other tryInitAutoclaim usage
sites (the other blocks that currently set autoclaimPulled on any "claimed"
result).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5138e676-8dc0-4ef2-a35a-51c16ece8551

📥 Commits

Reviewing files that changed from the base of the PR and between aca3db2 and 9eb1eb2.

📒 Files selected for processing (8)
  • packages/cli-core/src/commands/auth/README.md
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/index.test.ts
  • packages/cli-core/src/commands/init/index.ts
  • packages/cli-core/src/lib/autoclaim.test.ts
  • packages/cli-core/src/lib/autoclaim.ts
  • packages/cli-core/src/lib/keyless.test.ts
  • packages/cli-core/src/lib/keyless.ts

Comment on lines +104 to +116
let autoclaimPulled = false;
if (!keyless && !manualSetup) {
bar();
const createIfMissing = agent
? await deriveProjectName(ctx.cwd, bootstrap?.projectName)
: undefined;
await authenticateAndLink(ctx.cwd, options.app, createIfMissing);

const claimed = !options.app && authed && (await tryInitAutoclaim(ctx.cwd));
if (!claimed) {
const createIfMissing = agent
? await deriveProjectName(ctx.cwd, bootstrap?.projectName)
: undefined;
await authenticateAndLink(ctx.cwd, options.app, createIfMissing);
} else {
autoclaimPulled = true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle partial autoclaim success before skipping normal link/env pull

tryInitAutoclaim treats every status: "claimed" as fully complete, but attemptAutoclaim can return { status: "claimed", envPulled: false } when local linking or env pull failed. The current flow then skips both authenticateAndLink() and the later pull(), which can leave the project not fully configured.

Suggested fix
-  let autoclaimPulled = false;
+  let autoclaimPulled = false;
   if (!keyless && !manualSetup) {
     bar();

-    const claimed = !options.app && authed && (await tryInitAutoclaim(ctx.cwd));
-    if (!claimed) {
+    const autoclaim = !options.app && authed ? await tryInitAutoclaim(ctx.cwd) : { claimed: false, envPulled: false };
+    autoclaimPulled = autoclaim.envPulled;
+    if (!autoclaim.claimed || !autoclaim.envPulled) {
       const createIfMissing = agent
         ? await deriveProjectName(ctx.cwd, bootstrap?.projectName)
         : undefined;
       await authenticateAndLink(ctx.cwd, options.app, createIfMissing);
-    } else {
-      autoclaimPulled = true;
     }
   }
-async function tryInitAutoclaim(cwd: string): Promise<boolean> {
+async function tryInitAutoclaim(
+  cwd: string,
+): Promise<{ claimed: boolean; envPulled: boolean }> {
   const result = await attemptAutoclaim(cwd);
   if (result.status === "claimed") {
     const label = result.app.name || result.app.application_id;
-    log.success(`Claimed and linked \`${label}\``);
-    return true;
+    if (result.envPulled) {
+      log.success(`Claimed and linked \`${label}\``);
+    } else {
+      log.warn(`Claimed \`${label}\`, but local setup was incomplete; continuing setup.`);
+    }
+    return { claimed: true, envPulled: result.envPulled };
   }
   if (result.status !== "not_keyless") {
     log.debug(`init: autoclaim returned '${result.status}', falling through to link`);
   }
-  return false;
+  return { claimed: false, envPulled: false };
 }

Also applies to: 138-139, 322-333

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli-core/src/commands/init/index.ts` around lines 104 - 116,
tryInitAutoclaim currently returns a result object but the code treats any
status === "claimed" as fully done; change the flow to inspect the full result
from tryInitAutoclaim/attemptAutoclaim and only skip
authenticateAndLink()/pull() when result.status === "claimed" AND
result.envPulled === true (set autoclaimPulled = true only in that case); if
envPulled is false (or missing) treat it as not fully claimed and proceed to
call authenticateAndLink(ctx.cwd, ...) and later pull() so local linking/envs
are completed; apply the same adjustment to the other tryInitAutoclaim usage
sites (the other blocks that currently set autoclaimPulled on any "claimed"
result).

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