Commit Graph

15 Commits

Author SHA1 Message Date
Vitor Pamplona 1ff4f98fff docs(quic-interop): add quinn to the default peer set
quinn is now treated as a flawless-required interop target alongside
aioquic, picoquic, and quic-go. Most Rust-based Nostr/MoQ relays our
users run their servers on are built on quinn, so an interop regression
there is a user-visible regression.

Validated by a 3-round flakiness sweep (1 full matrix + 2 audio-critical
subsets) — zero result flakiness across 528 test executions across all
four peers, with two environmental docker-compose stall classes documented
separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:44:15 -04:00
Vitor Pamplona 5e3e5cc5a0 chore(quic-interop): auto-patch upstream certs.sh for macOS BSD tr
`LC_CTYPE=C tr -dc '[:alnum:]' </dev/urandom` in the upstream runner's
certs.sh trips "tr: Illegal byte sequence" on macOS — LC_CTYPE alone
doesn't override the runtime locale chain. Only the amplificationlimit
testcase exercises this loop (chain length > 1 → fakedns SAN inflation),
but if it aborts the runner stops the whole matrix BEFORE any later
test in TESTCASES_QUIC runs: handshakeloss, transferloss,
handshakecorruption, transfercorruption, ipv6, v2, rebind-port,
rebind-addr, connectionmigration, and the goodput/crosstraffic
measurements all silently never execute. The post-mortem summary
shows the 12 testcases that ran before the abort and looks deceptively
complete.

Add an idempotent `sed` step to run-matrix.sh that rewrites the line
to `LC_ALL=C tr` on every invocation, plus a plan-file note so the
next person hitting the deceptive summary doesn't repeat the
diagnosis. The patch is a working-tree edit to ../quic-interop-runner/,
not a fork; re-applied on every clone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:04:28 -04:00
Claude 4e00c8db07 revert(quic-interop): drop explicit QPACK SETTINGS — made multiplexing worse
The QPACK_MAX_TABLE_CAPACITY=0 advertisement worsened the result
(1 file → 0 files written). Empirical: empty SETTINGS = spec defaults
(both 0) and aioquic was already sending literal-encoded responses
under that condition.

Real bug isn't QPACK — diagnostic showed 1359 GETs processed in 58s
(~23 streams/sec). Throughput is hard-bottlenecked by :quic's single
conn.lock serializing send loop + read loop + openBidiStream across
1999 waiting coroutines. Even at maximum throughput we'd only complete
~1400 of 1999 in 60s. The 1 file we managed previously was just the
first response landing before lock contention spiked.

Filed multiplexing as a known-throughput-limit follow-up. Possible
fixes (per-level lock split / Semaphore-bounded concurrency / QPACK
dynamic-table support) are all non-trivial and the testcase is a
stress test, not a real-world load shape.

For now: revert to empty SETTINGS, accept multiplexing as the lone
deferred testcase. Aioquic + picoquic stay at 6/7.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-07 01:33:23 +00:00
Claude 0fea36fa56 docs(quic-interop): plan reflects Phase 5 — overnight bug-hunt complete
Records the qlog-driven debugging session that fixed:
  - QlogWriter close-race breaking healthy connections
  - QlogWriter per-event flush stalling the connection lock
  - PTO probe missing CRYPTO retransmit (aioquic close)
  - Retry test PN-reuse violation (RFC 9001 §5.7)
  - Multiplexing 30s timeout
  - Hung-stream blocks the parallel-await chain

Still open:
  - v2 testcase (needs RFC 9369 implementation)
  - Multiplexing throughput on Mac+Rosetta
  - Server role (would unlock handshakeloss vs aioquic)

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-07 01:13:06 +00:00
Claude 2ec995b1b6 docs(quic-interop): plan reflects Phase 4 + retry-debug roadmap
Documents:
  - Three overnight agents merged (VN defense in :quic, qlog observer
    + JSON-NDJSON writer wired into the prod endpoint, peer-uni-stream
    drainer fixing the multiplexing tear-down).
  - The agent-A versionnegotiation testcase mismatch — runner doesn't
    have that name; v2 is the closest, tests QUIC v2 which we don't
    speak. VN code stays as defensive support.
  - Open issues for tomorrow: retry test failure (qlog now available
    for diagnosis), v2 (deferred), re-runs against all three peers
    with the new fixes.
  - Documented run-matrix.sh's concurrency limit.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-07 00:38:11 +00:00
