Tags: Olib-AI/ConnectionPool
Tags
Add cross-platform transport module for Android interop Bonjour/mDNS + TCP transport with a platform-neutral wire protocol (canonical JSON, HKDF-SHA256 key derivation, ChaCha20-Poly1305 AEAD frames) interoperable with the Kotlin implementation. Includes session resume, host-side rate limiting, and frozen cross-platform reference vectors asserted byte-equal in tests. Legacy MultipeerConnectivity and relay wire formats are unchanged.
Persistent member identity + auto-rejoin without host Members can now reconnect to a pool indefinitely without the host being online. The load-bearing change is in WebSocketTransport: the client_public_key in JoinRequest now comes from a Keychain-persistent RemoteMemberIdentity (scoped per serverURL+poolID) instead of a fresh Curve25519.Signing.PrivateKey() per session. That makes the relay's new approved_peers set actually deliver value. New types: - RemoteMemberIdentity — Ed25519 keypair, Keychain-stored, per-pool scoped via sha256(serverURL + ":" + poolID), accessibility = WhenUnlockedThisDeviceOnly. Mirrors RemoteHostIdentity's raw-Security approach (no new dependencies). - RemoteMemberRecord + RemoteMemberRecordStore — ledger of pools the user has joined. Persisted via the existing pluggable remotePoolStateStorageProvider. - ServerFrame.memberRejoin — new client→server frame mirrored from StealthRelay v0.5.1. Domain-separated transcript prevents cross-replay against host_auth signatures. - TransportError.notApproved / .poolNotFound — terminal states that trigger purgeMemberStateForCurrentPool(). New flow on every remote-pool reconnect: 1. If RemoteMemberIdentity exists for (server, pool), send member_rejoin (signed with the persistent key) and skip JoinRequest entirely. Works whether host online or offline. 2. Otherwise, if an invitation is present, fall through to the existing JoinRequest/PoW path and persist the identity + record on JoinAccepted. 3. 403 not_approved → drop identity + record, surface "membership revoked". 404 pool_not_found → drop both, surface "pool no longer exists". Other errors don't touch saved state. UI: - Home view gains a "Saved Pools" section showing past memberships; tap to rejoin, swipe/trash to forget. - Lobby Disconnect for remote members now intercepts with a confirmation alert and routes to leaveRemoteMemberPool() (purges identity + record). Plain transient drops use the unchanged disconnect() path so blip-reconnects don't lose state. 183 ConnectionPool tests, 0 failures (160 baseline + 23 new). README updated with rejoin protocol description, Quick Start example, and new API entries. Bumped SPM version to 1.5.1.
Tunnel-exit client + pool persistence past host disconnect Two related additions targeting StealthRelay v0.5.0+: 1. Tunnel-exit client (RelayTunnelClient + TunnelStream + binary frame codec). Opens TCP/UDP streams through the relay so the relay's IP becomes the visible exit address. Per-stream credit-based flow control, monotonic stream IDs, async-stream-based receive, DNS query continuations. Hot-path bytes ride binary WebSocket frames (TUNNEL_DATA = 0x01, TUNNEL_UDP = 0x02, big-endian header) so the data path bypasses base64 + JSON. Control plane stays JSON for debuggability. The host bypasses their own per-pool tunnel-exit approval flag — it gates members. 2. Pool persistence past host disconnect. ConnectionPoolManager now publishes hostOnline / hostOfflineSince, mirrored on the view model. New ServerFrame.poolHostStatus broadcast with online + offline_since keeps the UI in sync. ServerPoolInfo.hostOnline added; legacy relays missing the field default to true. JoinRejected with reason "host_offline_unavailable" maps to TransportError.hostOffline and suppresses auto-reconnect on that path. The host re-authenticating with the same Ed25519 key + pool_id rebinds in place — no new pool_id is issued, members reconnect transparently. 154 ConnectionPool tests, 0 failures. README updated with feature bullets, security subsections, and Quick Start examples for both features. Bumped Swift Package version to 1.5.0.
Remote relay transport, security hardening, and 68 unit tests Remote Relay Transport: - WebSocketTransport for connecting to StealthRelay servers - Ed25519 host authentication with Keychain-stored identity - Invitation-based joining with stealth://invite/ URLs - SHA-256 proof-of-work solver for anti-DoS challenges - Session token injection for all privileged host operations - TLS certificate pinning via SPKI SHA-256 hash - Server claiming flow with QR code scanning - Automatic reconnection with invitation expiry checks - Auto-accept previously approved peers on reconnect Security Hardening (5 audit laps): - HMAC key derived from shared secret, not pool UUID - Constant-time HMAC verification via CryptoKit - Length-prefixed HMAC inputs prevent concatenation forgery - Envelopes without HMAC rejected (no backwards compat) - Hop path capped at maxTTL+1 - URLSession .ephemeral for WebSocket connections - Global brute-force rate limiting (not bypassable by rotating names) - Peer IDs use stable UUIDs, not attacker-controlled display names - Topology broadcast spoofing detection - Transport-authenticated player identity in multiplayer games - Poll vote and reaction spoofing fixed (use sender ID from transport) - Notification privacy (no message content in OS notifications) - Voice note file protection with NSFileProtectionComplete - Display name length capped at 128 characters - Failed attempt counts stored in protected Application Support UI Improvements: - ScrollView layout prevents content clipping - Delete button for saved relay servers - Full-width remote pool buttons (no truncation) - Share sheet auto-dismisses for join approval prompt - Max members picker in Remote Host sheet (default 8) - Single-use invitations removed after peer joins - Only host can create new invitations - Proper disconnect state cleanup with deferred reset Tests: - 68 unit tests across 6 test files - RelayEnvelope, MeshTopology, PoolMessage, dedup cache, RemotePoolState GitHub Actions: - Fixed update-readme-version workflow (checkout main, push origin main)
Fix concurrency issues across the package - ConnectionPoolConfiguration: protect logger with NSLock (was bare nonisolated(unsafe) mutable global) - MeshTopology: remove TOCTOU-prone computed property wrappers; all callers already access backing storage directly under lock - Peer: replace static var cache with static let closure for thread-safe one-time initialization - ConnectionPoolManager: store delayed Tasks in array, cancel on disconnect to prevent stale ops on reconnect - UncheckedSendableBox: add safety documentation