How to Debug Issues in BitChat: A Complete Guide to Mesh Messaging Troubleshooting

Enable SecureLogger.minimumLevel = .debug and inspect TransportEvent flow through BitchatDelegate.receiveTransportEvent to diagnose Bluetooth mesh, Noise encryption, and Nostr relay issues in BitChat.

BitChat is a dual‑transport, peer‑to‑peer messenger that blends a local Bluetooth mesh with the global Nostr network. Because it combines real‑time mesh routing, Noise‑based encryption, and asynchronous Nostr delivery, debugging issues in BitChat requires a systematic approach across multiple architectural layers. This guide covers the exact tools, source files, and techniques used by the permissionlesstech/bitchat development team.

Enable Verbose Logging with SecureLogger

BitChat uses the SecureLogger wrapper around Apple's OSLog. The most useful level for troubleshooting is debug.

import BitLogger

// Turn on debug logging for a specific category (e.g., .session)
SecureLogger.minimumLevel = .debug
SecureLogger.debug("Starting mesh handshake", category: .session)

The logger automatically drops debug messages in production builds, so you can leave these calls in code without affecting release performance.

Source: SecureLogger implementation — localPackages/BitLogger/Sources/SecureLogger.swift

Inspect the Transport Layer

All mesh activity funnels through the Transport protocol. The concrete implementation is BLEService, which conforms to many helper protocols including MeshFileTransferring and MeshVoiceStreaming.

// Example: Log a peer's connection state change
func transport(_ transport: Transport, didConnect peerID: PeerID) {
    SecureLogger.debug("Peer \(peerID) connected", category: .session)
}

Key source files:

Debug steps:

  1. Set a breakpoint in BLEService.startServices() to verify the mesh stack starts.
  2. Watch TransportEvent flow via BitchatDelegate.receiveTransportEvent (see the extension BitchatDelegate at the bottom of Transport.swift).
  3. Check peer snapshots (peerSnapshotsUpdated) to confirm the device sees nearby peers.

Verify Noise Handshake Authentication

Noise sessions secure mesh traffic. The handshake lifecycle is exposed through callbacks installed via installNoiseSessionCallbacks.

transport.installNoiseSessionCallbacks(
    onPeerAuthenticated: { peer, fingerprint in
        SecureLogger.debug("Authenticated \(peer) → \(fingerprint)", category: .session)
    },
    onHandshakeRequired: { peer in
        SecureLogger.debug("Handshake needed for \(peer)", category: .session)
    })

Debug steps:

Source: NoiseSessionManager — bitchat/Noise/NoiseSessionManager.swift

Test Nostr Relay Behavior

When the Bluetooth mesh is unavailable, BitChat falls back to Nostr. The NostrRelayManager handles subscriptions, outgoing queues, and duplicate‑event filtering.

// Log pending message count on a relay
SecureLogger.debug(
    "Relay \(relayURL) pending queue = \(manager.debugPendingMessageQueueCount)",
    category: .session)

Debug steps:

  1. Enable debugPendingMessageQueueCount checks in test builds only.
  2. Use the performance gate script to catch regressions affecting relay throughput:
swift test --filter PerformanceBaselineTests
scripts/check-perf-floors.sh path/to/test-output.log

Source: NostrRelayManagerTests — bitchatTests/Services/NostrRelayManagerTests.swift

Run the Complete Test Suite

The repository ships with a comprehensive SwiftPM test suite. Run it with:


# macOS debug build (no signing required)

xcodebuild -project bitchat.xcodeproj -scheme "bitchat (macOS)" \
  -configuration Debug CODE_SIGNING_ALLOWED=NO test

Key test groups:

Test Group What It Validates
ChatViewModelTests Message flow, UI state updates
BLEQueueContractTests Mesh queue invariants
SecureIdentityStateManagerTests Identity key handling
PerformanceBaselineTests Raw throughput of critical algorithms
Noise*Tests End‑to‑end encryption correctness

