33ca7cc651c21e17b6e4b9c84dbd6517aa32722a
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4a00b675d9 |
fix(wireless): read CAPsMAN radio channels from the controller (v0.23.7-beta)
Follow-ups on #97 and #106, both reported against the releases they shipped in. #97 — managed radios were missing from Channel Usage entirely The query required wireless_interfaces.frequency > 0. A CAPsMAN-managed access point stores no frequency of its own: the controller owns the channel, and the AP's own rows carry nulls. Every managed radio was therefore filtered out before analysis ran, which is why the reporter saw all access points collapse into "Standalone" and a controller vanish from the selector — the group had no surviving radios to name it. The channel now comes from capsman_radios.current_channel where present, parsed from RouterOS's `<freq>/<phy>/<control-positions>` form. The control letters give a precise operating width — Ce is 40 MHz, Ceee is 80 — rather than the capability list a width field reports. Client counts prefer the controller's per-radio figure for the same reason. Verified only by unit test here, since we run no CAPsMAN. The local fleet still reports its two standalone radios unchanged. #106 — the setting had no way to change it maps_enabled shipped without a control, which made it a setting in name only. Added under Settings, with the reasoning stated: tiles and geocoding are outbound requests to a third party, and the address entered is sent to them. Also removed the "map display is off" placeholder. The reporter pointed out it can never appear in the environment it was written for — with maps disabled from the start no coordinates are ever geocoded, so the block was dead code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
dcd89fe86b |
feat(ui): quieten the dashboard — mute checks, hide app noise, honest sparkline (v0.23.6-beta)
Closes #101, #103, #104, #106, #111. Partially addresses #96. A batch of signal-to-noise fixes reported together. Individually small; together they are the difference between a dashboard someone reads and one they learn to ignore. #101 — security heuristics can be muted A warning you disagree with and cannot dismiss discredits every other finding beside it. Cleartext API is the reported case: on a fleet reachable only across WireGuard, TLS adds nothing to the threat model, and the check is permanent noise. Missing input-chain firewall is the same on a switch behind a router — the message even says "confirm that is the case", while offering no way to record that you did. Checks can now be muted per device or fleet-wide. Muted findings are still listed, greyed and labelled, and excluded from the score — not hidden. Hiding them would make the posture unauditable, which is a worse failure than noise. #103 — the app's own requests are hidden from Recent activity Audit entries for writes made through the UI are noise from an operations point of view. Off by default now, with a toggle that names how many are hidden, remembered per browser since it is a viewing preference rather than fleet configuration. #104 — quick actions moved into the header strip Matching the Wireless page. "Things to handle" takes the full width it was already overflowing. #106 — map lookups can be turned off Geocoding hits nominatim.openstreetmap.org and the map pulls OSM tiles. On an isolated network those hang; everywhere else they disclose where your devices are to a third party, which the report understates. A maps_enabled setting stops both requests; stored coordinates are still shown as plain text so nothing is lost. #111 — the CPU sparkline says something It was drawn in one colour and auto-scaled to each device's own peak, so an idle device and a saturated one produced an identical picture. Now plotted against a fixed 0-100 scale, coloured by the band its peak falls into — a flat line low in the box is an idle device and looks like one. #96 — groundwork TX-retry figures come from CCQ, which only the legacy `wireless` driver reports, so the panel is permanently empty on a RouterOS 7 fleet. The wireless package each device runs is now recorded, and a capabilities endpoint answers the fleet-wide question. The panel is dropped and the density chart takes the full width when nothing can supply the data — and shown when nothing has been polled yet, rather than hiding a panel that may populate a minute later. Verified against the fleet: muting cleartext API moved a device's score from 87 to 90 with the finding still listed and marked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4cc8c84ea6 |
fix(wireless): model channel usage as spectrum, not channel numbers (v0.23.5-beta)
Closes #98, #97. The Channel Usage panel treated a radio as occupying a channel *number* and coloured a channel amber when two radios shared it. Both halves are wrong, and the result is not merely incomplete — it gives actively bad planning advice. Channels are not slots. In 2.4 GHz the centres are 5 MHz apart while a channel is 20 MHz wide, so an AP on channel 1 occupies about 2402-2422 MHz and pollutes channels 1 through 5. Showing 2 through 5 as free is why 1/6/11 exists as the only three-way non-overlapping plan, and a model built on channel indices cannot express that. Sharing a channel is also not the problem. Two radios on exactly the same channel hear each other and take turns; CSMA/CA does its job and the cost is airtime. Two radios partially overlapping cannot decode each other, transmit over one another, and the cost is corruption. Partial overlap has to rank above co-channel, and it was not represented at all. Occupancy is now modelled in megahertz and overlap classified by how two ranges relate. On the local fleet, a 40 MHz radio on channel 1 is correctly shown bleeding 5 MHz into two channel-6 radios — which are themselves correctly identified as co-channel with each other rather than as the worst problem in the band. The model lives in the backend (utils/rfSpectrum.ts) with 26 tests rather than in the browser, because it is correctness-critical and the frontend has no test runner. Tests pin the arithmetic that is easy to get wrong: 1/6/11 is clear, 1/3/5 is three partial overlaps, channels 4 and 8 abut exactly rather than overlapping, a 20 MHz radio nested inside an 80 MHz one is partial rather than co-channel, and a lone radio is clear rather than co-channel with itself. The panel now draws real spectrum to scale, stacking overlapping radios so each stays visible, with the clash list on hover naming the other radio and how many megahertz they share. Width is taken as the widest entry of a RouterOS capability list such as 20/40/80mhz, since that is the spectrum the radio may occupy and therefore what a planner must treat as unavailable. Per-location scoping (#97): analysis runs per CAPsMAN controller, with a selector, because two APs on channel 6 in different buildings are not a conflict. Radios with no controller are analysed together as standalone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0258ee47dc |
fix(wireless): attribute CAP radios to the access point, not the controller (v0.23.1-beta)
Third round of #94 field testing. The placement bug survived v0.23.0 because the previous fix addressed the wrong mechanism. Cause A CAPsMAN controller mirrors every CAP interface locally, so a CAP's radio MAC legitimately appears on two devices: the CAP that owns the hardware, and the controller that mirrors it. The MAC index was single-valued and first-wins over an unordered UNION, so each radio landed on whichever row the database happened to return first. On the reporter's fleet six radios attached to a router with no radios at all, while their siblings attached correctly — the same access point appearing in two places, and liable to move between polls. v0.23.0 excluded the controller from *prefix* matches only. These are exact matches, so the exclusion never applied. Consecutive MACs on one physical AP resolving to different devices is what gave it away. Fix The index is multi-valued: a MAC maps to every device carrying it. A radio the controller reports as local:false lives on a CAP by definition, so the controller is rejected as a candidate at exact strength as well as prefix — an exact match against it only ever means the mirror. Attribution now runs through matchRadiosToDevices, which was previously tested but unused while the collector kept its own copy of the logic. With no radios left attributed to it, a controller that has none of its own stops appearing in the access point list, which was the reporter's follow-up point. SSID on provisioned interfaces A CAP holds no configuration, so its interface rows carry a generic name and a null SSID. The controller knows it, on its mirror of the same interface. Radios now record the provisioned SSID from there, and the table is driven by the controller's radio list rather than the CAPs' own rows — the controller is the only side that knows the SSID, channel and client count together. Band and frequency columns removed Both were empty while the channel column beside them read 5680/ax/eCee/D. The channel string already carries the band and the extension layout; parsing it properly is its own task, and half-parsing it produced empty columns next to populated ones. Requested by the reporter. 75 tests across the CAPsMAN and topology modules, including fixtures built from the reporter's MAC set: both radios of one AP resolve to that AP, order-independently, with a genuinely local radio still landing on the controller and an unknown CAP left unattributed rather than absorbed. Reproduced the mirror scenario against the local fleet before and after. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
82efcaa371 |
fix(wireless,topology): CAPsMAN write guards, MAC neighbour resolution, unified panel (v0.23.0-beta)
Closes #95. Addresses the second round of #94 feedback. Includes a regression introduced in v0.22.0. Wireless write paths were not CAPsMAN-aware (regression) v0.22.0 widened getAP() and the bulk-SSID selector to include cap/controller roles so a controller — normally tagged a router — would stop being invisible to the wireless section. Those same helpers gate the write endpoints, so the change also allowed writes to devices whose radios a controller owns. Reads needed widening; writes needed a guard, and did not get one. Five paths now refuse or skip provisioned radios: bulk SSID deployment, single interface create, interface edit, interface delete, and guest network setup. Delete was the worst of them — RouterOS will not remove a provisioned interface, but the cached row was deleted anyway, so it vanished from the UI until the next poll brought it back. The check is shared: partitionRadios() splits physical radios into locally controllable and controller-owned, assertNotCapsmanManaged() refuses an edit to a provisioned interface and names the controller. Neighbours identified only by MAC (#95) Topology resolved a neighbour to a managed device by IP address. A trunk port has no IP, so LLDP across one reports a MAC and an identity but no address — and those links stayed unresolved, drawing an external node beside the very device it already had on the map. Two directly connected devices appeared four times. Resolution by MAC now runs first, before address matching, because a MAC is hardware identity and unique fleet-wide where an address is not. This is the same join the CAPsMAN radio matching uses. CAPsMAN panel, from the reporter's testing - Radios and provisioned interfaces were two tables describing the same things from different angles; they are now one, per interface, with the access point, SSID, band, frequency, channel, clients and status. - Provisioning rules sit inside the configuration they apply. Rules referencing a configuration we did not collect are listed separately rather than dropped, since that usually means a stale reference. - The Wireless header reported zero clients while the Clients page reported 32: it summed the CAP-local count, which is empty under central forwarding. It now takes the greater of that and the controller's per-radio counts, so a controller that is also a CAP is not double-counted. - A remote radio could be attributed to the controller by the five-octet prefix fallback when the two shared a MAC block. Prefix matches now refuse the controller for a non-local radio; exact matches are still honoured. Radios tab followed device_type device_type is user-supplied tagging, and a router with radios had no way to display them. The tab now follows the detected wifi_role, which is a fact about the hardware. 48 tests on the CAPsMAN module, 22 on topology, including a fixture built from the device output in #95. Verified against the fleet: topology output unchanged, so MAC resolution merged nothing it should not have; guards correctly permissive on a standalone AP. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e15556b601 |
fix(wireless): CAPsMAN panel corrections from field testing (v0.22.1-beta)
All five points raised on #94 after the reporter tested v0.22.0 against a live CAPsMAN deployment. Two were regressions in that release. Operating channel instead of a capability list (mine) current_channel was stored from /interface/wifi/radio's current-channels, which is every channel the radio supports rather than the one it is using. Measured on a wAP ax: 535 characters on the 2.4 GHz radio and 3037 on the 5 GHz one — the "two kilobytes of text" reported. /interface/wifi/monitor gives the operating channel (5500/ax/Ceeeeeee/D), and carries state, peer counts and TX power too, all now shown on the radio chip. Client counts that were zero everywhere (mine) The panel read registered_clients from the CAP's own wireless_interfaces rows, which are empty when traffic is processed centrally by the controller. Monitor's registered-peers is not the fix either, and testing caught that before it shipped: clients register on the virtual AP carrying the SSID, not on the physical radio, so monitor on a physical radio reports zero on a busy access point. On the test AP, wifi1 and wifi2 both read 0 while wifi3-wifi6 held all ten clients. Counts now come from the registration table, with each entry attributed to its radio by following master-interface up to the interface owning a radio-mac. That field requires /interface/wifi/print detail — it is absent from the plain listing. Verified against real hardware: ten registrations, attributed 8 and 2 across the two radios, none dropped. "no network" on remote interfaces The Network column resolved from the AP's own bridge ports. A CAPsMAN-provisioned interface is not a local bridge port, so nothing matched and the UI asserted the interface had no network at all. It now falls back to the datapath — inline fields or a named/.id reference — and labels the source. Where the datapath names no bridge it says "via controller" rather than claiming no network. Status labels "Serving Clients" claimed something the underlying field does not say: running means the radio is up, not that clients are connected. Now "Running" and "Enabled, not running". Table title "SSIDs / Wireless Interfaces" conflated two different things; the table lists interfaces, virtual and physical. Now "Wireless Interfaces". Guest WiFi on CAPsMAN-provisioned radios Raised by the reporter as an aside, and it was a real hazard already shipping. setupGuestNetwork created virtual APs locally on whatever physical radios it found, with no awareness that a controller might own them — the SSID would be ignored or overwritten on the next provision while the UI reported success. Provisioned radios are now skipped with an explanation, and if every radio is provisioned the setup is refused, pointing at the controller instead. 37 unit tests on the CAPsMAN module. No regression on a fleet without a controller: local bridge-port resolution unchanged, roles still correct. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
14bab24c7e |
feat(wireless): CAPsMAN awareness and read-only controller view (v0.22.0-beta)
Addresses #94, phases A and B. Write operations are deliberately out of scope. An access point provisioned by CAPsMAN holds none of its own wireless configuration, so /interface/wifi/print returns rows with the configuration.* and security.* fields absent. normalizeWifiInterface mapped nothing, stored nulls, and the UI rendered an AP with no SSID, security or band — indistinguishable from one that is simply broken. Phase A — stop showing blanks Devices are classified standalone / cap / controller / controller_cap from /interface/wifi/capsman and /interface/wifi/cap. Both toggles can be on: MikroTik supports a controller that also runs its own radios under itself, and the issue calls that case out, so it gets its own role. Provisioned interfaces are marked and labelled in the Radios tab with the controller's MAC and an explanation that the blank fields live on the controller. SSID, mode and channel are recovered from RouterOS's status line where present, so the AP is described rather than blank. Role is the signal, not missing data: an AP whose SSID is empty because the controller owns it looks identical to an unconfigured one, and only the toggle distinguishes them. A CAP can still hold locally-configured interfaces alongside provisioned ones, so per-interface state is checked rather than assuming the whole device. Phase B — read-only controller view Controllers now collect the radios they manage, named configurations, and provisioning rules, surfaced as a CAPsMAN panel on the Wireless page grouped by the access point each radio physically lives on. CAPs are matched to managed devices by radio MAC from the controller's own /interface/wifi/radio list, not by resolving addresses through neighbours as the issue proposed. A MAC is hardware identity and globally unique, so unlike an address it cannot mean two devices on two segments — the failure fixed in v0.20.1. A radio MAC often differs from the interface MAC in the final octet, so lookup falls back to a five-octet prefix while keeping the OUI exact. A CAP that is not a managed device is surfaced rather than hidden; that is usually the reason an AP appears to be missing from the fleet. The wireless section required device_type = 'wireless_ap' in five places, which hid a dedicated controller — normally classified as a router — from every wireless view. Collection was gated the same way. Both now include CAPsMAN roles, and the collector probes an unknown device once, records the role, and skips cheaply forever after. Not included Provisioning, config edits and interface enable/disable. A CAPsMAN configuration applies to every AP bound to it simultaneously, which is the fleet-wide version of the failure Change Guard exists to prevent. It needs the same prediction and revert treatment rather than being bolted on. Legacy /caps-man (pre-RouterOS 7) is a separate API tree and is not covered. Verified: API shapes probed against RouterOS 7.23 rather than taken from docs. 24 unit tests on the pure parsing and matching. No CAPsMAN hardware here, so the read path was exercised against a seeded controller with a local radio, a matched two-radio CAP and an unmanaged CAP; grouping, provisioning and SSID resolution all came out correct. Confirmed no regression on a fleet with no controller: roles detected correctly, no interface falsely flagged, wireless views unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9dfb8a5333 |
feat: Guest WiFi, network intelligence, firmware orchestration, platform automation, api-ssl (v0.16.3-beta)
Guest WiFi (Hotspot): one-click captive-portal wizard that creates the guest SSID (virtual AP per radio), VLAN/bridge segregation + L3 VLAN interface, pool, DHCP, portal, rate-limited guest profile, and masquerade; vouchers with limits + printable sheets; live guest sessions; walled garden. Network intelligence: per-client device fingerprinting (vendor/hostname → category) with a persisted per-client override on the detail page; rogue/neighbor AP detection from stored AP scans (evil-twin flagging); Mist-style anomaly insights (client-count/CPU vs same-hour 14-day baseline + error bursts) in the Operations engine. Firmware orchestration: staged wave-based RouterOS rollouts (canary first) with pre-upgrade backup, reboot-ride-out, version verification, halt-on-failure, and scheduling; live per-device progress + cancel. Platform & automation: scoped API tokens (mtm_, read/write, hashed at rest, Bearer auth in requireAuth); outbound webhooks (12 events, HMAC-signed) fired from the alert pipeline + firmware orchestrator; scheduled email reports. api-ssl: RouterOSClient negotiates TLS on port 8729 and accepts RouterOS's default self-signed certs (rejectUnauthorized:false), so SSL-secured API devices connect without manual cert trust. Fixes #57. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
4e6c9f481e |
feat(wireless+ports): RF Health panels + physical per-port clients (v0.16.1-beta)
Wireless RF Health (fleet Wireless page + per-AP Radios tab):
- Channel usage map across 2.4/5/6 GHz with co-channel overlap; operating
channel resolved live from /interface/wifi/monitor on the wifi package
(auto-channel radios report no frequency in /print).
- AP deployment density: client RSSI distribution on the -90..-30 dBm scale
with a coverage-gap warning.
- AP radio TX retries histogram (0%->35%+) with band selector, derived from
per-client CCQ (new wireless_radio_quality InfluxDB series).
- WiFi connectivity success funnel (Association/Auth/DHCP) derived from logs.
Per-port connected clients (Ports tab):
- New card lists clients physically connected to a selected port (host/MAC/
IP/VLAN/connection time, linked to client pages).
- Distinguishes physical clients from the raw MAC table: uplink/trunk ports
are auto-detected (topology neighbor, multi-VLAN MAC spread, or high MAC
count) and show an uplink explainer + "view MAC table" disclosure instead
of every reachable MAC.
- Added clients.first_seen ("connected since"); set once, preserved across polls.
|
||
|
|
6b0fc9fdf1 | Initial commit baby! |