RoamSwitch Sensor Operations Manual
The setup and operations guide for RoamSwitch Sensor — a network sensor that, once placed on your LAN, detects new devices and spoofing on the same segment, and runs active vulnerability audits against any endpoint already running RoamSwitch (Mac / Linux Client / Server Edition). Distributed via apt / dnf packages.
1. Overview & Positioning
RoamSwitch (Mac / Linux Client / Server Edition) is, in every case, a host-resident agent. That form factor has two structural blind spots.
- A single host's self-diagnosis has a fundamental blind spot: lateral-movement risk — "closed to the outside, but wide open to another device on the same LAN" — can never be detected by a host that only ever looks at itself.
- Some devices can never run RoamSwitch: IoT devices (network cameras, smart plugs, multifunction printers), guest/BYOD devices, network equipment itself, embedded devices, and more.
RoamSwitch Sensor is a dedicated node you place on the LAN to close these two gaps. What is implemented and verified today is: active vulnerability audits of RoamSwitch-enabled endpoints on the same LAN; a device ledger of the LAN built from an ARP sweep and ARP-table monitoring, with detection of new devices and spoofing; and reference information about suspicious behaviour (connections to malicious IPs, signs of botnet participation, DNS tunnelling and more) from opt-in passive capture (see §6). The Sensor only detects and reports; it never blocks traffic. It is designed as a lightweight NDR (Network Detection & Response) combined with an in-house proof-based vulnerability scanner — not as an EDR.
updates.fetch_cve_map, default true; set it to false for fully offline operation, using only the bundled data). This traffic carries no device or audit data. (2) Notifications and aggregation to destinations you configure: webhooks, syslog and a central collector receive data only when you configure them, and only at your own destinations (nothing is sent by default). Pairing is completed locally; no cloud registration or account is needed.Self-defense is intentionally not implemented (a deliberate design decision). If you need to protect the Sensor host itself from attack, we recommend separately installing RoamSwitch for Linux Server Edition on the same machine. The two run as fully independent processes and coexist without conflict.
2. Current Verification Environment
- Supported OS: Debian 12 (bookworm)+ / Ubuntu 22.04+ (apt), Fedora / RHEL family (dnf).
x86_64(amd64) only —arm64is not yet supported. - Target hardware (future plan): for real-world operation we envision a low-power, always-on-friendly device such as a fanless N100/N150-class mini PC, but no dedicated build or delivery targeting this is provided today.
- Network placement: it must be attached, physically or logically, to the LAN segment you want to watch (
roamswitch-sensor.serviceruns on the host network because it has to observe the ARP table directly). To accept pairing and audit requests from endpoints it must also run on a fixed IP address (a static assignment or a DHCP reservation). It listens on TCP port50543(on all interfaces by default; change it withcontrol.bind_addr; TLS 1.3 is accepted on the same port — see §4.4). - Resource usage: resident memory of a few tens of MB; idle CPU load is extremely light.
- Recommended:
nmap(used for the ARP sweep, the NSE supplementary checks and the MAC check of scheduled audits). Without it the other features still work, but no ARP sweep is performed and the Sensor falls back to passive operation that only sees devices it has itself communicated with.
3. Installation & Startup
Install the roamswitch-sensor package from the official, signed package repository. After installation, roamswitch-sensor.service (systemd) is automatically enabled and started.
3.1 APT (Ubuntu / Debian)
# 1. Register the repository signing key
curl -fsSL https://lafine.net/apt/roamswitch-archive-keyring.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/roamswitch-archive-keyring.gpg
# 2. Add the repository
echo "deb [signed-by=/usr/share/keyrings/roamswitch-archive-keyring.gpg] https://lafine.net/apt stable main" \
| sudo tee /etc/apt/sources.list.d/roamswitch.list
# 3. Install
sudo apt update && sudo apt install roamswitch-sensor
3.2 DNF / RPM (Fedora / RHEL)
# 1. Import the GPG key
sudo rpm --import https://lafine.net/rpm/RPM-GPG-KEY-roamswitch
# 2. Add the repository config file
sudo curl -fsSL -o /etc/yum.repos.d/roamswitch.repo https://lafine.net/rpm/fedora/roamswitch.repo
# 3. Install
sudo dnf install roamswitch-sensor
3.3 Verifying It Started
systemctl status roamswitch-sensor.service
sudo roamswitch-sensor status
3.4 Running the CLI / TUI
The CLI (roamswitch-sensor) and the interactive TUI (roamswitch-sensor-tui) can be run directly after installation.
sudo roamswitch-sensor status
sudo roamswitch-sensor-tui
ROAMSWITCH_SENSOR_PASSIVE_CAPTURE_IFACE environment variable (set via systemctl edit roamswitch-sensor.service) to observe Ethernet/IPv4 headers on that interface and detect new devices or contact with known-malicious IPs (see §6.1). Off by default.4. Pairing (Pairing-Code Method)
Out of the box, Sensor trusts nothing. Pairing is done with a single-use pairing code the Sensor operator issues. The trust relationship — “Sensor may run active vulnerability audits against this endpoint” — is established in both directions at once, in a single pairing step (there is no such thing as one-directional trust).
Prerequisite: run Sensor at a fixed IP address (a static assignment, or a DHCP reservation). After pairing, the endpoint always connects directly to the IP address it saw at pairing time (there is no automatic discovery via mDNS or anything else), so if Sensor’s IP later changes you’ll need to re-pair. The endpoint side can keep a dynamic IP.
4.1 Issuing a pairing code (Sensor side)
# Run on the Sensor side (or press c in the TUI)
sudo roamswitch-sensor issue-code
An 8-character single-use code is issued (uppercase letters and digits; the easily confused 0/O/1/I/L are excluded). It expires after 10 minutes and works once, so pass it promptly to the endpoint operator by any out-of-band means (voice, chat). The Sensor’s TLS certificate fingerprint (SHA-256) is shown as well. The endpoint pins that value, so pass it along with the code (you can always see it with roamswitch-sensor pairing show or on the TUI T screen).
4.2 Pairing with the code (endpoint side)
# Endpoint side (roamswitch-linux)
sudo roamswitch sensor pair --addr <SENSOR_IP> --code <CODE> --fingerprint <SHA256_FINGERPRINT>
On the Mac edition you can do the same in the GUI from the menu bar item “🔍 RoamSwitch Sensor Pairing…” by entering the Sensor’s IP address, the pairing code and the certificate fingerprint.
Once paired, every later active audit and result retrieval is authenticated with Ed25519 signatures. A Linux endpoint talks over TLS 1.3 only to a Sensor whose certificate matches the fingerprint you entered, and never falls back to plaintext automatically (§4.4). At this point the endpoint that knew the code and the Sensor that issued it treat each other as trusted.
issue-code.4.3 Manual pairing (registering a known public key directly)
If the Sensor operator already knows an endpoint’s public key and address out-of-band, it can be registered directly on the Sensor side without exchanging a pairing code (normally, use the code-based method in §4.2 and let the endpoint pair itself instead). You can look up your own public key and address with the following commands.
# Check your own public key/address on the endpoint side
sudo roamswitch sensor key
# Check the Sensor's own public key/address/MAC address
sudo roamswitch-sensor status
# Sensor side
sudo roamswitch-sensor pair <full public key> --addr <IP address> --confirm
4.4 Control-API (TCP 50543) Security: TLS, Fingerprint, Lockout
Pairing, audit requests and result retrieval go through the Sensor’s control API (TCP 50543). The following protections are built in.
- TLS 1.3 and a certificate pin: the
control.tlssetting (applied on daemon restart) isoff(plaintext only),optional(the default: TLS 1.3 and legacy plaintext on the same port, with a warning each time plaintext is accepted) orrequired(TLS 1.3 only). The certificate is self-signed (ECDSA P-256) and trust is decided by a pin of its SHA-256 fingerprint, not by expiry or name. The pin is never fetched over the network: the operator types the value shown on the Sensor’s screen into the endpoint. - Fingerprint binding of the pairing request: the endpoint signs the fingerprint it pinned with its own private key and includes it in the pairing request. A request that passed through a man-in-the-middle with a different certificate fails with
fingerprint_mismatch, and the pairing code is not consumed. - Replay-safe signatures (v2): audit-request and result-retrieval signatures use the v2 format, which includes a timestamp (±300 s) and a nonce, so replays are refused. Legacy v1 signatures carry neither and can be replayed;
control.allow_legacy_signatures(defaulttrue, meant for the migration period; every use logs a deprecation warning) decides whether they are accepted. Set it tofalseonce every endpoint sends v2. - Lockout: guessing pairing codes locks a source IP out for 60 seconds after 5 failures within 10 minutes (extended exponentially on repeats, up to 1 hour), and 20 failures across all sources trigger a global lockout. Signature failures are handled a little more leniently (10 per source IP, 100 in total). A lockout raises the notification
control_api_lockout. - Listen address: narrowing
control.bind_addr(default0.0.0.0, applied on restart) to the Sensor’s LAN address reduces exposure. The daemon warns while it is a wildcard address. - Certificate rotation:
roamswitch-sensor tls rotate(on the TUITscreen, pressR) regenerates the certificate. Every pin of the old fingerprint becomes invalid, so each endpoint must enter the new value (sudo roamswitch sensor repin <public key> --fingerprint <value>). The pairing itself is kept.
sudo roamswitch-sensor pairing show # mode, fingerprint, warnings
sudo roamswitch-sensor tls rotate # regenerate the certificate
sudo roamswitch sensor repin <PUBLIC_KEY> --fingerprint <NEW_FINGERPRINT> # on each endpoint
control.tls to requiredroamswitch-sensor status no longer shows a plaintext-connection warning → (3) run sudo roamswitch-sensor config set control.tls required → restart the daemon. off is normally not used, because Linux endpoints require TLS and could no longer connect.5. Active Vulnerability Audit
Sensor runs non-destructive, proof-based vulnerability diagnostics against paired endpoints. It never performs destructive operations (writing or deleting data, stopping services).
sudo roamswitch-sensor scan <public key or its prefix>
The audit consists of four phases.
- Full port scan: exhaustively detects every open TCP port on the target host.
- Known-signature diagnostics: non-destructive, proof-based checks against known vulnerability patterns — unauthenticated exposure of Redis / dockerd / Memcached / MongoDB / Elasticsearch / CouchDB / Jenkins / VNC, SMTP open-relay diagnostics (a safe method that sends only
MAIL FROM/RCPT TO, neverDATA), and dev-server CORS misconfiguration, path traversal, and open-redirect diagnostics, among others. - Generic banner grab: for open ports not covered by the signatures above, retrieves the banner string by connecting only — no data is sent.
- Supplementary nmap NSE diagnostics: broad, wide-protocol supplementary diagnostics via
nmap --script safe. Runs automatically whenevernmapis installed on the host (a no-op otherwise).
Every audit run is recorded to /var/lib/roamswitch-sensor/scan_history.json — regardless of whether anything was found, since a clean result at that point in time also has record value — and can be viewed from the CLI or TUI.
roamswitch-sensor history
sudo roamswitch-sensor report <public key or its prefix> --out /tmp/report.md
5.1 Requesting an audit from the client side (pull model)
A paired endpoint can also actively request an audit from Sensor. On the Mac edition, use “Request Audit from Sensor” in the menu bar; on Linux, use the following command.
sudo roamswitch sensor request-audit
After Sensor runs the audit, the endpoint polls for the result starting 5 minutes after the request, then every 5 minutes for up to 5 attempts (so up to 25 minutes total). Whatever result is retrieved is also saved locally on the endpoint.
roamswitch sensor results
On the Mac edition, results also appear in the settings window’s “Audit Results” list, or can be read through the MCP tool get_sensor_audit_results as input for an AI agent planning a remediation. Result retrieval can also be explicitly rejected — for example, if the pairing was revoked on the Sensor’s side (see §10 Q6).
6. Passive ARP Monitoring & LAN Visibility
The Sensor learns about LAN devices in two ways. (1) ARP-table monitoring: it reads its own ARP table (/proc/net/arp) about every 10 seconds and detects “a new IP/MAC never seen before” and “a change of MAC address for a known IP” (a sign of gateway spoofing and the like). (2) An active ARP sweep: nmap -sn -PR sweeps the directly attached subnet (up to a /22) and stores the IP, MAC, host name, vendor, online state, first/last seen and IP history in the device ledger. The sweep runs hourly, on demand (refresh / the TUI r key) and when the TUI starts; without nmap only the passive behaviour (1) remains. The vendor and a device class (IoT or not, etc.) are also inferred from the MAC’s OUI.
roamswitch-sensor arp-events
Detected events are shown by roamswitch-sensor arp-events and on the TUI “ARP Events” tab (Enter for details). Each event carries its time, a severity (critical to info; a MAC change of the gateway IP is “critical”), the reasoning, the identity of the devices involved and concrete next steps.
6.1 Passive LAN Visibility Extension (Opt-in)
While the ARP monitoring above only covers devices Sensor has itself communicated with, setting the ROAMSWITCH_SENSOR_PASSIVE_CAPTURE_IFACE environment variable to a target interface observes Ethernet/IPv4 headers on it (no payload inspection) to detect the following.
- Passive observation of new devices: devices Sensor has never directly communicated with, not covered by ARP monitoring alone. Learned from broadcast/multicast traffic.
- Detecting contact with known-malicious IPs: flags traffic whose destination IP matches the local threat feed (same format as RoamSwitch proper's Egress Guard) — detection only, never blocked.
sudo systemctl edit roamswitch-sensor.service
# [Service]
# Environment=ROAMSWITCH_SENSOR_PASSIVE_CAPTURE_IFACE=eth0
sudo systemctl restart roamswitch-sensor.service
roamswitch-sensor passive-events
6.2 Device Ledger and Device Notes
View the ledger with roamswitch-sensor device-inventory (the TUI “Network” tab). Registering a note such as a name, purpose or location with device-note (the n key in the TUI) marks the device as “accounted for”, while devices without a note are highlighted as “to check” (for reconciling against your asset register).
sudo roamswitch-sensor refresh
roamswitch-sensor device-inventory
sudo roamswitch-sensor device-note aa:bb:cc:dd:ee:ff "3F copier"
6.3 Reference Information on Suspicious Behaviour (Detection Only)
With passive capture (§6.1, opt-in) enabled, the following reference information is presented. All of it is detection and display only; the Sensor never blocks traffic.
- IoT device behaviour (
iot-events): connections from classified devices to known malicious IPs or to management ports (SSH/RDP/SMB and so on). Model identification from the devices’ own mDNS/SSDP/DHCP announcements is used as well. - Suspicious traffic (
network-threats): connections to many destinations within a 30-second window (a sign of scanning or a stepping stone), a flood of connections to one destination (a sign of DDoS participation) and signs of DNS tunnelling/C2 (only the structural features of query names are used; domain names are not stored). - Advisories (
iot-advisories): reference information about public CVEs for known IoT vendors’ products (bundled statically; there is no network update).
7. CLI Command Reference
| Command | Description |
|---|---|
roamswitch-sensor status |
Show the Sensor’s own public key, IP and MAC address, the number of paired endpoints, ARP events and pending audit requests, and the control-API TLS state |
roamswitch-sensor issue-code |
Issue a single-use pairing code (valid for 10 minutes); also shows the Sensor’s certificate fingerprint |
roamswitch-sensor list |
Lists all paired endpoints |
roamswitch-sensor pair <public-key> [--addr <IP>] [--name <name>] --confirm |
Manually pairs an endpoint by specifying its public key and address directly (normally, use issue-code and let the endpoint pair itself instead — it’s simpler) |
roamswitch-sensor unpair <public-key> |
Removes a pairing |
roamswitch-sensor scan <public-key> |
Runs an active vulnerability audit against a paired endpoint |
roamswitch-sensor history [public-key] |
Shows audit history (omit the public key to see all endpoints) |
roamswitch-sensor report <public-key> [--out <file>] |
Outputs the most recent audit result as a Markdown report |
roamswitch-sensor arp-events |
Shows detected ARP events (new devices, suspected spoofing) |
roamswitch-sensor pairing show / tls show |
Show the control-API TLS state (mode, fingerprint, plaintext-connection warning, listen IP, whether legacy v1 signatures are accepted); pairing show and tls show are equivalent |
roamswitch-sensor tls rotate |
Regenerate the TLS certificate (every pin of the old fingerprint becomes invalid; endpoints must re-pin) |
roamswitch-sensor refresh |
Rescan the LAN now (ARP sweep) and update the device ledger |
roamswitch-sensor device-inventory |
Show the device ledger (every device observed on the LAN, its online state and note) |
roamswitch-sensor device-note <MAC> [note] |
Register a note for a device (omit the note to delete it) |
roamswitch-sensor passive-events / iot-events / network-threats / iot-advisories |
Show the events from passive capture (new devices and connections to malicious IPs / IoT behaviour / suspicious traffic / IoT advisories) |
roamswitch-sensor config [show | set <key> <value>] |
Show or change settings (scheduled audits, notifications, retention, collector, control API, detection-data fetching, language). control.bind_addr and control.tls take effect on daemon restart; the others need no restart (§11.8) |
roamswitch-sensor diff [public key] |
Show what changed since the previous audit (new findings, resolved ones, newly open ports, suspected unreachable) |
roamswitch-sensor export <scans|inventory|audit-log|unmanaged|all> … |
Write audit evidence as CSV / JSON / printable HTML |
roamswitch-sensor audit-log [verify] |
Show the operation and approval log; verify detects tampering with the hash chain |
roamswitch-sensor notify-test |
Send a test notification to the configured destinations (webhook / syslog) to verify connectivity |
roamswitch-sensor audit-unmanaged <IP|MAC> --confirmroamswitch-sensor unmanaged-audits |
Light audit of devices without RoamSwitch (connect checks only). audit-unmanaged <IP or MAC> --confirm runs one by hand, unmanaged-audits [--detail] lists the results (§13) |
roamswitch-sensor lockdown [status | on | off] |
Show whether the Sensor is locked down (status), lock it down (on --reason TEXT) or release it (off). Locked down, it stops pairing, audits, uploads and notifications (§14) |
roamswitch-sensor decommission --confirm |
Erase this Sensor's keys and stored data and lock it down (cannot be undone). The audit log is kept, and the endpoints that were paired with it are listed with how to unpair them (§14) |
The CLI supports only Japanese and English (it follows the LANG environment variable).
8. TUI Operation Guide
The interactive TUI (roamswitch-sensor-tui) supports 10 languages and can operate every CLI subcommand, every daemon IPC method, every configuration key and the collector (press m for the feature menu). Six main tabs are switched with the Tab key (Shift+Tab goes back): Paired (the latest audit state of each endpoint) / Network (every device on the LAN, with notes) / ARP Events / Scan History / Diff (changes since the previous audit; new findings in red, resolved in green, suspected unreachable in yellow) / Audit Log (who approved or ran what and when; the hash-chain verification result is shown at the top). Views that do not fit in 80 columns (passive monitoring, IoT behaviour, suspicious traffic, IoT advisories) open from the m menu.
| Key | Action |
|---|---|
Tab | Switches tabs |
↑↓ / j k | Selects an item |
c | Issue a pairing code (valid for 10 minutes). The screen shows the command to run on the endpoint, sudo roamswitch sensor pair --addr <this Sensor’s IP> --code <code> --fingerprint <fingerprint>, with the real values filled in |
u | Removes a pairing |
s | Runs an active vulnerability audit against the selected endpoint |
a | (Network devices tab) Light-audit the selected device, after a confirmation screen (§13). Results are in the "Unmanaged audits" tab of the menu |
K | Lock down or release this Sensor, after a confirmation screen; a red notice stays at the bottom of every screen while locked down. Decommissioning is in the menu (§14) |
n | (Network tab) Add a note — name, purpose, location — to the selected device; a device with a note counts as accounted for |
r | Re-scan the LAN now (ARP sweep; it otherwise runs automatically every hour) |
e | Export audit evidence (CSV of scan results, the device ledger and the audit log; an HTML report; JSON). A form lets you choose the content, format, endpoint, start date and destination; files are produced in the display language (10 languages). The default destination is ~/.local/state/roamswitch-sensor/; files are created new and readable only by the owner (an existing file is never overwritten), and the export itself is recorded in the audit log |
p | Manual pairing (enter the public key and address, then a confirmation screen, then run). The form shows the Sensor’s TLS fingerprint that the endpoint pins |
T | TLS and pairing state (mode, fingerprint, plaintext-connection warning, listen IP, whether legacy v1 signatures are accepted). R regenerates the certificate (with a confirmation screen that explains the impact) |
C | Config editor (shows and changes every configuration key; webhook URLs are always masked; keys that need a restart are marked) |
N | Notification test (shows success or failure per destination; URLs are masked) |
m | Feature menu (passive monitoring, IoT behaviour, suspicious traffic, IoT advisories, status i, collector management and more) |
O | Collector management (only on a host that has the collector’s data directory): enroll, revoke and list Sensors, the read token, and clearing an audit-log anomaly |
f / l / v | (Scan History tab) filter by endpoint. (Audit Log tab) l changes how many rows are shown and v re-verifies the hash chain |
Enter | Show details for the selected row (audit history, changes, ARP events, devices, audit log) |
w | (In a detail view) write the report to a file at a destination you choose (produced in the display language) |
L | Choose the display language (saved to the language setting, so it also applies to notifications and logs produced by the daemon) |
q | Quits |
9. Trust Model & Security Design
- Out-of-band confirmation (not TOFU): the operator passes the certificate fingerprint shown on the Sensor’s screen and the pairing code to the endpoint by voice, chat or similar. The endpoint never fetches the pin over the network (it is not TOFU, which trusts the first connection unconditionally) and talks to, and pairs with, only a Sensor whose certificate matches the value entered. The public keys exchanged at pairing verify every later probe and request.
- Designed to find holes that would allow destructive actions, without ever performing a destructive action itself: every active vulnerability audit is non-destructive (it never writes or deletes data, or stops a service). What it does detect are the misconfigurations themselves — things like “writable without authentication” or “functions as an open relay” — that, if exploited, would allow a destructive action.
- False-positive mitigation (self-audit exclusion): a source-IP match exclusion is built in so that a paired Sensor's own full-port audit is not mistaken for reconnaissance by the incoming-port-scan-detection guard (a RoamSwitch-side feature). The IP is used only for this match, purely to avoid an automatic block — it is never used for trust determination itself. In addition, only from the moment this endpoint asks a Sensor for an audit until its report arrives (at most about 25 minutes if no report comes), the Mac edition words its detections from the Sensor's address, and its "SYN flood" warning (which has no source address), as most likely caused by the audit, in calmer language; the Linux edition does the same for the "SYN flood" warning. Outside that window the strong warning stays as before, and audits the Sensor starts on its own are not covered.
- Outbound traffic is limited, and device data is not sent by default: discovered device information, audit results and ARP events are stored only in the Sensor’s own local storage by default and are never sent to Lafine or any third party. The only outbound traffic is fetching detection data (the CVE map and the nmap NSE database; it can be stopped with
updates.fetch_cve_mapand contains no device or audit data) and the notifications (webhook, syslog) and central-collector uploads of §11, which go only to your own destinations and only when you configure them.
10. Troubleshooting & FAQ
Q1. Entering a pairing code gets rejected.
There are four likely causes: (1) the code expired 10 minutes after issue (ask the Sensor operator to issue a new one with issue-code), (2) a typo in the code (the easily confused 0/O/1/I/L are excluded when codes are issued, so a code never contains them), (3) the IP address given to --addr does not match the Sensor’s current fixed IP address, (4) --fingerprint does not match the Sensor’s certificate (pin_mismatch / fingerprint_mismatch; check the value from roamswitch-sensor pairing show. Repeated mistakes lock you out and return rate_limited).
Q2. The NSE supplementary diagnostic result is empty.
One of two causes: (1) nmap is not installed in the container; (2) it did run, but the safe scripts applicable to the target ports genuinely produced no output. nmap NSE supplementary diagnostics run automatically whenever nmap is installed on the host.
Q3. How long does an audit take to complete?
A full port scan alone takes roughly tens of seconds, but when nmap is installed on the host, its supplementary NSE diagnostics run automatically and add to that, taking up to about 2 minutes depending on how many open ports the target has. The TUI keeps showing the elapsed seconds while it runs, so you can watch progress as you wait.
Q4. I paired, but I still can't run an audit.
Likely causes: (1) sensor_pairing_enabled: true is not set on the endpoint (off by default), (2) the Sensor’s fixed IP address changed after pairing and the endpoint can no longer reach the old address it holds (re-pair), (3) the pairing was removed on the Sensor with unpair, (4) the Sensor regenerated its certificate (tls rotate), so the endpoint’s pin is stale (enter the new fingerprint with roamswitch sensor repin). Check with both roamswitch sensor list (endpoint) and roamswitch-sensor list (Sensor) that each side lists the other.
Q5. Can Sensor itself be used alongside RoamSwitch proper (Client/Server Edition)?
Yes. Because Sensor is designed without self-defense features, we recommend running RoamSwitch for Linux Server Edition alongside it on the same machine. The two operate as completely independent processes and data stores, with no conflicts.
Q6. roamswitch sensor results says the pairing was revoked.
This endpoint’s pairing was removed on the Sensor side with unpair. Ask the Sensor’s operator to issue a new pairing code, then pair again with roamswitch sensor pair --addr <ip> --code <code> --fingerprint <SHA-256>.
11. Organization-Scale Audit Features (Opt-In)
A set of features for security teams in large organizations who need audit evidence and a clear picture of what changed. Every one of them is off by default, and nothing is ever sent to Lafine or any third party — every notification or aggregation destination is one your organization configures. Settings are changed with sudo roamswitch-sensor config set <key> <value> (no daemon restart needed; each change is recorded in the operation log); config show prints the current values.
11.1 Scheduled Audits and Change Detection
Setting schedule.enabled to true audits every paired endpoint automatically (every 24 hours by default: schedule.interval_hours; the local-time window in which audits may start: schedule.window_start_hour / window_end_hour, which may wrap past midnight; concurrency: schedule.max_parallel). Each audit is compared with the previous one, and roamswitch-sensor diff shows new findings (regressions), resolved ones and newly open ports. If every port that used to be open is suddenly not visible, that is reported as suspected unreachable, not as "resolved" — a host that is switched off cannot be told apart from one that closed every port.
Unattended audits never break the guarantee that only consenting endpoints are audited. An endpoint's IP can be handed to a different device by DHCP, so the Sensor stores the MAC address the endpoint had when it last authenticated (pairing, or a signed audit request) and confirms with a live ARP request from nmap — before and after each scheduled audit — that the same MAC still answers at that IP. If it does not, the audit is skipped (or its result discarded) and a notification is raised. A manual scan is also refused when there is evidence the address now belongs to another device. This check requires nmap; endpoints paired before this feature existed are not scheduled until they re-pair or send a signed audit request; and only endpoints on the same L2 segment are covered.
11.2 Notifications (Webhook and syslog)
Regressions, new devices, ARP spoofing, suspicious traffic and more are sent to notify.webhook_urls (Slack, Discord or Teams incoming webhooks, or any generic JSON endpoint) and to notify.syslog.host / port / protocol (RFC 5424 over UDP or TCP). Narrow them with notify.min_severity (info / medium / high / critical) and notify.cooldown_minutes (minimum interval before the same event is sent again). roamswitch-sensor notify-test checks each destination.
11.3 Exporting Audit Evidence
roamswitch-sensor export <scans|inventory|audit-log|all> --format csv|json|html [--out FILE] [--endpoint KEY-PREFIX] [--since DATE]. The html output is a single file: open it in a browser and choose “Save as PDF” to get a PDF you can hand in. CSV is protected against spreadsheet formula injection. The device ledger (inventory) has a column showing whether each device is managed by RoamSwitch (paired). Output files are readable only by their owner (0600), and the export itself is recorded in the audit log. The TUI e key takes the same choices in a form, and reports and exports are produced in the display language (10 languages; the CLI is Japanese and English).
11.4 Operation and Approval Log (Tamper-Evident)
Pairing, unpairing, audit start and completion, configuration changes and rejected access attempts are recorded with who did it (UID / endpoint public key / source IP). Each line contains the SHA-256 of the previous one, so any edit or deletion is detected by roamswitch-sensor audit-log verify (exit code 2 when something is wrong). List entries with roamswitch-sensor audit-log [--limit N]. Webhook URLs often embed a token, so configuration-change entries never contain their values. A hash chain alone cannot reveal that the newest entries were cut off, so the chain head (sequence number and hash) is also sent to the collector (§11.6).
11.5 Retention
Set retention.scan_history_days, retention.audit_log_days and retention.inventory_stale_days to have old audit history, operation-log entries and long-unseen devices deleted automatically (0 = never delete by age, the default). Devices that have a note are never removed from the inventory.
11.6 Aggregating Several Sensors (Central Collector, Optional)
When several Sensors run at several sites, run roamswitch-sensor-collector (shipped in the package, disabled by default) on a separate host to collect signed summaries from each of them.
# On the collector host (a separate host from the Sensor is recommended)
sudo systemctl enable --now roamswitch-sensor-collector # listens on 127.0.0.1:8443 by default
sudo roamswitch-sensor-collector token # read token for the dashboard
sudo roamswitch-sensor-collector enroll <Sensor public key> --name Tokyo --site Tokyo
# On each Sensor
sudo roamswitch-sensor config set collector.url https://collector.example.org:8443
sudo roamswitch-sensor config set collector.enabled true
- Only signed requests from Sensors you have enrolled (
enroll) are accepted, using their Ed25519 identity; the timestamp also rejects replays. - TLS is required whenever it is reachable from outside the LAN. Put
--bind 0.0.0.0:8443 --tls-cert … --tls-key …inCOLLECTOR_ARGSin/etc/default/roamswitch-sensor-collector, or use--allow-plain-httpbehind a TLS-terminating reverse proxy. Listening on anything other than 127.0.0.1 without TLS is refused at startup. - The dashboard is protected by a token and offers per-site filtering, events by severity and CSV export. It highlights Sensors that have gone silent and Sensors whose audit log looks truncated, rewound or forked (clear it with
clear-anomaly <public-key>once investigated). - With
collector.send_unmanaged_devices(defaulttrue) the Sensor includes devices that are not paired (unmanaged devices) in the summary. Each device is classified as an “unpaired RoamSwitch host” (when RoamSwitch appears in its mDNS/SSDP self-announcement; enablingcollector.probe_control_portalso makes one TCP connection attempt to control port 50543, off by default) or an “unknown device”, and only the MAC, vendor, device class and first-seen time are sent (never any traffic content). In the TUI,Oopens the collector management screen.
11.7 Fetching Detection Data (updates.fetch_cve_map)
The Sensor fetches the signed CVE map (the known-vulnerability data used by active checks) and the nmap NSE database from the update service every day. This traffic carries no device or audit data. To run fully offline, run sudo roamswitch-sensor config set updates.fetch_cve_map false (the change takes effect from the next cycle without a restart, and only the bundled data is used from then on).
11.8 Configuration Key Reference
Check current values with roamswitch-sensor config show and change them with config set <key> <value> [<key> <value> …] (several keys are saved atomically; the TUI C config editor also changes them). Only keys marked “Yes” in the Restart column need a daemon restart (sudo systemctl restart roamswitch-sensor).
| Key | Type / values | Default | Restart |
|---|---|---|---|
site | string | (empty) | No |
language | ja | en | ko | zh-Hans | zh-Hant | de | fr | es | it | pt-PT | (empty = system locale) | No |
schedule.enabled | bool | false | No |
schedule.interval_hours | int | 24 | No |
schedule.window_start_hour | int 0–23 | (unset = always) | No |
schedule.window_end_hour | int 0–23 | (unset = always) | No |
schedule.max_parallel | int 1–8 | 1 | No |
notify.webhook_urls | URL list | (empty) | No |
notify.min_severity | info | medium | high | critical | medium | No |
notify.cooldown_minutes | int | 30 | No |
notify.syslog.host | string | (empty = off) | No |
notify.syslog.port | int | 514 | No |
notify.syslog.protocol | udp | tcp | udp | No |
retention.scan_history_days | int (0 = never prune) | 0 | No |
retention.audit_log_days | int (0 = never prune) | 0 | No |
retention.inventory_stale_days | int (0 = never prune) | 0 | No |
collector.enabled | bool | false | No |
collector.url | URL | (empty) | No |
collector.push_interval_minutes | int | 15 | No |
collector.send_unmanaged_devices | bool | true | No |
collector.probe_control_port | bool | false | No |
control.bind_addr | IP address | 0.0.0.0 | Yes |
control.tls | off | optional | required | optional | Yes |
control.allow_legacy_signatures | bool | true | No |
updates.fetch_cve_map | bool | true | No |
control.listen_ipv6 | bool | false | Yes |
dhcp.active_probe | bool | true | No |
dhcp.allowed_servers | MAC / IP list | (empty = learn the first) | No |
ipv6.enabled | bool | true | No |
ipv6.active_probe | bool | true | No |
ipv6.allowed_routers | MAC / IP list | (empty = trust the IPv4 gateway) | No |
unmanaged_audit.enabled | bool (needs authorization + allowed_cidrs first) | false | No |
unmanaged_audit.authorization | string | (empty) | No |
unmanaged_audit.allowed_cidrs | CIDR list (IPv4 ≤ /16 and IPv6 ≤ /48 refused) | (empty) | No |
unmanaged_audit.exclude | IP / CIDR / MAC list | (empty) | No |
unmanaged_audit.interval_hours | int | 168 | No |
unmanaged_audit.max_parallel | int 1–4 | 2 | No |
unmanaged_audit.product_check | bool (needs unmanaged_audit.enabled) | false | No |
security.on_revoked | lockdown | wipe | none | lockdown | No |
security.lockdown_after_offline_days | int (0 = off) | 0 | No |
12. Rogue DHCP and IPv6 Detection
This finds another router, or an attacker's machine, that has been brought onto the network and poses as the legitimate one to take over where clients' traffic goes. It only detects and notifies; the Sensor never blocks traffic. It is on by default and needs no passive-capture setup. Results appear in roamswitch-sensor network-threats, the TUI's Threats tab (from m), notifications (dhcp_rogue_server, ipv6_rogue_router, ndp_spoof, ipv6_unknown_device), the operation log and the exports.
12.1 Rogue DHCP Servers (IPv4 and DHCPv6)
Every hour (and on each roamswitch-sensor refresh) the Sensor sends one DHCP discover packet (a SOLICIT for DHCPv6) from a random MAC address and counts every server that answers. It sends no REQUEST, so no address lease is taken. DHCP replies seen by the passive capture go through the same judgement. The first server it hears is learned as the legitimate one, and any server that appears later is reported. A server that hands out a gateway or DNS server the legitimate one does not takes over where the clients that accept its lease send their traffic, so it is the most serious level, critical. If several servers answer the very first probe at once, the Sensor cannot tell which is genuine, so it learns none and reports all of them.
12.2 IPv6 (Rogue Router Advertisements, NDP Spoofing, IPv6-Only Devices)
Rogue router advertisements: anyone on the LAN can claim to be an IPv6 router. The Sensor combines the kernel's neighbour table and default routes, the router advertisements the passive capture sees, and the answers to its own router solicitation, and reports it when something other than the IPv4 default gateway behaves as a router (list the genuine one in ipv6.allowed_routers to treat it as legitimate; a network whose IPv6 router is a different device from its IPv4 gateway is a normal case, so list it there). Advertisements whose hop limit is not 255 are forged or routed in and are ignored. NDP spoofing: it reports an IPv6 address whose owner's MAC changes to another MAC (high when it is a router's address). IPv6-only devices: a device that never shows up in ARP but appears in the neighbour table is reported after one ARP sweep has had the chance to find it. The device inventory (the ipv6 CSV column, ipv6_addrs in JSON) lists IPv6 addresses as well.
12.3 Who Counts as Legitimate, and the Settings
Set dhcp.allowed_servers and ipv6.allowed_routers (MAC or IP) and everything else that answers is reported. Left empty, the first one found is learned. dhcp.active_probe and ipv6.active_probe (default true) switch the active probes off, and ipv6.enabled switches off all IPv6 monitoring. Listening for the control API (50543) on IPv6 is added with control.listen_ipv6 (default false, applied on restart); a global IPv6 address can be reachable from the Internet when the router has no IPv6 firewall, so it is not enabled by default. Connection limits and lockouts count IPv6 per /64. An endpoint that connected from a link-local address is not remembered as an audit target, because it cannot be dialled back without a scope ID.
13. Light Audit of Devices Without RoamSwitch (Opt-In)
The active vulnerability audit of §5 covers only endpoints that paired and agreed. For printers, cameras, NAS boxes and other devices that never paired, but whose exposed services you still want to know about, there is a separate and far more limited check. Deep diagnostics in the style of OpenVAS, login attempts, exploiting vulnerabilities, nmap NSE and full-range port scans are done only to endpoints that agreed.
13.1 What It Does
It makes a TCP connection to a fixed set of 54 ports (Telnet, FTP, RDP, VNC, SMB, Redis, MongoDB, Docker API, MQTT, RTSP and so on), reads the banner a service sends on its own from the ports that were open (plus one HEAD / to web ports), and adds a note when the vendor has published vulnerabilities (the IoT advisories table). Nothing else. A result says "this service can be reached from the LAN", never "this is vulnerable"; each finding states that whether authentication is enabled was not tested.
13.2 Turning It On
To enable unmanaged_audit.enabled, first set unmanaged_audit.authorization (who approved it: a name, ticket number or policy reference) and unmanaged_audit.allowed_cidrs (the ranges that may be audited; IPv4 ranges wider than /16 and IPv6 ranges wider than /48 are refused). Scheduled runs happen every interval_hours (default 168) inside the schedule window. A manual run needs --confirm as the second consent.
sudo roamswitch-sensor config set unmanaged_audit.authorization "IT policy 2026-07, approved by A. Suzuki"
sudo roamswitch-sensor config set unmanaged_audit.allowed_cidrs 192.168.10.0/24
sudo roamswitch-sensor config set unmanaged_audit.exclude 192.168.10.1
sudo roamswitch-sensor config set unmanaged_audit.enabled true
sudo roamswitch-sensor audit-unmanaged 192.168.10.40 --confirm
roamswitch-sensor unmanaged-audits --detail
13.3 Safeguards
Nothing outside allowed_cidrs, no device in unmanaged_audit.exclude (IP, range or MAC), no paired endpoint and not the Sensor itself is ever examined. Before and after a run, the MAC of the device at that address is confirmed with a live query (ARP for IPv4, which needs nmap; neighbour solicitation for IPv6); if it has changed, the run is stopped or the result discarded and a notification is sent. The text of authorization is written to the operation log with every run and appears in the exports (export unmanaged, JSON, the HTML report), so you can show who approved the audit.
13.4 Product check (is it a product with CISA KEV vulnerabilities? Opt-in)
Turning on unmanaged_audit.product_check (off by default) makes the light audit read the product name from the web page a device normally returns and tell you whether that product has vulnerabilities listed in the CISA KEV catalog. All it sends is a GET / to the web ports that accepted a connection (80, 8080, 8181, 443, 8443, 10443), the redirects the device itself points to at the same address (at most two), and a read of the HTTPS certificate's issuer and subject names. It sends no logins, no password attempts, no guessed paths and nothing that exploits a vulnerability. The limit is 12 requests per device, 150 ms apart. The consent conditions (enabled, authorization, allowed_cidrs, exclude, the exclusion of paired devices and the MAC re-check) are exactly those of §13.2–13.3, and if the light audit is off nothing is sent even when product_check is on.
A product is named only when its on-screen markers all appear in the same response. It is reported as affected (high) only when the version the device itself displays can be read and a CVE has an NVD affected range that contains it. When the version cannot be read, the result stays a notice (medium): this product has vulnerabilities listed in the KEV, the version could not be read, so whether it is affected is unconfirmed. It never says the device is vulnerable. Today only MikroTik RouterOS shows its version, so other products normally get the unconfirmed notice. Covered are Fortinet FortiGate, Ivanti Connect Secure, Citrix NetScaler, Cisco (the ASA SSL VPN and the IOS XE web UI), UniFi OS, GeoVision, TerraMaster, Check Point Security Gateway and MikroTik RouterOS; Aruba devices have no entry in the KEV and are not covered. The markers are based on public detection signatures and have not been checked against real devices; a marker that does not match errs on the side of detecting nothing. A copy of the KEV ships with the product and is refreshed, only when both product_check and updates.fetch_cve_map are on, by fetching one public CISA feed file every 24 hours (nothing about your LAN is sent).
14. When a Sensor Is Lost, Stolen or Retired
A Sensor holds the list of vulnerabilities found on each endpoint, the addresses of the paired endpoints and its signing key. The endpoints that paired with it also treat it as an approved scanner, so a stolen Sensor plugged into the same LAN could act as one. The following stops that.
14.1 Revoking at the Collector, and Lockdown
Run roamswitch-sensor-collector revoke <public key> --reason TEXT and from then on that Sensor's uploads are refused with 403 sensor_revoked (given only to requests with a valid signature, so only the holder of the key learns it). A revoked Sensor finds out on its next upload and stops itself (lockdown). While locked down, the control API answers only sensor_locked_down, and pairing, audits (scheduled and manual), uploads to the collector, notifications and probing of the network all stop. The stop is stored in a file, so it survives a restart, and if that file cannot be read the Sensor stays locked. The data is kept, so you can release it with roamswitch-sensor lockdown off (K in the TUI) once you are sure it is back in safe hands; if it is still revoked at the collector it locks again on its next upload. revoke also prints the endpoints that were paired with that Sensor and how to unpair each of them.
14.2 Erasing, Decommissioning, and Offline Detection
With security.on_revoked set to wipe, a revoked Sensor overwrites with zeros and deletes its keys, TLS key, settings (including webhook URLs), the list of paired endpoints, audit results and device inventory. Only the operation log is kept, as evidence. lockdown (the default) stops it and keeps the data, and none only notifies. To retire a Sensor you have in hand, use roamswitch-sensor decommission --confirm (lock down and erase; it cannot be undone). Setting security.lockdown_after_offline_days to a number of days makes a Sensor that could not reach the collector for that long stop itself, as a safeguard for one that is stolen and used on another network; it applies only when the collector upload is enabled and has succeeded at least once, and the default is 0 (off).
14.3 What This Cannot Do
The endpoints that were paired with the Sensor keep trusting it until each one removes the pairing. A Sensor has no way to notify its endpoints, so on each endpoint in the list that revoke and decommission print, run roamswitch sensor unpair <this Sensor's public key> on Linux, or Settings > Sensor > Unpair on Mac. A Sensor carried off while powered down or kept off the network never receives the instruction to stop; disk encryption (LUKS and the like) is the real protection there. Overwriting does not guarantee complete erasure on flash storage or copy-on-write filesystems.