# How Bitchat Android Integrates with Tor Using the Arti Library for Enhanced Privacy

> Discover how Bitchat Android uses the Arti library and a JNI bridge to route all messaging traffic through Tor for enhanced privacy, ensuring data only transmits on verified circuits.

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

---

**Bitchat Android routes all messaging traffic through the Tor network by wrapping the Rust-based Arti library in a three-layer JNI bridge, ensuring that no data transmits until the circuit is fully bootstrapped and verified.**

Bitchat Android is a permissionless messaging application that leverages the Arti library—an official Rust implementation of the Tor protocol—to provide users with censorship-resistant, anonymous communication. According to the bitchat-android source code, the integration embeds Arti directly via the Guardian Project's `arti-mobile-ex` patterns, creating a self-contained privacy layer that manages its own SOCKS proxy lifecycle through a reactive Kotlin service.

## The Three-Layer Tor Architecture

The Bitchat Android Tor integration consists of three distinct layers that bridge the Rust-based Arti binaries with the Android Java/Kotlin runtime.

### Native JNI Bridge

The lowest layer is `org.torproject.arti.ArtiNative`, which provides the JNI entry points to the compiled Arti binaries. This class exposes native methods including `initialize()`, `startSocksProxy()`, and `stop()`, handling the direct memory interfacing between Kotlin and the Rust Tor implementation.

### ArtiProxy Wrapper

The middle layer, `info.guardianproject.arti.ArtiProxy`, supplies a high-level Java/Kotlin API that mirrors the Guardian Project reference implementation. The `ArtiProxy.Builder` class (see its `build()` method at lines [79‑81]) constructs proxy instances configured with SOCKS and DNS ports:

```kotlin
val proxy = ArtiProxy.Builder(application)
    .setSocksPort(9050)
    .setDnsPort(9051)
    .setLogListener { Log.i("Arti", it ?: "") }
    .build()

```

The `start()` method (lines [50‑82]) registers the log callback, initializes the native runtime, and launches the SOCKS proxy daemon.

### ArtiTorManager Service

The top layer is `com.bitchat.android.net.ArtiTorManager`, a singleton service that manages the complete lifecycle of the Arti client. This class exposes a reactive `StateFlow<TorStatus>` that the UI and network stack consume to react to Tor state changes, implements bootstrap monitoring via `handleArtiLogLine` (lines [51‑35]), and coordinates port binding retries through `scheduleRetry()` (lines [13‑28]).

## Application Bootstrap and Initialization

Tor integration begins in `com.bitchat.android.BitchatApplication`. During `onCreate()`, the application initializes the Tor subsystem once per process:

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

```

The `init()` method (lines [22‑63]) creates the `ArtiProxy` builder, configures ports 9050 and 9051, and registers a log listener that forwards Arti log lines into the manager's internal state machine (see the log listener defined in `init` lines [30‑45]).

## Tor State Management and Bootstrapping

When users enable Tor, `ArtiTorManager.applyMode()` (lines [94‑55]) triggers `startArti()`, which transitions the proxy through several distinct states.

### Bootstrap Monitoring

The manager updates a `MutableStateFlow<TorStatus>` to `STARTING` and `BOOTSTRAPPING` (lines [78‑84]). A watchdog coroutine monitors the bootstrap percentage by parsing log lines through `handleArtiLogLine`. Once the log line *“We have found that guard … is usable.”* is received, the manager sets `bootstrapPercent = 100` and transitions `state` to `RUNNING` (see `handleArtiLogLine` at lines [90‑100]).

### Fail-Closed Routing

While Tor bootstraps, `ArtiTorManager` publishes an `InetSocketAddress("127.0.0.1", currentSocksPort)` (see `socksAddr` assignment at line [43]). Network consumers call `awaitSelectedRoute(timeoutMs)` (implementation at lines [81‑92]) to block until the proxy is fully functional, guaranteeing that no traffic leaks before Tor is ready.

## Network Stack Integration

All HTTP clients are constructed through `com.bitchat.android.util.OkHttpProvider`. Whenever the Tor mode changes, `resetNetworkConnections()` (lines [30‑34]) recreates OkHttp instances with a proxy pointing at the local SOCKS address. The UI layer reads the `statusFlow` to display connection status, such as in [`ChatHeader.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/ChatHeader.kt) at line [138].

If the proxy fails to bind (detected by `isBindError` at lines [16‑22]), the manager implements a back-off retry schedule through `scheduleRetry()`, attempting connection with incremented ports up to `MAX_RETRY_ATTEMPTS`.

