Commit Graph
4 Commits
Author SHA1 Message Date
2GT-RichandClaude Opus 5 63b5677522 fix(firmware,ssh,logs): five reported bugs (v0.24.1-beta)
Closes #136. Also fixes #133, #137, #138, #139 (GitHub closes only the first).

None of these came from multi-site; two came from me.

#136 — waves failed on the second device. A regression from v0.23.31, the
firmware rewrite that replaced fire-and-forget `install` with `download` plus
polling. `/system/package/update/download` blocks for a minute or more, and
the client's read timeout is 30 seconds. The timeout path was the real damage:
it nulled the pending read but left the socket open, so when the reply finally
arrived processBuffer() queued it and the *next* command took it as its own
answer. Every later call on that connection returned the previous one's data,
which is why the status polls could never see "Downloaded".

  - a timed-out read now abandons the connection instead of silently
    desynchronising it, so the failure is loud rather than wrong
  - long-running commands can state their own budget; check-for-updates,
    download and /log/print now do
  - a download whose command times out is re-checked on a fresh connection
    rather than assumed failed, because the image may well have landed

#133 — the SSH terminal could not use a deployed key. It read only
ssh_password_encrypted and refused to connect without one, so retiring the
password after deploying a key -- the workflow the feature exists for -- broke
the console. It now uses the same resolver as every other SSH consumer.
preferredAuth(), written to make exactly this decision, had never run outside
its own tests, and disagreed with the resolver that did: it accepted a
'deployed' key where the live path required 'verified'. Unified on the
stricter rule and wired in.

#139 — the rollout was written before start() was attempted, so a rejected
start left the rows behind: "already running", press again, another rollout
banked each time. Devices already in an unfinished rollout are now refused by
name, before anything is persisted.

#138 — cancel worked but the API's explanation that the in-flight device is
never interrupted was discarded, so the button looked dead. It is shown.

#137 — log overfetching. /log/print returns the whole ring buffer, and the
count was incremented per attempted insert rather than per accepted row, so a
poll that stored nothing still reported "Collected 1000 new log entries" after
a thousand round trips. Inserts are batched and counted by RETURNING, and the
id watermark is the maximum over recent rows rather than the last one
inserted, which could move backwards and make the whole buffer look new.

One self-inflicted wound worth recording. The first cut of the desync guard
discarded sentences whenever `connected` was false -- but `connected` is only
set *after* login succeeds, so it threw away every login reply and took the
whole fleet offline until it was caught. "Not yet connected" and "abandoned"
are different states and now have different flags. Every existing test pre-set
connected = true, so none of them could see it; the test added for it fails
against the bad version.

Verified on hardware: devices back online, check-update 5.2s (was a 30s
failure), full sync completes, and log collection now reports 298 / 22 / 12 /
12 across successive polls -- real decreasing counts rather than a constant
1000 -- with no failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 19:17:52 -07:00
2GT-RichandClaude Opus 5 2625fea8de feat(lte): cellular telemetry, band details and roaming history (v0.23.12-beta)
Adds LTE support, requested in discussion #85 by a contributor running a
MikroTik ATL 18 on a fixed antenna. Cellular was previously a blank page: the
only occurrence of "lte" in the tree excluded LTE interfaces from DHCP binding.

Built against two real field captures rather than documentation, which
disagreed with the hardware in several places — the modem reports `data-class`
not `access-technology`, `iccid` not `uicc`, `status: running` not `connected`,
embeds units inside values (`-97dBm`, `17dB`), and omits `registration-status`,
`pin-status` and `lac`/`tac` entirely. What an LTE interface reports depends on
the modem chipset, not on RouterOS, so every field is optional by construction.

Fixes a latent bug in the API client while it was in reach. A sentence carrying
the same attribute twice kept only the last value, and a Cat-18 modem reports
one `ca-band` per aggregated carrier. On the contributor's 4-carrier capture
that yielded B3 + B20 — 30 MHz — on a device actually running 70 MHz, because
the narrowest carrier happens to arrive last. Repeats are now preserved
alongside the flat value, so existing callers are byte-for-byte unaffected.

Signal quality is graded rather than merely displayed. RSRP alone misleads: the
capture shows −97 dBm, nominally "fair", on a link running 256QAM at CQI 15 with
two spatial streams. SINR leads the grade and RSRP moderates it.

Surfaces the uplink anchor, which no MikroTik UI shows. Left on automatic a
modem anchors to whichever band it hears loudest, often a narrow one, and
aggregates wider bands for downlink only — but uplink rides the primary carrier
alone, so upload stays capped. This is the contributor's actual reason for
locking bands, and it reframes band selection as anchor control rather than
coverage.

Band changes, tower handovers and re-registrations are reconstructed by
comparing consecutive polls, since scanning interrupts service and finds nothing
at a site served by a single base station. Observed bands accumulate as the
evidence a future band lock will be checked against.

Band selection itself is deliberately absent: it needs Change Guard plus a
free-space check, as the target hardware has 1.1 MB free of 16 MB flash.

Also fixes an unrelated display bug in the RF Health channel map. The 2.4 GHz
axis is labelled by channel, but ticks at 2400 and 2480 fall outside the channel
plan and fell back to printing raw frequencies — and the 2400 label sat at
exactly 0% with a centring transform, hanging half its width outside the card.
2.4 GHz is the only band with a tick at an edge. Unlabelled ticks are now
dropped and edge labels anchor inward.

  - backend/src/utils/lte.ts — parsing, quality, change detection (59 tests)
  - RouterOSClient — repeated attributes preserved (6 tests)
  - lte_interfaces, lte_cell_history, lte_observed_bands
  - GET /devices/:id/lte, /lte/history, /lte/metrics
  - LteTab, gated on has_lte from the interface list
  - scripts/seed-lte-fixture.sql — a cellular device for local development

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 13:06:28 -07:00
2GT-RichandClaude Opus 4.8 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>
2026-07-02 21:58:02 -07:00
Rich Teslow 6b0fc9fdf1 Initial commit baby! 2026-04-13 18:15:41 -07:00