use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::Path;
use std::process::{Child, Command, Stdio};
use std::sync::{Arc, Mutex, OnceLock};
use std::time::{Duration, Instant};
use tokio::io::copy_bidirectional;
use tokio::net::{TcpListener, TcpStream};
use tokio::runtime::Handle;
use ghostlink_core::cluster::{ClusterState, NodeStatus};
type HmacSha256 = Hmac<Sha256>;
const RPC_AUTH_PORT_OFFSET: u16 = 2000;
const RPC_ADMISSION_TTL: Duration = Duration::from_secs(30);
const RPC_AUTH_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(5);
const RPC_AUTH_CLIENT_TIMEOUT: Duration = Duration::from_secs(3);
const RPC_NONCE_LEN: usize = 16;
const RPC_MAC_LEN: usize = 32;
fn compute_hmac(secret: &str, nonce: &[u8]) -> Vec<u8> {
let mut mac = HmacSha256::new_from_slice(secret.as_bytes())
.expect("HMAC-SHA256 accepts a key of any length, per RFC 2104");
mac.update(nonce);
mac.finalize().into_bytes().to_vec()
}
static RPC_ADMISSIONS: OnceLock<Arc<Mutex<HashMap<IpAddr, Instant>>>> = OnceLock::new();
fn admissions_registry() -> Arc<Mutex<HashMap<IpAddr, Instant>>> {
RPC_ADMISSIONS
.get_or_init(|| Arc::new(Mutex::new(HashMap::new())))
.clone()
}
fn admission_is_valid(expiry: Instant, now: Instant) -> bool {
now < expiry
}
fn admit_peer(ip: IpAddr) {
let registry = admissions_registry();
let mut guard = registry.lock().unwrap_or_else(|p| p.into_inner());
guard.insert(ip, Instant::now() + RPC_ADMISSION_TTL);
}
fn is_admitted(ip: &IpAddr) -> bool {
let registry = admissions_registry();
let mut guard = registry.lock().unwrap_or_else(|p| p.into_inner());
let now = Instant::now();
guard.retain(|_, expiry| admission_is_valid(*expiry, now));
guard.contains_key(ip)
}
fn connection_allowed(ip_ok: bool, require_admission: bool, admitted: bool) -> bool {
ip_ok && (!require_admission || admitted)
}
const RPC_INTERNAL_PORT_OFFSET: u16 = 1000;
const RPC_PEER_MIN_DELIVERY_RATIO: f32 = 0.90;
static RPC_ALLOWLIST_PROXY_STARTED: OnceLock<()> = OnceLock::new();
static RPC_CONTRIBUTOR_PROCESS: OnceLock<Arc<Mutex<Option<Child>>>> = OnceLock::new();
fn contributor_handle() -> Arc<Mutex<Option<Child>>> {
RPC_CONTRIBUTOR_PROCESS
.get_or_init(|| Arc::new(Mutex::new(None)))
.clone()
}
pub fn get_rpc_server_bin() -> String {
if let Ok(bin) = std::env::var("GHOSTLINK_RPC_SERVER_BIN") {
let bin = bin.trim().to_string();
if !bin.is_empty() && Path::new(&bin).exists() {
return bin;
}
}
let relative_candidates = [
"third_party/llama.cpp/build/bin/ggml-rpc-server",
"third_party/llama.cpp/build/bin/Release/ggml-rpc-server.exe",
"third_party/llama.cpp/build/bin/ggml-rpc-server.exe",
"bin/ggml-rpc-server",
"bin/ggml-rpc-server.exe",
];
let mut search_roots = Vec::new();
if let Ok(cwd) = std::env::current_dir() {
search_roots.push(cwd);
}
if let Ok(exe) = std::env::current_exe() {
if let Some(parent) = exe.parent() {
search_roots.push(parent.to_path_buf());
if let Some(grand) = parent.parent().and_then(|p| p.parent()) {
search_roots.push(grand.to_path_buf());
}
}
}
for root in &search_roots {
for rel in &relative_candidates {
let candidate = root.join(rel);
if candidate.is_file() {
return candidate.to_string_lossy().to_string();
}
}
}
if cfg!(windows) {
"ggml-rpc-server.exe".to_string()
} else {
"ggml-rpc-server".to_string()
}
}
pub fn ensure_contributing(
bind_host: &str,
port: u16,
allowed_peers: &[String],
shared_secret: &str,
rt_handle: &Handle,
) {
let handle = contributor_handle();
let mut guard = handle.lock().unwrap_or_else(|p| p.into_inner());
if let Some(child) = guard.as_mut() {
if matches!(child.try_wait(), Ok(None)) {
return; }
}
let use_proxy = !allowed_peers.is_empty() || !shared_secret.is_empty();
let (spawn_host, spawn_port): (String, u16) = if !use_proxy {
(bind_host.to_string(), port)
} else {
let internal_port = port.saturating_add(RPC_INTERNAL_PORT_OFFSET);
maybe_start_allowlist_proxy(
bind_host,
port,
internal_port,
allowed_peers,
shared_secret,
rt_handle,
);
("127.0.0.1".to_string(), internal_port)
};
maybe_start_auth_port(bind_host, port, shared_secret, rt_handle);
let bin = get_rpc_server_bin();
if !use_proxy {
tracing::warn!(
"rpc_cluster: starting ggml-rpc-server on {spawn_host}:{spawn_port} \u{2014} this \
exposes local compute (GPU/CPU) to the network with NO AUTHENTICATION (an upstream \
llama.cpp limitation, not Ghostlink's), NO IP ALLOWLIST (rpc_allowed_peers is \
empty), and NO SHARED-SECRET HANDSHAKE (rpc_shared_secret is empty). Only enable \
contribute_compute on a network you trust, the same assumption UDP/mDNS discovery \
already makes. Set rpc_allowed_peers and/or rpc_shared_secret in settings to \
restrict which hosts may submit compute jobs."
);
} else {
tracing::warn!(
"rpc_cluster: starting ggml-rpc-server on loopback ({spawn_host}:{spawn_port}), \
fronted by an allowlist proxy on {bind_host}:{port} restricted to {} \
rpc_allowed_peers entries{} \u{2014} ggml-rpc-server itself still has NO \
AUTHENTICATION of its own (an upstream llama.cpp limitation).",
allowed_peers.len(),
if shared_secret.is_empty() {
", with NO rpc_shared_secret handshake required \u{2014} a device already \
inside an allowlisted range isn't stopped by IP allowlisting alone"
.to_string()
} else {
", and requiring a live rpc_shared_secret handshake admission before splicing \
any connection through"
.to_string()
}
);
}
let log_file = std::env::var("GHOSTLINK_RPC_SERVER_LOG")
.ok()
.filter(|p| !p.trim().is_empty())
.and_then(|path| {
std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&path)
.map_err(|err| {
tracing::warn!(
"rpc_cluster: failed to open GHOSTLINK_RPC_SERVER_LOG at '{path}': {err}. \
Falling back to discarding ggml-rpc-server output."
);
err
})
.ok()
});
let stdout_cfg = log_file
.as_ref()
.and_then(|f| f.try_clone().ok())
.map(Stdio::from)
.unwrap_or_else(Stdio::null);
let stderr_cfg = log_file
.as_ref()
.and_then(|f| f.try_clone().ok())
.map(Stdio::from)
.unwrap_or_else(Stdio::null);
match Command::new(&bin)
.arg("-H")
.arg(&spawn_host)
.arg("-p")
.arg(spawn_port.to_string())
.stdout(stdout_cfg)
.stderr(stderr_cfg)
.stdin(Stdio::null())
.spawn()
{
Ok(child) => *guard = Some(child),
Err(err) => {
tracing::warn!(
"rpc_cluster: failed to start ggml-rpc-server ('{bin}'): {err}. \
This node will not contribute compute to the cluster."
);
}
}
}
#[allow(dead_code)]
pub fn stop_contributing() {
let handle = contributor_handle();
if let Ok(mut guard) = handle.lock() {
if let Some(mut child) = guard.take() {
let _ = child.kill();
let _ = child.wait();
}
};
}
fn maybe_start_allowlist_proxy(
bind_host: &str,
public_port: u16,
internal_port: u16,
allowed_peers: &[String],
shared_secret: &str,
rt_handle: &Handle,
) {
if RPC_ALLOWLIST_PROXY_STARTED.get().is_some() {
return;
}
let public_addr_str = format!("{bind_host}:{public_port}");
let public_addr: SocketAddr = match public_addr_str.parse() {
Ok(addr) => addr,
Err(err) => {
tracing::warn!(
"rpc_cluster: cannot start rpc_allowed_peers proxy \u{2014} invalid bind address \
'{public_addr_str}': {err}. ggml-rpc-server will stay loopback-only and \
unreachable from the network until this is fixed."
);
return;
}
};
let backend_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), internal_port);
let allowed = allowed_peers.to_vec();
let require_admission = !shared_secret.is_empty();
if RPC_ALLOWLIST_PROXY_STARTED.set(()).is_ok() {
rt_handle.spawn(async move {
if let Err(err) =
run_rpc_allowlist_proxy(public_addr, backend_addr, allowed, require_admission).await
{
tracing::warn!(
"rpc_cluster: rpc_allowed_peers proxy on {public_addr} exited with error: \
{err} \u{2014} the node will stop accepting distributed-inference compute \
jobs from any peer until this process restarts."
);
}
});
}
}
async fn run_rpc_allowlist_proxy(
public_addr: SocketAddr,
backend_addr: SocketAddr,
allowed_peers: Vec<String>,
require_admission: bool,
) -> std::io::Result<()> {
let listener = TcpListener::bind(public_addr).await?;
tracing::info!(
"rpc_cluster: rpc_allowed_peers proxy listening on {public_addr}, forwarding allowed \
peers to ggml-rpc-server at {backend_addr} ({} allowlist entries, require_admission={})",
allowed_peers.len(),
require_admission
);
serve_rpc_allowlist_proxy(listener, backend_addr, allowed_peers, require_admission).await
}
async fn serve_rpc_allowlist_proxy(
listener: TcpListener,
backend_addr: SocketAddr,
allowed_peers: Vec<String>,
require_admission: bool,
) -> std::io::Result<()> {
loop {
let (inbound, peer_addr) = match listener.accept().await {
Ok(pair) => pair,
Err(err) => {
tracing::warn!("rpc_cluster: rpc_allowed_peers proxy accept() failed: {err}");
continue;
}
};
let ip_ok = ip_allowed(&peer_addr.ip(), &allowed_peers);
let admitted = is_admitted(&peer_addr.ip());
if !connection_allowed(ip_ok, require_admission, admitted) {
tracing::warn!(
"rpc_cluster: rejected ggml-rpc-server connection from {} \u{2014} {}",
peer_addr.ip(),
if !ip_ok {
"not in rpc_allowed_peers"
} else {
"no live rpc_shared_secret admission for this source IP"
}
);
continue; }
tokio::spawn(async move {
let mut inbound = inbound;
match TcpStream::connect(backend_addr).await {
Ok(mut outbound) => {
if let Err(err) = copy_bidirectional(&mut inbound, &mut outbound).await {
tracing::debug!(
"rpc_cluster: proxied connection from {peer_addr} ended: {err}"
);
}
}
Err(err) => {
tracing::warn!(
"rpc_cluster: rpc_allowed_peers proxy could not reach ggml-rpc-server at \
{backend_addr}: {err} \u{2014} closing connection from {peer_addr}"
);
}
}
});
}
}
static RPC_AUTH_PORT_STARTED: OnceLock<()> = OnceLock::new();
fn maybe_start_auth_port(bind_host: &str, rpc_port: u16, secret: &str, rt_handle: &Handle) {
if secret.is_empty() || RPC_AUTH_PORT_STARTED.get().is_some() {
return;
}
let auth_port = rpc_port.saturating_add(RPC_AUTH_PORT_OFFSET);
let addr_str = format!("{bind_host}:{auth_port}");
let addr: SocketAddr = match addr_str.parse() {
Ok(addr) => addr,
Err(err) => {
tracing::warn!(
"rpc_cluster: cannot start the RPC auth-port listener \u{2014} invalid bind \
address '{addr_str}': {err}. rpc_shared_secret admission will never succeed \
until this is fixed."
);
return;
}
};
let secret = secret.to_string();
if RPC_AUTH_PORT_STARTED.set(()).is_ok() {
rt_handle.spawn(async move {
if let Err(err) = run_rpc_auth_port(addr, secret).await {
tracing::warn!(
"rpc_cluster: RPC auth-port listener on {addr} exited with error: {err} \
\u{2014} this node will stop admitting new rpc_shared_secret handshakes \
until it restarts."
);
}
});
}
}
async fn run_rpc_auth_port(addr: SocketAddr, secret: String) -> std::io::Result<()> {
let listener = TcpListener::bind(addr).await?;
tracing::info!(
"rpc_cluster: RPC auth-port listener on {addr} (rpc_shared_secret configured, \
RPC_ADMISSION_TTL={RPC_ADMISSION_TTL:?})"
);
serve_rpc_auth_port(listener, secret).await
}
async fn serve_rpc_auth_port(listener: TcpListener, secret: String) -> std::io::Result<()> {
loop {
let (stream, peer_addr) = match listener.accept().await {
Ok(pair) => pair,
Err(err) => {
tracing::warn!("rpc_cluster: RPC auth-port accept() failed: {err}");
continue;
}
};
let secret = secret.clone();
tokio::spawn(async move {
match handle_auth_handshake(stream, &secret).await {
Ok(true) => {
admit_peer(peer_addr.ip());
tracing::info!("rpc_cluster: admitted RPC peer {}", peer_addr.ip());
}
Ok(false) => {
tracing::warn!(
"rpc_cluster: rejected RPC auth-port handshake from {} \u{2014} MAC did \
not match (wrong or missing rpc_shared_secret on the connecting side)",
peer_addr.ip()
);
}
Err(err) => {
tracing::warn!(
"rpc_cluster: RPC auth-port handshake with {} failed: {err}",
peer_addr.ip()
);
}
}
});
}
}
async fn handle_auth_handshake(mut stream: TcpStream, secret: &str) -> std::io::Result<bool> {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let nonce: [u8; RPC_NONCE_LEN] = rand::random();
tokio::time::timeout(RPC_AUTH_HANDSHAKE_TIMEOUT, stream.write_all(&nonce))
.await
.map_err(|_| {
std::io::Error::new(std::io::ErrorKind::TimedOut, "timed out sending nonce")
})??;
let mut response = [0u8; RPC_MAC_LEN];
tokio::time::timeout(RPC_AUTH_HANDSHAKE_TIMEOUT, stream.read_exact(&mut response))
.await
.map_err(|_| {
std::io::Error::new(std::io::ErrorKind::TimedOut, "timed out reading MAC")
})??;
let expected = compute_hmac(secret, &nonce);
let matched = expected == response;
let ack = [u8::from(matched)];
let _ = tokio::time::timeout(RPC_AUTH_HANDSHAKE_TIMEOUT, stream.write_all(&ack)).await;
Ok(matched)
}
pub async fn admit_via_secret(peer_ip: IpAddr, peer_rpc_port: u16, secret: &str) -> bool {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let auth_addr = SocketAddr::new(peer_ip, peer_rpc_port.saturating_add(RPC_AUTH_PORT_OFFSET));
let attempt = async {
let mut stream = TcpStream::connect(auth_addr).await?;
let mut nonce = [0u8; RPC_NONCE_LEN];
stream.read_exact(&mut nonce).await?;
let mac = compute_hmac(secret, &nonce);
stream.write_all(&mac).await?;
let mut ack = [0u8; 1];
stream.read_exact(&mut ack).await?;
Ok::<bool, std::io::Error>(ack[0] == 1)
};
match tokio::time::timeout(RPC_AUTH_CLIENT_TIMEOUT, attempt).await {
Ok(Ok(true)) => true,
Ok(Ok(false)) => {
tracing::warn!(
"rpc_cluster: RPC peer {peer_ip} rejected our rpc_shared_secret \u{2014} \
excluding it from this distributed-inference request"
);
false
}
Ok(Err(err)) => {
tracing::warn!(
"rpc_cluster: could not complete RPC auth handshake with {peer_ip}: {err} \
\u{2014} excluding it from this distributed-inference request"
);
false
}
Err(_) => {
tracing::warn!(
"rpc_cluster: RPC auth handshake with {peer_ip} timed out after \
{RPC_AUTH_CLIENT_TIMEOUT:?} \u{2014} excluding it from this \
distributed-inference request"
);
false
}
}
}
pub fn ip_allowed(ip: &IpAddr, allowlist: &[String]) -> bool {
if allowlist.is_empty() {
return true;
}
let ip_v4 = match ip {
IpAddr::V4(v4) => *v4,
IpAddr::V6(_) => {
tracing::warn!(
"rpc_cluster: rejecting connection from {ip} \u{2014} rpc_allowed_peers only \
supports IPv4 addresses/CIDR ranges today, so an IPv6 peer can never match"
);
return false;
}
};
allowlist
.iter()
.any(|entry| ipv4_entry_matches(entry, ip_v4))
}
fn ipv4_entry_matches(entry: &str, ip: Ipv4Addr) -> bool {
let entry = entry.trim();
let Some((network_str, prefix_str)) = entry.split_once('/') else {
return match entry.parse::<Ipv4Addr>() {
Ok(addr) => addr == ip,
Err(_) => {
tracing::warn!(
"rpc_cluster: rpc_allowed_peers entry '{entry}' is not a valid IPv4 address \
\u{2014} ignoring it"
);
false
}
};
};
let (Ok(network), Ok(prefix)) = (network_str.parse::<Ipv4Addr>(), prefix_str.parse::<u32>())
else {
tracing::warn!(
"rpc_cluster: rpc_allowed_peers entry '{entry}' is not a valid IPv4 CIDR range \
\u{2014} ignoring it"
);
return false;
};
if prefix > 32 {
tracing::warn!(
"rpc_cluster: rpc_allowed_peers entry '{entry}' has an invalid CIDR prefix length \
(must be 0-32) \u{2014} ignoring it"
);
return false;
}
let mask: u32 = if prefix == 0 {
0
} else {
u32::MAX << (32 - prefix)
};
(u32::from(network) & mask) == (u32::from(ip) & mask)
}
#[derive(Debug, Clone, PartialEq)]
pub struct RpcPeer {
pub node_id: String,
pub addr: SocketAddr,
pub vram_gb: f32,
pub system_memory_gb: f32,
}
pub fn discover_rpc_peers(
cluster: &ClusterState,
local_node_id: &str,
local_rpc_build_id: Option<&str>,
) -> Vec<RpcPeer> {
let nodes = cluster.nodes_snapshot();
let mut peers: Vec<RpcPeer> = nodes
.iter()
.filter(|node| node.id != local_node_id)
.filter_map(|node| {
let rpc_port = node.rpc_port?;
let metrics = cluster.get_metrics(&node.id)?;
if metrics.status != NodeStatus::Active {
return None;
}
if metrics.delivery_ratio < RPC_PEER_MIN_DELIVERY_RATIO {
tracing::warn!(
"rpc_cluster: excluding peer '{}' \u{2014} delivery_ratio {:.3} is below the \
{RPC_PEER_MIN_DELIVERY_RATIO} health floor for distributed inference.",
node.id,
metrics.delivery_ratio,
);
return None;
}
match (local_rpc_build_id, node.rpc_build_id.as_deref()) {
(Some(local), Some(peer)) if local != peer => {
tracing::warn!(
"rpc_cluster: excluding peer '{}' ({peer_addr:?}) \u{2014} llama.cpp \
build mismatch (local: '{local}', peer: '{peer}'). Distributed \
inference is skipping this peer because version-mismatched ggml-rpc \
peers have been confirmed to silently corrupt output for larger models \
while reporting healthy status throughout. Rebuild both nodes at the \
same llama.cpp commit to re-enable this peer.",
node.id,
peer_addr = metrics.ip_address,
);
return None;
}
(Some(local), Some(peer)) => {
debug_assert_eq!(local, peer);
}
_ => {
tracing::warn!(
"rpc_cluster: peer '{}' did not report a verifiable llama.cpp build \
fingerprint (predates version-compatibility checking, or couldn't \
determine its own build id) \u{2014} cannot verify it matches this \
node's build; proceeding anyway.",
node.id
);
}
}
let mut addr = metrics.ip_address?;
addr.set_port(rpc_port);
Some(RpcPeer {
node_id: node.id.clone(),
addr,
vram_gb: node.vram_gb,
system_memory_gb: metrics.system_memory_gb,
})
})
.collect();
peers.sort_by(|a, b| a.node_id.cmp(&b.node_id));
peers
}
pub fn compute_tensor_split(
local_vram_gb: f32,
local_system_memory_gb: f32,
peers: &[RpcPeer],
) -> Vec<f32> {
let all_vram_unreported = local_vram_gb <= 0.0 && peers.iter().all(|p| p.vram_gb <= 0.0);
if all_vram_unreported {
let mut split = vec![local_system_memory_gb.max(0.1)];
split.extend(peers.iter().map(|p| p.system_memory_gb.max(0.1)));
return split;
}
let mut split = vec![local_vram_gb.max(0.1)];
split.extend(peers.iter().map(|p| p.vram_gb.max(0.1)));
split
}
pub fn rpc_flag_value(peers: &[RpcPeer]) -> String {
peers
.iter()
.map(|p| p.addr.to_string())
.collect::<Vec<_>>()
.join(",")
}
pub fn tensor_split_flag_value(split: &[f32]) -> String {
split
.iter()
.map(|v| format!("{v:.4}"))
.collect::<Vec<_>>()
.join(",")
}
#[cfg(test)]
mod tests {
use super::*;
use ghostlink_core::protocol::NodeResources;
use std::net::{IpAddr, Ipv4Addr};
fn register_peer(
cluster: &ClusterState,
id: &str,
vram_gb: f32,
rpc_port: Option<u16>,
addr: Option<SocketAddr>,
status: NodeStatus,
) {
register_peer_with_build_id(cluster, id, vram_gb, rpc_port, None, addr, status);
}
#[allow(clippy::too_many_arguments)]
fn register_peer_with_build_id(
cluster: &ClusterState,
id: &str,
vram_gb: f32,
rpc_port: Option<u16>,
rpc_build_id: Option<&str>,
addr: Option<SocketAddr>,
status: NodeStatus,
) {
let mut node = NodeResources::new(id, vram_gb, 32.0, "8.6", None);
if let Some(port) = rpc_port {
node = node.with_rpc_port(port);
}
if let Some(build_id) = rpc_build_id {
node = node.with_rpc_build_id(build_id);
}
cluster.register_with_addr(node, addr);
cluster.get_metrics_mut(id, |m| m.status = status);
}
fn loopback(port: u16) -> SocketAddr {
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port)
}
#[test]
fn discovers_only_healthy_rpc_contributing_peers() {
let cluster = ClusterState::new();
register_peer(
&cluster,
"local",
16.0,
None,
Some(loopback(9000)),
NodeStatus::Active,
);
register_peer(
&cluster,
"contributor",
12.0,
Some(50052),
Some(loopback(9001)),
NodeStatus::Active,
);
register_peer(
&cluster,
"no-rpc",
8.0,
None,
Some(loopback(9002)),
NodeStatus::Active,
);
register_peer(
&cluster,
"unhealthy",
8.0,
Some(50053),
Some(loopback(9003)),
NodeStatus::Failed,
);
register_peer(
&cluster,
"no-addr",
8.0,
Some(50054),
None,
NodeStatus::Active,
);
let peers = discover_rpc_peers(&cluster, "local", None);
assert_eq!(peers.len(), 1);
assert_eq!(peers[0].node_id, "contributor");
assert_eq!(peers[0].addr, loopback(50052));
assert_eq!(peers[0].vram_gb, 12.0);
}
#[test]
fn excludes_peer_with_low_delivery_ratio_even_if_status_is_active() {
let cluster = ClusterState::new();
register_peer(
&cluster,
"flaky",
12.0,
Some(50052),
Some(loopback(9001)),
NodeStatus::Active,
);
cluster.get_metrics_mut("flaky", |m| m.delivery_ratio = 0.5);
let peers = discover_rpc_peers(&cluster, "local", None);
assert!(peers.is_empty());
}
#[test]
fn keeps_fresh_peer_with_no_delivery_samples_yet() {
let cluster = ClusterState::new();
register_peer(
&cluster,
"fresh",
12.0,
Some(50052),
Some(loopback(9001)),
NodeStatus::Active,
);
let peers = discover_rpc_peers(&cluster, "local", None);
assert_eq!(peers.len(), 1);
assert_eq!(peers[0].node_id, "fresh");
}
#[test]
fn excludes_local_node_even_if_self_advertises_rpc_port() {
let cluster = ClusterState::new();
register_peer(
&cluster,
"local",
16.0,
Some(50052),
Some(loopback(9000)),
NodeStatus::Active,
);
assert!(discover_rpc_peers(&cluster, "local", None).is_empty());
}
#[test]
fn peer_order_is_stable_and_sorted_by_id() {
let cluster = ClusterState::new();
register_peer(
&cluster,
"zeta",
8.0,
Some(50052),
Some(loopback(9001)),
NodeStatus::Active,
);
register_peer(
&cluster,
"alpha",
8.0,
Some(50053),
Some(loopback(9002)),
NodeStatus::Active,
);
let peers = discover_rpc_peers(&cluster, "local", None);
assert_eq!(peers.len(), 2);
assert_eq!(peers[0].node_id, "alpha");
assert_eq!(peers[1].node_id, "zeta");
}
#[test]
fn excludes_peer_with_confirmed_mismatched_rpc_build_id() {
let cluster = ClusterState::new();
register_peer_with_build_id(
&cluster,
"local",
16.0,
None,
None,
Some(loopback(9000)),
NodeStatus::Active,
);
register_peer_with_build_id(
&cluster,
"contributor",
12.0,
Some(50052),
Some("e920c523e"),
Some(loopback(9001)),
NodeStatus::Active,
);
let peers = discover_rpc_peers(&cluster, "local", Some("da296d6"));
assert!(
peers.is_empty(),
"peer with a confirmed different llama.cpp build id must be excluded"
);
}
#[test]
fn does_not_exclude_peer_with_matching_rpc_build_id() {
let cluster = ClusterState::new();
register_peer_with_build_id(
&cluster,
"local",
16.0,
None,
None,
Some(loopback(9000)),
NodeStatus::Active,
);
register_peer_with_build_id(
&cluster,
"contributor",
12.0,
Some(50052),
Some("da296d6"),
Some(loopback(9001)),
NodeStatus::Active,
);
let peers = discover_rpc_peers(&cluster, "local", Some("da296d6"));
assert_eq!(peers.len(), 1);
assert_eq!(peers[0].node_id, "contributor");
}
#[test]
fn does_not_exclude_peer_with_unknown_rpc_build_id() {
let cluster = ClusterState::new();
register_peer_with_build_id(
&cluster,
"local",
16.0,
None,
None,
Some(loopback(9000)),
NodeStatus::Active,
);
register_peer(
&cluster,
"contributor",
12.0,
Some(50052),
Some(loopback(9001)),
NodeStatus::Active,
);
let peers = discover_rpc_peers(&cluster, "local", Some("da296d6"));
assert_eq!(
peers.len(),
1,
"a peer with no rpc_build_id must still be used, not excluded"
);
assert_eq!(peers[0].node_id, "contributor");
}
#[test]
fn does_not_exclude_peer_when_local_build_id_is_unknown() {
let cluster = ClusterState::new();
register_peer_with_build_id(
&cluster,
"local",
16.0,
None,
None,
Some(loopback(9000)),
NodeStatus::Active,
);
register_peer_with_build_id(
&cluster,
"contributor",
12.0,
Some(50052),
Some("e920c523e"),
Some(loopback(9001)),
NodeStatus::Active,
);
let peers = discover_rpc_peers(&cluster, "local", None);
assert_eq!(peers.len(), 1);
assert_eq!(peers[0].node_id, "contributor");
}
#[test]
fn tensor_split_puts_local_first_proportional_to_vram() {
let peers = vec![
RpcPeer {
node_id: "a".to_string(),
addr: loopback(1),
vram_gb: 12.0,
system_memory_gb: 32.0,
},
RpcPeer {
node_id: "b".to_string(),
addr: loopback(2),
vram_gb: 8.0,
system_memory_gb: 16.0,
},
];
let split = compute_tensor_split(16.0, 64.0, &peers);
assert_eq!(split, vec![16.0, 12.0, 8.0]);
}
#[test]
fn tensor_split_floors_zero_vram_to_avoid_a_dead_device_entry() {
let split = compute_tensor_split(0.0, 0.0, &[]);
assert_eq!(split, vec![0.1]);
}
#[test]
fn tensor_split_falls_back_to_ram_proportional_when_no_device_reports_vram() {
let peers = vec![
RpcPeer {
node_id: "a".to_string(),
addr: loopback(1),
vram_gb: 0.0,
system_memory_gb: 32.0,
},
RpcPeer {
node_id: "b".to_string(),
addr: loopback(2),
vram_gb: 0.0,
system_memory_gb: 8.0,
},
];
let split = compute_tensor_split(0.0, 64.0, &peers);
assert_eq!(split, vec![64.0, 32.0, 8.0]);
}
#[test]
fn tensor_split_uses_vram_floor_not_ram_when_cluster_is_mixed() {
let peers = vec![RpcPeer {
node_id: "a".to_string(),
addr: loopback(1),
vram_gb: 0.0,
system_memory_gb: 128.0,
}];
let split = compute_tensor_split(16.0, 32.0, &peers);
assert_eq!(split, vec![16.0, 0.1]);
}
#[test]
fn rpc_flag_value_formats_host_port_list() {
let peers = vec![
RpcPeer {
node_id: "a".to_string(),
addr: loopback(50052),
vram_gb: 8.0,
system_memory_gb: 16.0,
},
RpcPeer {
node_id: "b".to_string(),
addr: loopback(50053),
vram_gb: 8.0,
system_memory_gb: 16.0,
},
];
assert_eq!(rpc_flag_value(&peers), "127.0.0.1:50052,127.0.0.1:50053");
}
#[test]
fn tensor_split_flag_value_formats_comma_separated_ratios() {
assert_eq!(
tensor_split_flag_value(&[16.0, 12.0, 8.0]),
"16.0000,12.0000,8.0000"
);
}
#[test]
fn rpc_flag_value_empty_for_no_peers() {
assert_eq!(rpc_flag_value(&[]), "");
}
fn v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
IpAddr::V4(Ipv4Addr::new(a, b, c, d))
}
#[test]
fn ip_allowed_empty_allowlist_allows_everything() {
assert!(ip_allowed(&v4(10, 0, 0, 29), &[]));
assert!(ip_allowed(&v4(203, 0, 113, 5), &[]));
}
#[test]
fn ip_allowed_exact_ipv4_match() {
let allowlist = vec!["10.0.0.29".to_string()];
assert!(ip_allowed(&v4(10, 0, 0, 29), &allowlist));
assert!(!ip_allowed(&v4(10, 0, 0, 30), &allowlist));
}
#[test]
fn ip_allowed_cidr_range_match() {
let allowlist = vec!["10.0.0.0/24".to_string()];
assert!(ip_allowed(&v4(10, 0, 0, 1), &allowlist));
assert!(ip_allowed(&v4(10, 0, 0, 254), &allowlist));
assert!(!ip_allowed(&v4(10, 0, 1, 1), &allowlist));
assert!(!ip_allowed(&v4(11, 0, 0, 1), &allowlist));
}
#[test]
fn ip_allowed_cidr_slash_32_is_exact_match() {
let allowlist = vec!["192.168.1.5/32".to_string()];
assert!(ip_allowed(&v4(192, 168, 1, 5), &allowlist));
assert!(!ip_allowed(&v4(192, 168, 1, 6), &allowlist));
}
#[test]
fn ip_allowed_cidr_slash_0_matches_everything() {
let allowlist = vec!["0.0.0.0/0".to_string()];
assert!(ip_allowed(&v4(1, 2, 3, 4), &allowlist));
assert!(ip_allowed(&v4(255, 255, 255, 255), &allowlist));
}
#[test]
fn ip_allowed_multiple_entries_any_match_wins() {
let allowlist = vec!["10.0.0.29".to_string(), "192.168.0.0/16".to_string()];
assert!(ip_allowed(&v4(10, 0, 0, 29), &allowlist));
assert!(ip_allowed(&v4(192, 168, 50, 7), &allowlist));
assert!(!ip_allowed(&v4(172, 16, 0, 1), &allowlist));
}
#[test]
fn ip_allowed_ipv6_input_rejected_not_panicking() {
let allowlist = vec!["10.0.0.0/24".to_string()];
let ipv6 = IpAddr::V6(std::net::Ipv6Addr::LOCALHOST);
assert!(!ip_allowed(&ipv6, &allowlist));
}
#[test]
fn ip_allowed_malformed_entry_is_skipped_not_panicking() {
let allowlist = vec![
"not-an-ip".to_string(),
"10.0.0.0/99".to_string(), "10.0.0.29".to_string(), ];
assert!(ip_allowed(&v4(10, 0, 0, 29), &allowlist));
assert!(!ip_allowed(&v4(10, 0, 0, 30), &allowlist));
}
use tokio::io::{AsyncReadExt, AsyncWriteExt};
async fn spawn_echo_backend() -> SocketAddr {
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
loop {
let (mut sock, _) = match listener.accept().await {
Ok(pair) => pair,
Err(_) => return,
};
tokio::spawn(async move {
let mut buf = [0u8; 1024];
loop {
match sock.read(&mut buf).await {
Ok(0) | Err(_) => return,
Ok(n) => {
if sock.write_all(&buf[..n]).await.is_err() {
return;
}
}
}
}
});
}
});
addr
}
#[tokio::test]
async fn allowlist_proxy_forwards_allowed_source() {
let backend_addr = spawn_echo_backend().await;
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).await.unwrap();
let proxy_addr = listener.local_addr().unwrap();
let allowed_peers = vec!["127.0.0.1".to_string()];
tokio::spawn(serve_rpc_allowlist_proxy(
listener,
backend_addr,
allowed_peers,
false,
));
let mut client = TcpStream::connect(proxy_addr).await.unwrap();
client.write_all(b"hello").await.unwrap();
let mut resp = [0u8; 5];
client.read_exact(&mut resp).await.unwrap();
assert_eq!(
&resp, b"hello",
"allowed peer's traffic must be spliced through"
);
}
#[tokio::test]
async fn allowlist_proxy_rejects_disallowed_source() {
let backend_addr = spawn_echo_backend().await;
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).await.unwrap();
let proxy_addr = listener.local_addr().unwrap();
let allowed_peers = vec!["10.0.0.1".to_string()];
tokio::spawn(serve_rpc_allowlist_proxy(
listener,
backend_addr,
allowed_peers,
false,
));
let mut client = TcpStream::connect(proxy_addr).await.unwrap();
let _ = client.write_all(b"hello").await; let mut resp = [0u8; 1];
let n = client.read(&mut resp).await.unwrap_or(0);
assert_eq!(
n, 0,
"disallowed peer's connection must be closed with no data forwarded"
);
}
#[test]
fn connection_allowed_requires_both_ip_allowlist_and_admission_when_required() {
assert!(
connection_allowed(true, false, false),
"allowlisted, no admission required at all"
);
assert!(
!connection_allowed(true, true, false),
"allowlisted but admission required and not yet granted"
);
assert!(
connection_allowed(true, true, true),
"allowlisted, admission required and granted"
);
assert!(
!connection_allowed(false, false, false),
"not on the allowlist at all, regardless of admission"
);
assert!(
!connection_allowed(false, true, true),
"admitted but not on the allowlist \u{2014} both checks must pass"
);
}
#[tokio::test]
async fn allowlist_proxy_forwards_allowlisted_ip_once_admitted() {
let backend_addr = spawn_echo_backend().await;
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).await.unwrap();
let proxy_addr = listener.local_addr().unwrap();
let allowed_peers = vec!["127.0.0.1".to_string()];
admit_peer(IpAddr::V4(Ipv4Addr::LOCALHOST));
tokio::spawn(serve_rpc_allowlist_proxy(
listener,
backend_addr,
allowed_peers,
true,
));
let mut client = TcpStream::connect(proxy_addr).await.unwrap();
client.write_all(b"hello").await.unwrap();
let mut resp = [0u8; 5];
client.read_exact(&mut resp).await.unwrap();
assert_eq!(
&resp, b"hello",
"a previously-admitted, allowlisted source must be spliced through"
);
}
#[test]
fn admission_is_valid_true_before_expiry_false_at_and_after() {
let base = Instant::now();
let expiry = base + Duration::from_millis(50);
assert!(admission_is_valid(expiry, base));
assert!(!admission_is_valid(expiry, expiry));
assert!(!admission_is_valid(
expiry,
expiry + Duration::from_millis(1)
));
}
#[test]
fn admit_peer_then_is_admitted_round_trips_for_a_fresh_ip() {
let ip = v4(198, 51, 100, 77);
assert!(!is_admitted(&ip), "must not be admitted before admit_peer");
admit_peer(ip);
assert!(
is_admitted(&ip),
"must be admitted immediately after admit_peer"
);
}
#[test]
fn is_admitted_false_for_an_ip_that_was_never_admitted() {
let ip = v4(198, 51, 100, 78);
assert!(!is_admitted(&ip));
}
#[test]
fn compute_hmac_is_deterministic_and_distinguishes_secret_and_nonce() {
let nonce = [1u8; RPC_NONCE_LEN];
let a = compute_hmac("secret-a", &nonce);
let b = compute_hmac("secret-a", &nonce);
let c = compute_hmac("secret-b", &nonce);
let other_nonce = [2u8; RPC_NONCE_LEN];
let d = compute_hmac("secret-a", &other_nonce);
assert_eq!(a, b, "same secret + nonce must produce the same MAC");
assert_ne!(a, c, "different secret must change the MAC");
assert_ne!(a, d, "different nonce must change the MAC");
assert_eq!(a.len(), RPC_MAC_LEN);
}
#[tokio::test]
async fn auth_handshake_admits_correct_secret_and_rejects_wrong_secret() {
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).await.unwrap();
let auth_addr = listener.local_addr().unwrap();
tokio::spawn(serve_rpc_auth_port(listener, "correct-horse".to_string()));
let mut client = TcpStream::connect(auth_addr).await.unwrap();
let mut nonce = [0u8; RPC_NONCE_LEN];
client.read_exact(&mut nonce).await.unwrap();
let correct_mac = compute_hmac("correct-horse", &nonce);
client.write_all(&correct_mac).await.unwrap();
let mut ack = [0u8; 1];
client.read_exact(&mut ack).await.unwrap();
assert_eq!(
ack[0], 1,
"the correct secret must be acknowledged as a match"
);
let mut client2 = TcpStream::connect(auth_addr).await.unwrap();
let mut nonce2 = [0u8; RPC_NONCE_LEN];
client2.read_exact(&mut nonce2).await.unwrap();
let wrong_mac = compute_hmac("totally-wrong", &nonce2);
client2.write_all(&wrong_mac).await.unwrap();
let mut ack2 = [0u8; 1];
client2.read_exact(&mut ack2).await.unwrap();
assert_eq!(
ack2[0], 0,
"a wrong secret must not be acknowledged as a match"
);
}
#[tokio::test]
async fn admit_via_secret_applies_the_auth_port_offset_and_admits_on_match() {
let rpc_port: u16 = 40000 + (std::process::id() % 5000) as u16;
let auth_port = rpc_port.saturating_add(RPC_AUTH_PORT_OFFSET);
let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), auth_port);
let listener = match TcpListener::bind(bind_addr).await {
Ok(l) => l,
Err(_) => return, };
tokio::spawn(serve_rpc_auth_port(listener, "cluster-secret".to_string()));
let ok =
admit_via_secret(IpAddr::V4(Ipv4Addr::LOCALHOST), rpc_port, "cluster-secret").await;
assert!(
ok,
"matching secret via the real offset-derived port must succeed"
);
assert!(is_admitted(&IpAddr::V4(Ipv4Addr::LOCALHOST)));
}
}