Skip to content

Auth and scope

Callspec keeps credentials out of the RPC contract. The client sends Authorization: Bearer …; that token is never part of the route’s input pred or generated client types. You verify tokens however you already do — JWT, session lookup, API keys — in one authenticate(token, req) hook on the spec. Whatever your hook returns becomes handler ctx; routes that allow anonymous callers normally see ctx: undefined.

Per route, choose whether a valid token is required: auth: 'bearer' (default) or auth: 'none'. Callspec runs the gate before the handler — missing or invalid credentials → 401 UNAUTHORIZED, and your handler never runs. If any route uses 'bearer', spec throws at load time when authenticate callback is missing.

  • auth: 'none' — no credentials required; ctx is normally undefined unless set by authenticate
  • auth: 'bearer' (default) — missing or invalid token → 401 before the handler runs

OpenAPI Bearer security is auto-derived from route auth.

Scope controls whether a route is published in specs (and by extension, docs and generated clients). Scope allows you to have “undocumented” routes. These routes are mounted and available either way, and have nothing to do with auth.

  • scope: 'public' (default) — on the public contract (callspec.json, OpenAPI, docs UI, SDK codegen, MCP tools/list)
  • scope: 'private' — documented when this mount uses visibility: 'all'. Does not change the auth gate.

visibility on mountSpec / emitCallspec / emitOpenApi (default 'public'):

  • 'public' — public-scope routes only (prod)
  • 'all' — public and private routes on the same /docs and JSON (dev/stage)
typescript
mountSpec(router, api, {
visibility: process.env.NODE_ENV === 'production' ? 'public' : 'all',
});

Callspec does not read NODE_ENV itself. There is no npx callspec --scope flag — the CLI reads whatever callspec.json the server already served. Point it at a mount that used visibility: 'all' if you want private methods in the client.

mountSpec · Next: Surfaces & exports