
Architecture
There three base architectural principles on which Æthernet is built on:
No handshakes protocol. After a client is registered then a request to a server is performed with just a single message containing all required information for encryption, client authentication and the request data itself.
Multiple simultaneous end-points. A client is able to make requests to any of the servers from the client’s cloud at any time including simultaneous requests.
Thick client allows to greatly improve the connectivity of the client - the client can make a decision about the connectivity problems known only on the client’s side.
Message delivery latency
A typical message broker can provide minimum message delivery latency when the Internet connection is stable. High latency or even no ability to be connected are coming when something went wrong:
Client-side connection with high rate of packet loss. The connection to the broker may not be established because losing any packet requires the connection process to be started from the beginning. If many hand-shakes are required for the connection then it is almost impossible to reach the end of the process.
Æthernet uses a “no hand-shakes” / “no round-trips” approach.
If a cloud server replies slowly or is not accessible then the client closes the connection and tries accessing another end-point. It involves seconds of delay.
Æthernet is designed to accept multiple simultaneous duplicated requests. It allows the client to issue the duplicated request to another server if the first server delays the reply for just several milliseconds on top of the ping time.
Identity
All Æthernet clients are identified with the 16 bytes unique identifier (UID) assigned to them at the client registration or self-provisioning process. The UID is a permanent non-public, non-secret number all requests to Æther are authenticated with. The UID is used as an address for sending messages and managing the client by the application server. A new client performs registration under the parent client (aka application server) also addressed by UID.
Client-server architecture
Æthernet is a cloud of independent servers (not connecting to each other) distributed across the globe. Servers cache data from the central database. Every client has a personal cloud which is a subset of the whole Æthernet cloud. A client is able to make requests to servers of this cloud at any rate, one-by-one or simultaneously (including multiple simultaneous requests to a server) depending on use-case. Æthernet constructs the cloud in a way to fit a client's needs: minimizing latency if everything is ok with the connection or when something went wrong. A cloud can be changed at any time. The first entry in the list describing cloud is the closest server on which the client is landed to provide minimum message delivery latency.
Application server
Application server is a regular Æthernet client controlled by a developer that manages child clients: allocates quotas, allows to receive messages from other clients, blocks/deletes clients. In contrast, other back-ends use separated REST/gRPC API to manage clients. Any Æthernet client is managed by the parent. The application server is notified when a new child client is registered or the allocated quota is almost reached. Application server implements just business logic of the application with no worries about infrastructure (scalability, balancing, accessibility, attack resilience, using public IP-addresses etc.) - Æthernet takes care of everything.
Æthernet is a tree structure with the root element - Æther. It manages all child clients at any depth in hierarchy. Dev is a helper application we host that allocates quotas for developers to be able to try the service. For example, when a user runs the chat example then a new client is created and registered. The chat application server (that we host) is notified with a client creation and allocates some quota that allows the client to send and receive messages. Also the Chat application server tells Æthernet that the client is allowed to receive messages from all other clients and all other clients can receive messages from the new one. When the client reaches the quota limit then the chat server deletes the client.
Users - is the namespace for the web-site user’s accounts.
Anonymous is where all self-provisioned applications are hosted.
A more complex hierarchy with Mobile, IoT and Infra nodes is shown above as an example
Admin is a node that is associated with the account on the Æthernet.io
Mobile – the application server that manages the smartphone application instances
IoT is a separate node that manages all IoT devices
Infra is a node for service functionality like storing data into the back-end database etc.
A client life-cycle
Client is the addressable entity of the Æthernet. A client always has a parent and can have multiple children.
There two separated clouds exist:
Registration cloud is resolved with register.aethernet.io and provides a list of servers that support registration API only a new client relies on to become permanently registered
Working cloud accepts registered clients requests only. IP-addresses of server’s end-points are passed during the registration process or can be obtained with cloud.Æthernet.io
Registering new client
First, a client becomes a client after registering in the Æthernet’s central database. At the end, the client receives:
There two separated clouds exist:
permanent unique identifier (UID). Non-public, non-secret (known to only necessary clients that interconnect with the client)
permanent master-key that is stored in the central database only and used for deriving keys for all servers in the Æthernet cloud.
A client’s cloud - a list of servers chosen by Æthernet to minimize response latency and maximize connectivity of the client.
The absolute minimum that represents a client is uid and master-key. Now, the client is able to send messages to Æthernet servers’ like pull-message, send message, request on-line status, request remaining quotas etc.
The registration process is often called “provisioning”, and “just-in-time provisioning” for self-registration.
Working cloud
A client makes connections to the client’s cloud and makes requests. A connection can be long-living or can be gracefully closed by the client or Æthernet server at any time. Pulling messages is performed as a heart-beat request. When connected, a client can receive an order from a server with a message: new message, result of the server’s request execution, a child client is registered, changing the cloud etc.
A client’s cloud can be re-configured by Æthernet at any time but only when the client is online. The purposes of re-configuring the cloud can be: load balancing, client moved closer to another Æthernet server, a server malfunctioning or is shutting down intentionally, a new server is launching.
Any interaction with the Æthernet servers is performed by sending messages which are spawned by Actions - an asynchronous operation of the Æther API.
Action
The action is the object that represents the asynchronous process that sends / receives data to the Æthernet cloud via messages.
A newly taken action is stored into the client’s action list. The action executes periodically. The action can perform a request to Æthernet by sending a message to servers of the registration and working clouds. The action is notified when a reply/request from the server is received. A request may contain several messages.
The action inspects messages and performs some operations:
changes Æther / client / cloud etc. states, for example, adding new servers into the cloud
releases themself or other actions
creates other actions
Message
A message is a set of parameters for a remote function executed asynchronously: a client pushes a message to the Æthernet cloud. The server can push a message or multiple messages to the client as a reply.
A message contains authentication information and encrypted data and requires no roundtrips.
Infrastructure
Enter a few basic parameters to estimate your monthly costs on Æthernet
For real-world usage scenarios check the pricing examples below
Protocol
Æthernet implements multilevel encapsulating binary protocol with versioning. The basic block of the protocol is Message with data that contains parameters of the remotely executed function on server, or server pushes a message to a client. The protocol uses a request-response schema or just sends a message with no reply from the other side. If a connection is established then the server can also send a message to the client - “push message”.
The protocol can use datagrams or streams as an underlying protocol for the Internet connection. Connect-oriented or connectionless protocols. The stream is divided into messages with size specified. A message can encapsulate other messages' data.
Message
The message always starts with a variable size identifier. The IDs are divided by namespaces. The root namespace is with what the remote side starts to deserialize the message. Once the message is deserialized completely (including all encapsulated messages) then another message starts deserialization. Messages are grouped into a packet.
Æthernet uses a limitation for a single packet size - 1500 bytes that is suitable for the most common internet routing with no fragmentation. A user can set a lower limit for C++ library to be able to use other hardware medium for transferring the information.
A particular message’s class instance is created when the message ID is taken from the stream. The message’s deserialization procedure comes with the data members to be initialized. When the message is deserialized then, if the message contains encapsulated messages, then the deserialization process continues with the data. The message can transform the data, for example, decrypt it. The most important part here is that the root messages' IDs are separated from other IDs namespaces. For example, #3 on the root namespace means libsodium encrypted message but #3 on the following namespace can be the NumMessages message.
A message can encapsulate multiple separated messages. Example structure:
0: LibsodiumEncrypted: nonce
1: Response: id
1: SetCloud: servers list
Debug information: timings
1: Compressed - a proxy message that redirects to another namespace
2: Zlib
1: Message: sender_id, text
1: Message: sender_id, text
1: Response: id
1: Online: last_timepoint_seen
Number is a namespace of the messages ID encoding. The root namespace (0) redirects the creation of next messages to the subsequent namespace. Compressed messages redirect to the selection of the compression algorithms and the Zlib message returns the stream decoder back to the namespace 1 where Messages come.
Message IDs namespaces
MessageID uses the variable size integer for encoding the message index to allow it to have more than 256 messages in a single namespace. But it’s difficult to reach because a number of namespaces can exist to extend the number of messages to be supported. RequestID is another great opportunity to reduce the number of IDs to be used.
Repeat message
Repeat messages don't use encryption. It is just a 4 bytes hash code and is extremely lightweight in terms of traffic and computational power.
If one side is going to send exactly the same message to another side then it uses the Repeat message that just says that the last message must be repeated. It uses Hash(Kdf(Meta::id, tx_key | Hash(Message))) with the Meta::id incremented for each message. The Hash(Message) is used to avoid repeated values when the Meta::id is looped. The other side does similar hash computation and, if matches, repeats the previous message and replies with the Repeat message if nothing changed or, replies with the regular message. If the hash code doesn’t match then drops the recorded message and does not reply because the 4 bytes code can be sent by the Man-in-the-Middle. Dropping a message is performed because an attacker can try to send a lot of 4 byte values trying to repeat the message but just a single incorrect value prohibits the Repeat message. The sender, once, not being replied sends the regular encrypted and authenticated message.
Versioning
The versioning of the protocol is implemented via adding new messages with message IDs. No altering of existing messages’ data allowed.