v3/docs/adr/ADR-108-native-quic-binding.md
Federation transport today (alpha.9 + [email protected]) uses WebSocket. The loader pattern (ADR-104) lets us auto-upgrade to QUIC when a native binding is available — set AGENTIC_FLOW_QUIC_NATIVE=1 and the same code path picks up the upgrade. But the native binding doesn't ship.
What exists today in upstream ruvnet/agentic-flow:
crates/agentic-flow-quic/ — Rust crate using quinn (the canonical Rust QUIC impl). Full client + server features behind compile-time client / server features.crates/agentic-flow-quic/src/wasm.rs — WASM bindings, but explicitly a stub ("WASM build is a stub since browsers don't support UDP/QUIC directly. For production QUIC, use native Node.js builds.")crates/agentic-flow-quic/wasm-pack-build.sh — build script for the WASM stub bundle (already runs)What's missing for a real native build:
crates/agentic-flow-quic-node/ or similar that wraps the existing client/server in napi-rs for Node.js native modules@ruvector/* packages: separate @agentic-flow/quic-native-darwin-arm64, @agentic-flow/quic-native-linux-x64-gnu, etc., resolved at install via optionalDependenciesloadQuicTransport. Today the env-var probe is a placeholder; it needs to detect the platform-specific package + try to load itDefer the native QUIC binding to a future iteration, gated on:
When the native binding ships upstream:
agentic-flow dep range in @claude-flow/plugin-agent-federation/package.json to the version that includes native (likely ^2.1 after their Phase-1 closes)AGENTIC_FLOW_QUIC_NATIVE=1 env var in the federation operator runbook--component federation reports selectedBackend=quic when native is loadedgetTransportCapabilities().selectedBackend === 'quic' when env is set + binding installedIn agentic-flow/src/transport/quic-loader.ts:
async function isRealQuicAvailable(): Promise<boolean> {
if (process.env.AGENTIC_FLOW_QUIC_NATIVE !== '1') return false;
try {
// Try to load the platform-specific native module
const platform = `${process.platform}-${process.arch}`;
const nativeName = `@agentic-flow/quic-native-${platform}`;
await import(nativeName);
return true;
} catch {
return false;
}
}
// In plugin.ts initialize():
const transport = await loadQuicTransport({
serverName: nodeId,
enable0Rtt: true, // pays off only with native QUIC
maxConcurrentStreams: 100, // ditto
// ...
});
// At the doctor surface — capability probe:
const caps = await getTransportCapabilities();
context.logger.info(`Federation transport: ${caps.selectedBackend}` +
(caps.selectedBackend === 'quic' ? ' (0-RTT, multiplexed streams)' : ' (fallback)'));
Add Check 9 to the 12h routine:
# With native QUIC available:
AGENTIC_FLOW_QUIC_NATIVE=1 node -e '
import("agentic-flow/transport/loader").then(async (m) => {
const caps = await m.getTransportCapabilities();
if (caps.selectedBackend !== "quic") {
console.error("FAIL: native QUIC env set but loader fell back to WS");
process.exit(1);
}
console.log("ok: native QUIC selected");
});
'
agentic-flow. The plugin only consumes the loader.quinn ships, we use. We won't constrain to a specific draft.| Step | Status |
|---|---|
| Loader-pattern transport (forward-compat) | Implemented (alpha.9 + ADR-104) |
AGENTIC_FLOW_QUIC_NATIVE env-var probe | Implemented (placeholder returns false today) |
| Native binding (Rust→N-API) | Deferred — gated on upstream agentic-flow#15-21 |
| Per-platform binary distribution | Deferred |
| Federation-side adoption (env var + doctor surface) | Deferred |
| Verification check | Deferred |
Re-open when:
agentic-flow ships a native binding (any of #15-21 closing with merged PRs)