Ops — Clerk Keys
Clerk provides authentication (magic link / email code sign-in). Two keys are used:
Clerk provides authentication (magic link / email code sign-in). Two keys are used:
| Variable | Where it lives | Notes |
|---|---|---|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Browser (public) | Safe to expose; used by the frontend ClerkProvider |
CLERK_SECRET_KEY | Server only | Never expose or commit |
Both are set in .env.local for local dev and in the Cloudflare Pages
dashboard for production (deploys.md).
The official rotation path: clerk env pull
The Clerk CLI (clerk, installed at /opt/homebrew/bin/clerk; verified
v3.0.0, 2026-08-06) pulls your instance's keys into the local env file — this
is the documented mechanism for rotating/refreshing keys:
# Pull the dev instance keys into .env.local (default)
clerk env pull
# Target a specific app by ID, or pull production keys to a different file
clerk env pull --app app_abc123
clerk env pull --instance prod --file .env
clerk env pull writes NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and
CLERK_SECRET_KEY (and any other Clerk-managed vars). It overwrites the
target file, so don't point it at .env.local carelessly if that file
carries non-Clerk vars — either merge by hand after, or pull to a scratch
file and copy the two key lines over. Never commit the result.
Rotating keys end-to-end
- In the Clerk dashboard (dashboard.clerk.com → API Keys), rotate the secret key (or switch to a different instance, e.g. production for pass 13).
- Pull locally:
clerk env pull(or the--instance prod/--appvariants) and update.env.local. - Restart
npm run dev; verify sign-in still works. - Update the Cloudflare Pages dashboard env vars for the same key pair
(Dashboard → Workers & Pages →
guild→ Settings → Environment variables), then re-deploy (deploys.md). - Confirm the middleware key check doesn't trip:
middleware.tsthrows at runtime (not build time) if either key is missing — a missing key surfaces as a clear error naming the variable.
Requirements on the instance
- Email sign-in (magic link / email code) must be enabled on the
instance — that's the platform's only sign-in method (pass 10 human step;
the sign-in page renders Clerk's
<SignIn />). - The Clerk
user_…id is not the UUID stored inpractitioners.user_id— the auth shim'sauth.usersbridge mapsclerk_id → UUIDon first request (lib/auth/helpers.ts::resolveUserId). Don't try to correlate the two directly; use the bridge.