Skip to content

feat: M7 server selection architecture & iOS native bootstrap hard cutover#10

Open
mrmidi wants to merge 18 commits into
masterfrom
pre-pr5/selection-architecture
Open

feat: M7 server selection architecture & iOS native bootstrap hard cutover#10
mrmidi wants to merge 18 commits into
masterfrom
pre-pr5/selection-architecture

Conversation

@mrmidi

@mrmidi mrmidi commented Jul 22, 2026

Copy link
Copy Markdown
Owner

M7 server selection architecture, native bootstrap, and iOS hard cutover

mrmidi added 18 commits July 20, 2026 09:43
…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.
…aces, and report OUTDATED_TOKEN on cert mismatch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant