Skip to content

One command, one verdict

Check a kit against the spec, yours or anyone's.

The verifier is a checker you run on your own machine, with nobody's permission and nothing sent anywhere. It says where a kit departs from SPEC.md and nothing else, and its exit code is the verdict. Passing is necessary and never sufficient.

?the template, no houselaid over a kit
01

A method, beside Quo.

The verifier is not Quo, and it is not the way to build a kit. It says where a kit departs from SPEC.md, and nothing else.

It holds the six algorithms and SPEC.md, and no kit and no bytes any kit produced. It takes bytes apart under the spec and never compares them with stored bytes, so whatever a kit draws, a correct box passes.

A kit that answers every arrival with silence, or with nothing, departs from nothing and passes, because silence is always a door's right. The verifier reports how many arrivals it could judge, and a count is never a failure.

02

One command.

A kit under the verifier is one executable, named stand, that reads requests on its stdin and writes answers on its stdout, as HARNESS.md says. From the examples folder, after a kit's build:

node ../verifier/cli.js --cwd go -- ./stand

It prints one line per check and a summary. The exit code is the verdict: 0 when no check failed, and 1 when a check failed or it was run wrongly.

Two kits against each other, through the verifier's proxy over TCP, which delivers frames late, twice, out of order, altered and not at all:

node ../verifier/cli.js --two -- "$PWD/go/stand" -- "$PWD/rust/target/debug/stand"

A kit may also check itself without the verifier against the one file of vectors, which pins the six algorithms. A vector is a convenience and not the authority: vectors/arithmetic.json

03

What passing does not say.

Passing is necessary, and it says nothing about whether a kit is good, useful or finished.

The verifier does not see what a kit keeps and how, across a restart or across two copies of one ward, what a kit draws, or how long a kit waits.

The verifier

The verifier is a method, offered beside Quo. It is not Quo, and it is not the way to build a kit. It says where a kit departs from SPEC.md, and nothing else. It is necessary to show a kit departs from nothing, and it is never enough to show a kit is good, useful or finished.

A kit that answers every arrival with silence, or with nothing, departs from nothing and passes. The verifier reports how many arrivals it could judge, and a count is never a failure.

The verifier prints one line per check and a summary. It exits 0 when no check failed, and 1 when a check failed or it was run wrongly.

What the verifier holds

The verifier holds the six algorithms and SPEC.md. It holds no kit, and no bytes any kit produced.

It reaches a kit through vectors/HARNESS.md. Requests go over the kit’s stdin and stdout, with boxes as hex. Where the kit carries bytes, it reaches it through the frames of CARRIER-TCP.md and the post and held line of CARRIER-WEB.md.

It holds that seam as well as Quo. It drives every error HARNESS.md names, and holds the kit to that vocabulary and to the order in which two errors are answered. It holds a kit to exiting 0 when its stdin closes, and to writing nothing on its stdout but the answers HARNESS.md names.

What the verifier refuses

The verifier refuses a kit only where SPEC.md fixes the bytes. It accepts every answer to every question KIT-SPEC.md asks.

  • On a count number below the highest a door has honoured, it accepts an object, silence and repeated, and holds that none of them moves a key.
  • On a delivered ask, it accepts a reply and nothing.
  • On seen, it accepts any string and null, except where a reach of the harness fixes it.
  • On at in a reply, it accepts any array of addresses a door writes, and none, except where a reach of the harness fixes it. As a reader, a kit may keep any of the addresses in their order, or none.
  • On the empty ask, it accepts silence and every describe, whatever its entries and its lang. It reads nothing inside description, an entry’s args, or a field SPEC.md does not name.
  • A reply text other than silence it parses, and never compares byte for byte.
  • The bytes a kit draws are the kit’s. The verifier reads none of them except through what SPEC.md makes visible.

Checking, not comparing

The verifier takes bytes apart under SPEC.md. A check that holds for every correct box holds for a kit whatever it draws.

The sixteen bytes of silence are the one thing it compares, because SPEC.md fixes them. Nothing else a kit wrote is held against stored bytes.

The kit as a door

The verifier stands a ward from a seed it chooses, and derives from that seed the signing key and the padlock. It holds the padlock’s secret.

It checks the ward pk against the seed. It asks for an invitation. It checks that the heir pk is the public key of the heir secret the invitation carries, and that the lock is an ML-KEM-768 encapsulation key. When the invitation carries at, it checks that at is an array of addresses. It checks that each address of a scheme a published carrier names is written as that carrier writes it. It hands the kit invitations whose at holds a scheme no carrier names, or is not an array, and they are still invitations.

