ROAMSWITCH FOR LINUX · OPERATIONS REFERENCE

CLI / Headless Operations Guide

How to operate RoamSwitch for Linux without a GUI — on cloud VPS instances, over SSH, from cron, monitoring scripts, and programmatic AI agents (MCP).

Choosing the Edition
  • Client Edition (roamswitch): Designed for laptops, mobile workstations, and developer devices. Autonomously switches nftables firewall profiles based on the connected network's trust level. This guide focuses primarily on headless/CLI management of the Client Edition.
  • Server Edition (roamswitch-server): Designed for cloud VPS instances (AWS, GCP, DigitalOcean, Linode, etc.) and on-premises servers exposed directly to the Internet. Features inbound default-drop filtering, SSH lockout prevention, Critical-Path File Integrity Monitoring (FIM), eBPF / Falco runtime integration, and instant notifications (Telegram / LINE / Webhooks). For full installation and management instructions, see the RoamSwitch Server Edition Operations Manual and the Server Security Whitepaper.

1. Components

Component Runs as Role
roamswitch-daemon (Client) root (systemd Type=notify) All client privileged operations: nftables control, network detection, ransomware/malware monitoring, fanotify, ARP/NDP pinning, DNS enforcement. Opens no TCP/UDP listening socket.
roamswitch-server-daemon (Server) root (systemd Type=notify) All server privileged operations: inbound default drop, SSH & admin bastion preservation, FIM (150+ critical binary hashes), Falco eBPF UNIX socket listener with autonomous SIGSTOP freezing, alert dispatching.
roamswitch (CLI) login user (some actions require sudo) Thin client reading daemon state. Uses /run/roamswitch/roamswitch.sock IPC on Client Edition, or provides --server, server, fim, and emergency-restore subcommands on Server Edition.
roamswitch-mcp spawned by AI clients Read-only MCP server (stdio / JSON-RPC) for programmatic status retrieval by AI agents. See MCP setup
roamswitch-app login user GTK GUI (Client Edition only). Not needed in headless environments.

A headless deployment runs on the daemon + roamswitch CLI (plus roamswitch-mcp if desired). All autonomous defense mechanisms work without any GUI.

Mutually Exclusive Packages
Client Edition (roamswitch) and Server Edition (roamswitch-server) are mutually exclusive packages (Conflicts). Deploy roamswitch-server on server environments.

2. Daemon (systemd services)

Client Edition

sudo systemctl status  roamswitch.service      # Status
sudo systemctl enable  roamswitch.service      # Auto-start at boot (enabled at install)
sudo systemctl restart roamswitch.service      # Restart
journalctl -u roamswitch.service -f            # Follow logs
journalctl -u roamswitch.service --since "1h ago"

What the client daemon does autonomously on startup and every cycle (3 s):

  • Identifies the connected gateway MAC and applies nftables profiles (open / balanced / lockdown) against trusted_networks
  • Behavioural ransomware detection (fanotify + Shannon entropy + canaries)
  • On-access malware scanning (fanotify, optionally ClamAV)
  • ARP spoof monitoring and preventive gateway ARP/NDP pinning on untrusted networks
  • Kernel hardening (sysctl / Yama / core dumps / /tmp noexec) per profile
  • Threat-protection DNS enforcement (dns_enabled + dns_scope)
  • Link guard (NFQUEUE) for phishing interception
  • Runtime state output to /run/roamswitch/state.json

Server Edition

sudo systemctl status  roamswitch-server.service      # Status
sudo systemctl restart roamswitch-server.service      # Restart
sudo systemctl reload  roamswitch-server.service      # Reload config file
journalctl -u roamswitch-server.service -f            # Follow logs

3. CLI Command Reference

Basic syntax: roamswitch <command> [options]. Running without arguments defaults to status. Output language automatically follows the OS locale (LC_ALL / LC_MESSAGES / LANG).

