root.system / 0x0F / system

One machine becomes many.

Every previous topic lived inside one machine. Binary, gates, CPU, memory, pointers, arrays, linked lists, hashing, the operating system. One isolated computer. The moment two computers connect, computer science changes completely. Networking is the discipline of making messages survive the trip across copper and fiber, across continents, across machines that do not trust each other. Every previous topic shows up here, in some new disguise.

Beginner// level 01

Two machines, one conversation

Imagine two computers. One in Tokyo. One in London. The Tokyo machine wants to send the bytes "hello" to the London one. Simple, right? It is not. The instant the message leaves the first machine, the universe becomes chaos:

  • How does the sender even find the receiver, across billions of other machines?
  • How does the receiver know the message is for it?
  • What if the message is corrupted in flight by a noisy cable or a faulty switch?
  • What if only half of it arrives?
  • What if the chunks arrive out of order?
  • What if someone intercepts it on the way?
  • What if one of the machines lies about who it is?

Networking is the body of agreements, code, and hardware that answers these questions, end to end. The wonderful thing is that almost every answer is built from concepts you have already seen.

Step one: every machine gets a number

The first job networking does is to give every connected machine an address. An IP address. Like 192.168.1.1. That looks like four small numbers separated by dots, but it is just one number, written in a friendly way. The same address in binary is:

11000000.10101000.00000001.00000001

Thirty-two bits. Four bytes. One machine on a network. The dots are for humans; the wire only sees the bits. The binary page already covered numbers; an IP address is one of those numbers, used to identify a place rather than a quantity.

There are now billions of these addresses in use, on phones, laptops, servers, routers, satellites, IoT toasters, Bitcoin nodes. IPv4 (32 bits, ~4 billion addresses) ran out of headroom; IPv6 (128 bits, more addresses than there are atoms in everything you can see) is taking over.

// connect back to the binary page
An IP address is the binary page's "numbers are just numbers" point, applied to identity instead of arithmetic. 192.168.1.1 and 11000000.10101000.00000001.00000001 and 0xC0A80101 are the same 32-bit integer written three ways. The dotted form is friendly notation, nothing more.

Step two: messages are split into packets

Long messages do not travel whole. The operating system on the sending side splits the data into small chunks called packets. Each packet carries a header full of metadata (where it came from, where it is going, which packet it is in the sequence) and a small chunk of the original payload.

TCP/IP PACKETtiny chunk of binary, addressable and routable on its ownHEADER (metadata)source IP32 bitsdest IP32 bitssource port16 bitsdest port16 bitsseq num32 bitsack num32 bitsPAYLOAD (the actual data)bytes (up to ~1460): HTTP, JSON, image, Bitcoin tx, anything

The browser does not send web pages. It sends packets. Your messaging app does not send messages, it sends packets. Netflix is packets. Spotify is packets. A Bitcoin transaction is packets. Every byte of every protocol you have ever used was, on the wire, a stream of these tiny binary chunks.

// connect back to the arrays page
A packet's payload is a fixed-size byte array, exactly the structure from the arrays page. The header is a struct of integers (IP addresses, port numbers, sequence numbers), laid out contiguously the way the variables page described. From the wire's point of view, everything is just bytes; the meaning is the protocol's job.

Step three: build a connection in code

The two snippets below open a TCP connection to a web server, send an HTTP request, read the response, and close. Notice how little you have to know about packets, addresses, or routing to do this. The OS does all of it for you.

Rust• • •
// A TCP client in Rust. Connect to a server, send bytes, read a reply.
// std::net::TcpStream is the "socket" the OS hands you; behind it,
// the kernel is doing the handshake, retransmits, reordering, the lot.
use std::io::{Read, Write};
use std::net::TcpStream;

