The Edsu Protocol, DRAFT Version 0.1, Rev. B 2018-11-04

1 ESON

The ESON format underlies much of Edsu. It doesn't stand for anything: it's a pun on JSON, with Edsu's E substituted. It was created to be simpler to emit and parse than JSON, but still be able to express the minimum amount of structure required by the higher-level protocols in Edsu that use it.

1.1 Format

An ESON document is composed of zero or more item lines, followed by an empty line. Each item line is composed of four parts:
┏━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━━━━━┓ ┃ key ┃ space ┃ value ┃ line feed ┃ ┗━━━━━┻━━━━━━━┻━━━━━━━┻━━━━━━━━━━━┛
The key is one of either: A) A caret: ^ (i.e. the ASCII value 0x5E), or B) One or more characters from the set: abcdefghijklmnopqrstuvwxyz0123456789:- In the case of B, keys cannot begin or end with a colon, nor can two or more colons be directly beside each other. The space is the ASCII value 0x20. The value is zero or more characters from the set of 0x20 to 0x7e (inclusive) in ASCII, i.e. all non-control-code characters from 7-bit ASCII. The line feed is the ASCII value 0x0A. Whitespace is strict - the space and line feed characters cannot be substituted with any other character. If a key is followed by two or more spaces, all but the first space becomes the initial part of the value. Carriage return characters (ASCII 0x0D) are invalid anywhere in an ESON document. There are no upper length restrictions on keys, values, or the number of lines. Unless there is a specific reason not to, item lines should be sorted in ascending order, without regard to the meaning of the characters (i.e. a binary comparison, e.g. case insensitive). The final, empty, line that signals the end of the ESON document is a single ASCII value 0x0A. For example, here's a complete document with a single item:
┏━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┓ ┃ key ┃ space ┃ value ┃ line feed ┃ line feed ┃ ┗━━━━━┻━━━━━━━┻━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━┛
And here's an ESON document with no items:
┏━━━━━━━━━━━┓ ┃ line feed ┃ ┗━━━━━━━━━━━┛

1.2 Interpretation

The abstract parsed representation ESON is a map of keys (which are strings), to lists of values (which are also strings). An item line with a non-caret key represents a new entry in the map, with the key pointing to a list with a single item - the value. An item line with a caret key represents an append of its value to the most- recent non-caret key's list. For example, the following ESON document: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ one two ^ three ^ four five six seven eight ^ nine ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Could be represented in JSON as: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ { "one": ["two", "three", "four"], "five": "six", "seven": ["eight", "nine"] } ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Note that in the above example, the list with a single item ["six"] was represented as a simpler scalar value of "six". This is a valid interpretation, but not mandatory - it's up to the language/library which is doing the parsing. An ESON document starting with an item line with a caret key is invalid. There is no upper limit to the number of items in a list. An item line with a zero-length value is not semantically equivalent to the absence of that line - a zero-length value represents an empty string. When a duplicate key is encountered during parsing, it is semantically equivalent to first discarding the complete list of values that the key previously mapped to. For example: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ a 1 ^ 2 b 3 a 4 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Is equivalent to: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ { "a": "4", "b": "3" } ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1.3 ESON as used in Edsu

Any block which is interpreted as an ESON document in Edsu must have a payload which contains nothing except exactly one valid ESON document (i.e. no extraneous data before or after). Extraneous ESON keys which are not defined by this specification will be ignored, unless otherwise stated. All ESON keys starting with "edsu:" are reserved.

2 Value Types Used in Edsu

Below are the requirements for converting ESON values to more meaningful types and back again. These types will be referred to in the rest of this document. Unless otherwise noted, whitespace is significant and is never discarded, and case is significant.

2.1 bool

One of exactly two strings: "true" and "false".

2.2 u16, u32, u64

Textual representations of non-negative integers, base 10. Leading zero and characters outside of the set 0123456789 (e.g. decimals or commas) are not permitted. The 16, 32, and 64 refer to the number of bits that the resulting number must fit into.

2.3 value

No conversion should be done.

2.4 version

A u16 followed immediately by a period (ASCII 0x2E) followed immediately by another u16

2.5 versions

One or more version values, separated by single spaces (ASCII 0x20)

2.6 encoding

An encoding ID. As of this version there is only one: deflate Any future encodings will be composed entirely of the following characters: abcdefghijklmnopqrstuvwxyz0123456789- Note that unlike HTTP, deflate refers specifically to a raw RFC 1951 encoding, without any encapsulation by the zlib or gzip formats. Servers must ignore any encodings that they do not recognize. The encodings of gzip, zlib, and brotli are disallowed.

2.7 encodings

One or more encoding IDs, separated by single spaces (ASCII 0x20)

2.8 multihash

A multihash as defined by the IPFS project, encoded in the base58 format using the following alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz As of this version of the protocol, servers must produce multihashes with the hash function sha2-256 (0x12) with a digest length of 32 bytes (0x20). It is expected that other hash functions and digest lengths will be added to maintain cryptographic strength as technology improves.

2.9 username

An ID followed immediately by an @ (ASCII 0x40) followed immediately by a domain name. The domain name must be apex: i.e. composed of exactly two labels joined with a period, the latter being the TLD; i.e. subdomains are not permitted. The ID has identical restrictions to a domain name label: it must be between 1 and 63 characters from the set: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789- and not start or end with a dash. Usernames are case-insensitive on interpretation (i.e. a@B.net and A@b.NET are equivalent). Clients and servers are required to accept mixed or uppercase versions of usernames, but are required to only emit lowercase versions.

2.10 name

An Edsu name is nine or more characters from the set: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_. A name cannot start or end with a period, nor can two or more periods be directly beside each other. The first 8 characters of a name must be one of the following: pub.std. pub.app. pub.srv. grp.std. grp.app. grp.srv. prv.std. prv.app. prv.srv. The maximum length of a name is 255 characters. The groups of characters between periods in a name are referred to in this specification as segments. Names must have 5 or more segments. Names with the third segment of "edsu" are reserved. By convention, names with a second segment of "app" have no restrictions, and those with "std" should be used in accordance with some published specification. Names with the second segment of "srv" are used exclusively by the server, and in interactions with it. By convention, in names with a second segment of "app", the third segment represents the user or organization which created the app, with the fourth segment representing the name of the app. For example, the name starting with pub.app.Ada.BernoulliCalc would be read as an app named BernoulliCalc created by someone named "Ada". By convention, in names with a second segment of "std", the third segment represents a category of standards, with the fourth naming the standard itself. For example a name starting with pub.std.communications.email would be read as relating to a communications standard, specifically email.

2.11 name-pattern

A name pattern is as follow:
┏━━━━━━┳━━━━┓ ┃ name ┃ .* ┃ ┗━━━━━━┻━━━━┛
Where name is exactly as defined above. ".*" is the ASCII characters 0x2E followed by 0x2A - it may be omitted, in which case that name pattern and a name are identical.

2.12 ESON-encoded UTF-8

This encoding takes any UTF-8 string and encodes it so it may be stored in an ESON value. Every byte which is valid in an ESON value, except for ~ (ASCII 0x7E), may be encoded as itself - all other bytes will be called "non-encodable" in this section. Every occurrence of one or more contiguous non-encodable bytes is replaced by:
┏━━━━━━━━━━━━┳━━━┓ ┃ ~ ┃ base58 ┃ ^ ┃ ┗━━━━━━━━━━━━┻━━━┛
Where ~ and ^ are ASCII characters 0x7E and 0x5E respectively, and base58 is the non-encodable bytes base58-encoded as described elsewhere in this specification. It is permitted to treat any or all encodable bytes as non-encodable (i.e. include them in the base58 encoding), but it is not recommended due to increased size and lack of consistency when being hashed. Likewise it is recommended to group as many adjacent non-encodable bytes together as possible before base58 encoding (for the same reason as just mentioned), however the encoder is permitted to group in any way.

