Add LAN device discovery (ping sweep + ARP, known/unknown labeling)
CI / web (push) Successful in 17s
CI / api (push) Successful in 23s

Runs as a host-level systemd timer on CT122 (scripts/discover-devices.sh)
rather than inside the api container, since real ARP entries live in the
host's network namespace, not Docker's bridge network. See
docs/device-discovery.md for the full writeup, including why literal
passive-only ARP reading was dropped (near-empty result in practice).

API reads the resulting JSON file each poll cycle, cross-references
config/hosts.yaml's knownDevices list by IP, and serves /api/devices.
Dashboard gets a new "Network Devices" table.

Closes #9.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 20:49:22 -06:00
parent d75bd58792
commit 04282232cc
15 changed files with 434 additions and 8 deletions
+10
View File
@@ -12,12 +12,18 @@ interface RawSshHost {
diskPaths?: { path: string; label: string }[];
}
interface RawKnownDevice {
ip: string;
name: string;
}
export interface HostsConfig {
proxmox: {
host: string;
node: string;
};
sshHosts?: RawSshHost[];
knownDevices?: RawKnownDevice[];
}
export interface AppConfig {
@@ -39,6 +45,8 @@ export interface AppConfig {
tokenSecret: string;
};
sshHosts: SshHostConfig[];
knownDevices: Map<string, string>;
discoveryFilePath: string;
hosts: HostsConfig;
}
@@ -80,6 +88,8 @@ export function loadConfig(hostsConfigPath: string): AppConfig {
tokenSecret: required("PROXMOX_TOKEN_SECRET"),
},
sshHosts,
knownDevices: new Map((hosts.knownDevices ?? []).map((d) => [d.ip, d.name])),
discoveryFilePath: process.env.DISCOVERY_FILE_PATH ?? "./data/devices-raw.json",
hosts,
};
}