1 Commits

Author SHA1 Message Date
xah30 5e553b79df feat(cli): v3.3 circuit rotation — background rebuild every N seconds
Adds RotatingCircuit: the multi-hop circuit is silently torn down and
rebuilt on a configurable interval (default off) so a long-running
client periodically rotates its on-wire path. Application packets never
see the swap.

- RotatingCircuit::new(hops, udp_opts, interval) seeds an initial
  CircuitConnection synchronously (errors surface), then spawns a
  background rotator that every `interval`:
    1. dial_circuit(&hops, udp_opts) -> next: CircuitConnection
    2. std::mem::replace inside Arc<RwLock<Arc<CircuitConnection>>>
    3. old Arc dropped when its last in-flight Arc clone is released
       (its Drop aborts forwarders / closes outers).
  send_packet/recv_packet grab a cheap snapshot of the current Arc
  before awaiting, so reads/writes never block under the rotator.
- [client.circuit] rotation_interval_secs: u64 (default 0 = disabled);
  serde(default) keeps old configs working. When 0, the path is exactly
  the v3.2 dial_circuit + optional CellPaddingConn wrap (back-compat).
- CellPaddingConn wraps RotatingCircuit on the OUTSIDE so every new
  circuit shares the same cell_size — on-wire size signature stays
  stable across rotations.
- Integration test multihop_rotation::rotating_circuit_swaps_inner_
  under_traffic: 6 s of 100-ms ping/echo at interval=1.5s -> 37 sent,
  37 received, 2 rotations counted via test-only AtomicU64 counter.
- Synchronous-failure test confirms initial dial errors bubble up from
  ::new without spawning the rotator task.

Workspace: 297 tests passed (+4), clippy -D warnings clean, fmt clean.
293 baseline tests unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 21:25:05 +03:00