fn main() -> std::io::Result<()> {
    // Open a connection. Under the hood: DNS lookup, three-way handshake,
    // a kernel-side socket file descriptor returned to us.
    let mut sock = TcpStream::connect("example.com:80")?;

    // Write some bytes. To us, .write_all() is a function call.
    // To the OS, it's: copy into the send buffer, segment into packets,
    // attach TCP and IP headers, hand to the NIC.
    sock.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")?;

    // Read until the server closes the stream. Each read may return less
    // than asked: TCP is a byte stream, not a message stream.
    let mut response = Vec::new();
    sock.read_to_end(&mut response)?;

    // The bytes that just arrived crossed the planet, were broken into
    // packets, took unknown routes, possibly arrived out of order, and
    // were reassembled into the exact sequence you see in this Vec.
    println!("{}", String::from_utf8_lossy(&response));
    Ok(())
}
C• • •
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void) {
    // 1. Ask the OS for a socket: a kernel data structure representing
    //    one endpoint of a future connection. Returns a small integer
    //    (a file descriptor) you treat like a file from now on.
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    // 2. Where are we connecting to? Build the destination address.
    struct sockaddr_in dest = { 0 };
    dest.sin_family = AF_INET;
    dest.sin_port   = htons(80);                 // port 80, network byte order
    inet_pton(AF_INET, "93.184.216.34", &dest.sin_addr); // example.com

    // 3. connect() triggers the TCP 3-way handshake under the hood.
    //    Blocks until the server agrees (or times out).
    connect(sock, (struct sockaddr*)&dest, sizeof dest);

    // 4. send() and recv(): from here on the socket is just a stream.
    //    The kernel handles packetisation, retransmission, ordering.
    const char *req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
    send(sock, req, strlen(req), 0);

    char buf[4096];
    ssize_t n = recv(sock, buf, sizeof buf - 1, 0);
    buf[n] = '\0';
    printf("%s", buf);

    close(sock);
    return 0;
}
// connect back to the pointers page
That sock is, in C, just a small integer (a file descriptor). The kernel hands you a number that indexes into one of its own internal tables, and you use the number to read and write to a network connection the same way you would read and write to a file. From your program's view, it's a handle. From the kernel's view, the handle points at a data structure full of buffers, sequence numbers, retransmit timers, and routing state. The pointers page would call it an opaque reference; the OS would call it the public face of a socket.
Intermediate// level 02

TCP/IP: how machines agree to talk

The internet is not one network. It is a network of networks, glued together by an agreement that everyone follows: TCP/IP. The ASCII page covered another agreement (the one that says A = 65); TCP/IP is the same kind of thing, several orders of magnitude bigger. It is the set of rules for how machines find each other, address messages, split them apart, reassemble them, and recover when something fails.

A network of networks

THE INTERNET IS A NETWORK OF NETWORKSTokyoclientFrankfurtRAmsterdamRSingaporeRNew YorkRLondonserverpackets find their own paths; routers make forwarding decisions billions of times per second

Between your laptop in Tokyo and a server in London sits a long chain of routers, each one a small specialised computer whose entire job is to look at incoming packets and decide where to forward them next. The decision is local: "is this packet's destination one of my directly connected networks? No? Then which of my neighbours is most likely to get it closer to where it is going?"

The remarkable thing is that no single router knows the whole map. They cooperate by sharing routing tables with their neighbours, and the global behaviour emerges. The internet is not a cloud or a magic substrate. It is millions of these little forwarding decisions, billions of times per second, all the way down.

// connect back to the CPU and logic-gates pages
Every router is a computer, with its own CPU (often a specialised network-processing unit), its own RAM, its own packet-forwarding engine built from logic gates. Some of them are commodity Linux boxes; some are custom silicon that forwards 10 terabits per second per chip. Either way, the same fetch-decode-execute story from the CPU page is running on each one, just decoding "forward packet" instead of "add two numbers".

Reliable byte streams on top of unreliable packets

IP (the bottom half of TCP/IP) delivers individual packets. It makes no promises. Packets can be dropped, duplicated, reordered, corrupted. The internet is, fundamentally, a best-effort packet delivery service. That is the whole guarantee.

TCP (the top half) is what turns that mess into a reliable, ordered byte stream. It adds three things on top of IP:

  • Sequence numbers on every packet, so the receiver can put them back in order.
  • Acknowledgements, so the sender knows what arrived and what didn't.
  • Retransmission, so anything that gets lost is sent again, automatically, without your application ever knowing.
SENT - in orderpkt 1pkt 2pkt 3pkt 4the network shuffles them: different routes, different latenciesARRIVED -out of orderpkt 2pkt 4pkt 1pkt 3TCP sorts by sequence number, asks for anything missing, hands ordered bytes up to your appif pkt 3 never arrives, the receiver notices the gap and asks for it again, automatically

