# How Bitchat Android Maintains Background Relay Connectivity

> Learn how Bitchat Android keeps Nostr relay WebSocket connections alive in the background using foreground services, ownership tagging, and retry logic.

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

---

**Bitchat Android keeps Nostr relay WebSocket connections alive in the background using a singleton `NostrRelayManager` coupled with Android foreground services, background ownership tagging, and automatic retry logic.**

The Bitchat Android app implements a sophisticated relay connectivity system that ensures persistent Nostr protocol communication even when the app is not actively visible. According to the [permissionlesstech/bitchat-android](https://github.com/permissionlesstech/bitchat-android) source code, this is achieved through tight integration between connection management, Android service lifecycles, and privacy-preserving network routing.

## Core Architecture: NostrRelayManager

The foundation of background relay connectivity lives 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). This **singleton manager** maintains all WebSocket connections to Nostr relays and provides lifecycle-aware connection ownership.

### Background Ownership Model

The manager distinguishes between UI-tied and persistent connections using an owner tagging system:

```kotlin
// From NostrRelayManager.kt line 33
const val OWNER_BACKGROUND = "background"

```

When a relay is created with `OWNER_BACKGROUND`, the manager treats it as a **long-running connection** that survives UI lifecycle events. The manager stores active `Relay` objects—each wrapping an OkHttp `WebSocket`—and prevents garbage collection of background-owned sockets.

## Background Runtime Initialization

The `NostrBackgroundRuntime` class ensures relay connectivity starts early in the application lifecycle:

```kotlin
// From NostrBackgroundRuntime.kt line 53
val manager = NostrRelayManager.getInstance(context)
manager.addRelay(
    url = "wss://relay.example.com",
    owner = NostrRelayManager.OWNER_BACKGROUND
)

```

This initialization pattern runs from the `Application` class, guaranteeing **background relays establish before any UI component requests them**.

## Foreground Service: MeshService

Android's background execution restrictions make foreground services essential for persistent sockets. Bitchat implements `MeshService` in [`app/src/main/java/com/bitchat/android/mesh/MeshService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/MeshService.kt):

- **Starts automatically** when `NostrRelayManager` detects active background relays
- **Runs with persistent notification**, qualifying as a foreground process under Android's classification
- **Prevents OS killing** while WebSocket connections remain open
- **Stops self-terminating** when no relays require maintenance

The service also coordinates with `BluetoothMeshService` for hybrid mesh-Nostr operation.

## Automatic Reconnection and Retry Logic

WebSocket failures trigger structured recovery through `RetryingControlPacketSender`. This component:

1. **Listens for `onFailure` and `onClosed` callbacks** on each `Relay` WebSocket
2. **Schedules exponential backoff retries** respecting Android background limits
3. **Surfaces diagnostics** through the same pattern as `DebugSettingsManager` (lines 157-228)

The debug UI in `DebugSettingsSheet` exposes relay statistics—packet counts, round-trip times, and connection states—helping diagnose background connectivity health.

## Tor Integration and Connection Reset

For privacy-focused users, `ArtiTorManager` 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) maintains connectivity across Tor circuit changes:

```kotlin
// From ArtiTorManager.kt line 335
NostrRelayManager.shared.resetAllConnections()

```

This **forced reconnection** ensures all relays establish fresh sockets over the new Tor circuit, preventing identity correlation across circuits.

## Graceful Shutdown Coordination

Clean termination prevents resource leaks and dangling sockets. `AppShutdownCoordinator` in [`app/src/main/java/com/bitchat/android/service/AppShutdownCoordinator.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/service/AppShutdownCoordinator.kt) handles this:

```kotlin
// From AppShutdownCoordinator.kt line 59
NostrRelayManager.shared.disconnect()

```

The coordinator iterates all active relays, closes each WebSocket, and stops `MeshService`—ensuring **no background process survives app closure**.

## Practical Implementation Example

```kotlin
class BitchatApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        // Initialize background relay connectivity
        val relayManager = NostrRelayManager.getInstance(this)
        
        // Add persistent relay for location-based notes
        relayManager.addRelay(
            url = "wss://relay.bitchat.network",
            owner = NostrRelayManager.OWNER_BACKGROUND
        )
        
        // Ensure connection established
        relayManager.ensureConnected(NostrRelayManager.OWNER_BACKGROUND)
    }
}

// After Tor circuit restart
ArtiTorManager.restartTor {
    NostrRelayManager.shared.resetAllConnections()
}

```

## Key Source Files

| File | Responsibility |
|------|---------------|
| [`nostr/NostrRelayManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/nostr/NostrRelayManager.kt) | Singleton WebSocket management and retry logic |
| [`nostr/NostrBackgroundRuntime.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/nostr/NostrBackgroundRuntime.kt) | Application-scoped background relay initialization |
| [`mesh/MeshService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/mesh/MeshService.kt) | Foreground service preventing OS process termination |
| [`net/ArtiTorManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/net/ArtiTorManager.kt) | Tor circuit coordination and forced reconnections |
| [`service/AppShutdownCoordinator.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/service/AppShutdownCoordinator.kt) | Clean relay disconnection on app termination |
| [`ui/debug/DebugSettingsManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/ui/debug/DebugSettingsManager.kt) | Connectivity diagnostics and statistics collection |

## Summary

- **Background relay connectivity** relies on `NostrRelayManager` with `OWNER_BACKGROUND` tagging to persist connections outside UI lifecycle
- **Foreground service (`MeshService`)** qualifies the process as foreground, preventing Android from killing active relay sockets
- **Automatic retry logic** handles WebSocket failures with exponential backoff and background-aware scheduling
- **Tor integration** forces connection resets on circuit changes to maintain privacy guarantees
- **Graceful shutdown** through `AppShutdownCoordinator` ensures clean resource release

## Frequently Asked Questions

### Why does Bitchat need a foreground service for relay connectivity?

Android's background execution restrictions introduced in API 26+ limit background services and network access for implicit processes. The `MeshService` foreground service runs with a visible notification, which Android classifies as a foreground process—allowing WebSocket connections to remain active indefinitely without being killed for memory reclamation.

### How does Bitchat handle relay disconnections automatically?

Each `Relay` object registers OkHttp WebSocket callbacks. When `onFailure` or `onClosed` fires, `RetryingControlPacketSender` schedules reconnection attempts with exponential backoff. This retry mechanism respects Android's background job limitations while ensuring eventual recovery without user intervention.

### What happens to relays when the app closes completely?

`AppShutdownCoordinator` intercepts application termination and calls `NostrRelayManager.shared.disconnect()`. This iterates all active relays, closes each WebSocket cleanly, and stops `MeshService`—preventing zombie connections, notification persistence, and battery drain from orphaned background processes.