# How Bitchat Android Integrates Tor for Private Internet Access: Complete Technical Guide

> Discover how Bitchat Android integrates Tor using a custom Arti client to enable private internet access, routing all traffic through the Tor network without external binaries.

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

---

**Bitchat Android integrates Tor by embedding a custom-built Arti client (Rust's native Tor implementation) through the `ArtiTorManager` singleton, which exposes a local SOCKS proxy and rebuilds all OkHttp clients to route HTTP, WebSocket, and Nostr traffic through the Tor network without external binaries.**

Modern messaging apps demand privacy by default. Bitchat Android, an open-source secure messaging client, implements **onion-routing** directly inside the application using Arti—The Tor Project's official Rust implementation. This architecture delivers true private internet access without requiring users to install separate Tor apps or binaries, fully compliant with Google Play's 16 KB page-size constraints.

---

## Architecture Overview: The ArtiTorManager Singleton

At the heart of Bitchat's Tor integration sits `ArtiTorManager`, a Kotlin singleton that orchestrates the entire Tor lifecycle. Unlike traditional Tor integrations that spawn external processes, this manager embeds Arti directly via the `info.guardianproject.arti` library imported in [`app/src/main/java/com/bitchat/android/net/ArtiTorManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/net/ArtiTorManager.kt).

The manager coordinates five critical responsibilities:

1. **Tor process bootstrap** – Initializes and monitors the Arti runtime
2. **SOCKS proxy exposure** – Publishes a local proxy address immediately upon activation
3. **State publication** – Emits `TorStatus` updates through a `MutableStateFlow`
4. **Lifecycle resilience** – Handles restarts, bind retries, and inactivity detection
5. **Client reconstruction** – Triggers `OkHttpProvider` to rebuild network stacks on state changes

This fail-closed design guarantees that when Tor mode is enabled, the SOCKS proxy address is published **before** the Tor circuit finishes bootstrapping—preventing any plaintext traffic from leaking during the transition period.

---

## Bootstrapping the Arti Tor Process

The initialization sequence begins when `BitchatApplication` calls `ArtiTorManager.getInstance().init(this)` in `onCreate()`. This single setup call prepares the manager to respond to user preference changes throughout the app lifecycle.

When a user enables Tor via `TorPreferenceManager`, the manager invokes `applyMode()`, which chains into `startArti()`. The Arti library then performs asynchronous bootstrap logging, with progress percentages streamed through `statusFlow` for real-time UI updates.

```kotlin
// BitchatApplication.kt
class BitchatApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // Initialise the Tor manager once for the whole process
        ArtiTorManager.getInstance().init(this)
    }
}

```

The bootstrap process handles mobile-specific failure modes: port binding conflicts trigger automatic retry with alternative ports, and bootstrap inactivity exceeding `AppConstants.Tor` timeout thresholds initiates supervised restart sequences.

---

## SOCKS Proxy Configuration and Fail-Closed Networking

Bitchat's privacy guarantees depend on immediate proxy publication. The default SOCKS port is defined in `AppConstants.Tor.DEFAULT_SOCKS_PORT` (found in [`app/src/main/java/com/bitchat/android/util/AppConstants.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/util/AppConstants.kt)), though the manager dynamically selects available ports when conflicts occur.

The critical architectural decision: **proxy availability precedes circuit readiness**. Applications attempting network calls receive the proxy address instantly, but connections block until Tor establishes its first circuit. This prevents the common leak pattern where apps "fall back" to direct connections during Tor startup.

`OkHttpProvider` in [`app/src/main/java/com/bitchat/android/net/OkHttpProvider.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/net/OkHttpProvider.kt) implements the client-side integration. It maintains singleton HTTP and WebSocket clients that are atomically rebuilt whenever `ArtiTorManager.statusFlow` emits a state change:

```kotlin
suspend fun fetchViaTor(url: String): String {
    // OkHttpProvider always uses the current SOCKS proxy, if any
    val client = OkHttpProvider.httpClient()
    val request = Request.Builder().url(url).build()
    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) error("Unexpected code $response")
        return response.body?.string().orEmpty()
    }
}

```

For operations requiring guaranteed circuit establishment, `awaitSelectedRoute()` provides suspendable waiting with configurable timeout:

```kotlin
suspend fun safeFetch(url: String): String {
    // Wait up to 10 seconds for Tor to finish bootstrapping
    val torReady = ArtiTorManager.getInstance().awaitSelectedRoute(10_000)
    require(torReady) { "Tor proxy not ready" }
    return fetchViaTor(url)
}

```

---

## State Flow Architecture for UI Integration

`ArtiTorManager` exposes `statusFlow: MutableStateFlow<TorStatus>` as the single source of truth for Tor connectivity. This Kotlin Flow emits immutable state objects containing:

- Current `TorMode` (ON/OFF/STARTING/ERROR)
- Bootstrap completion percentage
- Error messages for debugging
- Active proxy address

UI components consume this flow using Compose's `collectAsState()`:

```kotlin
val torManager = remember { ArtiTorManager.getInstance() }
val torStatus by torManager.statusFlow.collectAsState()

Switch(
    checked = torStatus.mode == TorMode.ON,
    onCheckedChange = {
        // Persisted via TorPreferenceManager; manager reacts automatically
        TorPreferenceManager.setMode(application, if (it) TorMode.ON else TorMode.OFF)
    }
)

```

The [`AboutSheet.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/AboutSheet.kt), [`ChatHeader.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/ChatHeader.kt), and location channel sheets all implement this pattern, providing consistent Tor status visibility without direct manager coupling.

---

## Nostr Relay Integration

Bitchat's decentralized messaging relies on Nostr protocol relays. `NostrRelayManager` in [`app/src/main/java/com/bitchat/android/nostr/NostrRelayManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/nostr/NostrRelayManager.kt) registers listeners on `ArtiTorManager.statusFlow` to reset WebSocket connections when Tor mode changes.

This ensures that relay traffic—which carries message content and metadata—immediately migrates to Tor routes upon user activation, without requiring manual reconnection or app restart. The WebSocket client obtained through `OkHttpProvider.webSocketClient()` inherits the same SOCKS proxy configuration as standard HTTP clients, providing uniform privacy coverage.

---

## Lifecycle Resilience on Mobile Networks

Android's aggressive background restrictions and intermittent connectivity demand robust Tor process management. `ArtiTorManager` implements:

- **Bind retry logic** – Detects `BindException` on SOCKS port allocation and iterates through alternative ports
- **Inactivity watchdog** – Monitors bootstrap progress; triggers restart if stall duration exceeds configured threshold
- **Graceful degradation** – Preserves error state in `statusFlow` for user-visible diagnostics rather than silent failure

These mechanisms ensure Tor availability survives airplane mode transitions, VPN conflicts, and network handoffs without user intervention.

---

## No External Dependencies: Google Play Compliance

Traditional Tor implementations require `tor` binaries or Orbot integration, complicating distribution and violating Play Store policies on executable code. Bitchat's pure-Kotlin/Rust Arti integration satisfies:

- **16 KB page-size constraint** – No native executable pages requiring special alignment
- **Single APK distribution** – All Tor functionality self-contained
- **Automatic updates** – Arti library updates through standard Gradle dependency management

This architecture eliminates the "install Orbot first" friction that degrades user experience in other privacy-focused applications.

---

## Summary

- **`ArtiTorManager`** ([`app/src/main/java/com/bitchat/android/net/ArtiTorManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/net/ArtiTorManager.kt)) provides singleton orchestration of the embedded Arti Tor client
- **Fail-closed SOCKS proxy** publishes immediately on Tor activation, blocking connections until circuit establishment
- **`OkHttpProvider`** atomically rebuilds HTTP/WebSocket clients to route all traffic through the current proxy
- **`statusFlow`** delivers reactive Tor state to Compose UI components with bootstrap progress and errors
- **Nostr relay traffic** inherits Tor routing through shared `OkHttpProvider` client reconstruction
- **Zero external binaries** achieves Google Play compliance while maintaining full Tor functionality

---

## Frequently Asked Questions

### Does Bitchat require Orbot or a separate Tor app?

No. Bitchat embeds Arti—the official Rust implementation of Tor—directly through the `info.guardianproject.arti` library. The `ArtiTorManager` class handles all Tor functionality internally without external dependencies.

### What happens if Tor fails to bootstrap?

The `statusFlow` emits `TorStatus` with error details, and the UI displays the failure state. Internally, `ArtiTorManager` implements retry logic with alternative port selection and inactivity-triggered restarts. Applications calling `awaitSelectedRoute()` receive `false` on timeout, allowing graceful degradation.

### Is WebSocket traffic (Nostr relays) also routed through Tor?

Yes. `NostrRelayManager` listens to `ArtiTorManager.statusFlow` and resets connections when Tor mode changes. All WebSocket clients are obtained through `OkHttpProvider.webSocketClient()`, which configures the SOCKS proxy identically to HTTP clients.

### How do I verify my connection is using Tor?

Collect `ArtiTorManager.getInstance().statusFlow` and check `torStatus.mode == TorMode.ON`. The bootstrap percentage indicates circuit establishment progress. For programmatic verification, `awaitSelectedRoute()` confirms operational Tor routing before sensitive operations.

### Why Arti instead of the traditional C Tor implementation?

Arti provides memory-safe Rust implementation suitable for mobile embedding, eliminates native binary distribution restrictions, and integrates cleanly with Kotlin coroutines through the Guardian Project's Android bindings.