Command Permissions Description
status [--server] (alias report / server-status) User Security health assessment (24 checks on client, 30 checks with --server), 0–100 score, grade, and per-item recommendations
server [config|setup|test-notify|restart|ack|ebpf] User/root Server Edition configuration management, interactive setup wizard, and test notifications. ack acknowledges the host-isolation safety timer (guard.yaml's safety_timer_secs) to stop the automatic restore. ebpf (alias incidents) shows the eBPF Runtime Guard's current containment status and detected incident history
fim [verify|update] [--quiet|-q] User/root Critical-Path File Integrity Monitoring verification (verify) and baseline hash database update (update). --quiet suppresses output for cron / systemd hooks
emergency-restore root Lift all emergency eBPF / firewall isolations and restore network baseline
ports [-a|--all] User Listening ports on 0.0.0.0, unauthenticated DBs, and dev servers. -a includes loopback-only ports
guards User Status of automatic defense guards (port anomaly, ARP, USB storage, download, DNS threat, canary, dev-server isolator, Bluetooth)
wifi User Wi-Fi encryption strength (Open / WEP / WPA / wired) and SSID
sharing [status|on|off] User Auto-stop / restore of SSH / Samba / RDP on untrusted networks (on disconnects active SSH when untrusted)
audit-url <URL> User Inspect URL phishing and threat risk via local feed + heuristics (never fetches target)
audit-secrets <text|path|directory> User Detect API keys, private keys, and tokens in text, a file, or a directory (recursive; skips .git/node_modules/etc.) — never transmits data
audit-logs [hours] User Aggregate and classify system journald / auth logs from the last N hours (default 24). API keys and tokens are automatically masked. Also shows log-pattern anomaly detection results (new patterns and statistical frequency spikes)
canary User Ransomware canary decoy file status and integrity, and detected incident history (up to the most recent 50)
port-anomaly (alias port-guard) general The Port Anomaly Guard's baseline status, currently auto-isolated ports, and detected incident history (up to the most recent 50)
quarantine [list] User Contents of malware quarantine vault (sample, original path, threat name, date)
knowledge [query] (alias faq) User Search the offline knowledge base
airgap [enable|disable] User/root Trigger or lift emergency Air-Gap isolation (enable drops all external traffic)
help (--help / -h) User Show help (roamswitch <command> --help for subcommand help)
scan-packages [FOLDER...] General Checks installed OS packages (auto-detects dpkg/pacman/dnf/zypper) against the local known-CVE map. Pass folder arguments to also check that project's dependency lockfiles (npm/PyPI/crates.io and 4 more ecosystems). No network activity at all
scan-vulns [--confirm] General Only runs when active_vuln_scan_enabled: true (off by default): 127.0.0.1-only unauthenticated-reachability checks against Redis/Memcached/MongoDB/dockerd, CORS misconfiguration/path traversal/open redirect checks against detected dev servers, SMTP open-relay diagnostics, and proof-based exposure checks for Telnet/FTP/SSH. Without --confirm it's a dry run
notifications (alias notification-history) User History of notifications RoamSwitch has sent (log-audit anomalies, ClickFix detections, and the like) — past 7 days, most recent first
frozen [list|resume <PID>|resume all] User / root Lists the processes the guards (ransomware burst detection, canary tampering, kernel-exploit detection) are currently holding in SIGSTOP, and releases (SIGCONT) one that turned out to be a false positive. A freeze is not a kill — a released process resumes exactly where it stopped. resume all releases every one. Releasing a root-owned process requires sudo
resource-guard (alias leak-guard) User The Resource Exhaustion / Process Anomaly Guard's (Server Edition only) detected incident history: sustained non-recovering RSS growth (memory leak / DoS) or crash-looping in network-exposed services, each with a confidence tier
timeline (alias incident-timeline) User Correlates link guard (ARP spoofing), ransomware canary, eBPF Runtime Guard and Resource Guard detections into one chronological view, with process ancestry and MITRE ATT&CK tags (experimental)
xdp-gate [status|enable|disable] User / root Show or toggle the XDP boot-time gate (experimental). status reports whether roamswitch-xdp-gate.service is enabled and the program’s current state. Disabled by default
sensor [key|list|pair|unpair|request-audit|results] User / root Pairing-code-based mutual trust with RoamSwitch Sensor, a network sensor you install on commodity hardware from a separate repository. pair --addr <ip> --code <code> redeems a code Sensor issued to pair (Sensor is assumed to run at a fixed IP), key shows this endpoint's own public key/address, unpair removes the pairing, and request-audit asks Sensor to run an active vulnerability audit (fetch the result with results). Requires sensor_pairing_enabled: true (off by default). See the Sensor Manual for details
port-scan (alias portscan) User Shows the incoming-port-scan-detection guard's detection history (source IP, whether it was auto-blocked, and block duration). A different feature from port-anomaly, which detects newly “exposed” unknown ports

Examples

sudo roamswitch status                 # Client full diagnostic (24 items)
sudo roamswitch status --server        # Server Edition full diagnostic (30 items)
sudo roamswitch ports -a               # All listening ports
roamswitch guards                      # Guard status
roamswitch audit-url https://examp1e-login.com
roamswitch audit-secrets ./deploy.env
roamswitch audit-secrets .                          # Recursively scan the current directory
roamswitch audit-logs 72               # Analyze logs from the last 72 hours
sudo roamswitch sharing on             # Auto-stop SSH/Samba/RDP on untrusted networks
sudo roamswitch fim verify             # FIM file integrity verification
sudo roamswitch fim update             # Update FIM baseline
sudo roamswitch emergency-restore      # Fully clear emergency lockdown and restore
sudo roamswitch airgap enable          # Emergency lockdown
sudo roamswitch airgap disable         # Disable
sudo roamswitch server ack             # Acknowledge safety timer (stop auto-recovery)
roamswitch canary                      # Canary incident history
roamswitch port-anomaly                # Port Anomaly Guard's incident history
roamswitch server ebpf                 # eBPF guard's containment status and incident history
roamswitch notifications               # Notification history from the past 7 days
roamswitch frozen                      # List processes held in SIGSTOP
sudo roamswitch frozen resume 12345    # Release a false-positive freeze
sudo roamswitch sensor pair --addr 192.168.1.50 --code AB3DFGH2  # Pairs with Sensor using a pairing code
sudo roamswitch sensor key             # Shows this endpoint's own public key/address
roamswitch port-scan                   # Port-scan detection history

The Server Edition daemon runs as root, so commands that need to read the nftables ruleset or identify a process's owner (status / status --server / ports -a) return inaccurate results without sudo (a false “inactive” verdict, or missing process info). Commands that change configuration (sharing / fim update / airgap / emergency-restore, etc.) likewise require sudo. Every other, read-only command works correctly without it.

Caveats & Limitations
  • Subcommand-specific help is supported via roamswitch <command> --help.
  • Machine-readable status should be queried via MCP (§6) or /run/roamswitch/state.json (§5).
  • In the Client Edition, there is no direct command to force a firewall profile; the daemon manages this autonomously based on network trust. To force a level, configure manual_override in config.json or call the set_security_level IPC directly (§5).
  • status returns exit code 0 regardless of score. For automated monitoring, parse the score line (see §7).

4. Configuration Files

Client Edition (~/.config/roamswitch/config.json)

The daemon runs as root and scans /home/*/.config/roamswitch/config.json, using the first valid file it finds (or /root/.config/roamswitch/config.json in root-only environments).

Key Type / Default Description
languagestring / OS localeUI & CLI language (ja / en / ko / zh-Hans / zh-Hant / de / fr / es / it / pt-PT)
trusted_networks[{name, mac, level}]Trusted networks; mac is gateway MAC, level is open / balanced / lockdown
away_protection_levelstring / lockdownDefault profile on unknown networks
manual_overridestring / nullForce open / balanced / lockdown (null for automatic)
dns_enabledbool / trueThreat-protection DNS enforcement
dns_providerstring / quad9quad9 / cloudflare / adguard / cleanBrowsing
dns_scopestring / untrusted_onlyuntrusted_only / always_on
arp_spoof_guard_enabledbool / trueARP spoof monitoring
gateway_arp_lock_enabledbool / truePreventive gateway ARP/NDP lock on untrusted networks
port_anomaly_guard_enabledbool / trueAuto-block new listening ports
system_wide_fanotify_enabledbool / trueSystem-wide fanotify malware guard
pre_exec_blocking_enabledbool / truePre-execution blocking (FAN_DENY)
entropy_freeze_enabledbool / trueRansomware fast-freeze (SIGSTOP)
mount_hardening_enabledbool / truenoexec on /tmp and /dev/shm (applied on non-open profiles)
yama_memory_protect_enabledbool / trueYama ptrace restrictions
usb_storage_guard_enabled / usb_keyboard_guard_enabledbool / falseUSB storage / BadUSB keyboard guard (off by default)
usb_zero_trust_enabledbool / falseUSB bus authorized_default=0
bluetooth_guard_enabledbool / falseDisable Bluetooth radio on untrusted networks
sharing_service_control_enabledbool / trueAuto-stop/restore SSH / Samba / RDP
scan_exclusions[string]Absolute paths excluded from scanning
link_guardobject{enabled, mode: "off"|"warn"|"block", allowlist, blocklist_extra, use_threat_dns}
vpn_on_untrusted_enabledbool / falseAuto-start VPN tunnel on untrusted networks
vpn_backendstring / wireguardwireguard / tailscale
active_vuln_scan_enabledbool / falseAllows scan-vulns to run (off by default). Requires explicit opt-in since it sends 127.0.0.1-only reachability probes
pkgcve_watched_folders[string]Project folders (absolute paths) the GUI's dependency scan checks. The CLI's scan-packages ignores this setting and only uses folders given as command-line arguments
sensor_pairing_enabledbool / falseAllows pairing-code-based mutual trust with RoamSwitch Sensor (off by default). Required to use the sensor command
port_scan_detect_enabledbool / trueIncoming-port-scan-detection guard
port_scan_detect_min_distinct_portsint / 15A source connecting to at least this many distinct ports within the detection window is judged a scan
port_scan_detect_window_secsint / 300Aggregation window for scan detection (seconds)
port_scan_block_enabledbool / trueAuto-blocks the detected scan source IP
port_scan_block_duration_secsint / 600Duration of the automatic block (seconds)
Remote SSH Caution
With sharing_service_control_enabled: true, connecting to an untrusted network disconnects active SSH sessions. Leave it disabled on headless servers.

Server Edition (/etc/roamswitch/server.conf)

Server Edition uses an INI-format configuration file with strict permissions (0600, root-only). Refer to Server Edition Operations Manual

5. Logs & Runtime State Files

Path Target Description
journalctl -u roamswitch.serviceClientClient daemon logs (profile switches, detections, errors)
journalctl -u roamswitch-server.serviceServerServer daemon logs (FIM events, Falco detections, isolations)
/run/roamswitch/roamswitch.sockClientClient daemon IPC Unix domain socket
/run/roamswitch/events.sockServerFalco / Tetragon eBPF integration socket (root:root, mode 0660; Falco runs as root by default, enabling zero-config direct socket writes)
/run/roamswitch/state.jsonClientCycle state: {active_level, network_trusted, fanotify_ready}
/run/roamswitch/alerts.jsonClientRecent alert queue
/run/roamswitch/approvals.jsonClientPending approval queue
/run/roamswitch/fanotify.readyClientFlag file indicating fanotify guard is running
/var/lib/roamswitch/fim_baseline.dbServerFIM SHA-256 baseline hash database
~/.local/share/roamswitch/quarantine/BothQuarantine Vault (0700, samples 0400) + .metadata.json

Calling IPC Directly (Advanced)

The daemon accepts newline-delimited JSON on /run/roamswitch/roamswitch.sock:

# Force-switch profile (Client Edition)
printf '{"id":1,"method":"set_security_level","params":{"level":"lockdown"}}\n' \
  | sudo socat - UNIX-CONNECT:/run/roamswitch/roamswitch.sock

# Reconcile gateway ARP lock now
printf '{"id":1,"method":"reconcile_gateway_lock","params":null}\n' \
  | sudo socat - UNIX-CONNECT:/run/roamswitch/roamswitch.sock

6. Programmatic Status (MCP)

roamswitch-mcp exposes read-only tools over JSON-RPC on stdio (get_security_report / get_exposed_ports / get_guard_status / audit_url_safety / audit_secrets / audit_security_logs / get_app_help / get_quarantine_status / get_file_scan_guard_status / verify_fim / get_port_anomaly_incidents / get_ebpf_incidents / get_resource_guard_incidents / get_canary_status / get_notification_history / get_incident_timeline / run_active_vuln_scan / run_package_cve_scan / run_package_cve_scan_languages).

It uses no external network communication, connecting locally to the daemon socket or calling roamswitch-core. See MCP Setup Guide

7. Automation Recipes

Daily cron health check → Email if score drops below threshold

#!/usr/bin/env bash
# /etc/cron.daily/roamswitch-health
out=$(runuser -u "$SUDO_USER" -- roamswitch status 2>&1)
score=$(printf '%s\n' "$out" | grep -oE '[0-9]+/100' | head -1 | cut -d/ -f1)
if [ -n "$score" ] && [ "$score" -lt 80 ]; then
  printf '%s\n' "$out" | mail -s "RoamSwitch health: ${score}/100" root
fi

Monitor alerts queue (poll alerts.json)

#!/usr/bin/env bash
last=0
while :; do
  ts=$(jq -r 'max_by(.timestamp).timestamp // 0' /run/roamswitch/alerts.json 2>/dev/null || echo 0)
  if [ "$ts" -gt "$last" ]; then
    jq -c ".[] | select(.timestamp > $last)" /run/roamswitch/alerts.json | logger -t roamswitch-alert
    last=$ts
  fi
  sleep 10
done

Verify fanotify guard health (state.json)

jq -e '.fanotify_ready == true' /run/roamswitch/state.json >/dev/null \
  || echo "WARNING: fanotify guard is not running" >&2

8. Troubleshooting

Symptom Resolution
roamswitch exits with "check that roamswitch-mcp is installed" Daemon is not running → sudo systemctl start roamswitch.service (or roamswitch-server.service). Verify socket exists
roamswitch status shows fanotify 🔴 "guard stopped" Transient fs.fanotify.max_user_groups exhaustion. Restart with sudo systemctl restart roamswitch.service and verify in journal
Profile stays in balanced, never reaches open Verify gateway MAC is registered in trusted_networks with level: open
SSH disconnects unexpectedly Client edition has sharing_service_control_enabled: true on an untrusted network. Disable via roamswitch sharing off
Config changes do not take effect The client daemon reads the first of /home/*/.config/…; apply with systemctl restart roamswitch.service. For the server edition, edit /etc/roamswitch/server.conf then run sudo roamswitch server restart (no reload).
Server communications accidentally blocked Access cloud console (VNC / Serial) and run sudo roamswitch emergency-restore

9. References