Download od.zip   Download Source Code

2026-08-22 — Initial public release prepared

Onion Downloader combines a native graphical queue and a complete command-line downloader in one
2.67 MiB Windows executable. Its original HTTP, SOCKS5, range scheduling and resume engines spread
one file over multiple independent Tor circuits. A specialized client-only Tor 0.4.9.11 is packed
into the application's icon resource, so there is no installer, browser bundle, external runtime
or loose tor.exe to carry beside the program.

Onion Downloader — Native Multi-Circuit Tor Downloader for Windows

**One file • Native GUI and CLI • Multiple independent Tor circuits** *Ten parallel range workers, durable resume and a complete Tor client inside 2.67 MiB* *C++23, WinAPI, no browser engine, no libcurl, no external runtime DLLs*

Graphical queue — a multi-gigabyte transfer spread across ten parallel circuits

Onion Downloader graphical interface downloading a 6.97 GB file over ten Tor circuits

Command line — the same engine and the same binary, od.exe --help

Onion Downloader command-line help screen


Table of Contents


Overview

Downloading a large file through one Tor circuit means that one slow path controls the whole transfer. Onion Downloader takes a different route. If an HTTP server supports byte ranges, the file is divided into independent regions and fetched by parallel workers distributed over several Tor daemons. Every daemon maintains its own circuits, so a weak route can continue working without preventing faster routes from advancing other parts of the file.

This is not a browser automation tool and it does not contain pieces of Tor Browser. The application implements the downloader itself: HTTP requests and response parsing, SOCKS5 negotiation, range planning, direct random-access writes, progress accounting, queue persistence and validated resume metadata. Tor is used for Onion transport and nothing else.

Property Current release
Final executable 2.67 MiB (2,801,152 bytes)
Platform Windows x64
Default parallelism 10 range workers
Tor process pool Automatically selected, normally 2–8 daemons
Embedded Tor 0.4.9.11, client-only, proof-of-work enabled
Interface Native Win32 GUI and CLI in the same PE file
Runtime dependencies Windows system libraries only
Default destination Current user's Windows Downloads folder

Real speed still depends on the Onion service, its range support, relay conditions and the user's connection. Multi-circuit scheduling removes the single-route bottleneck; it cannot manufacture bandwidth the remote service does not have.


Highlights

  • Parallel HTTP ranges over independent Tor paths — ten workers by default, configurable up to 64.
  • Adaptive Tor pool — multiple local SOCKS endpoints without requiring a separately installed Tor service.
  • Correct non-range fallback — a server that ignores Range is downloaded as one ordinary stream instead of producing a corrupted file.
  • Durable pause and resume — data already written is retained in place and described by compact metadata beside it.
  • Safe restart behaviour — unfinished GUI jobs are restored as paused; the user decides when network work resumes.
  • Native Windows presentation — automatic light/dark mode, Windows 11 Mica and PerMonitorV2 DPI scaling.
  • Window-level paste and drag-and-drop — Ctrl+V works without carefully focusing the edit control; dropped URLs go directly to the address field.
  • One self-contained binary — Tor is embedded, compressed and extracted to a version-aware local cache only when necessary.
  • No downloader libraries — no libcurl, aria2, Chromium, Gecko, Qt, Electron or .NET.
  • Clean child lifetime — Tor processes live in a Windows job and are terminated with the application.

Two Heads, One Download Engine

od.exe is a dual-mode executable in the same sense as a good native system utility: the presence and meaning of command-line arguments choose the interface, not a second launcher.

With no arguments it enters its Win32 message loop and presents the graphical queue. With a URL it behaves as a console downloader suitable for batch files and terminals. Both heads call the same coordinator and share every important implementation below the presentation layer. A transfer created in either mode follows the same range validation, circuit allocation and part-file rules.

This matters for correctness as much as size. There is only one resume implementation to test, one Tor lifecycle, one HTTP parser and one answer to what should happen when a server returns an unexpected status. Unknown command-line switches are rejected with an error and help text; they never accidentally open a graphical window.

Invocation Behaviour
od.exe Launch the graphical interface
od.exe http://service.onion/file.zip Download through the CLI using the default worker and circuit policy
od.exe --gui Explicitly launch the GUI
od.exe --help Print command-line usage