If packet 3 in the diagram above never arrives, the receiver notices the gap (it has packets 1, 2, 4 but no 3) and asks the sender to send packet 3 again. The application reading the socket sees one continuous stream of bytes. It does not see the retries, the reordering, the duplicates, or the gaps. That's the whole magic of TCP: it hands your program an illusion of a perfect pipe, on top of an actual network that is messy and lossy.

Before any of that: the handshake

TCP THREE-WAY HANDSHAKECLIENTSERVERSYNseq = xSYN-ACKseq = y, ack = x+1ACKack = y+1three messages to agree on starting sequence numbers, then bytes can flow reliably both ways

A TCP connection is opened with the famous three-way handshake: SYN from the client, SYN-ACK back from the server, ACK from the client. The point of these three messages is to agree on starting sequence numbers in both directions and to confirm that both ends can actually hear each other. After that, the byte stream is open and either side can write.

Closing is the same shape in reverse, with FIN and ACK. None of this is your application's problem. TcpStream::connect and connect() are wrappers that run the handshake before they return.

Look at what's actually on the wire

The C struct below is a simplified view of the IP and TCP headers that get prepended to every payload you send. Every write() on a socket walks through code like this inside the kernel.

Rust• • •
// The matching server. Accept connections in a loop, echo back the bytes.
// Notice: every accepted socket is just another stream you read/write.
use std::io::{Read, Write};
use std::net::TcpListener;

fn main() -> std::io::Result<()> {
    // bind() reserves a port; listen() tells the kernel to queue
    // incoming connections; both happen inside ::bind().
    let listener = TcpListener::bind("0.0.0.0:7878")?;

    for incoming in listener.incoming() {
        let mut sock = incoming?;
        let mut buf = [0u8; 1024];

        // accept() returned a fresh socket for this connection.
        // The kernel completed the handshake before we ever woke up.
        let n = sock.read(&mut buf)?;
        sock.write_all(&buf[..n])?;       // echo the bytes back
        // Dropping sock closes the connection (sends FIN, frees the fd).
    }
    Ok(())
}
C• • •
#include <stdint.h>

// A simplified view of what's actually on the wire when you send data.
// Every layer adds its own header before the payload of the layer above.
struct IpHeader {
    uint8_t  version_and_ihl;     // IPv4, header length
    uint8_t  type_of_service;
    uint16_t total_length;
    uint16_t identification;
    uint16_t flags_and_fragment;
    uint8_t  ttl;                 // time to live; routers decrement it
    uint8_t  protocol;            // 6 = TCP, 17 = UDP
    uint16_t header_checksum;
    uint32_t source_ip;           // the binary form of 192.168.1.1
    uint32_t dest_ip;
};

struct TcpHeader {
    uint16_t source_port;
    uint16_t dest_port;
    uint32_t sequence_number;     // for ordering and gap detection
    uint32_t ack_number;          // "I've received everything up to here"
    uint16_t data_offset_and_flags;
    uint16_t window_size;
    uint16_t checksum;
    uint16_t urgent_pointer;
};

// On the wire: [ IpHeader ][ TcpHeader ][ payload (your bytes) ]
//
// The OS builds this for you on every write(). The NIC turns it into
// electrical or optical pulses. The other side's NIC turns those pulses
// back into bytes. Its OS strips off the headers and hands your bytes
// up the stack to the receiving application.
// connect back to the memory page
Where do packets live while they're waiting to be sent or processed? In RAM. The OS allocates kernel-side packet buffers (often called sk_buffs on Linux), each one a small heap block holding the bytes plus a doubly linked list pointer so it can be queued and dequeued in O(1). When you write to a socket, your bytes are copied into the send buffer; when bytes arrive from the wire, they wait in the receive buffer until your program reads them. Too many packets arriving too fast? They queue. A traffic jam made of memory pages.
Advanced// level 03

Every previous topic, showing up at every layer

The whole stack, with everything labelled

The point of this page, more than any of the others, is that nothing here is new. Networking is the moment where every previous topic on the site lights up at once. Walk down a single packet from your laptop to a remote server, and you are walking through every page you have already read:

