Skip to content

x511()

The factory that wires up the verification gateway. Each framework adapter (x511/hono, x511/elysia, x511/next, x511/adonis) re-exports its own x511() that delegates to the same core implementation; they differ only in the shape of verify / verified.

Signature

ts
function x511(config: X511Config): {
  verify:   /* framework-specific middleware */
  verified: /* framework-specific middleware */
  config:   ResolvedConfig
}

The config field on the returned object is the ResolvedConfig — the input config with all defaults filled in. It is exposed mainly for introspection.

X511Config

ts
interface X511Config {
  domain: string
  basePath?: string
  dev?: boolean
  routes?: X511Routes
  disclosures: ResolvedDisclosures
  mode: X511Mode
  pendingTtl?: number
  providers: Provider[]
  sessionAdapter?: SessionAdapter
}
FieldTypeRequiredDefault
domainstringyes
disclosuresResolvedDisclosuresyes
modeX511Modeyes
providersProvider[]yes
basePathstringno'/'
devbooleannofalse
routesX511Routesno{ sse: '/sse', claim: '/claim' }
pendingTtlnumber (seconds)no600
sessionAdapterSessionAdapternonew MemorySessionAdapter()

See Getting started for descriptions of each field.

Validation

x511() validates the configuration synchronously and throws on:

  • An empty providers array (At least one provider is required …).
  • A missing or malformed mode.
  • A mode.ttl that is not a number when mode.type === 'session'.
  • A disclosures.nationality or disclosures.issuingState filter that specifies both included and excluded, or neither.
  • A route collision where a provider's route lands on the same ${basePath}${route} as routes.sse, routes.claim, or another provider.

Returned middleware

verify

Handles all internal X511 routes:

  • POST ${basePath}${provider.route} → routes to the matching provider's verify handler.
  • GET ${basePath}${routes.sse} → SSE endpoint that streams the access token to the verification page.
  • POST ${basePath}${routes.claim} → exchanges an access token for the x511 claim cookie.

The shape of verify differs per adapter:

AdapterShape
Hono(c, next) => Promise<Response | void>
Elysia(ctx) => Promise<Response | undefined>
Next.js(request: Request) => Promise<Response> (returns 404 if no route matches)
Adonis(ctx, next) => Promise<void>

For Hono / Elysia / Adonis the middleware delegates to the framework's next when no internal route matches. The Next.js adapter is mounted on a catch-all route (app/<basePath>/[...path]/route.ts) and therefore responds with 404 for any unmatched path inside basePath.

verified

The gate. If a valid x511 cookie is present, the request continues with { uniqueId?: string } attached to the framework context. Otherwise the response is 511 Network Authentication Required carrying the verification HTML page.

AdapterShapeIdentity available at
Hono(c, next) => Promise<Response | void>c.get('x511')
Elysia(ctx) => Promise<string | undefined> (HTML body when unverified)ctx.store.x511
Next.js(handler: VerifiedHandler) => (request) => Promise<Response>second argument of handler
Adonis(ctx, next) => Promise<void>ctx.x511

Internal layout

For a configuration with basePath: '/x511' and the default routes, the factory claims:

POST /x511/verify/self      ← self provider
POST /x511/verify/zk        ← zkpassport provider
POST /x511/<custom>         ← every other provider's route
GET  /x511/sse              ← Server-Sent Events token feed
POST /x511/claim            ← access-token-for-cookie exchange

Provider routes default to /verify/self and /verify/zk and can be overridden by passing route to the provider factory.

The x511 cookie value is ${claimId}.${claimToken}. Both halves are random UUIDs. The cookie attributes are:

HttpOnly; SameSite=Strict; Path=/; Max-Age=<ttl>; [Secure]

Secure is omitted when dev: true, otherwise included. The Max-Age and the underlying claim TTL come from mode:

ModeCookie Max-AgeClaim TTL in adapter
{ type: 'session', ttl }ttlttl
{ type: 'one-shot' }3060

In one-shot mode, the very first verified request consumes the claim and the response carries a Set-Cookie that immediately expires (Max-Age=0). The handler has already run by the time the cookie is cleared, so the request is processed normally.

The constants used internally (and not configurable) are:

  • VERIFIED_TOKEN_TTL = 60 — how long a permitted but unclaimed access token survives in the adapter.
  • ONE_SHOT_CLAIM_TTL = 60 — claim lifetime in the adapter for one-shot mode.
  • ONE_SHOT_COOKIE_MAX_AGE = 30 — cookie lifetime in one-shot mode.
  • DEFAULT_PENDING_TTL = 600 — default value of pendingTtl (overridable via config).