Multi-Circuit Architecture

flowchart TB INPUT["Onion URL
GUI paste, drop or CLI"] --> COORD["Download coordinator
probe, plan, progress, cancellation"] COORD --> RANGE{"HTTP byte ranges
supported?"} RANGE -->|Yes| MAP["Segment map
10 workers by default"] RANGE -->|No| STREAM["Safe single-stream fallback"] subgraph POOL["Independent Tor process pool"] T1["Tor daemon 1
private DataDirectory + SOCKS port"] T2["Tor daemon 2
private DataDirectory + SOCKS port"] TN["Tor daemon N
private DataDirectory + SOCKS port"] end MAP --> W1["Range worker 1"] MAP --> W2["Range worker 2"] MAP --> WN["Range worker N"] W1 --> T1 W2 --> T2 WN --> TN T1 --> ONION["Onion service"] T2 --> ONION TN --> ONION ONION --> PART["Random-access writes
target.odpart"] STREAM --> PART PART --> META["Atomic resume metadata
target.odmeta"] META --> DONE["Validated final file"]

Each Tor instance gets an isolated data directory and SOCKS listener. The pool waits for bootstrap rather than treating a process launch as network readiness. Workers then open SOCKS5 connections to the Onion hostname through selected instances and write only into their assigned regions of the pre-sized part file.

The coordinator owns the transfer state. It can stop issuing work immediately on Pause while preserving completed intervals, or cancel the entire operation and let the UI decide whether its recoverable files should remain.


Engineering Decisions and Invariants

The most important decisions are encoded as invariants rather than UI conventions. They remain true whether a transfer starts from the GUI, CLI or a restored session.

More segments than workers

A naive parallel downloader cuts a file into exactly as many pieces as it has workers. That is particularly poor over Tor: circuit speeds can differ by an order of magnitude, so nine workers may finish and sit idle while the slowest circuit owns the final tenth of the file.

Onion Downloader creates a work queue with a target of eight segments per worker. Segment size is rounded to whole MiB and clamped between 2 MiB and 16 MiB. Workers claim the next available segment dynamically, which lets a fast circuit complete several regions while a slow circuit is still handling one. Small files automatically receive fewer effective workers; parallelism is not created merely to satisfy a number in the settings.

A completed bit means a completed range

Progress bytes are useful for presentation but are not the source of resume truth. A segment changes from Pending to InFlight when claimed and to Complete only after its entire expected body has been received and written. If a connection stalls halfway through, the worker subtracts that partial contribution from the displayed counter and returns the whole segment to Pending. A later circuit overwrites that region from its first byte.

The completion bitmap therefore never claims that a partially received segment is durable. Resume restores only Complete bits. It does not attempt to infer safe ranges from the physical size of a sparse or pre-sized part file.

The server must prove the range

The initial probe asks for byte zero only. Parallel mode is accepted only when the response is HTTP 206, contains a parseable Content-Range, and reports a non-zero total instance length. HTTP 200 means that the server ignored the request, so the plan becomes one sequential segment. During transfer every ranged response must again be 206 and its Content-Length must equal the exact inclusive range length. An unexpected status or length is not written optimistically.

The probe follows at most five redirects and resolves their Location against the current URL. Client errors in the 4xx family stop the job because changing Tor circuits cannot make a missing or forbidden resource valid.

Retry means a new Tor isolation identity

Every worker authenticates to SOCKS5 with its own generated isolation username. When a connection fails, the socket closes and the credential nonce advances before the range is retried. Tor's IsolateSOCKSAuth policy therefore prevents the retry from silently returning to the path that just stalled.

A worker tolerates up to ten consecutive failures, with a short linear backoff capped at five seconds. Linear rather than long exponential delay is intentional: congested Onion services often recover quickly, and another circuit should notice that recovery without making a paused-looking application wait for minutes.

Cancellation must break blocked network calls

Pause cannot merely set a Boolean and wait for a long receive timeout. Each live HttpConnection is registered with the job; cancellation closes its atomic socket handle, which wakes a thread blocked inside Winsock. Workers observe the stop flag, release any unfinished claim, persist the current completion bitmap and leave the file resumable.

Disk writers share a file, not a file pointer

The part file is opened once, but each worker obtains a writer that performs explicit offset-based writes. No shared sequential file position exists to race between threads. The file is pre-sized to the known resource length before range work begins, making every final offset stable; actual write failures still stop the job and preserve only ranges that reached Complete.

The resume plan must match exactly

Stored metadata is accepted only when URL, total resource size and calculated segment size match the new probe and plan. Changing the worker count may change segmentation, so an incompatible bitmap is rejected instead of being projected onto different byte boundaries. Re-downloading is slower than guessing, but it cannot silently assemble a corrupt file.

Tor readiness is measured, not assumed

CreateProcess returning successfully does not mean Tor can carry traffic. Onion Downloader opens each daemon's cookie-authenticated control port and polls GETINFO status/bootstrap-phase until progress reaches 100 percent. The first profile pays for a cold directory fetch; once ready, its consensus and descriptor caches seed the remaining private profiles, which then bootstrap concurrently. Guard state remains separate for the independent instances.

Socks and control ports are selected in free pairs beginning above the usual Tor and Tor Browser ports, avoiding collisions with ports 9050 and 9150. A partial pool is useful: if some secondary daemons fail, every successfully bootstrapped instance remains available and workers are distributed round-robin over the ready ports.


Pause, Resume and Recovery

An unfinished archive.zip is represented by two files next to the future destination:

File Purpose
archive.zip.odpart Pre-sized data file; completed ranges already occupy their final offsets
archive.zip.odmeta Small JSON description used to validate the URL, expected size and completed segment map

Resume information is not rewritten for every received block. The engine checkpoints it at controlled intervals and uses a temporary file plus replacement for atomic updates. That keeps I/O low while avoiding a half-written JSON document if the process or machine stops during a save.

The GUI has a separate session.json for queue presentation. It remembers which jobs were present, but it does not pretend that a queue row is sufficient proof that the bytes are resumable. On startup unfinished jobs return in the Paused state. Selecting a row makes Resume available, and only an explicit Resume starts Tor and network activity. This avoids a confusing apparent hang when the machine starts without Internet access.

Once every byte range has been validated, the helper pair is removed and the final destination remains.


How the Entire Program Fits in 2.67 MiB

Onion Downloader treats application code, Tor and packaging as one engineering problem. The size is produced by a chain of independent reductions rather than by one compressor at the end.

flowchart LR UP["Upstream Tor 0.4.9.11"] --> MOD["Client-only modules
no relay, dirauth or dircache"] SSL["OpenSSL 3.5.7"] --> PRUNE["Unused algorithms, apps,
tests, docs and legacy removed"] EVENT["libevent + zlib"] --> STATIC["Static dependency closure"] MOD --> GC["Function/data sections
linker garbage collection"] PRUNE --> GC STATIC --> GC GC --> RAW["Specialized tor.exe
6.54 MiB"] RAW --> CAB["LZX-21 Cabinet
about 2.15 MiB"] ICO["Valid Windows icon"] --> JOIN["ICO + appended Cabinet"] CAB --> JOIN APP["Native GUI + CLI
shared C++23 engine"] --> PE["od.exe resources"] JOIN --> PE PE --> FINAL["Single 2.67 MiB executable"]

The principal size decisions are:

  1. GUI and CLI share one PE and one engine. There is no launcher or duplicated networking layer.
  2. The interface is direct WinAPI. No general-purpose GUI framework enters the dependency graph.
  3. Tor is compiled for the role it performs. Relay and directory-server machinery never reaches the linker.
  4. OpenSSL is pruned before it is built. Removing unused features at configuration time gives section garbage collection less code to consider later.
  5. Required libraries are static. The distribution does not grow into an executable plus a directory of DLLs.
  6. Windows Cabinet LZX-21 compresses the specialized PE. The payload falls from roughly 6.54 MiB to about 2.15 MiB.
  7. The cabinet is appended to a real ICO resource. Explorer sees the normal icon; the application sees both the icon and its embedded transport engine.
  8. Build-time hashes avoid pointless recompression. If Tor and the original icon are unchanged, build.ps1 reuses the verified payload.
  9. A runtime cabinet fingerprint controls extraction. The cached Tor is reused byte-for-byte until a different embedded payload appears.

The application never downloads Tor behind the user's back. The complete engine is already inside od.exe and can be reconstructed by Windows' native Cabinet FDI implementation in cabinet.dll.


The Embedded Tor Engine

The bundled Tor is not a renamed copy of an entire browser distribution. It is a separate, statically linked Windows x64 console program built from official C-Tor sources for one job: establish client circuits and expose local SOCKS endpoints to Onion Downloader.

Tor capability Included? Reason
Onion client and SOCKS proxy Yes Core transport required by the downloader
Onion-service proof-of-work client Yes Required for compatibility with services defending themselves under load
Relay module No Onion Downloader never carries traffic for other clients
Directory authority module No It never operates Tor network authority infrastructure
Directory cache role No It is a client, not a public cache
Unit tests, manuals and Tor applications No Build-time material, not runtime client functionality
System-wide torrc No Each managed process receives its explicit private configuration

Removing proof-of-work would save some code but would be the wrong optimization: a smaller engine that cannot reach a protected Onion service is not an equivalent engine. The build keeps this feature and still removes several megabytes of unrelated general-purpose functionality.


A Linux-Built Native Windows Tor

As a technical curiosity, the embedded engine uses Tor 0.4.9.11 — the newest official C-Tor release available when the build was prepared — compiled inside Ubuntu 26.04 under WSL. The output is nevertheless a genuine native Windows PE executable. MinGW-w64 cross-compiles every component for x86-64 Windows; Linux is only the build host.

Tor is configured as a static GPL client with relay and directory-authority modules disabled. OpenSSL 3.5.7 is separately configured without its applications, tests, documentation, legacy provider and cryptographic or protocol families the selected Tor client does not use. libevent and zlib are static as well. Functions and data occupy separate object sections, allowing the linker to remove unreachable sections before the final executable is stripped.

The Windows PE security flags remain intentional: NX compatibility, ASLR, high-entropy ASLR and a Windows 7-or-newer API target. The full reproducible command line is kept in build-tor.sh in the repository rather than turning this page into a configure log.


Graphical Interface

The GUI follows Windows rather than imposing a custom theme. It uses native controls, PerMonitorV2 scaling, system light/dark preference and Mica where Windows 11 provides it. Download rows use a restrained grayscale presentation: progress is visible without turning the queue into a collection of unrelated status colours.

Paste an Onion URL with Ctrl+V anywhere in the main window, drag a URL onto it, or focus the address field and type normally. Add places the address in the queue. Selecting a live or incomplete row exposes the operation that makes sense for its current state: Pause for running work, Resume for a recoverable stopped transfer, and Remove for queue cleanup.

The address hint explicitly advertises both methods — paste an Onion URL or use Ctrl+V — so the fastest path is visible before the user interacts with the window.


Command-Line Usage

Running with no arguments launches the GUI. Supplying a URL downloads to the current user's Windows Downloads directory with ten workers and the automatic Tor pool:

od.exe http://example.onion/dataset.zip

Choose the output directory or tune worker and Tor process counts when the service and local machine justify it:

od.exe -s 20 -c 8 -o D:\Data http://example.onion/bigfile.bin
Option Meaning
<URL> Target HTTP address, normally an Onion service
-s, --split <N> Parallel workers; default 10, maximum 64
-c, --circuits <N> Independent Tor daemons; default automatic, normally 2–8
-o, --out <dir> Destination directory; default is the user's Downloads known folder
-g, --gui Launch the graphical interface explicitly
-h, --help Print help and exit
-v, --version Print version and build information

The command-line banner uses ASCII (C) Marek Wesolowski (2026) deliberately, so it remains readable in shells and redirected files using different legacy code pages. The graphical About information retains the proper copyright symbol and Polish spelling.


Source Architecture

The source tree follows runtime responsibility rather than putting the whole program behind the window class:

Area Responsibility
src/core/DownloadEngine.* Job lifecycle, probing, worker creation, retry policy, progress monitoring and finalization
src/core/SegmentMap.* Dynamic range work queue, Pending/InFlight/Complete state and compact resume bitmap
src/core/PartFile.* Pre-sized random-access file, independent writers, atomic metadata and final rename
src/core/SessionManager.* GUI queue persistence and safe restoration of active work as Paused
src/core/TorPool.* Private profiles, ports, process job, control authentication, bootstrap and consensus seeding
src/core/TorExtractor.* In-memory Cabinet FDI callbacks, embedded payload fingerprint and local Tor cache
src/net/Socks5.* Cancellable Winsock stream and SOCKS5 hostname connection with authentication isolation
src/net/HttpClient.* URL parsing, HTTP requests, bounded headers, ranges, redirects and response validation
src/ui/OdWindow.* Native window lifecycle, queue interaction, clipboard, OLE drop target and command routing
src/ui/Controls.* List view, grayscale progress-cell drawing and control theming
src/cli/* Strict argument parsing, console ownership and terminal progress presentation

The dependencies point inward: GUI and CLI depend on the core; the core depends on the small networking layer and WinAPI; neither networking nor persistence knows how a row is painted. This is what makes the dual-mode design real instead of a GUI program with a thin command-line afterthought.

Concurrency ownership is similarly explicit. Job state and segment publication use engine mutexes, high-frequency counters and stop state are atomic, segment claims are serialized inside SegmentMap, and connection registration has its own mutex so cancellation can close live sockets without racing their destruction.


Validation Strategy

Correctness is tested at the boundaries where downloaders commonly fail:

  • Cold Tor bootstrap — start without a prepared profile, reach control-port progress 100%, then make a real request through the resulting SOCKS endpoint.
  • Warm multi-instance bootstrap — reuse directory material, seed sibling profiles and confirm that every reported pool member is actually ready.
  • Real Onion request — retrieve an official Onion endpoint through the compiled client rather than testing only against loopback HTTP.
  • Range negotiation — distinguish a valid 206 plus Content-Range from a server returning 200 to a range probe.
  • CLI process lifetime — complete a download, return an exit code and verify that no child tor.exe survives.
  • Embedded-engine identity — compare the Tor extracted from od.exe with the checked-in payload and confirm its compiled module list.
  • Build repeatability — verify the payload hash, reuse an unchanged LZX resource on the normal path and force a complete repack on the explicit path.
  • Release archive integrity — build od.zip, reopen it and test every archived byte before publication so the published binary is exactly the verified one.

The current specialized Tor reports proof-of-work support enabled and relay, directory-authority and directory-cache modules disabled. Its imported libraries are Windows system DLLs only. The complete CLI integration run exits cleanly after downloading through the embedded engine.

The engineering rule is that a visual success state follows the durable state, never precedes it. A transfer becomes Completed only after all segments are complete and the part file has been flushed, closed and moved into its final name. If finalization fails, the UI receives an error instead of a cosmetic 100 percent result.


Building from Source

The application build requires Windows x64, PowerShell and Visual Studio with the MSVC C++ x64 toolchain. Extract od_source_code.zip; the bundled resources\tor.exe is the pinned client payload, so an ordinary build does not require WSL:

# From the extracted source-tree root:
.\build.ps1

The script locates the newest usable Visual Studio installation, builds Release|x64, removes stale loose Tor files and leaves the single application in bin\od.exe. Its deterministic PE settings use a fixed file timestamp and reproducible-link option. Repack the Tor cabinet intentionally with:

.\build.ps1 -Clean -RepackTor

Rebuilding Tor itself is a separate WSL/MinGW path. On Ubuntu install the standard compiler, autotools, Perl and MinGW-w64 packages, then run ./build-tor.sh. The script downloads pinned Tor, OpenSSL, libevent and zlib releases, verifies the Tor source checksum, cross-builds the Windows client, replaces resources\tor.exe and invokes the Windows repack step.


Downloads and Source Code

Download od.zip   Download Source Code

od.zip contains the single Windows x64 binary od.exe (2,801,152 bytes) with the embedded Tor engine; od_source_code.zip contains the full source tree, the build scripts and the pinned resources. Neither archive is password-protected.


License

Onion Downloader's original source is released under the MIT License.

The embedded Tor executable remains a separate GPLv3-or-later program. OpenSSL, libevent and zlib retain their upstream licenses. Exact versions, source links and the reproducible Tor configuration are included in the public repository.


Author

Marek Wesołowski — WESMAR
https://kvc.pl