It writes every ask itself, keeps each ask’s lid secret, delivers the box with arrive, and opens what comes back:

  • the reply’s length is its reply text plus one hundred and twelve;
  • the reply opens under quo-seal from the lid;
  • the signature is the ward’s signing key over the lid, then the reply text;
  • the reply text is one of the three shapes, and silence is the sixteen bytes;
  • an at a door writes on an object is an array of addresses, each written as its carrier writes it;
  • an object that answers the empty ask is a describe, on a relation and on the zero head. An object of any other shape there is a departure;
  • the edge key that follows the reply is the one SPEC.md gives.

It drives each of the thirteen cases of SPEC.md by writing the arrival that provokes it. Every stranger’s reply has one reply text and one length.

It drives the signature check’s whole failure list. An s at or above the group order. An R that does not decode, and an R that is not canonical. A public key that does not decode, and one whose y is at or above the prime. A small-order public key in each spelling.

It drives the two signatures that list does not hold: a small-order R, and a public key with a torsion component that is not small-order. It holds that each is answered as an admitted key is.

It drives both tables of the move, and follows the door’s keys as they say.

It delivers two arrivals on one relation at one moment, without waiting for the first answer. Of two knocks on one fresh heir, one binds and the other is a stranger’s. One ask delivered twice is honoured once, and the other hears repeated. A kit that judges one arrival at a time passes both.

The kit as an asker

The verifier stands its own ward, mints an invitation, and hands it to the kit with ask. It takes apart the box the kit sealed, as the kit’s door would:

  • on a knock, the ciphertext decapsulates under the verifier’s lock, and the box is its payload plus 1,248;
  • otherwise the box is its payload plus one hundred and sixty;
  • the head opens under quo-seal to the heir pk;
  • the body opens under quo-edge-seal over the agreement and the edge key SPEC.md gives for that ask;
  • the payload is well formed, and signed by by over the bytes as sent;
  • by is the key the relation admits, and next is read as SPEC.md reads it.

The key and edge key for an ask are those the standing moved to. After a reply that moved nothing, they may instead be the key and edge key the last move was asked under, which a door may still hold.

The verifier answers with a reply it writes, delivers it with read, and checks what the kit says it read. It answers with objects, silence, each of the three words, nothing, and replies that read as silence. Among those are a reply and a word the ward signed over another ask’s lid, and a reply signed over its reply text alone. Objects that are no describe come back to the empty ask, and the kit reads each as an object and moves on it. Objects carry at as an array of addresses, as an array holding entries a reader skips, as a string and as null, and the kit reads each as an object. An at beside silence or a word, and at twice, read as silence.

It holds the number of every ask the kit sealed, and with them the highest the standing has moved on. It then checks, by the kit’s next ask, that the kit moved its signing key and its edge key when an object came back to an ask above that highest. It checks that it moved neither when an object came back to an ask at or below it. It runs two relations at once, answering each ask after the other’s, and holds the kit to keeping them apart.

A kit that answers ask with bad request is judged as a door alone.

The kit as a carrier

A kit that answers listen, route and send of part two of vectors/HARNESS.md is judged on CARRIER-TCP.md and CARRIER-WEB.md as well. The verifier asks for each scheme the harness names, tcp, http and ws, one at a time. A scheme a kit answers with bad request is one it does not stand, and the kit is judged without it. A kit that stands none is judged without a carrier.

Every address a kit answers is taken apart as its carrier writes it. A tcp address is tcp://host:port. An http or ws address is a URI with a host, and with no user information and no fragment. A second listen of one scheme answers the same address, and listen with no scheme is listen of tcp. listen of https or wss is bad request. A route to an address its scheme’s carrier does not write, of a scheme the kit stands, is bad request.

As a tcp listener

The verifier dials the address listen answers:

  • the kit writes nothing before a frame arrives, and may close an idle connection at any moment;
  • an ask to a ward it stands comes back one of three ways. As a reply frame with the same id, whose box is judged as at arrive. As a nothing frame. Or not at all;
  • an ask frame written one byte at a time is answered the same way, or the connection closes with it in flight and answers none;
  • an ask frame of the largest body, 1,048,645, carries a box of the size;
  • an ask frame with a ward pk and no box is a frame;
  • an ask to a ward pk it does not stand comes back as a nothing frame with the same id;
  • many asks in flight on one connection each come back once, in any order;
  • after bytes that are not a frame, the kit closes the connection, and answers no ask written after them;
  • a reply frame or a nothing frame sent to it is read, nothing is written back, and the connection stands;
  • a whole relation runs over the line, a knock and the asks after it, each judged as at arrive. Among them stand a repeated number, a spent heir’s knock, and a key the door does not admit.

As a listener of the held line

The verifier opens a WebSocket to the address, offering the subprotocol quo:

  • the kit selects quo, and a handshake that selects nothing fails;
  • the kit sends no message before a frame arrives;
  • an ask message to a ward it stands comes back one of three ways. As a reply message with the same id, whose box is judged as at arrive. As a nothing message. Or not at all;
  • an ask message of the largest body, 1,048,645 bytes, carries a box of the size, and an ask message with a ward pk and no box is a frame;
  • an ask to a ward pk it does not stand comes back as a nothing message with the same id;
  • many asks in flight on one line each come back once, in any order;
  • the kit closes the line after any of six messages, and answers no ask sent after it. A binary message longer than 1,048,645 bytes. One shorter than five. One of a kind that is none of the three. An ask with fewer than sixty-four bytes after its id. A nothing with bytes after its id. A text message;
  • a reply message or a nothing message sent to it is read, nothing is sent back, and the line stands;
  • a line that offers no subprotocol carries no frame: its handshake is refused, or an ask on it comes back as nothing at all;
  • a whole relation runs over the line, as over tcp.

As a listener of the post

The verifier sends each ask as a POST to the address, its body the ward pk and the box:

  • an ask to a ward it stands comes back as status 200 with a body, the reply’s box, judged as at arrive. Or it comes back as any other status, which is nothing;
  • status 200 with an empty body is a departure;
  • the Content-Type of the request changes nothing;
  • an ask to a ward pk it does not stand comes back with a status other than 200;
  • many posts in flight each come back;
  • five requests each come back with a status other than 200 and 204. A GET. A PUT of a whole ask. A POST of no body, one of sixty-four bytes, and one of 1,048,641 bytes;
  • a POST of 1,048,641 bytes whose connection closes before a status comes back is accepted, since a listener may answer a body too long before it reads the whole;
  • a POST of sixty-five bytes is an ask, and one of 1,048,640 carries a box of the size;
  • a whole relation runs over posts, as over tcp.

As a dialer

For each scheme the kit stands, the verifier holds a listener of its own, and routes a far ward to it with route. A web listener of the verifier’s answers at an address with a path and a query, and a request to any other path departs.

  • On tcp, the kit’s first bytes on a connection are an ask frame, and it writes only ask frames.
  • On the held line, the kit offers quo, and sends only binary ask messages.
  • On the post, each ask is a POST to the address, with a body of sixty-five to 1,048,640 bytes.
  • The ask carries the far ward’s pk, and its box is judged as at ask.
  • On tcp and the held line, an ask frame sent to the kit is read, nothing comes back, and the connection stands.
  • A reply, nothing, a closed connection and no answer at all each reach the kit, and the read it answers is checked. Nothing is a nothing frame on tcp and the held line, and any status other than 200 on the post.
  • On the post, status 500, status 200 with an empty body, and status 201 with a reply’s box each read as nothing.
  • A send to a ward with no route and no at reads nothing.
  • After a second route for one far ward, no ask reaches the first address.

The invitation’s at

For each scheme the kit dials, the verifier mints an invitation from a ward no route names. Its at is a number, then an address of a scheme no carrier names, then an address of a listener of its own with its scheme in capitals. A reader skips the first two and reads the scheme in any case.

  • A kit that reaches that listener is judged there as a dialer, over a whole relation.
  • A kit that reaches nothing and reads nothing is accepted, since KIT-SPEC.md leaves reading at to the kit. The verifier prints whether the kit reached the ward through at, scheme by scheme.
  • A kit that reaches the ward once and later reaches nothing departs, on one relation and across the schemes it dials.
  • With a route and an at naming another address, the kit dials the route alone, and no connection reaches the address in at.

Windows

An ask with no answer at all is closed at once, and a kit that waits reads the close. Two windows remain, because a kit that writes nothing can only be seen not to write. One is how long the verifier listens for bytes that must not come, QUO_VERIFIER_QUIET_MS, 500 unless the environment names another. The other is how long a frame is held to arrive late, QUO_VERIFIER_LATE_MS, 1000 unless named. A kit’s own tests may set both near zero.

Two kits

The verifier stands wards in one program and asks them from a ward in a second. It runs one pass for each scheme the first program listens on and the second dials. Two programs with no scheme in common cannot be judged together, and the run fails.

In each pass the verifier holds a listener of its own in that scheme, and forwards each ask to the first program’s listener of the same scheme. It holds the first ward’s seed and not its lock. For every ask it forwards, it checks the ask carries the first ward’s pk and that the head opens to an heir pk. It opens no body and no reply, and judges the rest by what the second program reads. It checks that what the second program reads is what the first program’s reach answers. A forwarded post the first program answers with any status other than 200 is nothing, and one it answers with status 200 and an empty body departs.