binary
Pulses on a wire
Every packet is binary. Voltages on copper, photons on glass, modulated radio on wifi. The binary page covered how numbers are encoded in bits; networking is that encoding moving through physical media at near the speed of light.
ASCII / agreements
HTTP, DNS, JSON
Most application-level protocols are ASCII (or UTF-8) on top of TCP. <code>GET /index.html HTTP/1.1</code> is human-readable text, agreed on by everyone, turned into bytes, sent across the world. ASCII said <code>A = 65</code>; HTTP says the line ending is <code>\\r\\n</code>.
logic gates
Routers in silicon
Every router and switch is a computer, every computer is logic gates, every gate is a transistor. The fastest core-network switches forward packets in single-digit nanoseconds because their forwarding tables are baked into ASICs.
CPU
Two CPUs, in parallel
Your network card has its own processor (a NIC) doing checksum offload, segmentation, sometimes encryption. While your main CPU runs your application, the NIC is handling packets in parallel, freeing the cores for application work.
memory
Packet buffers
Send buffers, receive buffers, socket queues, retransmit queues. All in kernel RAM. When the network is slow and your app keeps writing, the send buffer fills up; eventually the OS makes <code>write()</code> block until the buffer drains. Backpressure made of memory pressure.
arrays
Payloads are bytes
Every packet payload is a fixed-size byte array, the structure from the arrays page. Receive buffers are ring buffers, also arrays. The whole network stack moves arrays of bytes from one place to another.
linked lists
Packet queues
Inside the kernel, packets typically sit in doubly linked lists: one for the receive queue, one for the send queue, one for the retransmit queue, one for each socket's pending data. The linked-list page covered why: O(1) enqueue and dequeue, no copying, no shifting.
pointers
Sockets and FDs
A socket is, from your program's side, a number; from the kernel's side, a pointer at a struct. The pointers page's <em>a number that means somewhere else</em> is exactly the relationship between a file descriptor and the buffers, sequence numbers, and connection state it stands for.
hashing
HTTPS, signatures, tokens
Every <code>https://</code> URL relies on the hashing page. TLS uses cryptographic hashes for certificate verification, for the integrity check on every record, for the key derivation. JWTs, API signatures, session cookies, all hashes underneath. Without hashing, the open internet is unauthenticated and unsafe.
operating system
Owns the network stack
The OS page covered syscalls and the kernel boundary. Sockets, ports, packet buffers, retries, timeouts, routing tables, ARP caches: all kernel state. Your application barely sees any of it. <code>send()</code> and <code>recv()</code> are syscalls, and on the other side of that syscall is the entire stack.

HTTP, and why it's still mostly text

Rust• • •
// HTTP is just ASCII on top of TCP. Bytes are bytes; the protocol is
// a *convention* about what those bytes mean. Parse a request line:
fn parse_request_line(bytes: &[u8]) -> Option<(&str, &str)> {
    // Bytes -> str. Validates UTF-8 (HTTP request lines are ASCII, but
    // headers may contain UTF-8 in modern HTTP).
    let line = std::str::from_utf8(bytes.split(|&b| b == b'\r').next()?).ok()?;

    // "GET /index.html HTTP/1.1" -> ("GET", "/index.html")
    let mut parts = line.split(' ');
    let method = parts.next()?;
    let path   = parts.next()?;
    Some((method, path))
}

// HTTPS is the same thing, but TLS wraps the byte stream first.
// TLS does three jobs: identity (certificate, verified by cryptographic
// hash), confidentiality (symmetric encryption), and integrity (MAC).
// All three are built out of the hashing primitives from the hashing page.
C• • •
// HTTP is just ASCII on top of TCP. Bytes are bytes; the protocol is
// a *convention* about what those bytes mean. Parse a request line:
fn parse_request_line(bytes: &[u8]) -> Option<(&str, &str)> {
    // Bytes -> str. Validates UTF-8 (HTTP request lines are ASCII, but
    // headers may contain UTF-8 in modern HTTP).
    let line = std::str::from_utf8(bytes.split(|&b| b == b'\r').next()?).ok()?;

    // "GET /index.html HTTP/1.1" -> ("GET", "/index.html")
    let mut parts = line.split(' ');
    let method = parts.next()?;
    let path   = parts.next()?;
    Some((method, path))
}

