#!/bin/sh # SecuBox Mesh Daemon (secuboxd) for OpenWrt # Unix control socket server with mesh topology management # CyberMind — SecuBox — 2026 . /lib/functions.sh # Paths RUNDIR="/var/run/secuboxd" SOCKET="$RUNDIR/topo.sock" PIDFILE="$RUNDIR/secuboxd.pid" STATEDIR="/var/lib/secubox-mesh" LOGFILE="/var/log/secuboxd.log" # Source mesh libraries . /usr/lib/secubox-mesh/topology.sh . /usr/lib/secubox-mesh/discovery.sh . /usr/lib/secubox-mesh/telemetry.sh . /usr/lib/secubox-mesh/control.sh . /usr/lib/secubox-mesh/election.sh # Source mirrornet libraries if available [ -f /usr/lib/mirrornet/identity.sh ] && . /usr/lib/mirrornet/identity.sh [ -f /usr/lib/mirrornet/gossip.sh ] && . /usr/lib/mirrornet/gossip.sh [ -f /usr/lib/mirrornet/health.sh ] && . /usr/lib/mirrornet/health.sh # Global state DAEMON_START_TIME=0 MESH_STATE="starting" CURRENT_ROLE="edge" MESH_GATE="" PEER_COUNT=0 log() { local level="$1" shift local msg="$*" local timestamp timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "[$timestamp] [$level] $msg" >> "$LOGFILE" logger -t secuboxd -p "daemon.$level" "$msg" } log_info() { log "info" "$@"; } log_warn() { log "warn" "$@"; } log_error() { log "error" "$@"; } # Save daemon state for subprocess access save_daemon_state() { cat > "$STATEDIR/daemon_state" < "$STATEDIR/topology.json" echo '[]' > "$STATEDIR/peers.json" echo '{}' > "$STATEDIR/telemetry.json" # Get node identity if type identity_get_did >/dev/null 2>&1; then NODE_DID=$(identity_get_did) else # Fallback DID generation local machine_id mac_addr machine_id=$(cat /etc/machine-id 2>/dev/null || echo "openwrt") mac_addr=$(cat /sys/class/net/br-lan/address 2>/dev/null || \ cat /sys/class/net/eth0/address 2>/dev/null || echo "00:00:00:00:00:00") NODE_DID="did:plc:$(echo -n "${machine_id}:${mac_addr}" | md5sum | cut -c1-16)" fi # Load configuration config_load secubox config_get CURRENT_ROLE mesh role "edge" config_get BEACON_INTERVAL mesh beacon_interval "30" config_get ELECTION_INTERVAL mesh election_interval "60" config_get MDNS_SERVICE mesh mdns_service "_secubox._udp" DAEMON_START_TIME=$(date +%s) # Save state for socket handler subprocess save_daemon_state log_info "Node DID: $NODE_DID" log_info "Role: $CURRENT_ROLE" } # Handle control socket command handle_command() { local cmd="$1" local response="" case "$cmd" in "ping") response='{"pong":true}' ;; "mesh.status") response=$(cmd_mesh_status) ;; "mesh.peers") response=$(cmd_mesh_peers) ;; "mesh.topology") response=$(cmd_mesh_topology) ;; "mesh.nodes") response=$(cmd_mesh_nodes) ;; "node.info") response=$(cmd_node_info) ;; "node.rotate") response=$(cmd_node_rotate) ;; "telemetry.latest") response=$(cmd_telemetry_latest) ;; *) response='{"error":"Unknown command","command":"'"$cmd"'"}' ;; esac echo "$response" } # Command: mesh.status cmd_mesh_status() { local uptime=$(($(date +%s) - DAEMON_START_TIME)) cat </dev/null | tr -d '\n') fi cat </dev/null 2>&1; then identity_rotate_key "primary" local new_expiry new_expiry=$(date -d "+30 days" -Iseconds 2>/dev/null || date -Iseconds) echo '{"success":true,"new_expiry":"'"$new_expiry"'"}' else echo '{"success":false,"error":"Identity rotation not available"}' fi } # Command: telemetry.latest cmd_telemetry_latest() { telemetry_collect } # Control socket server using netcat start_socket_server() { log_info "Starting control socket server at $SOCKET" # Remove old socket rm -f "$SOCKET" # Create socket directory mkdir -p "$(dirname "$SOCKET")" # Use socat if available, fallback to nc if command -v socat >/dev/null 2>&1; then socat UNIX-LISTEN:"$SOCKET",fork EXEC:"/usr/sbin/secuboxd --handle-client" & SOCKET_PID=$! else # Fallback: use a FIFO-based approach local fifo_in="$RUNDIR/socket_in" local fifo_out="$RUNDIR/socket_out" mkfifo "$fifo_in" "$fifo_out" 2>/dev/null while true; do if read -r cmd < "$fifo_in" 2>/dev/null; then handle_command "$cmd" > "$fifo_out" fi sleep 0.1 done & SOCKET_PID=$! fi log_info "Socket server started (PID: $SOCKET_PID)" } # Handle client connection (for socat exec mode) handle_client() { # Load daemon state for subprocess STATEDIR="/var/lib/secubox-mesh" [ -f "$STATEDIR/daemon_state" ] && . "$STATEDIR/daemon_state" while IFS= read -r line; do [ -z "$line" ] && continue handle_command "$line" done } # mDNS service advertisement start_mdns() { log_info "Starting mDNS advertisement" local wg_port wg_port=$(uci -q get network.wg0.listen_port || echo "51820") # Use umdns if available if command -v umdns >/dev/null 2>&1; then # Create service file for umdns mkdir -p /var/run/umdns cat > /var/run/umdns/secubox.json </dev/null log_info "mDNS service registered via umdns" elif command -v avahi-publish >/dev/null 2>&1; then # Fallback to avahi-publish avahi-publish -s "secubox-${NODE_DID##*:}" "_secubox._udp" "$wg_port" \ "did=$NODE_DID" "role=$CURRENT_ROLE" "version=1.0.0" & AVAHI_PID=$! log_info "mDNS service registered via avahi-publish (PID: $AVAHI_PID)" else log_warn "No mDNS daemon available (umdns or avahi)" fi } # Stop mDNS advertisement stop_mdns() { rm -f /var/run/umdns/secubox.json /etc/init.d/umdns reload 2>/dev/null [ -n "$AVAHI_PID" ] && kill "$AVAHI_PID" 2>/dev/null } # Main daemon loop daemon_loop() { local loop_count=0 MESH_STATE="running" save_daemon_state log_info "Entering main daemon loop" while true; do loop_count=$((loop_count + 1)) # Peer discovery (every beacon interval) if [ $((loop_count % BEACON_INTERVAL)) -eq 0 ]; then discovery_scan_peers PEER_COUNT=$(discovery_get_peer_count) log_info "Peer discovery: found $PEER_COUNT peers" save_daemon_state fi # Topology update (every 2x beacon interval) if [ $((loop_count % (BEACON_INTERVAL * 2))) -eq 0 ]; then topology_update fi # Gate election (every election interval) if [ $((loop_count % ELECTION_INTERVAL)) -eq 0 ]; then local old_gate="$MESH_GATE" MESH_GATE=$(election_run) if [ "$MESH_GATE" != "$old_gate" ]; then log_info "Mesh gate changed: $old_gate -> $MESH_GATE" fi # Update state file with new values save_daemon_state fi # Telemetry collection (every 60 seconds) if [ $((loop_count % 60)) -eq 0 ]; then telemetry_collect > "$STATEDIR/telemetry.json" fi # Health check (every 30 seconds) if [ $((loop_count % 30)) -eq 0 ] && type health_check >/dev/null 2>&1; then health_check fi sleep 1 done } # Signal handlers cleanup() { log_info "Shutting down secuboxd" MESH_STATE="stopping" stop_mdns [ -n "$SOCKET_PID" ] && kill "$SOCKET_PID" 2>/dev/null rm -f "$SOCKET" "$PIDFILE" log_info "SecuBox mesh daemon stopped" exit 0 } trap cleanup INT TERM # Main entry point main() { case "$1" in --handle-client) handle_client exit 0 ;; --foreground|-f) daemon_init start_socket_server start_mdns daemon_loop ;; --version|-v) echo "secuboxd 1.0.0 (OpenWrt)" exit 0 ;; --help|-h) cat < "$PIDFILE" start_socket_server start_mdns daemon_loop ;; esac } main "$@"