Claude 70355898b3 docs(quic-interop): record overnight agent roadmap + post-fix predictions
Three agents in flight in worktrees:
  A — versionnegotiation testcase + configurable initial-version writer
  B — qlog observer infrastructure (QlogObserver interface in :quic,
      JSON-NDJSON writer in :quic-interop, hooks at packet/key/recovery
      sites, reads $QLOGDIR the runner already sets)
  C — multiplexing channel-saturation fix (consume server's peer uni
      streams in Http3GetClient — RFC 9114 §6.2 says clients MUST
      process incoming control + QPACK streams)

Plan doc now also captures the latest test matrix (pre-acfe815e1
multi-ALPN-offer fix) and the predictions for the next run.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-07 00:13:04 +00:00
Claude 038eb18617 feat(quic-interop): enable retry + ipv6 testcases; document phase 3
Two more testcases now dispatched through runTransferTest:
  - retry  — exercises the RFC 9000 §17.2.5 + RFC 9001 §5.8 Retry
             handling that landed in d03e17981 / 671f9c705 (DCID swap,
             integrity-tag verify, key re-derivation, token threading).
  - ipv6   — same flow over an IPv6 socket. JDK's DatagramChannel.connect
             handles the v6 address resolution natively; if anything
             breaks it'll be an actual bug worth surfacing.

Plan doc updated to reflect Phase 3 landings (ALPN per testcase,
HqInteropGetClient, multi-stream FIN delivery fix) and the current
validation matrix across aioquic / picoquic / quic-go.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-07 00:06:25 +00:00
Claude a009dfc425 chore(quic-interop): drop the smoke target — runner is the canonical path
make smoke was the bisector for "is the bug in :quic or in the runner
environment?" while we were debugging the initial close-path / PTO /
padding issues. Now that the runner reliably runs the matrix end-to-end
(handshake + chacha20 green vs aioquic), smoke's only job — running
picoquic outside the runner — is unneeded, and the picoquic image
keeps changing its entrypoint / required args in ways that make smoke
finicky to maintain.

Removes:
  - make smoke / smoke-down targets
  - SMOKE_NET / SMOKE_PICOQUIC / SMOKE_CLIENT vars
  - SMOKE_MODE handling in run_endpoint.sh (just always tolerates
    /setup.sh failure now — same effect, less ceremony)
  - Plan doc note about make smoke updated to reflect removal.

If we ever need a non-runner bisector again, it's two `docker run`
commands; not worth permanent maintenance.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-06 23:25:08 +00:00
Claude d1dadfb962 fix(quic-interop): runner renamed implementations.json → implementations_quic.json
Upstream quic-interop-runner split its config into implementations_quic.json
+ implementations_webtransport.json so QUIC and WebTransport endpoint
registrations don't collide. Schema is unchanged; just the filename.

Also updated the Makefile comment + plan doc for consistency.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-06 22:08:05 +00:00
Claude 86192312ca feat(quic-interop): add run-matrix.sh wrapper for the per-run loop
One-shot wrapper that clones quic-interop-runner alongside this repo if
missing, sets up its venv, merges our implementations.json snippet, builds
the endpoint image, then invokes run.py with passed-through args. Every
step is idempotent so repeated invocations just iterate.

Designed to script only the per-run loop, not first-time tooling install
(Docker Desktop, Homebrew, Wireshark prefs) — those are GUI / one-time
steps where a script either fails awkwardly or hides what the user is
consenting to.

Cross-platform install hints (apt-get on Linux, brew on macOS) on missing
prereqs. Daemon liveness check (docker info) catches the common macOS
"Docker Desktop installed but not running" trap. SKIP_BUILD=1 escape
hatch for tight image-unchanged inner loops.

Plan doc updated: run-matrix.sh is now the documented happy path; the
manual jq-merge sequence is kept as a fallback.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-06 22:06:06 +00:00
Claude 093a39a3ee feat(quic-interop): alias sim-driven testcases + document unsupported ones
The quic-interop-runner exposes several testcases that drive the same
client logic but vary the network conditions injected by the ns-3 sim.
These don't need new client code on our side — they exercise the
existing handshake / transfer paths under loss / corruption / high-RTT /
cross-traffic, which is exactly the bug-finding signal we want.

Aliases added:
  - transferloss, transfercorruption, longrtt, goodput, crosstraffic
    → transfer (H3 GET against varying sim configs)
  - handshakeloss → handshake