2.13 address

An address is composed of usernames, names, and multihashes in one of three forms:
┏━━━━━━━━━━┳━━━┳━━━━━━┓ ┃ username ┃ : ┃ name ┃ ┗━━━━━━━━━━┻━━━┻━━━━━━┛
┏━━━━━━━━━━┳━━━┳━━━━━━━━━━━┓ ┃ username ┃ # ┃ multihash ┃ ┗━━━━━━━━━━┻━━━┻━━━━━━━━━━━┛
┏━━━━━━━━━━┳━━━┳━━━━━━┳━━━┳━━━━━━━━━━━┓ ┃ username ┃ : ┃ name ┃ # ┃ multihash ┃ ┗━━━━━━━━━━┻━━━┻━━━━━━┻━━━┻━━━━━━━━━━━┛
Where : is ASCII 0x3A, and # is 0x23.

2.14 length-or-stop

If this is correctly parses as a u16, it is done so, otherwise it is treated as a unmodified value.

2.15 oob-code

One of: advisory redirect authentication-error authentication-challenge dropped-subs not-found hash-mismatch timed-out permission-denied invalid-input invalid-content rate-limited just-sent server-error

2.16 chain

The chain format is detailed in the section of this specification devoted to chaining.

2.17 token

The tokens used in the hello message must be: * 64 characters in length or less * Meet the same requirements as a name segment

3 Edsu Wire Protocol

Edsu is a client/server, connection-based protocol. All connections are made, conceptually, between two Edsu users: the user-from and the user-to. The user- from is the client, with the server acting on behalf of the user-to. It's often the case that user-to and user-from are the same. The first step to initiate a connection is to convert the user-to's username into a fully qualified domain name (FQDN): this is done by replacing the "@" with the characters ".edsu.", e.g. hello@example.com becomes hello.edsu.example.com. Once the IP address is resolved from the FQDN, a TCP/IP connection is established by the client to one of port 3216 or 443 at that address. Both ports only accept connections which are encapsulated with TLS. The protocol spoken on ports 3216 and 443 are identical, except that on port 443 it is further encapsulated by the WebSocket protocol. Edsu WebSocket connections must be encapsulated by SSL, and use the path /edsu/ws on connection. Once the connection is established, communication consists of messages sent from client to server and vice versa. A message is composed of a header, which always a valid ESON document, followed by an optional payload. A payload is zero or more unrestricted bytes (i.e. from 0-0xFF), followed by a line feed character (0x0A). The maximum size of a payload (excluding the aforementioned terminating line feed character) is 64,512 bytes (i.e. 63kB). Before or after messages, the client and server may send zero or more line feed characters - these must be discarded. The first item line of a header ESON document must contain the key "edsu" with the value stating the message type. Both whitespace and letter case is always significant in both the keys and values of message headers. If the header is found to be invalid (i.e. not a valid ESON document), if the values are not correctly formatted for the message type (e.g. a malformed multihash), or if the terminating payload character is not a line feed, the server response must be an oob with the code invalid-input and close-connection set to "true". Every key across all Edsu messages maps to a scalar value - i.e. the list functionality of ESON is not used. The exhaustive list of messages that may be sent from the client: hello block-put block-get name-put name-get name-append sub-put sub-clear ping pong And the similarly exhaustive list of messages from the server: hello oob ok block name sub-notify authenticated ping pong Though some of them share the same name, the client and server messages are distinct, and messages defined as being from the server must not be sent by the client, and vice versa. Extraneous keys in the ESON header of messages are allowed, however they must contain at least one ":" character and cannot begin with "edsu:".

3.1 Order of Actions

Unless otherwise specified, the actions taken by the Edsu server must be performed in the order that the instigating messages arrive on the connection. The exceptions are: * Block-get messages and any resulting chains may be completed at any time after the message arrives. * Ping messages may be serviced at any time. Multiple connections to the same hosts are independant of each other from an order of actions perspective.

3.2 Channels

In every client-to-server message there is an optional key "channel", the value of which can be any valid ESON value. Similarly, for every server-to-client message there is a required channel key of the same type. If used in a client message, all server messages generated as a result must echo the channel unchanged. If unused, or in the case of a server message that is unrelated to any specific client message, the channel returned in the server message must be the default value of "0". In all message descriptions that follow, the channel key for both client and server messages is implied.

3.3 Encoding

The client message "block-put" and the server messages "block" and "oob" have a key called encoding. This states the encoding (presumably but not necessarily a compression algorithm) which has been used on the payload of that message. The decoding capabilities of both the client and server are exchanged during connection initiation. It is at their descrection which encoding to use (or none) on a per-message basis, provided that the encoding is reported to be decodable by the receiver.

3.4 Out Of Band Message

At any time the server may send an Out Of Band message (oob). This has the format:
┏━━━━━━━━━━━━━━━━━━┓ ┃ From server: oob ┃ ┣━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━╋━━━━━━━━━━┫ ┃ code ┃ oob-code ┃ ☑ ┃ ┃ close-connection ┃ bool ┃ ┃ ┃ retry-delay-ms ┃ u32 ┃ ┃ ┃ payload-length ┃ u16 ┃ ┃ ┃ encoding ┃ encoding ┃ ┃ ┗━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━┻━━━━━━━━━━┛
The oob-code key describes the reason for the message. The close-connection key, if present and "true", means that the server has requested that the client close the connection immediately. The server will stop processing further messages from the client and will close the connection on its side if the client does not do it quickly enough. The retry-delay-ms key is a recommendation that the request that caused this OOB message to be retried, unchanged, after a wait. That wait is specified in milliseconds. This is an advisory and it can be ignored by retrying at any time or not at all. The payload-length key specifies, in bytes, how long the payload of the message is (if there is one). Likewise, the encoding key specifies the payload's encoding (if any). The payload is dependant on the oob-code. If not detailed explicitly in this specification, the server may send a payload that is an ESON document, and it may include debug information using the key "debug", with the value being a natural language description of what went wrong, and any futher details.

3.5 Connection Initiation

The first messages sent on a newly established connection must be the client sending a hello. Once that is sent (i.e. before the response is received) it is then free to send any other valid client-to-server message, though it is disallowed (OOB code invalid-input) to send another hello message during the lifetime of the connection. The first message sent from the server to the client must be a hello in response to the client's. Like the client, that hello can be sent only once per connection. The client-to-server hello message is:
┏━━━━━━━━━━━━━━━━━━━━┓ ┃ From client: hello ┃ ┣━━━━━━━━━━━━━━━━━━┳━┻━━━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ versions ┃ versions ┃ ☑ ┃ ┃ encodings ┃ encodings ┃ ┃ ┃ secret ┃ value ┃ ┃ ┃ token ┃ value ┃ ┃ ┃ visitor-username ┃ value ┃ ┃ ┗━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
The versions key states the versions of the Edsu protocol that the client is able to speak. If the server does not speak any of them it must send an oob with the code server-error, with close-connection set to "true". The encodings key states the payload encodings that the client is able to parse. If none of secret, token, or visitor-username is sent, the client is requesting an anonymous connection. Alternatively, they can request to authenticate themselves as A) the user-to via secret, B) the user-to via token, or C) a user- from via token and visitor-username. No matter the method of authentication, the immediate response from the server is:
┏━━━━━━━━━━━━━━━━━━━━┓ ┃ From server: hello ┃ ┣━━━━━━━━━━━┳━━━━━━━━┻━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ version ┃ version ┃ ☑ ┃ ┃ encodings ┃ encodings ┃ ┃ ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
The version key states the version of the Edsu protocol that the rest of the connection will be using. The encodings key states the payload encodings that the server is able to parse.

