Patch release: dependency updates, Rust 1.91.0 MSRV alignment, deterministic simulation CI migration, and WebSocket/WSS upgrade-path buffering fixes.
✨ Added
test(sim): moonpool-sim-driven deterministic UDP simulation. A new test-only workspace cratesim/(sozu-sim,publish = false) hosts a deterministic simulation of the sans-io UDP core (sim/tests/udp_simulation.rs) driven by themoonpool-simengine (seeded runtime, virtual clock, RNG,buggifyfault injection). It ports the former handmade harness's weighted action grammar, shadow model, cross-step invariants and determinism guard onto moonpool, keeping theSOZU_UDP_SIM_SEED/_SEEDS/_STEPSreplay/sweep knobs, and replaces it: the handmadelib/tests/udp_simulation.rsis removed — moonpool is now the sole UDP deterministic-sim.lib//bin/stay async-free: moonpool/tokio aresim/dev-dependencies only.moonpool-simis pinned to a git rev (the published 0.7.0 doesn't build on stable;maindoes) — swapping to a crates.io release later is a follow-up, not a blocker.
🔄 Changed
- MSRV bumped 1.88.0 → 1.91.0 (
rust-toolchain+ every crate'srust-version). Required bymoonpool-sim, which usesDuration::from_hours/from_mins— const-stable only since Rust 1.91. moonpool also needs--cfg tokio_unstable(it seeds tokio's runtime RNG via the unstableRngSeedAPI for scheduler determinism), but it is scoped to thesozu-simbuild only — the moonpool dev-deps live under[target.'cfg(tokio_unstable)'.dev-dependencies]and the harness is#![cfg(tokio_unstable)]-gated, socargo test --workspaceand every production build stay free of the flag (the sweep sets it viaRUSTFLAGS). No workspace-wide.cargo/config.toml. chore(lint): resolved the Rust 1.91 clippy lints (collapsible_if,manual_is_multiple_of,derivable_impls,mismatched_lifetime_syntaxes) surfaced acrosslib/command/binby the MSRV bump. No behavior change.
🐛 Fixed
fix(pipe): WebSocket/WSS server-first frames are flushed after101 Switching Protocols. The post-upgrade pipe now arms writable readiness when bytes enter either sozu-owned direction buffer, so edge-triggered epoll cannot park a backend frame such as Pusher/Reverbconnection_establisheduntil the client sends its first WebSocket frame. It also arms writable readiness for bytes already inherited from the H1 mux during upgrade, including a backend frame carried in the same read buffer as the101 Switching Protocolsresponse.
🤖 CI
ci(sim): the moonpoolsozu-simsweep is the sole UDP swarm. A per-PRudp-simulationjob runs a modest seeded sweep (with--cfg tokio_unstable), and the nightlysimulation-sweepworkflow goes deep — seed-bounded to fit the 45-min cap, since moonpool's per-seed tokio-runtime construction is ~100× slower per seed-step than the former pure-sync handmade swarm. All CI toolchain pins move 1.88.0 → 1.91.0.
🔐 Security
chore(deps): bumpanyhow1.0.102 → 1.0.103 for RUSTSEC-2026-0190 (unsoundness inError::downcast_mut()afterError::context()). Lockfile-only — the workspace^1.0.102requirement already permitted it; restores a greencargo audit --deny warnings.