Skip to content

Two carriers, beside the spec

Two ways to carry the bytes.

In the spec a carrier is one sentence: bytes to a ward pk in, bytes or nothing out. Quo names no carrier. Two are published beside the spec so that two strangers' kits can meet, over TCP and over the web, and neither is part of Quo. An invitation's at says which of them reaches its ward, and a reply's at says it again once the ward moves. Beside them, DOMAIN.md lets a domain say a ward is its own, with one file at /.well-known/quo that no door reads.

not Quonot Quolengthkindidresta kitanother kitkind 00, an ask00kind 01, a reply01
01

Why it stands beside the spec.

This is all SPEC.md says about a carrier:

A carrier is one sentence: bytes to a ward pk in, bytes or nothing out, and nothing means not delivered.

Quo names no carrier and no port. A carrier's own document names the scheme its addresses are written in, and how they are written.

The carrier is not Quo. Yet two strangers' kits must be able to meet, so two carriers are published beside the spec. Over TCP, a frame is a length, a kind and an id; kind 00 is an ask, 01 a reply, 02 nothing; and how long a dialer waits is its own. Over the web, where a browser or a worker cannot open TCP, the post carries one ask in one HTTP request, and the held line carries the same frames as WebSocket messages.

A carrier sees a ward, a time and a length, and never what is inside a house.

Quo over TCP

This is one carrier of Quo. CARRIER-WEB.md is another. Any other carrier is a kit’s own. Every term here is the term SPEC.md defines.

The line

A listener holds a TCP address, a host and a port, and answers for the wards behind it. A dialer opens a connection to that address and asks on it. The dialer asks and the listener answers.

A connection carries many asks at once. Either side closes it at any moment.

Quo names no port. An address is whatever reaches the listener.

In an invitation’s at, this carrier’s scheme is tcp, and an address is written tcp://host:port. The host is a name, an IPv4 address, or an IPv6 address in brackets, as RFC 3986 writes them. A name is looked up as it is written. The port is decimal, from 1 to 65535. Nothing follows the port: an address with a path, a lone / included, a query, a fragment or user information is not one of this carrier’s.

The frames are the same bytes on any reliable stream that reaches the listener, plain TCP, TCP inside TLS, or a tunnel.

The line does not greet. The first bytes a dialer sends are its first frame.

The frame

frame   = length (4, big-endian) || body
body    = kind (1) || id (4, big-endian) || rest

length counts the bytes of body.

There are three kinds and no fourth. rest follows the kind.

00  ask       rest = ward pk (64) || box      dialer to listener
01  reply     rest = box                      listener to dialer
02  nothing   rest = empty                    listener to dialer

The ward pk in an ask is sixty-four raw bytes, the signing pk followed by the padlock, the key SPEC.md writes as 128 hex. The listener routes by it. It is never read from the box.

The box is the box of SPEC.md, an ask’s box on 00 and a reply’s box on 01.

The dialer chooses the id. The listener copies it onto the reply or the nothing that answers that ask. A dialer that reuses an id among its asks in flight does not tell their replies apart. Replies arrive in any order.

What is not a frame

The largest body is 1,048,645 bytes: one kind byte, four id bytes, sixty-four pk bytes, and a box of 1,048,576.

A length above 1,048,645 is not a frame. A length below five is not a frame. A kind that is none of the three is not a frame. An ask with fewer than sixty-four bytes after its id is not a frame. A nothing with bytes after its id is not a frame.

Nothing after one of these is read on that stream. The side that reads one closes the connection.

A frame of a kind a side does not read is a frame: a reply or a nothing at a listener, an ask at a dialer. It is read, nothing is said to it, and the connection stands.

An ask’s box is length - 69 bytes. A body of 1,048,645 carries a box of 1,048,576. A reply’s box is length - 5 bytes. A reply’s box above the size is a frame, and it reads as silence.

Nothing

02 means the ask was not delivered. The listener answers for no ward under that pk, or did not hand those bytes on.

After the bytes reach a door that answers, the answer is a reply and never 02. When a door gives nothing, the listener answers 02 or writes no frame for that id.

A listener that does not carry an ask answers 02, and the ask may be sent again.

A connection that closes with asks in flight answers none of them.

An ask whose frame never left was not delivered. An ask whose frame left may have been heard. How long a dialer waits for its answer is the dialer’s own.

The empty ask

The empty ask is framed as 00, like every other ask. Its reply comes back as 01.

Quo over the web

This is one carrier of Quo, in two forms: the post and the held line. Any other carrier is a kit’s own. Every term here is the term SPEC.md defines, and a frame is the frame of CARRIER-TCP.md.

A listener answers for the wards behind it. A dialer asks, and the listener answers.

The addresses

