Stream Isolation Policies in webtor-rs: Controlling Tor Circuit Grouping
webtor-rs provides four distinct stream isolation policies—PerDomain (default), PerSubdomain, PerOrigin, and None—that determine how HTTP requests are grouped into separate Tor circuits to balance privacy protection and network performance.
The webtor-rs crate implements sophisticated stream isolation policies to manage how HTTP connections are routed through the Tor network. These policies control whether requests share circuits or use independent paths, directly impacting both privacy guarantees and resource utilization. The StreamIsolationPolicy enum, defined in webtor/src/isolation.rs (lines 12-25), provides four distinct strategies for circuit grouping.
Understanding Stream Isolation in Tor
When using Tor, each circuit represents a unique encrypted path through the network. Stream isolation determines which HTTP requests share the same circuit. Without isolation, all traffic flows through a single circuit, allowing different destinations to be linked together. The isolation mechanism in webtor-rs generates unique keys based on URL components, then routes requests with matching keys through the same circuit while ensuring different keys obtain separate circuits.
The Four Stream Isolation Policies
webtor-rs implements four distinct isolation strategies, each suited to different privacy and performance requirements.
PerDomain (Default)
The PerDomain policy uses the registrable domain (eTLD+1) as the isolation key. For example, foo.bar.example.com and baz.example.com both map to example.com, causing them to share a circuit. This implementation uses the Public Suffix List via the extract_domain() function in webtor/src/isolation.rs (lines 91-126).
This approach mirrors Tor Browser's first-party isolation, reducing circuit count while preventing cross-site tracking.
PerSubdomain
The PerSubdomain policy treats each full hostname as a distinct isolation key. Unlike PerDomain, foo.bar.example.com and bar.example.com receive separate circuits. The key is derived directly from url.host_str().
Use this when services under the same parent domain require circuit separation, such as when different subdomains represent distinct user accounts or security contexts.
PerOrigin
The PerOrigin policy provides maximum isolation by combining scheme, host, and port. A request to https://example.com:443 uses a different circuit than http://example.com:80. The key format is "{scheme}://{host}:{port}".
This ensures complete separation even between HTTP and HTTPS versions of the same site, or between different ports on the same host.
None
The None policy disables stream isolation entirely. All requests share the same circuit pool, reproducing historic Tor client behavior. This minimizes circuit overhead but eliminates privacy boundaries between destinations.
Configure this only for debugging or when explicit circuit reuse across all destinations is required.
Configuring Stream Isolation
The active policy is stored in TorClientOptions.stream_isolation, defaulting to PerDomain as defined in webtor/src/config.rs (lines 90-93).
To configure a custom policy when building your client:
use webtor::{TorClient, TorClientOptions, StreamIsolationPolicy};
let mut opts = TorClientOptions::default();
// Use PerOrigin for maximum circuit separation
opts.stream_isolation = StreamIsolationPolicy::PerOrigin;
let client = TorClient::new(opts).await?;
How Isolation Keys Are Generated
When processing HTTP requests, the client generates isolation keys via IsolationKey::from_url() as implemented in webtor/src/http.rs (lines 64-66). This function inspects the URL and applies the configured policy to extract the appropriate key components.
For debugging purposes, you can inspect the generated key:
use url::Url;
use webtor::isolation::{IsolationKey, StreamIsolationPolicy};
let url = Url::parse("https://api.example.com/v1/data").unwrap();
let key = IsolationKey::from_url(&url, StreamIsolationPolicy::PerDomain);
// Results in IsolationKey containing "example.com"
The circuit manager then routes requests with matching keys through the same circuit, while ensuring that different keys obtain separate circuits through the Tor network.
Summary
- webtor-rs implements four stream isolation policies to control Tor circuit grouping:
PerDomain,PerSubdomain,PerOrigin, andNone. - The
StreamIsolationPolicyenum is defined inwebtor/src/isolation.rs, with the default policy set toPerDomaininwebtor/src/config.rs. - PerDomain (default) groups by registrable domain using the Public Suffix List, balancing privacy and performance.
- PerSubdomain isolates by full hostname, while PerOrigin adds scheme and port for maximum separation.
- None disables isolation entirely, routing all traffic through shared circuits.
- Configure policies via
TorClientOptions.stream_isolationwhen building yourTorClient.
Frequently Asked Questions
What is the default stream isolation policy in webtor-rs?
The default policy is PerDomain, which groups HTTP requests by registrable domain (eTLD+1). This setting is defined in webtor/src/config.rs at lines 90-93 and provides a balance between privacy protection and circuit efficiency by ensuring that all subdomains of the same site share a circuit while keeping different sites isolated.
How does PerDomain isolation differ from PerSubdomain?
PerDomain extracts the registrable domain using the Public Suffix List, meaning foo.example.com and bar.example.com share the same circuit because both map to example.com. PerSubdomain uses the full hostname as the isolation key, so foo.example.com and bar.example.com receive separate circuits. Choose PerSubdomain when you need stronger isolation between services hosted under the same parent domain.
When should I use PerOrigin isolation?
Use PerOrigin when you require maximum circuit separation, as it incorporates the scheme, host, and port into the isolation key. This ensures that https://example.com and http://example.com use different circuits, as do requests to different ports on the same host. This policy is ideal for high-security applications where even protocol-level distinctions must be isolated to prevent correlation attacks.
Can I disable stream isolation entirely?
Yes, by setting the policy to None, you disable stream isolation and force all HTTP requests to share the same circuit pool. This reproduces the behavior of historic Tor clients and minimizes circuit overhead, but eliminates privacy boundaries between different destinations. Only use this configuration for debugging purposes or in specialized scenarios where you explicitly want to reuse circuits across all requests.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →