Discovery Agent Overview
The FreeSDN Agent (freesdn-agent) is a lightweight network discovery daemon that runs on a machine inside your LAN and feeds real device intelligence back to the FreeSDN control plane.
Why an agent?
Section titled “Why an agent?”The FreeSDN control plane runs as a Docker stack. Docker containers operate in isolated network namespaces with Layer 3+ access only - they cannot open raw sockets, listen for Layer 2 frames (LLDP/CDP/ARP), enter promiscuous mode, or bind privileged ports like UDP 162 (SNMP traps) or UDP 514 (syslog) without special host-mode setup.
An agent running as a native system service on a host inside the LAN has full OS-level access:
| Capability | Docker container | FreeSDN Agent |
|---|---|---|
| Raw sockets / ARP | No | Yes |
| L2 frames (LLDP, CDP) | No | Yes |
| Promiscuous mode | No | Yes |
| SNMP trap receiver (UDP 162) | Port-map only | Yes |
| Syslog receiver (UDP 514) | Port-map only | Yes |
| DHCP snooping | No | Yes |
| Multi-VLAN access | No | Yes |
| mDNS / SSDP / ONVIF multicast | Partial | Yes |
A single control plane can manage agents at multiple sites simultaneously - each agent scans its local LAN and streams results back over an outbound HTTPS/WebSocket connection, with no inbound firewall holes required.
Two form factors, one scanner engine
Section titled “Two form factors, one scanner engine”| Daemon | Desktop app | |
|---|---|---|
| Mode | Headless system service, runs 24/7 | Interactive PySide6 GUI application |
| Target user | Sysadmin deploying at a remote site | Network technician on-site |
| Connection | Persistent WebSocket to control plane | REST API |
| Platforms | Windows, Linux, macOS (Py >= 3.11) | Windows, Linux, macOS |
| Approximate size | ~30 MB (no Qt) | ~150 MB (includes Qt) |
Both share the same scanner engine. The 14 active scanners in freesdn_agent/scanners/ are pure Python with zero Qt dependency - only the orchestration layer differs (asyncio event loop for the daemon, QThread for the desktop app).
The 14 active scanners
Section titled “The 14 active scanners”| Scanner | Protocol / Layer | Root required? |
|---|---|---|
arp | ARP (L2) | Yes - raw L2 sockets via Scapy |
ping | ICMP via subprocess | No |
tcp_port | TCP connect | No |
http_service | HTTP/HTTPS (L7) | No |
banner | SSH/Telnet banner grab | No |
snmp | SNMP v1/v2c (UDP 161) | No |
netbios | NetBIOS/SMB (UDP 137) | No |
mdns | mDNS/Bonjour (UDP 5353) | No |
ssdp | SSDP/UPnP (UDP 1900) | No |
sip | SIP OPTIONS (UDP 5060) | No |
dns | DNS reverse lookup | No |
rtsp | RTSP (TCP 554) | No |
onvif | ONVIF WS-Discovery (UDP 3702) | No (Scapy library needed) |
sadp | Hikvision SADP (UDP 37020) | No (Scapy library needed) |
The 5 passive listeners
Section titled “The 5 passive listeners”Passive listeners run as long-lived background tasks alongside the daemon. They are off by default and must be enabled in PassiveConfig.
| Listener | What it captures | Root required? |
|---|---|---|
lldp | LLDP neighbor frames (ethertype 0x88CC) | Yes |
cdp | Cisco CDP frames (multicast 01:00:0C:CC:CC:CC) | Yes |
snmp_trap | SNMP trap PDUs (UDP 162) | Yes (privileged port) |
syslog | Syslog messages (UDP 514) | Yes (privileged port) |
dhcp | DHCP traffic (UDP 67/68) - detects new devices via DHCP DISCOVER/REQUEST/ACK | Yes |
Capability advertisement
Section titled “Capability advertisement”With each heartbeat the agent sends a capability advertisement to the control plane so the UI knows exactly what this agent can run:
{ "scanners": ["arp", "icmp", "ports", "http", "banner", "netbios", "sip", "mdns", "ssdp", "snmp", "onvif", "sadp"], "listeners": ["lldp", "cdp"], "scan_types": ["quick", "camera", "voip", "iot", "port", "windows", "full"], "platform": "linux", "python_version": "3.12.3", "agent_version": "1.0.0", "has_scapy": true, "has_root": true, "interfaces": ["eth0", "eth1"]}The backend uses scan_types to filter the scan-type choices when creating a scheduled scan for a specific agent - if the agent lacks Scapy, the camera scan type is not offered.
Control plane connection
Section titled “Control plane connection”The agent maintains a persistent WebSocket to the control plane at:
wss://<server>/api/v1/agents/ws/<agent_id>The agent_id is embedded in the WebSocket URL path. Immediately after connecting, the agent sends a JSON authentication message:
{"agent_key": "<key>", "site_id": "<uuid>"}The backend verifies the key against the stored SHA-256 hash. The agent key is a 32-byte random token issued once at registration; the backend stores only its SHA-256 hash. The key is kept in the OS keyring (Windows Credential Manager, macOS Keychain, Linux Secret Service) - never in plaintext config files.
Commands flow server → agent (e.g. scan_network, fingerprint_device). Results, heartbeats, and scan progress flow agent → server over the same connection. If the connection drops, the agent reconnects with exponential backoff (initial 5 s, max 5 min).
Required outbound access: HTTPS (port 443) to your FreeSDN server. No inbound ports are needed on the agent machine.
Heartbeat
Section titled “Heartbeat”Every 30 seconds (configurable, 10 s-1 h) the agent sends a heartbeat containing CPU/memory/disk utilisation, active task count, uptime, and current subnet list. (A managed_devices field is present in the payload but is not available in this release; the daemon always emits 0 for that value.) Heartbeats are stored in the TimescaleDB time-series DB and drive the online/offline/error status indicator in the Agents UI.
Next steps
Section titled “Next steps”All product names, logos, and brands are property of their respective owners. FreeSDN is an independent project and is not affiliated with or endorsed by the vendors it integrates with. See Trademarks.