A local-only Tor client for 1M5: attaches to a Tor daemon already
running on this host — SOCKS5 proxy 127.0.0.1:9050, control port
127.0.0.1:9051 (probed for readiness only).
A Python port of tor-java;
mirrors the local backend of tor-rust
(no embedded backend — Arti is Rust-only, see DESIGN.md).
This local-daemon-only model is being retired. tor-java no longer
attaches to a pre-existing Tor instance at all - it downloads the official Tor
Project binary, verifies it, and spawns/owns it directly, so there is no
fallback to some other already-running Tor anywhere in that library. This port
should adopt the same model; see "Embedded Tor (planned)" below and TODO.md.
Not implemented yet. The plan, matching tor-java's current design -
and genuinely simpler here than in most other ports, since the stdlib already
covers every primitive needed:
- Download the official Tor Project "Expert Bundle" for the current
OS/arch into a local cache (
urllib.request- stdlib, HTTPS built in, no new dependency), verify its SHA-256 against a value pinned in this port's own source (hashlib.sha256- stdlib; never trusted from the network alongside the download itself), and extract it with the stdlibtarfilemodule - no need to shell out to the systemtarthe waytor-javadoes, since Python's stdlib has a real tar reader. - Spawn it (
subprocess.Popen) with a generatedtorrc(SocksPort auto,ControlPort auto, realCookieAuthentication 1,__OwningControllerProcess <our pid>). - Authenticate over the control port with the real cookie and block until
Tor reports 100% bootstrap - needs at least a minimal control client
(
AUTHENTICATE,GETINFO status/bootstrap-phase,GETINFO net/listeners/socks), a subset of the full control-protocol port already tracked inTODO.mdP2.
Install Tor (apt install tor, brew install tor, …) and make sure
/etc/tor/torrc (or ~/.torrc) has:
SocksPort 9050
ControlPort 9051
CookieAuthentication 0
Then systemctl start tor (or tor -f ~/.torrc). Check:
curl --socks5-hostname 127.0.0.1:9050 https://check.torproject.org/api/ip.
from ra_common.envelope import Envelope
from tor import TorClient
client = TorClient.from_config({})
if client.start(): # False (cleanly) if Tor is unavailable
env = Envelope()
env.headers["url"] = "http://example.onion/"
client.send(env) # body -> env.headers["body"], errors -> env.headers["error"]| key | default | meaning |
|---|---|---|
ra.tor.host |
127.0.0.1 |
local daemon host |
ra.tor.socksPort |
9050 |
local daemon SOCKS5 proxy port |
ra.tor.controlPort |
9051 |
local daemon control port (probed only) |
ra.tor.requestTimeoutSecs |
60 |
per-request timeout |
python3.13 -m venv .venv
.venv/bin/pip install -e ../../common/ra-common-python
.venv/bin/pip install -e '.[test]'
.venv/bin/pytest
Early. HTTP (http://) works; HTTPS needs a TLS layer wrapped around the
SOCKS socket (see TODO.md). The local daemon's control port is only
probed, not spoken — no event stream or hidden-service management yet.
Inbound / onion hosting is not implemented. See DESIGN.md and TODO.md.