In an invitation’s at, this carrier writes four schemes.

  • https and http name the post. The address is the URL the dialer posts to.
  • wss and ws name the held line. The address is the URL the dialer opens.

An address is a URI of RFC 3986 with a host, and any port, path and query it needs. An address with user information or a fragment is not one of this carrier’s.

https and wss carry the bytes inside TLS. http and ws carry them plain. The bytes of Quo are the same in both.

The post

The post is one ask in one HTTP request, and its reply in the response.

The dialer sends a POST to the address. Its body is the ward pk, sixty-four raw bytes, followed by the ask’s box. The listener routes by that ward pk. It is never read from the box. The request’s Content-Type is not read.

A response of status 200 with a body that is not empty is a reply, and its body is the reply’s box. A reply’s box above the size reads as silence. Every other response is nothing.

A listener that gives nothing answers status 204 with an empty body. Two listeners answer any status other than 200: one that does not carry an ask, and one that answers for no ward under that pk. After either, the ask may be sent again. A listener never answers status 200 with an empty body.

A request that is not a POST, or whose body is shorter than sixty-five bytes or longer than 1,048,640, is not an ask. The listener answers it with a status other than 200 and 204.

A listener that answers pages of other origins answers them as the CORS protocol of the Fetch standard says. Which origins it answers is its own.

The held line

The held line is a WebSocket of RFC 6455, opened to the address. It carries many asks at once, and either side closes it at any moment.

The dialer offers the subprotocol quo, and the listener selects it. A listener refuses a handshake that does not offer it, or completes it and answers no frame on that connection.

Each message is binary and carries one frame body: kind, id and rest, as CARRIER-TCP.md writes them, with no length in front. The message’s length is the body’s length. The three kinds, their rest, their directions and the id are those of CARRIER-TCP.md.

A binary message longer than 1,048,645 bytes or shorter than five is not a frame, and so is a text message. The rest of what CARRIER-TCP.md says is not a frame holds as written. The side that reads one closes the connection.

A frame of a kind a side does not read is read, nothing is said to it, and the connection stands.

Nothing on the web

Nothing means the ask was not delivered, as CARRIER-TCP.md says, on the post and on the held line alike. After the bytes reach a door that answers, the answer is a reply and never nothing.

A post whose response never came may have been heard. How long a dialer waits is its own.

What a reply shows

A reply that comes back over https or wss, from a host whose certificate the dialer checked, shows that the ward answers at that host. The reply is signed by the ward’s signing key over the lid of the dialer’s own ask, so no other ward wrote it. Every reply carries that signature, a stranger’s silence included.

It shows that the host carries asks to the ward. It does not show that the host names the ward its own, since a listener may carry asks to any ward. DOMAIN.md says how a domain names a ward its own.

A domain vouches for a ward

This document says how a domain name vouches for a ward. It is published beside SPEC.md and is not part of it. A door reads none of it, and no relation depends on it. Every term here is the term SPEC.md defines.

A ward’s name is its ward pk, and nothing in the bytes of Quo ties it to a domain. A person handed an invitation may still want to know whose ward it is. This document lets the holder of a domain answer: this ward is ours.

The file

A domain vouches at one URL: https://, the domain, then /.well-known/quo. The domain is a host name as RFC 3986 writes it, read without regard to case. No port and no other path is written.

The reader sends a GET to that URL over TLS, and checks the server’s certificate for that domain. A response of status 200 carries the file in its body. Every other response vouches for nothing. A redirect is not followed, so the file always comes from the domain it vouches for. The response’s Content-Type is not read.

The body is a value, as the chapter on values of SPEC.md reads one. A body above 1,048,576 bytes vouches for nothing.

The shape

vouch = { wards }

wards is an array. Each item that is a ward pk, 128 lowercase hex, is vouched for by the domain. An item of any other kind or spelling is skipped. A field beside wards is ignored.

A body that is no value, or is not one object, vouches for nothing. So does an object with two keys of one name among its own keys, compared as SPEC.md compares a payload’s. So does an object whose wards is absent or not an array.

What a vouch says

A ward that the file of a domain lists is vouched for by that domain. The holder of the domain names the ward as its own.

A vouch is for the one host it was read from. The file of example.com vouches for nothing at shop.example.com, and the reverse holds too. Each host that vouches serves its own file.

A vouch says nothing of where the ward is reached. A ward vouched for by example.com may be reached at any address, on any carrier. An invitation’s at still says where.

A vouch says nothing of any relation and nothing of what stands behind a door. A ward the file does not list is not vouched for by that domain, whatever it listed before, and every relation on it stands as it stood.

Which domain a kit checks for a ward, when it reads the file, and how it shows what it read are the kit’s, as question 28 of KIT-SPEC.md asks.