Knock docs

Reading the index…

Checks and refusals

The eight checks a verifier runs, in order, and every refusal each can give, with its status code and what to tell the person knocking.

Markdown

A verifier runs eight checks, in order, and the first one that fails gives the answer. Every check before it passed, and none after it ran. A refused knock uses up nothing: its challenge, the Wedgie's counter and the entry stay as they were.

Each refusal has a check, an error code for your code, and a why for people. The whys below are exact, with the parts that vary in angle brackets.

Check 1: well formed

The knock is whole and well formed, before anything is read from a chain.

errorwhyMeans
bad_assertionnot a knock: <details>Missing or extra fields, or wrong types.
bad_assertionversion <v>: this verifier reads 1A format this verifier doesn't read.
bad_assertionname: a-z, 0-9 and "-", with dots betweenNot a name a Wedgie can have.
bad_assertiondeviceNode isn't the name's namehashThe name and its node don't match.
bad_assertionorigin: https://host[:port], or http://localhostNot an origin a Wedgie knocks for.
bad_assertioncounter: from 1 upA counter of 0, or too large.
bad_assertionmode: 1 (Bound) or 2 (Fresh)No such mode.
bad_assertionsignature: r and s, s lowA zero, or a high s: every Wedgie signs with a low s.
bad_assertionthe body is {assertion, action?}knock-verifier only: the request body isn't that, or is over 16 KiB.
bad_assertionaction: a-z, 0-9 and "-", up to 32knock-verifier only: not an action name.

Status 400. These come from your code, or from someone sending made-up knocks, never from a Wedgie and the Door. Log them, and show "Something went wrong. Try again."

Check 2: for this site, just now

The challenge is one this verifier gave out, unused, under 120 seconds old, and for its origin. The knock is for its origin too.

errorwhyMeans
bad_challengenot a challenge this verifier gave outMade up, from another verifier, or the verifier's database was replaced.
bad_challengethat challenge was used alreadyA knock with this challenge already got in.
bad_challengethat challenge expired: knock againMore than 2 minutes passed between the challenge and the check.
bad_challengethat challenge was used or expired meanwhileTwo checks of the same knock at once, and the other one won.
wrong_originthe knock is for <origin>, not <ours>The page that knocked isn't at VERIFIER_ORIGIN.
wrong_origina challenge for another originThe challenge came from a verifier for another origin, sharing this database.
busytoo many challenges out at once: try again in a minuteFrom POST /challenge: 10,000 challenges are outstanding. Status 429.

Status 403, except busy. Tell people to knock again. If it's wrong_origin, check VERIFIER_ORIGIN against the address bar, port included: localhost and 127.0.0.1 are different origins.

Check 3: named, and switched on

The name has Knock's records in ENS on mainnet, with knock.status set to active, and they're the same 3 blocks deep as at the latest block. A revoke counts at the latest block.

errorwhyMeans
no_name<name> has no Knock records in ENS: never named, or killedNo records, or the name expired.
revoked<name> is revoked: its owner turned it offknock.status is revoked.
revokedthis Wedgie's key was seen revoked, and stays revokedThis verifier saw the key revoked before. Turning it back on doesn't help here.
not_knock<name> isn't a Knock name (knock.v isn't 1)The name has records, but not Knock's.
records_changing<name>'s Knock records changed in the last few blocks: try again in a minuteThe name was set up or changed less than about 36 seconds ago.
not_active<name>'s knock.status isn't activeAny status but active or revoked.
bad_records<name>'s knock.key isn't a compressed P-256 key
bad_records<name>'s knock.nullifier isn't a wordNot 0x and 64 hex digits.
bad_records<name>'s knock.poh isn't eip155:480:tx:<hash>
unavailablecouldn't read the chain, try again: mainnet: <error>The mainnet RPC failed. Status 503.

Status 403, except unavailable. Show the why: it names the name, and says what its owner can do.

Check 4: signed by its Wedgie

The Wedgie whose key is in knock.key signed this knock, for this verifier's origin, this challenge and this name. The verifier makes the digest from its own origin and the knock's fields, so a knock signed for another site fails here even if its origin field was changed.

errorwhyMeans
bad_signaturenot signed by <name>'s key, for this site and challengeMade up, changed on the way, or signed by another key.

Status 403. A real Wedgie and the Door never give this, since the Door checks the signature before it sends a knock. Treat it as an attempt, and show "This knock didn't come from that Wedgie."

Check 5: not seen before

The Wedgie's counter is higher than in any knock of this key's that this verifier has let in.

errorwhyMeans
replayedthe Wedgie's counter didn't rise: a knock seen beforeA knock sent again, or an older one.

Status 403. Tell people to knock again: a new knock has a higher counter.

Check 6: one human, bound

The knock's human is the name's, and the binding knock.poh names is a successful transaction on KnockBinding v3, production, with a Bound log for this key, this human and this name. Its World ID proof is no older than VERIFIER_MAX_BINDING_DAYS, and for a strict verifier, its block is final enough.

errorwhyMeans
bad_assertionhumanRef: zero in mode 1A mode 1 knock with a humanRef that isn't zero.
wrong_humanthe knock's human isn't <name>'sThe knock's nullifier isn't the one in knock.nullifier.
no_bindingWorld Chain has no transaction <tx>knock.poh names a transaction World Chain doesn't have.
no_bindingthe binding's transaction <tx> failedIt reverted.
no_bindingtransaction <tx> holds no Bound from Knock's KnockBinding for this key, human and nameAnother contract, an older KnockBinding, staging's, or a binding for another key, human or name.
binding_not_finalthis site is strict: it takes a binding once World Chain has posted it to Ethereum, which it does every ~6 minutes. Try again in a few minutesStrict, safe: the binding is newer than World Chain's safe head.
binding_not_finalthis site is strict: it takes a binding once World Chain's post of it to Ethereum is final, ~20–25 minutes after binding. Try again laterStrict, finalized.
binding_expiredthe binding is more than <days> days old: bind the Wedgie againIts World ID proof is older than the verifier allows.
unavailablecouldn't read the chain, try again: World Chain: <error>The World Chain RPC failed. Status 503.

Status 403, except unavailable. For binding_not_final and binding_expired, show the why. For the others, the Wedgie's owner should set it up again in the Knock Console.

Check 7: a mode it offers

errorwhyMeans
mode_unsupportedFresh (mode 2) isn't offered yetA mode 2 knock.

Status 403. Check 7 runs just before check 6, so a mode 2 knock gets this answer whatever its binding.

Check 8: first time

Only when the request names an action: this human hasn't done it here before.

errorwhyMeans
already_enteredthis human has done <action> here alreadyThe human's nullifier is in this action's entries, from any of their Wedgies.

Status 403. Tell them they've already done it. One human, once covers actions.

The verifier's own trouble

checkerrorwhyStatus
0busythe verifier's limit for now: try again later429
0internalthe verifier's store: <error>500

busy comes from the verifier's hourly limits, 600 challenges and 600 checks per process. internal means its SQLite store failed. Neither says anything about the knock.

Every code

errorChecksStatus
bad_assertion1, 6400 at check 1, 403 at check 6
bad_challenge2403
wrong_origin2403
busy0, 2429
no_name3403
revoked3403
not_knock3403
records_changing3403
not_active3403
bad_records3403
bad_signature4403
replayed5403
wrong_human6403
no_binding6403
binding_not_final6403
binding_expired6403
mode_unsupported7403
already_entered8403
unavailable3, 6503
internal0500