mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-09 16:24:45 +00:00
Add connect/disconnect control commands and maelstrom chaos scenario
Add runtime peer management to the FIPS daemon via control socket commands, and a new chaos simulation scenario that exercises dynamic topology mutation with ephemeral node identities. Daemon (connect/disconnect commands): - Extend control socket Request with optional params field - Add commands.rs module for mutating command dispatch, separate from read-only queries - Add api_connect() on Node: builds ephemeral PeerConfig (no auto- reconnect), pre-seeds identity cache, reuses initiate_peer_connection - Add api_disconnect() on Node: calls remove_active_peer(), clears retry_pending to suppress reconnection - Route non-show_* commands to async command dispatch in rx_loop fipsctl CLI: - Add Connect and Disconnect subcommands accepting npub or hostname - Resolve hostnames from /etc/fips/hosts before sending to daemon - Refactor socket I/O into reusable send_request helper Chaos simulator (maelstrom scenario): - Add PeerChurnManager: periodically disconnects a random active link and connects a random unconnected node pair via control socket - Add send_command() to control.py using base64-encoded JSON payloads to avoid shell quoting issues in docker exec - Add PeerChurnConfig to scenario with interval and ephemeral_fraction - Ephemeral identity support: nodes configured without nsec generate fresh keypairs on restart; simulator queries show_status for new npub and updates its cache via on_node_restart callback - Add maelstrom.yaml: all chaos dimensions (netem, link flaps, node churn, peer topology churn, traffic) with 50% ephemeral identity
This commit is contained in:
@@ -739,4 +739,76 @@ impl Node {
|
||||
|
||||
info!(sent, total = peer_addrs.len(), reason = %reason, "Sent disconnect notifications");
|
||||
}
|
||||
|
||||
// === Control API methods ===
|
||||
|
||||
/// Connect to a peer via the control API.
|
||||
///
|
||||
/// Creates an ephemeral peer connection (not persisted to config, no
|
||||
/// auto-reconnect). Reuses the same connection path as auto-connect
|
||||
/// peers. Returns JSON data on success or an error message.
|
||||
pub(crate) async fn api_connect(
|
||||
&mut self,
|
||||
npub: &str,
|
||||
address: &str,
|
||||
transport: &str,
|
||||
) -> Result<serde_json::Value, String> {
|
||||
let peer_config = crate::config::PeerConfig {
|
||||
npub: npub.to_string(),
|
||||
alias: None,
|
||||
addresses: vec![crate::config::PeerAddress::new(transport, address)],
|
||||
connect_policy: crate::config::ConnectPolicy::Manual,
|
||||
auto_reconnect: false,
|
||||
};
|
||||
|
||||
// Pre-seed identity cache (same as initiate_peer_connections does)
|
||||
if let Ok(identity) = PeerIdentity::from_npub(npub) {
|
||||
self.peer_aliases
|
||||
.insert(*identity.node_addr(), identity.short_npub());
|
||||
self.register_identity(*identity.node_addr(), identity.pubkey_full());
|
||||
}
|
||||
|
||||
self.initiate_peer_connection(&peer_config)
|
||||
.await
|
||||
.map(|()| {
|
||||
info!(
|
||||
npub = %npub,
|
||||
address = %address,
|
||||
transport = %transport,
|
||||
"API connect initiated"
|
||||
);
|
||||
serde_json::json!({
|
||||
"npub": npub,
|
||||
"address": address,
|
||||
"transport": transport,
|
||||
})
|
||||
})
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Disconnect a peer via the control API.
|
||||
///
|
||||
/// Removes the peer and suppresses auto-reconnect.
|
||||
pub(crate) fn api_disconnect(&mut self, npub: &str) -> Result<serde_json::Value, String> {
|
||||
let peer_identity = PeerIdentity::from_npub(npub)
|
||||
.map_err(|e| format!("invalid npub '{npub}': {e}"))?;
|
||||
let node_addr = *peer_identity.node_addr();
|
||||
|
||||
if !self.peers.contains_key(&node_addr) {
|
||||
return Err(format!("peer not found: {npub}"));
|
||||
}
|
||||
|
||||
// Remove the peer (full cleanup: sessions, indices, links, tree, bloom)
|
||||
self.remove_active_peer(&node_addr);
|
||||
|
||||
// Suppress any pending auto-reconnect
|
||||
self.retry_pending.remove(&node_addr);
|
||||
|
||||
info!(npub = %npub, "API disconnect completed");
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"npub": npub,
|
||||
"disconnected": true,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user