# How BitChat Implements TOR Integration for Network Privacy

> Discover how BitChat integrates the Arti Tor client for enhanced network privacy. Learn about its fail-closed proxy architecture designed to prevent IP leaks.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: deep-dive
- Published: 2026-07-27

---

**BitChat embeds a lightweight Tor client (Arti) directly into the app and routes all external traffic through a local SOCKS5 proxy, implementing a fail-closed architecture that prevents IP leaks unless explicitly disabled.**

The permissionlesstech/bitchat repository demonstrates a production-grade approach to anonymous networking by integrating the Arti Tor client as an in-process library rather than relying on external binaries. This TOR integration in BitChat ensures that all external requests are anonymized by default while providing graceful degradation controls and comprehensive bootstrap monitoring.

## Embedded Arti Architecture

BitChat bundles the Arti Tor client as a Rust static library compiled into an XCFramework located at `localPackages/Arti`. Unlike traditional Tor implementations that require a separate `tor` binary and configuration files, this embedded approach runs Arti in-process with no external dependencies.

The client exposes a SOCKS5 proxy listener on the fixed local endpoint `127.0.0.1:39050`. Swift code interacts with the Rust core through Foreign Function Interface (FFI) bindings, calling functions such as `arti_start` and `arti_is_running` to manage the Tor daemon state.

## TorManager: Lifecycle and Bootstrap Monitoring

The `TorManager` singleton (`TorManager.shared`) in [`localPackages/Arti/Sources/TorManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/Arti/Sources/TorManager.swift) serves as the central controller for the Arti instance. It manages the Tor data directory under the app's Application Support folder, monitors connection health, and exposes readiness state through published properties.

Key responsibilities include:

- **Bootstrap tracking**: The manager monitors `bootstrapProgress` and sets `bootstrapDidStall` if the connection fails to establish within 75 seconds.
- **State exposure**: Properties `isReady`, `bootstrapProgress`, and `bootstrapDidStall` allow the UI to display connection status.
- **Lifecycle control**: Handles graceful shutdown and restart via `shutdownCompletely()` and `startIfNeeded()`.

```swift
// Checking Tor readiness before sensitive operations
let ready = await TorManager.shared.awaitReady(timeout: 75)
guard ready else {
    // Handle timeout or stall detection
    return
}

```

## NetworkActivationService: Orchestrating Tor Access

The `NetworkActivationService` in [`bitchat/Services/NetworkActivationService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NetworkActivationService.swift) coordinates when Tor may activate based on user preferences, network reachability, and app policy. It reads the user-visible toggle from `UserDefaults` using the key `networkActivationService.userTorEnabled` and persists the `userTorEnabled` setting.

When network conditions permit (location permission granted, mutual favorites established, or presence in a location channel) and the device has reachable connectivity, the service calls `torController.setAutoStartAllowed(true)` and `applyTorState(torDesired:)`. This triggers `TorManager.startIfNeeded()` if the user has enabled Tor, or initiates shutdown if disabled.

```swift
// User toggles Tor in Settings
Toggle("Use Tor for network", isOn: $networkActivationService.userTorEnabled)
    .onChange { enabled in
        NetworkActivationService.shared.setUserTorEnabled(enabled)
    }

```

## TorURLSession: Proxy-Aware Networking

The `TorURLSession` class in [`localPackages/Arti/Sources/TorURLSession.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/Arti/Sources/TorURLSession.swift) provides a centralized `URLSession` that automatically switches between direct connections and SOCKS5-proxied connections based on the Tor preference. The proxy configuration mirrors the TorManager defaults, targeting `127.0.0.1:39050`.

When Tor restarts, `TorURLSession` recreates its underlying session to bind to the new proxy port, ensuring that requests do not leak onto clearnet during transitions.

```swift
// Making requests through the Tor-enabled session
let url = URL(string: "https://example.com/resource")!
let (data, response) = try await TorURLSession.shared.session.data(for: URLRequest(url: url))

```

## Fail-Closed Security Model

BitChat implements a **fail-closed** networking model that prevents IP leaks. The `TorManager.torEnforced` property forces all network calls to wait for `TorManager.isReady`. If Tor is not ready, requests are queued rather than falling back to clearnet.

The compile-time flag `BITCHAT_DEV_ALLOW_CLEARNET` disables this enforcement for local development. In production builds without this flag, the system guarantees that no traffic leaves the device unproxied unless the user explicitly disables Tor in settings.

```swift
// Programmatically forcing a Tor restart after network changes
await TorManager.shared.ensureRunningOnForeground()

```

## Summary

- BitChat embeds **Arti** as an in-process Rust library via XCFramework, eliminating external binary dependencies.
- The **`TorManager`** singleton monitors bootstrap progress with a 75-second deadline and exposes readiness via `isReady` and `bootstrapDidStall`.
- **`NetworkActivationService`** coordinates Tor activation based on user preferences stored under `networkActivationService.userTorEnabled` and current network policy.
- **`TorURLSession`** provides a proxy-aware `URLSession` that routes traffic through `127.0.0.1:39050` when Tor is active.
- **Fail-closed** architecture ensures requests wait for Tor readiness or fail rather than leaking onto clearnet, unless the `BITCHAT_DEV_ALLOW_CLEARNET` flag is set.

## Frequently Asked Questions

### How does BitChat handle Tor bootstrap failures?

If Arti fails to establish a connection within 75 seconds, `TorManager` sets `bootstrapDidStall` to true and notifies the UI. The app queues network requests rather than falling back to clearnet, maintaining the fail-closed guarantee unless the user explicitly disables Tor routing in settings.

### Can developers bypass Tor for local testing?

Yes. The `BITCHAT_DEV_ALLOW_CLEARNET` compile-time flag disables `torEnforced`, allowing clearnet connections during development. Production builds should never include this flag to ensure the fail-closed privacy guarantee remains active.

### Where does BitChat store the user's Tor preference?

The `NetworkActivationService` persists the Tor setting using `UserDefaults` with the key `networkActivationService.userTorEnabled`. This boolean drives the `userTorEnabled` property and determines whether `applyTorState(torDesired:)` triggers `startIfNeeded()` or shutdown.

### What happens to network requests when Tor is restarting?

`TorURLSession` recreates its underlying `URLSession` instance when Tor restarts to bind to the new SOCKS5 port. During the transition, the fail-closed mechanism ensures requests await `isReady` or timeout rather than transmitting over clearnet, preventing IP leaks during circuit re-establishment.