Overview
stem-rs is a Rust implementation of Stem, the Python library for interacting with Tor's control protocol. It provides idiomatic, type-safe Rust APIs while maintaining complete functional parity with Python Stem.
Whether you're building privacy tools, monitoring Tor relays, managing circuits, or creating onion services — stem-rs gives you the building blocks you need with the safety guarantees Rust provides.
use ;
async
✨ Features
🔌 Control Socket
Connect to Tor via TCP or Unix domain sockets with full async I/O powered by Tokio.
- TCP port connections (
127.0.0.1:9051) - Unix domain sockets (
/var/run/tor/control) - Non-blocking async operations
- Automatic reconnection handling
🔐 Authentication
All authentication methods with automatic detection and secure credential handling.
- SAFECOOKIE — Challenge-response (recommended)
- COOKIE — File-based authentication
- PASSWORD — HashedControlPassword
- NONE — Open control port
🎛️ Controller API
High-level interface for complete Tor interaction.
- Query configuration and status
- Send signals (NEWNYM, RELOAD, etc.)
- Create, extend, and close circuits
- Attach and manage streams
- Create ephemeral hidden services
- Map addresses for custom routing
📄 Descriptor Parsing
Complete parsing for all Tor descriptor types.
- Server Descriptors — Full relay metadata
- Microdescriptors — Compact client-side info
- Consensus Documents — Network status
- Extra-Info — Bandwidth statistics
- Hidden Service — v2 and v3 descriptors
- Bandwidth Files — Authority measurements
📡 Event Handling
Subscribe to real-time Tor events with strongly-typed event structs.
- Bandwidth monitoring (
BW,CIRC_BW) - Circuit lifecycle (
CIRC,CIRC_MINOR) - Stream tracking (
STREAM,STREAM_BW) - Log messages (
DEBUG→ERR) - Status updates (
STATUS_*) - Hidden service events (
HS_DESC)
🚪 Exit Policy
Parse and evaluate relay exit policies.
- Full exit policy parsing
- IPv4 and IPv6 support
- CIDR notation for address ranges
- Port range evaluation
- Policy summarization
🚀 Quick Start
Add stem-rs to your Cargo.toml:
[]
= "1"
= { = "1", = ["full"] }
Or install via cargo:
Enable Tor's Control Port
Add to your torrc:
ControlPort 9051
CookieAuthentication 1
Or for password authentication:
ControlPort 9051
HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C
Generate a hashed password:
💡 Examples
Connect and Authenticate
use ;
async
Query Information
// Get Tor version
let version = ctrl.get_version.await?;
println!;
// Get process ID
let pid = ctrl.get_pid.await?;
println!;
// Query arbitrary info
let traffic_read = ctrl.get_info.await?;
let traffic_written = ctrl.get_info.await?;
println!;
// Get configuration
let socks_ports = ctrl.get_conf.await?;
for port in socks_ports
Circuit Management
use CircStatus;
// List all circuits
let circuits = ctrl.get_circuits.await?;
for circuit in circuits
// Create a new circuit
let circuit_id = ctrl.new_circuit.await?;
println!;
// Close a circuit
ctrl.close_circuit.await?;
Stream Management
use StreamStatus;
// List all streams
let streams = ctrl.get_streams.await?;
for stream in streams
Event Subscription
use EventType;
// Subscribe to events
ctrl.set_events.await?;
// Process events
loop
Send Signals
use Signal;
// Request new identity (new circuits)
ctrl.signal.await?;
// Clear DNS cache
ctrl.signal.await?;
// Reload configuration
ctrl.signal.await?;
// Graceful shutdown
ctrl.signal.await?;
Hidden Services
use SocketAddr;
// Create ephemeral hidden service
let ports = vec!;
let onion_address = ctrl.create_ephemeral_hidden_service.await?;
println!;
// Remove hidden service
ctrl.remove_ephemeral_hidden_service.await?;
Descriptor Parsing
use ;
// Download and parse consensus
let consensus = download_consensus.await?;
println!;
println!;
// Parse server descriptor
let content = read_to_string?;
let descriptor = parse?;
println!;
println!;
// Compute digest
let digest = descriptor.digest?;
println!;
Exit Policy Evaluation
use ExitPolicy;
use IpAddr;
let policy = parse?;
// Check if traffic is allowed
let addr: IpAddr = "93.184.216.34".parse?;
if policy.can_exit_to
// Get policy summary
println!;
Version Comparison
use Version;
let version = ctrl.get_version.await?;
// Compare versions
let min_version = parse?;
if version >= min_version
📦 Module Reference
| Module | Description |
|---|---|
controller |
High-level Tor control interface |
socket |
Low-level control socket communication |
auth |
Authentication methods and protocol info |
descriptor |
Tor descriptor parsing (server, micro, consensus, hidden service) |
events |
Event types and real-time handling |
exit_policy |
Exit policy parsing and evaluation |
version |
Version parsing and comparison |
client |
Direct ORPort relay communication |
interpreter |
Interactive Tor control interpreter |
util |
Validation utilities (fingerprints, nicknames, etc.) |
🔒 Security
stem-rs is designed with security as a priority:
- 100% Safe Rust — No
unsafecode - Constant-time comparison — For authentication tokens and cookies
- Memory clearing — Sensitive data cleared after use
- Input validation — Prevents protocol injection attacks
- Signature verification — Optional cryptographic validation for descriptors
⚡ Performance
- Async-first — Built on Tokio for high-performance async I/O
- Zero-copy parsing — Efficient descriptor parsing where possible
- Event streaming — Non-blocking real-time event handling
- Connection pooling — Efficient socket management
🛠️ Requirements
- Rust 1.70+
- Tokio runtime
- Tor instance with control port enabled
🧪 Testing
# Run unit tests
# Run with integration tests (requires running Tor)
# Run extensive tests
📊 Comparison with Python Stem
stem-rs maintains functional parity with Python Stem while providing Rust's safety guarantees:
| Feature | Python Stem | stem-rs |
|---|---|---|
| Control Protocol | ✅ | ✅ |
| All Auth Methods | ✅ | ✅ |
| Descriptor Parsing | ✅ | ✅ |
| Event Handling | ✅ | ✅ |
| Exit Policy | ✅ | ✅ |
| Hidden Services | ✅ | ✅ |
| Type Safety | ❌ | ✅ |
| Memory Safety | ❌ | ✅ |
| Async/Await | ❌ | ✅ |
| Zero-cost Abstractions | ❌ | ✅ |
📄 License
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
🤝 Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request