Roadmap in Reverse
A roadmap is a plan drawn at altitude: a high-level account of where a product is headed and roughly in what order it intends to get there. In Scrum and the agile tradition more broadly it isn't one of the ceremonial artifacts — not a backlog, not a sprint commitment — but the planning layer that sits above them, the bridge between a vision that rarely changes and a backlog that changes constantly. A team reads its roadmap and points the same direction: engineers lay groundwork for what's coming, stakeholders learn what to expect and when, and everyone gains a shared answer to why this is being built before that. Modern roadmaps lean toward themes and horizons — now, next, later — rather than hard dates, precisely because the further out you look the fuzzier the road becomes. At its best a roadmap is a forecast offered in good faith, not a contract; it aligns people, frames trade-offs, and tells the story of a project moving deliberately toward something.
This project is going to do the opposite, and name it honestly while doing so. ftorrent is non-commercial, built by human and AI contributors as time and energy allow, with no quarter to hit and no one owed a delivery date — so a forward-looking roadmap would be a promise we have no standing to make. Instead we keep a roadmap in reverse: not the windshield but the rear-view mirror, a record of the road already behind us. Each entry is an epic completed, an initiative carried across the line, written down only once it's actually done. The conceit is tongue-in-cheek and we know it, but the inversion keeps most of what makes a roadmap worth having — it still chronicles a sustained, lengthy course of significant work, still shows direction, still gives contributors something to point at with some pride — while quietly reversing the usual hazards. It cannot over-promise, cannot slip, and cannot fall out of date, because it predicts nothing. It is, for once, a plan guaranteed to be correct: a roadmap whose every milestone has already been reached.
2010-Feb-25: First commit
The project's first commit, named simply start, laid down a bare Win32 scaffold on this date.
2010-Jul-14: Secured ftorrent.com
Registered the ftorrent.com domain, choosing the project's name, and the products' brand and mark. The domain was available in a search across the ASCII alphabet, and nods to the market leader, without the Greek and Latin ambiguity around µ or u. We make our residency in the .com TLD, the foundational namespace of the web, resonant with the turn-of-the-millennium energy of the dot-com bubble. So what does the f stand for? Something practical: Fast. Free. Or more poetic: the virtues Frictionless; Fearless. A storied name like Formula One, or Fuji 🗻. Or foundational: Frontier. Firmament. We leave it unspecified.
2012-Oct-22: Native Windows client
The first ftorrent was a desktop program — a BitTorrent client for Windows, written in C++ on Rasterbar libtorrent. It was built directly against the Win32 API in the Petzold tradition: hand-written window procedures, dialog resource scripts, and icons drawn by hand, down to the red, yellow, and green stage lights. No interface framework sat between the code and the operating system. More than four hundred commits across 2010 to 2012 carried it from a bare "start" to a working application. The source still lives in this repository, in the classic workspace.
2026-Mar-31: Monorepo and return
After more than a decade in the shed, the project came back — and rather than a single program, it was rebuilt as a pnpm monorepo: one repository, many workspaces, each a distinct but related tool or guide that can be improved on its own, in the open, shipped early and often. The original Windows client was preserved in place as the classic workspace, a working record of the C++/Win32 era kept rather than discarded. The open workspace holds the public tracker — the hardened Aquatic deployment and the guide that documents it. And docs is this documentation site, a VitePress build at docs.ftorrent.com meant to grow over time into a repository of documentation, essays, brainstorming, planning, and the occasional rant. More workspaces stand ready as the project widens — a project homepage, an IPv4/IPv6 connectivity diagnostic, a cross-platform desktop client — each a separate piece of one effort: better tools and clearer guides for peer-to-peer and the decentralized web.
2026-Apr-08: Dockerized Aquatic
The engine is Aquatic, a tracker written in Rust. We surveyed the field — opentracker, the C implementation behind most of the internet's large public trackers, among them — and chose Aquatic for three reasons: it speaks all three tracker protocols as separate binaries (UDP for desktop clients, HTTP for clients behind restrictive firewalls, and the WebSocket protocol that serves browser-based WebTorrent and WebRTC peers), it's fast — benchmarked at millions of requests per second on commodity hardware — and Rust's memory safety removes whole classes of vulnerability from software that accepts raw packets from the entire internet.
Aquatic was built for bare metal — CPU pinning, native SIMD, io_uring — and ships no Dockerfile. We put it in hardened Docker containers anyway, trading a performance cost that is unmeasurable at any scale a 1 Gbps link can carry for real security isolation: each tracker runs read-only, as nobody, with every Linux capability dropped, no-new-privileges set, and outbound traffic blocked at the DOCKER-USER firewall chain, so a compromised process can do nothing but answer the requests it is handed. Two obstacles stood between Aquatic and a clean containerization, both left to the operator by Aquatic's own documentation. glommio, the async runtime under the HTTP and WebSocket trackers, requires io_uring with no fallback, and Docker's default seccomp profile blocks it; we ship a minimal custom profile that adds exactly three syscalls and changes nothing else. And Docker's bridge networking leans on the kernel's connection-tracking table, whose defaults silently drop packets under a tracker's load; a tuned set of sysctls — a larger conntrack table, shorter UDP timeouts, deeper socket buffers and receive backlog — keeps the path clean under continuous global load. The network footprint was kept deliberately quiet: the UDP tracker answers on port 443, indistinguishable from QUIC and unblockable without also breaking HTTP/3, rather than the conspicuous 6969 of tradition; the HTTP and WebSocket trackers share the standard 443 behind a reverse proxy; and the whole stack is dual-stack, because IPv6's NAT-free addressing is exactly what peer-to-peer wants.
2026-Apr-12: Tracker page
With the trackers answering, the project gave them a face: a status page on the same subdomain, one domain and one certificate routed by path. At its center, NASA's Blue Marble — the cloudless, public-domain satellite Earth — turns on a three.js sphere at the planet's real rate of one full rotation per day, lit so the center of the globe is always wherever the sun stands at that moment: solar noon, live. There is no grabbing it, no zoom, no pins or labels; the texture swaps each month so the season on screen is the season outside, and to see the far side you wait twelve hours. What we're after is the whole-planet view astronauts call the overview effect — an Earth with weight and rigidity, the opposite of a spinnable virtual globe.
Below it sits a graphing-calculator LCD panel: six live traffic counters and a ninety-day uptime history. The counters are honest about being a simulation. Rather than stream live counts to every visitor, each one starts from the tracker's real recorded 24-hour total and then advances from a Poisson process matched to that tracker's measured rate, so the digits move — bursts, gaps, the runs and held breaths — the way independent arrivals actually do, and snap back to the recorded number every ten minutes so a long-open tab never drifts. At the foot of the page, a quote of the day, chosen by a Squirrel3 noise hash of the calendar day, so every visitor anywhere on earth sees the same one with no database, cache, or server state — the shared fact everyone agrees on is the date itself. The quotes come from a curated research pull spanning antiquity to the present — philosophers, inventors, activists, scientists, legal texts, and sacred traditions from every inhabited continent — on the themes the project is built around: free expression, shared knowledge, open networks, and the interconnection of humanity. The page's type is set in Monaspace, GitHub's monospaced superfamily — Krypton's sharp, mechanical digits for the LCD counters, Radon's hand-drawn feel for the announce URLs. The page itself is static files behind the reverse proxy; nothing runs at request time but the proxy handing back pre-rendered HTML and a small JSON file.
2026-Apr-30: Gauge and breaker
Two unseen components keep the page honest and the server standing. The gauge is a small Python container that runs beside the trackers: once a minute it scrapes each tracker's Prometheus metrics, reads memory straight from the kernel's cgroup files, and writes the single page.json the page renders. It has no HTTP server and no open ports — it only writes a file. A ring buffer of 1,440 slots, one per minute of the day, lets it report a true 24-hour total as the proven difference between two recorded moments, stepping cleanly over gaps and over the counter resets a container restart produces. And because the gauge cannot tell on its own whether the server is reachable from the outside, a companion probe runs on the host — pinging Cloudflare and Google over both IPv4 and IPv6 each minute — so the uptime figure separates "the gauge was down" from "the internet was down."
The circuit breaker is the deployment's graceful load shedding. It meters each of the six services independently — three protocols times two IP versions — so a saturated UDP-over-IPv4 trips on its own while UDP-over-IPv6 keeps serving, nudging clients toward whatever path still works rather than refusing everyone equally. The gauge decides and writes its intent — six timestamps saying when each service may run again; the breaker reads that file and reconciles reality to match, returning fast 503s at the reverse proxy for HTTP and WebSocket and dropping UDP in iptables raw PREROUTING, before the kernel even opens a connection-tracking entry. It keeps no state of its own: every minute it compares what the file asks for against what is actually in effect and converges, idempotently. The two layers stay independent — the gauge writes a file, the breaker enforces it — and the whole data path stays small by design: a static page backed by honest, self-checking statistics, able to introduce tens of millions of peers from a single modest server.
2026-May-23: Software and hardware upgrade
We had been running nginx for everything — TLS termination, reverse-proxying to the tracker containers, static hosting, and the breaker's per-service gating — with certbot for certificates. We migrated to HAProxy fronting Caddy, with lego for ACME. HAProxy terminates TLS more efficiently, which matters because TLS is the most expensive work the box does — a fresh handshake on every new connection — and that cost is unavoidable even while shedding load, since answering a paused service with a fast 503 still means completing the handshake first. It also exposes a runtime admin socket that fits the breaker exactly: per-service toggles push to the live process every minute with no config reload. Caddy serves the static dashboard and page.json that HAProxy, not a web server, can't, and lego replaced certbot because HAProxy's own ACME support is still behind an experimental flag. While we removed nginx from the server, we kept it in the repository and documentation, generalizing the guide into a framework-agnostic pattern with both stacks as worked examples.
On the hardware, we moved from an Intel Core i5-8500 (Coffee Lake, 2018) to an Intel Core i7-14700 (Raptor Lake Refresh), about six years and several microarchitecture generations newer: 6 cores and 6 threads became 20 cores and 28 threads — 8 hyperthreaded performance cores plus 12 efficiency cores — with L3 cache up from 9 MB to 33 MB, peak turbo from 4.1 to 5.4 GHz, and single-box throughput up roughly five- to six-fold. Yet it's still a mainstream 65 W desktop chip, not a server part — a generational leap, not an exotic one, well within reach of a modest build. Networking hardware went from a single 1-Gigabit port to a dual-port 10-Gigabit card (Intel X550-T2). The old single-queue controller that funneled every packet's interrupt onto one core gave way to as many as 28 receive and 28 transmit queues with Receive Side Scaling, which hashes connections across queues so packet processing spreads over all 20 cores instead of piling onto one. That removes the single-core network ceiling, but it's headroom rather than a requirement: at today's load of about 77 million requests a day the tracker pushes only 10.1 Mbps inbound and 18.5 Mbps outbound — the outbound side under 2% of a single gigabit line — so a plain 1 GbE box is more than enough to fork this setup and run it.
2026-Jun-11: DHT bootstrap node
A tracker answers "who else has this?" from a server; the mainline DHT answers the same question from no server at all — a Kademlia network spread across millions of participating clients, the machinery behind every trackerless magnet link. But the DHT has a cold-start problem: a fresh client knows the protocol and not a single peer, so the whole network leans on a short, hardcoded list of well-known bootstrap nodes: ordinary high-uptime nodes that simply happen to be where everyone is told to knock first. It is a small and quietly load-bearing list, and ftorrent now adds one more independent entry to it: dht.ftorrent.com runs qBittorrent-nox on libtorrent as a pure DHT citizen — no torrents, no seeding, just a well-connected node that helps newcomers find their place — hardened like the trackers beside it and folded into the same dashboard as a live count of the nodes in its routing table. The contribution isn't the node, it's the diversity: bootstrapping is one of the last places the decentralized web still funnels through a handful of named hosts, and every independent one, owned by no single client project, makes that first step more resilient for everyone. The quiet irony is the best part — a new bootstrap node joins the network the only way anyone can, by bootstrapping off the ones already there, and every node on that list was itself, once, seeded by the others.
2026-Jun-18: Home page scaffolded to be modern and private
We scaffolded the home page at the apex of ftorrent.com choosing Nuxt, a modern, full-stack JavaScript framework, and then turned the full stack off. With ssr: false set, the build emits a static front-end bundle and nothing else: one index.html shell and its hashed JavaScript. Routes resolve in the browser, and the web server's job collapses to one rule any static host can follow — serve a real file if it exists, otherwise hand back the shell and let the page sort out the route. We keep the framework's conveniences — including access to the entire front-end npm ecosystem — and pay for no runtime at all: the server does nothing but deliver static files.
This construction opens a third part of the URL: the fragment, the part after the #. You already know it from Wikipedia — the #History in a link that jumps you to that section. By RFC 3986, the fragment is the browser's to handle, so the browser splits it off before sending the server its request. That gives the page a place to put a payload — peer-to-peer information, like a magnet link, with uses to grow into: a friendlier landing for it, an inspector that takes it apart, even an in-browser client. Today, ftorrent.com gained a modern web application framework ready and able to give visitors rich peer-to-peer experiences in the page itself, all delivered from a simple and stateless server.
2026-Jul-16: Connection checker good.ftorrent.com
Most people think about their internet connection only when it breaks — Is the wifi down? Is the internet slow? — and turn to familiar sites to check. Netflix's fast.com reports your speed for streaming. ipchicken.com is the classic for your public IP address. ipv6test.google.com asks Ready for the future of the internet? but then surrenders immediately when the answer is no, giving an IPv4-only connection a green checkmark with the label "no problems detected."
good.ftorrent.com is dedicated to a single, opinionated question: Is this connection good? Good, meaning capable of peer-to-peer — ultimately, for digital citizenship. Does this connection let me be both a speaker and a listener — reachable directly by another computer, or is it merely able to fetch from a server? That is what the modern internet quietly stopped guaranteeing — the mark of full participation in the network rather than the second-class standing of a connection that can only consume.
Under the hood are three reflectors, each simply handing back the address your request arrived from. ip.ftorrent.com answers over whichever family you reach it on, while ip4.ftorrent.com and ip6.ftorrent.com carry single-family DNS records that force IPv4 or IPv6. The page calls that pair at once, and which of the two answers gives your dual-stack verdict. All three are public primitives in the ipify tradition, open to any caller: a stateless echo that reads your address and hands it straight back. This first version is our foundation, reporting the address families you have. From here we'll add whether your NAT will let a peer punch through, and whether your browser can hold a WebRTC connection.
2026-Jul-20: Public STUN server stun.ftorrent.com
STUN, from RFC 8489, is a UDP exchange that tells a client the public IP address and port its request arrived from. We run one at stun.ftorrent.com as part of good.ftorrent.com — its workspace, guide, and diagnostic page — where our ip.ftorrent.com reflector already reports the address but, being TCP, can't give the port or test that UDP gets through. STUN adds both.
The server runs coturn, the reference implementation, on the standard port 3478. Our short configuration file mostly turns things off, with stun-only refusing TURN relaying and a handful of no- flags closing listeners we don't need. stun.ftorrent.com joins our other public services as a small contribution to the decentralized web, and using it is as simple as adding stun.ftorrent.com to your list of STUN servers.
2026-Aug-25: Desktop client scaffolded
ftorrent began as a desktop program, and today it has one again. The desktop workspace became a running application — Tauri 2 with a Vue front end over a Rust core — built and launched from fresh clones on both macOS and Windows, with Linux beside them. Where the classic client was written directly against the Win32 API and ran on one operating system, this one is cross-platform from its first commit. It downloads nothing yet, and that is the plan: we are building it from the outside in, so installation, updates, and the window itself come before the engine, which arrives last onto a foundation already settled.
2026-Sep-22: libtorrent in the desktop client
We put libtorrent into the desktop client scaffold today, without needing to build it from source. libtorrent 2.1 shipped in July with WebTorrent on by default, and its maintainers publish wheels for every platform we ship from a workflow in libtorrent's own repository, so we know who built each one and how. We pin each platform's wheel by SHA-256 in a lockfile, and every build machine fetches the same bytes and verifies them before it builds.
ftorrent will run libtorrent in a second process, the engine: a small Python program, since libtorrent's only maintained bindings are Python, frozen with its interpreter into a folder the app carries beside itself. The app starts the engine as its own child and talks to it over pipes, one line of JSON per message, rather than over a local network port. A pipe between a parent and its child has no address, so no other process on the machine can reach the conversation. We built and ran it on macOS and on Windows.
Linux ships four packages from a pipeline that runs in Docker on the Mac, with no Linux computer involved. One Dockerfile builds a toolchain image per architecture, arm64 native on Apple Silicon and x86-64 emulated, and a container made from it lives for exactly one build: it takes a read-only copy of the source, freezes the engine on Linux from the same lockfile, compiles the app, and writes a .deb, and on x86-64 an .rpm from the same compile. A third container wraps that .deb into a Flatpak, the one package that installs on SteamOS and Bazzite, whose root filesystems cannot take a .deb or an .rpm. Then bare Debian 12 and Fedora 40 containers, with nothing installed, unpack the .deb and .rpm packages and run the engine from where each put it. We build on a Debian 12 base for its glibc, which sets the floor on who can run the result: Debian 12 and 13, Ubuntu 24.04 LTS and 26.04, Mint 22, Fedora 40 and up, and both current generations of Raspberry Pi OS.
The engine creates no session yet. But it holds BitTorrent and WebTorrent together from the start, so when it does, a browser tab and a PC will share one swarm.
2026-Oct-08: Desktop client shell
We build the desktop client from the outside in, and this is the outside: installing, updating, keeping one copy running, settling into the places each operating system keeps for a program, and asking to open the files and links a torrent client opens. It is built on Windows and macOS, with Linux carrying what applies to it, and the client still downloads nothing. The work is ordinary, the kind every desktop program owes its platform, and we did all of it before the first torrent, the same way on each system, because the shell is the part that is hardest to change once people depend on it.
ftorrent ships as six packages from two machines: Windows builds the installer, and the Mac builds its own disk image and, in one Docker command, the four Linux packages, two .deb files, one of them for the ARM Raspberry Pi, an .rpm, and the Flatpak that SteamOS and Bazzite take. pnpm hash stages each under a stable published name and writes a sidecar beside it, a small JSON file with the version, architecture, size, SHA-256, and date; the sidecars are committed, so git history dates every release. pnpm upload sends each package and then its sidecar over scp, as an account confined to the downloads directory with no shell, so a compromised build machine could overwrite installers and nothing else. The downloads live at the apex, with no download subdomain: the home page is https://ftorrent.com and the Windows installer is https://ftorrent.com/ftorrent.exe, and behind that one hostname the server tries the site's own files first, then the downloads directory, and hands back the single-page shell for everything else, a few lines in its Caddy configuration. A published name never carries a version, so a link anyone shares keeps handing out the current build, and the version belongs on the page, which reads it from the sidecar. The installing guide on docs.ftorrent.com does exactly that, with a download box per package that fetches the sidecar live, and a one-line install command for Windows and for the Mac with the current hash written into it, so the command checks the download before anything opens.
The Mac's disk image is made without Finder. Tauri's own way drives Finder through AppleScript to lay out the image's window and save what it saw, which steals focus on every build and stalls until somebody closes a window, and all Finder ever contributed was one .DS_Store. dmgbuild, a Python tool run through uvx, the same uv that freezes the engine, writes that file directly: a build takes about five seconds, touches nothing on screen, and comes out the same every time, with the background picture and the two icons measured to the point. The app inside is signed with a certificate of ftorrent's own, self-signed and naming ftorrent and nothing else. Gatekeeper still shows the dialog it has for an app from no known developer, but macOS now knows every release as the same app, so a permission given once, like access to the Downloads folder, carries over to the next version.
The Windows installer is a small C program of our own, in place of the NSIS installer Tauri makes by default. It descends from a setup program we wrote for an earlier project in 2005 and keeps its stance: no wizard, no questions, nothing to click. A user double-clicks ftorrent.exe, and about a second later ftorrent is running, installed for that user alone under local application data, with no administrator prompt, one Start menu shortcut, and an entry in Add or Remove Programs. The files ride as a cabinet packed by Windows' own makecab, appended to the compiled program, and unpacked by cabinet.dll, the same code that unpacks Windows Update, so the installer needs no runtime and no library beyond what every Windows has. An upgrade writes the new files over the old and touches no registry entry, where an installer that uninstalls first deletes ftorrent's entries for a moment and lets Windows reset the user's sealed choice of default app; here that choice survives every release. The same program, written back into the folder without its cabinet, is the uninstaller, which takes back only what ftorrent wrote and only while it still names ftorrent, and leaves settings and downloads alone.
One copy of ftorrent runs per copy of the app, with one window and one engine. At startup the Rust core takes an exclusive lock on an empty file in the data folder, which the kernel releases however the process ends, so a crash never leaves a stale lock, and which belongs to the data folder rather than the program, so an installed copy and a portable one run side by side. On Windows, double-clicking a .torrent file or a magnet link while ftorrent is open starts a second launch, which finds the lock held, writes its command line into a named pipe the running copy serves, and exits; the running copy brings its window forward and queues the request for the page. On macOS, Launch Services delivers the file or link to the running app as an Apple Event, and the lock is a backstop. Closing the window hides it, on Windows and the Mac, while the page, the web view, and the engine keep running, and quitting is a separate, explicit act: every way out reaches one exit, which writes the settings and stops the engine. On Linux, closing still quits, since there is no tray there yet to bring a hidden window back.
The window comes back where the user left it. ftorrent records its position and size in the settings file beside the screen it was on, that screen's place among the others, its size, and its scale, and at the next launch puts the window back only onto a screen whose numbers all match. A new resolution, a changed scale, a rotation, or a monitor unplugged or moved means a fresh place rather than a guess at where the old one went. A fresh window is sized to the primary screen and dropped at a random spot inside a centered field whose margin, an eighth of the screen on every side, is deeper than any taskbar, menu bar, or Dock, so the window misses the operating system's chrome without asking where it is. Maximized is remembered; minimized and fullscreen are never put back. The window is placed and themed while still hidden, and shown once, where it belongs.
While it runs, ftorrent costs the desktop one small glyph. On Windows that is a tray icon in the notification area, the mark as a stencil in white on a dark taskbar and black on a light one, swapped the moment the user changes the taskbar's theme; a click brings the window back, and its menu has Show and Exit. On the Mac it is a menu bar icon near the clock, a template image the system paints in the menu bar's own color, with Show ftorrent and Quit ftorrent, and the Dock icon is there only while the window is, so ftorrent runs in the background while you stay on your desktop. Run at startup takes the same care: on the Mac through SMAppService, so the login item is listed under Open at Login and stops existing when the app is deleted, and on Windows through a value under the user's Run key, and a copy the system started at login stays hidden, with only the glyph near the clock. ftorrent writes the system's entry only when the user answers on the Settings page, and otherwise only reads, since a missing entry looks the same whether the user removed it or it was never made; when the setting and the system disagree, the page says so and links to the system's own screen.
The page is set the way the system sets its own windows. The root font is the CSS keyword menu, which asks the web view for the font the platform draws its menus in, family and size together: Segoe UI at 9 points on Windows and San Francisco at 13 on the Mac. Every size and space on the page measures from it, so ftorrent is a little denser on Windows and a little roomier on the Mac, each at home beside its own menus. That is the factory choice, and two more sit beside it on the Settings page: Inter, with IBM Plex Mono for fixed-width text, both carried by ftorrent, a face of ftorrent's own that reads the same wherever it runs; and Verdana, which both systems install, for the way Windows programs looked around 2000, when BitTorrent was young. Picking a face changes the face and nothing else, since the root keeps the system's size. Dark mode is the window's own theme, system, light, or dark, which the web view reports to the page, where every color is a light-dark() pair, and following the system as it changes takes no code at all.
What ftorrent asks today, in place of downloading, is one question: whether it should open .torrent files and magnet: links. The stance is simple, modern, polite, and assertive, with the user in control, which point for point differs from how Windows programs have done it for twenty-five years. Windows keeps what opens each type at two levels: the legacy association any program may write, which installers have fought over since the 1990s, and the sealed one Windows 8 put above it, the user's own pick, held with a hash only Windows' own screens compute. ftorrent never computes that hash and never tries one method with another behind it. The installer registers nothing; the installed copy, as it runs, offers itself every time, per user, so it appears in every Open with menu and in Settings, and leaves whatever opens each type now exactly as it is. A gray bar across the top of the window asks whenever the system opens one of its types with another program: yes claims them, no gives back what ftorrent wrote and nothing more, and the bar's close keeps it down until the next launch. Where Windows has sealed another program's choice, a yes opens Windows' own Default apps screen for the user to finish there, and ftorrent reads what the system says and follows it, so a type another program takes later brings the question back rather than being seized whenever the window is clicked. On the Mac the same stance takes fewer moves: the app's Info.plist is the offer, and a yes is one Launch Services call per type, which macOS makes without asking, so the user's yes in ftorrent is the whole of the consent and the reason ftorrent asks at all. What the page does with a file once it arrives, adding the torrent, is the next act.
An installed copy checks ftorrent.com for a newer version, on the Settings page's button and on its own, at a random time a day or two after each answer, so every copy doesn't ask the morning after a release. The check reads the sidecar over HTTPS with Mozilla's roots compiled into the client, follows no redirect, and caps the time and the size, and nothing downloads before the click. On the Mac the click downloads the zipped app into a temporary folder in the data folder, checks its SHA-256 against the sidecar, and hands over to the newer copy, which runs as the installer: it waits for the old copy's lock, which the kernel releases only when that process has ended, exchanges the two bundles in one atomic step, touches the new one, and opens it through Launch Services by its path. The order is Sparkle's, the updater most Mac apps outside the App Store use, and our first version, which moved its own bundle out from under itself, showed why each step is there. A copy that can't replace itself, under a standard account or with its home folder on another drive, never gets the button and shows a link to ftorrent.com instead. The window goes away and comes back updated a second or two later, with no wizard, no progress bar, and no prompt. On Windows the check is built and the install is next, with the setup program above as the piece that replaces the files; the Linux packages update through the package manager.
One question ran through all of it: where should ftorrent be consistent with itself across platforms, and where should it match the other programs on this one? A client that begins on one system and later reaches the others carries its first home with it; ftorrent took the three together from the start. So on Windows the window has the menu bar a Windows program has had since the 1990s, File, Tools, and Help, with access keys, and Tools, Options opens the options, while on the Mac the app menu holds About ftorrent and Settings…, in Apple's own order, and the page calls it Settings, the word macOS has used since Ventura, and the two pages hold the same things. Windows says Exit and the Mac says Quit. The tray icon and the menu bar icon are one glyph, each in its platform's own manner, and the type is Segoe UI on Windows and San Francisco on the Mac, each at its system's own size. Past those few words and faces, the page reads the same on every platform.
2026-Oct-12: Research section, with two IPv6 articles
docs.ftorrent.com gained a Research section, and we published two articles into it. Open Address, Closed Device is a measurement: forty-one months of the web requests people made, May 2023 through September 2026, cut by whether the device is one its owner controls and whether the address is one others can reach. We built it on Cloudflare Radar's request shares by bot class, operating system, device type, and address family, and checked it against Google, APNIC, StatCounter, and Wikimedia. In September 2026, 46.6% of requests came from open devices and 41.0% from IPv6 addresses, and 13.7% were both; on open devices the IPv6 share was 29.3%, against 48.0% on closed ones. IPv6 is arriving through phones and carriers, slowest on the computers that could use a reachable address, and the migration to closed devices finished around 2017 and has held since. Waiting for IPv6 is the history behind that slope: the forecasts of the 1990s, the protocol's choosing, the promises, the government deadlines and what was measured when each arrived, the day the pool ran out and the two switch-on days, the reasons it went the way it did, where it stands, and a ten-year outlook, with the sources quoted and linked in place.
The measurement is reproducible from the repository. docs/research/ipv6/ holds three standard-library Python scripts, the 772 frozen pulls they made, and the nine figures they drew, with a README on running them and a note on each source. fetch.py pulls every source and keeps a manifest; table.py builds the joint table for each month and checks it against Cloudflare's direct figures; chart.py writes every figure as SVG by hand, with no plotting library, so that opened alone each renders in gray and orange, and inlined on the site each takes the site's inks, its divider, and its brand orange through CSS variables, in light and dark mode. We measured every label against Verdana's real advance widths to be sure nothing clips, and settled the figures on two type sizes, with bold and ink carrying the hierarchy. Running the two offline scripts reproduces every table and figure byte for byte.
The articles took a form of their own, since the site's essays did not have one for research: a header under the title in the README header's shape, with the date published and revised, the data period, links to the code and data, a cite-as line, and keywords; a one-paragraph abstract; numbered sections; figures and tables marked up as figures with numbered captions; and a references list with full citations. The site's stylesheet gained one treatment for all of that set-off matter, blockquotes, captions, and references at 14px against the 16px prose, so a quotation, a caption, and a header read as apparatus rather than running text. A first draft of the measurement from March 2026, a single page from one month of Radar data, retired with this.