Over tcp, the second program is routed to the verifier’s listener. The verifier forwards a knock and an ask as they came. Then it delivers frames late, twice, out of order, altered and not at all, and checks that each program answers as SPEC.md says. An ask delivered twice carries a number the door has honoured. A knock delivered twice is on a spent heir. An altered box is a stranger’s. A frame not delivered is nothing.

Over the post and the held line, the second program is routed to the verifier’s listener, and a knock and an ask are forwarded as they came.

In every pass, the second program then holds an invitation from a ward no route names, and whose at the verifier writes with its listener’s address alone. A second program that reaches the listener is judged over a knock and an ask as they came. One that reaches nothing and reads nothing is accepted, and the verifier prints whether it reached the first program through at.

Vectors

A vector is one precomputed case a kit checks itself against without running the verifier. A vector is a convenience and is not the authority.

vectors/arithmetic.json is the one file of vectors. It pins the six algorithms, each record taken from the standard that publishes it or built on one and saying how. The verifier’s own tests replay every record.

What no check derives

The six HKDF labels, quo-seal, quo-edge-seal, quo-lock, quo-edge, quo-ward-sign and quo-ward-seal, are the ASCII bytes of those names. Every HKDF-SHA-256 call takes the zero-length salt of RFC 5869. The fixed sizes stand as SPEC.md writes them. These stand on the text alone.

What the verifier does not see

Whatever a kit keeps, and how it keeps it, across a restart or across two copies of one ward. What a kit draws. How long a kit waits. How many arrivals a door judges at once, since a kit that judges one at a time answers two sent together as it answers two sent apart.

A domain’s vouch of DOMAIN.md. It is read for a person, and no door reads it, so no arrival shows whether a kit read one.

HARNESS

This is test tooling beside the protocol, offered as a method. It is not Quo, and it is not the way to build a kit. A kit that answers none of it still speaks Quo. Every term SPEC.md defines is used as SPEC.md defines it. Every other term is defined where it is introduced. Nothing here adds a byte to a box or a field to a payload.

Part one is what a kit answers to be judged without a carrier. Part two is what a kit answers to be judged with its bytes carried, to the verifier or to another kit.

Part one: judged

A kit under the verifier is one executable, named stand. It reads requests on its stdin and writes answers on its stdout. In part one no listener is opened and no socket is used. Boxes cross this channel as hex.

The verifier judges a kit twice. As a door, through arrive. As an asker, through ask and read. A kit that has no asking side answers ask with bad request, and is judged as a door alone.

The channel

Both directions are UTF-8. One JSON value is one line. A line ends with \n, and \r is not part of a line. No value carries a newline inside it. Answers come in any order, and id says which request each answers.

request = { id, op, ... }
answer  = { id, ... } | { id, error: <string> }

id is minted by the verifier, a string, unique among the requests in flight. op is a string, one of the requests this document names.

An error is one of these strings and no other:

no such op     op is none this document names
bad request    a line that is no JSON object, a field missing, null or of the
               wrong type, hex that is not lowercase hex of the right length,
               an invitation that is not one, or a request the kit does not
               serve
ward stood     a ward with that ward pk already stands
no such ward   ward names no ward this program stands
name held      heir names an heir this ward already holds
not reached    reach names nothing the kit provides

A line with no id, or an id that is not a string, is answered bad request with id set to null. An empty line is skipped. A channel line is plain JSON, and the value rules of SPEC.md do not apply to it.

Where two errors apply, the first in this order is the answer: bad request, no such op, no such ward, not reached, ward stood, name held.

stand runs until its stdin closes, and then exits. Exit 0 is a run that read its stdin to the end. Exit 1 is a run that could not start.

Nothing else is written on stdout. What a kit writes for its own eyes goes on stderr.

The six requests

{ id, op: "ward",    seed, reach? }               { id, ward: <128 hex> }
{ id, op: "invite",  ward, heir, reach? }         { id, invitation }
{ id, op: "release", ward, heir }                 { id, released: <string> | null }
{ id, op: "arrive",  ward, box }                  { id, reply: <hex> | null }
{ id, op: "ask",     ward, invitation, method?, args? }
                                                  { id, box: <hex> }
{ id, op: "read",    ward, invitation, reply }    { id, read }

ward stands a ward from seed. seed is text, taken as SPEC.md takes a seed that is not thirty-two bytes. It is hashed whatever its length. The answer is that ward’s ward pk.

