# Stream Isolation Policies in webtor-rs: Controlling Tor Circuit Grouping

> Explore webtor-rs stream isolation policies: PerDomain, PerSubdomain, PerOrigin, and None. Control Tor circuit grouping for optimal privacy and performance.

- Repository: [igor53627/webtor-rs](https://github.com/igor53627/webtor-rs)
- Tags: deep-dive
- Published: 2026-03-04

---

**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`](https://github.com/igor53627/webtor-rs/blob/main/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`](https://github.com/igor53627/webtor-rs/blob/main/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`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/config.rs) (lines 90-93).

To configure a custom policy when building your client:

```rust
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`](https://github.com/igor53627/webtor-rs/blob/main/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:

```rust
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`, and `None`.
- The `StreamIsolationPolicy` enum is defined in [`webtor/src/isolation.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/isolation.rs), with the default policy set to `PerDomain` in [`webtor/src/config.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/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_isolation` when building your `TorClient`.

## 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`](https://github.com/igor53627/webtor-rs/blob/main/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.