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-side privileged work: inbound default drop, SSH / admin-IP exception protection, FIM (tamper detection for 150+ critical files and directory file sets), receiving Falco eBPF events on a UNIX socket and freezing processes (SIGSTOP), the exec recorder, event forwarding (opt-in) and sending notifications
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-ransomware-rollback root (watch runs as the systemd unit roamswitch-ransomware-rollback-watch.service) Ransomware recovery tool. Takes snapshots of btrfs subvolumes and LVM logical volumes and extracts only the files you need. Included in the client package; also available from the GUI’s Incident Response tab
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
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 (27 items normally, the 34-item server assessment with --server), score (0–100), grade and advice. --json prints the same report as machine-readable JSON on stdout
server [status|config|setup|tui|test-notify|restart|ack|ebpf|bans|unban|allowlist] User/root Server Edition management. config shows and changes settings, setup is the interactive wizard, tui launches the full-coverage TUI (roamswitch-server-tui; every action as root, read-only for ordinary users), test-notify sends a test notification and restart restarts the service (reloading the configuration is not supported). ack acknowledges the current emergency isolation (it keeps a FULL isolation strict and releases a DEGRADED one; see the isolation policy in §3). ebpf (alias incidents) shows the eBPF runtime guard’s current containment state and detected incident history. allowlist [list|add <rule> <process> [--exe <absolute-path>]|remove] manages the permanent false-positive allowlist; binding an entry to the executable with --exe means a renamed process cannot use it. bans [--json] shows the SSH brute-force guard and the sources it has banned (the list is readable by root only), and unban <IP|IPv6 /64|--all> lifts a ban (root)
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
bans [--json] User/root Shows the state of the SSH brute-force guard (blocking / detect only / stopped, and why), the sources blocked right now with the time left, and the recent records. The ban list is readable by root only, so add sudo. --json is available. Same as server bans on the Server Edition
unban <IP | IPv6 /64 | --all> root Lifts a ban of the SSH brute-force guard and also clears that source's failure count. --all lifts every ban. Same as server unban on the Server Edition
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 (Client Edition only). enable drops all external traffic (connections that were already open are cut too) and disable restores it. See “Isolation (Air-Gap) policy” below for how automatic release works
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). On Ubuntu (20.04, 22.04, 24.04, 26.04) it uses the Ubuntu-specific data and checks only CVEs of CVSS 7.0 or higher that Ubuntu has already shipped a fix for ("no findings" does not mean there are no known CVEs). Ubuntu derivatives and unsupported releases are reported as not covered and are not switched to the Debian data. 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/SSRF 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
vuln-status General Lists the last recorded result and days elapsed (pass_age) for every probe scan-vulns --confirm has recorded. Outcomes are one of three states — vulnerable / safe / inconclusive — and elapsed time shows as 'today', 'N day(s) ago', or 'unknown' (never run). If no scan has ever been run, it tells you to run scan-vulns --confirm first, then exits (there is no scheduled re-scan yet, so the elapsed time reflects only how often it's actually been run manually). The same data is also available via the GUI (ports tab) and the MCP tool get_vulnerability_scan_history
notifications (alias notification-history) User The history of notifications RoamSwitch has sent (log-audit anomalies, ClickFix detections and so on) for the past week, newest first. --json prints a JSON array and --export <FILE> writes JSON Lines
frozen [list|resume <PID>|resume all] User / root Lists the processes the guards (ransomware rapid-encryption detection, canary tampering, kernel-exploit detection, and the eBPF guard’s pinpoint containment) hold in SIGSTOP, and releases one (SIGCONT) that was a false positive. A freeze is not a kill, so a released process resumes exactly where it stopped. resume all releases every one. Releasing a process owned by root 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 Merges detections from the link guard (ARP spoofing), the ransomware canary, the eBPF runtime guard and the resource guard into one timeline, with process lineage and MITRE ATT&CK tags (experimental). --json prints every record as a JSON array on stdout, and --export <FILE> writes JSON Lines (one record per line) to a file
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|repin|unpair|request-audit|results] User / root Mutual trust, through pairing codes, with the “RoamSwitch Sensor” network sensor that is installed on a general-purpose machine (a separate product). pair --addr <IP> --code <code> --fingerprint <SHA-256> pairs using the code the Sensor issued (the Sensor is assumed to run on a fixed IP; the fingerprint is the value shown on the Sensor’s screen or by roamswitch-sensor pairing show, and pins the certificate used for TLS 1.3 — it is never fetched from the network and there is no plaintext fallback). repin enters the new fingerprint after the Sensor regenerated its certificate, key shows this endpoint’s public key and address, unpair removes the pairing, and request-audit asks the Sensor for an active vulnerability audit (fetch the result with results). Requires sensor_pairing_enabled: true (off by default). See the Sensor Operations 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
lockfile-fim [verify|update] [--quiet|-q] root Verify (verify) or reset (update) the dependency lockfile tamper-monitoring baseline. Requires root. The watched project folders are the targets (lockfile_watch_dirs on the server edition)
scan-scripts <FOLDER...> User Statically lists the preinstall / install / postinstall / prepare scripts declared by package.json files under node_modules in the given project folders (an inventory, not a verdict; no network activity)
scan-typosquat <FOLDER...> User Checks dependency names in package.json against a bundled list of popular npm package names by edit distance 1–2 and flags likely typosquats (reference only; no network activity)
web-scan [FOLDER...] [--quarantine] [--json] User/root Checks for PHP, JavaScript, JSP and ASP web shells, injected code, PHP disguised as images and .htaccess abuse with the web application layer rules (built in, the signed daily feed, and your own rules in /etc/roamswitch/rules.d). Without a folder it checks the document roots in the web server configuration. Check-only by default; --quarantine (root) quarantines. Exit code: 0 = nothing found, 2 = a critical or high finding, 1 = could not run
web-rules [status|list|validate <file.json>|locked|unlock-account <user>|example] User/root Shows the state of the web application layer rules (version, installed feed version, custom rules directory, quarantine level, account response, post-detection hook) (status), lists the loaded rules (list), validates a custom rule file (validate), shows login shells disabled by the account response (locked) and restores them (unlock-account, root), and prints examples of the configuration and hook (example)
kernel-modules [--json] [--apply [MODULE...]] [--remove] User/root Checks, by only reading files (nothing is sent), whether the kernel modules that became paths for Copy Fail, Dirty Frag and act_pedit (algif_aead, esp4, esp6, rxrpc, act_pedit) can be loaded. --apply (root) writes install … /bin/false to modprobe.d for the unused ones only, and --remove undoes it. It does not count in the diagnosis score
audit-agent-setup [FOLDER...] [--json] User/root Read-only check (no network) of the instruction files and skills you give AI agents (~/.claude, ~/.codex, ~/.cursor, ~/.gemini, ~/.agents, ~/.openclaw, plus CLAUDE.md, AGENTS.md and similar in the project folders registered for package-CVE scanning and any FOLDER given) and of OpenClaw's openclaw.json. It flags invisible Unicode characters, instruction overrides, fetch-and-run, secret-file references next to an upload, compiled code inside a skill, OpenClaw with authentication off or a short token and more; anything inside an HTML comment is rated higher. File contents and secrets are never printed. A match means a person should read that line, not that it is malicious. --json prints JSON
bluetooth-audit [--json] User Checks the BlueZ configuration (/etc/bluetooth/input.conf and main.conf) for settings that let unpaired devices connect as keyboards, and lists the connected Bluetooth keyboards and mice (read-only)
audit-npm-signatures <DIR> [--confirm] User Verifies the signatures and provenance of installed packages with npm audit signatures (the only RoamSwitch command that talks to npmjs.com; without --confirm it is a dry run; the server edition also needs npm_audit_signatures_enabled)
install [--pm npm|pnpm] [--yes] User Wraps npm / pnpm install and confines only the lifecycle-script phase inside a bwrap sandbox (the package download itself uses the network as usual)
forward status|test|queue (alias event-forward) User/root Shows event-forwarding status (status), sends a test event (test) or lists the retry queue (queue) for syslog RFC 5424 / CEF, JSON Lines and an HMAC-signed structured webhook. Opt-in, off by default, and sends only to the destinations you configure (Server Operations Manual §16)
events search|tree|export|verify|status (alias exec-events) root Searches (search: --since 2h --pid N --exe S --user U --parent P --text S --json), prints the process tree of (tree <pid>), exports (export), verifies the hash chain of (verify; tamper-evident, not tamper-proof) and shows the status of (status) the local exec recorder log. The log is root-only, so sudo is normally needed (Server Operations Manual §15)

Isolation (Air-Gap) Policy

Releasing on a fixed timer alone lets a patient attacker wait it out, so the trigger of an isolation is classified by confidence (both editions).

  • Low confidence (a manual Air-Gap, generic anomalies, eBPF Error / Warning and below): fully restored after a short timer (client: airgap_low_confidence_secs, 600 s by default; server: safety_timer_secs in guard.yaml, 300 s by default; 0 disables the timer).
  • High confidence (canary tampering, ransomware-style mass rewriting, ARP spoofing under lockdown, eBPF Critical / Emergency / Alert, repeated firing of the same rule and so on): never restored silently. After the timer the cause (frozen PID, canary, ARP, re-firing rule) is re-checked and the isolation is released with a notification only when the cause is verified cleared. If that cannot be confirmed, nobody has acknowledged it and the cap is reached (client: airgap_hard_cap_secs; server: isolation_hard_cap_secs in server.conf; both 3600 s by default, 0 = no cap), it moves to DEGRADED mode.
  • DEGRADED mode never opens on a timer (the client allows loopback only; the server allows only the maintenance-SSH and whitelisted-IP management paths). It is released only by a verified-cleared re-check, an explicit acknowledgement (server: roamswitch server ack) or a manual release (the GUI button / roamswitch emergency-restore). In the client’s DEGRADED mode the Wi-Fi rfkill is not lifted either, unless airgap_degraded_lift_rfkill is true.
  • Established connections other than the management paths are cut (server: everything except the maintenance SSH ports and whitelisted IPs, so a C2 channel or reverse shell the attacker already has open is cut too). A manual Air-Gap is low confidence when it comes from the menu / GUI / roamswitch airgap enable with no warning context, but when it is started from the “block now” button of an ARP-spoofing warning it inherits that warning’s (high) confidence, and the gateway’s MAC is re-checked without sending any traffic until it matches the trusted MAC on consecutive checks (arp_recheck_consecutive checks spanning at least arp_recheck_min_secs seconds). Every transition is recorded in the incident timeline.