In every other request, ward is the 128 hex ward pk of a ward this program stands.

invite holds a new heir of that ward under the name heir, a string the verifier chooses, and answers the invitation of SPEC.md. A released name may be invited again, and names a new heir.

release makes that ward stop holding the heir named heir. It answers that name, or null where the ward holds no heir of that name.

arrive delivers box, an ask’s box as hex, to that ward’s door. The answer is the reply’s box as hex, or null where the door gives back nothing.

ask makes that ward ask, as a standing, on the relation invitation names. The first ask on an invitation is a knock. method absent is the empty ask. args absent is the empty object. The answer is the box the kit sealed, as hex. The kit chooses every key it announces and every count number it sends. After a knock that brought no object back, a kit may answer ask on that invitation with the knock it sent before. It answers with the same bytes, whatever method and args ask.

read hands that ward reply, the reply’s box as hex, or null for nothing, as the answer to its last ask on that invitation. The answer read is what the kit read:

{ object, seen, at? }  an object came back
{ silence: true }      silence came back
{ quo: word }          a word came back
{ nothing: true }      nothing came back

object is the value the kit read, and seen the seen it read. at is present only for a kit that keeps addresses from a reply’s at. It holds those it kept, in their order, and is absent where the reply carried no array.

What answers behind a door

reach names what answers behind that door. How a kit provides it is the kit’s own. The verifier sends these five and no other:

echo     a named ask: the object is the bytes of `args` exactly as they
         arrived, or silence where the kit does not read that `args`.
         the empty ask: the object is the describe {"asks":[]}.
         seen is null, and no at is written.
marked   as echo, and on every ask seen is the string "1", the empty
         ask included.
moved    as echo, and every object carries at, the array
         ["tcp://127.0.0.1:9"].
null     a named ask: the object is null. seen is null.
         the empty ask: as echo.
silent   every ask: silence.

A reach that is none of the five, and that the kit does not provide, is answered not reached.

reach on ward names what answers the zero head at that ward. With no reach on ward, nothing answers the zero head there, and an ask on the zero head is case 4 of SPEC.md. reach on invite names what answers asks on that heir. With no reach on invite, it is echo.

Part two: carried

Part two is part one, and three requests more, for a kit that carries its bytes over CARRIER-TCP.md or CARRIER-WEB.md. A kit that carries no bytes answers each of the three with bad request, and is judged by part one alone.

An address here is an address of SPEC.md, written as the carrier its scheme names writes it. A scheme is tcp, http or ws. A kit stands a scheme when it carries bytes in that scheme’s carrier. http and ws stand for https and wss as well, since the harness runs no TLS. listen of https or wss is bad request. Whether route takes https and wss is the kit’s.

The three requests

{ id, op: "listen", scheme? }                      { id, at: <address> }
{ id, op: "route",  far, at }                      { id, routed: <128 hex> }
{ id, op: "send",   ward, invitation, method?, args? }
                                                   { id, read }

listen makes the program hold one listener of scheme, tcp when absent, on a loopback address it chooses, and answer on it for every ward it stands. The answer is that listener’s address. A second listen of one scheme answers the same address. A scheme the kit does not stand is bad request.

invite, once the program holds a listener, may write that listener’s address, or any of its listeners’ addresses, in the invitation’s at.

route tells the program that the ward whose 128 hex ward pk is far is reached at at, one address. A second route for one far replaces the first. An at that is not an address, as the carrier of its scheme writes one, of a scheme the kit stands is bad request. far names a ward anywhere, and is not held to no such ward.

send makes that ward ask, as a standing, on the relation invitation names, and carry the box to the ward the invitation names. Where that ward has a route, the kit dials the route alone. Where it has none, a kit that reads at dials the addresses it holds for that ward, as it tries them. It holds them from the invitation’s at or a reply’s. A kit that does not read at delivers nothing. With neither, the ask is not delivered. The first send on an invitation is a knock. The answer read is what the kit read, as in part one. How long the kit waits is its own, and { nothing: true } is the answer when nothing came back.

Between two kits

The verifier stands a ward in one program and asks from a ward in another. It holds no kit. It routes the asking program’s far ward to an address of its own, or hands it an invitation whose at names that address. It forwards the frames to the answering program’s listener. Standing between them, it may deliver a frame as it came, late, twice, out of order, altered, or not at all. No kit implements any of that.

What the harness does not reach

How a kit keeps what it keeps, across a restart or across two copies of one ward, is the kit’s own, as KIT-SPEC.md asks. The harness names no partition, no restart and no copy. A door that forgets its relations departs from nothing.