Skip to main content

Crate hypertor

Crate hypertor 

Source
Expand description

Tor for Rust: make HTTP requests over the Tor network, and host onion services.

hypertor is a thin, honest layer over two mature pieces of software: arti, the Tor Project’s Rust implementation of Tor, and hyper, the HTTP stack reqwest is built on. It supplies the seam between them and the ergonomics on top; it implements no Tor protocol and no HTTP protocol of its own.

§Making requests

use hypertor::TorClient;

#[tokio::main]
async fn main() -> hypertor::Result<()> {
    let client = TorClient::new().await?;

    let body = client
        .get("https://check.torproject.org/api/ip")?
        .send()
        .await?
        .error_for_status()?
        .text()?;

    println!("{body}");
    Ok(())
}

.onion addresses work the same way, and never touch an exit relay:

let response = client.get("http://example.onion/api")?.send().await?;

§Hosting an onion service

use hypertor::{OnionApp, ServeResponse};

pub async fn run() -> hypertor::Result<()> {
    let app = OnionApp::new()
        .get("/", |_req| async { ServeResponse::text("hello") });

    let service = app.serve("my-service").await?;
    println!("{}", service.onion_address());
    service.wait().await
}

§What hypertor adds over wiring arti and hyper together yourself

  • Connection pooling that actually pools. The Tor connector plugs into hyper’s pooling client, so a warm circuit is reused across requests rather than paying seconds of circuit setup every time.
  • Circuit isolation as a first-class concept. IsolationLevel and IsolationToken let you decide explicitly which of your activities may be linked to one another.
  • Redirects that do not betray you. Credentials are stripped across origins, and a redirect from a .onion out to clearnet is refused unless you opt in. See RedirectPolicy.
  • No local DNS, ever. Hostnames are resolved by the exit relay. A Tor integration that resolves names locally leaks every site you visit.
  • Bodies that stream in both directions. Body::from_file uploads without buffering; send_streaming downloads without buffering. Decompression happens incrementally, so the size limit bounds a decompression bomb instead of discovering one.
  • Errors that do not leak. Hostnames in Error are scrubbed when displayed, because error messages end up in logs.
  • Onion services that are not distinguishable. A hosted service accepts only BEGIN streams for the virtual ports it publishes, which is what every other implementation does — behaving differently is itself a fingerprint.

§What hypertor does not do

It is not an anonymity system in its own right, and no library can be. Tor protects the network path; it cannot protect you from an application that logs in with your real identity, from timing patterns in your own traffic, or from anything running on a compromised machine. Read the Tor Project’s guidance before relying on this for anything that matters.

It also keeps no cookie jar, deliberately. A jar shared across requests would relink activities that IsolationLevel exists to keep apart — the separation would still hold at the network layer while the application layer gave the correlation away for free. Set Cookie yourself, and its scope is yours to choose.

§Feature flags

FeatureDefaultWhat it adds
clientyesTorClient and the HTTP client stack
rustlsyesTLS via rustls — one fingerprint on every platform
native-tlsnoTLS via the OS stack; leaks your platform, see tls
servernoOnionService and OnionApp
pownoEqui-X proof-of-work; pulls in LGPL-3.0 crates
socksnoSocksProxy, a local SOCKS5 front-end
wsnoTorWebSocket
static-sqlitenolink SQLite statically; needed on Windows

full is client + server + socks + ws + rustls. It deliberately excludes pow, so a default build stays entirely permissively licensed.

There is no python feature. The bindings are a separate, unpublished crate (bindings/python in the repository) that consumes this one through its public API, so a Rust dependency on hypertor never pulls in pyo3.

Re-exports§

pub use config::Config;
pub use config::ConfigBuilder;
pub use config::DEFAULT_USER_AGENT;
pub use config::TlsConfig;
pub use config::TlsVersion;
pub use error::Error;
pub use error::Result;
pub use isolation::IsolatedSession;
pub use isolation::IsolationLevel;
pub use isolation::IsolationToken;
pub use redirect::RedirectAction;
pub use redirect::RedirectPolicy;
pub use stream::TorStream;
pub use body::Body;
pub use body::Encoding;
pub use client::TorClient;
pub use client::TorClientBuilder;
pub use request::RequestBuilder;
pub use response::Response;
pub use response::Streaming;
pub use onion_service::OnionService;
pub use onion_service::OnionServiceBuilder;
pub use onion_service::OnionServiceConfig;
pub use onion_service::OnionStream;
pub use serve::OnionApp;
pub use serve::Request as ServeRequest;
pub use serve::Response as ServeResponse;
pub use serve::ServingApp;
pub use socks::SocksConfig;
pub use socks::SocksProxy;
pub use websocket::Close as WsClose;
pub use websocket::Message as WsMessage;
pub use websocket::Receiver as WsReceiver;
pub use websocket::Sender as WsSender;
pub use websocket::TorWebSocket;
pub use websocket::TorWebSocketBuilder;

Modules§

body
Request bodies, and response body decoding.
client
The Tor HTTP client.
config
Client configuration.
error
Typed errors for hypertor.
isolation
Circuit isolation.
onion_service
Hosting onion services.
prelude
The common imports, in one line.
redirect
Redirect handling.
request
Building and sending requests.
response
HTTP responses.
serve
A small HTTP framework for onion services.
socks
A local SOCKS5 proxy that routes traffic over Tor.
stream
A Tor stream, optionally wrapped in TLS.
tls
TLS for clearnet targets reached through a Tor exit relay.
websocket
WebSocket over Tor.

Enums§

VanguardMode
Vanguard mode, re-exported from arti.

Constants§

VERSION
The version of this crate.