Repository navigation
fix(config): vendor_ie_discovery not Required — 1-line schema fix - #332
Amperstrand wants to merge 11 commits into
Conversation
The repository-dispatch to OpenTollGate/tollgate-os fails when the REPO_ACCESS_TOKEN is expired or the downstream repo is unavailable. This is a notification step — all packaging jobs succeed regardless. - Added continue-on-error: true so dispatch failure doesn't fail CI - Added if: github.ref == 'refs/heads/main' so it only runs on main
Replaces the old dead-code bypass (which referenced undefined bitcoinOUI/nostrOUI) with a clean TollGate vendor IE implementation: - EncodeTollGateVendorIE: builds 802.11 vendor-specific IE hex string (DD <len> 212121 01 <version> <flags> [TLV mint_url] [TLV pubkey]) - ParseTollGateVendorIE: extracts TollGateAdvertisement from raw IE bytes - ParseVendorIEsFromScanData: walks raw IE bytes from iw scan output - EmitTollGateVendorIE: emits via ubus set_vendor_elements on hostapd - ExecuteUbus method added to Connector Config: vendor_ie_discovery flag in upstream_wifi section (default false). Types: TollGateAdvertisement struct, IsTollGate field on NetworkInfo. Wire format and tier structure based on commit 70e4320's design. Fixes #47.
The contract-lint and config_manager tests failed because the new VendorIEDiscovery field was added to UpstreamWifiConfig but not to the schema definition in config_schema.go.
18 tests covering: - Encode→Parse roundtrip (basic, all flag combos, with TLV fields) - Edge cases: empty MintURL/Pubkey, max-length rejection - Malformed input: wrong OUI, wrong elem type, non-DD element - Truncated data: too short, truncated TLV value - Multi-IE scan data: multiple TollGate IEs, non-vendor IEs interspersed - Truncated scan data at end - calculateScore: TollGate SSID bonus, IsTollGate flag bonus, combined
1. Encoder: guard total body length against uint8 overflow (per-field checks existed but sum could exceed 255) 2. Parser: respect declared bodyLen instead of raw[2:] (was reading trailing bytes as TLVs) 3. Docs: update vendor_element_manager description (was referencing removed 'Bitcoin/Lightning info') Addresses items 1, 2, 6 from c03rad0r's consolidated review. Items 3 (OUI hardware validation), 4 (ubus health check), 5 (JSON via fmt.Sprintf), 7 (map ordering) are informational — no code change needed for this PR's scope.
…TollGate#235) Addresses c03rad0r's change requests: 1. Encoder: moved len(body) > 255 check BEFORE uint8 cast (was after, meaning truncation already happened) 2. Added 11 encoder/parser unit tests: - 5 encode→decode round-trip tests (minimal, flags, mint, pubkey, full) - TestEncodeRejectsOversizedBody (>255 bytes) - TestParseRejectsInvalidOUI (wrong OUI) - TestParseRejectsTooShort (<8 bytes) - TestParseRejectsWrongElementType (wrong elem type byte) - TestParseRespectsBodyLen (trailing garbage ignored) All tests pass.
Required fix 3: Replace fmt.Sprintf JSON construction with encoding/json marshal in SetLocalAPVendorElements. Prevents injection if element values contain quotes or special characters. Note on fix 1 (encoder overflow): The check at line 62 already runs BEFORE the uint8 cast at line 65. The code is correct as-is — the overflow check prevents the cast from ever seeing a value > 255. Items 2 (encoder unit tests) and 4 (hardware validation) still pending. Item 2 requires adding encode→decode round-trip tests. Item 4 requires real WiFi hardware — documented as follow-up.
…search Research across macOS (-75 dBm trigger, 12 dB hysteresis), iOS (-70 dBm, 8 dB active), Android (band award +40/~10% of max score), wpa_supplicant (5 dBm rate tolerance), Cisco (6 dB hysteresis), and Aruba (5-10 dB delta bound) shows the previous +200 boost was 17-40x larger than any production WiFi steering system. Changes: - Vendor IE boost: +200 unconditional → +15 conditional (signal > -75 dBm) - SSID prefix boost: +100 → +10 (SSID is spoofable, weak identifier) - New test: weak signal (≤-75 dBm) gets no IE boost - Updated 3 existing tests with new expected values
|
Code looks solid — the encoder/decoder is clean with proper bounds checking and thorough test coverage. However, this PR's entire changeset is duplicated verbatim in #312 (confirmed via identical blob hashes across all 9 files). #312 was built on top of this work. Recommend closing this PR in favor of #312 to avoid merge conflicts and double-review. #312 already includes everything here cleanly with CI green. |
Re-tested against latest main (Jul 29)
|
Amperstrand
left a comment
There was a problem hiding this comment.
Feature PR — WGM vendor IE discovery
Implements vendor-specific information element parsing from beacon frames for TollGate router discovery. This is a significant feature that requires physical WiFi hardware to test properly.
Suggestion
Merge after v0.6.0. Recommend testing on physical hardware with multiple TollGate routers before merge.
- DefaultDiscoveryLogPath /tmp/tollgate-discovery.jsonl → /etc/tollgate/discovery_log.jsonl: /tmp is tmpfs on OpenWrt, so discovery history vanished on every reboot; path now matches the CHANGELOG (rotation already caps growth). - LoadExistingLog counted one scan per JSONL line; entries of one scan batch share a timestamp, so count distinct batch timestamps instead — TotalScans reflects scan cycles after restart reload. Regression test covers 4 lines / 2 batches. - CHANGELOG: is_tollgate/price_per_step/step_size documented as placeholders until vendor-IE discovery (OpenTollGate#332) populates them. Review findings: cold-code review 2026-08-17 items 2b/2c.
felixfelix-bot
left a comment
There was a problem hiding this comment.
Cold review — new findings only (5 issues previously raised, not repeated).
3 major:
-
IsTollGate flag is never set to true in any production code path. It's added to NetworkInfo and read in calculateScore (ni.IsTollGate && ni.Signal > -75), but no scanner or upstream_manager code sets it. The +15 vendor IE score boost is effectively dead code
-
VendorIEDiscovery config flag is dead config — added to UpstreamWifiConfig, config_schema.go, and UpstreamManagerConfig, but no production code reads it to gate behavior. Docs claim the feature is "gated behind vendor_ie_discovery config flag" but it isn't
-
ParseVendorIEsFromScanData, EmitTollGateVendorIE, and NewVendorElementProcessor are defined but never called from any production code (scanner.go, upstream_manager.go, connector.go). The entire vendor IE discovery pipeline is not wired into the system — only test code exercises these functions
3 minor (non-blocking):
- SetLocalAPVendorElements silently swallows all errors — returns nil even when ubus list fails
- TestEncodeDecodeRoundTrip doesn't assert Pubkey roundtrip
- Config schema has Required: true with Default: false for VendorIEDiscovery — existing configs without this key would be rejected
Note: The vendor IE encode/decode logic itself is correct and well-tested. The issue is that it's not connected to anything. If this is intentional staging (wire in a follow-up), a comment saying so would help.
- scanner.go: staging marker where NetworkInfo is built — IsTollGate/RawIEs/pricing stay zero here; the vendor-IE parse that fills them and the VendorIEDiscovery gate land in the wiring follow-up. Makes the intentional staging legible at the seam. - config schema: vendor_ie_discovery Required true → false. A Required key with Default false rejects every existing config that predates the key; optional-with-default upgrades cleanly. Review findings: cold review 2026-08-18 (majors 1-3 are the staged wiring by design — deferred with this PR per merge order; minor 3).
|
Cold-review response in Your three majors are all the same true observation — the vendor-IE pipeline isn't wired — and that's by design for this staging PR: it ships the (verified-correct) encode/decode + scoring so the wiring diff stays reviewable. Per the agreed merge order this PR defers to v0.7.x with a soak week; the wiring follow-up is where
If you'd rather this PR wire the pipeline itself before merging in v0.7.x, that's doable — it just grows this diff substantially. |
felixfelix-bot
left a comment
There was a problem hiding this comment.
Re-reviewed at e80cec2: Required:false on vendor_ie_discovery (existing configs no longer rejected), staging legible at the scanner seam. Roundtrip Pubkey assertion left as non-blocking follow-up minor. Good to merge.
…312) * fix(ci): make TollGate OS trigger non-blocking and main-only The repository-dispatch to OpenTollGate/tollgate-os fails when the REPO_ACCESS_TOKEN is expired or the downstream repo is unavailable. This is a notification step — all packaging jobs succeed regardless. - Added continue-on-error: true so dispatch failure doesn't fail CI - Added if: github.ref == 'refs/heads/main' so it only runs on main * feat(wgm): re-implement vendor IE discovery from wire format spec Replaces the old dead-code bypass (which referenced undefined bitcoinOUI/nostrOUI) with a clean TollGate vendor IE implementation: - EncodeTollGateVendorIE: builds 802.11 vendor-specific IE hex string (DD <len> 212121 01 <version> <flags> [TLV mint_url] [TLV pubkey]) - ParseTollGateVendorIE: extracts TollGateAdvertisement from raw IE bytes - ParseVendorIEsFromScanData: walks raw IE bytes from iw scan output - EmitTollGateVendorIE: emits via ubus set_vendor_elements on hostapd - ExecuteUbus method added to Connector Config: vendor_ie_discovery flag in upstream_wifi section (default false). Types: TollGateAdvertisement struct, IsTollGate field on NetworkInfo. Wire format and tier structure based on commit 70e4320's design. Fixes Amperstrand#47. * fix: add vendor_ie_discovery to config schema The contract-lint and config_manager tests failed because the new VendorIEDiscovery field was added to UpstreamWifiConfig but not to the schema definition in config_schema.go. * test(wgm): comprehensive vendor IE encode/decode tests 18 tests covering: - Encode→Parse roundtrip (basic, all flag combos, with TLV fields) - Edge cases: empty MintURL/Pubkey, max-length rejection - Malformed input: wrong OUI, wrong elem type, non-DD element - Truncated data: too short, truncated TLV value - Multi-IE scan data: multiple TollGate IEs, non-vendor IEs interspersed - Truncated scan data at end - calculateScore: TollGate SSID bonus, IsTollGate flag bonus, combined * fix(wgm): address c03rad0r change requests on #199 1. Encoder: guard total body length against uint8 overflow (per-field checks existed but sum could exceed 255) 2. Parser: respect declared bodyLen instead of raw[2:] (was reading trailing bytes as TLVs) 3. Docs: update vendor_element_manager description (was referencing removed 'Bitcoin/Lightning info') Addresses items 1, 2, 6 from c03rad0r's consolidated review. Items 3 (OUI hardware validation), 4 (ubus health check), 5 (JSON via fmt.Sprintf), 7 (map ordering) are informational — no code change needed for this PR's scope. * chore: re-trigger CI after rebase * fix(wgm): encoder overflow check before cast + round-trip tests (#235) Addresses c03rad0r's change requests: 1. Encoder: moved len(body) > 255 check BEFORE uint8 cast (was after, meaning truncation already happened) 2. Added 11 encoder/parser unit tests: - 5 encode→decode round-trip tests (minimal, flags, mint, pubkey, full) - TestEncodeRejectsOversizedBody (>255 bytes) - TestParseRejectsInvalidOUI (wrong OUI) - TestParseRejectsTooShort (<8 bytes) - TestParseRejectsWrongElementType (wrong elem type byte) - TestParseRespectsBodyLen (trailing garbage ignored) All tests pass. * fix(#235): address cobrador's CHANGES_REQUESTED feedback Required fix 3: Replace fmt.Sprintf JSON construction with encoding/json marshal in SetLocalAPVendorElements. Prevents injection if element values contain quotes or special characters. Note on fix 1 (encoder overflow): The check at line 62 already runs BEFORE the uint8 cast at line 65. The code is correct as-is — the overflow check prevents the cast from ever seeing a value > 255. Items 2 (encoder unit tests) and 4 (hardware validation) still pending. Item 2 requires adding encode→decode round-trip tests. Item 4 requires real WiFi hardware — documented as follow-up. * fix(wgm): calibrate vendor IE score boost from cross-platform WiFi research Research across macOS (-75 dBm trigger, 12 dB hysteresis), iOS (-70 dBm, 8 dB active), Android (band award +40/~10% of max score), wpa_supplicant (5 dBm rate tolerance), Cisco (6 dB hysteresis), and Aruba (5-10 dB delta bound) shows the previous +200 boost was 17-40x larger than any production WiFi steering system. Changes: - Vendor IE boost: +200 unconditional → +15 conditional (signal > -75 dBm) - SSID prefix boost: +100 → +10 (SSID is spoofable, weak identifier) - New test: weak signal (≤-75 dBm) gets no IE boost - Updated 3 existing tests with new expected values * docs: reference #311 in score boost comment * feat(wgm): discovery logger — structured scan history + tollgate-cli upstream known Adds structured JSONL logging of every AP discovered during background scan cycles, with an in-memory registry that tracks known TollGates across scans (BSSID, SSID, signal range, sample count, latest pricing). - New: discovery_log.go with DiscoveryLogger (LogScan, Summary, LoadExistingLog) - New: discovery_log_test.go with 5 tests (all pass) - Wire: UpstreamManager.runScanCycle calls DiscoveryLog.LogScan after each scan - CLI: 'upstream scan' output now includes is_tollgate, price_per_step, step_size - CLI: New 'tollgate-cli upstream known' shows discovery summary - Logs to /etc/tollgate/discovery_log.jsonl (JSONL format for analysis) - Foundation for Phase 2 (speed probing) and Phase 3 (advertised pricing) in #311 Depends on #235 (vendor IE parsing populates IsTollGate/PricePerStep). * fix: log to tmpfs + add speed probe (Phase 2) Flash protection: discovery log moved from /etc/tollgate/ (flash) to /tmp/ (tmpfs/RAM). Log survives across scans within a boot but does not wear out the router flash. Speed probe: new SpeedProbe(gatewayIP) function does HTTP GET to gateway:2121/, measures RTT, and reads response size. Probe results logged to /tmp/tollgate-discovery.jsonl.probes and tracked in the in-memory registry (last_rtt_ms, best_rtt_ms, probe_count). Architecture: two-speed model — we measure link RTT, TollGate advertises uplink speed. Vendor IE stays static (radio restart required to change). Dynamic data (price, load, speed) comes from HTTP API (GET / on 2121). * feat(wgm): wire SpeedProbe into post-switch verification After UpstreamManager connects to a new AP and DHCP assigns a gateway IP, the existing probeTollGateGateway() now uses SpeedProbe() instead of a raw http.Client. This measures RTT and records the result in the discovery registry via LogProbe(bssid, result). Changes: - Candidate struct: added BSSID field, set at both creation sites - verifyPostSwitchConnectivity: now takes (ssid, bssid) - probeTollGateGateway: replaced inline HTTP GET with SpeedProbe + LogProbe, preserving existing return-bool semantics - Removed unused net/http import from upstream_manager.go Flow: scan -> find candidate(BSSID) -> switch -> DHCP -> waitForDefaultRoute -> SpeedProbe(gatewayIP) -> LogProbe(bssid, result) -> registry RTT data now accumulates per-TollGate alongside signal history. * fix: call LoadExistingLog on startup so registry survives restarts Without this, the in-memory discovery registry starts empty after every backend restart, even though /tmp/tollgate-discovery.jsonl persists. LoadExistingLog reads the JSONL back into the registry on Start(). * fix: add log rotation to prevent unbounded tmpfs growth DiscoveryLogMaxEntries constant was declared but never enforced. On a router scanning every 30s, the JSONL file would grow without bound in /tmp/ (tmpfs), eventually exhausting RAM. Replaced with size-based rotation: when the file exceeds 1MB, it is renamed to .old and a fresh file starts. The in-memory registry retains all historical data across rotations. LoadExistingLog reads only the current file (plus .old if present). * chore: gofmt files touched by this branch * fix(wgm): persistent discovery log path + batch-accurate reload counting - DefaultDiscoveryLogPath /tmp/tollgate-discovery.jsonl → /etc/tollgate/discovery_log.jsonl: /tmp is tmpfs on OpenWrt, so discovery history vanished on every reboot; path now matches the CHANGELOG (rotation already caps growth). - LoadExistingLog counted one scan per JSONL line; entries of one scan batch share a timestamp, so count distinct batch timestamps instead — TotalScans reflects scan cycles after restart reload. Regression test covers 4 lines / 2 batches. - CHANGELOG: is_tollgate/price_per_step/step_size documented as placeholders until vendor-IE discovery (#332) populates them. Review findings: cold-code review 2026-08-17 items 2b/2c. * docs: fix stale SSID format comment Update example from old 'TollGate-ABCD-2.4GHz-1' (band suffix) to simplified 'TollGate-A1B2' (random chars only). --------- Co-authored-by: Amperstrand <amperstrand@localhost> Co-authored-by: amperstand <atlas@oh-my-opencode.com> Co-authored-by: Sisyphus <sisyphus@opencode.local> Co-authored-by: c03rad0r <1100745+c03rad0r@users.noreply.github.com>
|
Closing — rebased onto main as #353. Conflicts resolved, build verified. |
Surgical rebuild after the merge wave: OpenTollGate#312's squash already carried this PR's vendor-IE content to main (encode/decode/scoring, staging markers, tests — verified zero semantic delta in vendor_element_manager.go, vendor_ie_encode_test.go, scanner.go). The only content main still lacked is this schema fix: a Required key with Default false rejected every existing config predating the key; optional-with-default upgrades cleanly. Includes gofmt of the file (converges with OpenTollGate#341's repo-wide pass). Cold-review item from the OpenTollGate#332 round (minor 3).
|
Rebuilt onto post-wave main (81dd1da) — and shrunk to what main actually still needs. Auditing the residual after the merge wave: #312's squash already carried this PR's vendor-IE content to main — encode/decode/scoring ( What remains is exactly one line — the cold-review schema fix: The rest of the original PR's scope (wiring the pipeline, |
…penTollGate#312) * fix(ci): make TollGate OS trigger non-blocking and main-only The repository-dispatch to OpenTollGate/tollgate-os fails when the REPO_ACCESS_TOKEN is expired or the downstream repo is unavailable. This is a notification step — all packaging jobs succeed regardless. - Added continue-on-error: true so dispatch failure doesn't fail CI - Added if: github.ref == 'refs/heads/main' so it only runs on main * feat(wgm): re-implement vendor IE discovery from wire format spec Replaces the old dead-code bypass (which referenced undefined bitcoinOUI/nostrOUI) with a clean TollGate vendor IE implementation: - EncodeTollGateVendorIE: builds 802.11 vendor-specific IE hex string (DD <len> 212121 01 <version> <flags> [TLV mint_url] [TLV pubkey]) - ParseTollGateVendorIE: extracts TollGateAdvertisement from raw IE bytes - ParseVendorIEsFromScanData: walks raw IE bytes from iw scan output - EmitTollGateVendorIE: emits via ubus set_vendor_elements on hostapd - ExecuteUbus method added to Connector Config: vendor_ie_discovery flag in upstream_wifi section (default false). Types: TollGateAdvertisement struct, IsTollGate field on NetworkInfo. Wire format and tier structure based on commit 70e4320's design. Fixes Amperstrand#47. * fix: add vendor_ie_discovery to config schema The contract-lint and config_manager tests failed because the new VendorIEDiscovery field was added to UpstreamWifiConfig but not to the schema definition in config_schema.go. * test(wgm): comprehensive vendor IE encode/decode tests 18 tests covering: - Encode→Parse roundtrip (basic, all flag combos, with TLV fields) - Edge cases: empty MintURL/Pubkey, max-length rejection - Malformed input: wrong OUI, wrong elem type, non-DD element - Truncated data: too short, truncated TLV value - Multi-IE scan data: multiple TollGate IEs, non-vendor IEs interspersed - Truncated scan data at end - calculateScore: TollGate SSID bonus, IsTollGate flag bonus, combined * fix(wgm): address c03rad0r change requests on OpenTollGate#199 1. Encoder: guard total body length against uint8 overflow (per-field checks existed but sum could exceed 255) 2. Parser: respect declared bodyLen instead of raw[2:] (was reading trailing bytes as TLVs) 3. Docs: update vendor_element_manager description (was referencing removed 'Bitcoin/Lightning info') Addresses items 1, 2, 6 from c03rad0r's consolidated review. Items 3 (OUI hardware validation), 4 (ubus health check), 5 (JSON via fmt.Sprintf), 7 (map ordering) are informational — no code change needed for this PR's scope. * chore: re-trigger CI after rebase * fix(wgm): encoder overflow check before cast + round-trip tests (OpenTollGate#235) Addresses c03rad0r's change requests: 1. Encoder: moved len(body) > 255 check BEFORE uint8 cast (was after, meaning truncation already happened) 2. Added 11 encoder/parser unit tests: - 5 encode→decode round-trip tests (minimal, flags, mint, pubkey, full) - TestEncodeRejectsOversizedBody (>255 bytes) - TestParseRejectsInvalidOUI (wrong OUI) - TestParseRejectsTooShort (<8 bytes) - TestParseRejectsWrongElementType (wrong elem type byte) - TestParseRespectsBodyLen (trailing garbage ignored) All tests pass. * fix(OpenTollGate#235): address cobrador's CHANGES_REQUESTED feedback Required fix 3: Replace fmt.Sprintf JSON construction with encoding/json marshal in SetLocalAPVendorElements. Prevents injection if element values contain quotes or special characters. Note on fix 1 (encoder overflow): The check at line 62 already runs BEFORE the uint8 cast at line 65. The code is correct as-is — the overflow check prevents the cast from ever seeing a value > 255. Items 2 (encoder unit tests) and 4 (hardware validation) still pending. Item 2 requires adding encode→decode round-trip tests. Item 4 requires real WiFi hardware — documented as follow-up. * fix(wgm): calibrate vendor IE score boost from cross-platform WiFi research Research across macOS (-75 dBm trigger, 12 dB hysteresis), iOS (-70 dBm, 8 dB active), Android (band award +40/~10% of max score), wpa_supplicant (5 dBm rate tolerance), Cisco (6 dB hysteresis), and Aruba (5-10 dB delta bound) shows the previous +200 boost was 17-40x larger than any production WiFi steering system. Changes: - Vendor IE boost: +200 unconditional → +15 conditional (signal > -75 dBm) - SSID prefix boost: +100 → +10 (SSID is spoofable, weak identifier) - New test: weak signal (≤-75 dBm) gets no IE boost - Updated 3 existing tests with new expected values * docs: reference OpenTollGate#311 in score boost comment * feat(wgm): discovery logger — structured scan history + tollgate-cli upstream known Adds structured JSONL logging of every AP discovered during background scan cycles, with an in-memory registry that tracks known TollGates across scans (BSSID, SSID, signal range, sample count, latest pricing). - New: discovery_log.go with DiscoveryLogger (LogScan, Summary, LoadExistingLog) - New: discovery_log_test.go with 5 tests (all pass) - Wire: UpstreamManager.runScanCycle calls DiscoveryLog.LogScan after each scan - CLI: 'upstream scan' output now includes is_tollgate, price_per_step, step_size - CLI: New 'tollgate-cli upstream known' shows discovery summary - Logs to /etc/tollgate/discovery_log.jsonl (JSONL format for analysis) - Foundation for Phase 2 (speed probing) and Phase 3 (advertised pricing) in OpenTollGate#311 Depends on OpenTollGate#235 (vendor IE parsing populates IsTollGate/PricePerStep). * fix: log to tmpfs + add speed probe (Phase 2) Flash protection: discovery log moved from /etc/tollgate/ (flash) to /tmp/ (tmpfs/RAM). Log survives across scans within a boot but does not wear out the router flash. Speed probe: new SpeedProbe(gatewayIP) function does HTTP GET to gateway:2121/, measures RTT, and reads response size. Probe results logged to /tmp/tollgate-discovery.jsonl.probes and tracked in the in-memory registry (last_rtt_ms, best_rtt_ms, probe_count). Architecture: two-speed model — we measure link RTT, TollGate advertises uplink speed. Vendor IE stays static (radio restart required to change). Dynamic data (price, load, speed) comes from HTTP API (GET / on 2121). * feat(wgm): wire SpeedProbe into post-switch verification After UpstreamManager connects to a new AP and DHCP assigns a gateway IP, the existing probeTollGateGateway() now uses SpeedProbe() instead of a raw http.Client. This measures RTT and records the result in the discovery registry via LogProbe(bssid, result). Changes: - Candidate struct: added BSSID field, set at both creation sites - verifyPostSwitchConnectivity: now takes (ssid, bssid) - probeTollGateGateway: replaced inline HTTP GET with SpeedProbe + LogProbe, preserving existing return-bool semantics - Removed unused net/http import from upstream_manager.go Flow: scan -> find candidate(BSSID) -> switch -> DHCP -> waitForDefaultRoute -> SpeedProbe(gatewayIP) -> LogProbe(bssid, result) -> registry RTT data now accumulates per-TollGate alongside signal history. * fix: call LoadExistingLog on startup so registry survives restarts Without this, the in-memory discovery registry starts empty after every backend restart, even though /tmp/tollgate-discovery.jsonl persists. LoadExistingLog reads the JSONL back into the registry on Start(). * fix: add log rotation to prevent unbounded tmpfs growth DiscoveryLogMaxEntries constant was declared but never enforced. On a router scanning every 30s, the JSONL file would grow without bound in /tmp/ (tmpfs), eventually exhausting RAM. Replaced with size-based rotation: when the file exceeds 1MB, it is renamed to .old and a fresh file starts. The in-memory registry retains all historical data across rotations. LoadExistingLog reads only the current file (plus .old if present). * chore: gofmt files touched by this branch * fix(wgm): persistent discovery log path + batch-accurate reload counting - DefaultDiscoveryLogPath /tmp/tollgate-discovery.jsonl → /etc/tollgate/discovery_log.jsonl: /tmp is tmpfs on OpenWrt, so discovery history vanished on every reboot; path now matches the CHANGELOG (rotation already caps growth). - LoadExistingLog counted one scan per JSONL line; entries of one scan batch share a timestamp, so count distinct batch timestamps instead — TotalScans reflects scan cycles after restart reload. Regression test covers 4 lines / 2 batches. - CHANGELOG: is_tollgate/price_per_step/step_size documented as placeholders until vendor-IE discovery (OpenTollGate#332) populates them. Review findings: cold-code review 2026-08-17 items 2b/2c. * docs: fix stale SSID format comment Update example from old 'TollGate-ABCD-2.4GHz-1' (band suffix) to simplified 'TollGate-A1B2' (random chars only). --------- Co-authored-by: Amperstrand <amperstrand@localhost> Co-authored-by: amperstand <atlas@oh-my-opencode.com> Co-authored-by: Sisyphus <sisyphus@opencode.local> Co-authored-by: c03rad0r <1100745+c03rad0r@users.noreply.github.com>
What
Rebased version of #235 (closed due to branch conflict).
Vendor IE discovery with encoder/decoder, hostapd integration, and calibrated scoring algorithm.
Key changes from original #235
Reviewed