- Cache getRSSIColor results with key format: "\(rssi)_\(isDark)"
- Cache getEncryptionStatus results with peerID as key
- Add cache invalidation methods for both caches
- Invalidate encryption cache when:
- Encryption status is updated
- Fingerprints are verified
- Peer authentication callbacks trigger
- Invalidate RSSI cache when peer list updates
- Clear all caches in panicClearAllData
This reduces redundant calculations during view updates and improves performance
- Add URL detection to message formatting with blue color and underline
- Replace List with ScrollView for precise spacing control
- Use minimal vertical padding (1px) between messages
- Remove LazyLinkPreviewView, use LinkPreviewView directly
- URLs now appear highlighted in message text AND show preview below
- Reduce VStack spacing from 4 to 2 and nested VStack from 2 to 0
- Set list row insets top/bottom to 0 to minimize spacing on iPhone
- Remove all markdown link parsing and formatting
- Share plain URLs instead of markdown when using share extension
- Keep only plain text URL detection and preview functionality
- Convert BitchatMessage from struct to class for efficient caching
- Cache formatted AttributedStrings to avoid expensive regex on every render
- Replace ScrollView+LazyVStack with native List for better cell reuse
- Implement message windowing (show last 100 messages for performance)
- Add LazyLinkPreviewView that defers loading for 0.5s to improve scroll performance
These optimizations significantly improve scroll performance, especially with large message lists.
- Validate nickname only when field loses focus or on submit
- Replace empty nicknames with anon+random 4 digits (e.g. anon3847)
- Remove auto-save timer to prevent validation while typing
- Add FocusState to detect when user exits the field
- Don't show PM notifications when the private chat is already open
- Store peer ID in notification userInfo for reliable chat opening
- Tap notification to open the correct private chat using peer ID
- Check both in ChatViewModel (when receiving) and NotificationDelegate (when presenting)
- Tap any message to show action sheet with user options
- Actions include: private message, hug, slap, and block
- Only appears for messages from other users (not system or self)
- Integrates with existing chat commands (/hug, /slap, /block)
- Make entire peer list row tappable for starting private chats
- Fix separator bar visibility when private chat is fully open (offset by -1px)
- Keep favorite star as separate tap target
- Preserve double-tap gesture for showing fingerprints
- Update peripheral ID to peer ID mapping for all message types including handshakes
- Ensure disconnect messages appear even if peer disconnects right after handshake
- Add detailed logging for peripheral mapping to help debug connection issues
- Fix blank message rows by ensuring display names are never empty
- Fix blank row appearing when peer disconnects by ensuring display names are never empty
- Add validation to prevent system messages with empty content from being displayed
- Fix API MISUSE warnings by adding connection state checks before writing to peripherals
- Improve write queue processing with additional safety checks
- Update activity tracking when processing queued writes
These fixes address UI glitches and improve Bluetooth reliability.
- Add in-memory cache for peer public keys with TTL to reduce keychain I/O
- Implement write queue for disconnected peripherals with backpressure handling
- Add connection pooling limits with LRU eviction when pool is full
- Track peripheral activity for intelligent connection management
- Replace all direct peripheral.writeValue calls with new writeToPeripheral method
- Add automatic cleanup of expired cache entries and write queues
These optimizations reduce battery consumption and improve performance by:
- Minimizing expensive keychain operations
- Queueing writes instead of dropping them when peripherals disconnect
- Limiting concurrent connections to prevent resource exhaustion
- Implementing intelligent connection management based on usage patterns
Previously, NoiseSessionManager would reject handshake initiations if it had an existing established session. This caused deadlocks when one peer cleared their session (e.g., after decryption failure) but the other peer rejected the new handshake.
Changes:
- NoiseSessionManager now always accepts handshake initiations, clearing any existing session
- Added comprehensive tests for handshake recovery scenarios
- Tests verify proper re-establishment after decryption failures and nonce desynchronization
Always accept handshake initiations even with valid sessions, as the peer must have cleared their session for a good reason (e.g., decryption failure). This prevents deadlock where one peer has no session while the other maintains an invalid one.
- Add test for peer restart detection and session recovery
- Add test for nonce desynchronization detection
- Add test for concurrent encryption thread safety
- Add test for session stale detection
- Add test for handshake after decryption failure
- Add integration test for peer presence tracking and reconnection
- Add integration test for encrypted messages after peer restart
These tests ensure:
- Sessions properly recover when a peer restarts
- Nonce desynchronization is detected correctly
- Encryption operations are thread-safe under concurrent load
- Identity announcements are sent on reconnection after silence
- Encrypted messages work after session re-establishment
- Add peer presence tracking with lastHeardFromPeer to detect reconnections
- Automatically send identity announcement when detecting peer reconnection after 30s
- Clear stale sessions when receiving handshake from recently-seen peer (likely restart)
- Add peers to activePeers when successfully decrypting their messages
- Fix thread safety in handshake coordinator with concurrent collections
- Extend message delivery timeouts (30s→120s private, 60s→180s room, 300s→600s favorite)
- Add per-peer encryption queues to prevent nonce desynchronization
- Make NoiseSession encrypt/decrypt operations thread-safe with barrier flag
- Initialize lastSuccessfulMessageTime when handshake completes
- Send identity announcement when decryption fails to prompt session reset
- Improve handshake state logging and debugging
This fixes:
- Peers stuck in "establishing encryption" after restart
- Nonce desynchronization causing "Decryption failed at nonce N"
- Asymmetric peer visibility (one peer sees the other but not vice versa)
- Thread safety issues causing crashes with pendingPrivateMessages
- Sessions marked as stale immediately after establishment
When panic mode is triggered (triple tap on logo), it now properly clears:
- All handshake coordinator states
- Handshake attempt times
- Notifies UI that all peers are disconnected
This fixes the issue where handshake states would persist after clearing identity, causing confusion when the device showed "zero peers connected" but still had established handshake states in logs.
Also ensures UI is properly notified to update the peer list to empty.
Added multiple UI update triggers to ensure the lock icon updates:
1. In onPeerAuthenticated callback when handshake completes
2. When detecting an already established session on reconnect
This ensures the encryption status is reflected in the UI immediately after:
- Initial handshake completion
- Reconnection with existing session
- Peer authentication events
The handshake was completing successfully but the UI wasn't being notified to update the encryption status icon. Added a delegate notification after handshake completion to trigger updateEncryptionStatusForPeers() in the UI.
This ensures the lock icon changes from empty to filled when encryption is established.
When a peer restarts and gets a new peer ID, the session migration was only happening on one side, causing a state mismatch where one peer had an encrypted session but the other didn't.
Changed approach to clear the old session instead of migrating it, ensuring both peers establish a fresh handshake after ID rotation. This fixes the issue where one peer shows empty lock (no encryption) while the other shows lock with circle (encryption established).
## Summary
- Added timestamps to SecureLogger for precise timing analysis
- Implemented NoiseHandshakeCoordinator to prevent race conditions
- Added deterministic role selection based on peer ID comparison
- Implemented proper handshake state machine with retry logic
- Added duplicate message detection for handshake messages
- Improved logging and diagnostics for handshake debugging
## Details
The coordinator ensures only one peer initiates handshakes by using deterministic role selection (lower peer ID initiates). This prevents the simultaneous handshake attempts that were causing failures. The state machine tracks handshake progress and handles retries with exponential backoff.
## Testing
Successfully builds with no errors or warnings. The implementation should resolve the "establishing encryption" stuck state issue by ensuring proper handshake coordination between peers.
- Fixed mock service property overrides to match base class properties
- Added missing CryptoKit imports where needed
- Fixed immutable property assignments by creating new instances
- Replaced XCTAssertThrows with XCTAssertThrowsError
- Fixed DeliveryAck serialization method names (serialize -> encode)
- Fixed unused variable warnings
- Ensured all BitchatPacket modifications create new instances
- Fixed BitchatMessage property mutations by creating new instances
All test targets now build successfully for both iOS and macOS platforms.
- Created test utilities and helpers for common test operations
- Implemented Binary Protocol tests covering encoding/decoding, compression, and padding
- Added Noise Protocol tests for handshake, encryption, and session management
- Created Public Chat E2E tests for broadcasting, routing, TTL, and mesh topologies
- Implemented Private Chat E2E tests for direct messaging, delivery ACKs, and retry logic
- Added Integration tests for multi-peer scenarios, network resilience, and mixed traffic patterns
- Created mock implementations for BluetoothMeshService and NoiseSession
Test coverage includes:
- Protocol layer (binary encoding, message serialization)
- Security layer (Noise handshake, encryption/decryption)
- Application layer (public/private messaging, delivery tracking)
- Network scenarios (mesh topology, partitions, churn)
- Performance and stress testing
- Remove channel UI elements from ContentView
- Remove channel data structures and methods from ChatViewModel
- Remove channel commands (/j, /leave, /channels)
- Remove channel field from BitchatMessage protocol
- Remove channel message types and handling
- Remove NoiseChannelEncryption.swift entirely
- Clean up all channel references across the codebase
- Fix compilation warnings (var to let conversions)
- Remove all outdated test files that used incorrect APIs
- Simplify app to only support public broadcast and 1:1 private messages
- Replaced all print() calls with appropriate SecureLogger.log() calls
- Used proper categories: noise, encryption, session, security
- Applied appropriate log levels: debug, info, warning, error
- Converted 25 prints in BluetoothMeshService.swift
- Converted 1 print in ChatViewModel.swift
- Converted 8 prints in DeliveryTracker.swift
- Test files kept as-is for debugging purposes
- Verified successful build on iOS
This improves security by using structured logging that can filter sensitive data.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Network empty reset delay: 5 min → 1 min
- Notification cooldown: 10 min → 5 min
- More responsive when peers drop and rejoin
- Still prevents spam from flaky connections
- Completely remove UIApplication/NSApplication state checks
- Let NotificationDelegate handle foreground presentation
- All notifications now sent regardless of app state
- Fixes all thread checker warnings
- Move all app state checks inside DispatchQueue.main.async
- Ensures NSApplication.isActive is only accessed from main thread
- Prevents thread checker warnings on macOS
- Favorite notifications now bypass sendLocalNotification to avoid thread issues
- All notification code properly wrapped in DispatchQueue.main.async
- Added logging for favorite notifications
- Network and favorite notifications now work independently
- Wrap UIApplication.applicationState access in DispatchQueue.main.async
- Network notifications bypass app state check entirely
- Added more detailed logging for debugging
- Notifications now handled properly from background threads
- Network notifications now show even when app is in foreground
- Added unique identifiers to prevent iOS deduplication
- Set interruptionLevel to timeSensitive for prominence
- Added comprehensive logging throughout notification flow
- Improved error handling for notification permissions
- 10 minute cooldown between notifications
- 5 minute hysteresis before resetting notification flag after network becomes empty
- Prevents spam when peers briefly disconnect/reconnect
- Emergency disconnect immediately resets all notification state
- Show notification when network transitions from empty to having peers
- Single notification per session, resets when network becomes empty
- Background Bluetooth modes enabled for iOS
- Generic message: 'bitchatters nearby\! 1 person around' or 'X people around'