Skip to main content

Best-in-class Tor HTTP client and server library - consume AND host .onion services

Project description

🧅 hypertor

The Tor network library for Rust and Python — consume AND host onion services with the simplicity of reqwest and axum.

CI Crates.io Documentation License: MIT MSRV


Why hypertor?

Most Tor libraries only do one thing: make requests. hypertor does both:

Component Purpose Similar To
TorClient Make HTTP requests over Tor reqwest, httpx
OnionService Host .onion services axum, FastAPI

Production-Ready Security — All features wired to real arti APIs:

Feature Purpose arti API
🛡️ Vanguards Guard discovery protection VanguardConfigBuilder::mode()
Proof-of-Work DoS protection (Equi-X) OnionServiceConfigBuilder::enable_pow()
🚦 Rate Limiting Intro point flooding protection rate_limit_at_intro()
🔐 Client Auth Restricted service discovery RestrictedDiscoveryConfigBuilder
🌉 Bridges Censorship circumvention TorClientConfigBuilder::bridges()
🔌 Pluggable Transports Traffic obfuscation TransportConfigBuilder

Quick Start

Rust — Client

use hypertor::{TorClient, Result};

#[tokio::main]
async fn main() -> Result<()> {
    // Create client (connects to Tor network)
    let client = TorClient::new().await?;
    
    // Make requests just like reqwest
    let resp = client
        .get("http://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion")?
        .send()
        .await?;
    
    println!("Tor Check: {}", resp.text()?);
    Ok(())
}

Rust — Server

use hypertor::{OnionApp, ServeResponse};

