Skip to content

feat(capture): live-proxy sockets, MITM handshake relay + concurrency (closes #56)#64

Merged
sepehr-safari merged 1 commit into
mainfrom
feat/proxy-sockets
Jul 12, 2026
Merged

feat(capture): live-proxy sockets, MITM handshake relay + concurrency (closes #56)#64
sepehr-safari merged 1 commit into
mainfrom
feat/proxy-sockets

Conversation

@sepehr-safari

Copy link
Copy Markdown
Member

Closes #56 — completes the MITM proxy, the heart of the S5 flagship. Part 1 (the socket-free pump pipeline) landed in #63; this is part 2: the transport, the handshake relay, and the concurrency.

What's here

  • Sockets (run) — wires std.Io.net: IpAddress.listenServer.accept (downstream CP) and IpAddress.connect (upstream CSMS), driving the relay over each stream's reader/writer.
  • MITM handshake relay (relayHandshake) — two independent RFC 6455 handshakes, one per peer (server to the CP, client to the CSMS), reusing the feat(capture): WebSocket transport — RFC 6455 subset (ADR-0008) #54 both-halves codec and mirroring the CP's request path upstream.
  • Concurrency (relayStreams) — the two pump directions run under io.concurrent (true parallelism — both block on reads, so io.async could deadlock), guarded on the shared Sink by std.Io.Mutex. One side closing cancels the other.
  • ADR-0010 records these decisions (and catches the ADR index up).

The integration test = the S5 exit criterion

relayStreams is generic over *Io.Reader/*Io.Writer, so the whole relay is driven in-memory under test with a real std.Io.Threaded io (genuine concurrency + the mutex). One test proves, end to end: MITM handshake → concurrent pump → relay correctness → the recording re-parses offline → streaming detection matches a cold offline pass.

No OS socketpair is used — socketpair(2) here supports only AF_INET, which the OS rejects, and no AF_UNIX pair is exposed. The thin socket glue in run() is force-analyzed by a test (_ = &run) and will be exercised live when the studio capture CLI wires it (#57).

Zig 0.16 notes (hard-won)

  • Time moved onto io (Clock.now(.real, io)), so a plain ?i64 closure can't supply wall-clock — replaced with a small TimeSource union (fixed for tests, wall for live). This also fixes pt1's wallClockMs, which used the removed std.time.milliTimestamp and had never been analyzed (lazy analysis).
  • std.Thread.Mutexstd.Io.Mutex; std.crypto.random/std.time timestamps gone.

Verification

  • native test -Dplatform=null150/150.
  • native build (ReleaseFast) → clean.
  • zig fmt --check clean.

Deferred (noted)

Single session per run (accept one CP, relay, return) — a multi-session accept loop is a later refinement behind the same relay core. TLS stays post-0.5 (ADR-0008).

Completes the MITM proxy (#56 part 2): the real-socket transport, the MITM
opening-handshake relay, and the bidirectional concurrency that pt1's pump
pipeline plugs into.

- run() wires std.Io.net: IpAddress.listen -> Server.accept (CP) and
  IpAddress.connect (CSMS), driving the relay over each stream's reader/writer.
- relayHandshake performs two independent RFC 6455 handshakes (server to the CP,
  client to the CSMS), reusing the #54 both-halves codec and mirroring the CP's
  request path upstream. The upstream key is derived deterministically from the
  CP's key (sessionNonce) -- not a secret, and reproducible under test.
- relayStreams runs the two pump directions with io.concurrent (true parallelism,
  since both block on reads; io.async could deadlock), guarded on the shared Sink
  by std.Io.Mutex. When one side closes, the other is cancelled.
- relayStreams is generic over *Io.Reader/*Io.Writer, so the whole relay is
  driven in-memory under test with a std.Io.Threaded io -- no OS socketpair
  (socketpair(2) here supports only AF_INET, which the OS rejects). The
  integration test proves the MITM handshake, concurrent pump, relay correctness,
  the recording re-parsing offline, and streaming detection matching a cold
  offline pass -- the S5 exit criterion.
- Wall-clock time moved onto io (Clock.now(.real, io)) behind a small TimeSource
  union (fixed for tests, wall for live), replacing pt1's wallClockMs, which used
  the removed std.time.milliTimestamp and had never been analyzed.

ADR-0010 records the transport and concurrency decisions.

Closes #56.
@sepehr-safari sepehr-safari merged commit ce93ce6 into main Jul 12, 2026
3 checks passed
@sepehr-safari sepehr-safari deleted the feat/proxy-sockets branch July 12, 2026 12:57
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.

feat(capture): MITM proxy — streaming detection + record-to-trace

1 participant