feat: M7 server selection architecture & iOS native bootstrap hard cutover#10
Open
mrmidi wants to merge 18 commits into
Open
feat: M7 server selection architecture & iOS native bootstrap hard cutover#10mrmidi wants to merge 18 commits into
mrmidi wants to merge 18 commits into
Conversation
…nostics PR-1 (Measurement Safety) — stop legacy telemetry from contaminating native memory baselines before PR0/PR1 optimization work. Memory pressure: - Remove triggerMemoryPressureReconnect(): the emergency threshold (42 MiB) no longer destroys the WebSocket bridge and schedules a full reconnect. Recreating TLS, queues and buffers near the memory ceiling increased peak usage and obscured the actual growth source. - evaluateMemoryPressure() is now telemetry-only: both warning and emergency emit a throttled os_log entry. No bridge action, no JSONL event recording, no heartbeat rewrite originates from this method. - Remove memoryEmergencyReconnectInProgress state and all references. Measurement configuration: - Add a real Measurement build configuration (release-based) to project.yml with FPTN_MEASUREMENT_BUILD compilation condition. - recordProviderEvent() uses @autoclosure + #if gate: interpolated message strings are never constructed in Measurement builds. - updateDiagnosticsHeartbeat() returns before constructing the full runtime snapshot (native status query, ISO-8601 dates, string copies) in Measurement builds. - emitTelemetry() samples only Mach memory counters in Measurement builds, skipping the full TunnelRuntimeSnapshot construction. - TunnelLogLevelStore forces .warning in Measurement builds so info/debug messages never reach the formatter or redactor. - RingLogSink.tunnel.write() gated behind #if DEBUG: the 1 MiB ring buffer and 512 KiB file sink are compiled out of Release builds. Release builds do not retain provider text in the application-owned ring or shared log file; sparse messages continue through os_log. - TunnelDiagnosticsStore.providerRecordingEnabled runtime guard remains as defense-in-depth for non-Measurement builds. Thresholds (30/42 MiB) retained as telemetry-only markers. Recalibration deferred until real-device measurements post-PR0. Tests: 22/22 passed. Debug + Measurement configurations build.
PR0 (Production-Like Native Build) — align the native build system with production requirements and remove verified dead code. C++23 (full stack): - FptnLib/CMakeLists.txt: -std=c++23, cxx_std_23. - project.yml + Tools/FptnCLI/project.yml: CLANG_CXX_LANGUAGE_STANDARD c++23. - fptn submodule pinned to 7f52909 (C++23 + YAFF ranges patch). YAFF fix: C++23 libc++ ranges probes .begin() eagerly via input_or_output_iterator concept; patch defers BaseArray::begin/end to out-of-line definitions after ArrayIterator is fully defined. Dead code removal (verified zero references): - Remove -DO3 (preprocessor macro, not optimization flag). - Remove unconditional -DNDEBUG (now Debug-only; assertions active). - Remove all WSP_GGML_* defines (vestigial whisper project). - Remove -framework Accelerate/Metal/MetalKit (zero references). - Remove duplicate -lssl/-lcrypto (OpenSSL:: targets = BoringSSL). Size-oriented release flags: - MinSizeRel/Release: -Oz -ffunction-sections -fdata-sections with -Wl,-dead_strip. Build configuration mapping: - build_fptn_lib.sh: Debug→Debug, Release/Measurement→MinSizeRel. - Config-specific build dirs + build manifest for stale detection. Swift/C++ interop: - SWIFT_NONCOPYABLE on WebsocketSwiftBridge and SwiftApiClient. AGENTS.md: updated stale architecture docs. Tests: 22/22 passed. Debug build succeeded.
…tics Pin fptn submodule to 2016d5f (pr1a/ios-native-baseline). Socket buffers: - Remove conflicting 4 MiB / 1 MiB TCP buffer assignments (commented out for easy restoration). First experiment uses kernel default. - FPTN_IOS_SOCKET_BUFFER_BYTES CMake parameter for subsequent tests (256 KiB, 512 KiB) without source edits. - Buffers applied before TCP connect; effective values queried after. - Requested + effective values exposed in status struct. Beast reader: - 64 KiB initial reserve with a 256 KiB WebSocket message limit (was 4 MiB reserve). Packet path: - Remove SPDLOG_WARN + Mach task_info syscalls from packet callback adapter. Atomic counters remain for getStatus(). Concurrency: - io_context concurrency hint of 1 on iOS (was 4). Not a thread reduction — the wrapper already runs one native thread. Lifecycle counters: - Process-wide statics: live_clients, active_reader/sender_coroutines. - RAII guard handles all exit paths. Build manifest: - ios_socket_buffer_bytes added to manifest + cache validation. Wrapper status struct extended with 7 new fields (both app + tunnel). Tests: 22/22 passed. Debug build succeeded.
Pin fptn submodule to 7443f40 (pr1b/queue-bounds). C++ bridge: - sendPacket() returns uint8_t (0=accepted, 1=queue_full, 2=transport_stopped, 3=invalid_packet) - Status struct: +4 queue fields (queued_packets, queued_bytes, queued_bytes_peak, queue_full_count) Swift: - WebsocketSendResult enum in SharedTunnelRuntime (shared by app + tunnel). Mapped from C++ uint8_t via init(bridgeValue:). - TunnelWebSocketTransport protocol updated: sendPacket returns WebsocketSendResult. - Both wrappers (app + tunnel) return typed result. - Status struct: +4 queue diagnostic fields. Provider: - startReadLoop uses switch by result: .queueFull → feeds backpressure .transportStopped → breaks batch early (labeled loop) .invalidPacket/.unknown → counted, does not slow valid traffic Tests: 22/22 passed. Debug build succeeded.
…connect Pin fptn submodule to aad9923 (pr1c/teardown-ownership). C++ bridge: - stop() accepts origin (uint16): 1=swift_tunnel_stop, 2=swift_reconnect, 3=native_failure, 4=peer, 255=unknown - Status struct: +disconnect_code, stop_origin, stop_cleanup_completed, active_operations - client_run_thread: local strong pointer, terminal reason stored after Run() returns Swift: - WebsocketDisconnectCode + WebsocketStopOrigin enums in SharedTunnelRuntime (matching C++ ABI values) - TunnelWebSocketTransport protocol: stop(origin:) - Both wrappers: stop(origin:), status mapping for 4 new fields - Provider: existing stop() calls use default .swiftTunnelStop Tests: 24/24 passed. Debug build succeeded.
Provider invariants: - ProviderInvariant enum (bridgeReplacementOverlap, multipleNativeClients, incompleteNativeTeardown). Logged once per type via logInvariantOnce(). - replaceWebSocketClient checks for overlap and incomplete teardown. - No manual activeBridgeCount — uses existing native status fields. Generation ownership for pending reads: - pendingReadGeneration captured when calling readPackets. - Stale batch (old generation) dropped after reconnect instead of being sent through the new bridge. Final native status preservation: - lastNativeStatus / lastNativeStatusGeneration stored before detaching a client. currentSnapshot() falls back to saved status when no live client is attached. currentSnapshot() outside lock: - Swift fields copied under stateLock, bridge reference copied, lock released, then native status queried outside the lock. TunnelRuntimeSnapshot enrichment: - All PR1A–1C native fields: socket buffers, lifecycle counters, queue diagnostics, disconnect code/origin, active operations, stop cleanup status. Stop-origin plumbing: - replaceWebSocketClient accepts stopOrigin parameter (default .swiftTunnelStop). All call sites use correct origin. VPNService fallback reconnect: - Cancellable Task replaces boolean guard. Cancelled on connected/connecting/reasserting/disconnect(). - FallbackReconnectDecision: doNotReconnect / reconnectNow / recheckAfter(seconds). Fresh heartbeat (< 45s) defers recheck. - Rechecks NE status after sleeping. - Budget off-by-one fixed: check before decrement, N allows N attempts. Tests: 24/24 passed. Debug build succeeded.
TunnelDiagnosticsCore static library: - C++23 implementation with RAII (PosixFile, FlightRecorder, LifecycleStore) - C-compatible public ABI (void* opaque handles, extern "C") - Explicit little-endian byte serialization (no struct dumps) - CRC32 checksums on all records and snapshots - 72-byte flight events, 120-record ring (8,704 bytes fixed) - Double-buffered lifecycle snapshot (2 × 512 bytes) - Thread-safe recorder (internal mutex) - Reopen recovery: scans existing records, resumes from max sequence - No exceptions, no RTTI, no heap allocation on record path Swift wrappers: - TunnelFlightRecorder: RAII over C handle, allocation-free record() - TunnelLifecycleSnapshotStore: RAII over C handle - ProviderProcessIdentity: process-lifetime token + mach timestamp - TunnelFlightEventCode: 24 numeric event codes Build integration: - DiagnosticsCoreBase XcodeGen template (library.static) - Platform targets: iOS, macOS, tvOS - Bridging headers include FptnTunnelDiagnostics.h - HEADER_SEARCH_PATHS for app + tunnel targets Provider integration (initial): - Flight recorder + snapshot store initialized in startTunnel - process_started + start_tunnel events recorded with fsync - Per-tunnel session token generated Remaining: full call-site migration, app-side decoder, fallback migration, legacy JSON gate — follow-up commits. Tests: 27/27 passed. Debug build succeeded.
TunnelSignposts (FptnVPNTunnel/Diagnostics/TunnelSignposts.swift): - OSSignposter with subsystem net.mrmidi.FptnVPN.FptnVPNTunnel - Compiled only when FPTN_SIGNPOSTS is defined (Debug + Measurement) Intervals: - TunnelStartup: startTunnel → finishStart - BridgeLifetime: bridge created → bridge stopped (per generation) - NativeTeardown: bridge detached → final status captured - TunnelShutdown: stopTunnel → completionHandler - ReconnectDelay: reconnect scheduled → fired/cancelled Point events: - TransportConnected, TransportDisconnected - NetworkPathChanged, MemoryWarning, InvariantViolation - PacketReadLoopStarted, PacketReadLoopStopped Build config: - FPTN_SIGNPOSTS added to Debug and Measurement configs - No signposts in Release (production) No persistence, no JSON, no per-packet instrumentation. Numeric metadata only, no interpolated strings. Tests: 27/27 passed. Debug build succeeded.
TunnelDiagnosticsDecoder (FptnVPN/Services/): - Injectable paths, clocks, ticksToSeconds (mach_timebase_info) - Per-process wall-time anchors from processStarted events - Unknown-safe: preserves raw event codes, timestamp nil without anchor - FptnReadStatus: OK/FileNotFound/InvalidHeader/Empty/IoError - Full ring header validation: magic, schema, geometry, CRC, file size - errno-based status (ENOENT vs IoError) - Null-safe output_count check - Failure report filtered by processToken + sessionToken - Logical clear watermark (hide events at/below watermark) VPNService migration: - evaluateFallbackDecision uses binary snapshot - Controlled-stop check first (trust window), then freshness recheck - Failure report uses makeFailureReport (session-filtered) LogsViewModel migration: - Export uses binary decoder - Clear writes logical watermark instead of deleting binary files Event value payloads: - transportDisconnected: value0=disconnectCode, value1=stopOrigin, value2=activeOps, flags[0]=wasConnected - reconnectScheduled: value0=attempt, value1=delaySeconds, flags[0]=pathSatisfied - memoryWarning/Critical: value0=footprint, value1=resident, value2=peak - Memory recording separated from OSLog throttle (level-change tracking) - physicalFootprintPeakBytes captured under diagnosticsLock Schema ABI: - fptn_diagnostics_schema_version, capacity, record_size, ring_file_size - FptnReadStatus enum in public C header Remaining: FptnShared 0.3.0 integration (after PR4a merge/tag) Tests: 27/27 passed. 8 files, +500/-65.
… connection coordinators
…o prevent segfault
…aces, and report OUTDATED_TOKEN on cert mismatch
…ert mismatch fixes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
M7 server selection architecture, native bootstrap, and iOS hard cutover