# 0010. Dedicated `net` crate with a confined async runtime - **Status:** Accepted - **Date:** 2026-07-12 ## Context The network transport is QUIC via `quinn` which is an asynchronous library built on the `tokio` runtime and requires TLS 1.3 through `rustls`. These are heavy dependencies that pull an entire async ecosystem into the build. The `shared` crate is mandated to stay lean and dependency-light: it is the protocol/data layer, holding pure serde message types with no async, rendering, or engine internals. Placing transport code in `shared` would violate that mandate and force every consumer of the protocol types to compile `tokio` and `rustls`. At the same time, the simulation is synchronous: the `server` runs a synchronous `bevy_ecs` loop and the `client` runs a synchronous `winit` event loop. Introducing an async runtime must not turn those loops async or leak `tokio` throughout the workspace. ## Decision Transport lives in a dedicated `net` crate, separate from `shared`, and the `tokio` runtime is confined to it. - `net` owns the `quinn`, `tokio`, and `rustls` dependencies, plus the QUIC endpoints, connection lifecycle, and wire framing. - `shared` continues to hold only the protocol message *types* (serde, no async). - Both `client` and `server` depend on `net`. - The async runtime is bridged to the synchronous simulation over channels (`crossbeam-channel`), consistent with the message-passing concurrency model in `AGENTS.md`. The synchronous loops never `.await`; they send and receive protocol messages across the boundary. ## Consequences - `shared` stays lean: consumers of the protocol types do not compile the async stack. - The async surface is quarantined. Only `net` deals with `tokio`, keeping the `server` and `client` loops synchronous and unchanged. - The workspace now has six crates. `net` sits between `shared` (types it carries) and the two binaries (which drive it). - The channel bridge is an explicit boundary that must be maintained: work crossing between the async runtime and the sync simulation flows through channels, never through shared async state or by making the sim async. - A crypto provider backend is required by `rustls`; the transport code must install one before building QUIC configuration.