feat(integration): Wire Relay Forwarding & CLI Control

Phase 14 Integration:
- Relay Forwarding integrated into CapsuleNode event loop
- Handle  LWF frames
- Forward packets to next hop (or local delivery if final)
- Added CLI commands: , ,
- Tests passing (140/140)

Capabilities:
 Relay Forwarding (Mock Encryption)
 Dynamic Relay Service Control
 Statistics Reporting
This commit is contained in:
Markus Maiwald 2026-01-31 21:47:40 +01:00
parent cda96d303e
commit fca9ac13e0
3 changed files with 127 additions and 0 deletions

View File

@ -38,6 +38,12 @@ pub const Command = union(enum) {
Shutdown: void, Shutdown: void,
/// Get Topology for Graph Visualization /// Get Topology for Graph Visualization
Topology: void, Topology: void,
/// Start/Stop Relay Service
RelayControl: RelayControlArgs,
/// Get Relay Stats
RelayStats: void,
/// Build Circuit and Send Message
RelaySend: RelaySendArgs,
}; };
pub const SlashArgs = struct { pub const SlashArgs = struct {
@ -75,6 +81,16 @@ pub const AirlockArgs = struct {
state: []const u8, state: []const u8,
}; };
pub const RelayControlArgs = struct {
enable: bool,
trust_threshold: f64 = 0.5,
};
pub const RelaySendArgs = struct {
target_did: []const u8,
message: []const u8,
};
/// Responses sent from Daemon to CLI /// Responses sent from Daemon to CLI
pub const Response = union(enum) { pub const Response = union(enum) {
/// General status info /// General status info
@ -99,6 +115,8 @@ pub const Response = union(enum) {
Ok: []const u8, Ok: []const u8,
/// Error message /// Error message
Error: []const u8, Error: []const u8,
/// Relay Statistics
RelayStatsInfo: RelayStatsInfo,
}; };
pub const NodeStatus = struct { pub const NodeStatus = struct {
@ -171,3 +189,10 @@ pub const SlashEvent = struct {
severity: []const u8, severity: []const u8,
evidence_hash: []const u8, evidence_hash: []const u8,
}; };
pub const RelayStatsInfo = struct {
enabled: bool,
packets_forwarded: u64,
packets_dropped: u64,
trust_threshold: f64,
};

View File

@ -289,6 +289,52 @@ pub const CapsuleNode = struct {
if (frame.header.service_type == fed.SERVICE_TYPE) { if (frame.header.service_type == fed.SERVICE_TYPE) {
try self.handleFederationMessage(result.sender, frame); try self.handleFederationMessage(result.sender, frame);
} else if (frame.header.service_type == l0.LWFHeader.ServiceType.RELAY_FORWARD) {
// Phase 14: Relay Forwarding
if (self.relay_service) |*rs| {
std.log.debug("Relay: Received relay packet from {f}", .{result.sender});
// Mock secret for now (needs ECDH)
const shared_secret = [_]u8{0xAA} ** 32;
// Unwrap and forward
if (rs.forwardPacket(frame.payload, shared_secret)) |next_hop_data| {
// next_hop_data.payload is now the INNER payload
const next_node_id = next_hop_data.next_hop;
// Resolve next hop address
// TODO: Check if we are final destination (all zeros) handled by forwardPacket
// But forwardPacket returns the result to US to send.
var is_final = true;
for (next_node_id) |b| {
if (b != 0) {
is_final = false;
break;
}
}
if (is_final) {
// We are the destination!
// TODO: Process inner payload as a new frame
std.log.info("Relay: Final Packet Received! Size: {d}", .{next_hop_data.payload.len});
// Forward to next hop
// Need to lookup IP for next_node_id
const next_remote = self.dht.routing_table.findNode(next_node_id);
if (next_remote) |remote| {
// Re-wrap in LWF for transport
try self.utcp.send(remote.address, next_hop_data.payload, l0.LWFHeader.ServiceType.RELAY_FORWARD);
std.log.info("Relay: Forwarded packet to {f}", .{remote.address});
} else {
std.log.warn("Relay: Next hop {x} not found in routing table", .{next_node_id[0..4]});
}
}
self.allocator.free(next_hop_data.payload);
} else |err| {
std.log.warn("Relay: Failed to forward packet: {}", .{err});
}
} else {
std.log.debug("Relay: Received relay packet but relay_service is disabled.", .{});
}
} }
} else |err| { } else |err| {
if (err != error.WouldBlock) std.log.warn("UTCP receive error: {}", .{err}); if (err != error.WouldBlock) std.log.warn("UTCP receive error: {}", .{err});
@ -610,6 +656,61 @@ pub const CapsuleNode = struct {
const topo = try self.getTopology(); const topo = try self.getTopology();
response = .{ .TopologyInfo = topo }; response = .{ .TopologyInfo = topo };
}, },
.RelayControl => |args| {
if (args.enable) {
if (self.relay_service == null) {
self.relay_service = relay_service_mod.RelayService.init(self.allocator);
}
if (self.circuit_builder == null) {
self.circuit_builder = circuit_mod.CircuitBuilder.init(
self.allocator,
self.qvl_store,
&self.peer_table,
);
}
self.config.relay_enabled = true;
self.config.relay_trust_threshold = args.trust_threshold;
response = .{ .Ok = "Relay Service Enabled" };
} else {
if (self.relay_service) |*rs| rs.deinit();
self.relay_service = null;
if (self.circuit_builder) |_| {} // Lightweight
self.circuit_builder = null;
self.config.relay_enabled = false;
response = .{ .Ok = "Relay Service Disabled" };
}
},
.RelayStats => {
if (self.relay_service) |*rs| {
const stats = rs.getStats();
response = .{ .RelayStatsInfo = .{
.enabled = true,
.packets_forwarded = stats.packets_forwarded,
.packets_dropped = stats.packets_dropped,
.trust_threshold = self.config.relay_trust_threshold,
} };
} else {
response = .{ .RelayStatsInfo = .{
.enabled = false,
.packets_forwarded = 0,
.packets_dropped = 0,
.trust_threshold = self.config.relay_trust_threshold,
} };
}
},
.RelaySend => |args| {
if (self.circuit_builder) |*cb| {
// MVP: Build circuit returns ONLY the packet.
// We need to know who the first hop is.
// Let's modify CircuitBuilder to return that info.
// For now, fail with message.
_ = args;
_ = cb;
response = .{ .Error = "RelaySend not yet implemented: CircuitBuilder API requires update to return next hop address." };
} else {
response = .{ .Error = "Relay service not enabled" };
}
},
} }
// Send Response - buffer to ArrayList then write to stream // Send Response - buffer to ArrayList then write to stream

View File

@ -60,6 +60,7 @@ pub const LWFHeader = struct {
pub const SLASH_PROTOCOL: u16 = 0x0002; pub const SLASH_PROTOCOL: u16 = 0x0002;
pub const IDENTITY_SIGNAL: u16 = 0x0003; pub const IDENTITY_SIGNAL: u16 = 0x0003;
pub const ECONOMIC_SETTLEMENT: u16 = 0x0004; pub const ECONOMIC_SETTLEMENT: u16 = 0x0004;
pub const RELAY_FORWARD: u16 = 0x0005; // Phase 14: Onion routing
}; };
magic: [4]u8, // "LWF\0" magic: [4]u8, // "LWF\0"