Ransomware Recovery (Snapshots & File Restore)

The following commands belong to roamswitch-ransomware-rollback (they are not roamswitch subcommands). Its state is stored in /var/lib/roamswitch.

Command Permissions Description
roamswitch-ransomware-rollback snapshot [--path PATH] [--label TEXT] root Take a snapshot of the target (default $HOME) right now. Targets are btrfs subvolumes and LVM logical volumes
roamswitch-ransomware-rollback watch [--path PATH] [--periodic-interval-secs N] root Long-running. Takes a snapshot automatically on bursts of file rewrites and on Critical events recorded by other guards, and also takes pre-damage snapshots on a schedule (every 6 hours by default; keeps 4 generations on btrfs, 2 on LVM; after a detection-time snapshot, periodic snapshots pause for 7 days by default)
roamswitch-ransomware-rollback list [--path PATH] [--json] root List the snapshots this tool created (kind, whether it still exists, and the recommended pre-damage snapshot). --json for machine-readable output
roamswitch-ransomware-rollback restore-files <ID> <PATH> [--dest DIR] root Copy only the given file or directory out of a snapshot into a separate location without overwriting the current state (default <target>/RoamSwitch-Recovered/<ID>/). The first choice for recovery
roamswitch-ransomware-rollback mount-for-browsing <ID> / unmount-browse <MOUNT_POINT> root Make a snapshot’s contents visible so you can pick the path for restore-files (btrfs: as is; LVM: a temporary read-only mount). Unmount with unmount-browse when done
roamswitch-ransomware-rollback config get|set --periodic-interval-secs N root Pre-damage snapshot interval (seconds; 0 disables, otherwise at least 300). Applied to a running watch without a restart. There is no setting to turn off detection-time snapshots
roamswitch-ransomware-rollback restore <ID> [--yes] [--accept-tampered] root Roll the whole target back to a snapshot (destructive). A detection-time snapshot contains already-encrypted files, so it is refused without --accept-tampered. Try restore-files first