Plan doc now lists every standard testcase and either marks it landed,
aliased, or explicitly unsupported with a written reason — so anyone
returning to this knows what's left and why each gap exists. Unsupported
set covers: versionnegotiation (writer hard-codes V1), resumption /
zerortt (no session ticket / 0-RTT), keyupdate (no KEY_PHASE handling),
retry (parser exists but not wired to feed-loop), rebinding-* (no client
migration), amplificationlimit (server-side), blackhole (inverse test),
ipv6 (UdpSocket v6 path unverified).

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-06 21:57:52 +00:00
Claude 68d0cdf2a2 feat(quic-interop): add transfer / multiplexing / http3 testcases via H3 GET client
Adds a minimal Http3GetClient (in :quic-interop, NOT :quic — interop-test
surface, not a production HTTP/3 client) that opens the three required
client uni streams (control + QPACK encoder + QPACK decoder per RFC 9114
§6.2.1), sends an empty SETTINGS, and per request opens a bidi stream,
encodes the four pseudo-headers via the existing literal-only QpackEncoder,
FINs, and reassembles HEADERS+DATA frames from the response.

Wires three testcases:
  - transfer: GET each REQUESTS URL sequentially, write body to
    \$DOWNLOADS/<basename>. status != 200 fails.
  - http3: identical to transfer.
  - multiplexing: same fetches but issued in parallel via
    coroutineScope { async { … } } so request streams genuinely
    overlap on the wire (what tshark verifies).

Out-of-scope (deliberate): GOAWAY, PUSH_PROMISE, dynamic QPACK table,
trailers, priority — none are required by these testcases.

Unit-tests round-trip the request encoding through the existing
Http3FrameReader + QpackDecoder so the wire format is verified without
needing a real peer.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-06 21:42:56 +00:00
Claude 7c41aa6dde test(quic-interop): unit-test SslKeyLogger + ship runner snippet
Refactors SslKeyLogger to take the ClientHello random at flush() time
(removes the lookup-lambda + bindConnection ceremony) so the formatter
is testable without spinning up a real QuicConnection. Adds three
unit tests covering: the four NSS Key Log lines emitted in the right
order, flush idempotency, and lower-case hex encoding.

Also drops a checked-in implementations.json snippet
(quic-interop-runner-snippet.json) so a sibling clone of the runner
can be registered with one jq merge instead of hand-edited JSON.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-06 21:37:06 +00:00
Claude afe11ac651 feat(quic-interop): wire SSLKEYLOGFILE + add chacha20 testcase (Phase 1a)
Adds two debugging hooks to :quic so the interop endpoint can produce
artifacts that make the runner actually useful for finding bugs:

  - TlsClient.clientRandom: capture and expose the 32-byte ClientHello
    random so a SSLKEYLOG line can correlate to this connection.
  - QuicConnection.extraSecretsListener: optional chained secrets
    listener (default null, no-op for production callers). Fires
    alongside the connection's own key-installation listener at every
    encryption-level transition.
  - QuicConnection.cipherSuites: knob to override the offered TLS
    cipher suites in ClientHello.

InteropClient now:
  - Writes NSS Key Log Format lines to $SSLKEYLOGFILE when set, so
    Wireshark can decrypt the sim's pcap captures.
  - Implements the `chacha20` testcase by offering only
    TLS_CHACHA20_POLY1305_SHA256 — exercises the ChaCha20 AEAD path
    end-to-end against a peer.

Defers QLOGDIR (needs qlog observer infrastructure across packet/
frame/recovery layers — own design doc) and `versionnegotiation`
(needs the writer to accept a configurable initial QUIC version).

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-06 21:32:44 +00:00
Claude 1586c0cced feat(quic-interop): scaffold quic-interop-runner endpoint module
Adds :quic-interop, a JVM-only module at quic/interop/ that implements
the quic-interop-runner Docker contract so we can drive matrix tests
against aioquic / quiche / picoquic / msquic / ngtcp2 / etc. as a
bug-finding harness.

Phase 0 supports only the `handshake` testcase; everything else returns
127 so the runner skips rather than fails. SSLKEYLOGFILE / QLOGDIR
plumbing is deferred to Phase 1.

https://claude.ai/code/session_01HcvfQq1ttPV9PkRoJb4nyT
2026-05-06 21:08:02 +00:00