Files
Claude d2a10c9e4a feat(quic): live interop harness — picoquic Docker + InteropRunner main
Setup for driving the pure-Kotlin QUIC client against real reference
servers, kicking off the live-interop validation that closes the loop on
the audit cycles.

PermissiveCertificateValidator (commonMain test-only):
  Accepts any cert chain and any signature. For local dev servers (picoquic,
  quic-go, nests-rs in Docker) where the system trust store would reject the
  self-signed cert. Documented as test-only — must never be wired into
  production code.

InteropRunner.main (jvmTest):
  Standalone runnable that opens a UDP socket, builds a QuicConnection with
  PermissiveCertificateValidator, drives the handshake under a configurable
  timeout, and reports CONNECTED / HandshakeFailed / Timeout / UdpFailed
  with peer transport parameters on success. Exit code 0 on connect, 1 on
  any failure mode — wirable into CI.

`quic/scripts/run-picoquic.sh`:
  One-line Docker harness that runs Christian Huitema's picoquic reference
  server on UDP 4433. Picoquic is the IETF QUIC WG's reference impl and
  the most permissive target for a fresh client (clear qlog traces, accepts
  a wide range of transport params).

`./gradlew :quic:interop` Gradle task:
  Wraps the runner so live-interop is one command:
    ./gradlew :quic:interop -PinteropHost=127.0.0.1 -PinteropPort=4433
  Validated against a non-running server: returns Timeout cleanly with
  exit 1 (the failure-path proves the harness wires correctly).

Workflow documented in `quic/scripts/README.md` covering picoquic,
quic-go, aioquic, quiche, and the eventual graduation path to the
quic-interop-runner Docker matrix at https://interop.seemann.io.

Next step: actually run picoquic in Docker and chase whatever real-world
incompatibilities surface. Each will be a focused fix commit.

https://claude.ai/code/session_01EC1tfXfap8k8GyKvrxkxZx
2026-04-25 23:08:18 +00:00
..

:quic interop harness

Scripts and test entry points for driving the pure-Kotlin QUIC client against real reference servers.

Quickstart — picoquic

# Terminal 1: run the picoquic reference server in Docker.
quic/scripts/run-picoquic.sh -d

# Terminal 2: drive our client at it.
./gradlew :quic:jvmTestClasses
java -cp "$(./gradlew -q :quic:printTestRuntimeClasspath)" \
  com.vitorpamplona.quic.interop.InteropRunnerKt 127.0.0.1 4433

Expected output:

== :quic interop runner ==
target:  127.0.0.1:4433
timeout: 10s

✓ HANDSHAKE COMPLETE
  status:               CONNECTED
  negotiated ALPN:      h3
  peer transport params: max_data=…, max_streams_bidi=…, …

What this proves

  • TCP-equivalent UDP connection setup
  • QUIC v1 Initial / Handshake / 1-RTT packet flow with RFC-correct PADDING
  • TLS 1.3 over QUIC with the SHA-256 cipher suites
  • ALPN h3 negotiation
  • QUIC transport parameters round-trip
  • Header protection (AES-ECB)
  • AEAD payload protection (AES-128-GCM)

It does NOT yet prove WebTransport / MoQ — picoquic doesn't speak WT.

Live nests interop

For a full WebTransport + MoQ test against the actual Nostr nests server, the target is nostrnests.com:443:

java -cp "..." com.vitorpamplona.quic.interop.InteropRunnerKt nostrnests.com 443

This goes against the real CA-signed cert, so revert to the default JdkCertificateValidator (the InteropRunner currently uses PermissiveCertificateValidator for self-signed dev servers — change the constant before pointing at production).

Other reference servers worth trying

Server Image Notes
picoquic privateoctopus/picoquic Most permissive; clear qlog traces
quic-go martenseemann/quic-go-interop Stable, widest scenario coverage
aioquic aiortc/aioquic Easy to debug, Python reference
quiche cloudflare/quiche Production-grade, strict
nests-rs (local cargo build) The actual MoQ relay; needs WebTransport

The IETF's quic-interop-runner exposes all of these via a single Docker matrix. Wrapping our client in its container contract (TESTCASE env, REQUESTS= URL list, /certs mount) would let us join the public matrix at https://interop.seemann.io.