# Scoping Clerk auth in Fastify

`fastify.register(clerkPlugin)` at the top level applies auth state attachment to every route in that context, including `/health`, `/metrics`, and webhook endpoints that must stay public.

## The pattern

Use Fastify's encapsulation: register the plugin inside a scoped context for the routes that need auth, and leave public routes outside it.

```ts
// public: no auth
fastify.post('/api/webhooks', webhookHandler)
fastify.get('/health', async () => ({ ok: true }))

// authenticated area
fastify.register(async (instance) => {
  instance.register(clerkPlugin)
  instance.get('/protected', async (request, reply) => {
    const { isAuthenticated, userId } = getAuth(request)
    if (!isAuthenticated) {
      return reply.code(401).send({ error: 'User not authenticated' })
    }
    // ...
  })
})
```

## Why it matters

- Webhook routes must never require auth state: incoming webhooks come from Clerk's servers, not from a signed-in user, and they carry no session. Gating them breaks delivery and triggers retries.
- Health and readiness probes from your platform get no session either; auth overhead on them is pure latency.
- `getAuth()` outside a plugin-registered context has no auth state to read. If `isAuthenticated` is mysteriously always false on a route, check that the route lives inside the context where `clerkPlugin` was registered.

## Checklist

- Decide per route group: public, session-authenticated, or machine-authenticated, and register accordingly.
- Webhook verification (Svix) is its own check, separate from session auth; do not mix the two on one route.
- Keep the raw-body content-type parser (needed for webhook signature verification) on the webhook route only, not global.