2026-04-29

End-of-day snapshot. Everything below was verified live on a physical iPhone 17 (iOS 26.4.2) with the VPN active, browsing real sites in Safari.

Status: working as expected

  • DNS forwarding (UDP/53 → NWConnection → response packet back to TUN): healthy.
  • TCP forwarding (userspace state machine, MSS segmentation, FIN bookkeeping centralised in send()): healthy. No SYN-replay races (per-flow epoch token), no half-open leaks (idle eviction sweep at 90 s).
  • TLS SNI extraction: now works on any port — dns.google (DoT 853), *.push.apple.com (5223), IMAPS (993), etc. resolve to hostnames in the Network tab.
  • HTTPS MITM decryption: working end-to-end on busymate.io, busydrivers.com, portal.busydrivers.app. Cert generation hits the per-host cache after the first request; concurrent sessions for the same host start in <1 ms.
  • Per-pair HTTP/1.1 framing (HTTP1MessageParser): traffic-log entries land in the in-app Network tab in real time. No more 2-minute keep-alive parking. Verified on multi-request keep-alive connections.
  • ALPN stop-gap: http/1.1 advertised on both MITM TLS legs so upstream falls back to HTTP/1.1; status codes parse correctly (200/304/400/404/204 all observed).
  • Log rotation: traffic.jsonl capped 10/5 MB, diagnostics.log 5/2.5 MB, truncation lands at the next newline so JSONL stays parseable.
  • Incremental polling: single seek + readToEnd per tick (4 Hz), no full-file decode.
  • CA private key (ca.key) now written with .completeFileProtectionUntilFirstUserAuthentication.

Configuration

  • App Group: group.com.busymatehelper
  • Main app bundle ID: com.busymatehelper.app
  • Extension bundle ID: com.busymatehelper.app.tunnel
  • SSL proxy domain list (managed in Settings tab): busymate.io, busydrivers.com, portal.busydrivers.app

Repo layout (as of today)

busymate-devtools/ios/BusymateHelper/SwiftUI appExtensions/TrafficLogEntry+NetworkRequest (main-app-only adapter)BusymateHelper.xcodeprojPacketTunnelExtension/Tunnel target (zero-width-char path)Shared/← shared synchronized group, both targetsTrafficLogEntry.swiftweb/docs/Next.js viewer for the repo-root docs/ treedocs/architecture/busymate-helper.mdchangelog/2026-04-29.md(this file)CLAUDE.md.gitignore

Today's commits (in order)

CommitSummary
a343e0cRename BusymateVPN → BusymateHelper; tighten bundle IDs
deb5778Restructure monorepo: ios/, web/, docs/
b62e5b8Apply audit Tier 1 fixes (10 items)
8b241c9Apply audit Tier 2 fixes (6 items)
e2b29d6SNI buffering: fall back early on complete-but-unparseable records
9bee201ALPN http/1.1 stop-gap on MITM TLS legs
6c62558Tier 3 #2: per-request HTTP/1.1 framing in MITMSession
db95a53Fix HTTP1MessageParser crash on Data subscript with non-zero startIndex
1dd4ad1Audit polish: Y2050 DER, UInt64 session IDs, surface log-write errors
18ac2c1sniffTraffic: extract SNI on any TLS port (not just 443)
6e609ceUpdate architecture doc + monorepo blurb
83f2f1dAudit #13: dedupe TrafficLogEntry via shared synchronized group

Audit close-out

23 of 24 items shipped. Only remaining item:

  • HTTP/2 framer + HPACK decoder — would let MITM speak HTTP/2 to upstream and remove the http/1.1-only ALPN constraint. Multi-hour, only needed if/when there's a need to inspect h2-only servers' wire frames.

Plus one cosmetic-only nit untouched: indent glitch at ios/Packet​Tunnel​Extension/PacketTunnelProvider.swift:126.

Known gaps (separate from the audit)

  • UDP/443 (QUIC / HTTP/3) is silently dropped at inspectPacket. Modern Safari tries HTTP/3 first and falls back to TCP/443 after a short timeout. This adds latency on first request to QUIC-preferring origins.
  • Network-tab entries for long-lived non-MITM connections (APNs, DoT) appear only when the connection closes (FIN/RST/eviction). Persistent connections that stay open for hours won't show until the tunnel stops.
  • The shared-group Shared/ synced root group is a relatively new Xcode feature. If anyone opens the project in older Xcode, the dedup may not be honoured.