We already learned in the introductory article that Urbit has an architecture where galaxies and stars are responsible for peer discovery. In this article we will better understand how the Urbit network communicates.
What is Ames?
Ames is the name of the Urbit’s network. It is responsible for sending and receiving messages across the network, which are directed to and from ships. Ships include galaxies, stars, planets, and moons, each with different roles and responsibilities.
Ames handles all the network communication tasks such as encryption, decryption, routing, and congestion control. It also determines how to direct incoming packets to the correct ship or app.
The Ames network communicates through Arvo, the Urbit operating system, using a mechanism of passing or giving moves (instructions) to and from ships. The actual transfer of bytes in an Ames packet across a network is handled by the Urbit operating system’s runtime.
An operating system’s (OS) runtime is essentially the engine that executes and manages applications. It is responsible for resource allocation, scheduling tasks, input/output, and other system management tasks. In Urbit, Arvo is the operating system and it has a runtime that communicates with the outside world (operational systems or the internet). In the context of this text, the runtime is responsible for the actual transportation of packets across the network.
Ames also has the ability to send packets to specific addresses. This can be done either by sending packets to an identifier (opaque atom), or to the Urbit address of a galaxy. Let’s explain what these terms mean.
Urbit uses a hierarchical addressing scheme, where galaxies are top-tier identities (like the root of a tree). Each galaxy can have 256 stars, each star can have 65,536 planets, and planets can have moons. When Ames sends packets to a galaxy address, it’s like sending mail to a post office box: the packets are sent to that address, and then the galaxy routes it to the correct star, planet, or moon. The packet’s ultimate destination is encoded within the packet itself, and each level (galaxy, star, etc.) helps route the packet to the correct destination.
An atom is an unsigned integer, meaning it can represent any non-negative whole number. The system doesn’t inherently know what these numbers represent (for example, whether a certain atom represents a user ID, a timestamp, or something else entirely). “Opaque” here suggests that the atom doesn’t convey meaningful information on its own; it’s simply a unique identifier. Ames, the network protocol, uses this identifier to send packets to specific addresses.
Remember that Ames doesn’t send the packet directly to the destination. Instead, it hands off the packet to the Urbit runtime, and the runtime uses the information provided by Ames (like the galaxy address or opaque atom) to route the packet to the right ship (galaxy, star, planet, or moon) in the network.
In essence, Ames packs up the data and labels it with the right address (the opaque atom or the galaxy address). The Urbit runtime then handles the actual delivery of the packet to the specified address over the internet. This is analogous to packing up a box (the data), writing the correct address on it (the opaque atom or galaxy address), and then handing it off to a delivery service (the Urbit runtime) which ensures it reaches the correct location.
The message communication process
When it comes to message communication, the following process is used:
A local ship (the source) passes a message request, called a %plea, to Ames. Ames then transmits this message over the network to the destination ship’s Ames, which passes the message on to its destination.
Once the destination ship processes the %plea, it sends back an acknowledgment packet, an “ack“. This can either be positive (if the request was processed successfully) or negative (if the request failed), in which case it’s called a “nack“.
If the source ship receives either a positive ack or a combination of a nack and explanation, it completes the message request process.
A local ship can also send response messages, called %boon, in response to a %plea. Ames transmits a %boon over the network to the destination ship’s Ames, which gives it to the destination.
In case a %boon message is lost during the transfer, Ames sends a %lost message to the local ship, indicating the %boon was missed.
When it comes to ensuring security, Ames encrypts every message using a technique known as symmetric-key encryption. In the event of a continuity breach (a disruption in the system’s integrity), Ames removes all messaging state related to the affected peer.
Each Ames address is a 128-bit atom (unique identifier) that represents a user’s identity, tied to a public key for secure communication. Ames is capable of delivering messages of arbitrary length, broken into smaller packets suitable for transmission (MTU-sized fragments). All communication in Ames is end-to-end encrypted, ensuring secure peer-to-peer communication.
Packets of data
The actual data transmitted over the Ames network is handled in packets. Each packet has a specific format, including a header and a body, with various data fields. Ames splits messages into smaller packets if they exceed a certain size. To ensure packets are received in order and none is lost, Ames employs techniques similar to Transmission Control Protocol (TCP), retransmitting packets if needed.
Finally, Ames uses a system of acknowledgments (acks) and negative acknowledgments (nacks) to confirm the successful receipt or failure of packets or messages. Acknowledgments play a crucial role in ensuring smooth communication between different ships in the network.
Now let’s go into more detail.
Packet Relaying and Peer Discovery: This is about how ships (users in Urbit) find each other (peer discovery) and send messages (packet relaying). Initially, when two ships want to communicate, they might only know each other’s @p (Urbit ID), but they need the other ship’s IP address and port for a direct connection. An intermediary (usually a galaxy) that knows the IP and port of the receiving ship facilitates the initial communication. Over time, the galaxy’s role in peer discovery will be delegated to stars for their sponsored planets. Packet relaying is needed when two ships can’t communicate directly, often due to NAT or firewalls. In the case of moons, the parent ship is responsible for packet relaying and peer discovery.
In the context of the Ames network, packet relaying refers to the process where a third party (usually a galaxy) forwards messages between two ships (users). This is particularly necessary when direct communication between the two ships is not possible due to obstacles like NAT or firewalls. The relaying ship, aware of the IP addresses and port numbers of both communicating ships, acts as an intermediary, taking the packet from the sender, adjusting some details (like marking the packet as relayed, updating checksum etc.), and then sending it along to the receiver. This ensures that messages can be passed along even in situations where direct peer-to-peer communication cannot be established.
The Serf and the King: These are the two main components of Urbit’s functionality. The Serf is the Nock runtime (Nock is the low-level functional language Urbit is built on). It manages the current state of Arvo (the Urbit OS) and updates the state by processing instructions. The King handles snapshots of the Arvo state and manages inputs and outputs with Unix. The Serf only communicates with the King, while the King communicates with both the Serf and Unix.
Unix is an operating system. It has been widely used in various domains, including servers, workstations, mainframes, and embedded systems. It has also been the foundation for many other operating systems, such as Linux and macOS. When we say “The king communicates with Unix” that means “The king communicates with the operating system of the machine where Urbit is installed“.
Ames I/O submodule: This is a component of the King that is responsible for managing the communication of Ames packets. It wraps outgoing Ames packets into UDP packets, unwraps incoming UDP packets into Ames packets, and maintains an incoming packet queue.
In computing, I/O stands for input/output, which refers to any operation, program, or device that transfers data to or from a computer. Within Urbit’s architecture, the I/O submodule of the King is responsible for handling all Ames-related networking operations. It prepares outgoing Ames packets to be sent over the network, and it processes incoming network packets into Ames packets that can be understood by the Urbit system.
UDP stands for User Datagram Protocol, which is one of the core communication protocols used on the internet. Unlike TCP (Transmission Control Protocol), another common protocol, UDP is connectionless, meaning it does not require a persistent connection between two points for data transfer. This makes UDP suitable for situations where speed is preferred over reliability, as it doesn’t need to perform the checks and balances that TCP does to ensure every packet arrives and is in order. Ames uses UDP as the underlying protocol for its network communication. The I/O submodule wraps outgoing Ames packets as UDP packets for transmission, and unwraps incoming UDP packets into Ames packets for processing.
In the Ames protocol, the assumption is made that no node involuntarily loses state, which is called ‘permanent networking’. This differs from classical internet protocols such as TCP/IP, which lose state when rebooted because session state is in volatile RAM. In Ames, nodes maintain a permanent shared sequence number, allowing for exactly-once message delivery. This is not generally possible with classical internet protocols, which offer at-most-once or at-least-once message delivery guarantees.
In other words: on the traditional internet, when two computers are sending data to each other, a failure in the process requires the sending to be restarted. In Urbit, as everything happens permanently, what has already been transmitted is always maintained and a loss of connection with subsequent reestablishment results in a continuation of the process where it left off.
Fatal Brain Damage: The downside of permanent networking is that if a node is damaged and loses its persistent session numbers, it effectively loses its ability to communicate and cannot be easily recovered or recreated.
Persistence Latency: Another cost of permanent networking is the addition of persistence latency to the network roundtrip time. This is the time it takes for data to be stored permanently, adding to the total time a message takes to be sent and acknowledged.
Permanent Applications: Within Urbit, there are multiple persistent conversations happening, as the OS isn’t a monolithic codebase. Both kernel modules and user-level applications must adhere to the same permanence rules. There’s no correct way to remove and then reinstall an app. You can only upgrade the app.
End-to-End Acknowledgement: Ames uses end-to-end acknowledgements to simplify error handling and acknowledgement at different layers. There are two acknowledgment layers: messages and fragments. An acknowledgement implies successful receipt, decoding, and processing of a message.
To summarize, Urbit’s Ames protocol offers some unique features compared to traditional internet protocols, primarily revolving around the concept of ‘permanent networking’. This allows for more reliable communication between nodes, simplified error handling, and seamless application updates. However, these features come with the trade-offs of increased complexity in terms of persistence latency and irrevocable damage to a node in certain scenarios.