## Practical Integration Examples

### Reacting to Tor Status in Compose

UI components consume the reactive state to display anonymity status:

```kotlin
@Composable
fun TorStatusBanner() {
    val torProvider = remember { ArtiTorManager.getInstance() }
    val status by torProvider.statusFlow.collectAsState()
    if (status.mode == TorMode.ON && status.state == TorState.RUNNING) {
        Text("Tor is active – your traffic is anonymised")
    }
}

```

### Ensuring Secure Request Routing

Before executing sensitive network operations, code must verify Tor readiness:

```kotlin
suspend fun performSecureRequest(client: OkHttpClient, request: Request) {
    val torManager = ArtiTorManager.getInstance()
    if (!torManager.isProxyEnabled()) {
        // Block until Tor is bootstrapped (or timeout after 10 s)
        torManager.awaitSelectedRoute(timeoutMs = 10_000)
    }
    client.newCall(request).execute().use { response ->
        // Handle response …
    }
}

```

### Manual Tor Toggle

Settings screens can toggle the proxy via `applyMode()`:

```kotlin
fun toggleTor(enabled: Boolean) {
    val tor = ArtiTorManager.getInstance()
    tor.applyMode(application, if (enabled) TorMode.ON else TorMode.OFF)
}

```

## Key Implementation Files

| File | Role |
|------|------|
| [`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) | Singleton orchestrating the Arti client, bootstrap monitoring, and reactive state management. |
| [`app/src/main/java/info/guardianproject/arti/ArtiProxy.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/info/guardianproject/arti/ArtiProxy.kt) | High-level wrapper around native Arti; manages SOCKS proxy lifecycle. |
| [`app/src/main/java/org/torproject/arti/ArtiNative.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/org/torproject/arti/ArtiNative.kt) | JNI bridge to Rust Arti binaries. |
| [`app/src/main/java/com/bitchat/android/util/OkHttpProvider.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/util/OkHttpProvider.kt) | Factory for OkHttp clients configured to use the local Tor SOCKS proxy. |
| [`app/src/main/java/com/bitchat/android/BitchatApplication.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/BitchatApplication.kt) | Application bootstrap initializing the Tor subsystem. |

## Summary

- **Bitchat Android** embeds the Rust-based **Arti** library directly, eliminating dependency on external Tor apps like Orbot.
- The architecture separates concerns into **ArtiNative** (JNI), **ArtiProxy** (API wrapper), and **ArtiTorManager** (lifecycle service).
- The **fail-closed design** blocks all network requests until `awaitSelectedRoute()` confirms Tor is bootstrapped, preventing IP leakage.
- A **reactive StateFlow** exposes `TorStatus` to the UI, enabling real-time connection indicators.
- **Automatic retry logic** handles port binding conflicts with exponential back-off.

## Frequently Asked Questions

### How does Bitchat Android prevent traffic leakage before Tor is ready?

The `ArtiTorManager` implements a fail-closed design where network requests must call `awaitSelectedRoute(timeoutMs)` before executing. This method suspends coroutines until the bootstrap percentage reaches 100% and the state transitions to `RUNNING`, ensuring the SOCKS proxy is fully established at `127.0.0.1:9050` before any data transmits.

### What is the difference between ArtiNative and ArtiProxy?

**`ArtiNative`** (`org.torproject.arti.ArtiNative`) is the low-level JNI bridge that exposes raw native functions from the compiled Rust Arti binaries. **`ArtiProxy`** (`info.guardianproject.arti.ArtiProxy`) is the high-level Kotlin wrapper that encapsulates JNI complexity, providing a builder pattern for configuration and managing the SOCKS proxy lifecycle through simple `start()` and `stop()` methods.

### How does the app handle Tor connection failures?

When the native proxy fails to bind to the configured port (detected by `isBindError` at lines [16‑22]), `ArtiTorManager` triggers `scheduleRetry()` with exponential back-off, incrementing the port number and attempting connection up to `MAX_RETRY_ATTEMPTS`. If all retries exhaust, the `StateFlow` updates to reflect the failure state, alerting the UI to display connection errors.

### Can users toggle Tor without restarting the application?

Yes. Calling `ArtiTorManager.applyMode(context, TorMode.ON)` or `TorMode.OFF` at runtime triggers `startArti()` or `stopArti()`, respectively. The method automatically invokes `resetNetworkConnections()` to recreate OkHttp clients with or without the SOCKS proxy configuration, allowing seamless switching between clearnet and Tor routing without process restarts.