#[tokio::main]
async fn main() -> hypertor::Result<()> {
    let app = OnionApp::new()
        .get("/", || async { ServeResponse::text("Hello from .onion!") })
        .get("/health", || async { 
            ServeResponse::text(r#"{"status":"ok"}"#)
                .with_header("Content-Type", "application/json")
        });
    
    // Start the hidden service
    let addr = app.run().await?;
    println!("🧅 Live at: {}", addr);
    Ok(())
}

Python — Client

import asyncio
from hypertor import AsyncClient

async def main():
    async with AsyncClient(timeout=60) as client:
        # Check our Tor IP
        resp = await client.get("https://check.torproject.org/api/ip")
        print(f"Tor IP: {resp.json().get('IP')}")
        
        # Access an onion service
        resp = await client.get(
            "http://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion"
        )
        print(f"Status: {resp.status_code}")

asyncio.run(main())

Python — Server (FastAPI-style)

from hypertor import OnionApp

app = OnionApp()

@app.get("/")
async def home():
    return "Welcome to my .onion service!"

@app.post("/api/echo")
async def echo(request):
    data = await request.json()
    return {"received": data}

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    return {"id": user_id, "name": "Alice"}

if __name__ == "__main__":
    app.run()  # 🧅 Service live at: xyz...xyz.onion

Installation

Rust

[dependencies]
hypertor = "0.4"
tokio = { version = "1", features = ["full"] }

Python

pip install hypertor

TLS Backends

hypertor defaults to rustls for security reasons:

Backend Default Security Notes
rustls Best Consistent TLS fingerprint, pure Rust, memory-safe
native-tls Good Uses OS TLS stack, platform-specific fingerprints

Why rustls is the default

For anonymity-focused applications, TLS fingerprinting is a real threat. Different TLS libraries produce different fingerprints based on cipher suite ordering, extensions, and timing. Using native-tls means:

  • Linux: OpenSSL fingerprint
  • macOS: SecureTransport fingerprint (also has Tor stream compatibility issues)
  • Windows: SChannel fingerprint

This leaks your operating system to any observer. With rustls, you get the same fingerprint on all platforms, making it harder to distinguish users.

Additionally, rustls is:

  • Memory-safe: Pure Rust, no C library vulnerabilities
  • Auditable: Easier to verify for security
  • Isolated: Not affected by compromised system CA stores

If you need native-tls for specific compatibility:

[dependencies]
hypertor = { version = "0.4", default-features = false, features = ["client", "native-tls"] }

Features

🔌 TorClient — HTTP Client

use hypertor::{TorClient, IsolationLevel};
use std::time::Duration;

// Builder pattern for full control
let client = TorClient::builder()
    .timeout(Duration::from_secs(60))
    .max_connections(20)
    .isolation(IsolationLevel::PerRequest)  // Fresh circuit per request
    .follow_redirects(true)
    .build()
    .await?;

// POST with typed JSON
let resp = client.post("http://api.onion/users")?
    .json(&User { name: "Alice".into() })
    .send().await?;

// Query parameters
let resp = client.get("http://api.onion/search")?
    .query(&[("q", "rust"), ("page", "1")])
    .send().await?;

🧅 OnionApp — Hidden Service Framework

use hypertor::{OnionApp, OnionAppConfig, ServeRequest, ServeResponse, get};
use std::time::Duration;

let config = OnionAppConfig::new()
    .with_port(80)
    .with_timeout(Duration::from_secs(30))
    .with_key_path("/var/lib/myapp/keys");  // Persist .onion address

let app = OnionApp::with_config(config)
    .get("/", || async { ServeResponse::text("Home") })
    .post("/api/data", |req: ServeRequest| async move {
        let body = req.text().unwrap_or_default();
        ServeResponse::text(&format!(r#"{{"echo":{}}}"#, body))
    })
    .route("/health", get(|| async {
        ServeResponse::text(r#"{"status":"healthy"}"#)
            .with_header("Content-Type", "application/json")
    }));

let addr = app.run().await?;  // Returns "abc...xyz.onion"

🔐 Security Configuration

hypertor provides security presets that configure real arti hardening features:

use hypertor::security::{SecurityLevel, ServiceSecurityConfig};
use hypertor::onion_service::OnionServiceConfig;

// Security presets for onion services
let standard = ServiceSecurityConfig::standard();   // Basic protection
let enhanced = ServiceSecurityConfig::enhanced();   // PoW + rate limiting
let maximum = ServiceSecurityConfig::maximum();     // Full hardening

// Or configure manually with fluent API
let config = OnionServiceConfig::new("my-service")
    .with_pow()                        // Proof-of-Work (Equi-X)
    .pow_queue_depth(16000)            // Queue depth
    .rate_limit_at_intro(10.0, 20)     // Rate: 10/s, burst: 20
    .max_streams_per_circuit(100)      // Stream limit
    .vanguards_full()                  // Full vanguards
    .num_intro_points(5);              // High availability

🌉 Censorship Circumvention (China, Iran, Russia)

use hypertor::{TorClientBuilder, VanguardMode};

let client = TorClientBuilder::new()
    // Multiple bridges for redundancy
    .bridge("obfs4 192.0.2.1:443 FINGERPRINT cert=... iat-mode=0")
    .bridge("obfs4 192.0.2.2:443 FINGERPRINT cert=... iat-mode=0")
    // Pluggable transport binary
    .transport("obfs4", "/usr/bin/obfs4proxy")
    // Full vanguards for hostile networks
    .vanguards(VanguardMode::Full)
    .build()
    .await?;

🔄 Stream Isolation

Keep different activities on separate Tor circuits:

use hypertor::{TorClient, IsolationToken};

let client = TorClient::new().await?;

// Create isolated sessions
let banking = IsolationToken::new();
let browsing = IsolationToken::new();

// These use the same circuit (banking identity)
client.get("http://bank.onion")?.isolation(banking.clone()).send().await?;
client.get("http://bank.onion/transfer")?.isolation(banking.clone()).send().await?;

// This uses a different circuit (browsing identity)
client.get("http://news.onion")?.isolation(browsing.clone()).send().await?;

⚡ Resilience Features

use hypertor::{
    CircuitBreaker, BreakerConfig,
    AdaptiveRetry, AdaptiveRetryConfig,
    TokenBucket,
};

// Circuit breaker - fail fast when service is down
let breaker = CircuitBreaker::new(BreakerConfig {
    failure_threshold: 5,
    reset_timeout: Duration::from_secs(30),
    ..Default::default()
});

// Adaptive retry - learns optimal behavior
let retry = AdaptiveRetry::new(AdaptiveRetryConfig {
    max_attempts: 3,
    min_delay: Duration::from_millis(100),
    max_delay: Duration::from_secs(10),
    ..Default::default()
});

// Rate limiting
let limiter = TokenBucket::new(100, 100.0);  // 100 req/sec

📊 Observability

use hypertor::{TorMetrics, export_metrics};

let metrics = TorMetrics::new();

// Record operations
metrics.record_request("GET", 200, 1.5, 100, 5000);
metrics.record_circuit_build(true, 2.0);

// Export Prometheus format
let prometheus_text = metrics.export();
// # HELP hypertor_http_requests_total Total HTTP requests
// # TYPE hypertor_http_requests_total counter
// hypertor_http_requests_total{method="GET",status="200"} 1

🌉 Bridge Support (Censorship Circumvention)

use hypertor::{TorClientBuilder, VanguardMode};

// Configure bridges and transports via builder
let client = TorClientBuilder::new()
    .bridge("obfs4 192.0.2.1:443 FINGERPRINT cert=CERT iat-mode=0")
    .transport("obfs4", "/usr/bin/obfs4proxy")
    .vanguards(VanguardMode::Full)
    .build()
    .await?;

� SOCKS5 Proxy

Run a local SOCKS5 proxy to route ANY application through Tor:

use hypertor::{Socks5Proxy, ProxyConfig};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

// Start SOCKS5 proxy on localhost:9050
let proxy = Socks5Proxy::with_defaults();
proxy.run().await?;  // Runs on 127.0.0.1:9050

Then use with any SOCKS5-compatible tool:

# curl
curl --socks5-hostname 127.0.0.1:9050 https://check.torproject.org/api/ip

# Python requests
proxies = {'http': 'socks5h://127.0.0.1:9050', 'https': 'socks5h://127.0.0.1:9050'}
requests.get('https://example.com', proxies=proxies)

# wget, git, ssh, browsers...

�🔌 WebSocket over Tor

use hypertor::websocket::TorWebSocket;

// Connect to WebSocket over Tor
let mut ws = TorWebSocket::connect("ws://chat.onion/ws").await?;

// Send and receive messages
ws.send_text("Hello, Tor!").await?;
let msg = ws.recv().await?;

📡 HTTP/2 Support

use hypertor::http2::{Http2Connection, Http2Config};

// HTTP/2 multiplexing over Tor
let mut conn = Http2Connection::client(Http2Config::default());
let stream_id = conn.create_stream()?;
conn.send_headers(stream_id, headers, false)?;
conn.send_data(stream_id, body, true)?;

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        YOUR APPLICATION                          │
├─────────────────────────────────────────────────────────────────┤
│  TorClient          │  OnionService       │  OnionApp (serve)   │
│  (HTTP over Tor)    │  (host .onion)      │  (axum-like API)    │
├─────────────────────────────────────────────────────────────────┤
│                         hypertor core                            │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐   │
│  │ Circuits │ │ Security │ │Resilience│ │ Observability    │   │
│  │ Pooling  │ │ Vanguards│ │ Retry    │ │ Metrics/Tracing  │   │
│  │ Isolation│ │ PoW/Auth │ │ Breaker  │ │ Health Checks    │   │
│  └──────────┘ └──────────┘ └──────────┘ └──────────────────┘   │
├─────────────────────────────────────────────────────────────────┤
│                     arti-client 0.38 (Tor)                       │
└─────────────────────────────────────────────────────────────────┘

Module Overview

Category Modules
Core client, serve, onion_service, config, error, body
Security security (presets, vanguards, PoW, rate limiting)
Networking pool, isolation, circuit, proxy (SOCKS5), dns, doh, tls
Resilience retry, breaker, rotation, adaptive, backpressure
Performance cache, dedup, ratelimit, queue, prewarm, batch
Protocols http2, websocket
Observability observability, prometheus, tracing, health, metrics

Examples

Basic Client Usage

cargo run --example basic_usage

Security Features Demo

cargo run --example security_features

SOCKS5 Proxy

cargo run --example socks_proxy
# Then: curl --socks5-hostname 127.0.0.1:9050 https://check.torproject.org/api/ip

Metrics

Metric Value
Source Files 51
Lines of Code ~26,900
Unit Tests 207
Integration Tests 26
Security Tests 58
Doc Tests 1
Total Tests 292
arti Version 0.38
Rust Edition 2024
MSRV 1.85

Run all tests:

cargo test                     # All 292 tests
cargo test --test security     # Security tests (58)

Security Considerations

What hypertor Protects Against

Threat Protection Implementation
Guard discovery Vanguards (Lite/Full) VanguardConfigBuilder::mode()
DoS attacks Proof-of-Work (Equi-X) OnionServiceConfigBuilder::enable_pow()
Intro flooding Rate limiting rate_limit_at_intro()
Stream flooding Stream limits max_concurrent_streams_per_circuit()
IP exposure Bridges + transports TorClientConfigBuilder::bridges()
Unauthorized access Client authorization RestrictedDiscoveryConfigBuilder
Secret leaks Zeroize on drop SecretKey with ZeroizeOnDrop

What hypertor Does NOT Protect Against

  • ❌ Application-level data leaks (your code)
  • ❌ Timing attacks from your application logic
  • ❌ Malware on your system
  • ❌ Compromised exit nodes (for clearnet access)

Development

# Run tests
cargo test --lib

# Run clippy
cargo clippy --lib -- -D warnings

# Build Python wheel
maturin develop --features python

# Run example
cargo run --example basic_usage

# Serve docs locally
just docs

Documentation


⚠️ Disclaimer

This software is provided for educational and research purposes only.

  • No Anonymity Guarantee: While hypertor leverages the Tor network via arti, no software can guarantee complete anonymity. Your operational security practices, threat model, and usage patterns significantly impact your privacy.
  • No Warranty: This software is provided "as is" without warranty of any kind. The authors are not responsible for any damages or legal consequences arising from its use.
  • Legal Compliance: Users are solely responsible for ensuring their use of this software complies with all applicable laws and regulations in their jurisdiction.
  • Not Endorsed by Tor Project: This is an independent project and is not affiliated with, endorsed by, or sponsored by The Tor Project.
  • Security Considerations: Always review the Security Guide before deploying in production.

License

MIT License. See LICENSE for details.

Contributing

Contributions welcome! Please open issues and pull requests on GitHub.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hypertor-0.2.2.tar.gz (306.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

hypertor-0.2.2-cp311-cp311-win_amd64.whl (7.3 MB view details)

Uploaded CPython 3.11Windows x86-64

hypertor-0.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

hypertor-0.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (7.3 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

hypertor-0.2.2-cp311-cp311-macosx_11_0_arm64.whl (7.1 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

File details

Details for the file hypertor-0.2.2.tar.gz.

File metadata

  • Download URL: hypertor-0.2.2.tar.gz
  • Upload date:
  • Size: 306.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for hypertor-0.2.2.tar.gz
Algorithm Hash digest
SHA256 5617400cde0436f6f1042d20fb767159c8abf7db829dd1c4697bad69e6786c00
MD5 0af61381bd0dd0d54d7d787d1612f53b
BLAKE2b-256 3dec2add5bd27fe1a184d371b67763a757f81ffc2106e0c48746b2b86440c32f

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.2.tar.gz:

Publisher: release.yml on hupe1980/hypertor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hypertor-0.2.2-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: hypertor-0.2.2-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 7.3 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for hypertor-0.2.2-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 199f9a26ef9eb8dbdc559bcfab5708c0048a155dcbde9a2a606ac7c91a520a45
MD5 892a540cc969f505fa34eb43c5118d49
BLAKE2b-256 45a336c588ed9e24964044c1b252ebc4c2966878d5960397d025ada4c8f243bc

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.2-cp311-cp311-win_amd64.whl:

Publisher: release.yml on hupe1980/hypertor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hypertor-0.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for hypertor-0.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 dd9157f5a90e22fd70a8e20114f5cd16e36191ff56a8e0338066bde7d20edd1a
MD5 02c9da4e106e1e211b68191a60f6edf3
BLAKE2b-256 a79d61b4bbc1bd60cc321f4fa2351af4d16c72be7bbf3769244071d24a535b47

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on hupe1980/hypertor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hypertor-0.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for hypertor-0.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 117f6691dde248e53abada2643c4c6dcd30d2b54f7b3d94d07741a766b877c8c
MD5 86caceef882d228e2d1b0e7d874c0607
BLAKE2b-256 ed17590af85d5e4d4afd1edf6832af32004b0c2a034d42a88d74e5e96867938c

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on hupe1980/hypertor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hypertor-0.2.2-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for hypertor-0.2.2-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 9d47e7eeeca47e0c3b8a85ede3941017d6d4d70af681b711712ec5d5243471af
MD5 38cd5974c834f8d5bc9e2c935a8d3b5b
BLAKE2b-256 e4a1afb55566cc58258e6ff4d19ec96d769c0efc308f378a441bdb5a76ea8db4

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.2-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: release.yml on hupe1980/hypertor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page