This document is a draft. Please be aware that it may change at any time, and some of it is probably on crack, insecure, and may kick your dog.
The on-disk data structure is stored within a self-contained portable MongoDB instance.
Collection | Type | Description |
hosts | capped | The cache of host IDs, address, and public key. Also includes the private and public keys you generate for each host. |
Allows for identity verification during re-connection to recent hosts. | ||
pow | capped | Proof-of-Work history to prevent re-use of work tokens. |
identities | normal | Your collection of public identities and private keys. |
TLS |
Negotiated Middleware |
Frame Chunking |
Messaging |
Application |
Negotiated middleware would include features such as zlib or lzma compression, etc.
The frame layer self-organizes available middleware between client and server and provides a rich, DoS attack-resistant, and asynchronous low-level protocol for bidirectional TCP communication between nodes.
Each frame may contain no more than 4 + 16 + 64 + 2^24 bytes (roughly 16MB, though few frames ever reach this size) with the following layout:
Length | Type | Name | Notes |
4 Bytes | UInt32 | Payload Size | The 8 highest-order bits should be random and are ignored. |
16 Bytes | UUID | Frame ID | This is to allow for the deferred message received/denied/corrupt messages. |
64 Bytes | SHA-512 | Payload Hash | This ensures data integrity of the bencoded payload. |
<2^24 Bytes | Bencode | Payload | Potentially multi-part; each message is decoded and parsed separately. |
The header signature is ignored (and should be padded with random data) during the key/feature negotiation phase of communication.
Middleware layers may add additional prefix headers (e.g. the “compressed or not” flag, anti-analysis pre/postfix padding) which are stripped before the frame layer handles the remaining packet.
A ‘hello’ packet is passed from server to client upon connection, and the client responds.
{ 'msg': 'system.hello', 'pubkey': '...', 'challenge': (20, UUID(...)), 'requires': { 'header.signature.sha': [256, 512] }, 'offers': { 'frame.compression': ['gzip', 'lzma'], 'frame.salting': [] } }
The challenge is a 2-tuple/list defining the required bits and cleartext UUID.
This is sent in response if all is well.
{ 'msg': 'system.hello:ok', 'pubkey': '...', 'response': '...', 'accept': { 'header.signature.sha': 512, 'frame.compression': 'lzma', 'frame.salting': [] } }
If the client is unable to meet the requirements, or itself has unmet requirements, communication is halted with the client disconnecting after sending this message. The requires
key indicates unmet requirements, the rejects
key indicates server requirements that can not be met.
{ 'msg': 'system.hello:error', 'requires': { 'header.signature.md5': [] }, 'rejects': { 'header.signature.sha': [256, 512] } }
The application layer defines a rich object-oriented RPC mechanism that allows for the proxy of bencode-able objects, the execution of public remote methods, etc. Other application layer interfaces can be defined for more explicit control on server and client. Each side of the connection has full introspection capabilities. Proxied objects are given UUIDs. Common messages include:
{ 'msg': 'rpc.construct', 'cls': 'Foo', 'args': [], 'kwargs': {} }
Returns:
{ 'msg': 'rpc.construct:ok', 'value': UUID('6b92fb58-ef99-4421-8d0e-3650932b3a1f'), 'dict': {} }
The UUID
is an object reference, with the actual object stored server-side. Client-side the object reference will proxy property and method requests across the network. Objects can optionally allow certain data to be cached locally, returned as dict
.
{ 'msg': 'rpc.call', 'oid': UUID('6b92fb58-ef99-4421-8d0e-3650932b3a1f'), 'method': 'hello', 'args': [], 'kwargs': {} }
Returns:
{ 'msg': 'rpc.call:ok', 'value': "Hello world!" }
{ 'msg': 'rpc.attribute.get', 'oid': UUID('6b92fb58-ef99-4421-8d0e-3650932b3a1f'), 'attribute': 'name' }
Returns:
{ 'msg': 'rpc.attribute.get:ok', 'value': "world" }
{ 'msg': 'rpc.attribute.set', 'oid': UUID('6b92fb58-ef99-4421-8d0e-3650932b3a1f'), 'attribute': 'name', 'value': 'Alice' }
Returns:
{ 'msg': 'rpc.attribute.set:ok' }