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).
- 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.
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) againsttrusted_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 /
/tmpnoexec) 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.
- 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_overrideinconfig.jsonor call theset_security_levelIPC directly (§5). statusreturns 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 |
|---|---|---|
language | string / OS locale | UI & 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_level | string / lockdown | Default profile on unknown networks |
manual_override | string / null | Force open / balanced / lockdown (null for automatic) |
dns_enabled | bool / true | Threat-protection DNS enforcement |
dns_provider | string / quad9 | quad9 / cloudflare / adguard / cleanBrowsing |
dns_scope | string / untrusted_only | untrusted_only / always_on |
arp_spoof_guard_enabled | bool / true | ARP spoof monitoring |
gateway_arp_lock_enabled | bool / true | Preventive gateway ARP/NDP lock on untrusted networks |
port_anomaly_guard_enabled | bool / true | Auto-block new listening ports |
system_wide_fanotify_enabled | bool / true | System-wide fanotify malware guard |
pre_exec_blocking_enabled | bool / true | Pre-execution blocking (FAN_DENY) |
entropy_freeze_enabled | bool / true | Ransomware fast-freeze (SIGSTOP) |
mount_hardening_enabled | bool / true | noexec on /tmp and /dev/shm (applied on non-open profiles) |
yama_memory_protect_enabled | bool / true | Yama ptrace restrictions |
usb_storage_guard_enabled / usb_keyboard_guard_enabled | bool / false | USB storage / BadUSB keyboard guard (off by default) |
usb_zero_trust_enabled | bool / false | USB bus authorized_default=0 |
bluetooth_guard_enabled | bool / false | Disable Bluetooth radio on untrusted networks |
sharing_service_control_enabled | bool / true | Auto-stop/restore SSH / Samba / RDP |
scan_exclusions | [string] | Absolute paths excluded from scanning |
link_guard | object | {enabled, mode: "off"|"warn"|"block", allowlist, blocklist_extra, use_threat_dns} |
vpn_on_untrusted_enabled | bool / false | Auto-start VPN tunnel on untrusted networks |
vpn_backend | string / wireguard | wireguard / tailscale |
active_vuln_scan_enabled | bool / false | Allows 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_enabled | bool / false | Allows pairing-code-based mutual trust with RoamSwitch Sensor (off by default). Required to use the sensor command |
port_scan_detect_enabled | bool / true | Incoming-port-scan-detection guard |
port_scan_detect_min_distinct_ports | int / 15 | A source connecting to at least this many distinct ports within the detection window is judged a scan |
port_scan_detect_window_secs | int / 300 | Aggregation window for scan detection (seconds) |
port_scan_block_enabled | bool / true | Auto-blocks the detected scan source IP |
port_scan_block_duration_secs | int / 600 | Duration of the automatic block (seconds) |
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.service | Client | Client daemon logs (profile switches, detections, errors) |
journalctl -u roamswitch-server.service | Server | Server daemon logs (FIM events, Falco detections, isolations) |
/run/roamswitch/roamswitch.sock | Client | Client daemon IPC Unix domain socket |
/run/roamswitch/events.sock | Server | Falco / Tetragon eBPF integration socket (root:root, mode 0660; Falco runs as root by default, enabling zero-config direct socket writes) |
/run/roamswitch/state.json | Client | Cycle state: {active_level, network_trusted, fanotify_ready} |
/run/roamswitch/alerts.json | Client | Recent alert queue |
/run/roamswitch/approvals.json | Client | Pending approval queue |
/run/roamswitch/fanotify.ready | Client | Flag file indicating fanotify guard is running |
/var/lib/roamswitch/fim_baseline.db | Server | FIM SHA-256 baseline hash database |
~/.local/share/roamswitch/quarantine/ | Both | Quarantine 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 |