Examples

sudo roamswitch status                 # Client full diagnostic (27 items)
sudo roamswitch status --server        # Server Edition full diagnostic (34 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 the emergency isolation (keeps a FULL isolation strict, releases a DEGRADED one)
roamswitch canary                      # Canary incident history
roamswitch vuln-status                 # Shows the last scan result and days elapsed (pass_age)
roamswitch port-anomaly                # Port Anomaly Guard's incident history
roamswitch server ebpf                 # eBPF guard's containment status and incident history
roamswitch server bans                 # State of the SSH brute-force guard and the sources it has banned
sudo roamswitch server unban 203.0.113.9   # Lift an SSH-guard ban (needs root)
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 --fingerprint 3F:45:89:E1:...:36:10  # 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
roamswitch timeline --json             # all incident-timeline records as JSON
roamswitch notifications --export n.jsonl   # JSON Lines
sudo roamswitch events search --since 2h --exe curl
sudo roamswitch forward status
sudo roamswitch server allowlist add "<rule>" mytool --exe /usr/local/bin/mytool
sudo roamswitch sensor repin <PUBLIC_KEY> --fingerprint <NEW_FINGERPRINT>
sudo roamswitch server tui

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
  • Per-subcommand --help is supported (for example roamswitch ports --help).
  • For machine-readable output use status --json, --json (a JSON array) and --export <FILE> (JSON Lines) on timeline / notifications, events search --json, MCP (§6) or /run/roamswitch/state.json (§5).
  • On the client edition there is no CLI command that switches the profile directly (the daemon decides autonomously). To force one, set manual_override in config.json or call the IPC method set_security_level directly (§5).
  • status returns exit code 0 regardless of the result. When using it for monitoring, parse the score line of the output (an example is in §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)
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 / trueTurns Bluetooth "discoverable" off on an untrusted network (on by default; the radio stays on and connected devices stay connected)
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)
airgap_low_confidence_secsint / 600Seconds until a low-confidence Air-Gap is restored automatically (0 = never automatically). Not applied to high-confidence isolations (§3, “Isolation (Air-Gap) policy”)
airgap_hard_cap_secsint / 3600Seconds before a high-confidence isolation moves to DEGRADED mode (0 = no cap). DEGRADED mode never opens on a timer
airgap_degraded_lift_rfkillbool / falseWhether DEGRADED mode also lifts the Wi-Fi rfkill (it does not by default)
airgapAutoRfkillbool / trueWhether Air-Gap also blocks the Wi-Fi radio with rfkill (restored on release)
arp_recheck_consecutiveint / 3Number of consecutive checks in which the gateway MAC must match the trusted MAC before an ARP-spoofing isolation is considered cleared (1 or more)
arp_recheck_min_secsint / 60The minimum number of seconds that consecutive matching must span
arp_recheck_active_probebool / trueFor an ARP-triggered isolation, when the gateway has no neighbour-cache entry (and the link is up), send one direct ARP request to verify the gateway MAC. Any reply carrying a MAC other than the trusted one counts as a mismatch. At most one probe per 20 seconds
credential_access_watch_enabledbool / trueNotifies when an unexpected process opens a credential file (a private key in ~/.ssh, ~/.aws/credentials, ~/.config/gcloud, a browser credential store); notify only, on by default. Backup tools and indexers are exempt from the start; exempt others with credential_access_watch_allow. Changes apply within about 30 seconds. A very short read such as cat or head, where the program exits before RoamSwitch can identify its executable, may not be reported.
ssh_guard_*—Settings of the SSH brute-force guard. The 10 keys and their defaults are the same as on the Server Edition (server.conf): ssh_guard_enabled (true), ssh_guard_block_enabled (true), ssh_guard_max_failures (6), ssh_guard_window_secs (600), ssh_guard_ban_secs (3600), ssh_guard_ban_max_secs (604800), ssh_guard_allow (empty; an array of IPs / CIDRs or a comma-separated string), ssh_guard_log_source (auto), ssh_guard_yield_to_fail2ban (true) and ssh_guard_trust_key_days (30). It works only while sshd is running. An out-of-range value falls back to the default. Changes are picked up within about a minute (no restart). List and lift bans with roamswitch bans and roamswitch unban
fim_intrusion_context_enabledbool / trueAdds the account and executable of the process that wrote the file, and the SSH login that process belongs to (source, time, authentication method), to file-tampering (FIM) and lockfile-tampering notifications; to a credential-file notification only when the process belongs to an SSH login. Takes effect after a daemon restart. Nothing is sent anywhere
forward_*—Event-forwarding settings (syslog / JSON Lines / signed webhook). Opt-in, off by default. The keys are forward_enabled, forward_syslog_url, forward_syslog_format, forward_syslog_framing, forward_tls_ca_file, forward_file_path, forward_file_max_mb, forward_file_keep, forward_webhook_url, forward_webhook_secret, forward_webhook_secret_file, forward_min_severity, forward_queue_max_mb and forward_queue_max_age_hours (types and defaults are in Server Operations Manual §18)
exec_recorder_* / exec_rule*—Settings for the local exec recorder and the notify-only correlation rules EXR-001 to EXR-007. Enabled by default (disabled on hosts with less than 1 GiB of memory; an explicit setting wins). The keys are exec_recorder_enabled, exec_recorder_retention_mb, exec_recorder_retention_days, exec_recorder_hash_chain, exec_recorder_hash_exe, exec_recorder_cpu_budget_pct, exec_rules_enabled, exec_rules_disabled, exec_rule_service_users, exec_rule_window_secs and exec_rules_isolation_optin (default false; only when true do high-severity rules trigger isolation)
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)