// HTTPS is the same thing, but TLS wraps the byte stream first.
// TLS does three jobs: identity (certificate, verified by cryptographic
// hash), confidentiality (symmetric encryption), and integrity (MAC).
// All three are built out of the hashing primitives from the hashing page.

HTTP is a deliberately simple convention on top of TCP. A request line, some headers, a blank line, a body. Plain bytes. You can talk HTTP by hand with telnet or nc. The reason the web took off in the 1990s is that this protocol is straightforward enough for a human to read on a terminal, simple enough for a beginner to implement in an afternoon, and rich enough to carry anything you want on top.

HTTPS is the same thing, with TLS wrapping the byte stream first. TLS does three jobs: identity (the server proves who it is, using a certificate verified by cryptographic hash), confidentiality (the bytes are symmetrically encrypted in flight), and integrity (a MAC catches any tampering). All three jobs are built out of the hashing primitives from the hashing page.

The convergence: blockchain as a networking story

This is where the whole site comes together. Bitcoin is not, fundamentally, a currency. It is a networking system: thousands of mutually distrusting machines, scattered across the planet, somehow agreeing on the same history of transactions.

Every Bitcoin node is a computer on the open internet. They find each other by IP, talk over TCP, and exchange messages using a documented protocol on top. When you broadcast a transaction, your node sends a packet to every peer it knows about. Each of those peers verifies it (signatures, hashes, balances) and forwards it to their peers. The transaction spreads across the network the same way a rumour spreads through a crowded room. This is called gossip.

GOSSIP PROTOCOL: every node forwards to every peerN1N2N3N4N5N6N7N8originatorno central coordinator: every node forwards every new tx to every connected peerwithin seconds the whole network has the same transaction in its mempoolevery receiving node verifies signatures and hashes before forwarding; nobody trusts anybody

Within a few seconds, every node in the network has the new transaction in its pending pool (its "mempool"). No central server. No coordinator. No trust between nodes. The protocol just says: forward what you see, after you verify it.

Then comes the agreement step. Miners assemble valid transactions into a candidate block. They race to find a nonce that makes the block's hash come out with enough leading zeros (the hashing page's proof-of-work). The first one to succeed broadcasts the block via the same gossip protocol. Every other node receives it, verifies the hash, verifies that every transaction in it is valid, verifies that the block's prev_hash matches the tip of the chain they already have, and if it all checks out, adds the block to their own copy of the chain.

Notice that nobody trusts anybody, and yet, within minutes, every node in the world agrees on the same chain. That is networking, hashing, and proof-of-work doing their jobs together. The agreement is not social; it is mathematical.

// the whole site, in one paragraph
Transistors switch on and off. Logic gates compose them into adders and registers. Adders and registers compose into a CPU. The CPU runs the operating system. The operating system manages memory and sockets. Sockets carry packets across networks. Networks span the planet. Hashing secures the packets. And on top of all of it, blockchains turn the network itself into a machine for agreement, where mutually distrusting computers converge on the same truth not because they chose to, but because the mathematics gave them no other choice. That is the full ladder, top to bottom, no gaps.

Where to dig in next

Networking has decades of depth. If any of this lit you up:

  • BGP. The routing protocol that holds the public internet together. ASes, peering, route leaks, the entire political economy of who can reach whom.
  • QUIC and HTTP/3. The newer generation of transport, running over UDP, multiplexing streams, cutting handshake latency. Built into every modern browser.
  • TLS 1.3 in detail. Modern cryptography, modern key exchange, modern forward secrecy. Read the RFC; it is unusually readable.
  • libp2p. The networking stack used by IPFS and Ethereum, generalising the lessons of BitTorrent and Bitcoin into a reusable peer-to-peer toolkit.
  • DPDK and io_uring. How modern servers move packets at line rate by bypassing the traditional kernel network stack.
  • Tigerbeetle, ScyllaDB, and friends. Databases built around the realities of modern networking: zero-copy I/O, NUMA awareness, lock-free queues. The systems where the whole stack from this site is in play simultaneously.
next up / 0x10
No single machine knows everything. Many machines, no boss, eventual agreement.
distributed systems