Commit graph

32 commits

Author SHA1 Message Date
92b6113d72 app: Require link carrier to consider Ethernet uplink active
A static IPv4 (e.g. the hotspot gateway address on eth0) was wrongly
classified as a DHCP uplink on stacks where ip(8) omits the "dynamic"
flag, and a NO-CARRIER interface with any address was reported as up.
Gate probeEthernet on /sys/class/net/<iface>/carrier == 1, and accept
either the dynamic flag or a finite valid_lft as the DHCP signal so
BusyBox's ip output is handled too.
2026-05-02 11:37:28 +08:00
8b1debdddc app: Surface Ethernet uplink in the UI and gate wpa_supplicant access
When the configured Ethernet interface holds a DHCP-assigned IPv4 at
startup, the app now skips wifi.Initialize / StartEventMonitoring and
guards every wifi.* wrapper against a nil backend. This prevents D-Bus
calls to fi.w1.wpa_supplicant1 from re-activating the daemon via
dbus-activation, honoring the "do nothing" intent of the Ethernet path.

The probed state is exposed in SystemStatus and rendered in the header
as a third pill ("Ethernet · <IP>"); a new "disabled" connectionState
covers the WiFi pill in this mode.
2026-05-02 11:24:36 +08:00
5a3942f351 station/hostapd: Resolve station IPs via udhcpd leases and ARP fallback
The hostapd backend never populated IPs: NewDHCPCorrelator was defined
but never instantiated, and even when it was, the parser only handled
ISC dhcpd's text format. On a BusyBox-based router using udhcpd, every
device showed up with an empty IP.

Two fixes:

- Add a udhcpd binary lease parser. The format is documented in
  busybox/networking/udhcp/dhcpd.{h,c}: an 8-byte big-endian unix-time
  header followed by 36-byte dyn_lease records (expires, IP, MAC,
  20-byte hostname, 2-byte pad). ParseLeases auto-detects the format
  by inspecting the header so the same code path handles both udhcpd
  and ISC text leases.

- Wire the DHCPCorrelator into Backend.Initialize and have it merge
  two sources: ARP first (universal IP fallback for any station that
  has been talked to) and DHCP leases on top (authoritative, carries
  the hostname). ARP fills the gap when leases are missing or the
  station uses a static IP; DHCP wins on conflict.

Default DHCPLeasesPath updated to /var/lib/udhcpd/udhcpd.leases — the
common BusyBox path. Configurable as before.
2026-05-02 11:07:37 +08:00
249217d4ad app: Use service(8) instead of systemctl to start wpa_supplicant
The target system uses sysvinit-style service management, not systemd.
2026-05-01 22:35:59 +08:00
d57c08a6c4 app: Start wpa_supplicant only when Ethernet has no DHCP lease
Probe the configured Ethernet interface (default eth0, overridable via
-ethernet-interface) at startup. If no DHCP-assigned IPv4 is present,
start the wpa_supplicant service so the WiFi backend has something to
talk to; otherwise leave it alone and rely on the wired uplink.
2026-05-01 22:35:07 +08:00
70140bc289 station: Identify devices by MAC OUI vendor lookup
Embed the IEEE OUI registry (~1MB pre-processed text file) and resolve
the vendor for every station MAC. Locally administered MACs (U/L bit
set, used by iOS/Android private addresses and virtual interfaces) are
skipped so we don't return spurious matches against randomized prefixes.

The vendor name shows up in the device card as a secondary line, and
falls back to the title position when no DHCP hostname is available —
"Apple" with the IP and MAC is far more useful than "Sans nom".

The lookup table loads lazily (sync.Once) on the first call so the
~40k-entry parse only runs when the station discovery code is exercised.
2026-05-01 22:32:57 +08:00
950f73371c station: Surface signal, traffic and connection time in API and UI
Hostapd already parsed signal strength and rx/tx counters but the
station -> ConnectedDevice conversion threw them away. Add signalDbm,
rxBytes, txBytes and connectedAt to the OpenAPI schema and the
ConnectedDevice model, and centralise the conversion in
station.ToConnectedDevice so handlers, the periodic refresh and the
event callbacks all serialise the same shape.

Two follow-on bugs surfaced while wiring this up:

- The hostapd backend only stored station entries on first contact.
  Subsequent polls were dropped, so signal and byte counters never
  refreshed. Reconcile updates in checkStationChanges.
- ConnectedAt was reset to time.Now() on every conversion. Track
  FirstSeen on HostapdStation when the station joins, and preserve
  the timestamp across periodic refreshes in app.go so the UI's
  "connected since" badge is stable.

