chmonitorchmonitor
Authentication

Clerk

Authenticate chmonitor users with Clerk — browser sign-in via __session cookie, with optional public read-only mode and MCP OAuth support.

Use Clerk when you want browser-based sign-in with user accounts. The browser authenticates via a Clerk __session cookie, verified networklessly on each /api/v1/* call. The MCP server additionally accepts Clerk OAuth bearer tokens.

  • Browser clients sign in through Clerk's hosted UI.
  • The __session cookie is sent automatically same-origin and verified on the server using the Clerk secret key — no network call per request.
  • The publishable key must be present at build time so the client-side gate enables Clerk UI (sign-in button, user menu). The secret key is read at runtime.
Loading diagram…

Prerequisites

A Clerk application with its publishable and secret keys. The client VITE_AUTH_PROVIDER / VITE_CLERK_PUBLISHABLE_KEY values are derived from the canonical CHM_* names at build time, so set them before pnpm run build. CLERK_SECRET_KEY (the sk_... key) is the runtime secret and stays server-side.

Migrating from Next.js?

If migrating from the legacy Next.js app, see Migrate to v0.3 for the NEXT_PUBLIC_* → VITE_* rename.

Setup

Prop

Type

Create a Clerk application

Create a Clerk application at clerk.com.

Copy your keys

Copy the Publishable key (pk_live_...) and Secret key (sk_live_...) from the Clerk dashboard.

Set build-time env vars

Set these before building the client (the client VITE_* values are derived from them):

CHM_AUTH_PROVIDER=clerk
CHM_CLERK_PUBLISHABLE_KEY=pk_live_...

Set runtime env vars

Set these on the server (or as Worker secrets):

CHM_AUTH_PROVIDER=clerk
wrangler secret put CLERK_SECRET_KEY   # paste sk_live_...

Rebuild and redeploy

The sign-in button appears in the header after redeploy.

Verify

After redeploy, the sign-in button appears in the header. Sign in through Clerk's hosted UI, then confirm data loads on the dashboard — an authenticated __session cookie satisfies the guard on every /api/v1/* call.

Clerk OAuth for MCP

The MCP server at /api/mcp also accepts Clerk OAuth bearer tokens. Clerk acts as the authorization server (login, consent, dynamic client registration); chmonitor is the resource server and verifies tokens via Clerk's REST introspection. This lets MCP clients use an OAuth flow instead of a chm_ API key. It uses the same CLERK_SECRET_KEY — no extra configuration needed.

See MCP Server for connection details.

Gate features to authenticated users

Most features are public even with Clerk enabled. The AI agent (agent) and control actions (actions — KILL QUERY, OPTIMIZE TABLE) default to authenticated, so they require sign-in. To change a feature's access:

CHM_FEATURE_AGENT_ACCESS=public          # loosen the agent to public
CHM_FEATURE_TABLES_ACCESS=authenticated  # require sign-in for tables

See Feature Permissions for all feature ids and options.

Public read-only mode

Default is fully gated

By default, Clerk gates every /api/v1/* request — an anonymous visitor sees the dashboard shell but no data (every data call returns 401). Set CHM_CLERK_PUBLIC_READ to let anonymous visitors view read-only monitoring content while keeping sign-in required for writes.

CHM_AUTH_PROVIDER=clerk
CHM_CLERK_PUBLIC_READ=true   # runtime / Worker var

Access is then split along a read / write boundary:

Reads

Predefined monitoring data (overview, queries, tables, metrics, charts, schema browsing) serve to anonymous visitors.

Writes

AI agent, control actions (KILL QUERY, OPTIMIZE TABLE), and arbitrary SQL still return 401 for anonymous callers.

Keys and signed-in users

API keys (chm_ bearer) and signed-in Clerk users get full read + write.

Anonymous capability matrix

Auth modeAnonymous readAnonymous write
none✅✅ (everyone is authenticated)
clerk + CHM_CLERK_PUBLIC_READ=true✅❌
clerk (default)❌❌
proxy❌❌ (proxy authenticates upstream)

The flag only applies to the clerk provider — none is already fully public and proxy fronts its own auth. It is off by default, so existing locked-down Clerk deployments are unchanged. The client reads these flags from GET /api/v1/config (capabilities.read / capabilities.write) to decide what to surface.

On this page