refactor: fold driver pumps into iroh-driver and codecs into transport

This commit is contained in:
Zachery Aaron Shores-Chmielewski 2026-07-30 12:18:02 +04:00
parent 8fb1f90d1f
commit 38383e1113
19 changed files with 156 additions and 313 deletions

1
.gitignore vendored
View file

@ -5,6 +5,7 @@ corpus
.loop/ .loop/
.model-cache/ .model-cache/
.deployment-notes/ .deployment-notes/
.omp/
# environment variables for deployment configurations # environment variables for deployment configurations
.config/ .config/

View file

@ -5,17 +5,13 @@
//! injected by the runtime crate so the datastream core does not depend on the //! injected by the runtime crate so the datastream core does not depend on the
//! concrete QUIC writer implementation. //! concrete QUIC writer implementation.
use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use iroh::EndpointAddr; use iroh::EndpointAddr;
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize as DeriveSerialize}; use serde::{Deserialize, Serialize as DeriveSerialize};
use swactor::Error;
use swactor::actor::ActorInterface; use swactor::actor::ActorInterface;
use swactor::runtime::Ctx; use swactor::runtime::Ctx;
use swactor_transport::{Codec, CodecRegistry, NetworkMessage}; use swactor_transport::{CodecRegistry, JsonCodec, NetworkMessage};
use crate::{DatastreamEndpoint, DatastreamSubscription, SubscriptionRequest}; use crate::{DatastreamEndpoint, DatastreamSubscription, SubscriptionRequest};
@ -80,22 +76,7 @@ impl ActorInterface for DatastreamPublisherActor {
/// Register JSON encoding for remote datastream publisher messages. /// Register JSON encoding for remote datastream publisher messages.
pub fn register_datastream_publisher_codec(registry: &mut CodecRegistry) { pub fn register_datastream_publisher_codec(registry: &mut CodecRegistry) {
registry.register::<DatastreamPublisherMsg, JsonCodec<DatastreamPublisherMsg>>(JsonCodec( registry.register::<DatastreamPublisherMsg, _>(
PhantomData, JsonCodec::<DatastreamPublisherMsg>::default(),
)); );
}
struct JsonCodec<M>(PhantomData<M>);
impl<M> Codec<M> for JsonCodec<M>
where
M: Serialize + DeserializeOwned + Clone + Send + Sync + 'static,
{
fn encode(&self, msg: &M) -> Result<Vec<u8>, Error> {
serde_json::to_vec(msg).map_err(|e| Error::from(format!("encode: {e}")))
}
fn decode(&self, bytes: &[u8]) -> Result<M, Error> {
serde_json::from_slice(bytes).map_err(|e| Error::from(format!("decode: {e}")))
}
} }

View file

@ -1,9 +1,9 @@
//! Protocol messages for SWIM membership and the standalone gossip frames //! Protocol messages for SWIM membership and the standalone gossip frames
//! (registry, node metadata, directory), plus their JSON codec. //! (registry, node metadata, directory), plus codec registration.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use swactor::Error; use swactor::Error;
use swactor_transport::{Codec, CodecRegistry, NetworkMessage}; use swactor_transport::{CodecRegistry, JsonCodec, NetworkMessage};
use crate::node_metadata::NodeMetadataEntry; use crate::node_metadata::NodeMetadataEntry;
use crate::registry::RegistryEntry; use crate::registry::RegistryEntry;
@ -164,52 +164,21 @@ impl NetworkMessage for DirectoryGossip {
} }
} }
// ─── JSON Codec ─────────────────────────────────────────────────────────────
/// JSON codec for distribution protocol messages.
///
/// Using JSON for simplicity and debuggability. Can be swapped for
/// bincode/msgpack in production via the Codec trait.
pub struct JsonCodec;
macro_rules! impl_json_codec {
($ty:ty) => {
impl Codec<$ty> for JsonCodec {
fn encode(&self, msg: &$ty) -> Result<Vec<u8>, Error> {
serde_json::to_vec(msg).map_err(|e| Error::from(format!("encode: {e}")))
}
fn decode(&self, bytes: &[u8]) -> Result<$ty, Error> {
serde_json::from_slice(bytes).map_err(|e| Error::from(format!("decode: {e}")))
}
}
};
}
impl_json_codec!(Ping);
impl_json_codec!(Ack);
impl_json_codec!(PingReq);
impl_json_codec!(IndirectAck);
impl_json_codec!(JoinRequest);
impl_json_codec!(JoinResponse);
impl_json_codec!(RegistryGossip);
impl_json_codec!(MetadataGossip);
impl_json_codec!(DirectoryGossip);
/// Build a `CodecRegistry` with all distribution protocol messages registered. /// Build a `CodecRegistry` with all distribution protocol messages registered.
pub fn distribution_codec_registry() -> CodecRegistry { pub fn distribution_codec_registry() -> CodecRegistry {
let mut cr = CodecRegistry::new(); let mut cr = CodecRegistry::new();
cr.register::<Ping, _>(JsonCodec); cr.register::<Ping, _>(JsonCodec::<Ping>::default());
cr.register::<Ack, _>(JsonCodec); cr.register::<Ack, _>(JsonCodec::<Ack>::default());
cr.register::<PingReq, _>(JsonCodec); cr.register::<PingReq, _>(JsonCodec::<PingReq>::default());
// §6.1 / §14.5: `IndirectAck` is folded into the shared registry so all six // §6.1 / §14.5: `IndirectAck` is folded into the shared registry so all six
// SWIM message types decode through one uniform path; concrete drivers no // SWIM message types decode through one uniform path; concrete drivers no
// longer need to hand-dispatch it by tag. // longer need to hand-dispatch it by tag.
cr.register::<IndirectAck, _>(JsonCodec); cr.register::<IndirectAck, _>(JsonCodec::<IndirectAck>::default());
cr.register::<JoinRequest, _>(JsonCodec); cr.register::<JoinRequest, _>(JsonCodec::<JoinRequest>::default());
cr.register::<JoinResponse, _>(JsonCodec); cr.register::<JoinResponse, _>(JsonCodec::<JoinResponse>::default());
cr.register::<RegistryGossip, _>(JsonCodec); cr.register::<RegistryGossip, _>(JsonCodec::<RegistryGossip>::default());
cr.register::<MetadataGossip, _>(JsonCodec); cr.register::<MetadataGossip, _>(JsonCodec::<MetadataGossip>::default());
cr.register::<DirectoryGossip, _>(JsonCodec); cr.register::<DirectoryGossip, _>(JsonCodec::<DirectoryGossip>::default());
cr cr
} }

