feat: import Chinese-localized Buzz source snapshot
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
//! buzz-relay-mesh — the inter-relay QUIC mesh.
|
||||
//!
|
||||
//! One iroh endpoint per relay runtime (identity = a boot-unique mesh
|
||||
//! keypair, attested by the relay's signing key — see [`wire::RuntimeId`]),
|
||||
//! a warm full mesh of authenticated connections, scuttlebutt membership
|
||||
//! gossip on a control substream, and a fenced wire contract that carries
|
||||
//! tunnel traffic (reliable streams + realtime datagrams) between pods.
|
||||
//!
|
||||
//! The relay consumes this crate exclusively through two seams:
|
||||
//!
|
||||
//! - [`RelayMeshMembership`] — "who is alive / draining / dialable?"
|
||||
//! - [`RelayPeerTransport`] — "move these bytes to that runtime."
|
||||
//!
|
||||
//! The seams are what keep single-instance deployments and same-pod sessions
|
||||
//! mesh-free: when `BUZZ_MESH=off` or no peers exist, the relay never
|
||||
//! constructs a mesh and the in-process fast path is untouched.
|
||||
//!
|
||||
//! **The law:** mesh membership is a hint; the Redis fenced generation is the
|
||||
//! arbiter. Nothing in this crate grants ownership — see [`wire::FencedHeader`].
|
||||
|
||||
pub mod endpoint;
|
||||
pub mod gossip;
|
||||
pub mod membership;
|
||||
pub mod peer;
|
||||
pub mod registry;
|
||||
pub mod runtime;
|
||||
pub mod status;
|
||||
pub mod wire;
|
||||
|
||||
// Lane modules — one owner per file (see the mesh thread for lane map):
|
||||
// endpoint.rs, peer.rs — Mari (transport core)
|
||||
// registry.rs, gossip.rs,
|
||||
// membership.rs, status.rs — Max (membership + /_mesh)
|
||||
// Session directory + tunnel routing live relay-side (Perci), consuming the
|
||||
// seams below; huddle fan-out lives in buzz-relay's audio module (Dawn).
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
pub use gossip::{GossipDigestEntry, GossipMessage, GossipRecord, GossipState, PhiAccrual};
|
||||
pub use membership::MeshMembership;
|
||||
pub use registry::{ReadyHeartbeat, ReadyRecord, ReadyRegistry, RuntimeAttestation};
|
||||
pub use runtime::MeshRuntime;
|
||||
pub use status::{ConnectionState, MeshCounters, MeshPeerCounters, MeshPeerStatus, MeshStatus};
|
||||
pub use wire::{
|
||||
FencedHeader, GoodbyeReason, MeshDatagram, MeshStreamFrame, Profile, RuntimeId, StreamHello,
|
||||
StreamRole, ALPN, WIRE_VERSION,
|
||||
};
|
||||
|
||||
/// Mesh configuration, resolved from env by the relay.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MeshConfig {
|
||||
/// `BUZZ_MESH` — `on` (default when replicas can exist) | `off` kill
|
||||
/// switch. When off, the relay must behave exactly like single-instance.
|
||||
pub enabled: bool,
|
||||
/// UDP bind for the iroh endpoint (`BUZZ_MESH_BIND_ADDR`, default
|
||||
/// `0.0.0.0:3478`). Excluded from istio sidecar capture in k8s.
|
||||
pub bind_addr: std::net::SocketAddr,
|
||||
/// Ready-registry heartbeat refresh (default 15s; expiry is 3x).
|
||||
pub registry_refresh: std::time::Duration,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum MeshError {
|
||||
#[error("frame encode: {0}")]
|
||||
Encode(#[source] postcard::Error),
|
||||
#[error("frame decode: {0}")]
|
||||
Decode(#[source] postcard::Error),
|
||||
#[error("unknown wire version {0}")]
|
||||
UnknownWireVersion(u8),
|
||||
#[error("empty frame")]
|
||||
EmptyFrame,
|
||||
#[error("frame exceeds max size ({size} > {max})")]
|
||||
FrameTooLarge { size: usize, max: usize },
|
||||
#[error("datagram exceeds connection max_datagram_size ({size} > {max})")]
|
||||
DatagramTooLarge { size: usize, max: usize },
|
||||
#[error("peer {0} not connected")]
|
||||
PeerNotConnected(RuntimeId),
|
||||
#[error("peer {0} is draining")]
|
||||
PeerDraining(RuntimeId),
|
||||
#[error("stale generation for session {session_id}: frame {frame_generation} < known {known_generation}")]
|
||||
StaleGeneration {
|
||||
session_id: uuid::Uuid,
|
||||
frame_generation: u64,
|
||||
known_generation: u64,
|
||||
},
|
||||
// The three variants below complete the fence-rejection taxonomy alongside
|
||||
// `StaleGeneration` (Wren's chaos-gate ruling: every fence-visible reject
|
||||
// is a typed variant, never a generic `Transport`, so live kill-9 /
|
||||
// partition / replay evidence is unambiguous). Counter surface:
|
||||
// `mesh_fence_rejections_total{reason=...}` with reasons
|
||||
// `stale_generation` | `no_active_lease` | `owner_mismatch` |
|
||||
// `future_generation`. None of these are serialized — the wire-level fence
|
||||
// signal remains `GoodbyeReason::StaleGeneration`.
|
||||
#[error("no active lease for session {session_id}: frame generation {frame_generation}, known generation {known_generation}, claimed owner {frame_owner_runtime_id}")]
|
||||
NoActiveLease {
|
||||
session_id: uuid::Uuid,
|
||||
frame_generation: u64,
|
||||
known_generation: u64,
|
||||
/// The owner the *frame* claimed — there is no current owner by
|
||||
/// definition when no live lease exists.
|
||||
frame_owner_runtime_id: RuntimeId,
|
||||
},
|
||||
#[error("owner mismatch for session {session_id} generation {generation}: frame owner {frame_owner_runtime_id} != current owner {current_owner_runtime_id}")]
|
||||
OwnerMismatch {
|
||||
session_id: uuid::Uuid,
|
||||
generation: u64,
|
||||
frame_owner_runtime_id: RuntimeId,
|
||||
current_owner_runtime_id: RuntimeId,
|
||||
},
|
||||
#[error("future generation for session {session_id}: frame {frame_generation} > known {known_generation}")]
|
||||
FutureGeneration {
|
||||
session_id: uuid::Uuid,
|
||||
frame_generation: u64,
|
||||
known_generation: u64,
|
||||
},
|
||||
#[error("mesh is disabled (BUZZ_MESH=off)")]
|
||||
Disabled,
|
||||
#[error("transport: {0}")]
|
||||
Transport(String),
|
||||
#[error("redis: {0}")]
|
||||
Redis(#[from] redis::RedisError),
|
||||
}
|
||||
|
||||
/// A peer as membership sees it. Everything here is a routing HINT.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PeerInfo {
|
||||
pub runtime_id: RuntimeId,
|
||||
pub draining: bool,
|
||||
/// Phi-accrual suspicion; `None` until enough heartbeats observed.
|
||||
pub phi: Option<f64>,
|
||||
/// Advisory load factor gossiped by the peer (0.0..).
|
||||
pub load: f32,
|
||||
}
|
||||
|
||||
/// Boxed future used across the seam traits. Public because implementors of
|
||||
/// [`StreamSendHalf`]/[`StreamRecvHalf`]/[`RelayPeerTransport`] outside this
|
||||
/// crate must name it.
|
||||
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
|
||||
|
||||
/// Seam 1: membership. Answers "who can I route to?" — never "who owns what."
|
||||
pub trait RelayMeshMembership: Send + Sync + 'static {
|
||||
/// Live, non-suspect peers (self excluded).
|
||||
fn peers(&self) -> Vec<PeerInfo>;
|
||||
/// This runtime's mesh identity.
|
||||
fn local_runtime_id(&self) -> RuntimeId;
|
||||
/// Begin drain: gossip `draining=true`, stop accepting new sessions.
|
||||
fn begin_drain(&self);
|
||||
}
|
||||
|
||||
/// Seam 2: transport. Moves fenced bytes to a specific runtime.
|
||||
///
|
||||
/// Implementations perform the datagram-size and wire-version checks; they do
|
||||
/// NOT perform generation fencing — that belongs to the session layer on both
|
||||
/// ends (fencing at every hop means every consumer checks, not the pipe).
|
||||
pub trait RelayPeerTransport: Send + Sync + 'static {
|
||||
/// Fire-and-forget realtime datagram (drop-on-full, never blocks on old
|
||||
/// audio). Errors only for disconnected peer / oversize frame.
|
||||
fn send_datagram(&self, to: RuntimeId, dgram: MeshDatagram) -> Result<(), MeshError>;
|
||||
|
||||
/// Open a reliable bi-stream to a peer for a session (`ReliableStream`
|
||||
/// or `HuddleControl` profile). Sends the `Hello` before returning.
|
||||
fn open_session_stream(
|
||||
&self,
|
||||
to: RuntimeId,
|
||||
hello: StreamHello,
|
||||
) -> BoxFuture<'_, Result<MeshStream, MeshError>>;
|
||||
|
||||
/// Register the handler invoked for inbound datagrams / session streams.
|
||||
/// Called once at relay startup.
|
||||
fn set_inbound(&self, handler: Box<dyn InboundHandler>);
|
||||
}
|
||||
|
||||
/// Inbound mesh traffic, delivered after wire decode + Hello validation.
|
||||
pub trait InboundHandler: Send + Sync + 'static {
|
||||
fn on_datagram(&self, from: RuntimeId, dgram: MeshDatagram);
|
||||
fn on_session_stream(&self, from: RuntimeId, hello: StreamHello, stream: MeshStream);
|
||||
}
|
||||
|
||||
/// A reliable mesh stream: length-delimited `MeshStreamFrame`s over QUIC.
|
||||
/// Concrete type (not a trait) so lanes share one framing implementation.
|
||||
pub struct MeshStream {
|
||||
// Mari: wrap iroh SendStream/RecvStream with the u32-LE length framing
|
||||
// from `wire`. Placeholder halves keep the seam compilable pre-transport.
|
||||
pub(crate) send: Box<dyn StreamSendHalf>,
|
||||
pub(crate) recv: Box<dyn StreamRecvHalf>,
|
||||
}
|
||||
|
||||
pub trait StreamSendHalf: Send + 'static {
|
||||
fn send_frame(&mut self, frame: MeshStreamFrame) -> BoxFuture<'_, Result<(), MeshError>>;
|
||||
fn finish(&mut self) -> Result<(), MeshError>;
|
||||
}
|
||||
|
||||
pub trait StreamRecvHalf: Send + 'static {
|
||||
fn recv_frame(&mut self) -> BoxFuture<'_, Result<Option<MeshStreamFrame>, MeshError>>;
|
||||
}
|
||||
|
||||
impl MeshStream {
|
||||
pub fn send_frame(&mut self, frame: MeshStreamFrame) -> BoxFuture<'_, Result<(), MeshError>> {
|
||||
self.send.send_frame(frame)
|
||||
}
|
||||
pub fn recv_frame(&mut self) -> BoxFuture<'_, Result<Option<MeshStreamFrame>, MeshError>> {
|
||||
self.recv.recv_frame()
|
||||
}
|
||||
pub fn finish(&mut self) -> Result<(), MeshError> {
|
||||
self.send.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw bytes helper used by transport internals.
|
||||
pub fn encode_datagram_checked(
|
||||
dgram: &MeshDatagram,
|
||||
max_datagram_size: usize,
|
||||
) -> Result<Bytes, MeshError> {
|
||||
let bytes = wire::encode(dgram)?;
|
||||
if bytes.len() > max_datagram_size {
|
||||
return Err(MeshError::DatagramTooLarge {
|
||||
size: bytes.len(),
|
||||
max: max_datagram_size,
|
||||
});
|
||||
}
|
||||
Ok(Bytes::from(bytes))
|
||||
}
|
||||
Reference in New Issue
Block a user