How it works
Twelve hops
and a phone call.
A 486 dials a telephone number and a present-day website appears on its screen. Between those two events are twelve distinct pieces of machinery, each of which exists because the one before it cannot do the next thing. This page walks the whole path, in order, and says why every hop is there.
The diagram scrolls sideways on a narrow screen. Every stage is described below in the same order.
The path, hop by hop
Each stage answers one question: what does the previous stage hand over, and why can it not simply hand it to the stage after next?
-
The vintage machine
A 486 on Windows 95, an Amiga, a Mac Plus. Its dialler sends ATDT and its TCP/IP stack — Trumpet Winsock, Dial-Up Networking, MacTCP — waits for a login it can authenticate with PAP.
Why it exists: it is the entire point. Everything downstream is arranged so that nothing on this machine has to be modified, patched or reconfigured.
-
The modem
Turns bits into sound inside the 300–3400 Hz voice band and back again. V.22bis carries 2,400 bps; V.32bis carries 14,400. V.42 adds error correction and V.42bis adds compression, which is why HTML — highly compressible — moves faster than the raw line rate suggests.
Why it exists: the telephone network carries sound and nothing else. Any data crossing it must first be made to sound like something.
-
The PSTN
An ordinary telephone call to an ordinary US number. No special line, no data service, no arrangement with your telephone company.
Why it exists: it is the one network that already reaches everywhere. It is also what makes LiteNet reachable from any telephone that can dial the United States — see Coverage.
-
The BulkVS SIP trunk
The call leaves the telephone network and arrives at the server as SIP signalling plus an RTP audio stream. The trunk group is LiteNet-Dialup, IP-authenticated, MaxIn=16, DialPlan=11DIGITS — which is why the dialplan matches 16109221996 rather than a bare seven digits.
Why it exists: LiteNet owns no copper, no switch and no DS1. A wholesale carrier is how a small ISP gets a real telephone number on the public network at all. It also supplies the one genuinely counter-intuitive fact on this page: BulkVS signals from its own SBCs but hands media to an unrelated upstream network, so a firewall that allowlists only the SBCs produces a call that rings, answers, and then sits in silence for two seconds before hanging up.
-
Asterisk 22, on a Hetzner CPX11
Two shared vCPUs and 2 GB of RAM in Ashburn, Virginia, running an Asterisk built from source with the AudioSocket modules in and a good deal else compiled out. Its dialplan answers the call, logs the caller, and immediately hands the audio away.
Why it exists: something has to terminate SIP, keep the RTP clock, and answer the telephone. Asterisk already knows how. It is configured to do that and then get out of the way — every transcoding and signal-processing feature that would touch modem audio is off, because a modem's carrier does not survive being helpfully improved.
-
AudioSocket
Asterisk streams the call's raw 8 kHz signed-linear frames over a plain TCP socket to 127.0.0.1:9092, and reads the reply frames back off the same socket.
Why it exists: it means the modem software never has to speak SIP. That is not a convenience — the obvious off-the-shelf answer, D-Modem, carries a documented inability to answer calls, and replacing its embedded SIP stack with an AudioSocket connection to the Asterisk that already exists makes that blocker stop existing rather than get worked around.
-
The software DSP — slmodemd or spandsp
The answering modem, in software. spandsp demodulates V.22bis at 2,400 bps; slmodemd — a Smart Link Winmodem driver, rebuilt to run at the wire's native 8 kHz — carries V.32bis at 14,400. Sixteen lines, one thread each, paced to an absolute deadline.
Why it exists: there is no hardware modem in a Hetzner rack. And the pacing is not a detail: a modem tolerates latency happily and tolerates variable latency not at all, so the audio must leave on a fixed cadence even when the machine is busy. Bursting to catch up is the most jitter you can possibly introduce, which is why it is never done.
-
The pty
The demodulated byte stream is presented as an ordinary serial device — a pseudo-terminal that behaves exactly like a tty with a modem on the end of it.
Why it exists: it is the seam. On one side is a 2026 program doing signal processing; on the other is thirty-year-old serial software that wants a device node and a baud rate. The pty lets both have exactly what they expect, with no modification to either.
-
pppd
LCP negotiates the link, PAP checks the username and password against /etc/ppp/pap-secrets, and IPCP hands the subscriber an address out of 10.64.0.0/16 along with the resolver at 10.64.0.1.
Why it exists: PPP is the protocol the subscriber's stack already speaks. The pool boundary is load-bearing rather than decorative: the egress rule opens by dropping any packet whose source is outside 10.64.0.0/16, so a link numbered anywhere else has every forwarded packet discarded while pings to the gateway keep working — which reads exactly like a modem fault and is not one.
-
nftables
Three jobs at once. It masquerades the pool behind the single public address; it logs and rate-limits every new outbound flow so a complaint can be traced back to a session; and it DNATs port 80 out of any ppp* interface to 10.64.0.1:80.
Why it exists: sixteen subscribers leave from one IP address, so attribution is not optional — one person running a port scan gets the whole server suspended. And that single DNAT rule is the entire mechanism behind “transparent”: the browser believes it opened a connection to the origin. Port 443 is deliberately left alone.
-
retroproxy
Fetches the real page over fibre, then rewrites it in a fixed order: kill the chrome, measure, extract the article, downgrade the markup to HTML 3.2, fold the images into a byte budget, serialise. Eight fetches in flight at once, a 2 MiB ceiling on any upstream body, a fifteen-minute cache so sixteen lines asking for the same front page fetch it once.
Why it exists: 380 KB at 1,444 B/s is 263 seconds. Nobody waits 263 seconds. The same page as 5,933 bytes of HTML 3.2 is 4.1 seconds, and it is still the news.
-
The live web
Today's actual site, fetched now, with today's TLS — terminated at the proxy, because a browser from 1996 cannot complete a modern handshake and a machine under about 40 MHz times out during the key exchange even when it can.
Why it exists: because an archive would have been easier and would not have been the same thing at all.
Then the whole path runs backwards. The transformed page goes out through NAT, into pppd, down the pty, into the DSP, out as modulated audio over AudioSocket, into Asterisk's RTP stream, across the carrier, out of the telephone network as sound, into the subscriber's modem, and up the serial port into a browser that has no idea any of this happened.
The contract between the two halves
One file, written at CONNECT
The modem knows how fast the line trained. The proxy needs to know, and the two share nothing else. So the modem writes it down.
When the carrier trains, modemd writes the negotiated line rate to /run/litenet/sessions/<subscriber-ip> and unlinks it at hangup. The file holds a bare number and a newline:
# /run/litenet/sessions/10.64.0.10 2400
That file is how the proxy knows which reduction profile to apply. On each request retroproxy takes the subscriber's address, stats the matching file — cached for two seconds, so a busy line costs a handful of stats per second rather than one per request — converts the line rate to goodput at ten bits per byte and 0.8 for LAPM framing, and maps the result onto a budget. Without that file the profile switch is a guess, and the guess it makes is the pessimistic one: no session file means assume the slowest line in the pool, 275 B/s, because overestimating delivery time is free and underestimating it costs a subscriber a twenty-minute page.
| Profile | Goodput | Images | Image budget | Tables |
|---|---|---|---|---|
| text | ≤ 400 B/s | Deferred to numbered links | 0 B | No |
| lowgfx | 401–800 B/s | Up to 3, longest edge 320 px, 32 colours | 20,000 B | No |
| gfx | > 800 B/s | Up to 6, longest edge 480 px, 64 colours | 60,000 B | Yes |
The boundaries come from wall-clock time rather than taste. At 400 B/s a 20 KB image is fifty seconds of nothing happening on screen, which is past the point where people hang up and redial. Above 800 B/s a 60 KB budget lands around forty seconds of page, which people sit through because the text has already painted above it.
The read side is deliberately paranoid about a path built from a network value: anything that does not parse as a literal IP address never reaches the filesystem, the read is bounded to 64 bytes so a symlink cannot stall the proxy, and a modem reporting its DTE rate instead of its carrier rate — 115200 on a V.32bis line is fiction — is clamped rather than believed.
Twelve hops, one telephone call
If you have a machine with a modem and a phone line, the whole path above is waiting for you to dial into it.