3.5.1 Secret

The value of the secret key must be exactly 64 characters long. In the likely event that the actual secret is shorter than this, it must extended on the right with spaces (ASCII 0x20). To arrive at the secret to be compared, the server must first strip all trailing spaces, then converted to UTF-8 using the method described elsewhere in this specification, and then normalized using the NFKC form. The secret key is exclusive of the token and visitor-username keys. If the secret (which is established via some external mechanism) matches, the client is authenticated as the user-to; i.e. user-from and user-to are the same. A message is sent:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ From server: authenticated ┃ ┣━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┻┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┗━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
This indicates to the client that the authentication is successful. That capability block must at least grant the required permissions to use the functionality at pub.srv.edsu.authentication.tokens.generate.*. If the password doesn't match, an Out Of Band message (oob) is sent with the oob-code of "authentication-error", and close-connection set to "true".
3.5.1.1 Authentication Challenge
On a successful match of the secret, instead of responding with an authenticated message, the server may send an OOB message with the code "authentication- challenge". The payload of this message will be an ESON document with the key "edsu:type". The value pointed to by this key must be: "question" (it is expected that in later versions of this specification there will be other possible values). In the case of a "question" challenge, the ESON will also contain a key "edsu:question", with an ESON-encoded UTF-8 value which should be presented verbatim to the user. The client must then collect an answer from the user, base58-encode it, then append that base-58 encoded value to the string "pub.srv.edsu.authentication.challenge.answer.", forming a name. This name should then be sent to the server in a name-get message. If the challenge has been successfully answered, the server will respond with a name message - the hash of which has no meaning and should be ignored - followed by an authenticated message identical to what is sent when there is no challenge and the secret match is successful. Alternately, if the challenge has not been met, an OOB message with the code "not-found" will be sent, followed by an OOB message with the code "authentication-error". This second OOB will have the key "close-connection" set to "true" and the connection will be closed shortly after.

3.5.2 Token, Without Visitor-Username

A token key without a visitor-username is interpreted as requesting authentication as the user-to based on that token. The content of the token is left up to the server, as long as it meets the requirements of the "token" type described elsewhere in this specification. The mechanism for validating the token is up to the server. The response to the client is identical to that of a secret-based authentication which generates no challenge.

3.5.3 Token, With Visitor-Username

A hello message with both token key and visitor-username present is interpreted as requesting authentication as the user stated in visitor-username; if authentication is successful, the user-from of the connection is set to the contents of visitor-username. To authenticate, the server which received the hello message (referred to in this section as the user-to server), opens a non-WebSocket, anonymous Edsu connection to the user-from's server, acting as a client. On connection, a name-get message is sent, with the name being:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓ ┃ pub.srv.edsu.authentication.visitors.capabilities. ┃ token ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━┛
Where token is the value from the client's hello message. Authentication is considered successful if the user-from server responds with a multihash which resolves to a valid visitor capabilities block. These capabilities are then applied to the client's connection. The user-to server may optionally cache the results of this authentication for up to 4096 seconds, after which it must repeat the transaction before performing any further actions for the client. In the event of the capabilities being different from those obtained by a previous authentication, the connection must be amended to reflect this. The response to the client hello message from the user-to server is identical to that of a token authentication without a username. Before being used in the hello message, a visitor token must be specialized on the user-to. First, the visitor token (as returned from the token generating service) is divided into two parts: the prefix and the postfix, with the former being the first 8 characters, and the latter being the remaining character. An HMAC-SHA256 function is then applied, with its key being the postfix, and its message being the user-to username. The result of this function is base58-encoded, and then appended to the prefix to produce the specialized visitor token which can then be used in the hello message. Server implementations must take care that the postfix is still of sufficient length to provide adequate security properties.

3.6 General OOB Messages

3.6.1 At Any Time