Frontend gains a metrics row on each device card with signal bars,
total traffic and a live duration. Falls back gracefully when a
backend (DHCP, ARP) doesn't expose these fields.
2026-05-01 22:26:43 +08:00
a758c331c0 station: Default device type to "unknown" and propagate DHCP hostname
GuessDeviceType silently returned "mobile" for any device whose hostname
or MAC OUI didn't match a known pattern, so the UI labelled every
unidentified device as a phone. Default to "unknown" instead and broaden
hostname matching (pixel/galaxy, thinkpad, imac/-pc, QEMU OUI).

The hostapd backend was also dropping DHCP hostnames on the floor: the
correlator only forwarded MAC->IP, and convertStation hard-coded the
hostname to "". Replace UpdateIPMapping with UpdateLeaseInfo that carries
both maps so hostnames flow through to ConnectedDevice.Name.

Frontend gains a "Sans nom" fallback when no hostname is available and
French labels for the device-type badge.
2026-05-01 22:21:14 +08:00
0797f7dd50 wpa_supplicant: Use log package and tolerate GetNetworks failures
Switch warning prints to the log package for consistent output, and
fall back to AddNetwork when listing existing networks fails instead
of aborting Connect entirely.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 22:14:38 +08:00
07f8673f2f Harden API surface and station/wifi backends
Bind to localhost by default and stop echoing backend errors (which can
embed credentials or low-level details) back over the API and log
broadcast. Validate hotspot SSID/passphrase/channel before writing
hostapd.conf and tighten its mode to 0600 since it stores the WPA PSK.
Restrict WebSocket upgrades to same-origin so a LAN browser can't be
turned into a proxy for the API.

Guard shared state: status reads/writes go through StatusMutex (the
periodic updater races with the toggle and status handlers otherwise),
broadcastToWebSockets no longer mutates the client map under RLock, and
station-event callbacks now run under SafeGo so a panic in app code can't
take down the daemon. Stop channels in hostapd, dhcp, and iwd signal
monitors are now closed under sync.Once to survive concurrent Stop calls.

App.Shutdown is idempotent and waits for the periodic loops before
closing backends, so signal-driven and deferred shutdowns no longer race.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 21:56:50 +08:00
77370eff19 wpa_supplicant: Reuse existing networks and preserve saved ones on connect
Connect() called AddNetwork unconditionally, creating duplicate entries for
the same SSID, and SelectNetwork's side-effect of disabling all other
networks was being persisted by SaveConfig — making previously saved
networks appear erased. Disconnect() also removed the current network from
the config. Now reuse an existing network entry when the SSID matches,
re-enable other networks after SelectNetwork, and keep entries on
disconnect.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:25:30 +08:00
08cbe4f9ff Redesign frontend with minimalist Swiss-Scandinavian aesthetic
Replace gradient-heavy design with clean typography (Outfit + DM Sans),
warm neutral palette, terracotta accent, and generous whitespace.
Preserve all JS-facing IDs, classes, and functionality.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:32:56 +07:00
4414c99da9 wpa_supplicant: Save config after successfull connection 2026-01-02 18:03:16 +07:00
5532457cdf wpa_supplicant: Fix connection 2026-01-02 17:54:07 +07:00
10a7998495 wpa_supplicant: Fix detection of open networks 2026-01-02 17:53:52 +07:00
2922a03724 Refactor stations discovery and add hostapd discovery 2026-01-01 23:31:01 +07:00
69594c2fe4 syslog filter could take several filters 2026-01-01 23:31:01 +07:00
04ada45f44 Implement wpa_supplicant backend 2026-01-01 23:31:01 +07:00
79c28da9c5 Create wifi backend abstraction 2026-01-01 23:31:01 +07:00
f4481bca62 Stream logs from syslog 2026-01-01 23:31:01 +07:00
02b93a3ef0 Handle connecting/disconnecting states 2026-01-01 23:31:01 +07:00
1477d909b0 Add websocket wifi updates 2026-01-01 23:31:01 +07:00
c443fce24f Report hotspot config 2026-01-01 23:31:01 +07:00
17d665e21a Implementation with iwd 2026-01-01 23:31:01 +07:00
2b3a5b89f8 Add configuration system and ARP-based device discovery
Implement comprehensive configuration management with CLI flags for WiFi interface, device discovery method, and file paths. Add ARP table parsing as an alternative to DHCP leases for more reliable device detection. Improve WiFi scanning to handle concurrent scan requests gracefully.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 23:31:01 +07:00
accd7e75d8 Handle config options 2026-01-01 23:31:01 +07:00
b1b9eaa028 Migrate to a better architectured project 2026-01-01 23:31:01 +07:00
cc5ed5f23e Add OpenAPI specs 2025-10-28 18:25:45 +07:00
b3da6774ac Do stations scan/retrieval 2025-07-17 16:05:04 +02:00
5f1d7adaa4 Embed static assets into binary 2025-07-17 16:04:48 +02:00
a044bb759e Split style and js code in anothers files 2025-07-17 13:35:53 +02:00
8c8986e8dc Initial version, from Claude 2025-07-17 13:32:39 +02:00