---
title: "Clerk"
description: "Use Clerk for MainSDK auth - application setup, development and production instances, main.yaml, secret keys, local testing and swapping."
section: "integrations"
canonical: "https://mainsdk.dev/docs/integrations/clerk"
updated: "2026-10-28"
---

# Clerk

> Use Clerk for MainSDK auth - application setup, development and production instances, main.yaml, secret keys, local testing and swapping.

Canonical: https://mainsdk.dev/docs/integrations/clerk · Updated: 2026-10-28

Clerk is a hosted user management service and one of the drivers behind [`auth`](https://mainsdk.dev/docs/interfaces/auth). Clerk holds your users, their sign-in methods and their organisations; `auth` gives your routes one way to read the session and require a role, whatever provider sits underneath. Pick Clerk when you want a hosted user directory with ready-made sign-in screens and organisations, and you don't want to run identity yourself. The `auth` spec names Clerk as its default and proposes Clerk and Better Auth as the two drivers that make `auth` stable in 0.3.

## Interfaces it serves

### Sign-in and sessions (`auth`)

- Status: Building for 0.3 · ships as a stable API
- Driver: `clerk` (Clerk)
- Docs: https://mainsdk.dev/docs/interfaces/auth.md

```yaml
auth:
  driver: clerk
```

Swap to another provider: `github`, `auth0`, `better-auth`, `workos`, `supabase`, `local`.

## Before you start

You need a Clerk account.

1. **Create an application** in the Clerk Dashboard. Clerk creates a development instance for it straight away.
2. **Choose sign-in methods** for the application. Turn on passkeys; MainSDK makes them the default method. Add the social providers you want, such as GitHub.
3. **Copy the API keys.** Each instance has a publishable key, used in the browser, and a secret key, used only on your server.
4. **Create the production instance** when you are ready to go live. It has its own keys and needs your own domain, which Clerk asks you to verify with DNS records.
5. **For organisations:** turn on Clerk's organisations feature if your customers sign in as teams.

Store the keys in your secrets manager, never in code or a committed env file.

## Configure

```yaml title="main.yaml"
auth:
  driver: clerk
  methods: [passkeys, whatsapp, sso]
  whatsapp: { driver: whatsapp-cloud }
  sso: { protocols: [saml, oidc], scim: true }
  session: { ttl: 15m, refresh: 7d, bind: dbsc }
  # [TBD] key names; suggested secrets: CLERK_SECRET_KEY, CLERK_PUBLISHABLE_KEY
```

The `auth` spec does not define the keys a driver reads its credentials from yet. When it does, they take `secret:` references. Use development-instance keys in development and stage, and production-instance keys in production.

## Use it

```ts title="app/api/orders/route.ts"
import { auth, db } from "mainsdk";

export async function GET(req: Request) {
  const session = await auth.require(req);   // 401 with MAIN-AUTH-0001 if not signed in
  return Response.json(await db.orders.for(session.user).list({ limit: 20 }));
}
```

Turn on SSO for one of your customer organisations:

```ts title="server/sso.ts"
import { auth } from "mainsdk";

export async function enablePinecartSso() {
  await auth.sso.enable("pinecart", {
    protocol: "saml",
    domains: ["pinecart.com"],
    metadata: new URL("https://idp.pinecart.com/saml/metadata"),
  });
}
```

## Local development and testing

- **No Clerk account:** the `local` auth driver runs in development with no keys.
- **Clerk development instance:** use its keys locally to try the real sign-in screens. Development instances are meant for testing and have looser limits and settings than production.
- **Tests:** `createTestApp()` from `@mainsdk/testing` puts `auth` on its in-memory fake, and `chaos.on("auth", { outage: true })` simulates Clerk being unreachable; `auth.require()` fails closed.

```ts title="app/api/orders/route.test.ts"
import { it } from "node:test";
import assert from "node:assert/strict";
import { createTestApp } from "@mainsdk/testing";
import { GET } from "./route";

it("auth.AC1 refuses a request with no session", async () => {
  createTestApp({ main: "./main.yaml" });
  assert.equal((await GET(new Request("http://localhost/api/orders"))).status, 401);
});
```

## What MainSDK adds on top

- Short-lived, HttpOnly, Secure, SameSite sessions with rotating refresh tokens, bound to the device where the browser supports it.
- Passkeys by default, with conditional create after a password sign-in and a report of the share of users on passkeys.
- Fails closed when Clerk cannot be reached.
- WhatsApp codes single use, expiring and rate limited per phone and per IP; SAML and SCIM provided in your app where the driver lacks them.
- Every recovery audited, never weaker than sign-in.

See [Sign-in and sessions](https://mainsdk.dev/docs/interfaces/auth) for the full contract.

## Limits and notes

- Users, sessions and MFA stay in your Clerk instance. MainSDK never becomes the identity provider.
- Clerk and Better Auth are the proposed pair for a stable `auth` in 0.3 (spec ticket for both drivers and the conformance suite); that choice is still open.
- Check where Clerk stores user data against your residency needs. MainSDK's regions (US, EU, India) govern the records in your own `db`.
- `auth.raw()` returns the Clerk client for features `auth` does not wrap. `mainsdk check` lists every use.

## Swap to or from it

```yaml title="main.yaml"
auth:
  driver: auth0   # was clerk
```

> **Warning:** Identity is stateful. Your users, their passkeys and their organisations live in Clerk, so moving them to another provider is a migration that MainSDK runs as a guided job with a rollback, not a silent switch. Passkeys are bound to a domain, so check what the new provider can import before you plan the move.

## FAQ

**Do I have to use Clerk's sign-in screens?** `auth` gives you passkey, WhatsApp and SSO calls you can build your own screens on. Whether the driver also mounts Clerk's prebuilt components is not specified.

**Where do I put the publishable key?** It is safe in the browser, but keep it in your secrets setup like the secret key so each environment gets the right instance.

**Can I keep my existing Clerk users?** Yes. The driver runs on your existing instance (`byo-identity`); nothing moves.

## Next

- [Sign-in and sessions](https://mainsdk.dev/docs/interfaces/auth) and [Permissions](https://mainsdk.dev/docs/interfaces/access)
- [Better Auth](https://mainsdk.dev/docs/integrations/better-auth), [Auth0](https://mainsdk.dev/docs/integrations/auth0) and [GitHub](https://mainsdk.dev/docs/integrations/github)
- [Swap a driver](https://mainsdk.dev/docs/guides/swap-a-driver)
