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
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
interface X511Config {
domain: string
basePath?: string
dev?: boolean
routes?: X511Routes
disclosures: ResolvedDisclosures
mode: X511Mode
pendingTtl?: number
providers: Provider[]
sessionAdapter?: SessionAdapter
}| Field | Type | Required | Default |
|---|---|---|---|
domain | string | yes | — |
disclosures | ResolvedDisclosures | yes | — |
mode | X511Mode | yes | — |
providers | Provider[] | yes | — |
basePath | string | no | '/' |
dev | boolean | no | false |
routes | X511Routes | no | { sse: '/sse', claim: '/claim' } |
pendingTtl | number (seconds) | no | 600 |
sessionAdapter | SessionAdapter | no | new MemorySessionAdapter() |
See Getting started for descriptions of each field.
Validation
x511() validates the configuration synchronously and throws on:
- An empty
providersarray (At least one provider is required …). - A missing or malformed
mode. - A
mode.ttlthat is not anumberwhenmode.type === 'session'. - A
disclosures.nationalityordisclosures.issuingStatefilter that specifies bothincludedandexcluded, or neither. - A route collision where a provider's
routelands on the same${basePath}${route}asroutes.sse,routes.claim, or another provider.
Returned middleware
verify
Handles all internal X511 routes:
POST ${basePath}${provider.route}→ routes to the matching provider'sverifyhandler.GET ${basePath}${routes.sse}→ SSE endpoint that streams the access token to the verification page.POST ${basePath}${routes.claim}→ exchanges an access token for thex511claim cookie.
The shape of verify differs per adapter:
| Adapter | Shape |
|---|---|
| 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.
| Adapter | Shape | Identity 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 exchangeProvider routes default to /verify/self and /verify/zk and can be overridden by passing route to the provider factory.
Cookie and TTL behaviour
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:
| Mode | Cookie Max-Age | Claim TTL in adapter |
|---|---|---|
{ type: 'session', ttl } | ttl | ttl |
{ type: 'one-shot' } | 30 | 60 |
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 forone-shotmode.ONE_SHOT_COOKIE_MAX_AGE = 30— cookie lifetime inone-shotmode.DEFAULT_PENDING_TTL = 600— default value ofpendingTtl(overridable via config).