From b7d13e019b00f41f515505e2a97ba98c655ed567 Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 09:26:24 -0400 Subject: [PATCH 01/59] Adapt Sush to the current Rumors API Let Rumors own per-link connection reuse and remove qorb's peer-wide pools, recycle state, and READY tasks. Keep attestation and TLS shutdown in the Sprockets adapter. Bound initial inbound routing with the new deadline hook; established gossip remains free to wait between rounds. Add the required Eq bounds and handle send admission results. A locally authored Sush message that fails admission is a codec bug; assert success before advancing execution boundaries. Existing wire fixtures now exercise Rumors admission as well as byte compatibility. This ongoing compatibility branch is based on locker at fa405c2. The current Rumors routing changes are under review and have no commit to pin yet. Validation uses an ignored Cargo config patch to that worktree. The checked-in Git pin and lockfile remain portable and must advance when Rumors lands; the new routing hook requires the local override until then. Validation: workspace check and clippy with warnings denied; build, doctests, unchanged OpenAPI output, and optional permslip client checks passed. The full nextest run passed transport conformance, gossip, and message fixtures, but failed PTY and process/job tests on macOS. Linux PTY-path assertions and rejected address-space limits need validation on Linux before final review. --- Cargo.lock | 129 ----------------- Cargo.toml | 7 +- server/Cargo.toml | 1 - server/src/link.rs | 322 ++++++++--------------------------------- server/src/messages.rs | 5 + server/tests/link.rs | 6 +- 6 files changed, 75 insertions(+), 395 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7832a8a1..a226fdc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1135,17 +1135,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "derive-where" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "dice-mfg-msgs" version = "0.3.0" @@ -1418,18 +1407,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "869b0adbda23651a9c5c0c3d270aac9fcb52e8622a8f2b17e57802d7791962f2" -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "env_filter" version = "2.0.0" @@ -1848,51 +1825,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hickory-proto" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna", - "ipnet", - "once_cell", - "rand 0.8.7", - "thiserror 1.0.69", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "hickory-resolver" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" -dependencies = [ - "cfg-if", - "futures-util", - "hickory-proto", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand 0.8.7", - "resolv-conf", - "smallvec 1.15.2", - "thiserror 1.0.69", - "tokio", - "tracing", -] - [[package]] name = "hkdf" version = "0.12.4" @@ -2292,19 +2224,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "ipconfig" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" -dependencies = [ - "socket2", - "widestring", - "windows-registry", - "windows-result", - "windows-sys 0.61.2", -] - [[package]] name = "ipnet" version = "2.12.1" @@ -2572,12 +2491,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -2656,15 +2569,6 @@ dependencies = [ "hashbrown 0.17.1", ] -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "lru-slab" version = "0.1.2" @@ -3469,25 +3373,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "qorb" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f223a85f489c9548719fb367c6733e8bc9d4eef76d6ea59a18299acd9497c60" -dependencies = [ - "anyhow", - "async-trait", - "debug-ignore", - "derive-where", - "futures", - "hickory-resolver", - "rand 0.9.5", - "thiserror 2.0.20", - "tokio", - "tokio-stream", - "tracing", -] - [[package]] name = "quinn" version = "0.11.11" @@ -3794,12 +3679,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "resolv-conf" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" - [[package]] name = "rfc6979" version = "0.4.0" @@ -4975,7 +4854,6 @@ dependencies = [ "p256", "percent-encoding", "pwd", - "qorb", "rand_core 0.6.4", "rumors", "rustix 1.1.4", @@ -5378,7 +5256,6 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util", ] [[package]] @@ -5882,12 +5759,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "widestring" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 597351bc..fb03fda0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,6 @@ permission-slip-common = { git = "https://github.com/oxidecomputer/permission-sl progenitor = "0.14" progenitor-client = "0.14" pwd = "1" -qorb = { version = "0.4", default-features = false } rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } @@ -77,10 +76,8 @@ tokio-util = "0.7" x509-cert = { version = "0.2", features = ["pem", "std"] } xdg = "3" -# Every sprockets handshake verifies P-384 attestation cert chains, and every -# gossip link stream is a handshake. The RustCrypto stack is ~100ms per verify -# unoptimized, which makes the sprockets-backed tests crawl, so optimize the -# crates that do the math; everything else keeps fast debug builds. +# Optimize the arithmetic used to verify attestation certificates on fresh +# connections. Keep the rest of the debug build unoptimized. [profile.dev.package.crypto-bigint] opt-level = 3 [profile.dev.package.ecdsa] diff --git a/server/Cargo.toml b/server/Cargo.toml index a4b1bd44..cb8bee95 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -38,7 +38,6 @@ memmap2.workspace = true p256.workspace = true percent-encoding.workspace = true pwd.workspace = true -qorb.workspace = true rumors.workspace = true rand_core.workspace = true rustix.workspace = true diff --git a/server/src/link.rs b/server/src/link.rs index 5549f81d..649b7272 100644 --- a/server/src/link.rs +++ b/server/src/link.rs @@ -2,45 +2,27 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -//! Sprockets as a rumors [`routed`] transport. +//! Sprockets transport for routed Rumors links. //! -//! [`rumors::link::routed`] adapts an accept/connect transport to the link -//! contract, mapping every link stream to its own connection so that flow -//! control and half-close are the transport's own. This module supplies the -//! sprockets end of that adapter: a dialer, a listener, and the connection -//! type they exchange. Peers are authenticated and attested by sprockets, -//! which is what rumors asks of a transport it trusts. -//! -//! A fresh connection costs a full attested handshake, most of a second -//! with the RoT in the loop, and the RoT serializes them. The dialer -//! therefore pools. rumors hands a completed stream's connection back -//! through [`Dial::recycle`], a qorb pool per peer holds it, and the next -//! stream to that peer draws it instead of dialing. Only clean returns -//! are reused: a connection dropped mid-stream is discarded at the -//! pool's door. - -use std::collections::{BTreeMap, BTreeSet, HashMap}; +//! Sprockets authenticates and attests each connection. Rumors owns reuse +//! within each link, avoiding repeated attestation for completed streams. +//! This adapter owns handshake timeouts and clean TLS shutdown. + +use std::collections::{BTreeMap, BTreeSet}; use std::io; use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6}; use std::pin::Pin; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; -use async_trait::async_trait; use camino::Utf8PathBuf; -use qorb::backend::{self, Backend}; -use qorb::claim; -use qorb::policy::{Policy, SetConfig}; -use qorb::pool::Pool; -use qorb::resolvers::fixed::FixedResolver; -use rumors::link::STREAM_COUNT; use rumors::link::routed::{Config, Dial, Endpoint, Incoming, Listen, RoutedLink}; use sled_hardware_types::BaseboardId; use slog::{Logger, debug, o, warn}; use sprockets_tls::keys::SprocketsConfig; use sprockets_tls::{Client, Server}; -use tokio::io::{AsyncRead, AsyncReadExt as _, AsyncWrite, AsyncWriteExt as _, ReadBuf}; +use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt as _, ReadBuf}; use tokio::net::TcpStream; use tokio::runtime::Handle; use tokio::sync::{mpsc, watch}; @@ -57,92 +39,21 @@ pub type CorpusSource = Arc Vec + Send + Sync>; /// Completed handshakes the listener holds while the router catches up. const HANDSHAKE_QUEUE_DEPTH: usize = 64; -/// Inbound connections the router holds mid-header. Recycled idle -/// connections park here between streams, so the bound admits a whole -/// rack's stream complements at once. -const PENDING_HEADERS: usize = STREAM_COUNT * 32; - /// How long to wait after a failed accept before accepting again. const ACCEPT_RETRY: Duration = Duration::from_millis(100); -/// Connections per peer. The link contract's worst case is one control -/// stream plus a full complement for each of a pair's two links, and -/// claims parked on the ready byte can briefly overlap the next -/// session's opens. Slots are created on demand, so the cap is free -/// when idle. -const MAX_SLOTS: usize = 3 * STREAM_COUNT + 1; - -/// Idle connections kept warm per peer. Taking one stream leaves a -/// spare, so qorb fires no refill and reuses the recycled connection -/// rather than culling it. -const SPARES_WANTED: usize = 2; - -/// Floor between one slot's reconnect attempts after a failure, -/// matched to the RoT's roughly one-per-second handshake rate. -const MIN_CONNECTION_BACKOFF: Duration = Duration::from_secs(1); - -/// How often qorb re-checks an idle connection. The check is a no-op -/// (no ping), so the tick only bounces slot state. It must be finite: -/// qorb arms the timer by adding this to the current instant, and -/// `Duration::MAX`, which its docs suggest for disabling checks, -/// overflows the addition and kills the slot task. -const HEALTH_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60); - -/// One attested connection as a peer's pool holds it. -struct PooledConn { - stream: Option>, - /// qorb returns a dropped claim to the pool no matter how its - /// stream ended, so this bit tells a clean return from an abort: - /// set as the connection is claimed, cleared only by - /// [`Dial::recycle`], and checked at the pool's door, where a dirty - /// connection is discarded. - dirty: bool, -} - -impl Drop for PooledConn { - fn drop(&mut self) { - let Some(mut stream) = self.stream.take() else { - return; - }; - // A dropped TLS connection sends a bare FIN, which rustls - // reports to the peer as an unexpected end of file, so the drop - // hands the connection to a task that shuts it down properly. - // Outside a runtime there is no one left to read the closing - // alert either, so let the connection drop abruptly. - if let Ok(handle) = Handle::try_current() { - handle.spawn(async move { - let _ = stream.shutdown().await; - }); - } - } -} - -/// One sprockets connection, carrying one link stream at a time. -/// -/// A dialed connection is a claim on its peer's pool: dropping it -/// returns it, and the pool reuses it only if [`Dial::recycle`] marked -/// it clean first. An accepted connection belongs to the router, and -/// dropping it closes it. -pub struct SprocketsConn(Conn); - -enum Conn { - /// Accepted by the listener. - Direct(Option>), - /// Claimed from a peer's pool. - Pooled(claim::Handle), -} +/// An attested connection, closed with a TLS shutdown on drop. +pub struct SprocketsConn(Option>); impl SprocketsConn { + /// Borrow the live TLS stream for I/O. fn stream(&mut self) -> Pin<&mut sprockets_tls::Stream> { - let stream = match &mut self.0 { - Conn::Direct(stream) => stream.as_mut(), - Conn::Pooled(handle) => handle.stream.as_mut(), - }; - Pin::new(stream.expect("stream present until drop")) + Pin::new(self.0.as_mut().expect("stream present until drop")) } } impl AsyncRead for SprocketsConn { + /// Read decrypted bytes from the TLS stream. fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -153,6 +64,7 @@ impl AsyncRead for SprocketsConn { } impl AsyncWrite for SprocketsConn { + /// Write bytes through the TLS stream. fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -161,26 +73,24 @@ impl AsyncWrite for SprocketsConn { self.stream().poll_write(cx, buf) } + /// Flush pending TLS output. fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.stream().poll_flush(cx) } + /// Send TLS shutdown after pending output. fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.stream().poll_shutdown(cx) } } impl Drop for SprocketsConn { + /// Finish TLS shutdown in a task when a runtime is available. fn drop(&mut self) { - // A pooled connection's drop is its return to the pool, which - // discards it (shutting it down) unless it was recycled clean. - let Conn::Direct(stream) = &mut self.0 else { + let Some(mut stream) = self.0.take() else { return; }; - let Some(mut stream) = stream.take() else { - return; - }; - // As for `PooledConn`: shut down properly inside a runtime. + // Send close_notify so the peer sees EOF after accepted bytes. if let Ok(handle) = Handle::try_current() { handle.spawn(async move { let _ = stream.shutdown().await; @@ -293,74 +203,7 @@ fn baseboard(log: &Logger, platform_id: &str) -> Option { } } -/// Establishes a peer pool's connections, one attested handshake each. -struct PoolConnector { - log: Logger, - config: SprocketsConfig, - corpus: CorpusSource, - baseboards: Baseboards, - timeout: Duration, -} - -#[async_trait] -impl backend::Connector for PoolConnector { - type Connection = PooledConn; - - async fn connect(&self, backend: &Backend) -> Result { - // The resolver only ever names IPv6 backends; see `Dial::dial`. - let SocketAddr::V6(addr) = backend.address else { - return Err(backend::Error::from(io::Error::other(format!( - "sush gossip needs IPv6: {}", - backend.address - )))); - }; - let config = self.config.clone(); - let corpus = self.corpus.clone(); - let log = self.log.clone(); - // Sprockets connects are not cancel safe, and qorb may cancel - // this future, so the handshake runs in its own task and this - // future only awaits the result. The deadline lives inside the - // task, so an abandoned handshake still terminates at it - // instead of holding the RoT unbounded. The corpus source is - // consulted inside the task: if it panics, this connect fails, - // not the pool. - let deadline = self.timeout; - let dial = spawn(async move { - match timeout(deadline, Client::connect(config, addr, (corpus)(), log)).await { - Ok(connected) => connected.map_err(io::Error::other), - Err(_) => Err(io::Error::new( - io::ErrorKind::TimedOut, - format!("dialing {addr} timed out"), - )), - } - }); - let stream = dial.await.map_err(io::Error::other)??; - self.baseboards - .dialed(&self.log, addr, stream.peer_platform_id().as_str()); - Ok(PooledConn { - stream: Some(stream), - dirty: false, - }) - } - - async fn on_acquire(&self, conn: &mut PooledConn) -> Result<(), backend::Error> { - // Pessimistic: only a clean recycle clears it. - conn.dirty = true; - Ok(()) - } - - async fn on_recycle(&self, conn: &mut PooledConn) -> Result<(), backend::Error> { - if conn.dirty { - return Err(backend::Error::from(io::Error::new( - io::ErrorKind::ConnectionAborted, - "connection was dropped mid-stream", - ))); - } - Ok(()) - } -} - -/// Dials attested sprockets connections, pooled per peer. +/// Opens fresh attested connections with a handshake timeout. #[derive(Clone)] pub struct SprocketsDial { log: Logger, @@ -368,13 +211,10 @@ pub struct SprocketsDial { corpus: CorpusSource, baseboards: Baseboards, timeout: Duration, - pools: Arc>>>>, } impl SprocketsDial { - /// Dial with `config`, appraising peers against the corpus of the - /// moment, and giving up on any one handshake or claim after - /// `timeout`. + /// Appraise each peer against the current corpus and bound its handshake. pub fn new( log: &Logger, config: SprocketsConfig, @@ -382,90 +222,46 @@ impl SprocketsDial { baseboards: Baseboards, timeout: Duration, ) -> Self { - SprocketsDial { + Self { log: log.new(o!("component" => "sprockets dial")), config, corpus, baseboards, timeout, - pools: Arc::default(), - } - } - - /// The pool dialing `addr`, created on first use. - fn pool(&self, addr: SocketAddrV6) -> Arc> { - self.pools - .lock() - .expect("pool table lock") - .entry(addr) - .or_insert_with(|| Arc::new(self.build(addr))) - .clone() - } - - fn build(&self, addr: SocketAddrV6) -> Pool { - let connector = Arc::new(PoolConnector { - log: self.log.clone(), - config: self.config.clone(), - corpus: self.corpus.clone(), - baseboards: self.baseboards.clone(), - timeout: self.timeout, - }); - let resolver = Box::new(FixedResolver::new([SocketAddr::V6(addr)])); - let policy = Policy { - spares_wanted: SPARES_WANTED, - max_slots: MAX_SLOTS, - claim_timeout: self.timeout, - set_config: SetConfig { - max_count: MAX_SLOTS, - min_connection_backoff: MIN_CONNECTION_BACKOFF, - health_interval: HEALTH_INTERVAL, - ..SetConfig::default() - }, - ..Policy::default() - }; - match Pool::new(format!("gossip {addr}"), resolver, connector, policy) { - Ok(pool) => pool, - Err(err) => err.into_inner(), } } - - /// Drop the pools of peers outside `peers`, closing their idle - /// connections. - fn retain(&self, peers: &BTreeSet) { - self.pools - .lock() - .expect("pool table lock") - .retain(|addr, _| peers.contains(addr)); - } } impl Dial for SprocketsDial { + /// The peer's advertised listen address. type Addr = SocketAddr; + /// An attested TLS connection. type Conn = SprocketsConn; + /// Open and attest a fresh connection within the handshake timeout. async fn dial(&self, addr: &SocketAddr) -> io::Result { let SocketAddr::V6(addr) = *addr else { return Err(io::Error::other(format!("sush gossip needs IPv6: {addr}"))); }; - let handle = - self.pool(addr).claim().await.map_err(|err| { - io::Error::other(format!("claiming a connection to {addr}: {err}")) - })?; - Ok(SprocketsConn(Conn::Pooled(handle))) - } - - fn recycle(&self, _peer: &SocketAddr, mut conn: SprocketsConn) { - // Reusable only once the peer's router says it is ready: read - // that byte off the session's task, then let the drop return - // the claim clean. - spawn(async move { - let mut ready = [0u8; 1]; - if conn.read_exact(&mut ready).await.is_ok() - && let Conn::Pooled(handle) = &mut conn.0 - { - handle.dirty = false; + let config = self.config.clone(); + let corpus = self.corpus.clone(); + let log = self.log.clone(); + let deadline = self.timeout; + // Sprockets handshakes are not cancellation-safe. Keep the timeout + // inside the spawned task so an abandoned dial still terminates. + let dial = spawn(async move { + match timeout(deadline, Client::connect(config, addr, (corpus)(), log)).await { + Ok(connected) => connected.map_err(io::Error::other), + Err(_) => Err(io::Error::new( + io::ErrorKind::TimedOut, + format!("dialing {addr} timed out"), + )), } }); + let stream = dial.await.map_err(io::Error::other)??; + self.baseboards + .dialed(&self.log, addr, stream.peer_platform_id().as_str()); + Ok(SprocketsConn(Some(stream))) } } @@ -474,7 +270,6 @@ impl Dial for SprocketsDial { pub struct Transport { endpoint: Endpoint, incoming: Incoming, - dial: SprocketsDial, baseboards: Baseboards, bound: SocketAddrV6, } @@ -482,6 +277,8 @@ pub struct Transport { impl Transport { /// Listen on `listen_addr` and stand up the routing endpoint. Its /// router runs until `shutdown`, or until the listener fails. + /// `dial_timeout` bounds outgoing handshakes and incoming initial routing; + /// it does not limit idle gossip links. pub async fn new( log: &Logger, config: SprocketsConfig, @@ -497,16 +294,13 @@ impl Transport { corpus.clone(), baseboards.clone(), listen_addr, + dial_timeout, shutdown.clone(), ) .await?; let dial = SprocketsDial::new(log, config, corpus, baseboards.clone(), dial_timeout); - let router_config = Config { - pending_headers: PENDING_HEADERS, - ..Config::default() - }; let (endpoint, incoming, router) = - Endpoint::new(listen, SocketAddr::V6(bound), dial.clone(), router_config) + Endpoint::new(listen, SocketAddr::V6(bound), dial, Config::default()) .map_err(io::Error::other)?; let log = log.new(o!("component" => "link router")); spawn(async move { @@ -520,7 +314,6 @@ impl Transport { Ok(Transport { endpoint, incoming, - dial, baseboards, bound, }) @@ -541,10 +334,8 @@ impl Transport { &self.baseboards } - /// Drop the connection pools and recorded baseboards of peers - /// outside `peers`. + /// Forget recorded baseboards of peers outside `peers`. pub fn retain_peers(&self, peers: &BTreeSet) { - self.dial.retain(peers); self.baseboards.retain(peers); } @@ -565,6 +356,7 @@ impl Transport { /// router may cancel freely. pub struct SprocketsListen { connections: mpsc::Receiver, + routing_timeout: Duration, } impl SprocketsListen { @@ -576,6 +368,7 @@ impl SprocketsListen { corpus: CorpusSource, baseboards: Baseboards, listen_addr: SocketAddrV6, + routing_timeout: Duration, shutdown: CancellationToken, ) -> io::Result<(Self, SocketAddrV6)> { let log = log.new(o!("component" => "sprockets listen")); @@ -590,13 +383,26 @@ impl SprocketsListen { }; let (tx, connections) = mpsc::channel(HANDSHAKE_QUEUE_DEPTH); spawn(pump(server, corpus, baseboards, tx, log, shutdown)); - Ok((SprocketsListen { connections }, bound)) + Ok(( + SprocketsListen { + connections, + routing_timeout, + }, + bound, + )) } } impl Listen for SprocketsListen { + /// An attested TLS connection. type Conn = SprocketsConn; + /// Bound initial routing without timing out established gossip. + fn routing_deadline(&self) -> impl Future + Send + 'static { + sleep(self.routing_timeout) + } + + /// Receive the next completed handshake or report listener shutdown. async fn accept(&mut self) -> io::Result { self.connections .recv() @@ -630,7 +436,7 @@ async fn pump( let id = stream.peer_platform_id().as_str(); baseboards.accepted(&log, *peer.ip(), id); } - let conn = SprocketsConn(Conn::Direct(Some(stream))); + let conn = SprocketsConn(Some(stream)); let _ = connections.send(conn).await; } Err(err) => { diff --git a/server/src/messages.rs b/server/src/messages.rs index b6885ee1..7bed6a55 100644 --- a/server/src/messages.rs +++ b/server/src/messages.rs @@ -317,6 +317,11 @@ mod wire_format { } let decoded: VersionedMessage = from_cbor(bytes.as_slice()).unwrap(); assert_eq!(decoded, message, "wire format should round-trip"); + // Exercise Rumors' admission checks as well as the wire snapshot. + rumors::Peer::::seed() + .into_rumors() + .send(message) + .expect("locally authored Sush messages must encode faithfully"); } fn sid(name: &str) -> SessionId { diff --git a/server/tests/link.rs b/server/tests/link.rs index 9cbdfb54..b7ec745e 100644 --- a/server/tests/link.rs +++ b/server/tests/link.rs @@ -82,11 +82,12 @@ impl Drop for TestNet { } } -// Slow (~35s each): CI always runs these, and local runs should -// whenever `link.rs` or the gossip configuration changes: +// These tests perform real attestation. CI runs them; run them locally +// when changing the transport or gossip configuration: // // cargo test --package sush-server --test link -- --include-ignored +/// The attested transport satisfies the Rumors link contract. #[tokio::test] #[ignore] async fn conformance() { @@ -99,6 +100,7 @@ async fn conformance() { .expect("conformance suite timed out"); } +/// Peers bootstrap and exchange messages over an attested link. #[tokio::test] #[ignore] async fn gossip_convergence() { From 9829804227b7e04f398706f2121588a9912e8ab9 Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 09:53:47 -0400 Subject: [PATCH 02/59] Clarify the shared gossip connection timeout --- server/src/gossip.rs | 4 ++-- server/src/link.rs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/gossip.rs b/server/src/gossip.rs index f947763a..2a4cfdfc 100644 --- a/server/src/gossip.rs +++ b/server/src/gossip.rs @@ -68,8 +68,8 @@ impl LinkedBaseboards { pub struct GossipConfig { /// How often absent links are re-established. pub reconnect: Duration, - /// Ceiling on establishing one link, and on each data-stream dial - /// inside a live one. + /// Timeout for establishing a link, dialing a data stream, or initially + /// routing an incoming connection. Established gossip may wait indefinitely. pub connect_timeout: Duration, /// Ceiling on one bootstrap join. pub join_timeout: Duration, diff --git a/server/src/link.rs b/server/src/link.rs index 649b7272..ee183306 100644 --- a/server/src/link.rs +++ b/server/src/link.rs @@ -355,7 +355,9 @@ impl Transport { /// concurrently, and [`Listen::accept`] is a queue receive, which the /// router may cancel freely. pub struct SprocketsListen { + /// Attested connections waiting for the router. connections: mpsc::Receiver, + /// Initial routing uses the same timeout as outgoing dials. routing_timeout: Duration, } From 6bfaf2911150bb1adef60393383343594179b164 Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 10:40:39 -0400 Subject: [PATCH 03/59] Use Rumors conformance deadlines Supply a fresh timer for each check, including connected-pair setup. Validated with sush-server link Clippy and both attested transport tests against the routed-pooling Rumors worktree. --- server/tests/link.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/server/tests/link.rs b/server/tests/link.rs index b7ec745e..8cfda0d7 100644 --- a/server/tests/link.rs +++ b/server/tests/link.rs @@ -2,8 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -//! The sprockets transport against the rumors link contract, and a -//! two-peer gossip smoke test over it. +//! Attested transport conformance and two-peer gossip. mod common; @@ -14,7 +13,7 @@ use futures::StreamExt as _; use futures::stream; use slog::Logger; use tempfile::TempDir; -use tokio::time::timeout; +use tokio::time::{sleep, timeout}; use tokio::{join, spawn}; use tokio_util::sync::CancellationToken; @@ -24,13 +23,19 @@ use sush_server::link::{SprocketsLink, Transport}; use common::{corpus, dial_timeout, localhost, pki, sprockets_config, test_logger}; +/// Two attested endpoints and the PKI used by their handshakes. struct TestNet { + /// Endpoint for identity 1. a: Transport, + /// Endpoint for identity 2. b: Transport, + /// Cancels both endpoints' transport tasks. shutdown: CancellationToken, + /// Keeps certificate and attestation files alive during the test. _dir: TempDir, } +/// Bind one identity's authenticated transport for the test network. async fn transport( log: &Logger, dir: &Utf8PathBuf, @@ -50,6 +55,7 @@ async fn transport( } impl TestNet { + /// Create test PKI and start both attested endpoints. async fn new(test_name: &'static str) -> TestNet { let (tmp, dir) = pki("sush-link-", 2); let log = test_logger(test_name); @@ -77,6 +83,7 @@ impl TestNet { } impl Drop for TestNet { + /// Cancel the network's transport tasks. fn drop(&mut self) { self.shutdown.cancel(); } @@ -92,12 +99,11 @@ impl Drop for TestNet { #[ignore] async fn conformance() { let mut net = TestNet::new("conformance").await; - timeout( - Duration::from_secs(600), - check(async || net.link_pair().await), + check( + async || net.link_pair().await, + || sleep(Duration::from_secs(600)), ) - .await - .expect("conformance suite timed out"); + .await; } /// Peers bootstrap and exchange messages over an attested link. From 6279e93e55e7e58bd9fc636bcace577c9052ecfa Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 15:00:09 -0400 Subject: [PATCH 04/59] Adapt gossip handling to Rumors error categories Handle network mismatches through Mismatch and log protocol diagnostics. Use the local network carried by the error instead of caching it separately. Validated on ox-east-1-agent with native workspace check and clippy. The workspace suite passes except for link conformance, which also hits its dial timeout in isolation; that follow-up remains recorded in the Rumors triage notes. Final non-exhaustive match patterns pass native workspace check. --- server/src/gossip.rs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/server/src/gossip.rs b/server/src/gossip.rs index 2a4cfdfc..80a517ae 100644 --- a/server/src/gossip.rs +++ b/server/src/gossip.rs @@ -6,7 +6,7 @@ //! //! Every peer seeds its own universe at startup, so peers meet across //! unrelated universes and their sessions fail with -//! [`Error::NetworkMismatch`]. That error carries everything both sides need +//! [`Mismatch::Network`]. That error carries everything both sides need //! to agree on which universe survives, without coordination: the greater //! minimum event count wins, and the lesser network id breaks ties. rumors //! documents the rule under `Peer`, "Bootstrapping without consensus". The @@ -29,6 +29,7 @@ use std::net::{SocketAddr, SocketAddrV6}; use std::time::Duration; use futures::StreamExt as _; +use rumors::error::Mismatch; use rumors::{Error, Joined, Network, Peer, Rumors, Ticks}; use serde::Serialize; use serde::de::DeserializeOwned; @@ -519,21 +520,26 @@ async fn sessions( where T: DeserializeOwned + Serialize + Eq + Send + Sync + 'static, { - let ours = rumors.network(); let mut driver = rumors.gossip_when(rumors.changes(), &mut link); while let Some(session) = driver.next().await { match session { Ok(_) => {} - Err(Error::NetworkMismatch { + Err(Error::Mismatch(Mismatch::Network { + local_network, + local_min_events, remote_network, remote_min_events, - local_min_events, - }) => { - let dominated = - remote_dominates(&local_min_events, &remote_min_events, ours, remote_network); + .. + })) => { + let dominated = remote_dominates( + &local_min_events, + &remote_min_events, + local_network, + remote_network, + ); debug!( log, "universe mismatch"; - "ours" => %ours, "theirs" => %remote_network, + "ours" => %local_network, "theirs" => %remote_network, "our_events" => %local_min_events, "their_events" => %remote_min_events, "we_lose" => dominated, @@ -544,13 +550,15 @@ where Stopped::Failed }; } - // A bookmark failure also stops every later session at the - // persist gate, so it deserves a warning where routine - // link churn does not. + // Storage failures need attention; reconnecting alone cannot repair them. Err(Error::Bookmark(error)) => { warn!(log, "bookmark failure stops gossip"; "error" => %error); return Stopped::Failed; } + Err(Error::Protocol(error)) => { + warn!(log, "gossip protocol violation"; "diagnostic" => ?error); + return Stopped::Failed; + } Err(err) => { debug!(log, "session failed"; "error" => %err); return Stopped::Failed; From ea422ad592565fe8e759b7be59dda04941128d8e Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 15:49:15 -0400 Subject: [PATCH 05/59] Run attested link conformance on a multithreaded runtime Simultaneous connection opens in the expanded Rumors conformance suite serialize synchronous attestation work on the default single-thread test runtime. The isolated concurrency check reproduces the ten-second dial timeout on Helios. Use four runtime workers, matching the server runtime model while bounding test resources. Keep the full conformance suite and production dial deadline unchanged. Validation: formatting; Helios workspace check and Clippy with warnings denied; all 163 native workspace tests passed, including attested link conformance. --- server/tests/link.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/tests/link.rs b/server/tests/link.rs index 8cfda0d7..b274cfd3 100644 --- a/server/tests/link.rs +++ b/server/tests/link.rs @@ -95,7 +95,11 @@ impl Drop for TestNet { // cargo test --package sush-server --test link -- --include-ignored /// The attested transport satisfies the Rumors link contract. -#[tokio::test] +/// +/// Concurrent handshakes perform synchronous cryptographic work. Use a small +/// multithreaded runtime, as the server does, so they can progress in parallel +/// within the production dial deadline. +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] #[ignore] async fn conformance() { let mut net = TestNet::new("conformance").await; From 76b180f84ba02a91ea9ffff1474d04c417e61865 Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 18:13:58 -0400 Subject: [PATCH 06/59] Adapt the link test to typed bootstrap outcomes --- server/tests/link.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/tests/link.rs b/server/tests/link.rs index b274cfd3..04799999 100644 --- a/server/tests/link.rs +++ b/server/tests/link.rs @@ -133,15 +133,15 @@ async fn gossip_convergence() { }); // Bob joins Alice's universe through the link and hears her message. - let bob = timeout( + let rumors::Joined::Joined { peer: bob } = timeout( Duration::from_secs(60), Peer::::bootstrap().join(&mut link_b), ) .await - .expect("bootstrap timed out") - .expect("bootstrap failed") - .expect("mutual bootstrap bail") - .into_rumors(); + .expect("bootstrap timed out") else { + panic!("Alice must serve Bob's bootstrap"); + }; + let bob = bob.into_rumors(); assert_eq!(bob.network(), alice.network()); assert_eq!(bob.snapshot().len(), 1); From c673d9766519b260189ea5a100efcf7080bb7f10 Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 18:40:37 -0400 Subject: [PATCH 07/59] Track the approved Rumors triage revision --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a226fdc7..d3cf0497 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=90e4509dc66330affb38afa74813a8dd500edd93#90e4509dc66330affb38afa74813a8dd500edd93" +source = "git+https://github.com/oxidecomputer/rumors?rev=a1ca7cf12fb43027ee035fb92ff239bbde335711#a1ca7cf12fb43027ee035fb92ff239bbde335711" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=90e4509dc66330affb38afa74813a8dd500edd93#90e4509dc66330affb38afa74813a8dd500edd93" +source = "git+https://github.com/oxidecomputer/rumors?rev=a1ca7cf12fb43027ee035fb92ff239bbde335711#a1ca7cf12fb43027ee035fb92ff239bbde335711" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=90e4509dc66330affb38afa74813a8dd500edd93#90e4509dc66330affb38afa74813a8dd500edd93" +source = "git+https://github.com/oxidecomputer/rumors?rev=a1ca7cf12fb43027ee035fb92ff239bbde335711#a1ca7cf12fb43027ee035fb92ff239bbde335711" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index fb03fda0..8deaf52c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "90e4509dc66330affb38afa74813a8dd500edd93" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "a1ca7cf12fb43027ee035fb92ff239bbde335711" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 8577259fbd71ba234448bc36acc881f2b944b1ae Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 18:56:09 -0400 Subject: [PATCH 08/59] Track the approved Rumors tree cleanup --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3cf0497..a7159fde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=a1ca7cf12fb43027ee035fb92ff239bbde335711#a1ca7cf12fb43027ee035fb92ff239bbde335711" +source = "git+https://github.com/oxidecomputer/rumors?rev=2f124d2bef6025252c653b1d58c1184554399523#2f124d2bef6025252c653b1d58c1184554399523" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=a1ca7cf12fb43027ee035fb92ff239bbde335711#a1ca7cf12fb43027ee035fb92ff239bbde335711" +source = "git+https://github.com/oxidecomputer/rumors?rev=2f124d2bef6025252c653b1d58c1184554399523#2f124d2bef6025252c653b1d58c1184554399523" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=a1ca7cf12fb43027ee035fb92ff239bbde335711#a1ca7cf12fb43027ee035fb92ff239bbde335711" +source = "git+https://github.com/oxidecomputer/rumors?rev=2f124d2bef6025252c653b1d58c1184554399523#2f124d2bef6025252c653b1d58c1184554399523" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 8deaf52c..af87b39d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "a1ca7cf12fb43027ee035fb92ff239bbde335711" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "2f124d2bef6025252c653b1d58c1184554399523" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 332d3d1d7df4e78553f1afe95e07b3a2b5229220 Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 19:17:48 -0400 Subject: [PATCH 09/59] Track Rumors with metadata prepared before publication Rebase compatibility work onto current Sush main, preserving its send-error logging. Pin Rumors bdb79ecb and validate the published Git dependency without a local override. Validation: native Helios server/test Clippy and both attested link tests pass; cargo fmt --all --check passes. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7159fde..1f839e39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=2f124d2bef6025252c653b1d58c1184554399523#2f124d2bef6025252c653b1d58c1184554399523" +source = "git+https://github.com/oxidecomputer/rumors?rev=bdb79ecbfdad490d234133bd4a4d14d4561d921b#bdb79ecbfdad490d234133bd4a4d14d4561d921b" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=2f124d2bef6025252c653b1d58c1184554399523#2f124d2bef6025252c653b1d58c1184554399523" +source = "git+https://github.com/oxidecomputer/rumors?rev=bdb79ecbfdad490d234133bd4a4d14d4561d921b#bdb79ecbfdad490d234133bd4a4d14d4561d921b" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=2f124d2bef6025252c653b1d58c1184554399523#2f124d2bef6025252c653b1d58c1184554399523" +source = "git+https://github.com/oxidecomputer/rumors?rev=bdb79ecbfdad490d234133bd4a4d14d4561d921b#bdb79ecbfdad490d234133bd4a4d14d4561d921b" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index af87b39d..366c53a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "2f124d2bef6025252c653b1d58c1184554399523" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "bdb79ecbfdad490d234133bd4a4d14d4561d921b" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From d0ecc1c6b3c142a17588f2b327a1ca0b481b7785 Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 10 Sep 2026 19:54:11 -0400 Subject: [PATCH 10/59] Track Rumors with compact cached message encodings --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f839e39..9e92919b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=bdb79ecbfdad490d234133bd4a4d14d4561d921b#bdb79ecbfdad490d234133bd4a4d14d4561d921b" +source = "git+https://github.com/oxidecomputer/rumors?rev=2139d59407b6a4f621a46b8ce9e7ada9af516d11#2139d59407b6a4f621a46b8ce9e7ada9af516d11" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=bdb79ecbfdad490d234133bd4a4d14d4561d921b#bdb79ecbfdad490d234133bd4a4d14d4561d921b" +source = "git+https://github.com/oxidecomputer/rumors?rev=2139d59407b6a4f621a46b8ce9e7ada9af516d11#2139d59407b6a4f621a46b8ce9e7ada9af516d11" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=bdb79ecbfdad490d234133bd4a4d14d4561d921b#bdb79ecbfdad490d234133bd4a4d14d4561d921b" +source = "git+https://github.com/oxidecomputer/rumors?rev=2139d59407b6a4f621a46b8ce9e7ada9af516d11#2139d59407b6a4f621a46b8ce9e7ada9af516d11" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 366c53a9..13f98b81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "bdb79ecbfdad490d234133bd4a4d14d4561d921b" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "2139d59407b6a4f621a46b8ce9e7ada9af516d11" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From f152701d021551105bf81ad9b815c2e0e2987069 Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 11:02:51 -0400 Subject: [PATCH 11/59] Track Rumors leaf-hash allocation cleanup Advance the manifest and lockfile to published Rumors main bcfb6ec7. Validated with server/test Clippy and the attested link conformance and two-peer gossip tests against the Git dependency. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e92919b..3d07e471 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=2139d59407b6a4f621a46b8ce9e7ada9af516d11#2139d59407b6a4f621a46b8ce9e7ada9af516d11" +source = "git+https://github.com/oxidecomputer/rumors?rev=bcfb6ec7d29ddcbaaee535fbcef6d326582c6334#bcfb6ec7d29ddcbaaee535fbcef6d326582c6334" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=2139d59407b6a4f621a46b8ce9e7ada9af516d11#2139d59407b6a4f621a46b8ce9e7ada9af516d11" +source = "git+https://github.com/oxidecomputer/rumors?rev=bcfb6ec7d29ddcbaaee535fbcef6d326582c6334#bcfb6ec7d29ddcbaaee535fbcef6d326582c6334" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=2139d59407b6a4f621a46b8ce9e7ada9af516d11#2139d59407b6a4f621a46b8ce9e7ada9af516d11" +source = "git+https://github.com/oxidecomputer/rumors?rev=bcfb6ec7d29ddcbaaee535fbcef6d326582c6334#bcfb6ec7d29ddcbaaee535fbcef6d326582c6334" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 13f98b81..553e21cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "2139d59407b6a4f621a46b8ce9e7ada9af516d11" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "bcfb6ec7d29ddcbaaee535fbcef6d326582c6334" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From f0ee40b392c77c9c9bd983ad541a0c0f7db2d982 Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 11:11:26 -0400 Subject: [PATCH 12/59] Track Rumors borrowed root-hash reads Advance to published Rumors main 4c6f01b3. Server/test Clippy, attested link conformance, and two-peer gossip passed using the published Git dependency. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d07e471..cd3cf5c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=bcfb6ec7d29ddcbaaee535fbcef6d326582c6334#bcfb6ec7d29ddcbaaee535fbcef6d326582c6334" +source = "git+https://github.com/oxidecomputer/rumors?rev=4c6f01b36a39f24e8637d714700bc34b4d9ab83c#4c6f01b36a39f24e8637d714700bc34b4d9ab83c" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=bcfb6ec7d29ddcbaaee535fbcef6d326582c6334#bcfb6ec7d29ddcbaaee535fbcef6d326582c6334" +source = "git+https://github.com/oxidecomputer/rumors?rev=4c6f01b36a39f24e8637d714700bc34b4d9ab83c#4c6f01b36a39f24e8637d714700bc34b4d9ab83c" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=bcfb6ec7d29ddcbaaee535fbcef6d326582c6334#bcfb6ec7d29ddcbaaee535fbcef6d326582c6334" +source = "git+https://github.com/oxidecomputer/rumors?rev=4c6f01b36a39f24e8637d714700bc34b4d9ab83c#4c6f01b36a39f24e8637d714700bc34b4d9ab83c" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 553e21cb..e609815f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "bcfb6ec7d29ddcbaaee535fbcef6d326582c6334" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "4c6f01b36a39f24e8637d714700bc34b4d9ab83c" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 9dbb63117f1985c5c04602d34cd63802c9aaba9b Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 11:51:00 -0400 Subject: [PATCH 13/59] Track Rumors unused-interface cleanup Advance to published Rumors main 20d1cd67. Server/test Clippy, attested link conformance, and two-peer gossip passed against the published Git dependency. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd3cf5c7..1b369286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=4c6f01b36a39f24e8637d714700bc34b4d9ab83c#4c6f01b36a39f24e8637d714700bc34b4d9ab83c" +source = "git+https://github.com/oxidecomputer/rumors?rev=20d1cd6710760122b7800863f7ffea9cd8d04c9f#20d1cd6710760122b7800863f7ffea9cd8d04c9f" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=4c6f01b36a39f24e8637d714700bc34b4d9ab83c#4c6f01b36a39f24e8637d714700bc34b4d9ab83c" +source = "git+https://github.com/oxidecomputer/rumors?rev=20d1cd6710760122b7800863f7ffea9cd8d04c9f#20d1cd6710760122b7800863f7ffea9cd8d04c9f" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=4c6f01b36a39f24e8637d714700bc34b4d9ab83c#4c6f01b36a39f24e8637d714700bc34b4d9ab83c" +source = "git+https://github.com/oxidecomputer/rumors?rev=20d1cd6710760122b7800863f7ffea9cd8d04c9f#20d1cd6710760122b7800863f7ffea9cd8d04c9f" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index e609815f..be8ea781 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "4c6f01b36a39f24e8637d714700bc34b4d9ab83c" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "20d1cd6710760122b7800863f7ffea9cd8d04c9f" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 5e6b9f41b324c153c6c6fd00bc0a9c86659ff30b Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 12:05:31 -0400 Subject: [PATCH 14/59] Track Rumors branch-buffer disposition Advance the published Rumors pin to 84ffc11f. That commit changes only triage notes; library code matches the previously validated pin. Validation against the Git dependency: cargo metadata --format-version 1; cargo nextest run --locked -p sush-server --test link --run-ignored all (conformance and gossip convergence passed). --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b369286..e1c50756 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=20d1cd6710760122b7800863f7ffea9cd8d04c9f#20d1cd6710760122b7800863f7ffea9cd8d04c9f" +source = "git+https://github.com/oxidecomputer/rumors?rev=84ffc11f04a1848e56300fd461fdb19dc8d3be15#84ffc11f04a1848e56300fd461fdb19dc8d3be15" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=20d1cd6710760122b7800863f7ffea9cd8d04c9f#20d1cd6710760122b7800863f7ffea9cd8d04c9f" +source = "git+https://github.com/oxidecomputer/rumors?rev=84ffc11f04a1848e56300fd461fdb19dc8d3be15#84ffc11f04a1848e56300fd461fdb19dc8d3be15" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=20d1cd6710760122b7800863f7ffea9cd8d04c9f#20d1cd6710760122b7800863f7ffea9cd8d04c9f" +source = "git+https://github.com/oxidecomputer/rumors?rev=84ffc11f04a1848e56300fd461fdb19dc8d3be15#84ffc11f04a1848e56300fd461fdb19dc8d3be15" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index be8ea781..bea60027 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "20d1cd6710760122b7800863f7ffea9cd8d04c9f" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "84ffc11f04a1848e56300fd461fdb19dc8d3be15" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 0c3a19204f5c78cce436f6e0f80dd49a3ed1cc7d Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 12:18:56 -0400 Subject: [PATCH 15/59] Track Rumors inline node prefixes Advance Rumors to published main 6ed760f0. Validate without a local dependency override: workspace/all-target clippy with warnings denied, and the opt-in link conformance and gossip convergence tests. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1c50756..5142ebe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=84ffc11f04a1848e56300fd461fdb19dc8d3be15#84ffc11f04a1848e56300fd461fdb19dc8d3be15" +source = "git+https://github.com/oxidecomputer/rumors?rev=6ed760f05b58a248ead45701f85711d93f1b887e#6ed760f05b58a248ead45701f85711d93f1b887e" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=84ffc11f04a1848e56300fd461fdb19dc8d3be15#84ffc11f04a1848e56300fd461fdb19dc8d3be15" +source = "git+https://github.com/oxidecomputer/rumors?rev=6ed760f05b58a248ead45701f85711d93f1b887e#6ed760f05b58a248ead45701f85711d93f1b887e" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=84ffc11f04a1848e56300fd461fdb19dc8d3be15#84ffc11f04a1848e56300fd461fdb19dc8d3be15" +source = "git+https://github.com/oxidecomputer/rumors?rev=6ed760f05b58a248ead45701f85711d93f1b887e#6ed760f05b58a248ead45701f85711d93f1b887e" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index bea60027..64655730 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "84ffc11f04a1848e56300fd461fdb19dc8d3be15" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "6ed760f05b58a248ead45701f85711d93f1b887e" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 39349ea91a7bf43236caf16d93cc8fb013d3e208 Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 12:58:56 -0400 Subject: [PATCH 16/59] Track Rumors leaf-reconstruction disposition Advance to published Rumors main 913ae385. This revision changes triage notes only; library code matches the previously validated pin. The opt-in link conformance and gossip convergence tests pass against the Git dependency without a local override. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5142ebe0..81c1927c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=6ed760f05b58a248ead45701f85711d93f1b887e#6ed760f05b58a248ead45701f85711d93f1b887e" +source = "git+https://github.com/oxidecomputer/rumors?rev=913ae385f81423c21b341cb78983848f25b5d4e4#913ae385f81423c21b341cb78983848f25b5d4e4" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=6ed760f05b58a248ead45701f85711d93f1b887e#6ed760f05b58a248ead45701f85711d93f1b887e" +source = "git+https://github.com/oxidecomputer/rumors?rev=913ae385f81423c21b341cb78983848f25b5d4e4#913ae385f81423c21b341cb78983848f25b5d4e4" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=6ed760f05b58a248ead45701f85711d93f1b887e#6ed760f05b58a248ead45701f85711d93f1b887e" +source = "git+https://github.com/oxidecomputer/rumors?rev=913ae385f81423c21b341cb78983848f25b5d4e4#913ae385f81423c21b341cb78983848f25b5d4e4" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 64655730..2587bcf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "6ed760f05b58a248ead45701f85711d93f1b887e" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "913ae385f81423c21b341cb78983848f25b5d4e4" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From e373acc8afe27b776275549b214c07d0458a5cb9 Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 13:12:49 -0400 Subject: [PATCH 17/59] Track Rumors owned iterator cleanup Advance the Rumors pin to 1aefec1128906db28683790569754a1163e94877, which includes the approved owned leaf iterator cleanup. No Sush source changes are needed for this internal refactor. Validated against the published Git revision without the local override: locked workspace/all-targets Clippy, attested link conformance, and bootstrap/gossip convergence all pass. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81c1927c..d3d1d48e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=913ae385f81423c21b341cb78983848f25b5d4e4#913ae385f81423c21b341cb78983848f25b5d4e4" +source = "git+https://github.com/oxidecomputer/rumors?rev=1aefec1128906db28683790569754a1163e94877#1aefec1128906db28683790569754a1163e94877" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=913ae385f81423c21b341cb78983848f25b5d4e4#913ae385f81423c21b341cb78983848f25b5d4e4" +source = "git+https://github.com/oxidecomputer/rumors?rev=1aefec1128906db28683790569754a1163e94877#1aefec1128906db28683790569754a1163e94877" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=913ae385f81423c21b341cb78983848f25b5d4e4#913ae385f81423c21b341cb78983848f25b5d4e4" +source = "git+https://github.com/oxidecomputer/rumors?rev=1aefec1128906db28683790569754a1163e94877#1aefec1128906db28683790569754a1163e94877" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 2587bcf0..efef4fea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "913ae385f81423c21b341cb78983848f25b5d4e4" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "1aefec1128906db28683790569754a1163e94877" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From b74a0894b75f47e277cefa16aae238d04800604a Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 13:19:40 -0400 Subject: [PATCH 18/59] Track Rumors height definition cleanup Advance Rumors to ea972b5ada8e148144ddc5edbff8520278c7d32a after its approved main merge. Its height definition cleanup is internal and needs no Sush source changes. Validated against the published Git revision without the local override: locked workspace/all-targets Clippy, attested link conformance, and bootstrap/gossip convergence all pass. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3d1d48e..2583e6c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=1aefec1128906db28683790569754a1163e94877#1aefec1128906db28683790569754a1163e94877" +source = "git+https://github.com/oxidecomputer/rumors?rev=ea972b5ada8e148144ddc5edbff8520278c7d32a#ea972b5ada8e148144ddc5edbff8520278c7d32a" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=1aefec1128906db28683790569754a1163e94877#1aefec1128906db28683790569754a1163e94877" +source = "git+https://github.com/oxidecomputer/rumors?rev=ea972b5ada8e148144ddc5edbff8520278c7d32a#ea972b5ada8e148144ddc5edbff8520278c7d32a" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=1aefec1128906db28683790569754a1163e94877#1aefec1128906db28683790569754a1163e94877" +source = "git+https://github.com/oxidecomputer/rumors?rev=ea972b5ada8e148144ddc5edbff8520278c7d32a#ea972b5ada8e148144ddc5edbff8520278c7d32a" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index efef4fea..1b0b42d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "1aefec1128906db28683790569754a1163e94877" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ea972b5ada8e148144ddc5edbff8520278c7d32a" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From ba2c744a20e77793930e4fe437e1abbb591a50e3 Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 13:32:52 -0400 Subject: [PATCH 19/59] Track Rumors fan iterator cleanup Advance Rumors to ce8f12c41fdb26bd0649a3c41c283a2351259416 after its approved main merge. Its iterator adapters and derived cloning are internal changes and require no Sush source changes. Validated against the published Git revision without the local override: locked workspace/all-targets Clippy, attested link conformance, and bootstrap/gossip convergence all pass. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2583e6c3..8a5db5e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ea972b5ada8e148144ddc5edbff8520278c7d32a#ea972b5ada8e148144ddc5edbff8520278c7d32a" +source = "git+https://github.com/oxidecomputer/rumors?rev=ce8f12c41fdb26bd0649a3c41c283a2351259416#ce8f12c41fdb26bd0649a3c41c283a2351259416" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ea972b5ada8e148144ddc5edbff8520278c7d32a#ea972b5ada8e148144ddc5edbff8520278c7d32a" +source = "git+https://github.com/oxidecomputer/rumors?rev=ce8f12c41fdb26bd0649a3c41c283a2351259416#ce8f12c41fdb26bd0649a3c41c283a2351259416" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ea972b5ada8e148144ddc5edbff8520278c7d32a#ea972b5ada8e148144ddc5edbff8520278c7d32a" +source = "git+https://github.com/oxidecomputer/rumors?rev=ce8f12c41fdb26bd0649a3c41c283a2351259416#ce8f12c41fdb26bd0649a3c41c283a2351259416" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 1b0b42d5..835e35d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ea972b5ada8e148144ddc5edbff8520278c7d32a" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ce8f12c41fdb26bd0649a3c41c283a2351259416" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 05bc9dab78b60c5193a5abdfba77f310d81fc2c9 Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 13:44:47 -0400 Subject: [PATCH 20/59] Track Rumors path geometry cleanup Advance Rumors to e24d9817423e45ddbc597afcbe7bface29b5eb1a after its approved main merge. The path and prefix changes are internal and require no Sush source changes. Validated against the published Git revision without the local override: locked workspace/all-targets Clippy, attested link conformance, and bootstrap/gossip convergence all pass. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a5db5e5..25290abe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ce8f12c41fdb26bd0649a3c41c283a2351259416#ce8f12c41fdb26bd0649a3c41c283a2351259416" +source = "git+https://github.com/oxidecomputer/rumors?rev=e24d9817423e45ddbc597afcbe7bface29b5eb1a#e24d9817423e45ddbc597afcbe7bface29b5eb1a" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ce8f12c41fdb26bd0649a3c41c283a2351259416#ce8f12c41fdb26bd0649a3c41c283a2351259416" +source = "git+https://github.com/oxidecomputer/rumors?rev=e24d9817423e45ddbc597afcbe7bface29b5eb1a#e24d9817423e45ddbc597afcbe7bface29b5eb1a" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ce8f12c41fdb26bd0649a3c41c283a2351259416#ce8f12c41fdb26bd0649a3c41c283a2351259416" +source = "git+https://github.com/oxidecomputer/rumors?rev=e24d9817423e45ddbc597afcbe7bface29b5eb1a#e24d9817423e45ddbc597afcbe7bface29b5eb1a" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 835e35d5..f3e1086c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ce8f12c41fdb26bd0649a3c41c283a2351259416" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "e24d9817423e45ddbc597afcbe7bface29b5eb1a" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 1f201a1870a659e816cf124b4067944c7bfbf6d5 Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 13:55:28 -0400 Subject: [PATCH 21/59] Track Rumors typed-tree precondition coverage Advance Rumors to c5a3bf877d6e793499576e8daa277586e04dc2f9 after its approved main merge. This adds internal tests and clarifies test helpers; no Sush source adaptation is needed. Validated against the published Git revision without the local override: locked workspace/all-targets Clippy, attested link conformance, and bootstrap/gossip convergence all pass. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25290abe..ff1601c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=e24d9817423e45ddbc597afcbe7bface29b5eb1a#e24d9817423e45ddbc597afcbe7bface29b5eb1a" +source = "git+https://github.com/oxidecomputer/rumors?rev=c5a3bf877d6e793499576e8daa277586e04dc2f9#c5a3bf877d6e793499576e8daa277586e04dc2f9" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=e24d9817423e45ddbc597afcbe7bface29b5eb1a#e24d9817423e45ddbc597afcbe7bface29b5eb1a" +source = "git+https://github.com/oxidecomputer/rumors?rev=c5a3bf877d6e793499576e8daa277586e04dc2f9#c5a3bf877d6e793499576e8daa277586e04dc2f9" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=e24d9817423e45ddbc597afcbe7bface29b5eb1a#e24d9817423e45ddbc597afcbe7bface29b5eb1a" +source = "git+https://github.com/oxidecomputer/rumors?rev=c5a3bf877d6e793499576e8daa277586e04dc2f9#c5a3bf877d6e793499576e8daa277586e04dc2f9" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index f3e1086c..f442d808 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "e24d9817423e45ddbc597afcbe7bface29b5eb1a" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "c5a3bf877d6e793499576e8daa277586e04dc2f9" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 79e83f32d15ac3fc75f394ec34e59e45258bbd0f Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 14:13:39 -0400 Subject: [PATCH 22/59] Track Rumors response-error delivery fix --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff1601c1..8e4aacfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=c5a3bf877d6e793499576e8daa277586e04dc2f9#c5a3bf877d6e793499576e8daa277586e04dc2f9" +source = "git+https://github.com/oxidecomputer/rumors?rev=57bade1afd1217a3896ef693badcb534c2274ab4#57bade1afd1217a3896ef693badcb534c2274ab4" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=c5a3bf877d6e793499576e8daa277586e04dc2f9#c5a3bf877d6e793499576e8daa277586e04dc2f9" +source = "git+https://github.com/oxidecomputer/rumors?rev=57bade1afd1217a3896ef693badcb534c2274ab4#57bade1afd1217a3896ef693badcb534c2274ab4" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=c5a3bf877d6e793499576e8daa277586e04dc2f9#c5a3bf877d6e793499576e8daa277586e04dc2f9" +source = "git+https://github.com/oxidecomputer/rumors?rev=57bade1afd1217a3896ef693badcb534c2274ab4#57bade1afd1217a3896ef693badcb534c2274ab4" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index f442d808..95ef0039 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "c5a3bf877d6e793499576e8daa277586e04dc2f9" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "57bade1afd1217a3896ef693badcb534c2274ab4" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 0b81b000d13e76d8cb36476958345132a1695099 Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 14:44:38 -0400 Subject: [PATCH 23/59] Track Rumors peer-model scope closeout Advance the Rumors Git pin and lockfile to published main after the owner-approved documentation and conformance-test cleanup. No Sush API adaptation is needed. Validation without a local override: locked workspace/all-target Clippy; attested-link conformance and gossip convergence. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e4aacfe..02dce278 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=57bade1afd1217a3896ef693badcb534c2274ab4#57bade1afd1217a3896ef693badcb534c2274ab4" +source = "git+https://github.com/oxidecomputer/rumors?rev=26e8811c91c68ae7ad3baeaf3599572df1c0def9#26e8811c91c68ae7ad3baeaf3599572df1c0def9" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=57bade1afd1217a3896ef693badcb534c2274ab4#57bade1afd1217a3896ef693badcb534c2274ab4" +source = "git+https://github.com/oxidecomputer/rumors?rev=26e8811c91c68ae7ad3baeaf3599572df1c0def9#26e8811c91c68ae7ad3baeaf3599572df1c0def9" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=57bade1afd1217a3896ef693badcb534c2274ab4#57bade1afd1217a3896ef693badcb534c2274ab4" +source = "git+https://github.com/oxidecomputer/rumors?rev=26e8811c91c68ae7ad3baeaf3599572df1c0def9#26e8811c91c68ae7ad3baeaf3599572df1c0def9" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 95ef0039..0040b48d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "57bade1afd1217a3896ef693badcb534c2274ab4" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "26e8811c91c68ae7ad3baeaf3599572df1c0def9" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From a4664721f5cd916785b7dc0c5a0af24c3c8ec9cb Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 17:45:14 -0400 Subject: [PATCH 24/59] Adopt Rumors session policies with heartbeats and migration cleanup Use ten-second heartbeat sessions alongside change notifications and a one-second deadline for all wire exchanges. Wait for old drivers to stop before joining another network, and clarify bookmark ownership during migration. Validated workspace Clippy and all seven gossip/attested-link integration tests against the published Rumors revision. --- Cargo.lock | 6 ++-- Cargo.toml | 2 +- server/Cargo.toml | 1 + server/src/bookmark.rs | 7 ++-- server/src/gossip.rs | 72 +++++++++++++++++++++++++++--------------- server/tests/gossip.rs | 67 +++++++++++++++++++++++++++++++++++++++ server/tests/link.rs | 13 +++++--- 7 files changed, 129 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02dce278..4b3b4352 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=26e8811c91c68ae7ad3baeaf3599572df1c0def9#26e8811c91c68ae7ad3baeaf3599572df1c0def9" +source = "git+https://github.com/oxidecomputer/rumors?rev=1617433ce885e594c1a564b7b54754ac1316f27c#1617433ce885e594c1a564b7b54754ac1316f27c" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=26e8811c91c68ae7ad3baeaf3599572df1c0def9#26e8811c91c68ae7ad3baeaf3599572df1c0def9" +source = "git+https://github.com/oxidecomputer/rumors?rev=1617433ce885e594c1a564b7b54754ac1316f27c#1617433ce885e594c1a564b7b54754ac1316f27c" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=26e8811c91c68ae7ad3baeaf3599572df1c0def9#26e8811c91c68ae7ad3baeaf3599572df1c0def9" +source = "git+https://github.com/oxidecomputer/rumors?rev=1617433ce885e594c1a564b7b54754ac1316f27c#1617433ce885e594c1a564b7b54754ac1316f27c" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 0040b48d..ee48b464 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "26e8811c91c68ae7ad3baeaf3599572df1c0def9" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "1617433ce885e594c1a564b7b54754ac1316f27c" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } diff --git a/server/Cargo.toml b/server/Cargo.toml index cb8bee95..06b5acb5 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -63,6 +63,7 @@ tokio-tungstenite.workspace = true x509-cert.workspace = true [dev-dependencies] +tokio = { workspace = true, features = ["test-util"] } attest-mock.workspace = true ciborium.workspace = true function_name.workspace = true diff --git a/server/src/bookmark.rs b/server/src/bookmark.rs index eeecd7e4..f7cd81f4 100644 --- a/server/src/bookmark.rs +++ b/server/src/bookmark.rs @@ -84,11 +84,8 @@ impl BookmarkSource { Self::new(&Logger::root(Discard, o!()), &Locker::null()) } - /// A persisting handle for a peer. Rumors persists a bookmark only - /// when a gossip session starts, and the gossip manager stops - /// every session before it hands a new peer its handle, so no two - /// peers persist concurrently; see the migration notes in - /// [`gossip`](crate::gossip). + /// A persisting handle for one peer. Before replacing it during migration, + /// the gossip manager waits for all sessions using the old handle to stop. pub fn handle(&self) -> SushBookmark { SushBookmark { log: self.log.clone(), diff --git a/server/src/gossip.rs b/server/src/gossip.rs index 80a517ae..267a4871 100644 --- a/server/src/gossip.rs +++ b/server/src/gossip.rs @@ -28,9 +28,9 @@ use std::io; use std::net::{SocketAddr, SocketAddrV6}; use std::time::Duration; -use futures::StreamExt as _; +use futures::{Stream, StreamExt as _, stream}; use rumors::error::Mismatch; -use rumors::{Error, Joined, Network, Peer, Rumors, Ticks}; +use rumors::{Changes, Error, Gossip, Joined, Network, Peer, Rumors, Ticks}; use serde::Serialize; use serde::de::DeserializeOwned; use sled_hardware_types::BaseboardId; @@ -38,8 +38,9 @@ use slog::{Logger, debug, info, o, warn}; use sprockets_tls::keys::SprocketsConfig; use tokio::sync::watch; use tokio::task::{AbortHandle, JoinSet}; -use tokio::time::{MissedTickBehavior, interval, timeout}; +use tokio::time::{Instant, MissedTickBehavior, Sleep, interval, interval_at, sleep, timeout}; use tokio::{select, spawn}; +use tokio_stream::wrappers::IntervalStream; use tokio_util::sync::CancellationToken; use rumors::link::routed::Endpoint; @@ -64,15 +65,36 @@ impl LinkedBaseboards { } } +/// Push changes promptly and probe each connection every ten seconds. +fn gossip_policy( + changes: Changes, +) -> impl Stream + Send { + let period = Duration::from_secs(10); + // Changes already requests the opening session. Delay the first heartbeat, + // and avoid a burst of overdue probes after a scheduling pause. + let mut heartbeat = interval_at(Instant::now() + period, period); + heartbeat.set_missed_tick_behavior(MissedTickBehavior::Delay); + stream::select( + changes.map(Gossip::from), + IntervalStream::new(heartbeat).map(|_| Gossip::Unconditionally), + ) +} + +/// Allow one second for each gossip, bootstrap, or retirement exchange. +fn session_deadline() -> Sleep { + sleep(Duration::from_secs(1)) +} + /// Manager timing. The defaults suit a rack, tests shrink them. #[derive(Clone, Debug)] pub struct GossipConfig { /// How often absent links are re-established. pub reconnect: Duration, /// Timeout for establishing a link, dialing a data stream, or initially - /// routing an incoming connection. Established gossip may wait indefinitely. + /// routing an incoming connection. pub connect_timeout: Duration, - /// Ceiling on one bootstrap join. + /// Ceiling on joining and attaching its bookmark, including local storage. + /// The wire exchange also has its own session deadline. pub join_timeout: Duration, } @@ -137,7 +159,12 @@ impl Seed { Ok(()) => bookmarks.handle(), Err(_) => bookmarks.shed_handle(), }; - let rumors = match Peer::seed().bookmark(handle).await { + let rumors = match Peer::seed() + .gossip_when(gossip_policy) + .session_deadline(session_deadline) + .bookmark(handle) + .await + { Ok(peer) => peer.into_rumors(), Err(unbookmarked) => match unbookmarked.peer.bookmark(bookmarks.shed_handle()).await { Ok(peer) => peer.into_rumors(), @@ -437,7 +464,7 @@ where } /// Spawn a session driver owning `link`: push our changes, and serve - /// whatever the peer initiates, until the link fails. + /// whatever the peer initiates, with periodic probes until the link fails. fn drive(&mut self, peer: SocketAddr, link: SprocketsLink) { debug!(self.log, "driving link"; "peer" => %peer); let rumors = self.rumors.clone(); @@ -450,31 +477,24 @@ where } } - /// Abandon our universe for the one `peer` belongs to, joining over the - /// fresh link to it. Every driver stops first: none may gossip across - /// the swap. On failure our universe is intact and the debt stands, so - /// the next link retries; either way all links are rebuilt, since the - /// old ones belong to the universe we are leaving. + /// Stop old sessions, then join the network reached through this fresh link. + /// On failure retain our network and retry later; all old links are rebuilt. /// - /// The new peer gets its own handle on the same bookmark storage. - /// That is safe because rumors persists a bookmark only when a - /// session starts, and aborting the drivers above ends every - /// session before the handle exists: the abandoned peer can never - /// store again. A store it already had in flight either loses to - /// the locker's sequence guard, or records a session that was - /// aborted before it sent anything, so nothing on the wire - /// outruns the record. If the received identity cannot be - /// persisted, we keep gossiping with a shed handle rather than - /// take the sled out of gossip; a stranded identity is harmless, - /// unlike a support shell that cannot reach a degraded rack. + /// Wait for the driver futures to drop before another peer uses the shared + /// bookmark storage. Locker orders any disk writes that outlive cancellation. + /// If attaching the bookmark fails, use a shed handle so storage failure + /// does not prevent gossip. async fn migrate(&mut self, peer: SocketAddr, mut link: SprocketsLink) { - self.drivers.abort_all(); + self.drivers.shutdown().await; self.live.clear(); info!( self.log, "joining the universe that beat ours"; "peer" => %peer, "ours" => %self.rumors.network(), ); - let bootstrap = Peer::bootstrap().bookmark(self.bookmarks.handle()); + let bootstrap = Peer::bootstrap() + .gossip_when(gossip_policy) + .session_deadline(session_deadline) + .bookmark(self.bookmarks.handle()); match timeout(self.config.join_timeout, bootstrap.join(&mut link)).await { Ok(Joined::Joined { peer }) => self.adopt(peer), Ok(Joined::Unbookmarked(unbookmarked)) => { @@ -520,7 +540,7 @@ async fn sessions( where T: DeserializeOwned + Serialize + Eq + Send + Sync + 'static, { - let mut driver = rumors.gossip_when(rumors.changes(), &mut link); + let mut driver = rumors.gossip(&mut link); while let Some(session) = driver.next().await { match session { Ok(_) => {} diff --git a/server/tests/gossip.rs b/server/tests/gossip.rs index 2ea2c3d4..fed11409 100644 --- a/server/tests/gossip.rs +++ b/server/tests/gossip.rs @@ -208,3 +208,70 @@ async fn linked_follows_live_links() { drop(b); eventually("dead peer unlinked", 120, async || a.linked().is_empty()).await; } + +/// Idle links probe every ten seconds without delaying changes or bursting +/// after a pause; a silent peer then trips the one-second session deadline. +#[tokio::test(start_paused = true)] +async fn idle_heartbeats_detect_a_silent_peer() { + use std::time::Duration; + + use futures::{FutureExt as _, StreamExt as _}; + use rumors::{Error, Joined, Led, Peer}; + use tokio::time::advance; + + let a: Rumors = Seed::grow( + &test_logger("idle_heartbeats_detect_a_silent_peer"), + &Locker::null(), + ) + .await + .into_rumors(); + let (mut near, mut far) = rumors::link::memory(); + let (served, joined) = futures::join!( + a.gossip_once(&mut near), + Peer::::bootstrap().join(&mut far), + ); + served.unwrap(); + let Joined::Joined { peer } = joined else { + panic!("join succeeds") + }; + let b = peer.into_rumors(); + let mut sessions = a.gossip(&mut near); + let (sent, received) = futures::join!(sessions.next(), b.gossip_once(&mut far)); + assert_eq!(sent.unwrap().unwrap().led, Led::Local); + received.unwrap(); + + advance(Duration::from_secs(9)).await; + assert!(sessions.next().now_or_never().is_none()); + a.send("a change before the heartbeat".into()).unwrap(); + let (sent, received) = futures::join!(sessions.next(), b.gossip_once(&mut far)); + sent.unwrap().unwrap(); + received.unwrap(); + assert_eq!(a.snapshot().hash(), b.snapshot().hash()); + + // The heartbeat still fires at ten seconds, even though the set is current. + advance(Duration::from_secs(1)).await; + assert!(sessions.next().now_or_never().is_none()); + let (sent, received) = futures::join!(sessions.next(), b.gossip_once(&mut far)); + assert_eq!(sent.unwrap().unwrap().led, Led::Local); + received.unwrap(); + + // Several missed intervals produce one probe, followed by a full interval. + advance(Duration::from_secs(35)).await; + assert!(sessions.next().now_or_never().is_none()); + let (sent, received) = futures::join!(sessions.next(), b.gossip_once(&mut far)); + assert_eq!(sent.unwrap().unwrap().led, Led::Local); + received.unwrap(); + assert!(sessions.next().now_or_never().is_none()); + advance(Duration::from_secs(9)).await; + assert!(sessions.next().now_or_never().is_none()); + + // Keep the remote link open but stop serving it: no EOF can expose failure. + advance(Duration::from_secs(1)).await; + assert!(sessions.next().now_or_never().is_none()); + advance(Duration::from_secs(1)).await; + assert!(matches!( + sessions.next().await, + Some(Err(Error::DeadlineExceeded)) + )); + assert!(sessions.next().await.is_none()); +} diff --git a/server/tests/link.rs b/server/tests/link.rs index 04799999..9e379e1c 100644 --- a/server/tests/link.rs +++ b/server/tests/link.rs @@ -119,13 +119,16 @@ async fn gossip_convergence() { // Alice seeds a universe with one message and serves sessions on her // end of the link. - let alice: Rumors = Peer::seed().into_rumors(); + let alice: Rumors = Peer::seed() + .gossip_when(|_| stream::pending::<()>()) + .session_deadline(|| sleep(Duration::from_secs(1))) + .into_rumors(); alice.send("from alice".to_string()).unwrap(); let server = spawn({ let alice = alice.clone(); async move { let mut link_a = link_a; - let mut driver = alice.gossip_when(stream::pending::<()>(), &mut link_a); + let mut driver = alice.gossip(&mut link_a); while let Some(session) = driver.next().await { session.expect("serving gossip session"); } @@ -135,7 +138,9 @@ async fn gossip_convergence() { // Bob joins Alice's universe through the link and hears her message. let rumors::Joined::Joined { peer: bob } = timeout( Duration::from_secs(60), - Peer::::bootstrap().join(&mut link_b), + Peer::::bootstrap() + .session_deadline(|| sleep(Duration::from_secs(1))) + .join(&mut link_b), ) .await .expect("bootstrap timed out") else { @@ -147,7 +152,7 @@ async fn gossip_convergence() { // Bob's own message reaches Alice within one gossip session. bob.send("from bob".to_string()).unwrap(); - timeout(Duration::from_secs(60), bob.gossip(&mut link_b)) + timeout(Duration::from_secs(60), bob.gossip_once(&mut link_b)) .await .expect("gossip timed out") .expect("gossip failed"); From 495af9cd5a212ff05705b35a0ce772ed287d5303 Mon Sep 17 00:00:00 2001 From: finch Date: Fri, 11 Sep 2026 18:09:17 -0400 Subject: [PATCH 25/59] Track the approved Rumors pipelining validation batch --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b3b4352..8168c033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=1617433ce885e594c1a564b7b54754ac1316f27c#1617433ce885e594c1a564b7b54754ac1316f27c" +source = "git+https://github.com/oxidecomputer/rumors?rev=7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc#7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc" dependencies = [ "bytes", "dashu-int", @@ -1451,7 +1451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2244,7 +2244,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3427,7 +3427,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=1617433ce885e594c1a564b7b54754ac1316f27c#1617433ce885e594c1a564b7b54754ac1316f27c" +source = "git+https://github.com/oxidecomputer/rumors?rev=7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc#7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc" dependencies = [ "async-stream", "before", @@ -3802,7 +3802,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3869,7 +3869,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=1617433ce885e594c1a564b7b54754ac1316f27c#1617433ce885e594c1a564b7b54754ac1316f27c" +source = "git+https://github.com/oxidecomputer/rumors?rev=7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc#7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc" dependencies = [ "dashu-int", ] @@ -5010,7 +5010,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5781,7 +5781,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ee48b464..3e33e11f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "1617433ce885e594c1a564b7b54754ac1316f27c" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From f68933f278b1fc52a086c50850e0c21dba13ebea Mon Sep 17 00:00:00 2001 From: finch Date: Sat, 12 Sep 2026 10:55:00 -0400 Subject: [PATCH 26/59] Track the approved Rumors window-sizing batch --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8168c033..4e537cb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc#7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc" +source = "git+https://github.com/oxidecomputer/rumors?rev=69bdab34a70eb3365399f673117dbc8244d16126#69bdab34a70eb3365399f673117dbc8244d16126" dependencies = [ "bytes", "dashu-int", @@ -1451,7 +1451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2244,7 +2244,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3427,7 +3427,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc#7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc" +source = "git+https://github.com/oxidecomputer/rumors?rev=69bdab34a70eb3365399f673117dbc8244d16126#69bdab34a70eb3365399f673117dbc8244d16126" dependencies = [ "async-stream", "before", @@ -3802,7 +3802,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3869,7 +3869,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc#7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc" +source = "git+https://github.com/oxidecomputer/rumors?rev=69bdab34a70eb3365399f673117dbc8244d16126#69bdab34a70eb3365399f673117dbc8244d16126" dependencies = [ "dashu-int", ] @@ -5010,7 +5010,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5781,7 +5781,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3e33e11f..8931f80e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "7f6551f4fb3960cc1726f00397d88cbbdb2c5bfc" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "69bdab34a70eb3365399f673117dbc8244d16126" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From d8ccd6ebb030826c236d62182573c9b4dfb157ab Mon Sep 17 00:00:00 2001 From: finch Date: Sat, 12 Sep 2026 10:56:39 -0400 Subject: [PATCH 27/59] Preserve unrelated lockfile selections --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e537cb9..8f5fbd46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1451,7 +1451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2244,7 +2244,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3427,7 +3427,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3802,7 +3802,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3869,7 +3869,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5010,7 +5010,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5781,7 +5781,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] From 63de21385a2fd0055f8b4f481b4d8c5b2dc13d2f Mon Sep 17 00:00:00 2001 From: finch Date: Sat, 12 Sep 2026 20:05:28 -0400 Subject: [PATCH 28/59] Track the approved Rumors backend-conformance batch --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f5fbd46..15734ca4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=69bdab34a70eb3365399f673117dbc8244d16126#69bdab34a70eb3365399f673117dbc8244d16126" +source = "git+https://github.com/oxidecomputer/rumors?rev=adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a#adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=69bdab34a70eb3365399f673117dbc8244d16126#69bdab34a70eb3365399f673117dbc8244d16126" +source = "git+https://github.com/oxidecomputer/rumors?rev=adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a#adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=69bdab34a70eb3365399f673117dbc8244d16126#69bdab34a70eb3365399f673117dbc8244d16126" +source = "git+https://github.com/oxidecomputer/rumors?rev=adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a#adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 8931f80e..48372739 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "69bdab34a70eb3365399f673117dbc8244d16126" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 172175763648d84ad8be0e9bbe416aa26cbee2ff Mon Sep 17 00:00:00 2001 From: finch Date: Sat, 12 Sep 2026 20:21:22 -0400 Subject: [PATCH 29/59] Track the approved Rumors deep-session coverage batch Advance the published Rumors dependency and lockfile without changing unrelated resolutions. Workspace Clippy and all gossip and attested-link integration tests pass against the Git dependency without a local override. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15734ca4..720187b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a#adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a" +source = "git+https://github.com/oxidecomputer/rumors?rev=38d39a33bd8f5f7386bfd9f97ae588141a9a812a#38d39a33bd8f5f7386bfd9f97ae588141a9a812a" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a#adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a" +source = "git+https://github.com/oxidecomputer/rumors?rev=38d39a33bd8f5f7386bfd9f97ae588141a9a812a#38d39a33bd8f5f7386bfd9f97ae588141a9a812a" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a#adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a" +source = "git+https://github.com/oxidecomputer/rumors?rev=38d39a33bd8f5f7386bfd9f97ae588141a9a812a#38d39a33bd8f5f7386bfd9f97ae588141a9a812a" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 48372739..5fd7250b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "adcbf4fca1f6fd16f3ede1af5a09ec34ff19721a" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "38d39a33bd8f5f7386bfd9f97ae588141a9a812a" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From b90dfaf451ec9a458707861e73d4a4c8ca65c33c Mon Sep 17 00:00:00 2001 From: finch Date: Mon, 14 Sep 2026 12:00:21 -0400 Subject: [PATCH 30/59] Adapt bookmarks to owned Rumors records and direct storage errors --- Cargo.lock | 6 +-- Cargo.toml | 2 +- server/src/bookmark.rs | 98 ++++++++++++++++++------------------------ 3 files changed, 45 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 720187b0..ed307cec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=38d39a33bd8f5f7386bfd9f97ae588141a9a812a#38d39a33bd8f5f7386bfd9f97ae588141a9a812a" +source = "git+https://github.com/oxidecomputer/rumors?rev=d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481#d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481" dependencies = [ "bytes", "dashu-int", @@ -3737,7 +3737,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=38d39a33bd8f5f7386bfd9f97ae588141a9a812a#38d39a33bd8f5f7386bfd9f97ae588141a9a812a" +source = "git+https://github.com/oxidecomputer/rumors?rev=d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481#d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481" dependencies = [ "async-stream", "before", @@ -4662,7 +4662,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=38d39a33bd8f5f7386bfd9f97ae588141a9a812a#38d39a33bd8f5f7386bfd9f97ae588141a9a812a" +source = "git+https://github.com/oxidecomputer/rumors?rev=d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481#d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 5fd7250b..635817f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "38d39a33bd8f5f7386bfd9f97ae588141a9a812a" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } diff --git a/server/src/bookmark.rs b/server/src/bookmark.rs index f7cd81f4..ac55124e 100644 --- a/server/src/bookmark.rs +++ b/server/src/bookmark.rs @@ -2,32 +2,23 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -//! Durable gossip peer identity across restarts. +//! Store Rumors restart bookkeeping in this server's locker. //! -//! A rumors [`Bookmark`] records a peer's identity and how far it has -//! advanced, so that a restarted sled may reclaim its previous identity -//! instead of stranding it. The invariant is (as usual) that we must -//! never adopt stale data, because in this case it could lead to causality -//! violations (which are bad). -//! -//! The record format and when to load & store are dictated by rumors. -//! We use a [`Tenant`] of a [`Locker`] to store it on disk(s); -//! if a load fails or the slots disagree, we assume a new identity -//! rather than risk resuming with a stale one. The record keeps every -//! universe's identities, so a lost write costs at most a stranded -//! identity, never a stale one. - -use std::io::{self, Cursor}; +//! Rumors supplies an opaque record that lets it reclaim a departed peer's +//! identity after catching up. The locker stores the record across local disks +//! and rejects stale or conflicting copies. An unusable record starts fresh +//! bookkeeping; it must never cause us to reuse an uncertain identity. + +use std::io::Cursor; use std::sync::Arc; -use rumors::{Bookmark, BookmarkError, Serialized}; +use rumors::Bookmark; use slog::{Discard, Logger, o, warn}; -use thiserror::Error; -use tokio::io::AsyncWrite; use crate::format::{self, NoFormat, Record, Versioned}; use crate::locker::{Locker, StoreError, Tenant, TenantSpec, Verdict}; +/// Locker namespace and format marker for Rumors bookmark records. pub const BOOKMARK: TenantSpec = TenantSpec { file: "bookmark", magic: b"SUSHBOOKMARK", @@ -37,37 +28,38 @@ pub const BOOKMARK: TenantSpec = TenantSpec { #[derive(serde::Deserialize, serde::Serialize)] struct BookmarkRecord(#[serde(with = "format::cbor_bytes")] Vec); +/// Identify the outer Sush record format. impl Versioned for BookmarkRecord { + /// Version of this wrapper, independent of Rumors’ opaque payload format. const VERSION: u16 = 0; } +/// This wrapper has no older representation to migrate. impl Record for BookmarkRecord { + /// No prior wrapper format exists. type Previous = NoFormat; } +/// Complete the migration interface for a format with no predecessor. impl TryFrom for BookmarkRecord { + /// Required by the migration interface; this conversion cannot fail. type Error = &'static str; + /// No value of the source type exists. fn try_from(none: NoFormat) -> Result { match none {} } } -/// What a bookmark load or store failed at. -#[derive(Debug, Error)] -pub enum BookmarkIoError { - #[error("serializing the bookmark record failed: {0}")] - Serialize(#[source] io::Error), - #[error("storing the bookmark failed: {0}")] - Store(#[source] StoreError), -} - /// This server's bookmark storage. Every handle shares the one record. #[derive(Clone, Debug)] pub struct BookmarkSource { + /// Logger carrying this bookmark’s component context. log: Logger, + /// Shared locker record for this server across peer replacements. tenant: Arc, } +/// Create storage handles for the gossip manager. impl BookmarkSource { /// A source persisting to `locker`. /// [`Seed::grow`](crate::gossip::Seed::grow) makes the one source @@ -108,18 +100,22 @@ impl BookmarkSource { /// One peer's handle on the [`BookmarkSource`]. #[derive(Debug)] pub struct SushBookmark { + /// Logger carrying this bookmark’s component context. log: Logger, + /// Shared locker record for this server across peer replacements. tenant: Arc, + /// Whether this handle bypasses storage after a persistence failure. shed: bool, } -impl BookmarkError for SushBookmark { - type Error = BookmarkIoError; -} - +/// Read locker records and store the complete bytes supplied by Rumors. impl Bookmark for SushBookmark { + /// Failure to durably store the locker record. + type Error = StoreError; + /// An owned snapshot of the stored Rumors record. type Reader = Cursor>; + /// Load a usable record, starting fresh if locker recovery rejects it. async fn load(&self) -> Result, Self::Error> { if self.shed { return Ok(None); @@ -145,22 +141,14 @@ impl Bookmark for SushBookmark { } } - async fn store(&self, write: F) -> Result<(), Self::Error> - where - F: for<'a> FnOnce(&'a mut (dyn AsyncWrite + Unpin + Send)) -> Serialized<'a> + Send, - { + /// Store the encoded Rumors record unless this handle has shed persistence. + async fn store(&self, bytes: Vec) -> Result<(), Self::Error> { if self.shed { return Ok(()); } - let mut buf = Cursor::new(Vec::new()); - write(&mut buf).await.map_err(BookmarkIoError::Serialize)?; - let record = BookmarkRecord(buf.into_inner()); - + let record = BookmarkRecord(bytes); let mut guard = self.tenant.lock().await; - guard - .store(&format::encode(&record)) - .await - .map_err(BookmarkIoError::Store) + guard.store(&format::encode(&record)).await } } @@ -172,14 +160,7 @@ mod test { use camino::Utf8PathBuf; use tempfile::TempDir; - use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _}; - - /// A serializer closure writing fixed bytes, shaped like rumors'. - fn record( - bytes: &'static [u8], - ) -> impl for<'a> FnOnce(&'a mut (dyn AsyncWrite + Unpin + Send)) -> Serialized<'a> + Send { - move |w| Box::pin(async move { w.write_all(bytes).await }) - } + use tokio::io::AsyncReadExt as _; /// Two slot directories, like two M.2s. fn slots(dir: &TempDir) -> Vec { @@ -193,14 +174,17 @@ mod test { .collect() } + /// Silence routine logging in storage tests. fn test_log() -> Logger { Logger::root(Discard, o!()) } + /// Create a bookmark source backed by the requested disk slots. fn source(slots: Vec) -> BookmarkSource { BookmarkSource::new(&test_log(), &Locker::new(&test_log(), slots).unwrap()) } + /// Read the exact bytes visible through the public Bookmark interface. async fn read_back(handle: &SushBookmark) -> Option> { let mut reader = handle.load().await.unwrap()?; let mut bytes = Vec::new(); @@ -216,7 +200,7 @@ mod test { let handle = source.handle(); assert!(read_back(&handle).await.is_none()); - handle.store(record(b"who we are")).await.unwrap(); + handle.store(b"who we are".to_vec()).await.unwrap(); assert_eq!(read_back(&handle).await.unwrap(), b"who we are"); } @@ -227,7 +211,7 @@ mod test { let slots = slots(&dir); for (slot, bytes) in slots.iter().zip([b"one", b"two"]) { let lone = source(vec![slot.clone()]); - lone.handle().store(record(bytes)).await.unwrap(); + lone.handle().store(bytes.to_vec()).await.unwrap(); } assert!(read_back(&source(slots).handle()).await.is_none()); } @@ -238,7 +222,7 @@ mod test { let dir = TempDir::with_prefix("sush-bookmark-").unwrap(); let source = source(slots(&dir)); - source.handle().store(record(b"shared")).await.unwrap(); + source.handle().store(b"shared".to_vec()).await.unwrap(); assert_eq!(read_back(&source.handle()).await.unwrap(), b"shared"); } @@ -248,15 +232,15 @@ mod test { async fn null_and_shed_touch_nothing() { let null = BookmarkSource::null(); let handle = null.handle(); - handle.store(record(b"lost")).await.unwrap(); + handle.store(b"lost".to_vec()).await.unwrap(); assert!(read_back(&handle).await.is_none()); let dir = TempDir::with_prefix("sush-bookmark-").unwrap(); let source = source(slots(&dir)); - source.handle().store(record(b"kept")).await.unwrap(); + source.handle().store(b"kept".to_vec()).await.unwrap(); let shed = source.shed_handle(); assert!(read_back(&shed).await.is_none()); - shed.store(record(b"dropped")).await.unwrap(); + shed.store(b"dropped".to_vec()).await.unwrap(); assert_eq!(read_back(&source.handle()).await.unwrap(), b"kept"); } } From 1c32b35a30ea06e9f2cfa0b6672d67c11fb8bdea Mon Sep 17 00:00:00 2001 From: finch Date: Mon, 14 Sep 2026 19:30:44 -0400 Subject: [PATCH 31/59] Track Rumors recovery fix and version Sush bookmark records Pin Rumors to published main f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d, including the fix for bookmark recovery that could reuse versions. Advance the Sush bookmark wrapper to version 1 and store the Rumors format version alongside its opaque bytes. An older wrapper or an incompatible Rumors record starts fresh bookkeeping, allowing subsequent checkpoints instead of repeatedly failing during session startup. Pin both wrapper layouts and test that incompatible records can be replaced. Validation against the published Git dependency, without a path override: formatting; native Helios workspace checking and Clippy for all targets; bookmark, locker, and gossip-manager tests. --- Cargo.lock | 24 +++++++-- Cargo.toml | 2 +- server/src/bookmark.rs | 116 +++++++++++++++++++++++++++++++++++------ 3 files changed, 121 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed307cec..455ef1c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481#d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481" +source = "git+https://github.com/oxidecomputer/rumors?rev=f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d#f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d" dependencies = [ "bytes", "dashu-int", @@ -1752,6 +1752,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "hashbrown" version = "0.14.5" @@ -3737,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481#d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481" +source = "git+https://github.com/oxidecomputer/rumors?rev=f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d#f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d" dependencies = [ "async-stream", "before", @@ -3748,6 +3754,7 @@ dependencies = [ "hex", "itertools", "rand 0.8.7", + "schnellru", "seq-macro", "serde", "sha3 0.12.0", @@ -4001,6 +4008,17 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "schnellru" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" +dependencies = [ + "ahash", + "cfg-if", + "hashbrown 0.13.2", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -4662,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481#d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481" +source = "git+https://github.com/oxidecomputer/rumors?rev=f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d#f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 635817f8..c7f74bc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d73fe2c9720fd64dc8b0d27733ec75b6a7bd7481" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } diff --git a/server/src/bookmark.rs b/server/src/bookmark.rs index ac55124e..35b304ea 100644 --- a/server/src/bookmark.rs +++ b/server/src/bookmark.rs @@ -8,6 +8,8 @@ //! identity after catching up. The locker stores the record across local disks //! and rejects stale or conflicting copies. An unusable record starts fresh //! bookkeeping; it must never cause us to reuse an uncertain identity. +//! The wrapper records the Rumors format version so an incompatible record is +//! discarded before it can repeatedly abort gossip at session startup. use std::io::Cursor; use std::sync::Arc; @@ -24,29 +26,66 @@ pub const BOOKMARK: TenantSpec = TenantSpec { magic: b"SUSHBOOKMARK", }; -/// Wrap the opaque bytes rumors writes. +/// The Rumors format version and its opaque restart bookkeeping. #[derive(serde::Deserialize, serde::Serialize)] -struct BookmarkRecord(#[serde(with = "format::cbor_bytes")] Vec); - -/// Identify the outer Sush record format. +struct BookmarkRecord( + /// Identifies which Rumors codec can read these bytes. + u64, + /// Complete record supplied by Rumors. + #[serde(with = "format::cbor_bytes")] + Vec, +); + +/// Identify the Sush wrapper format, independently of the enclosed Rumors format. impl Versioned for BookmarkRecord { - /// Version of this wrapper, independent of Rumors’ opaque payload format. - const VERSION: u16 = 0; + /// The wrapper stores an explicit Rumors format version. + const VERSION: u16 = 1; } -/// This wrapper has no older representation to migrate. +/// Read the versioned wrapper through the locker's format chain. impl Record for BookmarkRecord { - /// No prior wrapper format exists. - type Previous = NoFormat; + /// The wrapper without an explicit Rumors version. + type Previous = v0::BookmarkRecord; } -/// Complete the migration interface for a format with no predecessor. -impl TryFrom for BookmarkRecord { - /// Required by the migration interface; this conversion cannot fail. +/// An unlabelled record cannot establish which Rumors codec owns its bytes. +impl TryFrom for BookmarkRecord { + /// The reason recovery must start fresh. type Error = &'static str; - /// No value of the source type exists. - fn try_from(none: NoFormat) -> Result { - match none {} + /// Decline recovery rights whose format is unknown; replicated data is unaffected. + fn try_from(_: v0::BookmarkRecord) -> Result { + Err("bookmark has no Rumors format version") + } +} + +/// Frozen wrapper format without a Rumors version marker. +mod v0 { + use super::*; + + /// Opaque bookmark bytes in wrapper format zero. + #[derive(serde::Deserialize, serde::Serialize)] + pub(super) struct BookmarkRecord(#[serde(with = "format::cbor_bytes")] pub(super) Vec); + + /// Identify this member of the durable format chain. + impl Versioned for BookmarkRecord { + /// The initial Sush wrapper format. + const VERSION: u16 = 0; + } + + /// Terminate the wrapper's history at its initial format. + impl Record for BookmarkRecord { + /// No preceding format exists. + type Previous = NoFormat; + } + + /// Complete the migration interface for the initial format. + impl TryFrom for BookmarkRecord { + /// No source value can exist. + type Error = &'static str; + /// The source type has no inhabitants. + fn try_from(none: NoFormat) -> Result { + match none {} + } } } @@ -124,7 +163,17 @@ impl Bookmark for SushBookmark { match guard.load().await { Verdict::Adopt(record) | Verdict::Restore(record) => { match format::decode::(&record) { - Ok(BookmarkRecord(bytes)) => Ok(Some(Cursor::new(bytes))), + Ok(BookmarkRecord(version, bytes)) + if version == rumors::BOOKMARK_FORMAT_VERSION => + { + Ok(Some(Cursor::new(bytes))) + } + Ok(BookmarkRecord(version, _)) => { + warn!(self.log, "starting fresh Rumors bookkeeping"; + "stored_format" => version, + "current_format" => rumors::BOOKMARK_FORMAT_VERSION); + Ok(None) + } // Stranding the old identity is harmless; // resuming from a misread record is not. Err(error) => { @@ -146,7 +195,7 @@ impl Bookmark for SushBookmark { if self.shed { return Ok(()); } - let record = BookmarkRecord(bytes); + let record = BookmarkRecord(rumors::BOOKMARK_FORMAT_VERSION, bytes); let mut guard = self.tenant.lock().await; guard.store(&format::encode(&record)).await } @@ -204,6 +253,39 @@ mod test { assert_eq!(read_back(&handle).await.unwrap(), b"who we are"); } + /// Unsupported bookmark formats start fresh and allow subsequent persistence. + #[tokio::test] + async fn incompatible_formats_allow_fresh_checkpoints() { + let dir = TempDir::with_prefix("sush-bookmark-").unwrap(); + let source = source(slots(&dir)); + let old = format::encode(&v0::BookmarkRecord(b"old".to_vec())); + let future = format::encode(&BookmarkRecord( + rumors::BOOKMARK_FORMAT_VERSION + 1, + b"future".to_vec(), + )); + for encoded in [old, future] { + source.tenant.lock().await.store(&encoded).await.unwrap(); + let handle = source.handle(); + assert!(read_back(&handle).await.is_none()); + handle.store(b"fresh".to_vec()).await.unwrap(); + assert_eq!(read_back(&source.handle()).await.unwrap(), b"fresh"); + } + } + + /// Pin both Sush wrapper layouts independently of Rumors' opaque payload. + #[test] + fn wrapper_formats_pin_their_bytes() { + assert_eq!( + format::encode(&v0::BookmarkRecord(b"old".to_vec())), + [0x82, 0x00, 0x44, 0x43, b'o', b'l', b'd'] + ); + // Version 6 is an example marker here, not a requirement on the linked codec. + assert_eq!( + format::encode(&BookmarkRecord(6, b"new".to_vec())), + [0x82, 0x01, 0x46, 0x82, 0x06, 0x43, b'n', b'e', b'w'] + ); + } + /// A discarded verdict is a fresh start, not an error. #[tokio::test] async fn discard_assumes_fresh_identity() { From 5a0d44f125c767b62ea7b1c56bdf00aa2b2a51c6 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 11:58:43 -0400 Subject: [PATCH 32/59] Fix large bookmark record decoding and adopt Rumors conformance Request owned CBOR bytes so records larger than the decoder scratch buffer load completely. Keep the stored format unchanged and cover the failure with a large-record regression and the public Rumors Bookmark conformance suite. Track published Rumors ce670e87. Validation without a local override: native Helios bookmark, format, locker, and gossip tests, plus workspace Clippy for all targets. Local formatting passes. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- server/src/bookmark.rs | 17 +++++++++++++++++ server/src/format.rs | 25 ++++++++++++++++++++++++- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 455ef1c5..fe86e057 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d#f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d" +source = "git+https://github.com/oxidecomputer/rumors?rev=ce670e87dd524db2fd63161096d10d4eab27f70a#ce670e87dd524db2fd63161096d10d4eab27f70a" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d#f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d" +source = "git+https://github.com/oxidecomputer/rumors?rev=ce670e87dd524db2fd63161096d10d4eab27f70a#ce670e87dd524db2fd63161096d10d4eab27f70a" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d#f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d" +source = "git+https://github.com/oxidecomputer/rumors?rev=ce670e87dd524db2fd63161096d10d4eab27f70a#ce670e87dd524db2fd63161096d10d4eab27f70a" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index c7f74bc5..1ac05c7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "f4e2b1bb8efc89965a6d94ab84be7c12f8f8485d" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ce670e87dd524db2fd63161096d10d4eab27f70a" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } diff --git a/server/src/bookmark.rs b/server/src/bookmark.rs index 35b304ea..b606cd1f 100644 --- a/server/src/bookmark.rs +++ b/server/src/bookmark.rs @@ -241,6 +241,23 @@ mod test { Some(bytes) } + /// The locker-backed adapter preserves records across replacement and cancellation. + #[tokio::test] + async fn conforms_to_bookmark_contract() { + let mut directories = Vec::new(); + rumors::conformance::bookmark::check( + async || { + let dir = TempDir::with_prefix("sush-bookmark-conformance-").unwrap(); + let handle = source(slots(&dir)).handle(); + // Keep each fresh fixture's files until its check has finished. + directories.push(dir); + handle + }, + || tokio::time::sleep(std::time::Duration::from_secs(30)), + ) + .await; + } + /// A stored record loads back verbatim. #[tokio::test] async fn round_trip() { diff --git a/server/src/format.rs b/server/src/format.rs index a6b0e94e..d586760d 100644 --- a/server/src/format.rs +++ b/server/src/format.rs @@ -174,22 +174,35 @@ pub(crate) mod cbor_bytes { use serde::{Deserializer, Serializer}; use std::fmt; + /// Encode the buffer as one byte string. pub fn serialize(bytes: &[u8], serializer: S) -> Result { serializer.serialize_bytes(bytes) } + /// Decode an owned buffer without imposing the decoder's scratch-buffer limit. pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + /// Accept borrowed or owned byte strings as an owned record. struct Bytes; + /// Preserve the complete byte string regardless of how the decoder supplies it. impl<'de> Visitor<'de> for Bytes { + /// The record's opaque bytes. type Value = Vec; + /// Describe the required CBOR value on a type mismatch. fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("bytes") } + /// Copy a decoder's temporary buffer. fn visit_bytes(self, v: &[u8]) -> Result { Ok(v.to_vec()) } + /// Take the decoder's allocation without another copy. + fn visit_byte_buf(self, v: Vec) -> Result { + Ok(v) + } } - deserializer.deserialize_bytes(Bytes) + // The result is owned. Requesting borrowed bytes would restrict + // Ciborium to records that fit in its fixed scratch buffer. + deserializer.deserialize_byte_buf(Bytes) } } @@ -289,6 +302,16 @@ mod test { assert_eq!(decoded, value); } + /// Records larger than the decoder's scratch buffer round-trip completely. + #[test] + fn large_record_round_trip() { + let value = TestV1 { + count: 7, + label: "x".repeat(16 * 1024), + }; + assert_eq!(decode::(&encode(&value)).unwrap(), value); + } + #[test] fn old_version_converts_up() { let old = encode(&TestV0 { count: 7 }); From 958a5205d4e686bd61ed0a9f749707e729f73d91 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 12:17:35 -0400 Subject: [PATCH 33/59] Track Rumors bookmark durability coverage --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe86e057..a2fd1be8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ce670e87dd524db2fd63161096d10d4eab27f70a#ce670e87dd524db2fd63161096d10d4eab27f70a" +source = "git+https://github.com/oxidecomputer/rumors?rev=ff8bcb6f2085c8bc50fa378a40a5e25af1722525#ff8bcb6f2085c8bc50fa378a40a5e25af1722525" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ce670e87dd524db2fd63161096d10d4eab27f70a#ce670e87dd524db2fd63161096d10d4eab27f70a" +source = "git+https://github.com/oxidecomputer/rumors?rev=ff8bcb6f2085c8bc50fa378a40a5e25af1722525#ff8bcb6f2085c8bc50fa378a40a5e25af1722525" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ce670e87dd524db2fd63161096d10d4eab27f70a#ce670e87dd524db2fd63161096d10d4eab27f70a" +source = "git+https://github.com/oxidecomputer/rumors?rev=ff8bcb6f2085c8bc50fa378a40a5e25af1722525#ff8bcb6f2085c8bc50fa378a40a5e25af1722525" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 1ac05c7d..52b28fe1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ce670e87dd524db2fd63161096d10d4eab27f70a" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ff8bcb6f2085c8bc50fa378a40a5e25af1722525" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 2d78f1f48ab7d74983e0db009fba0fa9cc4ddadc Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 12:32:25 -0400 Subject: [PATCH 34/59] Track Rumors checkpoint schedule coverage --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2fd1be8..311a97ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ff8bcb6f2085c8bc50fa378a40a5e25af1722525#ff8bcb6f2085c8bc50fa378a40a5e25af1722525" +source = "git+https://github.com/oxidecomputer/rumors?rev=d4215257bb0fda913f21211b4952e6e395da08b5#d4215257bb0fda913f21211b4952e6e395da08b5" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ff8bcb6f2085c8bc50fa378a40a5e25af1722525#ff8bcb6f2085c8bc50fa378a40a5e25af1722525" +source = "git+https://github.com/oxidecomputer/rumors?rev=d4215257bb0fda913f21211b4952e6e395da08b5#d4215257bb0fda913f21211b4952e6e395da08b5" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ff8bcb6f2085c8bc50fa378a40a5e25af1722525#ff8bcb6f2085c8bc50fa378a40a5e25af1722525" +source = "git+https://github.com/oxidecomputer/rumors?rev=d4215257bb0fda913f21211b4952e6e395da08b5#d4215257bb0fda913f21211b4952e6e395da08b5" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 52b28fe1..ce762669 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ff8bcb6f2085c8bc50fa378a40a5e25af1722525" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d4215257bb0fda913f21211b4952e6e395da08b5" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 4784db5e49fa9535ce90a30b082b3f00cd573a72 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 12:51:04 -0400 Subject: [PATCH 35/59] Track Rumors bookmark validation fixes --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 311a97ab..ba3ccf5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d4215257bb0fda913f21211b4952e6e395da08b5#d4215257bb0fda913f21211b4952e6e395da08b5" +source = "git+https://github.com/oxidecomputer/rumors?rev=dcd52184b87644bb2267fdb2ab240bed316668d3#dcd52184b87644bb2267fdb2ab240bed316668d3" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d4215257bb0fda913f21211b4952e6e395da08b5#d4215257bb0fda913f21211b4952e6e395da08b5" +source = "git+https://github.com/oxidecomputer/rumors?rev=dcd52184b87644bb2267fdb2ab240bed316668d3#dcd52184b87644bb2267fdb2ab240bed316668d3" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d4215257bb0fda913f21211b4952e6e395da08b5#d4215257bb0fda913f21211b4952e6e395da08b5" +source = "git+https://github.com/oxidecomputer/rumors?rev=dcd52184b87644bb2267fdb2ab240bed316668d3#dcd52184b87644bb2267fdb2ab240bed316668d3" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index ce762669..0815255c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d4215257bb0fda913f21211b4952e6e395da08b5" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "dcd52184b87644bb2267fdb2ab240bed316668d3" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 8b4963188a8672211283fa894fdd90f4c25d5eb7 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 13:35:48 -0400 Subject: [PATCH 36/59] Track Rumors decoder boundary validation Advance to published Rumors b0d0abbb, including the frame-head EOF classification fix and expanded decoder properties. Preserve unrelated lockfile selections. Validation: native Helios cargo nextest run --locked --workspace -E "test(bookmark::) | test(locker::) | test(format::)" (49 passed); cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; local cargo fmt --check and locked fetch. The previously recorded lifecycle failures remain outside this focused pin-update run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba3ccf5b..fccbecb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=dcd52184b87644bb2267fdb2ab240bed316668d3#dcd52184b87644bb2267fdb2ab240bed316668d3" +source = "git+https://github.com/oxidecomputer/rumors?rev=b0d0abbb38841a812e740f2cd889df4bb8de5969#b0d0abbb38841a812e740f2cd889df4bb8de5969" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=dcd52184b87644bb2267fdb2ab240bed316668d3#dcd52184b87644bb2267fdb2ab240bed316668d3" +source = "git+https://github.com/oxidecomputer/rumors?rev=b0d0abbb38841a812e740f2cd889df4bb8de5969#b0d0abbb38841a812e740f2cd889df4bb8de5969" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=dcd52184b87644bb2267fdb2ab240bed316668d3#dcd52184b87644bb2267fdb2ab240bed316668d3" +source = "git+https://github.com/oxidecomputer/rumors?rev=b0d0abbb38841a812e740f2cd889df4bb8de5969#b0d0abbb38841a812e740f2cd889df4bb8de5969" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 0815255c..bc2ab6a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "dcd52184b87644bb2267fdb2ab240bed316668d3" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "b0d0abbb38841a812e740f2cd889df4bb8de5969" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 2bfd4a24a8fd91e87324039e4926506c7521e605 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 13:49:24 -0400 Subject: [PATCH 37/59] Track Rumors tree-join validation Advance to published Rumors c75a57b8, which strengthens tree-join tests and documentation without changing the API. Preserve unrelated lockfile selections. Validation: native Helios cargo nextest run --locked --workspace -E "test(bookmark::) | test(locker::) | test(format::)" (49 passed); cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; local cargo fmt --check and locked fetch. Previously recorded lifecycle failures remain outside this focused pin-update run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fccbecb6..2a15480f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=b0d0abbb38841a812e740f2cd889df4bb8de5969#b0d0abbb38841a812e740f2cd889df4bb8de5969" +source = "git+https://github.com/oxidecomputer/rumors?rev=c75a57b86e256a94ca6ede33570af76ff853153a#c75a57b86e256a94ca6ede33570af76ff853153a" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=b0d0abbb38841a812e740f2cd889df4bb8de5969#b0d0abbb38841a812e740f2cd889df4bb8de5969" +source = "git+https://github.com/oxidecomputer/rumors?rev=c75a57b86e256a94ca6ede33570af76ff853153a#c75a57b86e256a94ca6ede33570af76ff853153a" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=b0d0abbb38841a812e740f2cd889df4bb8de5969#b0d0abbb38841a812e740f2cd889df4bb8de5969" +source = "git+https://github.com/oxidecomputer/rumors?rev=c75a57b86e256a94ca6ede33570af76ff853153a#c75a57b86e256a94ca6ede33570af76ff853153a" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index bc2ab6a8..7e19f800 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "b0d0abbb38841a812e740f2cd889df4bb8de5969" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "c75a57b86e256a94ca6ede33570af76ff853153a" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 50de03190179bb2d5ffbfec190c4120adcbd1233 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 14:19:03 -0400 Subject: [PATCH 38/59] Track Rumors observer validation Advance to published Rumors fa9a1f0e, which strengthens observer tests and comments without changing the API. Preserve unrelated lockfile selections. Validation: native Helios cargo nextest run --locked --workspace -E "test(bookmark::) | test(locker::) | test(format::)" (49 passed); cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; local cargo fmt --check and locked fetch. Previously recorded lifecycle failures remain outside this focused pin-update run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a15480f..e5896503 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=c75a57b86e256a94ca6ede33570af76ff853153a#c75a57b86e256a94ca6ede33570af76ff853153a" +source = "git+https://github.com/oxidecomputer/rumors?rev=fa9a1f0eec858c2b6697626edd370bd15bb548bf#fa9a1f0eec858c2b6697626edd370bd15bb548bf" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=c75a57b86e256a94ca6ede33570af76ff853153a#c75a57b86e256a94ca6ede33570af76ff853153a" +source = "git+https://github.com/oxidecomputer/rumors?rev=fa9a1f0eec858c2b6697626edd370bd15bb548bf#fa9a1f0eec858c2b6697626edd370bd15bb548bf" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=c75a57b86e256a94ca6ede33570af76ff853153a#c75a57b86e256a94ca6ede33570af76ff853153a" +source = "git+https://github.com/oxidecomputer/rumors?rev=fa9a1f0eec858c2b6697626edd370bd15bb548bf#fa9a1f0eec858c2b6697626edd370bd15bb548bf" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 7e19f800..675712d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "c75a57b86e256a94ca6ede33570af76ff853153a" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "fa9a1f0eec858c2b6697626edd370bd15bb548bf" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 9e7a3a20a3af0b345352652faa5181321f716b27 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 16:14:49 -0400 Subject: [PATCH 39/59] Track Rumors retirement validation Advance to published Rumors 7cb864cc, which strengthens retirement tests without changing the API. Preserve unrelated lockfile selections. Validation: native Helios cargo nextest run --locked --workspace -E "test(bookmark::) | test(locker::) | test(format::)" (49 passed); cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; local cargo fmt --check and locked fetch. Previously recorded lifecycle failures remain outside this focused pin-update run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5896503..c4be2029 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=fa9a1f0eec858c2b6697626edd370bd15bb548bf#fa9a1f0eec858c2b6697626edd370bd15bb548bf" +source = "git+https://github.com/oxidecomputer/rumors?rev=7cb864cc501a075365d7432e513d2ccfc98c0b69#7cb864cc501a075365d7432e513d2ccfc98c0b69" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=fa9a1f0eec858c2b6697626edd370bd15bb548bf#fa9a1f0eec858c2b6697626edd370bd15bb548bf" +source = "git+https://github.com/oxidecomputer/rumors?rev=7cb864cc501a075365d7432e513d2ccfc98c0b69#7cb864cc501a075365d7432e513d2ccfc98c0b69" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=fa9a1f0eec858c2b6697626edd370bd15bb548bf#fa9a1f0eec858c2b6697626edd370bd15bb548bf" +source = "git+https://github.com/oxidecomputer/rumors?rev=7cb864cc501a075365d7432e513d2ccfc98c0b69#7cb864cc501a075365d7432e513d2ccfc98c0b69" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 675712d7..f4c0f057 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "fa9a1f0eec858c2b6697626edd370bd15bb548bf" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "7cb864cc501a075365d7432e513d2ccfc98c0b69" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 0cbb6ee7c111accf69ed1dd69ae09a6d153ddb14 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 16:36:15 -0400 Subject: [PATCH 40/59] Track Rumors session accounting validation Advance to published Rumors a889c8fa, which strengthens session-accounting tests without changing the API. Preserve unrelated lockfile selections. Validation: native Helios cargo nextest run --locked --workspace -E "test(bookmark::) | test(locker::) | test(format::)" (49 passed); cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; local cargo fmt --check and locked fetch. Previously recorded lifecycle failures remain outside this focused pin-update run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c4be2029..44aac410 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=7cb864cc501a075365d7432e513d2ccfc98c0b69#7cb864cc501a075365d7432e513d2ccfc98c0b69" +source = "git+https://github.com/oxidecomputer/rumors?rev=a889c8fa00205cc5c37881a709cdc26d6b6b29e5#a889c8fa00205cc5c37881a709cdc26d6b6b29e5" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=7cb864cc501a075365d7432e513d2ccfc98c0b69#7cb864cc501a075365d7432e513d2ccfc98c0b69" +source = "git+https://github.com/oxidecomputer/rumors?rev=a889c8fa00205cc5c37881a709cdc26d6b6b29e5#a889c8fa00205cc5c37881a709cdc26d6b6b29e5" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=7cb864cc501a075365d7432e513d2ccfc98c0b69#7cb864cc501a075365d7432e513d2ccfc98c0b69" +source = "git+https://github.com/oxidecomputer/rumors?rev=a889c8fa00205cc5c37881a709cdc26d6b6b29e5#a889c8fa00205cc5c37881a709cdc26d6b6b29e5" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index f4c0f057..0eba8bfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "7cb864cc501a075365d7432e513d2ccfc98c0b69" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "a889c8fa00205cc5c37881a709cdc26d6b6b29e5" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 40e9a9af6243165b5af85044e8f96418dc2c7292 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 17:15:49 -0400 Subject: [PATCH 41/59] Track Rumors overlap validation Advance to published Rumors 16b3c51b, which strengthens overlapping-session validation without changing the API. Preserve unrelated lockfile selections. Validation: native Helios cargo nextest run --locked --workspace -E "test(bookmark::) | test(locker::) | test(format::)" (49 passed); cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; local cargo fmt --check and locked fetch. Previously recorded lifecycle failures remain outside this focused pin-update run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44aac410..943cfce7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=a889c8fa00205cc5c37881a709cdc26d6b6b29e5#a889c8fa00205cc5c37881a709cdc26d6b6b29e5" +source = "git+https://github.com/oxidecomputer/rumors?rev=16b3c51b67c65be954cd2f3b334f17a2a498288d#16b3c51b67c65be954cd2f3b334f17a2a498288d" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=a889c8fa00205cc5c37881a709cdc26d6b6b29e5#a889c8fa00205cc5c37881a709cdc26d6b6b29e5" +source = "git+https://github.com/oxidecomputer/rumors?rev=16b3c51b67c65be954cd2f3b334f17a2a498288d#16b3c51b67c65be954cd2f3b334f17a2a498288d" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=a889c8fa00205cc5c37881a709cdc26d6b6b29e5#a889c8fa00205cc5c37881a709cdc26d6b6b29e5" +source = "git+https://github.com/oxidecomputer/rumors?rev=16b3c51b67c65be954cd2f3b334f17a2a498288d#16b3c51b67c65be954cd2f3b334f17a2a498288d" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 0eba8bfe..696530d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "a889c8fa00205cc5c37881a709cdc26d6b6b29e5" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "16b3c51b67c65be954cd2f3b334f17a2a498288d" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From f099888a682bf07b1d57faa7235a74513555b807 Mon Sep 17 00:00:00 2001 From: finch Date: Tue, 15 Sep 2026 17:52:18 -0400 Subject: [PATCH 42/59] Track Rumors observation hook validation Advance to published Rumors 8575ff36, which strengthens observation-hook tests without changing the API. Preserve unrelated lockfile selections. Validation: native Helios cargo nextest run --locked --workspace -E "test(bookmark::) | test(locker::) | test(format::)" (49 passed); cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; local cargo fmt --check and locked fetch. Previously recorded lifecycle failures remain outside this focused pin-update run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 943cfce7..428d990c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=16b3c51b67c65be954cd2f3b334f17a2a498288d#16b3c51b67c65be954cd2f3b334f17a2a498288d" +source = "git+https://github.com/oxidecomputer/rumors?rev=8575ff360b7a1f8a608926267edb4b129af58207#8575ff360b7a1f8a608926267edb4b129af58207" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=16b3c51b67c65be954cd2f3b334f17a2a498288d#16b3c51b67c65be954cd2f3b334f17a2a498288d" +source = "git+https://github.com/oxidecomputer/rumors?rev=8575ff360b7a1f8a608926267edb4b129af58207#8575ff360b7a1f8a608926267edb4b129af58207" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=16b3c51b67c65be954cd2f3b334f17a2a498288d#16b3c51b67c65be954cd2f3b334f17a2a498288d" +source = "git+https://github.com/oxidecomputer/rumors?rev=8575ff360b7a1f8a608926267edb4b129af58207#8575ff360b7a1f8a608926267edb4b129af58207" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 696530d2..586db9a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "16b3c51b67c65be954cd2f3b334f17a2a498288d" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "8575ff360b7a1f8a608926267edb4b129af58207" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 7ba318a9ae2e7b3a2b0d14820a1a2446aba0f1a9 Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 15:21:21 -0400 Subject: [PATCH 43/59] Adopt Rumors snapshot equality Compare complete snapshots in the heartbeat test instead of exposing their Merkle hashes. Advance the dependency to published Rumors 8b6c55f3, including the send-version and Snapshot API changes; existing send call sites need no adaptation. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local formatting and locked fetching pass. Known unrelated lifecycle failures remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- server/tests/gossip.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 428d990c..af4c374a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=8575ff360b7a1f8a608926267edb4b129af58207#8575ff360b7a1f8a608926267edb4b129af58207" +source = "git+https://github.com/oxidecomputer/rumors?rev=8b6c55f3e08d72d1823f8b396cce3c827b668ad3#8b6c55f3e08d72d1823f8b396cce3c827b668ad3" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=8575ff360b7a1f8a608926267edb4b129af58207#8575ff360b7a1f8a608926267edb4b129af58207" +source = "git+https://github.com/oxidecomputer/rumors?rev=8b6c55f3e08d72d1823f8b396cce3c827b668ad3#8b6c55f3e08d72d1823f8b396cce3c827b668ad3" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=8575ff360b7a1f8a608926267edb4b129af58207#8575ff360b7a1f8a608926267edb4b129af58207" +source = "git+https://github.com/oxidecomputer/rumors?rev=8b6c55f3e08d72d1823f8b396cce3c827b668ad3#8b6c55f3e08d72d1823f8b396cce3c827b668ad3" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 586db9a4..72ef10f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "8575ff360b7a1f8a608926267edb4b129af58207" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "8b6c55f3e08d72d1823f8b396cce3c827b668ad3" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } diff --git a/server/tests/gossip.rs b/server/tests/gossip.rs index fed11409..f24e8a3f 100644 --- a/server/tests/gossip.rs +++ b/server/tests/gossip.rs @@ -246,7 +246,7 @@ async fn idle_heartbeats_detect_a_silent_peer() { let (sent, received) = futures::join!(sessions.next(), b.gossip_once(&mut far)); sent.unwrap().unwrap(); received.unwrap(); - assert_eq!(a.snapshot().hash(), b.snapshot().hash()); + assert_eq!(a.snapshot(), b.snapshot()); // The heartbeat still fires at ten seconds, even though the set is current. advance(Duration::from_secs(1)).await; From 790a096ce6c6203728893e833739816a4a0199cb Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 15:49:22 -0400 Subject: [PATCH 44/59] Match Rumors payload bounds in gossip wrappers Require Send + Sync + static payloads on Universe, Seed, and Manager, matching the Rumors handles they contain. Keep only codec bounds on Seed::grow and advance the Git dependency to published Rumors c4f71d49. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- server/src/gossip.rs | 15 +++++++++------ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af4c374a..2f4659a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=8b6c55f3e08d72d1823f8b396cce3c827b668ad3#8b6c55f3e08d72d1823f8b396cce3c827b668ad3" +source = "git+https://github.com/oxidecomputer/rumors?rev=c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b#c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=8b6c55f3e08d72d1823f8b396cce3c827b668ad3#8b6c55f3e08d72d1823f8b396cce3c827b668ad3" +source = "git+https://github.com/oxidecomputer/rumors?rev=c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b#c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=8b6c55f3e08d72d1823f8b396cce3c827b668ad3#8b6c55f3e08d72d1823f8b396cce3c827b668ad3" +source = "git+https://github.com/oxidecomputer/rumors?rev=c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b#c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 72ef10f7..8b2cdc6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "8b6c55f3e08d72d1823f8b396cce3c827b668ad3" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } diff --git a/server/src/gossip.rs b/server/src/gossip.rs index 267a4871..9f116e02 100644 --- a/server/src/gossip.rs +++ b/server/src/gossip.rs @@ -110,12 +110,13 @@ impl Default for GossipConfig { /// A gossip universe. #[derive(Clone, Debug)] -pub struct Universe { +pub struct Universe { /// The gossiped set. pub rumors: Rumors, } -impl Universe { +impl Universe { + /// Wrap the initial gossip network for publication. pub fn genesis(rumors: Rumors) -> Self { Self { rumors } } @@ -134,12 +135,12 @@ impl Universe { /// the pair whole, so a seed can never gossip against a source other /// than its own. #[derive(Debug)] -pub struct Seed { +pub struct Seed { rumors: Rumors, bookmarks: BookmarkSource, } -impl Seed { +impl Seed { /// Seed a fresh universe with this server as its only peer, over /// `locker`'s storage, making the locker's one [`BookmarkSource`]. /// @@ -152,7 +153,7 @@ impl Seed { /// is harmless. pub async fn grow(log: &Logger, locker: &Locker) -> Self where - T: DeserializeOwned + Serialize + Eq + Send + Sync + 'static, + T: DeserializeOwned + Serialize + Eq, { let bookmarks = BookmarkSource::new(log, locker); let handle = match locker.probe().await { @@ -174,6 +175,7 @@ impl Seed { Self { rumors, bookmarks } } + /// Borrow the seeded network without separating it from its bookmark source. pub fn rumors(&self) -> &Rumors { &self.rumors } @@ -288,7 +290,8 @@ enum Stopped { Failed, } -struct Manager { +/// Maintains peer links and publishes the current gossip network. +struct Manager { log: Logger, config: GossipConfig, endpoint: Endpoint, From a9ccfa7a0abe4ad5d7323b983a836704bc9473af Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 16:15:02 -0400 Subject: [PATCH 45/59] Track the public Rumors Link API Advance to published Rumors 3006158d. Sush uses routed links directly and needs no adaptation to the transport-access API. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | binary(link)" (27 passed, including attested Link conformance). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f4659a2..eb95a783 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b#c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b" +source = "git+https://github.com/oxidecomputer/rumors?rev=3006158dfeab4dba0360a787b240bb5fa38aaaac#3006158dfeab4dba0360a787b240bb5fa38aaaac" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b#c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b" +source = "git+https://github.com/oxidecomputer/rumors?rev=3006158dfeab4dba0360a787b240bb5fa38aaaac#3006158dfeab4dba0360a787b240bb5fa38aaaac" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b#c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b" +source = "git+https://github.com/oxidecomputer/rumors?rev=3006158dfeab4dba0360a787b240bb5fa38aaaac#3006158dfeab4dba0360a787b240bb5fa38aaaac" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 8b2cdc6f..92c87f40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "c4f71d495dcbd2a9cee0c657ae9a60d8eba3fb9b" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "3006158dfeab4dba0360a787b240bb5fa38aaaac" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 782714751510db9d94d6b5777e91f1a9734e3cfa Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 16:24:36 -0400 Subject: [PATCH 46/59] Track Rumors Protocol cleanup Advance to published Rumors adeb2ec3. Sush does not depend on Protocol defaults or its representation, so no source adaptation is needed. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb95a783..21638d18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=3006158dfeab4dba0360a787b240bb5fa38aaaac#3006158dfeab4dba0360a787b240bb5fa38aaaac" +source = "git+https://github.com/oxidecomputer/rumors?rev=adeb2ec3edd46650be59a778320e6a5be1e773d0#adeb2ec3edd46650be59a778320e6a5be1e773d0" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=3006158dfeab4dba0360a787b240bb5fa38aaaac#3006158dfeab4dba0360a787b240bb5fa38aaaac" +source = "git+https://github.com/oxidecomputer/rumors?rev=adeb2ec3edd46650be59a778320e6a5be1e773d0#adeb2ec3edd46650be59a778320e6a5be1e773d0" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=3006158dfeab4dba0360a787b240bb5fa38aaaac#3006158dfeab4dba0360a787b240bb5fa38aaaac" +source = "git+https://github.com/oxidecomputer/rumors?rev=adeb2ec3edd46650be59a778320e6a5be1e773d0#adeb2ec3edd46650be59a778320e6a5be1e773d0" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 92c87f40..13f2e844 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "3006158dfeab4dba0360a787b240bb5fa38aaaac" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "adeb2ec3edd46650be59a778320e6a5be1e773d0" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 0bfaca83183cd0824f1a22a8bb3a8c909d561dbd Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 16:54:46 -0400 Subject: [PATCH 47/59] Track Rumors observer accumulation Advance to published Rumors 437604d1. Sush does not attach wire observers, so no source adaptation is needed. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21638d18..1ea8ee2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=adeb2ec3edd46650be59a778320e6a5be1e773d0#adeb2ec3edd46650be59a778320e6a5be1e773d0" +source = "git+https://github.com/oxidecomputer/rumors?rev=437604d1af17ad767206b0e66a2afa7748c6edcc#437604d1af17ad767206b0e66a2afa7748c6edcc" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=adeb2ec3edd46650be59a778320e6a5be1e773d0#adeb2ec3edd46650be59a778320e6a5be1e773d0" +source = "git+https://github.com/oxidecomputer/rumors?rev=437604d1af17ad767206b0e66a2afa7748c6edcc#437604d1af17ad767206b0e66a2afa7748c6edcc" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=adeb2ec3edd46650be59a778320e6a5be1e773d0#adeb2ec3edd46650be59a778320e6a5be1e773d0" +source = "git+https://github.com/oxidecomputer/rumors?rev=437604d1af17ad767206b0e66a2afa7748c6edcc#437604d1af17ad767206b0e66a2afa7748c6edcc" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 13f2e844..e02848ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "adeb2ec3edd46650be59a778320e6a5be1e773d0" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "437604d1af17ad767206b0e66a2afa7748c6edcc" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 0adcf450a9e151b954c9d8862a52952fac42e736 Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 17:21:59 -0400 Subject: [PATCH 48/59] Track Rumors synchronization settings Advance to published Rumors 317b3f54. Sush does not use the changed payload-depth type or settings readback API, so no source adaptation is needed. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ea8ee2b..13880e30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=437604d1af17ad767206b0e66a2afa7748c6edcc#437604d1af17ad767206b0e66a2afa7748c6edcc" +source = "git+https://github.com/oxidecomputer/rumors?rev=317b3f544e8df3f4c63b3757026e1cfdff1acdae#317b3f544e8df3f4c63b3757026e1cfdff1acdae" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=437604d1af17ad767206b0e66a2afa7748c6edcc#437604d1af17ad767206b0e66a2afa7748c6edcc" +source = "git+https://github.com/oxidecomputer/rumors?rev=317b3f544e8df3f4c63b3757026e1cfdff1acdae#317b3f544e8df3f4c63b3757026e1cfdff1acdae" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=437604d1af17ad767206b0e66a2afa7748c6edcc#437604d1af17ad767206b0e66a2afa7748c6edcc" +source = "git+https://github.com/oxidecomputer/rumors?rev=317b3f544e8df3f4c63b3757026e1cfdff1acdae#317b3f544e8df3f4c63b3757026e1cfdff1acdae" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index e02848ab..7d3d5cb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "437604d1af17ad767206b0e66a2afa7748c6edcc" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "317b3f544e8df3f4c63b3757026e1cfdff1acdae" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 63deece9d337d32cd79d9623061c10a917fe9bbe Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 18:04:27 -0400 Subject: [PATCH 49/59] Track Rumors session diagnostics Advance to published Rumors 310125338. Sush does not consume the changed session statistics or observer callbacks, so no source adaptation is needed. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13880e30..3ac6ac3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=317b3f544e8df3f4c63b3757026e1cfdff1acdae#317b3f544e8df3f4c63b3757026e1cfdff1acdae" +source = "git+https://github.com/oxidecomputer/rumors?rev=310125338c024d742cd31a657b492e792f946791#310125338c024d742cd31a657b492e792f946791" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=317b3f544e8df3f4c63b3757026e1cfdff1acdae#317b3f544e8df3f4c63b3757026e1cfdff1acdae" +source = "git+https://github.com/oxidecomputer/rumors?rev=310125338c024d742cd31a657b492e792f946791#310125338c024d742cd31a657b492e792f946791" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=317b3f544e8df3f4c63b3757026e1cfdff1acdae#317b3f544e8df3f4c63b3757026e1cfdff1acdae" +source = "git+https://github.com/oxidecomputer/rumors?rev=310125338c024d742cd31a657b492e792f946791#310125338c024d742cd31a657b492e792f946791" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 7d3d5cb8..0a1f172c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "317b3f544e8df3f4c63b3757026e1cfdff1acdae" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "310125338c024d742cd31a657b492e792f946791" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From e4c0063bf253ae01d00c76b536e94e3252b11a49 Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 18:23:34 -0400 Subject: [PATCH 50/59] Adapt Sush to routed link information Extract the routed link from Endpoint::link results while retaining address-based gossip management. Check the returned peer address in the attested test helper and advance to published Rumors 18aaeb09. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | binary(link)" (27 passed, including attested Link conformance). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- server/src/gossip.rs | 2 +- server/tests/link.rs | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ac6ac3b..646c7566 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=310125338c024d742cd31a657b492e792f946791#310125338c024d742cd31a657b492e792f946791" +source = "git+https://github.com/oxidecomputer/rumors?rev=18aaeb0916767cad7fe37546f201931508fe3ff0#18aaeb0916767cad7fe37546f201931508fe3ff0" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=310125338c024d742cd31a657b492e792f946791#310125338c024d742cd31a657b492e792f946791" +source = "git+https://github.com/oxidecomputer/rumors?rev=18aaeb0916767cad7fe37546f201931508fe3ff0#18aaeb0916767cad7fe37546f201931508fe3ff0" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=310125338c024d742cd31a657b492e792f946791#310125338c024d742cd31a657b492e792f946791" +source = "git+https://github.com/oxidecomputer/rumors?rev=18aaeb0916767cad7fe37546f201931508fe3ff0#18aaeb0916767cad7fe37546f201931508fe3ff0" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 0a1f172c..36ef8e10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "310125338c024d742cd31a657b492e792f946791" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "18aaeb0916767cad7fe37546f201931508fe3ff0" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } diff --git a/server/src/gossip.rs b/server/src/gossip.rs index 9f116e02..16b22fcb 100644 --- a/server/src/gossip.rs +++ b/server/src/gossip.rs @@ -425,7 +425,7 @@ where let endpoint = self.endpoint.clone(); let handle = self.dials.spawn(async move { let result = match timeout(deadline, endpoint.link(peer)).await { - Ok(Ok(link)) => Ok(link), + Ok(Ok((_, link))) => Ok(link), Ok(Err(err)) => Err(err.to_string()), Err(_) => Err("link establishment timed out".to_string()), }; diff --git a/server/tests/link.rs b/server/tests/link.rs index 9e379e1c..54c9280e 100644 --- a/server/tests/link.rs +++ b/server/tests/link.rs @@ -75,7 +75,8 @@ impl TestNet { let endpoint = self.a.endpoint(); let peer = *self.b.endpoint().local_addr(); let (linked, accepted) = join!(endpoint.link(peer), self.b.accept()); - let link_a = linked.expect("peer router accepts the link"); + let (info, link_a) = linked.expect("peer router accepts the link"); + assert_eq!(info.peer, peer); let (from, link_b) = accepted.expect("router is live"); assert_eq!(from, *endpoint.local_addr()); (link_a, link_b) From c688bc0563e659b6d909c5de7d0db5ba4cbdada9 Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 18:50:46 -0400 Subject: [PATCH 51/59] Track Rumors routed-link construction checks Advance the exact Git dependency to published Rumors 49a12e646. This batch requires no Sush source adaptation. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | binary(link)" (27 passed, including attested Link conformance). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 646c7566..5b94df02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=18aaeb0916767cad7fe37546f201931508fe3ff0#18aaeb0916767cad7fe37546f201931508fe3ff0" +source = "git+https://github.com/oxidecomputer/rumors?rev=49a12e646e9b32de14b7f138ed31033c405f57f2#49a12e646e9b32de14b7f138ed31033c405f57f2" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=18aaeb0916767cad7fe37546f201931508fe3ff0#18aaeb0916767cad7fe37546f201931508fe3ff0" +source = "git+https://github.com/oxidecomputer/rumors?rev=49a12e646e9b32de14b7f138ed31033c405f57f2#49a12e646e9b32de14b7f138ed31033c405f57f2" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=18aaeb0916767cad7fe37546f201931508fe3ff0#18aaeb0916767cad7fe37546f201931508fe3ff0" +source = "git+https://github.com/oxidecomputer/rumors?rev=49a12e646e9b32de14b7f138ed31033c405f57f2#49a12e646e9b32de14b7f138ed31033c405f57f2" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 36ef8e10..87fe47b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "18aaeb0916767cad7fe37546f201931508fe3ff0" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "49a12e646e9b32de14b7f138ed31033c405f57f2" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 98175038017a46b7927dafc20e9f1821e1d575c2 Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 19:15:09 -0400 Subject: [PATCH 52/59] Track Rumors public documentation checks Advance the exact Git dependency to published Rumors d37ffca7. The documentation and Debug additions require no Sush source adaptation. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b94df02..a7d58b76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=49a12e646e9b32de14b7f138ed31033c405f57f2#49a12e646e9b32de14b7f138ed31033c405f57f2" +source = "git+https://github.com/oxidecomputer/rumors?rev=d37ffca701ad2e6a3087e703d0645d89ace1eabb#d37ffca701ad2e6a3087e703d0645d89ace1eabb" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=49a12e646e9b32de14b7f138ed31033c405f57f2#49a12e646e9b32de14b7f138ed31033c405f57f2" +source = "git+https://github.com/oxidecomputer/rumors?rev=d37ffca701ad2e6a3087e703d0645d89ace1eabb#d37ffca701ad2e6a3087e703d0645d89ace1eabb" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=49a12e646e9b32de14b7f138ed31033c405f57f2#49a12e646e9b32de14b7f138ed31033c405f57f2" +source = "git+https://github.com/oxidecomputer/rumors?rev=d37ffca701ad2e6a3087e703d0645d89ace1eabb#d37ffca701ad2e6a3087e703d0645d89ace1eabb" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 87fe47b8..630cec72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "49a12e646e9b32de14b7f138ed31033c405f57f2" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d37ffca701ad2e6a3087e703d0645d89ace1eabb" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 3178ab5c46f34971b197f89800e3b5fb4130f013 Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 20:35:11 -0400 Subject: [PATCH 53/59] Track Rumors file bookmarks Advance to published Rumors ab57339c and its required Rand 0.9 dependency selection. Sush keeps its locker-backed bookmark adapter; the additive file adapter needs no source change or feature enablement. Validation against the published Git pin on Helios: cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings; cargo nextest run --locked --workspace --run-ignored all -E "test(bookmark::) | test(locker::) | test(format::) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (56 passed). Local formatting and locked fetching pass. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7d58b76..63cdc3c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d37ffca701ad2e6a3087e703d0645d89ace1eabb#d37ffca701ad2e6a3087e703d0645d89ace1eabb" +source = "git+https://github.com/oxidecomputer/rumors?rev=ab57339c2e23fb60435693dd99042e097a42ec25#ab57339c2e23fb60435693dd99042e097a42ec25" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d37ffca701ad2e6a3087e703d0645d89ace1eabb#d37ffca701ad2e6a3087e703d0645d89ace1eabb" +source = "git+https://github.com/oxidecomputer/rumors?rev=ab57339c2e23fb60435693dd99042e097a42ec25#ab57339c2e23fb60435693dd99042e097a42ec25" dependencies = [ "async-stream", "before", @@ -3753,7 +3753,7 @@ dependencies = [ "futures-util", "hex", "itertools", - "rand 0.8.7", + "rand 0.9.5", "schnellru", "seq-macro", "serde", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d37ffca701ad2e6a3087e703d0645d89ace1eabb#d37ffca701ad2e6a3087e703d0645d89ace1eabb" +source = "git+https://github.com/oxidecomputer/rumors?rev=ab57339c2e23fb60435693dd99042e097a42ec25#ab57339c2e23fb60435693dd99042e097a42ec25" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 630cec72..f1aca729 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d37ffca701ad2e6a3087e703d0645d89ace1eabb" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ab57339c2e23fb60435693dd99042e097a42ec25" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From b7cb88609f99938167a30358c6db444489772b1f Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 21:55:12 -0400 Subject: [PATCH 54/59] Track Rumors bookmark attachment cleanup Pin Rumors to published revision bf43cc48d6785ed01840392ef91d1ce5ca4fabb0. Its attachment and checkpoint refactor requires no Sush source changes. Validated on Helios with cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings, followed by cargo nextest run --locked --workspace --run-ignored all -E "test(bookmark::) | test(locker::) | test(format::) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (56 passed). Local cargo fetch --locked --target x86_64-unknown-illumos, cargo fmt --check, and git diff --check passed. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63cdc3c0..268e027d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ab57339c2e23fb60435693dd99042e097a42ec25#ab57339c2e23fb60435693dd99042e097a42ec25" +source = "git+https://github.com/oxidecomputer/rumors?rev=bf43cc48d6785ed01840392ef91d1ce5ca4fabb0#bf43cc48d6785ed01840392ef91d1ce5ca4fabb0" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ab57339c2e23fb60435693dd99042e097a42ec25#ab57339c2e23fb60435693dd99042e097a42ec25" +source = "git+https://github.com/oxidecomputer/rumors?rev=bf43cc48d6785ed01840392ef91d1ce5ca4fabb0#bf43cc48d6785ed01840392ef91d1ce5ca4fabb0" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=ab57339c2e23fb60435693dd99042e097a42ec25#ab57339c2e23fb60435693dd99042e097a42ec25" +source = "git+https://github.com/oxidecomputer/rumors?rev=bf43cc48d6785ed01840392ef91d1ce5ca4fabb0#bf43cc48d6785ed01840392ef91d1ce5ca4fabb0" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index f1aca729..f7250a89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "ab57339c2e23fb60435693dd99042e097a42ec25" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "bf43cc48d6785ed01840392ef91d1ce5ca4fabb0" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 2afb18c79ccef04590b371bb43abb958fbc61db6 Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 22:06:40 -0400 Subject: [PATCH 55/59] Track Rumors payload admission cleanup Pin Rumors to published revision b403e8c2711d024d7bc09ef20a8391009b7e9d48. Sush does not match EncodeError variants, so making that enum non-exhaustive requires no source changes. Validated on Helios with cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings, followed by cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local cargo fetch --locked --target x86_64-unknown-illumos, cargo fmt --check, and git diff --check passed. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 268e027d..d4efde5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=bf43cc48d6785ed01840392ef91d1ce5ca4fabb0#bf43cc48d6785ed01840392ef91d1ce5ca4fabb0" +source = "git+https://github.com/oxidecomputer/rumors?rev=b403e8c2711d024d7bc09ef20a8391009b7e9d48#b403e8c2711d024d7bc09ef20a8391009b7e9d48" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=bf43cc48d6785ed01840392ef91d1ce5ca4fabb0#bf43cc48d6785ed01840392ef91d1ce5ca4fabb0" +source = "git+https://github.com/oxidecomputer/rumors?rev=b403e8c2711d024d7bc09ef20a8391009b7e9d48#b403e8c2711d024d7bc09ef20a8391009b7e9d48" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=bf43cc48d6785ed01840392ef91d1ce5ca4fabb0#bf43cc48d6785ed01840392ef91d1ce5ca4fabb0" +source = "git+https://github.com/oxidecomputer/rumors?rev=b403e8c2711d024d7bc09ef20a8391009b7e9d48#b403e8c2711d024d7bc09ef20a8391009b7e9d48" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index f7250a89..0c4f29c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "bf43cc48d6785ed01840392ef91d1ce5ca4fabb0" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "b403e8c2711d024d7bc09ef20a8391009b7e9d48" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 9aeea450120041989ba2a99a6b1813b1254ac7fe Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 22:26:11 -0400 Subject: [PATCH 56/59] Track Rumors reconciliation driver consolidation Pin Rumors to published revision d9a295126e9af5ab63eecb847aa0a5a3c31c8d61. The shared bootstrap and gossip reconciliation driver requires no Sush source changes. Validated on Helios with cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings, followed by cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed, including cold and staggered bootstrap, node replacement, and attested-link convergence). Local cargo fetch --locked --target x86_64-unknown-illumos, cargo fmt --check, and git diff --check passed. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4efde5a..2c430ac1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=b403e8c2711d024d7bc09ef20a8391009b7e9d48#b403e8c2711d024d7bc09ef20a8391009b7e9d48" +source = "git+https://github.com/oxidecomputer/rumors?rev=d9a295126e9af5ab63eecb847aa0a5a3c31c8d61#d9a295126e9af5ab63eecb847aa0a5a3c31c8d61" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=b403e8c2711d024d7bc09ef20a8391009b7e9d48#b403e8c2711d024d7bc09ef20a8391009b7e9d48" +source = "git+https://github.com/oxidecomputer/rumors?rev=d9a295126e9af5ab63eecb847aa0a5a3c31c8d61#d9a295126e9af5ab63eecb847aa0a5a3c31c8d61" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=b403e8c2711d024d7bc09ef20a8391009b7e9d48#b403e8c2711d024d7bc09ef20a8391009b7e9d48" +source = "git+https://github.com/oxidecomputer/rumors?rev=d9a295126e9af5ab63eecb847aa0a5a3c31c8d61#d9a295126e9af5ab63eecb847aa0a5a3c31c8d61" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 0c4f29c9..884c93d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "b403e8c2711d024d7bc09ef20a8391009b7e9d48" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d9a295126e9af5ab63eecb847aa0a5a3c31c8d61" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From 4375ae14d938ef935c39ae8bef4caf61de6f009e Mon Sep 17 00:00:00 2001 From: finch Date: Wed, 16 Sep 2026 22:38:36 -0400 Subject: [PATCH 57/59] Track Rumors session and bookmark audit Pin Rumors to published revision 5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3. Observer metadata trait additions and the internal audit require no Sush source changes. Validated on Helios with cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings, followed by cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local cargo fetch --locked --target x86_64-unknown-illumos, cargo fmt --check, and git diff --check passed. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c430ac1..2b6e3f5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d9a295126e9af5ab63eecb847aa0a5a3c31c8d61#d9a295126e9af5ab63eecb847aa0a5a3c31c8d61" +source = "git+https://github.com/oxidecomputer/rumors?rev=5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3#5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d9a295126e9af5ab63eecb847aa0a5a3c31c8d61#d9a295126e9af5ab63eecb847aa0a5a3c31c8d61" +source = "git+https://github.com/oxidecomputer/rumors?rev=5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3#5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d9a295126e9af5ab63eecb847aa0a5a3c31c8d61#d9a295126e9af5ab63eecb847aa0a5a3c31c8d61" +source = "git+https://github.com/oxidecomputer/rumors?rev=5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3#5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 884c93d4..6930fbdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d9a295126e9af5ab63eecb847aa0a5a3c31c8d61" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From e0719b4a40b66f9893c53bf630118df30c6f7ea0 Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 17 Sep 2026 11:10:58 -0400 Subject: [PATCH 58/59] Track Rumors internal diagnostics cleanup Pin Rumors to published revision d9e5a4772fd7977f74fdf19e469a4210bba56430. Public error handling is unchanged, so Sush needs no source adaptations. Validated on Helios with cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings, followed by cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | test(gossip_convergence)" (26 passed). Local cargo fetch --locked --target x86_64-unknown-illumos, cargo fmt --check, and git diff --check passed. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b6e3f5f..d42d71ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3#5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3" +source = "git+https://github.com/oxidecomputer/rumors?rev=d9e5a4772fd7977f74fdf19e469a4210bba56430#d9e5a4772fd7977f74fdf19e469a4210bba56430" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3#5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3" +source = "git+https://github.com/oxidecomputer/rumors?rev=d9e5a4772fd7977f74fdf19e469a4210bba56430#d9e5a4772fd7977f74fdf19e469a4210bba56430" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3#5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3" +source = "git+https://github.com/oxidecomputer/rumors?rev=d9e5a4772fd7977f74fdf19e469a4210bba56430#d9e5a4772fd7977f74fdf19e469a4210bba56430" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index 6930fbdd..c4dae4a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "5a1cb7ac7e9d3cbb482ca650f50c70853509cdb3" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d9e5a4772fd7977f74fdf19e469a4210bba56430" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] } From f6b7a4e38662b941dfae8db034dba4bb58ca5366 Mon Sep 17 00:00:00 2001 From: finch Date: Thu, 17 Sep 2026 17:02:29 -0400 Subject: [PATCH 59/59] Track Rumors link cancellation conformance Pin Rumors to published revision 30096f0871d9bcc00e024317a9303a3590080d48. Sush needs no source adaptations. Its attested transport passes the full public conformance suite, including the revised accept-cancellation probe that permits rendezvous connections. Validated on Helios with cargo clippy --locked --workspace --all-targets -- --no-deps --deny warnings, followed by cargo nextest run --locked --workspace --run-ignored all -E "test(messages::wire_format) | test(manager_tests::gossiped_identities) | binary(gossip) | binary(link)" (27 passed; attested link conformance passed in 61.429 seconds). Local cargo fetch --locked --target x86_64-unknown-illumos, cargo fmt --check, and git diff --check passed. Previously recorded lifecycle timeouts remain outside this focused compatibility run. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d42d71ea..f42cd6ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -457,7 +457,7 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "before" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d9e5a4772fd7977f74fdf19e469a4210bba56430#d9e5a4772fd7977f74fdf19e469a4210bba56430" +source = "git+https://github.com/oxidecomputer/rumors?rev=30096f0871d9bcc00e024317a9303a3590080d48#30096f0871d9bcc00e024317a9303a3590080d48" dependencies = [ "bytes", "dashu-int", @@ -3743,7 +3743,7 @@ dependencies = [ [[package]] name = "rumors" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d9e5a4772fd7977f74fdf19e469a4210bba56430#d9e5a4772fd7977f74fdf19e469a4210bba56430" +source = "git+https://github.com/oxidecomputer/rumors?rev=30096f0871d9bcc00e024317a9303a3590080d48#30096f0871d9bcc00e024317a9303a3590080d48" dependencies = [ "async-stream", "before", @@ -4680,7 +4680,7 @@ dependencies = [ [[package]] name = "suanpan" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/rumors?rev=d9e5a4772fd7977f74fdf19e469a4210bba56430#d9e5a4772fd7977f74fdf19e469a4210bba56430" +source = "git+https://github.com/oxidecomputer/rumors?rev=30096f0871d9bcc00e024317a9303a3590080d48#30096f0871d9bcc00e024317a9303a3590080d48" dependencies = [ "dashu-int", ] diff --git a/Cargo.toml b/Cargo.toml index c4dae4a7..d6a7f598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8" rand_core = "0.6" reqwest = { version = "0.13", features = ["json", "rustls"] } rlimit = "0.10" -rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "d9e5a4772fd7977f74fdf19e469a4210bba56430" } +rumors = { git = "https://github.com/oxidecomputer/rumors", rev = "30096f0871d9bcc00e024317a9303a3590080d48" } rustix = { version = "1", features = ["fs", "process", "pty", "termios"] } rustls = "0.23" rustyline = { git = "https://github.com/kkawakam/rustyline", rev = "5ab22f2f8d65e85f0b925360437ece8eeeb6db92", features = ["signal-hook", "termios"] }