{"id":36236387,"url":"https://github.com/tn3w/stem-rs","last_synced_at":"2026-01-11T06:00:44.546Z","repository":{"id":331255193,"uuid":"1125919856","full_name":"tn3w/stem-rs","owner":"tn3w","description":"A complete Rust library for Tor control protocol — build privacy-focused applications with type-safe, async-first APIs","archived":false,"fork":false,"pushed_at":"2026-01-03T14:50:05.000Z","size":586,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-05T01:58:26.918Z","etag":null,"topics":["anonymity","async","control-protocol","cryptography","onion-routing","privacy","rust","security","socks-proxy","stem","tokio","tor","tor-controller"],"latest_commit_sha":null,"homepage":"https://stem.tn3w.dev/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tn3w.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-31T16:37:35.000Z","updated_at":"2026-01-03T14:50:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tn3w/stem-rs","commit_stats":null,"previous_names":["tn3w/stem-rs"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tn3w/stem-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tn3w%2Fstem-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tn3w%2Fstem-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tn3w%2Fstem-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tn3w%2Fstem-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tn3w","download_url":"https://codeload.github.com/tn3w/stem-rs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tn3w%2Fstem-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28293188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T04:44:51.577Z","status":"ssl_error","status_checked_at":"2026-01-11T04:44:44.232Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anonymity","async","control-protocol","cryptography","onion-routing","privacy","rust","security","socks-proxy","stem","tokio","tor","tor-controller"],"created_at":"2026-01-11T06:00:25.161Z","updated_at":"2026-01-11T06:00:44.535Z","avatar_url":"https://github.com/tn3w.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003estem-rs\u003c/h1\u003e\n\n\u003ch3 align=\"center\"\u003eA complete Rust library for Tor control protocol\u003c/h3\u003e\n\u003cp align=\"center\"\u003e\n  Build privacy-focused applications with type-safe, async-first APIs\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://crates.io/crates/stem-rs\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/v/stem-rs?style=for-the-badge\u0026logo=rust\u0026logoColor=white\u0026color=f74c00\" alt=\"Crates.io\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://stem.tn3w.dev/docs/\"\u003e\n    \u003cimg src=\"https://img.shields.io/docsrs/stem-rs?style=for-the-badge\u0026logo=docs.rs\u0026logoColor=white\" alt=\"docs.rs\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/tn3w/stem-rs/blob/master/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-Apache--2.0-blue?style=for-the-badge\" alt=\"License\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/tn3w/stem-rs/actions\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/tn3w/stem-rs/tests.yml?style=for-the-badge\u0026logo=github\u0026logoColor=white\u0026label=CI\" alt=\"CI\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/tn3w/stem-rs\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/stars/tn3w/stem-rs?style=for-the-badge\u0026logo=github\u0026logoColor=white\" alt=\"Stars\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://stem.tn3w.dev\"\u003e🌐 Website\u003c/a\u003e •\n  \u003ca href=\"https://stem.tn3w.dev/docs/\"\u003e📚 Documentation\u003c/a\u003e •\n  \u003ca href=\"#-quick-start\"\u003e🚀 Quick Start\u003c/a\u003e •\n  \u003ca href=\"#-examples\"\u003e💡 Examples\u003c/a\u003e\n\u003c/p\u003e\n\n## Overview\n\n**stem-rs** is a Rust implementation of [Stem](https://stem.torproject.org/), 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.\n\nWhether 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.\n\n```rust\nuse stem_rs::{Controller, Error};\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Error\u003e {\n    // Connect to Tor's control port\n    let mut ctrl = Controller::from_port(\"127.0.0.1:9051\".parse()?).await?;\n\n    // Authenticate (auto-detects method)\n    ctrl.authenticate(None).await?;\n\n    // Query Tor version\n    let version = ctrl.get_version().await?;\n    println!(\"Connected to Tor {}\", version);\n\n    Ok(())\n}\n```\n\n## ✨ Features\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"50%\"\u003e\n\n### 🔌 Control Socket\n\nConnect to Tor via TCP or Unix domain sockets with full async I/O powered by Tokio.\n\n- TCP port connections (`127.0.0.1:9051`)\n- Unix domain sockets (`/var/run/tor/control`)\n- Non-blocking async operations\n- Automatic reconnection handling\n\n\u003c/td\u003e\n\u003ctd width=\"50%\"\u003e\n\n### 🔐 Authentication\n\nAll authentication methods with automatic detection and secure credential handling.\n\n- **SAFECOOKIE** — Challenge-response (recommended)\n- **COOKIE** — File-based authentication\n- **PASSWORD** — HashedControlPassword\n- **NONE** — Open control port\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd width=\"50%\"\u003e\n\n### 🎛️ Controller API\n\nHigh-level interface for complete Tor interaction.\n\n- Query configuration and status\n- Send signals (NEWNYM, RELOAD, etc.)\n- Create, extend, and close circuits\n- Attach and manage streams\n- Create ephemeral hidden services\n- Map addresses for custom routing\n\n\u003c/td\u003e\n\u003ctd width=\"50%\"\u003e\n\n### 📄 Descriptor Parsing\n\nComplete parsing for all Tor descriptor types.\n\n- **Server Descriptors** — Full relay metadata\n- **Microdescriptors** — Compact client-side info\n- **Consensus Documents** — Network status\n- **Extra-Info** — Bandwidth statistics\n- **Hidden Service** — v2 and v3 descriptors\n- **Bandwidth Files** — Authority measurements\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd width=\"50%\"\u003e\n\n### 📡 Event Handling\n\nSubscribe to real-time Tor events with strongly-typed event structs.\n\n- Bandwidth monitoring (`BW`, `CIRC_BW`)\n- Circuit lifecycle (`CIRC`, `CIRC_MINOR`)\n- Stream tracking (`STREAM`, `STREAM_BW`)\n- Log messages (`DEBUG` → `ERR`)\n- Status updates (`STATUS_*`)\n- Hidden service events (`HS_DESC`)\n\n\u003c/td\u003e\n\u003ctd width=\"50%\"\u003e\n\n### 🚪 Exit Policy\n\nParse and evaluate relay exit policies.\n\n- Full exit policy parsing\n- IPv4 and IPv6 support\n- CIDR notation for address ranges\n- Port range evaluation\n- Policy summarization\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## 🚀 Quick Start\n\nAdd stem-rs to your `Cargo.toml`:\n\n```toml\n[dependencies]\nstem-rs = \"1\"\ntokio = { version = \"1\", features = [\"full\"] }\n```\n\nOr install via cargo:\n\n```bash\ncargo add stem-rs tokio --features tokio/full\n```\n\n### Enable Tor's Control Port\n\nAdd to your `torrc`:\n\n```\nControlPort 9051\nCookieAuthentication 1\n```\n\nOr for password authentication:\n\n```\nControlPort 9051\nHashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C\n```\n\nGenerate a hashed password:\n\n```bash\ntor --hash-password \"your-password\"\n```\n\n## 💡 Examples\n\n### Connect and Authenticate\n\n```rust\nuse stem_rs::{Controller, Error};\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Error\u003e {\n    // Connect via TCP\n    let mut ctrl = Controller::from_port(\"127.0.0.1:9051\".parse()?).await?;\n\n    // Or via Unix socket\n    // let mut ctrl = Controller::from_socket_file(Path::new(\"/var/run/tor/control\")).await?;\n\n    // Auto-detect authentication method\n    ctrl.authenticate(None).await?;\n\n    // Or use password\n    // ctrl.authenticate(Some(\"my_password\")).await?;\n\n    println!(\"Connected!\");\n    Ok(())\n}\n```\n\n### Query Information\n\n```rust\n// Get Tor version\nlet version = ctrl.get_version().await?;\nprintln!(\"Tor {}\", version);\n\n// Get process ID\nlet pid = ctrl.get_pid().await?;\nprintln!(\"PID: {}\", pid);\n\n// Query arbitrary info\nlet traffic_read = ctrl.get_info(\"traffic/read\").await?;\nlet traffic_written = ctrl.get_info(\"traffic/written\").await?;\nprintln!(\"Traffic: {} read, {} written\", traffic_read, traffic_written);\n\n// Get configuration\nlet socks_ports = ctrl.get_conf(\"SocksPort\").await?;\nfor port in socks_ports {\n    println!(\"SOCKS port: {}\", port);\n}\n```\n\n### Circuit Management\n\n```rust\nuse stem_rs::CircStatus;\n\n// List all circuits\nlet circuits = ctrl.get_circuits().await?;\nfor circuit in circuits {\n    if circuit.status == CircStatus::Built {\n        println!(\"Circuit {} ({} hops):\", circuit.id, circuit.path.len());\n        for relay in \u0026circuit.path {\n            println!(\"  → {} ({:?})\", relay.fingerprint, relay.nickname);\n        }\n    }\n}\n\n// Create a new circuit\nlet circuit_id = ctrl.new_circuit(None).await?;\nprintln!(\"Created circuit: {}\", circuit_id);\n\n// Close a circuit\nctrl.close_circuit(circuit_id).await?;\n```\n\n### Stream Management\n\n```rust\nuse stem_rs::StreamStatus;\n\n// List all streams\nlet streams = ctrl.get_streams().await?;\nfor stream in streams {\n    println!(\"Stream {} → {}:{} ({:?})\",\n        stream.id,\n        stream.target_host,\n        stream.target_port,\n        stream.status\n    );\n}\n```\n\n### Event Subscription\n\n```rust\nuse stem_rs::EventType;\n\n// Subscribe to events\nctrl.set_events(\u0026[\n    EventType::Bw,      // Bandwidth\n    EventType::Circ,    // Circuits\n    EventType::Stream,  // Streams\n    EventType::Notice,  // Log messages\n]).await?;\n\n// Process events\nloop {\n    let event = ctrl.recv_event().await?;\n    match event {\n        ParsedEvent::Bandwidth(bw) =\u003e {\n            println!(\"BW: {} read, {} written\", bw.read, bw.written);\n        }\n        ParsedEvent::Circuit(circ) =\u003e {\n            println!(\"Circuit {}: {:?}\", circ.id, circ.status);\n        }\n        ParsedEvent::Log(log) =\u003e {\n            println!(\"[{}] {}\", log.runlevel, log.message);\n        }\n        _ =\u003e {}\n    }\n}\n```\n\n### Send Signals\n\n```rust\nuse stem_rs::Signal;\n\n// Request new identity (new circuits)\nctrl.signal(Signal::Newnym).await?;\n\n// Clear DNS cache\nctrl.signal(Signal::ClearDnsCache).await?;\n\n// Reload configuration\nctrl.signal(Signal::Reload).await?;\n\n// Graceful shutdown\nctrl.signal(Signal::Shutdown).await?;\n```\n\n### Hidden Services\n\n```rust\n// Create ephemeral hidden service (v3 onion)\nlet response = ctrl.create_ephemeral_hidden_service(\n    \u0026[(80, \"127.0.0.1:8080\")],  // Map port 80 to local 8080\n    \"NEW\",                       // Generate new key\n    \"ED25519-V3\",                // Use v3 onion (recommended)\n    \u0026[],                         // No special flags\n).await?;\n\nprintln!(\"Hidden service: {}.onion\", response.service_id);\nprintln!(\"Private key: {:?}\", response.private_key);\n\n// Remove hidden service\nctrl.remove_ephemeral_hidden_service(\u0026response.service_id).await?;\n```\n\n### Descriptor Parsing\n\n```rust\nuse stem_rs::descriptor::{\n    ServerDescriptor, Microdescriptor, NetworkStatusDocument,\n    Descriptor, DigestHash, DigestEncoding,\n    download_consensus, download_server_descriptors,\n};\n\n// Download and parse consensus\nlet consensus = download_consensus(None).await?;\nprintln!(\"Valid until: {}\", consensus.valid_until);\nprintln!(\"Relays: {}\", consensus.routers.len());\n\n// Parse server descriptor\nlet content = std::fs::read_to_string(\"cached-descriptors\")?;\nlet descriptor = ServerDescriptor::parse(\u0026content)?;\nprintln!(\"Relay: {} ({})\", descriptor.nickname, descriptor.fingerprint);\nprintln!(\"Bandwidth: {} avg, {} burst\",\n    descriptor.bandwidth_avg, descriptor.bandwidth_burst);\n\n// Compute digest\nlet digest = descriptor.digest(DigestHash::Sha1, DigestEncoding::Hex)?;\nprintln!(\"Digest: {}\", digest);\n```\n\n### Exit Policy Evaluation\n\n```rust\nuse stem_rs::exit_policy::ExitPolicy;\nuse std::net::IpAddr;\n\nlet policy = ExitPolicy::parse(\"accept *:80, accept *:443, reject *:*\")?;\n\n// Check if traffic is allowed\nlet addr: IpAddr = \"93.184.216.34\".parse()?;\nif policy.can_exit_to(addr, 443) {\n    println!(\"HTTPS traffic allowed\");\n}\n\n// Get policy summary\nprintln!(\"Policy: {}\", policy.summary());\n```\n\n### Version Comparison\n\n```rust\nuse stem_rs::Version;\n\nlet version = ctrl.get_version().await?;\n\n// Compare versions\nlet min_version = Version::parse(\"0.4.0.0\")?;\nif version \u003e= min_version {\n    println!(\"Tor {} supports required features\", version);\n}\n```\n\n## 📦 Module Reference\n\n| Module                                                   | Description                                                       |\n| -------------------------------------------------------- | ----------------------------------------------------------------- |\n| [`controller`](https://stem.tn3w.dev/docs/controller/)   | High-level Tor control interface                                  |\n| [`socket`](https://stem.tn3w.dev/docs/socket/)           | Low-level control socket communication                            |\n| [`auth`](https://stem.tn3w.dev/docs/auth/)               | Authentication methods and protocol info                          |\n| [`descriptor`](https://stem.tn3w.dev/docs/descriptor/)   | Tor descriptor parsing (server, micro, consensus, hidden service) |\n| [`events`](https://stem.tn3w.dev/docs/events/)           | Event types and real-time handling                                |\n| [`exit_policy`](https://stem.tn3w.dev/docs/exit_policy/) | Exit policy parsing and evaluation                                |\n| [`version`](https://stem.tn3w.dev/docs/version/)         | Version parsing and comparison                                    |\n| [`client`](https://stem.tn3w.dev/docs/client/)           | Direct ORPort relay communication                                 |\n| [`interpreter`](https://stem.tn3w.dev/docs/interpreter/) | Interactive Tor control interpreter                               |\n| [`util`](https://stem.tn3w.dev/docs/util/)               | Validation utilities (fingerprints, nicknames, etc.)              |\n\n## 🔒 Security\n\nstem-rs is designed with security as a priority:\n\n- **100% Safe Rust** — No `unsafe` code\n- **Constant-time comparison** — For authentication tokens and cookies\n- **Memory clearing** — Sensitive data cleared after use\n- **Input validation** — Prevents protocol injection attacks\n- **Signature verification** — Optional cryptographic validation for descriptors\n\n## ⚡ Performance\n\n- **Async-first** — Built on Tokio for high-performance async I/O\n- **Zero-copy parsing** — Efficient descriptor parsing where possible\n- **Event streaming** — Non-blocking real-time event handling\n- **Connection pooling** — Efficient socket management\n\n## 🛠️ Requirements\n\n- **Rust** 1.70+\n- **Tokio** runtime\n- **Tor** instance with control port enabled\n\n## 🧪 Testing\n\n```bash\n# Run unit tests\ncargo test\n\n# Run with integration tests (requires running Tor)\ncargo test --features integration\n\n# Run extensive tests\ncargo test --features extensive\n```\n\n## 📊 Comparison with Python Stem\n\nstem-rs maintains functional parity with Python Stem while providing Rust's safety guarantees:\n\n| Feature                | Python Stem | stem-rs |\n| ---------------------- | ----------- | ------- |\n| Control Protocol       | ✅          | ✅      |\n| All Auth Methods       | ✅          | ✅      |\n| Descriptor Parsing     | ✅          | ✅      |\n| Event Handling         | ✅          | ✅      |\n| Exit Policy            | ✅          | ✅      |\n| Hidden Services        | ✅          | ✅      |\n| Type Safety            | ❌          | ✅      |\n| Memory Safety          | ❌          | ✅      |\n| Async/Await            | ❌          | ✅      |\n| Zero-cost Abstractions | ❌          | ✅      |\n\n## 📄 License\n\nCopyright 2026 stem-rs contributors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n## 🤝 Contributing\n\nContributions are welcome! Please feel free to submit issues and pull requests.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## 🔗 Links\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://stem.tn3w.dev\"\u003eWebsite\u003c/a\u003e •\n  \u003ca href=\"https://stem.tn3w.dev/docs/\"\u003eDocumentation\u003c/a\u003e •\n  \u003ca href=\"https://crates.io/crates/stem-rs\"\u003ecrates.io\u003c/a\u003e •\n  \u003ca href=\"https://github.com/tn3w/stem-rs\"\u003eGitHub\u003c/a\u003e •\n  \u003ca href=\"https://stem.torproject.org/\"\u003ePython Stem\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003csub\u003eBuilt with 🦀 by the stem-rs contributors\u003c/sub\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftn3w%2Fstem-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftn3w%2Fstem-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftn3w%2Fstem-rs/lists"}