Knock docs

Reading the index…

Verifier HTTP API

knock-verifier's three routes, their requests and answers, status codes, CORS and limits.

Markdown

knock-verifier listens on VERIFIER_ADDR, 127.0.0.1:8788 by default, and takes JSON. Every refusal has the same shape, whichever route gives it.

POST /challenge

A challenge for one knock: 32 random bytes, for the verifier's origin, good once, for 120 seconds. It takes no body.

Shell
curl -s -X POST http://127.0.0.1:8788/challenge
JSON
{
  "challenge": "0x6c1f…e2a9",
  "origin": "https://club.example",
  "expiresAt": 1793860320
}
FieldWhat it is
challenge32 bytes in hex. Pass it to knock() as it is.
originThe origin knocks must be for: VERIFIER_ORIGIN.
expiresAtWhen it expires, in unix seconds.

It can refuse with busy and status 429, at check 0 when the hour's limit is reached, or at check 2 when 10,000 challenges are outstanding.

POST /verify

Checks a knock, V1 to V8 in order, and if all pass, uses up what it used in one transaction.

Request
{
  "assertion": {
    "v": 1,
    "name": "wedgie.itzraj.eth",
    "challenge": "0x6c1f…e2a9",
    "origin": "https://club.example",
    "deviceNode": "0x…",
    "counter": 12,
    "mode": 1,
    "nullifier": "0x…",
    "humanRef": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "r": "0x…",
    "s": "0x…"
  },
  "action": "enter"
}
FieldWhat it is
assertionThe knock from knock(), as it is. Unknown fields are refused.
actionOptional. Lets each human do this once: 1 to 32 characters of a–z, 0–9 and -. One human, once.

The body may have no other fields.

When it passes

Status 200:

JSON
{
  "ok": true,
  "knock": {
    "name": "wedgie.itzraj.eth",
    "key": "0x02…",
    "nullifier": "0x…",
    "counter": 12,
    "origin": "https://club.example",
    "binding": { "tx": "0x…", "block": 37301500, "provedAt": 1793860000 }
  }
}
FieldWhat it is
nameThe ENS name it knocked as.
keyThe Wedgie's public key, from ENS: compressed P-256, 33 bytes in hex.
nullifierThe human's nullifier, the same on every Knock site. Key your records by it.
counterThe Wedgie's counter for this knock.
originYour origin.
binding.txThe binding's World Chain transaction, from knock.poh.
binding.blockThe World Chain block it's in.
binding.provedAtAbout when its World ID proof was made, in unix seconds. Bindings older than VERIFIER_MAX_BINDING_DAYS are refused.

When it's refused

JSON
{
  "ok": false,
  "check": 3,
  "error": "revoked",
  "why": "wedgie.itzraj.eth is revoked: its owner turned it off"
}
FieldWhat it is
checkThe check that said no, 1 to 8, or 0 for the verifier's own trouble. Every check before it passed, and none after it ran.
errorA code your code can act on. Checks and refusals lists them all.
whyThe reason in words, for people.

A refused knock uses up nothing: its challenge, the counter and the entry are all as they were.

GET /health

What the verifier checks knocks for.

JSON
{
  "binding": "0x7748d2787dcb6e6024968edc19fca13ccaba1699",
  "maxBindingDays": 365,
  "ok": true,
  "origin": "https://club.example",
  "strict": "off"
}
FieldWhat it is
bindingThe KnockBinding whose bindings count: Knock's v3, production, on World Chain, or the one VERIFIER_KNOCK_BINDING names.
maxBindingDaysVERIFIER_MAX_BINDING_DAYS.
originVERIFIER_ORIGIN.
strictoff, safe or finalized. Strict mode.

Status codes

StatusWhen
200A challenge, a knock that passed, or health.
400Check 1: the body or the knock isn't well formed.
403Checks 2 to 8 said no.
429busy: the verifier's limits. Try again later.
500internal: the verifier's own store failed.
503unavailable: a chain couldn't be read. Try again.

CORS

The verifier allows exactly one origin, VERIFIER_ORIGIN, with GET, POST and the content-type header. Browsers on any other origin can't read its answers. Requests from your server aren't affected, since CORS is between browsers and sites.

Limits

LimitValue
Body16 KiB
POST /challenge600 an hour, per process
POST /verify600 an hour, per process
Outstanding challenges10,000
A challenge's life120 seconds, single use

TypeScript types

verifier.ts
export type Challenge = { challenge: `0x${string}`; origin: string; expiresAt: number }

export type Knocked = {
  name: string
  key: `0x${string}`
  nullifier: `0x${string}`
  counter: number
  origin: string
  binding: { tx: `0x${string}`; block: number; provedAt: number }
}

export type Verdict = { ok: true; knock: Knocked } | { ok: false; check: number; error: string; why: string }