One box, thirty services, zero open ports
My home server runs the family photos, the movies, the AI, and my side projects — and its web surface has no port-forwards at all. An architecture tour of a homelab built to be stolen from.
Essays on engineering, life, and everything in between.
My home server runs the family photos, the movies, the AI, and my side projects — and its web surface has no port-forwards at all. An architecture tour of a homelab built to be stolen from.
There is no container object in Linux. There's a normal process that has been fenced in, with its own PID 1, its own root, its own resource budget. We run the real syscalls and watch the fence go up.
A docker pull is a handful of HTTP requests you can read with curl. A 401 that hands you the keys, a token, a fat manifest that picks your CPU, and blobs by digest. We read every one.
A container image has no magic in it. It's a few tar files, a JSON, and a rule for naming things by their content. We pull a real one from Docker Hub and read every piece.
A git clone is just two HTTP requests, and you can read every byte of them. Two repos live in this page; watch one clone the other.
The week-2 assignment points one sigmoid unit at 209 photographs and gets 70% of held-out cats right. This is what happens to an image on the way in, why nobody loops over examples, and what a weight looks like when you draw it back into a picture.
Six equations turn a stack of matrix multiplies into something that learns. Here they are, stepped through on real matrices, checked against the definition of a derivative, and then run live on the dataset that beat a single neuron.
Before backprop, before layers, before any of it — one unit, three numbers, and the reason its loss function looks the way it does. Every figure runs a real network in your browser.
Loose objects are a lovely database at rest and a terrible thing to ship. Packfiles put the whole database in one file, and deltas are why it fits.
A branch is a tiny text file, HEAD is a pointer to a pointer, and checkout runs the whole machine in reverse. Let's keep opening up .git.
Git isn't black magic, just a small database made of plain files. Let's open it up, with a real git running in your browser.
MetaMask never sends your transaction — it signs it. A signed Ethereum tx is an RLP list with r, s and one recovery bit, and the `from` address is recomputed from the signature, never transmitted. Built and signed in your browser with real secp256k1, against my own exam dApp's contracts.
In 2022 I wrote a token exchange for a university exam and passed. In 2026 I compiled it and found that sellToken pays out ETH but never pulls your tokens. Here is the drain — the compiled Exchange and its ERC-20 both deployed in the mini-EVM from part seven, the Exchange genuinely calling the token across every sale, over real solc 0.8.9 bytecode.
An ERC-20 is a contract with a mapping(address => uint256), and a balance is one storage slot at keccak256(holder . 5). "Transfer" subtracts from one slot and adds to another — no coin travels anywhere. Real solc bytecode, my own exam token, executed live in your browser.
Ethereum runs one 256-bit stack machine on every node at once, and it stays halted for the strangest reason — it charges rent on computation and stops when you run out. Real solc bytecode, executed opcode by opcode in your browser.
The Merkle Patricia Trie is git's Merkle tree with a keyboard. Every account and every storage slot hangs off one 32-byte root, and changing a single balance rewrites only its path up. Real keccak256, go-ethereum's own test vector reproduced live in your browser.
Bitcoin has no accounts — only unspent outputs. Ethereum threw that out and kept a single mutable table of accounts, each a four-field row. Real accounts, read from a node and re-hashed live in your browser.
A full node is one append-only log plus a couple of rebuildable key-value caches. Real blocks dissected byte by byte, and a payment verified against fifteen years of history with four hashes and an 80-byte header.
A Bloom filter stores no data and still answers questions about it — nine bits per item, no false negatives ever, and a false-positive rate you choose in advance. The formula derived and then measured against a real filter, why deletion is impossible, and why Ethereum's is nearly useless on a busy block.
The padlock from part one opens: a real miner in your browser, a fork that resolves itself, and the arithmetic thermostat that keeps blocks ten minutes apart.
There are no accounts and no balances — only unspent outputs, each locked by a tiny program you unlock with a signature. Real 2010 transactions, re-verified live in your browser.
Every post in this series leaned on one sentence — "your public key is your private key times a point" — and then walked away from it. This is the walk back. Modular arithmetic, the group law, a curve over 97 elements you can see every point of, and the real secp256k1 doing the real job, all runnable here.
Take git's content-addressed chain, strip it down to an 80-byte header, and add one rule about leading zeros. Real blocks from 2009, re-hashed live in your browser.
Thirty years of failed digital cash, and every piece of Bitcoin already sitting on a shelf. Chaum's blind signatures, the 1991 chain of blocks, hashcash — all runnable here, plus the four things the whitepaper cites to nobody.
A Merkle tree is one idea applied recursively, and almost every diagram of it is of the broken version. Inclusion proofs, the odd-node bug that is consensus in Bitcoin forever, a second-preimage forgery you can watch succeed, and the append-only proof that keeps the web's certificates honest.
Your machine wakes up knowing one thing, its own MAC address. Four broadcasts later it has an IP, a router, and a lease with an expiry date. Every byte here is a real capture, read whole.
Before your browser can fetch anything, one UDP datagram has to turn a name into an address. It fits in 512 bytes, you can read every one of them, and this page sends one for real.
Every request you've ever sent is a few lines of text over a TCP socket. This page parses them live, and one figure fills itself with the request you just made by opening it.
A real compiler lives under this post. Edit a program and watch it become tokens, a tree, types and JVM bytecode, then step a small JVM through the result.