The server may at any time send an oob with the codes "advisory", "server- error", "redirect", and "dropped-subs". Advisory oobs can always be safely ignored. As of this version of the specification, there are no advisories specified. Server-error oobs can be sent at any time (using channel "0"), or in response to a particular client message (in which case it will use that message's channel value, or "0" if none was given). It indicates that something has interfered with the normal operation on the server in such a way that the client should respond. The client response is dictated by the presence and values of the close-connection and retry-delay-ms keys. Redirect oobs can be sent at any time, and indicate that the client should drop the connection and reconnect to a different server. The close-connection key must be "true". The payload is a (poentially encoded, using the encoding specified in the message) ESON document with the following keys:
┏━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━╋━━━━━━━━━━╋━━━━━━━━━━┫ ┃ username ┃ username ┃ ☑ ┃ ┃ alias ┃ bool ┃ ☑ ┃ ┗━━━━━━━━━━┻━━━━━━━━━━┻━━━━━━━━━━┛
Username is what should be used as the basis for the reconnection - i.e. it will be used to determine the FQDN of the server to connect to. A redirect with alias set to "false" indicates that the server has decided that another server on a different host is better equipped to handle the connection. Semantically it is completely transparent: the user-to is, other than from the perspective of the network protocols that underly Edsu, the same as before the redirect. This is used for load balancing. A redirect with alias set to "true" indicates the user-to has aliased the username to another. Semantically, the client is aware of the alias and can respond as it wants to this information. It is used in the event of an Edsu user changing their username, but wants addresses featuring their old username to resolve to their current account. Dropped-subs is addressed in the section of this specification which details subscriptions.

3.6.2 In Response to Client Messages

Not-found indicates that the name or block which has been requested does not exist on the server, or in some cases it exists but the connection is lacking the capabilities to know of its existance. Hash-mismatch is used to indicate when messages with existing-hash keys encounter a different multihash than what is specified. Timed-out indicates that the request took too long to complete and the server has given up on it. Note that it is possible that the request actually did complete, but the server was unaware of this at the time it generated the timed- out oob. This message is the only response to the originating client message - i.e. the server must not send a timed-out oob and then afterwards send a message indicating success or failure of the original request. Permission-denied indicates that the connection does not have sufficient capabilities to perform the action requested. Invalid-input indicates that the contents of the requesting message are non- sensical in some way. Invalid-content indicates that the data stored on the server does not make sense in the context of the request. E.g. if a request presumes that a certain block stored on the server is an appendable block, and it isn't, it will generate an invalid-content oob. Rate-limited indicates that either the connection, or all connections to a particular user-to have asked more of the server than it is willing to provide. This oob must specify a retry-delay-ms to advise when a reasonable time to retry (unchanged) the client message will be. However, this is not to be interpreted as a guarantee that waiting that amount of time will satisfy the rate limiter. Just-sent is detailed in the section of this specification devoted to chaining.

4 Blocks

Blocks are in one of two formats: text or binary. In either case, the upper size limit for the final encoded form is 64,512 bytes (i.e. 63kB).

4.1 Text Blocks

Text blocks consist of a header and a payload. The header is in the form:
┏━━━┳━━━━━━┳━━━━━━━━━━━┓ ┃ ~ ┃ salt ┃ line feed ┃ ┗━━━┻━━━━━━┻━━━━━━━━━━━┛
Where ~ and line feed are ASCII 0x7E and 0x0A respectively, and salt is zero or more characters in any valid UTF-8 encoding. The payload is zero or more characters in any valid UTF-8 encoding. Any Unicode byte order mark is prohibited anywhere in the block.

4.2 Binary Blocks

Binary blocks consist of a header and a payload. The header is in the form:
┏━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━━━━━━┓ ┃ vers ┃ cloc-msb ┃ cloc-lsb ┃ hloc-msb ┃ hloc-lsb ┃ salt ┃ hashes ┃ contents ┃ ┗━━━━━━┻━━━━━━━━━━┻━━━━━━━━━━┻━━━━━━━━━━┻━━━━━━━━━━┻━━━━━━┻━━━━━━━━┻━━━━━━━━━━┛
Vers is a byte indicating the encoding format's version, which is 0.1. The four most significant bits are the major version (0), and the four least significant bytes are the minor version (1). So 0.1 is 0x01, 3.4 is 0x34, etc. Servers must not reject blocks with a vers greater than what they recognize, however, the format is forwards-compatible, so any block must be parsable as if it were version 0.1 Cloc-msb and cloc-lsb are the most and least signficant bytes (respectively) of the 16-bit location of the start byte of the contents. Hloc-msb and hloc-lsb are the most and least signficant bytes (respectively) of the 16-bit location of the start byte of the hashes. Salt is zero or more bytes of salt, each byte of which can be any value from 0x00-0xFF. Hashes is the concatenation of zero or more byte-encoded multihashes. These are not the same multihash format as used elsewhere in this specification - the multihash format is the same, but it is not Base58-encode. E.g. a SHA-256 multihash would always be represented as 34 bytes: two bytes of header followed by 32 bytes of digest. Contents is zero or more bytes, each byte of which can be any value from 0x00-0xFF.

4.3 Block Messages

4.3.1 Block Put

The client stores blocks on the server using the block-put message:
┏━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ From client: block-put ┃ ┣━━━━━━━━━━━━━━┳━━━━━━━━━┻━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ payload-stop ┃ length-or-stop ┃ ☑ ┃ ┃ encoding ┃ encoding ┃ ┃ ┗━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━━━━┛
Where payload-stop is either a u16 or an unparsed value. If it is a u16, the server will read that many bytes of payload, then expect a line feed. The line feed is discarded. If payload-stop is not a u16, the server will read payload bytes until that value is encountered, after which it will expect a line feed. Both the payload- stop value and the line feed are discarded. If the connection does not have the capability to upload blocks, the server will respond with an oob with the code "permission-denied" and stop. If the encoding key is present, the server will then decode the payload using the specified method. If the specified method is not supported, or the encoding is invalid, the server will respond with an oob using the code "invalid-input" and stop. The decoded payload is referred to as "the payload" for the remainder of this section. The payload must be a valid text or binary block. If not the server will respond with an oob using the code "invalid-input" and stop. The payload is now stored on the server. It may be associated with the user-to account, but that is not necessary (i.e. it is acceptable for the server to pool blocks between users). If successful (i.e. none of the above-mentioned oobs are returned, nor any of the more generic oobs such as "server-error" or "timed-out"), the following message is sent to the client:
┏━━━━━━━━━━━━━━━━━┓ ┃ From server: ok ┃ ┣━━━━━━┳━━━━━━━━━━┻┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ hash ┃ multihash ┃ ┃ ┗━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Where hash key must be present, and is the multihash generated from the bytes of the payload (i.e. the block).

4.3.2 Block Get

The client retrieves blocks from the server using the block-get message:
┏━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ From client: block-get ┃ ┣━━━━━━━┳━━━━━━━━━━━┳━━━━┻━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ hash ┃ multihash ┃ ☑ ┃ ┃ chain ┃ chain ┃ ┃ ┗━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Where hash is the multihash returned when the block was stored with block-put. The chain key is addressed in the Chaining section of this specification - however no matter its value, the behaviour described below is invariant. The server responds with one of: a generic oob, an oob with the code "not-found" (if the server does not have a block stored at that hash), or the following message:
┏━━━━━━━━━━━━━━━━━━━━┓ ┃ From server: block ┃ ┣━━━━━━━━━━━━━━━━┳━━━┻━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ hash ┃ multihash ┃ ☑ ┃ ┃ payload-length ┃ u16 ┃ ☑ ┃ ┃ encoding ┃ encoding ┃ ┃ ┗━━━━━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Hash is echoed unaltered from the same key in the client message. Payload- length is the length of the payload in bytes, and encoding is the optional encoding of the payload. The server is free to use any encoding that the client has reported it supports in its hello message, or none, at its discretion.

4.4 Name Messages

4.4.1 Name Put

In addition to blocks, Edsu stores name associations. A name association is a one-to-one mapping between a name and a block, as referenced by its hash. The block referenced by the name must exist on the server. The client changes the association of a name with a block using the name-put message:
┏━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ From client: name-put ┃ ┣━━━━━━━━━━━━━━━┳━━━━━━━┻━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ name ┃ name ┃ ☑ ┃ ┃ hash ┃ multihash ┃ ┃ ┃ existing-hash ┃ encoding ┃ ┃ ┗━━━━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Name is the name association to be operated on. Hash is optional: if it is not present the assocation is deleted. If it exists the association is either created with the specified hash, or updated to it. Existing-hash is required for every operation which isn't a creation. If the association exists, it must match the hash that the association currently points to. If it doesn't match, the server must send an oob with the code "hash- mismatch" and stop. A non-existant hash compared to an existant one (e.g. existant-hash exists but the association doesn't) it is considered a mismatch. As a special case, if the hash in name-put already matches that name's existing association, existing-hash is disregarded and the server must act as if the name-put operation had occured and was successful. When hash is specified, if a corresponding block is found on the server the server will respond with an oob with the code "not-found" and stop. The corresponding block must be a text block, containing exactly one valid ESON document, with no extraneous data before or after it. If this requirement is not met, the server must respond with an oob with the code "invalid-content" and stop. If the name-put results in an updated hash, and that block's ESON has the key "edsu:previous", the list pointed to by that key must contain a single item which matches the existing-hash key in the client message. That is, if the name block has an "edsu:previous" key, it must be accurate. If not, the server must send an oob with the code "invalid-content" and stop. If the connection lacks the capabilities to perform the operation requested, the server must respond with an oob with the code "permission-denied" and stop. If the operation is successful the following message is sent:
┏━━━━━━━━━━━━━━━━━┓ ┃ From server: ok ┃ ┣━━━━━━┳━━━━━━━━━━┻┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ hash ┃ multihash ┃ ┃ ┗━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Where the hash key is identical to the same key from the client message (including its absence if it is absent from the client message).

4.4.2 Name Get

To retrieve a name association from the server, the client sends a name-get message:
┏━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ From client: name-get ┃ ┣━━━━━━━┳━━━━━━━┳━━━━━━━┻━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━╋━━━━━━━╋━━━━━━━━━━┫ ┃ name ┃ name ┃ ☑ ┃ ┃ chain ┃ chain ┃ ┃ ┗━━━━━━━┻━━━━━━━┻━━━━━━━━━━┛
Where name is the association requested. If the connection lacks the required capabilities to retrieve the association, the server will send an oob with the code "permission-denied" and stop. If the association doesn't exist, or the connection lacks the required capabilities to know if it exists or not, the server will send an oob with the code "not-found" and stop. Otherwise (and unless a more generic oob is sent), the server will respond with:
┏━━━━━━━━━━━━━━━━━━━┓ ┃ From server: name ┃ ┣━━━━━━┳━━━━━━━━━━━┳┻━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ name ┃ name ┃ ☑ ┃ ┃ hash ┃ multihash ┃ ☑ ┃ ┗━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Name is an echo of the name from the client message. Hash is the multihash which is associated with that name.

4.4.3 Name Append

The message name-append appends a value to a named appendable block and changes the name association to point to the resultant block:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ From client: name-append ┃ ┣━━━━━━┳━━━━━━━━━━━┳━━━━━━━┻━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ name ┃ name ┃ ☑ ┃ ┃ hash ┃ multihash ┃ ☑ ┃ ┗━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Name is an association which points to an appendable block. If the block associated with the name isn't an appendable block, the server will send an oob with the code "invalid-content" and stop. If the block does not exist, the server will send an oob with the code "not-found" and stop. If the connection lacks the required capabilities to retrieve the association, or fails to meet the requirements stated in the appendable block, the server will send an oob with the code "permission-denied" and stop. If the rate conditions stated in the appendable block would be exceeded by the append, the server will send an oob with the code "rate-limited", along with an advisory "retry-delay-ms" which will suggest when to try again, and stop. If none of the above-mention disqualifiers exist, and barring other more generic oob responses, the server will ensure that the following append value is added to the appendable block:
┏━━━━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┓ ┃ time ┃ space ┃ user-from ┃ space ┃ hash ┃ ┗━━━━━━┻━━━━━━━┻━━━━━━━━━━━┻━━━━━━━┻━━━━━━━┛
Where time is a u64 representing when the message was received, encoded as the number of seconds elapsed since the Unix epoch. Both spaces are ASCII 0x20. User-from is that of the connection. Hash is the hash contained in the message from the client - it does not need to reference a block which exists on the server. If the key "edsu:appended" does not exist in the appendable block, the key will be added, pointing to a list with a single item, the append value. If the key "edsu:appended" does exist, and the addition of the append value will not violate the size restriction specified in the appendable block, the append value is added to the top of the list (i.e. it will become its first item) of that key. If the key "edsu:appended" does exist, and the addition of the append value will violate the size restriction specified in the appendable block, the append value is added to the top of the list, and then the block is duplicated, with the top (most recent) half of the "edsu:appended" list being placed in one, and the bottom half being placed in the other. If the number of items is odd, the larger of the two lists will be that of the least recent block. The least recent block is stored in the user-to's storage, and its hash is placed in the most recent block under the key "edsu:append-previous" (replacing what came before if necessary). The amended appendable block (or the most recent one in the case of a split) is then stored in the user-to's account, and the name stated in the client message will have its association changed to the amended block's hash. Then the server sends the following message:
┏━━━━━━━━━━━━━━━━━┓ ┃ From server: ok ┃ ┣━━━━━━┳━━━━━━━━━━┻┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ hash ┃ multihash ┃ ┃ ┗━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Where hash is the multihash of the new name association. However, if the connection lacks the capabilities to see the name association, the hash key will not be present in the message. If more than one name-append message arrives at once or during the servicing of an already-begun name-append operation, the server is required to ensure that all of their resultant values are present in the appendable block, and that the times listed in the "edsu:appended" list monotonically decrease. Further details on appendable blocks can be found in the section devoted to them.

4.5 Subscription Messages

4.5.1 Subscription Put

Subscriptions are added or amended via the following message:
┏━━━━━━━━━━━━━━━━━━━━━━┓ ┃ From client: sub-put ┃ ┣━━━━━━━━━━━━━━━┳━━━━━━┻━━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ name ┃ name ┃ ☑ ┃ ┃ existing-hash ┃ multihash ┃ ┃ ┃ once ┃ bool ┃ ┃ ┃ expires ┃ u64 ┃ ┃ ┃ chain ┃ chain ┃ ┃ ┗━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━┻━━━━━━━━━━┛
Name is the name association to be monitored; it does not need to exist. When the message is received, the value of existing-hash is compared against the hash currently associated with the name given, and if they differ a notification is sent - note that the absence of this key matches the absence of the name assocation, and if one exists without the other this is considered a mismatch. Once being set to "true" causes the subscription to be deleted once it is responsible for a notification being sent to the client. If expires exist, once the server clock (as measure in seconds since the Unix epoch) matches its value, the subscription is deleted. If chain exists, that chain will be executed every time a sub-notify message is generated as a result of this subscription. Note that permissions checks are performed when notifications are generated, not when subscriptions are being created, so sub-put will never fail due to insufficient permissions. Each connection has a set between 0 and not more than 256 subscriptions. If a sub-put message is received which would result in the creation of a 257th subscription, the server will send an oob with the code "invalid-input" and stop. If no generic oobs are sent, the server will send the following message:
┏━━━━━━━━━━━━━━━━━┓ ┃ From server: ok ┃ ┣━━━━━━┳━━━━━━━━━━┻┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ hash ┃ multihash ┃ ┃ ┗━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
The hash key will not be present.

4.5.2 Subscription Clear

The client can delete all subscriptions attached to its connection by sending the following message:
┏━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ From client: sub-clear ┃ ┣━━━━━┳━━━━━━┳━━━━━━━━━━━┫ ┃ key ┃ type ┃ required ┃ ┣━━━━━╋━━━━━━╋━━━━━━━━━━━┫ ┗━━━━━┻━━━━━━┻━━━━━━━━━━━┛
The server will respond with:
┏━━━━━━━━━━━━━━━━━┓ ┃ From server: ok ┃ ┣━━━━━━┳━━━━━━━━━━┻┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ hash ┃ multihash ┃ ┃ ┗━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
The hash key will not be present.

4.5.3 Subscription Notify

At any time the server may send a sub-notify to indicate the current state of a subscribed name:
┏━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ From client: sub-notify ┃ ┣━━━━━━┳━━━━━━━━━━━┳━━━━━━┻━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ name ┃ name ┃ ☑ ┃ ┃ hash ┃ multihash ┃ ┃ ┗━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Name is the subscribed name. Hash is the associated hash - if the name association does not exist, the hash key will not be present. The server must send a sub-notify message for every change in any subscribed name association - i.e. it is created, deleted, or becomes associated with a different hash. However, there are no guarentees about not sending duplicate or spurious notifications, other than the hash key in each sub-notify message must always be accurate at the time of message being sent. If there are too many subscription notifications to be sent (as determined by the server), the server may choose to not send them. If this choice is made, the server must also delete all subscriptions on the connection (i.e. the same as if the client had sent a sub-clear message), and send an oob with the code "dropped-subs".

4.6 Ping Messages

Both the client and server can send ping messages at any time:
┏━━━━━━━━━━━━━━━━━━━┓ ┃ From server: ping ┃ ┣━━━━━┳━━━━━━┳━━━━━━┻━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━╋━━━━━━╋━━━━━━━━━━┫ ┗━━━━━┻━━━━━━┻━━━━━━━━━━┛
┏━━━━━━━━━━━━━━━━━━━┓ ┃ From client: ping ┃ ┣━━━━━┳━━━━━━┳━━━━━━┻━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━╋━━━━━━╋━━━━━━━━━━┫ ┗━━━━━┻━━━━━━┻━━━━━━━━━━┛
The receiver is obligated to send a pong message as soon as possible:
┏━━━━━━━━━━━━━━━━━━━┓ ┃ From client: pong ┃ ┣━━━━━┳━━━━━━┳━━━━━━┻━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━╋━━━━━━╋━━━━━━━━━━┫ ┗━━━━━┻━━━━━━┻━━━━━━━━━━┛
┏━━━━━━━━━━━━━━━━━━━┓ ┃ From server: pong ┃ ┣━━━━━┳━━━━━━┳━━━━━━┻━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━╋━━━━━━╋━━━━━━━━━━┫ ┗━━━━━┻━━━━━━┻━━━━━━━━━━┛

5 Special Blocks

Certain blocks will cause the behaviour of the Edsu server to change, or create an expectation on the part of those using its data. These are called special blocks. All of these blocks are required to be text blocks containing exactly one valid ESON document without any extraneous data outside of it document. A block is interpreted as one or more types of special blocks dependent on the context in which it is used. Any keys that are not recognized within the context that the block is being interpreted are ignored.

5.1 Group Block

A group block contains the key "edsu:users", and with pointing to a list of one or more of the items, as follows. A block that does not contain the key "edsu:users" is interpreted as an empty group. Note that membership in a group is a set union function - if multiple items indicate that a user is part of the group, that is semantically equivalent to them being included once. These are the possible items:

5.1.1 Uername

A user whose username appears in the list is part of the group.

5.1.2 Wildcard

An * (ASCII 0x2A) indicates that all Edsu users are part of this group

5.1.3 Domain Wildcard

┏━━━┳━━━┳━━━━━━━━┓ ┃ * ┃ @ ┃ domain ┃ ┗━━━┻━━━┻━━━━━━━━┛
Where * and @ are ASCII 0x2A and 0x40 respectively and the domain is a valid domain name part of a username. Any Edsu user whose username contains the same domain name is part of this group.

5.1.4 Block Include

┏━━━┳━━━━━━┓ ┃ # ┃ hash ┃ ┗━━━┻━━━━━━┛
The "#" is ASCII 0x23; hash is a multihash which points to another group block which is stored on the server. This group block is interpreted to be a union set with the referencing group block.

5.1.5 Name Include

┏━━━┳━━━━━━┓ ┃ : ┃ name ┃ ┗━━━┻━━━━━━┛
The ":" is ASCII 0x3A; name is a name association in the user-to's account which is associated with a group block. This group block is interpreted to be a union set with the referencing group block. If the association is non-existant, or if it's associated block is not a valid group block, this item is interpreted as not existing.

5.2 Name Block

When a name association is queried to find out its hash, the ESON document contained in the block which is referenced by that hash is interpreted.

5.2.1 Previous

As mentioned in the Name Messages section, "edsu:previous" must be accurate in that its value must equal the hash of the association that immediately preceeded the present one.

5.2.2 Once

If the name block contains the key "edsu:once" and it is set to "true", any action which sends any client the associated hash will then immediately delete the name association; e.g. name-get will trigger the deletion, as well as sub- notify. The server must guarantee that the deletion is atomic - i.e. the hash is seen exactly at most once.

5.2.3 Expires

If the name block contains the key "edsu:expires", and its value is a u64, the name association will be (to all outside appearences) deleted when the server time (number of seconds elapsed since the Unix epoch) equals the value of the key.

5.2.4 Deny and Allow

The values of both "edsu:deny" and "edsu:allow" are interpreted identically to the "edsu:users" key in a group block. If the connection's user-from belongs to the group specified by "edsu:deny", any attempt to determine the association of the name pointing to the name block will fail with either an "permission-denied" or "not-found" oob. If the connection's user-from belongs to the group specified by "edsu:allow", and does not match the group specified by "edsu:deny", this is factored into the permissions required to determine the of the name pointing to the name block, depending on the context. See the section on permissions for more details.

5.2.5 Description

A name block may optionally include the key "edsu:description", which is an ESON-encoded UTF-8 string which describes what the block contains. This value is meant to be read by humans.

5.3 Metablock

A metablock contains keys that refer to other blocks. The key "edsu:metablocks" should contain a list of one or more hashes of other blocks that are to be interpreted as metablocks. The key "edsu:blocks" should contain a list of one or more hashes of blocks that are not metablocks. By convention, when "resolving" a metablock into a single sequence of bytes, the steps are: 1. Start with an empty buffer for bytes 2. Concatenate onto the buffer all contents of blocks listed in "edsu:blocks", in the order in which they appear 3. For each block in "edsu:metablocks", in the order in which they appear, repeat steps 2 and 3 I.e. metablock trees are resolved depth-first, with the contents of the nodes being resolved before their branches. Any metablock may contain one or both of the keys "edsu:contents-size" and "edsu:size". The former is the total, in bytes, of the block contents referred to in that block's "edsu:blocks" key, and recursively the same for all metablocks in its "edsu:metablocks" key. I.e. the size of the resulting concatenation of block contents produced by the above procedure for resolving a metablock. The latter ("edsu:size") is the same, except instead of the contents of the blocks, what is totalled is the entire block size of each (i.e. including the header). Note that this total specifically does not include the size of the metablocks themselve, only the blocks which appear in "edsu:blocks" lists. For both "edsu:contents-size" and "edsu:size", blocks which appear more than once contribute to the total as many times as they appear.

5.4 Appendable Block

5.4.1 Deny and Allow

The values of both "edsu:append-deny" and "edsu:append-allow" are interpreted identically to the "edsu:users" key in a group block. If the connection's user-from is part of the "edsu:append-deny" group, an attempt to perform a name-append on that name block results in an oob with the code of "permission-denied". If the connection's user-from is not part of the "edsu:append-allow", then the result is the same. A block with does not contain the "edsu:append-allow" key is not considered an appendable block. An attempt to perform a name-append on that name block results in an oob with the code of "invalid-content".

5.4.2 Appended

All items appended appear in the "edsu:appended" key. If this key does not exist it will be created on the first successful append.

5.4.3 Division

If the result of an append is a block whose size is larger than the u16 specified in "edsu:append-bytes-max", the server will divide the block in two, as described in the section detailing the name-append message. The key "edsu:append-previous" will be set so that the divisions form a linked list from most to least recent. If the "edsu:append-bytes-max" key is absent, the default of 63,488 (62kB) is used.

5.4.4 Rate Limiting

If the key "edsu:append-rate-limit" exists, appends will be rate-limited. The format of the value is a single space (ASCII 0x20) separated string of sub- items. Sub-items may be in any order. The format for a sub-item is:
┏━━━━━━━━━┳━━━┳━━━━━━━━━━━┓ ┃ sub-key ┃ : ┃ sub-value ┃ ┗━━━━━━━━━┻━━━┻━━━━━━━━━━━┛
In this case, the sub-values are all u64 values. The sub-keys are "seconds", "all-users", and "per-user". "Seconds" is required, as is at least one of "all- users", and "per-user". "Seconds" must be greater than 0 and less than 86401 (one day). Rate limiting is based on a sliding window which is "seconds" long. "All- users", and "per-user" are the number of appends allowed within the time window. For example, a value of: "seconds:3600 all-users:10 per-user:1" would be interpreted as: no more than 10 appends per hour, with no more than 1 append per hour by any one user". Of the two rate limits (per-user and all-user), whichever one is the most restrictive for that append is applied.

5.4.5 Time to Live

If the key "edsu:appended-ttl" is present in the head of the appendable block linked list (i.e. the one pointed to by a name), the server will periodically remove items from the "edsu:appended" list. An item will be removed if it is older than time to live specified by the u64 value of "edsu:appended-ttl". Note that if any item is removed, all the hashes up the linked list of appendable blocks will change, including an updated name association with the head. There are no guarantees as to when or how often this will take place, but if the server is being run by a commercial entity that charges based on amount of storage used, garbage collection should be performed before those charges are evaluated.

5.5 Capabilities Block

Capabilities blocks are detailed in the pemissions section of this specification.

6 Chaining

When a block is sent to the client as result of a client message that contains a chain key, for the next one second the server may send additional blocks specified by the value of that key. If during that one second the client sends a block-get requesting a block that was sent as a result of the chain that began the one second countdown, the server may, instead of a block message, send an oob with a code "just-sent" and "retry-delay-ms" set to the milliseconds remaining in the one second period. A chain consists of zero or more chain items, separated by single spaces (ASCII 0x20), in the order of when the client would like them to be sent. Each item is one of the following:
┏━━━━┳━━━┳━━━━━┓ ┃ id ┃ * ┃ key ┃ ┗━━━━┻━━━┻━━━━━┛
┏━━━━┳━━━┳━━━━━┓ ┃ id ┃ $ ┃ key ┃ ┗━━━━┻━━━┻━━━━━┛
┏━━━━┳━━━┳━━━━━┳━━━┳━━━━┓ ┃ id ┃ $ ┃ key ┃ > ┃ id ┃ ┗━━━━┻━━━┻━━━━━┻━━━┻━━━━┛
┏━━━━┳━━━┳━━━━━┓ ┃ id ┃ @ ┃ key ┃ ┗━━━━┻━━━┻━━━━━┛
Where key has the same definition as an ESON key, *, $, >, and @ are ASCII 0x2A, 0x24, 0x3E, and 0x40 respectively, key is identical to the ESON key format, and id is one or more characters of the set: abcdefghijklmnopqrstuvwxyz0123456789- An ID represents a block. The ID "0" is implicit, and represents the block requested in a block-get message, or the block associated with the name in a name-get message or a subscription notification. The characters *, $, and @ are selectors. They cause the block identified by the ID to be parsed as ESON, and then have the selector operation performed on it, as described below. If the block does not contain a valid ESON document, it is treated as an ESON document with zero items. The * symbol indicates that any valid multihashes found in the list pointed to by the key in the chain item are requested blocks. The $ symbol indicates that the first item found in the list pointed to by the key in the chain item represents a requested block, if it is a valid multihash. If the item contains both a $ and > symbol, then the block requested by the $ operation thereafter becomes identifiable by the ID which comes after the > symbol, to be used in following chain items. The @ symbol indicates that the value pointed to by the key in the chain item (provided that it is in a valid chain format) should be substituted inside current chain, replacing that chain item. The sequence of ID -> selector -> ID, etc. represents a chain, each starting from the "0" ID. The server may send zero or more blocks from any of these chains, but it must not "skip" sending blocks within those chains. I.e. at any time during the one second, the client must have the data on hand to verify that the server has executed the chain faithfully. In the case of a name-get or subscription notification any chain value, including one with zero items, implies the request for the name-associated block to be sent.

7 Garbage Collection

Blocks may be deleted by the server when they are no longer referenced, ultimately, by a name association. The process of identifying and deleting these unreferenced blocks is called garbage collection. References are recursive; e.g. if block A is considered referenced by a name association and within it there is a multihash for block B (i.e. a reference to block B), block B is also considered referenced by a name association. What is considered a reference within a block is detailed below. The server may garbage collect when and as often as it chooses, however if that server is being run by a commercial entity that charges based on amount of storage used, garbage collection should be performed before those charges are evaluated.

7.1 Binary Block References

The header of a binary block contains zero or more multihashes, as detailed elswhere in this specification; from the perspective of the garbage collector these are references to other blocks. No other part of the block is examined for references.

7.2 Text Block References

The payload of text blocks are examined for likely multihashes; any that it finds is considered a reference. A likely multihash is in the format:
┏━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━┓ ┃ non-alphanum ┃ b58s ┃ non-alphanum ┃ ┗━━━━━━━━━━━━━━┻━━━━━━┻━━━━━━━━━━━━━━┛
Where non-alphanum is any UTF-8 character which is not in the following set: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 Alternately, the beginning or ending points of the block payload qualifies as a substitution for non-alphanum; e.g. a payload consisting entirely of a single multihash is interpreted as having one likely multihash. B58s is defined as more than 31 and less than 191 contiguous characters which are part of the set of valid base58 characters as defined by the multihash format.

7.3 Recently Put Blocks

Any block stored (or would have been stored had it not existed already) by any operation, including those done by the server (e.g. a name-append block split), is considered to be referenced by a name association for the 256 seconds following that storage event.

8 Permissions

Edsu permissions are layered, though not all layers are involved for each action. Before a requested action is performed, each relevant layer is evaluated, and the permissions are logically ANDed together; i.e. permission must be given at all levels before the action is permitted.

8.1 Name Prefix Layer

The first four characters of any name must be one of: pub. grp. prv. Actions involving a name which starts with pub. will always receive permission from this layer. Actions involving a name which starts with grp. will recieve permission from this layer if the connection has a user-from associated with it; i.e. some form of authentication has succeeded, and therefore it is not an anonymous connection. Actions involving a name which starts with prv. will recieve permission from this layer only if the connection's user-to and user-from are equal.

8.2 Block Layer

Special blocks can have keys which enact this layer when actions involving them are requested.

8.2.1 Name Block

The keys "edsu:deny" and "edsu:allow", if present in a name block, are enacted for any action which would reveal that the hash of that name block is associated with any given name (e.g. name-get, sub-notify, and the name listing service). If the key "edsu:deny" exists, and the connection's user-from is part of the group it defines, permission is denied at this layer. If the key "edsu:allow" exists, and the connection's user-from is not part of the group it defines, permission is denied at this layer. The above two permissions are logically ANDed together.

8.2.2 Appendable Block

The keys "edsu:append-deny", "edsu:append-allow", and "edsu:append-allow- anonymous", when present, are enacted when the name-append operation is attempted. They are orthogonal to the name block permission keys. If the key "edsu:append-deny" exists, and the connection's user-from is part of the group it defines, permission is denied at this layer. If the key "edsu:append-allow" exists, and the connection's user-from is not part of the group it defines, permission is denied at this layer. If the key "edsu:append-allow-anonymous" exists, its value is "true", and the connection is anonymous (i.e. no authentication was attempted or has succeeded), permission is granted. The permission at this layer is in the form of ((deny AND allow) OR allow- anonymous), where deny, allow, and allow-anonymous are the permission outcomes of the previous three paragraphs respectively.

8.3 Capabilities Layer

When a connection is authenticated, a capabilities block, chosen by the server as part of its authentication scheme, is interpreted and its capabilities are assigned to the connection for its duration. The capabilities block is a special block, with the following definition:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ edsu:version ┃ version ┃ ☑ ┃ ┃ edsu:name-get ┃ list-of-name-patterns ┃ ┃ ┃ edsu:name-append ┃ list-of-name-patterns ┃ ┃ ┃ edsu:name-put ┃ list-of-name-patterns-and-put-opts ┃ ┃ ┃ edsu:visiting-name-get ┃ list-of-name-patterns ┃ ┃ ┃ edsu:visiting-name-append ┃ list-of-name-patterns ┃ ┃ ┃ edsu:omni ┃ bool ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━┛
Where "edsu:version" must be "0.1", list-of-name-patterns is an ESON list of name patterns, and name-pattern-and-put-opts consists of a list of values consisting of a name pattern followed by zero, one, or both of the following (if both, the order is undefined):
┏━━━━━━━┳━━━━━━━━━━━━━┓ ┃ space ┃ destructive ┃ ┗━━━━━━━┻━━━━━━━━━━━━━┛
┏━━━━━━━┳━━━━━━━━━━━┓ ┃ space ┃ visitable ┃ ┗━━━━━━━┻━━━━━━━━━━━┛
Where space is ASCII 0x20, and "destructive" and "visitable" are the ASCII representations of those words. These are called options. In the context of capabilities blocks, a name pattern is considered to match a name if the two become identical when the final segment (and only the final segment) of the name is replaced by a "*". Each complete sequence of characters representing a name pattern (as distinct from its semantic meaning) must appear no more than once in each list, otherwise the capabilities block must be rejected as invalid.

8.3.1 Edsu:name-get

Any action which would reveal the hash associated with a name (or the existance of that association) only receives permission from this layer if the name matches at least one of the name patterns found in this list.

8.3.2 Edsu:name-append

A name-append message only receives permission from this layer if the name in the message matches at least one of the name patterns found in this list.

8.3.3 Edsu:name-put

To receive permission from this layer, a name-put message must match one or more of the values in this list. To match a value, the name in the message must match thats value's name pattern, as well its options. If the name-put operation is destructive, the "destructive" option must not be present for the value to match. A name-put operation is considered destructive if it will potentially expose blocks to deletion by the garbage collector which otherwise might not be (either because the hash key is omitted from the name-put message, or the introduction or amendment of "edsu:once", "edsu:expires", or "edsu:appended-ttl" keys), and if the proposed new name block does not contain a valid "edsu:previous" key. If the name-put operation has an impact on the effective permissions of a visitor in relation to the name, the "visitable" option must be present. Impact on the effective permissions means that the new name block must have identical non-existance or values for "edsu:deny", "edsu:allow", "edsu:append- deny", "edsu:append-allow", "edsu:append-rate-limit", and "edsu:append-bytes- max" as the name block the action would replace (or in the case of a new name, must not contain any of these keys).

8.3.4 Edsu:visiting-name-get

This is identical in function to edsu:name-get, except that edsu:name-get applies only to connections authenticated as owner, and edsu:visiting-name-get applies only to connections authenticated as visitor.

8.3.5 Edsu:visiting-name-append

This is identical in function to edsu:name-append, except that edsu:name-append applies only to connections authenticated as owner, and edsu:visiting-name- append applies only to connections authenticated as visitor.

8.3.6 Edsu:omni

The "edsu:omni" key being "true" will cause all actions to be evaluated as permitted at this layer.

8.3.7 Derived Permissions

If the lists for the keys "edsu:name-append" or "edsu:name-put" are not empty, or if "edsu:omni" is true, the connection is permitted to put blocks.

8.4 Services layer

Services, denoted by a second segment of "srv" in names used to interact with them, may institute their own permissions layer, as detailed in their specifications.

9 Block Gets

If the hash is possessed by the client, regardless of the authentication method or capabilities of their connection, they can request the matching block (e.g. via the block-get message or as the result of a chain).

10 Services

Unless otherwise noted, services accessed using a name-put will generate a new ESON block containing the detailed keys, and create or update a name association. This will be referred to as the "return block" and "return name" in this section. The return block may contain the "edsu:once" key with a value of "true" and an "edsu:expires" key with a value of not less than 256 seconds after the block is created. The return name will be the request name with an additional segment, that segment being an echo of the "hash" key in the initiating name-put message.

10.1 prv.srv.edsu.time.unix

A name-get for this name will result in the hash which resolves to a name block as follows:
┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━━━━━╋━━━━━━━━━━╋━━━━━━━━━━┫ ┃ edsu:time ┃ u64 ┃ ☑ ┃ ┗━━━━━━━━━━━━━━━━━┻━━━━━━━━━━┻━━━━━━━━━━┛
Where edsu:time is the server time in number of seconds elapsed since the Unix epoch.

10.2 pub.srv.edsu.authentication.visitors.capabilities.*

This service is described in the connection initiation section of this specification.

10.3 pub.srv.edsu.listings.blocks

A name-put to this address will cause the server to interpret the block referenced in that message as a metablock. The server generates a new metablock containing the hashes of all blocks referenced in the client's metablock which the server does not have stored - this is the return block. This service is the semantic equivalent of the client sending a block-get for each hash and compiling a list of hashes for which the server responds with a "not-found" oob.

10.4 pub.srv.edsu.listings.names

A name-put to this address will cause the server to interpret the block referenced in the message as follows:
┏━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━╋━━━━━━╋━━━━━━━━━━┫ ┃ edsu:after ┃ name ┃ ┃ ┃ edsu:prefix ┃ name ┃ ┃ ┗━━━━━━━━━━━━━┻━━━━━━┻━━━━━━━━━━┛
The server will create a list of all name associations contained in the user- to's account and sort them in ascending order according to the numeric ASCII values of the characters in the name. Further, for a name to appear in this listing, it must be named (either directly or using a wildcard) in the "edsu:name-get" value list of the capabilities block for the connection. I.e. the permission rules for listing and name- getting are similar, but not identical; they differ in that the rule stating that anything in the pub.* namespace is always readable does not apply in the case of listing. Otherwise they are the same. If the key "edsu:after" is present, all names from the beginning of the list up to and including the name specified will be discarded. If the key "edsu:prefix" is present, all names which do not have an identical initial segments as the specified name will be discarded. A name matching the specified name exactly is interpreted as having the same initial segments. A return block with the key "edsu:names" pointing to the remaining list of names will be created by the server. Alternatively the server may (because it would exceed the maximum size of a valid Edsu block or for any other reason) include only an initial portion of that list, in which case it must also include a key "edsu:truncated" with the value "true".

10.5 pub.srv.edsu.host.contact

This retrieves the contact information of whoever is hosting the Edsu account. There are currently two optional keys, each pointing to a single value: edsu:email, and edsu:www. These point to the email address and web site of the host, respectively, and are formatted according to their respective requirements.

10.6 prv.srv.edsu.authentication.tokens.generate.*

This service generates either an owner or a visitor token. To use it, a name is first generated by appending either "owner" or "visitor" to "prv.srv.edsu.authentication.tokens.generate.". This name is then used in a name-put operation, using the hash of an ESON block with the following structure:
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━━━┫ ┃ edsu:capabilities ┃ multihash ┃ ☑ ┃ ┃ edsu:label ┃ utf8 ┃ ☑ ┃ ┃ edsu:ttl ┃ u64 ┃ ┃ ┃ edsu:once ┃ bool ┃ ┃ ┃ edsu:request-id ┃ value ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━┛
Where "edsu:capabilities" is the hash of a previously-stored capabilities block; "edsu:label" is an ESON-encoded UTF-8, human-readable string which can help the client identify the purpose of the token later (e.g. for removal); "edsu:ttl" (Time To Live), when present, sets the token to expire a given number of seconds after it is created (or never, if omitted); "edsu:once", if set to "true", will cause the token to be single-use; and "edsu:request-id" is machine-readble ID which can be used to group related tokens together - this can be any value, but must be unique to the request (e.g. a large randomized value). This will generate either a token, or produce an OOB error. In the former case, the response can be read by appending the multihash of the above ESON block as a segment to the name used in the request, and then using that in a name-get. This will reference a block in the form:
┏━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━╋━━━━━━━╋━━━━━━━━━━┫ ┃ edsu:ok ┃ bool ┃ ☑ ┃ ┃ edsu:token ┃ token ┃ ☑ ┃ ┗━━━━━━━━━━━━┻━━━━━━━┻━━━━━━━━━━┛
Where "edsu:ok" will be "true", and where the token value can be used in the hello operation as described in that section.

10.7 prv.srv.edsu.authentication.tokens.delete.*

This service deletes either an owner or a visitor token. To use it, a name is first generated by appending either "owner" or "visitor" to "prv.srv.edsu.authentication.tokens.delete.", and then further appending the token itself as another segment. In the case of a visitor token, note that the token to be used is the original, i.e. what was returned from the token generate service. This name is then used in a name-get operation, which will either produce an OOB error, or delete the token and return the hash of the following block:
┏━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┓ ┃ key ┃ type ┃ required ┃ ┣━━━━━━━━━━━━╋━━━━━━━╋━━━━━━━━━━┫ ┃ edsu:ok ┃ bool ┃ ☑ ┃ ┗━━━━━━━━━━━━┻━━━━━━━┻━━━━━━━━━━┛
Where the value of "edsu:ok" is "true". This result happens whether the token in question exists or not - semantically its meaning is that the token no longer exists.

10.8 *.srv.ext.*

Servers may introduce services not listed in this specification, but all names must fit the pattern of one of: *.srv.ext.app.* *.srv.ext.std.* Where in each pattern the initial asterisk is replaced by one of pub, grp, and prv, and the trailing asterisk is replaced by one or more segments of the server's choosing. By convention, the first, "app", pattern's use is unrestricted, while the second, "std", pattern should be used according to some published specification.