Skip to content

feat(privacy): route source searches through Tor via TORLINK_TOR#107

Draft
AdinathChaudhari wants to merge 1 commit into
baairon:mainfrom
AdinathChaudhari:feat/tor-search
Draft

feat(privacy): route source searches through Tor via TORLINK_TOR#107
AdinathChaudhari wants to merge 1 commit into
baairon:mainfrom
AdinathChaudhari:feat/tor-search

Conversation

@AdinathChaudhari

Copy link
Copy Markdown

What

Add an opt-in TORLINK_TOR env var that routes source searches (scouting) through a local Tor SOCKS proxy. Searching the indexers otherwise exposes your IP to every site; this hides it and reaches their .onion mirrors.

TORLINK_TOR=1 npx torlnk                       # Tor daemon on 127.0.0.1:9050
TORLINK_TOR=socks5h://127.0.0.1:9150 npx torlnk # Tor Browser's port

Scope: search only (stated plainly in README + --help)

Downloads still connect to the swarm directly — Tor can't safely carry BitTorrent (UDP/DHT, no torrent peers of its own), so anonymising the download side would be a false promise. For that, use a VPN with a kill switch or a seedbox. This PR is deliberately honest about that boundary.

Design — matching the grain

  • Single chokepoint. Every source already funnels through fetchResilient (src/util/net.ts), so a swappable defaultFetch routes them all with zero per-source changes.
  • Fail closed. Once enabled, searches always go through Tor. A proxy that's down makes searches error (sources show offline, existing behaviour); a misconfigured TORLINK_TOR exits at startup. Neither ever silently falls back to a direct, de-anonymising connection — that would defeat the entire point.
  • socks5h (remote DNS). Tor resolves the hostname at the exit, so .onion works and no lookup leaks to the local resolver.
  • No new packages. undici and socks were already transitive deps (via webtorrent); this only promotes them to direct deps (lockfile grows 12 lines).

Tests

src/util/proxy.test.ts covers env parsing, proxy-URL parsing, install, the fail-closed misconfig path, and the reachability probe. Full suite green (277 tests), typecheck clean, build clean.

Not covered here

  • Live Tor routing wasn't exercised in this environment (no tor binary present). The SOCKS→undici path uses the standard socks + buildConnector pattern; I'd appreciate a maintainer smoke-test with a real Tor daemon.
  • TUI status surface: the enabled/‑down notices print to stderr, which the alt-screen clears for the interactive TUI (they're visible in headless modes). A small in-TUI indicator could be a nice follow-up, but I kept UI wiring out to stay tight and one-concern.

🤖 Generated with Claude Code

Searching the indexers exposes your IP to each site. Set TORLINK_TOR=1 to
send every source fetch through a local Tor SOCKS proxy (127.0.0.1:9050, or
socks5h://host:port for another, e.g. Tor Browser's 9150). This hides your
IP from the indexers and reaches their .onion mirrors.

Scope is search only, and the README/CLI help say so plainly: downloads
still connect to the swarm directly, because Tor cannot safely carry
BitTorrent (UDP/DHT, no torrent peers of its own). Use a VPN or seedbox for
the download side.

Design notes, matching the repo's grain:
- Single chokepoint: all sources already go through fetchResilient, so a
  swappable defaultFetch in net.ts routes them all with no per-source change.
- Fail closed: once enabled, searches always go through Tor. A proxy that is
  down makes searches error; a misconfigured TORLINK_TOR exits at startup.
  Neither ever falls back to a direct, de-anonymising connection.
- socks5h (remote DNS): Tor resolves the hostname at the exit, so .onion
  works and no lookup leaks to the local resolver.
- undici + socks were already transitive deps (via webtorrent); this only
  promotes them to direct deps — no new packages in the tree.

Tests cover proxy parsing, install, fail-closed, and the reachability probe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant