v3/docs/adr/ADR-120-midstream-quic-from-agentic-flow.md
Status: Proposed (2026-05-14)
Date: 2026-05-14
Authors: claude (drafted with rUv)
Related: ADR-104 (federation transport, WebSocket today + clean QUIC upgrade path) · ADR-108 (agentic-flow native QUIC binding plan, loader pattern) · ADR-111 (WireGuard mesh) · ADR-118 (AIMDS sibling adopted) · ADR-119 (midstreamer assessed — decision was wait; this ADR is the "what would change the answer" trigger)
Supersedes: nothing
Sources: agentic-flow QUIC-STATUS.md (Oct 17, 2025) · agentic-flow crates/agentic-flow-quic/ · midstream crates/quic-multistream/
Both ruvnet/agentic-flow and ruvnet/midstream have a QUIC crate. They are not the same crate:
| Concern | agentic-flow-quic (v0.1.0) | midstreamer-quic (v0.2.1) |
|---|---|---|
| Native Rust | quinn 0.11 + tokio 1.40 + rustls 0.23 + rcgen 0.13 | quinn 0.11 + tokio 1.42 + rustls-platform-verifier 0.6 + rcgen 0.12 |
| WASM target | wasm-bindgen + web-sys (console only) | wasm-bindgen + web-sys (WebTransport API: WebTransport, WebTransportBidirectionalStream, WebTransportDatagramDuplexStream, …) |
| Production status (per upstream docs) | 100% complete, Oct 17 2025 — UDP sockets, HTTP/3 QPACK, varint, handshake state machine, 0-RTT reconnection (91.2% faster), 53.7% lower latency than HTTP/2, 7931 MB/s throughput | Crate exists with native quinn impl and proptest-based tests; npm-published WASM ([email protected]) is a counter-tracking stub (per ADR-119) |
| Bridge to Node | src/transport/quic.ts — UDP dgram socket ↔ WASM sendMessage/recvMessage bridge, working | Not published — WASM build is purely WebTransport-targeted (browser) |
| Security posture | TLS via rustls 0.23 | Adds rustls-platform-verifier for OS trust store (ADR-0011 in upstream) — closer to production posture; explicit "never enable in production" flag for self-signed test mode |
So agentic-flow-quic has the integration layer (UDP socket bridge in TypeScript, handshake state machine, validated 0-RTT reconnection, packet-level WASM bridge); midstreamer-quic has the better Rust crate (OS-trust-store verifier, proptest coverage, slightly newer toolchain pin). Cross-pollinating these two would produce a single QUIC transport that is both production-grade in Rust and shippable as a working npm package — which is the gap ADR-108 has been waiting on.
ruflo's federation transport (ADR-104) and the native-QUIC upgrade plan (ADR-108) were both architected around a loader pattern (AGENTIC_FLOW_QUIC_NATIVE=1 + same AgentTransport interface) that's intentionally transport-agnostic. The federation WireGuard mesh (ADR-111) operates at the OS network layer below this — orthogonal. The AIMDS / aidefence sibling component (ADR-118) provides the in-flight safety gates. The only remaining gap to fully Rust-native federation + in-flight agentics is: real QUIC reachable from Node with a verified TLS posture.
This ADR proposes a three-step plan to close it.
Step 1 — Cross-port the agentic-flow QUIC bridge into midstream and republish midstreamer-quic with the production WASM build (upstream work in ruvnet/midstream). Step 2 — Update ruflo's ADR-108 loader to detect the new midstream WASM build. Step 3 — Compose midstreamer-quic + aimds-* into a single ruflo Rust transport that runs the federation hops and the in-flight gate in one process.
midstream adopts agentic-flow's bridge patternThe current midstreamer-quic Rust crate is the better foundation (newer tokio, OS-trust-store verifier, proptest coverage). What it's missing is the production WASM build + Node bridge that agentic-flow-quic already validated end-to-end (per QUIC-STATUS.md, October 17, 2025: UDP sockets working, handshake state machine complete, 53.7% latency improvement vs HTTP/2 validated, 0-RTT reconnection at 91.2% improvement validated).
The cross-port is structurally small because the two crates share quinn 0.11:
Bring over from agentic-flow-quic | Into midstreamer-quic |
|---|---|
src/transport/quic.ts UDP dgram ↔ WASM sendMessage/recvMessage bridge layer (~200 lines) | New npm-wasm/bridge.ts (or equivalent in midstream's existing npm-wasm/ layout) |
src/transport/quic-handshake.ts QuicHandshakeManager state machine (Initial → Handshaking → Established → Failed → Closed) | New npm-wasm/handshake.ts |
quic-loader.ts lazy-load + path resolution | New npm-wasm/loader.ts |
Performance benchmarks (tests/quic-performance-benchmarks.js, the ones that produced the 53.7% / 91.2% numbers) | Add as crates/quic-multistream/benches/ companion JS suite |
What midstream keeps:
rustls-platform-verifier (OS trust store) — strictly better security posture than agentic-flow-quic's rustls direct.insecure-dev-only-skip-server-verification feature flag — explicitly documented "MUST NEVER be enabled in production builds" plus runtime warning.What changes in midstream:
npm-wasm/ build for midstreamer (the published npm package) currently exports QuicMultistream as a counter-tracking stub. Replace its open_stream / send / receive implementations with the agentic-flow bridge pattern wired to the native crate's compiled wasm32-unknown-unknown target (separate from the browser WebTransport target which stays as-is).midstreamer (npm) to 0.3.0 to signal the real-QUIC contract; ship the existing TemporalCompare / StrangeLoop / NanoScheduler unchanged.Estimated effort: ~1-2 days of upstream work. The pattern is already proven in agentic-flow-quic; midstream just adopts it.
ADR-108 already defines the loader pattern. The implementation today is:
// pseudocode from ADR-108
if (process.env.AGENTIC_FLOW_QUIC_NATIVE === '1') {
try {
const native = await import('agentic-flow/transport/quic');
if (native.isNative()) return native;
} catch {}
}
return webSocketFallback(); // ADR-104
Extension after Step 1 lands:
// new pseudocode
const candidates = [
['MIDSTREAMER_QUIC_NATIVE', 'midstreamer'], // ← new, preferred
['AGENTIC_FLOW_QUIC_NATIVE', 'agentic-flow/transport/quic'],
];
for (const [envFlag, modulePath] of candidates) {
if (process.env[envFlag] !== '1') continue;
try {
const mod = await import(modulePath);
if (mod.isNative?.() ?? mod.QuicMultistream) return mod;
} catch {}
}
return webSocketFallback();
Why prefer midstreamer once available: it ships the same quinn 0.11 core but with rustls-platform-verifier (OS trust store) — a real production posture for federation peers. Plus it's the package ruflo already takes aidefence from (ADR-118), so adopting another crate from the same workspace lowers the dependency-coordination surface.
File that changes in ruflo: the existing loader in agentic-flow/src/transport/quic-loader.ts consumed by @claude-flow/plugin-agent-federation. One module, two new lines, one new env flag.
midstreamer-quic + aimds-* in a single peer binaryThis is the "Rust-based in-flight agentics" the question is really asking about. Today the ruflo federation peer is a Node.js process: it receives a federation message, passes it through aidefence_* MCP tools (the in-flight gate per ADR-118's 3-gate pattern), then dispatches to the local agent. With Step 1 done, we can collapse those layers into a single Rust binary per peer:
Federation peer (single Rust binary)
┌────────────────────────────────────────────────┐
│ midstreamer-quic ▶ QuicConnection │
│ (UDP + TLS + handshake) │
│ │ │
│ ▼ │
│ aimds-detection ▶ Sanitizer │
│ (<10ms, in-process) PatternMatcher │
│ │
│ aimds-analysis ▶ BehavioralAnalyzer │
│ (<100ms, in-process) PolicyVerifier │
│ │
│ aimds-response ▶ StrategyOptimizer │
│ (<50ms, in-process) AtomicCounters │
│ │ │
│ ▼ │
│ Dispatch to local Node MCP server │
│ (NDJSON over stdio, like today) │
└────────────────────────────────────────────────┘
What this gets you that today doesn't:
aimds-detection layer is documented at <10 ms; in-process Rust composition removes the IPC hop. Federation throughput is gated by gate latency under load (ADR-097's budget breaker assumes per-message work is the dominant cost).midstreamer-quic and aimds-* are already in the same Cargo workspace upstream — they share rustls, the same validator 0.20 (after ADR-118's bump), the same unsafe_code = "deny" workspace lint.await.File(s) that change in ruflo:
v3/crates/ruflo-federation-peer/ — depends on midstreamer-quic and aimds-{core,detection,analysis,response} from the upstream workspace. Exposes one CLI entry point: ruflo-federation-peer start --listen <addr>.plugins/ruflo-federation/scripts/ gains an opt-in launcher that prefers the native peer binary when present; falls back to the existing Node implementation.midstreamer-quic's rustls-platform-verifier enforces them.What stays:
ruvnet/midstream — cross-port the agentic-flow bridge into npm-wasm/. Republish [email protected]. (External to ruflo; this ADR proposes the design and links to ADR-108 as the consumer.)agentic-flow/src/transport/quic-loader.ts to detect midstreamer first when MIDSTREAMER_QUIC_NATIVE=1. Behind the env flag — no behavior change for default callers.v3/crates/ruflo-federation-peer/ — new crate composing midstreamer-quic + aimds-* + a stdio dispatcher. Ships as an optional native binary; launcher in plugins/ruflo-federation/scripts/ prefers it.plugins/ruflo-federation/scripts/smoke.sh runs against both transports (native peer + WebSocket fallback) and asserts identical 3-gate verdicts on a fixture set.agentic-flow-quic's QUIC-STATUS but now with the better security posture (OS trust store).aidefence (ADR-118); adding midstreamer-quic lowers coordination cost vs adding it from a different upstream.agentic-flow-quic/benches, midstreamer-quic/benches) and proptest coverage. The 3-gate parity smoke is the existing test.ruvnet/midstream). Ruflo can't ship the integration until midstream republishes. We can write the loader (Step 2) and the peer crate (Step 3) behind feature flags so they're ready, but they don't activate until upstream lands.[email protected] is breaking-ish. The QuicMultistream class' actual behavior changes from "counter stub" to "real QUIC." Any caller that depended on the stub semantics (none in ruflo today; verified via grep) would break.aimds-* directly, but plugins that invoke the MCP tools still get the same answers. The migration is transparent to consumer plugins.agentic-flow-quic uses wasm32-unknown-unknown with a TS bridge. WASI would let Rust own the UDP socket directly. Pro: simpler bridge. Con: WASI socket support in Node is still flagged. Recommend wasm32-unknown-unknown + TS bridge for parity with the proven agentic-flow-quic pattern.agentic-flow-quic's feature flags (client + server defaults) already provide the split; single binary with role flag is simpler.aimds-response meta-learning state across peer restarts? The state is in-process today (ADR-118 noted this). Native peer would need an AgentDB write path to persist; separate ADR if/when needed.ruvnet/agentic-flow · ruvnet/midstreamagentic-flow-quic · midstreamer-quicagentic-flow/docs/features/quic/QUIC-STATUS.md (October 17, 2025 — 100% complete, validated)