The server edition uses an INI-format configuration file protected with permissions 0600 (root only). The type, allowed values, default and restart requirement of every key are in Server Operations Manual §18. Change keys with roamswitch server config show|get|set or in the config editor of roamswitch server tui (all 146 keys of server.conf and guard.yaml). The server edition does not support reloading its configuration, so apply changes with sudo roamswitch server restart.

Shared machines: admin-only mode

On a machine shared by several people, sudo touch /etc/roamswitch/admin-only turns on admin-only mode. Changes that affect the whole machine (firewall, DNS, VPN, USB, kernel and mount hardening, radios, Sensor trust and the like) are then limited to root or users in the sudo, wheel or roamswitch group. Actions tied to your own files and processes (isolation, kill, freeze, scans) work as before. /etc/roamswitch/admin-only counts only when it is owned by root and not writable by others (a user cannot create it and switch the mode themselves). It is off by default (no file).

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.sockServerThe Falco / Tetragon eBPF integration socket (root:root, mode 0600; Falco also runs as root, so no extra permission setup is needed)
/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.dbServerFIM SHA-256 baseline hash database
~/.local/share/roamswitch/quarantine/BothQuarantine Vault (0700, samples 0400) + .metadata.json
/var/lib/roamswitch/exec_log/BothThe exec recorder’s hash-chained JSON Lines (root only, 0600). Search and verify with roamswitch events
/run/roamswitch/airgap_isolation.json / host_isolation.jsonBothThe isolation (Air-Gap) state (FULL / DEGRADED) and trigger record; the isolation survives a daemon restart

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 / verify_security_findings / get_exposed_ports / get_guard_status / audit_url_safety / audit_secrets / audit_security_logs / audit_mcp_configs / audit_agent_setup / 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_port_scan_incidents / get_ssh_guard_incidents / get_canary_status / get_vulnerability_scan_history / get_notification_history / search_exec_events / get_process_tree / get_incident_timeline / get_fleet_status / get_vpn_status / get_link_guard_status / get_air_gap_status / get_sharing_services_status / get_bluetooth_guard_status / get_usb_guard_status / run_active_vuln_scan / run_package_cve_scan / run_package_cve_scan_languages / run_package_lifecycle_script_scan / run_typosquat_scan / run_npm_audit_signatures / get_sensor_audit_results).

It does not connect to external hosts and talks locally to the daemon's Unix socket or calls roamswitch-core directly. get_security_report and verify_security_findings send nothing to the LAN either (the gateway is only looked up in the ARP cache and other local information). 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