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.1.tar.gz (307.5 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.1-cp311-cp311-win_amd64.whl (7.3 MB view details)

Uploaded CPython 3.11Windows x86-64

hypertor-0.2.1-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.1-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.1-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.1.tar.gz.

File metadata

  • Download URL: hypertor-0.2.1.tar.gz
  • Upload date:
  • Size: 307.5 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.1.tar.gz
Algorithm Hash digest
SHA256 5a175098b8751fce45d5f8c64417fbee6b859ab65e2a5db11d13784aa58cbaa1
MD5 06db7089adf5ea6d1f34792e2ddeb1c4
BLAKE2b-256 6283b34f30476931b8dfe012ff14dbcac4e08134a647b2a9043f3c55f4e32fa5

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.1.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.1-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: hypertor-0.2.1-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.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 318fed5da7e771f58f24b41eed0642b8d65dbffd4634fe265bff94b3c34c3438
MD5 441a242d1fd52f571d44eb99b586aa05
BLAKE2b-256 212ec8c823aab357a97d548c91300901538133abb7f85aa316531fc9f7116e7d

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.1-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.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for hypertor-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 69571c9d5404fa583b5676e683b578c8b32f7bdcdd9d86246d629f39e3a1c0b1
MD5 7cd7a751be15da210986da776d6afef6
BLAKE2b-256 eda39772d120d19e1ec177e5a367df24a549eb2579e67b6a6d889120d83196eb

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.1-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.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for hypertor-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 832812af4f861413a99a5a0d871c614dc9497891fda61dda6648562ca92f2c01
MD5 ed198c378b504a0bab0411b0eac7f52a
BLAKE2b-256 6fbe0568af123138042e41d14f6a824fc52b2514475dfc9965549a66b4507812

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.1-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.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for hypertor-0.2.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 bc828233d7f327b913494259e0c7fcd456bb3d8f186e7c3c46f1458f57568729
MD5 c0674a7443ddfe1c8d0244566397a8e7
BLAKE2b-256 f109ea2ab32d4150330707e7c3ddfb85fa65d415e8643c348726e63281d6fef6

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypertor-0.2.1-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