Set BITCHAT_SKIP_PERF_BASELINES=1 to bypass performance gates when debugging time‑consuming tests.

Use Xcode Diagnostics Effectively

  • View Debugger: Inspect TransportPeerSnapshot objects to confirm nicknames, connectivity flags, and isVerified status.
  • Memory Graph: Detect orphaned media files via PrivateMediaDeletionPersisting callbacks (Transport.swift, lines 99‑110).
  • Console Filter: Filter Xcode console on session or mesh categories to focus relevant logs.

Diagnose Crashes and Unexpected Behavior

When BitChat crashes, the stack trace often points to fatal errors in BLEService conformances. The Transport protocol supplies default no‑op implementations for optional features — verify you haven't overridden them with buggy code.

Critical checklist:

  1. Check Transport.delegate — must be non‑nil for event propagation.
  2. Handle peerListUpdated — missing UI updates create "ghost peer" symptoms.
  3. Refresh stale Noise sessions — call triggerHandshake(with:) when peers reappear.

Practical Debug Examples

Enable Debug Logging Across All Categories

import BitLogger

func configureLogging() {
    SecureLogger.minimumLevel = .debug
    
    let categories: [OSLog] = [.session, .keychain, .network, .noise]
    categories.forEach { cat in
        SecureLogger.debug("Logging enabled for category \(cat)", category: cat)
    }
}

Dump Peer Connection State

func dumpPeers(_ transport: Transport) {
    let snapshots = transport.currentPeerSnapshots()
    for snap in snapshots {
        SecureLogger.debug(
            """
            Peer: \(snap.peerID)
            Nick: \(snap.nickname)
            Connected: \(snap.isConnected)
            Verified: \(snap.isVerified)
            """,
            category: .session)
    }
}

Targeted Performance Test with Gate


# Run only mesh routing performance test

swift test --filter MeshRoutingPerformanceTests

# Ensure no regression occurred

scripts/check-perf-floors.sh .build/debug/TestOutput.log

Summary

  • Enable SecureLogger.minimumLevel = .debug to capture detailed diagnostic output across all BitChat subsystems.
  • Monitor TransportEvent flow through BitchatDelegate.receiveTransportEvent to trace Bluetooth mesh activity.
  • Validate Noise handshakes via installNoiseSessionCallbacks and the Noise*Tests suite.
  • Inspect relay queues using debugPendingMessageQueueCount when debugging Nostr fallback behavior.
  • Run scripts/check-perf-floors.sh after any algorithmic change to prevent performance regressions.
  • Use Xcode's View Debugger and Memory Graph to inspect TransportPeerSnapshot state and detect resource leaks.

Frequently Asked Questions

How do I enable debug logging in a release build of BitChat?

You cannot. According to the SecureLogger implementation in localPackages/BitLogger/Sources/SecureLogger.swift, debug logs are automatically stripped from production builds. This is by design — use a Debug build configuration or run via Xcode to capture verbose output.

Why are my peers not appearing in the BitChat UI?

Check three things: (1) verify BLEService.startServices() executes without error, (2) confirm Transport.delegate is set to receive peerListUpdated events, and (3) inspect TransportPeerSnapshot objects via the View Debugger to validate isConnected and isVerified flags. Missing UI updates often indicate the delegate callback isn't wired correctly.

What causes Noise handshake failures in BitChat?

The most common causes are key mismatches and stale session state. Use triggerHandshake(with:) to force re‑authentication when a peer becomes reachable again. Run NoiseSessionTests.swift and NoiseSecurityValidatorTests.swift to isolate whether the issue is cryptographic or network‑related.

How do I skip performance baseline checks during development?

Set the environment variable BITCHAT_SKIP_PERF_BASELINES=1 when running tests. This bypasses the scripts/check-perf-floors.sh gate, useful when iterating on time‑consuming tests. Remove this flag before committing to catch throughput regressions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →