# BitChat Tor Integration: How the iOS App Routes All Traffic Through Tor by Default

> Discover how BitChat integrates Tor by default, routing all traffic through the Tor network for enhanced privacy and security. Learn about its Arti client and fail-closed design.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: how-to-guide
- Published: 2026-08-21

---

**BitChat routes all Internet-bound traffic through the Tor network by default using an embedded Arti client that exposes a local SOCKS5 proxy on `127.0.0.1:39050`, with a fail-closed design that prevents clearnet leaks when the user enables "Tor routing."**

The `permissionlesstech/bitchat` repository implements comprehensive **BitChat Tor integration** by embedding the Arti library directly into the Swift application as a local package. This architecture ensures that all Nostr relay connections and geo-relay directory fetches are transparently proxied through the Tor network without requiring users to install external Tor daemon applications.

## Core Architecture of BitChat Tor Integration

### TorManager and the Embedded Arti Client

At the heart of the system lies **`TorManager`**, defined in [`localPackages/Arti/Sources/TorManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/Arti/Sources/TorManager.swift). This singleton owns the **Arti** library—an in-process Tor client compiled as a Swift package within `localPackages/Arti/`.

Key responsibilities include:

- Creating the Arti data directory under Application Support
- Starting the embedded client and exposing a SOCKS5 listener on `127.0.0.1:39050`
- Providing the async `awaitReady()` API for blocking until the proxy is reachable
- Managing lifecycle states including `goDormantOnBackground()` and `shutdownCompletely()`

### TorURLSession for Transparent Proxying

The **`TorURLSession`** class in [`localPackages/Arti/Sources/TorURLSession.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/Arti/Sources/TorURLSession.swift) wraps a shared `URLSession` instance. It automatically configures the SOCKS5 proxy when Tor is enabled via `setProxyMode(useTor:)`, ensuring all network requests route through the local Tor client without manual configuration by network consumers.

### NetworkActivationService for User Control

User preferences are managed by **`NetworkActivationService`** in [`bitchat/Services/NetworkActivationService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NetworkActivationService.swift). This service reads the user-visible "Tor routing" toggle (`userTorEnabled`) and determines whether `TorManager` should auto-start via the `isAutoStartAllowed` property.

## How BitChat Tor Integration Works

The integration follows a strict initialization sequence to maintain privacy guarantees:

1. **Startup Check** – `AppRuntime` queries `NetworkActivationService` to determine if Tor is allowed. If `isAutoStartAllowed` returns true, it calls `TorManager.shared.ensureRunningOnForeground()`.

2. **Arti Initialization** – `TorManager` launches the Arti static library, logging the SOCKS endpoint at `127.0.0.1:39050` upon successful bootstrap.

3. **Readiness Verification** – Network components like **`NostrRelayManager`** (in [`bitchat/Nostr/NostrRelayManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/NostrRelayManager.swift)) and **`GeoRelayDirectory`** call `awaitReady()` before opening connections. This blocks until the proxy responds or a timeout triggers a `.TorBootstrapDidStall` notification handled by `ChatViewModel+Tor.swift`.

4. **Traffic Proxying** – `TorURLSession.setProxyMode(useTor:)` switches the shared session to use the SOCKS5 proxy. All outbound traffic—from Nostr websockets to CSV directory downloads—flows through this single chokepoint.

5. **Graceful Shutdown** – When the app enters the background or the user disables the feature, `TorManager` either enters dormancy or terminates completely to conserve battery and reduce network fingerprinting.

### Fail-Closed Security Model

BitChat employs a **fail-closed** design: if Tor is enabled but not ready, network calls are queued or skipped rather than falling back to clearnet. Developers can override this behavior using the compile-time flag `BITCHAT_DEV_ALLOW_CLEARNET`, but this is strictly disabled in production builds to prevent IP leaks.

## Implementation Example: Using Tor in Swift Code

Enable Tor routing and wait for bootstrap completion before making requests:

```swift
import Bitchat
import Arti

// Enable Tor via the user preference system
NetworkActivationService.shared.setUserTorEnabled(true)

// Check current Tor status
if TorManager.shared.torEnforced && TorManager.shared.isReady {
    print("Tor is active – all traffic is proxy-wrapped")
}

// Block until SOCKS proxy is ready (60s default timeout)
Task {
    do {
        try await TorManager.shared.awaitReady()
        
        // Use the Tor-aware URLSession
        let session = TorURLSession.shared.session
        let (data, _) = try await session.data(from: URL(string: "https://example.onion")!)
    } catch {
        print("Tor bootstrap stalled or disabled")
    }
}

// Manually toggle proxy mode (normally handled automatically by NetworkActivationService)
TorURLSession.shared.setProxyMode(useTor: true)

```

## Summary

- **BitChat Tor integration** embeds the Arti client as a Swift package in `localPackages/Arti/`, eliminating external dependencies on Orbot or other Tor apps.
- The **`TorManager`** singleton exposes a local SOCKS5 proxy at `127.0.0.1:39050` and manages the embedded client lifecycle including dormancy and shutdown.
- **`TorURLSession`** provides a shared `URLSession` that automatically routes traffic through the Tor proxy when enabled, falling back to direct connections only when Tor is explicitly disabled.
- **`NetworkActivationService`** gates Tor startup based on the user-controlled "Tor routing" toggle, ensuring privacy preferences are respected across app sessions.
- The fail-closed architecture ensures no clearnet leakage occurs when Tor is enabled but unavailable, with optional developer overrides via compile-time flags.

## Frequently Asked Questions

### Does BitChat require Tor to function?

No. While **BitChat Tor integration** is enabled by default, users can disable it via the "Tor routing" toggle in Settings. When disabled, `TorURLSession` falls back to a direct `URLSession` connection without proxying through the local SOCKS port.

### What happens if Tor fails to bootstrap?

If the Arti client cannot establish a connection within the timeout period, `TorManager` posts a `.TorBootstrapDidStall` notification. The UI layer—handled in `bitchat/ViewModels/Extensions/ChatViewModel+Tor.swift`—displays a warning to the user, and the fail-closed design prevents any network requests from leaking to clearnet while Tor remains in a non-ready state.

### Can I disable the fail-closed behavior for testing?

Yes, but only at compile time. Developers can define the `BITCHAT_DEV_ALLOW_CLEARNET` flag to permit clearnet connections when Tor is unavailable. This flag is strictly disabled in production builds to guarantee user privacy and prevent accidental IP exposure.

### Which network components use the Tor proxy?

All network-dependent subsystems consume `TorURLSession.shared`, including `NostrRelayManager` for WebSocket connections to Nostr relays and `GeoRelayDirectory` for fetching the relay CSV directory. This ensures comprehensive traffic coverage through the single SOCKS5 endpoint at `127.0.0.1:39050`.