Skip to content

chore: remove Clerk - #27

Merged
kunjhirapara merged 1 commit into
mainfrom
chore/remove-clerk
Sep 13, 2026
Merged

kunjhirapara merged 1 commit into
mainfrom
chore/remove-clerk

Conversation

@kunjhirapara

Copy link
Copy Markdown
Owner

Auth.js has been the only working sign-in path since the migration landed. Clerk was still installed, still registered as a Convex auth provider, and still had a live webhook endpoint that could write to the users table. This removes all of it.

What's gone

  • @clerk/nextjs and svix as dependencies
  • convex/http.ts's /clerk-webhook route (179 lines) and its signature verification, plus syncUser / syncUserFromWebhook in convex/users.ts that only it called. http.ts is down to the one remaining route, /internal/backup-record.
  • Clerk's provider entry in convex/auth.config.ts, so a Clerk token is now rejected rather than accepted-but-unused
  • ConvexClerkProvider, UserSyncStatusProvider, useSyncUser, useClerkAppearance, clerkAppearance — none of them rendered any more
  • every CLERK_* variable from .env.example, docker-compose.yml, the Dockerfile build args, both workflows, src/lib/env.ts and /api/health

convex/lib/subjectResolution.ts drops to a single db.get

The by_clerk_id and by_legacy_clerk_id fallbacks existed because a token could arrive from either provider mid-migration. With one provider registered, a subject can only be a Convex document id — those reads could only ever miss. normalizeId still guards the lookup so a malformed subject resolves to "no such user" rather than a 500.

users.clerkId is deliberately kept

Worth being explicit about, since "remove Clerk completely" would suggest otherwise.

It stopped being an authentication identifier here — nothing verifies a token against it any more. But it is the internal user id that interviews.candidateId, interviews.interviewerIds and auditLogs.actorClerkId hold as a plain v.string(), across hundreds of call sites in 30 files. Dropping the column would orphan the participants of every interview.

Renaming it is a data migration of its own, and it additionally needs migrations/authBackfill:run to have run against production first. Tracked separately.

Docs

README.md's setup and pre-launch sections now describe Auth.js instead of Clerk, including the AUTH_URL empty-string trap that took production down — @auth/core uses ??, so an empty value disables trustHost and is strictly worse than an absent one. CLAUDE.md's identity table no longer describes resolution logic that has been deleted.

Verification

  • npm run typecheck — clean
  • npm run test — 265/265 pass
  • npm run build — succeeds

Note for after merge

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_WEBHOOK_SECRET are no longer read by anything, so the GitHub Actions secrets and the Portainer stack entries can be deleted whenever convenient. Leaving them set is harmless.

Auth.js has been the only working sign-in path since the migration landed;
Clerk was still installed, still registered as a Convex auth provider, and
still had a live webhook endpoint that could write to the users table. This
removes all of it.

Gone:
- @clerk/nextjs and svix as dependencies
- convex/http.ts's /clerk-webhook route (179 lines) and its signature
  verification, along with syncUser/syncUserFromWebhook in convex/users.ts
  that it called
- Clerk's provider entry in convex/auth.config.ts, so a Clerk token is now
  rejected rather than accepted-but-unused
- ConvexClerkProvider, UserSyncStatusProvider, useSyncUser, useClerkAppearance
  and clerkAppearance, none of which were rendered any more
- every CLERK_* variable from .env.example, docker-compose.yml, the Dockerfile
  build args, both workflows, src/lib/env.ts and the health endpoint

convex/lib/subjectResolution.ts drops to a single db.get. The by_clerk_id and
by_legacy_clerk_id fallbacks existed because a token could arrive from either
provider mid-migration; with one provider registered, a subject can only be a
Convex document id, so those reads could only ever miss.

users.clerkId is deliberately kept. It stopped being an authentication
identifier here, but it is the internal user id that interviews.candidateId,
interviews.interviewerIds and auditLogs.actorClerkId hold as plain v.string()
across hundreds of call sites. Dropping the column would orphan the
participants of every interview, so renaming it is a data migration of its own
and needs the production backfill to have run first.

README.md's setup and pre-launch sections now describe Auth.js, including the
AUTH_URL empty-string trap that took production down, and CLAUDE.md's identity
table no longer describes resolution logic that has been deleted.

Verified: typecheck clean, 265/265 tests pass, production build succeeds.
Copilot AI lite review requested due to automatic review settings September 13, 2026 16:32

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@kunjhirapara
kunjhirapara merged commit d95a9f7 into main Sep 13, 2026
1 check passed
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