View file

@ -1,25 +1,31 @@
#![allow(dead_code)] //! Driver edge/ring/stream bookkeeping.
//!
//! The driver is the node's swactor-to-iroh boundary. It tracks which edges
//! have an established send or recv pump, maps inbound uni-streams to their
//! recv rings, and emits driver lifecycle events (edge ready, stream fault,
//! pump stopped). This module is the pure state machine; the iroh stream pumps
//! themselves live in [`crate::edge_transport`].
use std::collections::BTreeMap; use std::collections::BTreeMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct EdgeId(pub(crate) u64); pub struct EdgeId(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct RingId(pub(crate) u64); pub struct RingId(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct StreamId(pub(crate) u64); pub struct StreamId(pub u64);
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum DriverEventOut { pub enum DriverEventOut {
DriverEdgeReady { edge_id: EdgeId }, DriverEdgeReady { edge_id: EdgeId },
StreamFault { edge_id: EdgeId }, StreamFault { edge_id: EdgeId },
PumpStopped { edge_id: EdgeId, ring_id: RingId }, PumpStopped { edge_id: EdgeId, ring_id: RingId },
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Driver { pub struct Driver {
sends: BTreeMap<EdgeId, RingId>, sends: BTreeMap<EdgeId, RingId>,
recv_specs: BTreeMap<EdgeId, RingId>, recv_specs: BTreeMap<EdgeId, RingId>,
pending_streams: BTreeMap<EdgeId, StreamId>, pending_streams: BTreeMap<EdgeId, StreamId>,
@ -28,7 +34,7 @@ pub(crate) struct Driver {
} }
impl Driver { impl Driver {
pub(crate) fn new() -> Self { pub fn new() -> Self {
Self { Self {
sends: BTreeMap::new(), sends: BTreeMap::new(),
recv_specs: BTreeMap::new(), recv_specs: BTreeMap::new(),
@ -38,20 +44,20 @@ impl Driver {
} }
} }
pub(crate) fn establish_send(&mut self, edge_id: EdgeId, ring_id: RingId) { pub fn establish_send(&mut self, edge_id: EdgeId, ring_id: RingId) {
self.sends.insert(edge_id, ring_id); self.sends.insert(edge_id, ring_id);
self.events self.events
.push(DriverEventOut::DriverEdgeReady { edge_id }); .push(DriverEventOut::DriverEdgeReady { edge_id });
} }
pub(crate) fn establish_recv(&mut self, edge_id: EdgeId, ring_id: RingId) { pub fn establish_recv(&mut self, edge_id: EdgeId, ring_id: RingId) {
self.recv_specs.insert(edge_id, ring_id); self.recv_specs.insert(edge_id, ring_id);
if let Some(stream_id) = self.pending_streams.remove(&edge_id) { if let Some(stream_id) = self.pending_streams.remove(&edge_id) {
self.spawn_recv(edge_id, stream_id); self.spawn_recv(edge_id, stream_id);
} }
} }
pub(crate) fn incoming_uni_stream(&mut self, edge_id: EdgeId, stream_id: StreamId) { pub fn incoming_uni_stream(&mut self, edge_id: EdgeId, stream_id: StreamId) {
if self.recv_specs.contains_key(&edge_id) { if self.recv_specs.contains_key(&edge_id) {
self.spawn_recv(edge_id, stream_id); self.spawn_recv(edge_id, stream_id);
} else { } else {
@ -69,11 +75,11 @@ impl Driver {
.push(DriverEventOut::DriverEdgeReady { edge_id }); .push(DriverEventOut::DriverEdgeReady { edge_id });
} }
pub(crate) fn read_error(&mut self, edge_id: EdgeId) { pub fn read_error(&mut self, edge_id: EdgeId) {
self.events.push(DriverEventOut::StreamFault { edge_id }); self.events.push(DriverEventOut::StreamFault { edge_id });
} }
pub(crate) fn stop_edge(&mut self, edge_id: EdgeId) { pub fn stop_edge(&mut self, edge_id: EdgeId) {
let ring_id = self let ring_id = self
.sends .sends
.get(&edge_id) .get(&edge_id)
@ -89,7 +95,8 @@ impl Driver {
self.events self.events
.push(DriverEventOut::PumpStopped { edge_id, ring_id }); .push(DriverEventOut::PumpStopped { edge_id, ring_id });
} }
pub(crate) fn events(&self) -> &[DriverEventOut] {
pub fn events(&self) -> &[DriverEventOut] {
&self.events &self.events
} }
} }

View file

@ -1,18 +1,29 @@
//! Endpoint advertisement masking.
//!
//! Controls how much of an iroh [`EndpointAddr`] a node advertises to peers.
//! `relay-only` strips direct IP/socket addresses so peers can only reach the
//! node via its relay URL — useful for NAT-egress-only or hidden nodes.
use std::fmt; use std::fmt;
use iroh::EndpointAddr; use iroh::EndpointAddr;
pub(crate) const MVP_IROH_ENDPOINT_ADDR_MASK_ENV: &str = "MVP_IROH_ENDPOINT_ADDR_MASK"; /// Environment variable selecting the advertised endpoint address mask.
///
/// Recognized values: `full` (default) and `relay-only`.
pub const MVP_IROH_ENDPOINT_ADDR_MASK_ENV: &str = "MVP_IROH_ENDPOINT_ADDR_MASK";
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) enum EndpointAddrMask { pub enum EndpointAddrMask {
/// Advertise the full endpoint address: relays and direct addresses.
#[default] #[default]
Full, Full,
/// Advertise relay URLs only, omitting direct socket addresses.
RelayOnly, RelayOnly,
} }
impl EndpointAddrMask { impl EndpointAddrMask {
pub(crate) fn parse(value: &str) -> Result<Self, String> { pub fn parse(value: &str) -> Result<Self, String> {
match value.trim().to_ascii_lowercase().as_str() { match value.trim().to_ascii_lowercase().as_str() {
"" | "full" | "none" => Ok(Self::Full), "" | "full" | "none" => Ok(Self::Full),
"relay-only" | "relay_only" | "relay" => Ok(Self::RelayOnly), "relay-only" | "relay_only" | "relay" => Ok(Self::RelayOnly),
@ -22,14 +33,14 @@ impl EndpointAddrMask {
} }
} }
pub(crate) fn as_str(self) -> &'static str { pub fn as_str(self) -> &'static str {
match self { match self {
Self::Full => "full", Self::Full => "full",
Self::RelayOnly => "relay-only", Self::RelayOnly => "relay-only",
} }
} }
pub(crate) fn requires_relay(self) -> bool { pub fn requires_relay(self) -> bool {
matches!(self, Self::RelayOnly) matches!(self, Self::RelayOnly)
} }
} }
@ -40,7 +51,8 @@ impl fmt::Display for EndpointAddrMask {
} }
} }
pub(crate) fn advertised_endpoint( /// Apply `mask` to `endpoint`, returning the address to advertise to peers.
pub fn advertised_endpoint(
endpoint: EndpointAddr, endpoint: EndpointAddr,
mask: EndpointAddrMask, mask: EndpointAddrMask,
) -> Result<EndpointAddr, String> { ) -> Result<EndpointAddr, String> {

View file

@ -5,13 +5,18 @@
//! wire message definitions. //! wire message definitions.
pub mod datastream_transport; pub mod datastream_transport;
pub mod driver_pumps;
pub mod edge_transport; pub mod edge_transport;
pub mod endpoint_advertisement;
pub mod iroh_driver; pub mod iroh_driver;
pub use iroh_driver::{ pub use iroh_driver::{
ConnType, DatastreamPublishHandle, IrohDriver, IrohDriverConfig, JoinPhase, JoinStatus, ConnType, DatastreamPublishHandle, IrohDriver, IrohDriverConfig, JoinPhase, JoinStatus,
conn_type_of, discover_lan_ips, conn_type_of, discover_lan_ips,
}; };
pub use endpoint_advertisement::{
MVP_IROH_ENDPOINT_ADDR_MASK_ENV, EndpointAddrMask, advertised_endpoint,
};
pub use edge_transport::{EDGE_ALPN, EdgeSendHandle, EdgeTransportEvent, EdgeTransportFault}; pub use edge_transport::{EDGE_ALPN, EdgeSendHandle, EdgeTransportEvent, EdgeTransportFault};

View file

@ -1,8 +1,8 @@
//! Behavior guarantees for the `transport` module. //! Behavior guarantees for endpoint advertisement masking.
use std::net::{Ipv4Addr, SocketAddr}; use std::net::{Ipv4Addr, SocketAddr};
use mvp_system::transport::endpoint_advertisement::{EndpointAddrMask, advertised_endpoint}; use iroh_driver::{EndpointAddrMask, advertised_endpoint};
#[test] #[test]
fn relay_only_mask_preserves_relay_urls_and_removes_direct_addresses() { fn relay_only_mask_preserves_relay_urls_and_removes_direct_addresses() {

View file

@ -30,7 +30,7 @@ use crate::node_provisioning::{ProviderKind, provider_kind};
use crate::observability::{benchmark, frame_archive::FrameArchive}; use crate::observability::{benchmark, frame_archive::FrameArchive};
use crate::orchestration::config::ResolvedVastAiConfig; use crate::orchestration::config::ResolvedVastAiConfig;
use crate::prompt::rpc::{PromptEvent, SubmitPrompt, write_json_line}; use crate::prompt::rpc::{PromptEvent, SubmitPrompt, write_json_line};
use crate::transport::endpoint_advertisement::EndpointAddrMask; use iroh_driver::EndpointAddrMask;
use crate::{ use crate::{
DEFAULT_PIPELINE_CACHED_MODEL_FILE, DEFAULT_PIPELINE_CACHED_MODEL_ID, DEFAULT_PIPELINE_CACHED_MODEL_FILE, DEFAULT_PIPELINE_CACHED_MODEL_ID,
DEFAULT_PIPELINE_CACHED_MODEL_MAX_CONTEXT, DEFAULT_PIPELINE_CACHED_MODEL_REPO, DEFAULT_PIPELINE_CACHED_MODEL_MAX_CONTEXT, DEFAULT_PIPELINE_CACHED_MODEL_REPO,

View file

@ -0,0 +1,13 @@
//! Registers wire codecs for every MVP actor message type.
//!
//! The runtime's [`CodecRegistry`](swactor_transport::CodecRegistry) needs an
//! encoder/decoder entry for each inter-node message type. This is the single
//! aggregator that wires up the node, orchestrator, prompt, and datastream
//! publisher codecs.
pub(crate) fn register_mvp_actor_codecs(registry: &mut swactor_transport::CodecRegistry) {
crate::node_actor::register_codecs(registry);
crate::orchestration::actor::register_codecs(registry);
datastream::register_datastream_publisher_codec(registry);
crate::prompt::rpc::register_codecs(registry);
}

View file

@ -26,8 +26,6 @@ pub fn run_worker_node_from_env() -> std::process::ExitCode {
node::worker_node_runtime::run_from_env() node::worker_node_runtime::run_from_env()
} }
#[path = "transport/driver_pumps.rs"]
mod driver_pumps;
#[path = "staging/gguf_common.rs"] #[path = "staging/gguf_common.rs"]
mod gguf_common; mod gguf_common;
#[path = "staging/gguf_shard.rs"] #[path = "staging/gguf_shard.rs"]
@ -50,7 +48,7 @@ mod observability;
mod orchestration; mod orchestration;
mod prompt; mod prompt;
mod staging; mod staging;
mod transport; mod codecs;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View file

@ -9,7 +9,7 @@ use crate::run_plan;
use crate::staging as stage; use crate::staging as stage;
use crate::orchestration::actor::OrchestratorMsg; use crate::orchestration::actor::OrchestratorMsg;
use crate::transport::json_codec::JsonCodec; use swactor_transport::JsonCodec;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) enum StageEdgeKindWire { pub(crate) enum StageEdgeKindWire {

View file

@ -22,7 +22,7 @@ use datastream::{
Lifetime, NodeId, Record, StreamDescriptor, StreamId, StreamOrigin, Lifetime, NodeId, Record, StreamDescriptor, StreamId, StreamOrigin,
}; };
use crate::driver_pumps as driver_model; use iroh_driver::driver_pumps as driver_model;
use crate::gguf_shard::{StageShardPlan, materialize_stage_shard_http, validate_stage_shard_cache}; use crate::gguf_shard::{StageShardPlan, materialize_stage_shard_http, validate_stage_shard_cache};
use crate::node_actor::{ use crate::node_actor::{
NodeAgentActor, NodeAgentMsg, NodeAgentReport, StageCommandWire, StageInboundEdgeWire, NodeAgentActor, NodeAgentMsg, NodeAgentReport, StageCommandWire, StageInboundEdgeWire,
@ -34,10 +34,10 @@ use crate::orchestration::provider_adapters::relay::relay_runtime_config_from_en
use crate::prompt::rpc::{PromptEvent, TokenizerEvent}; use crate::prompt::rpc::{PromptEvent, TokenizerEvent};
use crate::run_plan::{GgufSource, TokenizerSource}; use crate::run_plan::{GgufSource, TokenizerSource};
use crate::staging::control as stage; use crate::staging::control as stage;
use crate::transport::endpoint_advertisement::{ use iroh_driver::{
EndpointAddrMask, MVP_IROH_ENDPOINT_ADDR_MASK_ENV, advertised_endpoint, EndpointAddrMask, MVP_IROH_ENDPOINT_ADDR_MASK_ENV, advertised_endpoint,
}; };
use crate::transport::register_mvp_actor_codecs; use crate::codecs::register_mvp_actor_codecs;
use data_plane::arena; use data_plane::arena;
use data_plane::edge_lifecycle as edge; use data_plane::edge_lifecycle as edge;
use data_plane::ingress; use data_plane::ingress;
@ -1630,12 +1630,13 @@ fn run_stage_shard_fetcher() -> Result<(), String> {
fn run() -> Result<(), String> { fn run() -> Result<(), String> {
let config = DeploymentConfig::from_env()?; let config = DeploymentConfig::from_env()?;
emit_stdio_node_event( let boot = |phase: &str, status: &str, detail: Value| {
&config, emit_stdio_node_event(&config, NODE_BOOTSTRAP_CHANNEL, phase, status, detail)
NODE_BOOTSTRAP_CHANNEL, };
"config", let worker_evt = |phase: &str, status: &str, detail: Value| {
"ready", emit_stdio_node_event(&config, NODE_WORKER_CHANNEL, phase, status, detail)
json!({ };
boot("config", "ready", json!({
"worker_script":&config.worker_script, "worker_script":&config.worker_script,
"device":&config.device, "device":&config.device,
"model_id":&config.model_id, "model_id":&config.model_id,
@ -1645,35 +1646,16 @@ fn run() -> Result<(), String> {
"arena_bytes":config.arena_bytes, "arena_bytes":config.arena_bytes,
"arena_alignment":config.arena_alignment, "arena_alignment":config.arena_alignment,
"debug_join_socket":config.debug_join_socket.as_deref().unwrap_or("disabled"), "debug_join_socket":config.debug_join_socket.as_deref().unwrap_or("disabled"),
}), }))?;
)?; boot("process", "started", json!({"binary":"mvp-worker-node","pid":std::process::id()}))?;
emit_stdio_node_event(
&config,
NODE_BOOTSTRAP_CHANNEL,
"process",
"started",
json!({"binary":"mvp-worker-node","pid":std::process::id()}),
)?;
let tokio = match tokio::runtime::Runtime::new() { let tokio = match tokio::runtime::Runtime::new() {
Ok(runtime) => { Ok(runtime) => {
emit_stdio_node_event( boot("tokio_runtime", "ready", json!({"runtime":"tokio"}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"tokio_runtime",
"ready",
json!({"runtime":"tokio"}),
)?;
runtime runtime
} }
Err(error) => { Err(error) => {
emit_stdio_node_event( boot("tokio_runtime", "failed", json!({"error":error.to_string()}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"tokio_runtime",
"failed",
json!({"error":error.to_string()}),
)?;
return Err(format!("tokio runtime: {error}")); return Err(format!("tokio runtime: {error}"));
} }
}; };
@ -1689,42 +1671,18 @@ fn run() -> Result<(), String> {
) { ) {
Ok(driver) => driver, Ok(driver) => driver,
Err(error) => { Err(error) => {
emit_stdio_node_event( boot("iroh_driver", "failed", json!({"error":error.to_string()}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"iroh_driver",
"failed",
json!({"error":error.to_string()}),
)?;
return Err(format!("create iroh driver: {error}")); return Err(format!("create iroh driver: {error}"));
} }
}; };
let advertised_self_endpoint = let advertised_self_endpoint =
advertised_endpoint(driver.endpoint_addr(), config.endpoint_addr_mask)?; advertised_endpoint(driver.endpoint_addr(), config.endpoint_addr_mask)?;
emit_stdio_node_event( boot("iroh_driver", "ready", json!({"endpoint":advertised_self_endpoint.clone(),"has_relay":advertised_self_endpoint.relay_urls().next().is_some(),"direct_addr_count":advertised_self_endpoint.ip_addrs().count(),"relay_mode":format!("{:?}", config.relay_mode),"endpoint_addr_mask":config.endpoint_addr_mask.as_str()}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"iroh_driver",
"ready",
json!({"endpoint":advertised_self_endpoint.clone(),"has_relay":advertised_self_endpoint.relay_urls().next().is_some(),"direct_addr_count":advertised_self_endpoint.ip_addrs().count(),"relay_mode":format!("{:?}", config.relay_mode),"endpoint_addr_mask":config.endpoint_addr_mask.as_str()}),
)?;
if let Some(coordinator) = &config.coordinator_endpoint { if let Some(coordinator) = &config.coordinator_endpoint {
driver.join(std::slice::from_ref(coordinator)); driver.join(std::slice::from_ref(coordinator));
emit_stdio_node_event( boot("coordinator_join", "started", json!({"endpoint":coordinator,"has_relay":coordinator.relay_urls().next().is_some(),"direct_addr_count":coordinator.ip_addrs().count()}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"coordinator_join",
"started",
json!({"endpoint":coordinator,"has_relay":coordinator.relay_urls().next().is_some(),"direct_addr_count":coordinator.ip_addrs().count()}),
)?;
} else { } else {
emit_stdio_node_event( boot("coordinator_join", "skipped", json!({"reason":"MVP_COORDINATOR_ENDPOINT not set","mode":"standalone"}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"coordinator_join",
"skipped",
json!({"reason":"MVP_COORDINATOR_ENDPOINT not set","mode":"standalone"}),
)?;
} }
let stack = DistributionRuntimeStack::new_with_codecs( let stack = DistributionRuntimeStack::new_with_codecs(
@ -1735,20 +1693,8 @@ fn run() -> Result<(), String> {
datastream::wire::register_datastream_codec(registry); datastream::wire::register_datastream_codec(registry);
}, },
); );
emit_stdio_node_event( boot("distribution_stack", "ready", json!({"actors":"initialized","route_view":"initialized","swim":"initialized","outbox":"initialized"}))?;
&config, boot("codecs", "ready", json!({"registered":["node_agent","orchestrator","provisioner","prompt_rpc","datastream"]}))?;
NODE_BOOTSTRAP_CHANNEL,
"distribution_stack",
"ready",
json!({"actors":"initialized","route_view":"initialized","swim":"initialized","outbox":"initialized"}),
)?;
emit_stdio_node_event(
&config,
NODE_BOOTSTRAP_CHANNEL,
"codecs",
"ready",
json!({"registered":["node_agent","orchestrator","provisioner","prompt_rpc","datastream"]}),
)?;
driver.enable_actor_bridge( driver.enable_actor_bridge(
stack.runtime.clone(), stack.runtime.clone(),
stack.codec.clone(), stack.codec.clone(),
@ -1757,13 +1703,7 @@ fn run() -> Result<(), String> {
stack.relay_mirror.clone(), stack.relay_mirror.clone(),
stack.route_view.clone(), stack.route_view.clone(),
); );
emit_stdio_node_event( boot("actor_bridge", "ready", json!({"transport":"iroh","routes":"attached"}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"actor_bridge",
"ready",
json!({"transport":"iroh","routes":"attached"}),
)?;
let arena_manager = match arena::ArenaManager::boot(arena::ArenaConfig { let arena_manager = match arena::ArenaManager::boot(arena::ArenaConfig {
node_id: arena::NodeId(config.logical_node_id), node_id: arena::NodeId(config.logical_node_id),
@ -1771,26 +1711,14 @@ fn run() -> Result<(), String> {
base_alignment: config.arena_alignment, base_alignment: config.arena_alignment,
}) { }) {
Ok(manager) => { Ok(manager) => {
emit_stdio_node_event( boot("arena_manager", "ready", json!({
&config,
NODE_BOOTSTRAP_CHANNEL,
"arena_manager",
"ready",
json!({
"arena_bytes":config.arena_bytes, "arena_bytes":config.arena_bytes,
"arena_alignment":config.arena_alignment, "arena_alignment":config.arena_alignment,
}), }))?;
)?;
Arc::new(Mutex::new(manager)) Arc::new(Mutex::new(manager))
} }
Err(error) => { Err(error) => {
emit_stdio_node_event( boot("arena_manager", "failed", json!({"error":format!("{error:?}")}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"arena_manager",
"failed",
json!({"error":format!("{error:?}")}),
)?;
return Err(format!("boot arena manager: {error:?}")); return Err(format!("boot arena manager: {error:?}"));
} }
}; };
@ -1917,23 +1845,11 @@ fn run() -> Result<(), String> {
let reports = match stack.runtime.new_inbox::<NodeAgentReport>() { let reports = match stack.runtime.new_inbox::<NodeAgentReport>() {
Ok(inbox) => { Ok(inbox) => {
emit_stdio_node_event( boot("node_report_inbox", "ready", json!({"actor":inbox.addr()}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"node_report_inbox",
"ready",
json!({"actor":inbox.addr()}),
)?;
inbox inbox
} }
Err(error) => { Err(error) => {
emit_stdio_node_event( boot("node_report_inbox", "failed", json!({"error":error.to_string()}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"node_report_inbox",
"failed",
json!({"error":error.to_string()}),
)?;
return Err(format!("node report inbox: {error}")); return Err(format!("node report inbox: {error}"));
} }
}; };
@ -1941,13 +1857,7 @@ fn run() -> Result<(), String> {
"MVP_ORCHESTRATOR_ACTOR is required for runtime readiness signaling".to_owned() "MVP_ORCHESTRATOR_ACTOR is required for runtime readiness signaling".to_owned()
})?; })?;
let orchestrator_source = "env"; let orchestrator_source = "env";
emit_stdio_node_event( boot("orchestrator_actor", "ready", json!({"actor":orchestrator,"source":orchestrator_source}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"orchestrator_actor",
"ready",
json!({"actor":orchestrator,"source":orchestrator_source}),
)?;
let node_agent = NodeAgentActor::new( let node_agent = NodeAgentActor::new(
stage::NodeId(config.logical_node_id), stage::NodeId(config.logical_node_id),
orchestrator, orchestrator,
@ -1955,59 +1865,29 @@ fn run() -> Result<(), String> {
); );
let node_actor = match stack.runtime.spawn(node_agent) { let node_actor = match stack.runtime.spawn(node_agent) {
Ok(actor) => { Ok(actor) => {
emit_stdio_node_event( boot("node_agent", "ready", json!({"node_actor":actor,"source":"generated"}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"node_agent",
"ready",
json!({"node_actor":actor,"source":"generated"}),
)?;
actor actor
} }
Err(error) => { Err(error) => {
emit_stdio_node_event( boot("node_agent", "failed", json!({"error":error.to_string(),"source":"generated"}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"node_agent",
"failed",
json!({"error":error.to_string(),"source":"generated"}),
)?;
return Err(format!("spawn node agent: {error}")); return Err(format!("spawn node agent: {error}"));
} }
}; };
stack.register_local_actor(driver.register_actor(node_actor, 1)); stack.register_local_actor(driver.register_actor(node_actor, 1));
emit_stdio_node_event( boot("node_actor_registration", "ready", json!({"node_actor":node_actor,"network_reachable":true}))?;
&config,
NODE_BOOTSTRAP_CHANNEL,
"node_actor_registration",
"ready",
json!({"node_actor":node_actor,"network_reachable":true}),
)?;
emit_stdio_node_event( worker_evt("worker_process", "started", json!({
&config,
NODE_WORKER_CHANNEL,
"worker_process",
"started",
json!({
"program":"python3", "program":"python3",
"script":&config.worker_script, "script":&config.worker_script,
"device":&config.device, "device":&config.device,
"stdin":"piped", "stdin":"piped",
"stdout":"piped", "stdout":"piped",
"stderr":"piped", "stderr":"piped",
}), }))?;
)?;
let mut worker = match TinygradWorker::spawn(&config, arena_fd) { let mut worker = match TinygradWorker::spawn(&config, arena_fd) {
Ok(worker) => worker, Ok(worker) => worker,
Err(error) => { Err(error) => {
emit_stdio_node_event( worker_evt("worker_process", "failed", json!({"error":error}))?;
&config,
NODE_WORKER_CHANNEL,
"worker_process",
"failed",
json!({"error":error}),
)?;
return Err(error); return Err(error);
} }
}; };
@ -2019,30 +1899,12 @@ fn run() -> Result<(), String> {
sampler_health_context, sampler_health_context,
vec![std::process::id(), worker.pid()], vec![std::process::id(), worker.pid()],
); );
emit_stdio_node_event( worker_evt("worker_initialize", "started", json!({"command":"InitializeWorker","helper_abi_version":1,"device":&config.device}))?;
&config,
NODE_WORKER_CHANNEL,
"worker_initialize",
"started",
json!({"command":"InitializeWorker","helper_abi_version":1,"device":&config.device}),
)?;
let mut initial_pump = || {}; let mut initial_pump = || {};
match worker.initialize(&config.device, &config, &mut datastream, &mut initial_pump) { match worker.initialize(&config.device, &config, &mut datastream, &mut initial_pump) {
Ok(()) => emit_stdio_node_event( Ok(()) => worker_evt("worker_initialize", "ready", json!({"worker_event_type":"WorkerReady"}))?,
&config,
NODE_WORKER_CHANNEL,
"worker_initialize",
"ready",
json!({"worker_event_type":"WorkerReady"}),
)?,
Err(error) => { Err(error) => {
emit_stdio_node_event( worker_evt("worker_initialize", "failed", json!({"error":error}))?;
&config,
NODE_WORKER_CHANNEL,
"worker_initialize",
"failed",
json!({"error":error}),
)?;
return Err(error); return Err(error);
} }
} }
@ -2063,19 +1925,13 @@ fn run() -> Result<(), String> {
"logical_node_id": config.logical_node_id, "logical_node_id": config.logical_node_id,
"stage_index": config.stage_index, "stage_index": config.stage_index,
}); });
emit_stdio_node_event( boot("runtime_ready_local", "ready", json!({
&config,
NODE_BOOTSTRAP_CHANNEL,
"runtime_ready_local",
"ready",
json!({
"endpoint":advertised_self_endpoint.clone(), "endpoint":advertised_self_endpoint.clone(),
"node_actor":node_actor, "node_actor":node_actor,
"logical_node_id":config.logical_node_id, "logical_node_id":config.logical_node_id,
"stage_index":config.stage_index, "stage_index":config.stage_index,
"readiness_id":pending_runtime_ready.readiness_id, "readiness_id":pending_runtime_ready.readiness_id,
}), }))?;
)?;
if let Some(prompt) = &config.self_test_prompt { if let Some(prompt) = &config.self_test_prompt {
run_self_test( run_self_test(

View file

@ -6,7 +6,7 @@ use swactor_transport::{CodecRegistry, NetworkMessage};
use crate::run_fsm as core; use crate::run_fsm as core;
use crate::transport::json_codec::JsonCodec; use swactor_transport::JsonCodec;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct StageRefWire { pub(crate) struct StageRefWire {

View file

@ -20,7 +20,7 @@ use crate::observability::dashboard_view::MvpClusterDashboardView;
use crate::observability::{benchmark, frame_archive::FrameArchive}; use crate::observability::{benchmark, frame_archive::FrameArchive};
use crate::orchestration::actor::{OrchestratorActor, OrchestratorReport}; use crate::orchestration::actor::{OrchestratorActor, OrchestratorReport};
use crate::orchestration::config::{DEFAULT_CONFIG_PATH, TomlConfigOverlay}; use crate::orchestration::config::{DEFAULT_CONFIG_PATH, TomlConfigOverlay};
use crate::transport::register_mvp_actor_codecs; use crate::codecs::register_mvp_actor_codecs;
const PROVIDER_START_MAX_ATTEMPTS: usize = 4; const PROVIDER_START_MAX_ATTEMPTS: usize = 4;
use crate::gguf_shard::{StageShardPlan, plan_stage_shard}; use crate::gguf_shard::{StageShardPlan, plan_stage_shard};
@ -48,7 +48,7 @@ use crate::provisioning::{
}; };
use crate::run_fsm::{RunConfig, RunId}; use crate::run_fsm::{RunConfig, RunId};
use crate::run_plan::{self, GgufSource, TokenizerSource}; use crate::run_plan::{self, GgufSource, TokenizerSource};
use crate::transport::endpoint_advertisement::{ use iroh_driver::{
EndpointAddrMask, MVP_IROH_ENDPOINT_ADDR_MASK_ENV, advertised_endpoint, EndpointAddrMask, MVP_IROH_ENDPOINT_ADDR_MASK_ENV, advertised_endpoint,
}; };
use data_plane::object_record as ingress; use data_plane::object_record as ingress;

View file

@ -3,7 +3,7 @@ use std::io::{BufRead, Write};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use swactor_transport::{CodecRegistry, NetworkMessage}; use swactor_transport::{CodecRegistry, NetworkMessage};
use crate::transport::json_codec::JsonCodec; use swactor_transport::JsonCodec;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct SubmitPrompt { pub(crate) struct SubmitPrompt {

View file

@ -5,4 +5,3 @@ mod observability_guarantees;
mod orchestration_guarantees; mod orchestration_guarantees;
mod prompt_guarantees; mod prompt_guarantees;
mod staging_guarantees; mod staging_guarantees;
mod transport_guarantees;

View file

@ -1,11 +0,0 @@
//! MVP edge transport public surface.
pub(crate) fn register_mvp_actor_codecs(registry: &mut swactor_transport::CodecRegistry) {
crate::node_actor::register_codecs(registry);
crate::orchestration::actor::register_codecs(registry);
datastream::register_datastream_publisher_codec(registry);
crate::prompt::rpc::register_codecs(registry);
}
pub(crate) mod endpoint_advertisement;
pub(crate) mod json_codec;

View file

@ -1,11 +1,22 @@
//! Generic JSON codec for serde message types.
//!
//! [`JsonCodec<M>`] is the out-of-the-box [`Codec`] for messages that are
//! [`Serialize`] + [`DeserializeOwned`]. Use a custom codec when a wire format
//! needs schema stability or compactness beyond JSON.
use std::marker::PhantomData; use std::marker::PhantomData;
use serde::Serialize; use serde::Serialize;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use swactor::Error; use swactor::Error;
use swactor_transport::Codec;
pub(crate) struct JsonCodec<M>(PhantomData<M>); use crate::codec::Codec;
/// JSON codec for message type `M`.
///
/// Implements [`Codec<M>`] via `serde_json`. Construct with
/// [`JsonCodec::default`] / `JsonCodec::<M>::default()`.
pub struct JsonCodec<M>(PhantomData<M>);
impl<M> Default for JsonCodec<M> { impl<M> Default for JsonCodec<M> {
fn default() -> Self { fn default() -> Self {

View file

@ -8,9 +8,11 @@
pub mod codec; pub mod codec;
pub mod crypto; pub mod crypto;
pub mod identity; pub mod identity;
pub mod json_codec;
pub mod transport; pub mod transport;
pub use codec::{ pub use codec::{
hex_decode, hex_encode, Codec, CodecRegistry, NetworkMessage, NodeId, WireEnvelope, hex_decode, hex_encode, Codec, CodecRegistry, NetworkMessage, NodeId, WireEnvelope,
}; };
pub use json_codec::JsonCodec;
pub use transport::{CodecRemoteSink, InMemoryTransport, Transport, TransportRouter}; pub use transport::{CodecRemoteSink, InMemoryTransport, Transport, TransportRouter};