Infers device roles from LLDP/CDP capabilities, interface count,
vendor/manufacturer, and system description. Only overwrites
inferred roles, never manual overrides. Wired into process_device
pipeline.
Orchestrates evidence collection from LLDP, CDP, MAC, and ARP
data. Groups evidence by remote device, resolves to managed
devices, upserts links with merged confidence. Broadcasts
topology changes via PubSub. Skips self-links.
LLDP/CDP evidence (0.95 confidence) from snmp_neighbors.
MAC evidence (0.7) cross-references MAC tables with device
interface MACs. ARP evidence (0.6) matches by IP and MAC.
Self-links excluded. Unmatched entries tracked as discovered.
The plug was attempting to register a before_send callback after
blocking a banned IP and sending a 403 response. This caused
Plug.Conn.AlreadySentError in production.
Fixed by checking conn.halted before attempting to register the
404 tracking callback. If the request was already blocked and sent,
we skip the callback registration.
Also updated the test to verify proper behavior without the
workaround for AlreadySentError.
Ecto schemas for persistent topology model. DeviceLink tracks
connections between devices with confidence scores. DeviceLinkEvidence
records why each link is believed to exist. Device schema gets
device_role and device_role_source fields.
Filter port_died and write_failed/epipe errors from Honeybadger reports,
email notifications, and logs. Reorder supervision tree so Oban drains
before its dependencies shut down, and add 40s shutdown grace period.
Phase 3 audit found screenos.yaml detection profile was not registered
in the Juniper vendor module. Added to profile_names list.
ScreenOS is Juniper's legacy firewall operating system (pre-Junos).
Coverage: 5 Juniper profiles now registered
- juniper-mss
- juniperex2500os
- junos
- junose
- screenos (newly added)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Always broadcast from update_device_status/2 so the device index
LiveView refreshes even when status hasn't changed. Add a 15-second
tick timer to keep "Last Checked" timestamps fresh between polls.
Scope alert PubSub topics to organization for defense-in-depth.
Updated priv/static/changelog.txt with all recent changes from CHANGELOG.txt
in concise, user-facing format. Enhanced CLAUDE.md to document the dual
changelog system (technical CHANGELOG.txt vs user-facing priv/static/changelog.txt)
with clear guidelines on what to include in each and when to update them.
Add a Honeybadger NoticeFilter that sends an email with the full
error class, message, stacktrace, and server info to graham@mcintire.me
whenever an exception is reported. The notice passes through unchanged
so Honeybadger still receives it normally.
Replace individual Repo.insert() with Repo.insert_all() for sensor readings,
interface stats, processor readings, and storage readings in agent channel and
device poller worker. Add partial/composite database indexes for common query
patterns. Dashboard now uses GROUP BY aggregation instead of loading all devices.
DeviceLive.Show only reloads data for the active tab on PubSub events.
DeviceLive.Index debounces status changes and skips quota queries on updates.
Agent polling preloads filtered to monitored sensors/interfaces only.
Reduced file size by 62% while preserving all critical information:
- Simplified data model diagram to hierarchical text format
- Removed verbose code examples, keeping essential patterns
- Condensed sections while retaining actionable information
- Streamlined Custom Ecto Types, Browser Navigation, and Memory Leak sections
The condensed version maintains all necessary guidance for AI assistants
working on the project without overwhelming the context window.
Changed expect() to stub() in all discover_wireless_sensors/1 tests
to handle multiple SNMP get calls:
- Static sensor fetch calls Client.get() for CPU
- ePMP mode detection calls Client.get() for wirelessInterfaceMode
- Each sensor value fetch calls Client.get() (RSSI, SNR, Frequency, Clients)
Using stub() allows unlimited calls vs expect() which enforces
exactly one call, fixing Mox.UnexpectedCallError in all tests.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Update AgentLive.Index to properly handle browser back/forward buttons
by syncing the setup token modal to URL parameters.
Changes:
- Update handle_params/3 to read modal param and set modal state
- Update show_setup handler to use push_patch with ?modal=setup
- Update close_token_modal handler to use push_patch (remove modal param)
- Update handle_agent_creation_success to use push_patch after creating agent
Behavior:
- Browser back button now closes the setup modal without navigating away
- URL reflects current modal state (?modal=setup)
- Bookmarkable URLs restore exact modal state (can share agent setup URL)
- After creating agent, modal opens via URL state
Follows idiomatic Phoenix LiveView pattern using push_patch/2 and
handle_params/3 as documented in CLAUDE.md.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Update UserSettingsLive to properly handle browser back/forward buttons
by syncing all modal state to URL parameters.
Changes:
- Update handle_params/3 to read modal param and set modal states
- Add build_settings_path/2 helper to construct URLs with preserved params
- Update all modal show/close handlers to use push_patch instead of assign:
- add_token modal
- token_created modal (shown after creating token)
- revoke_all modal
- add_device modal (TOTP)
- device_qr modal (shown after creating device)
- recovery_codes modal
Behavior:
- Browser back button now closes modals without navigating away
- URL reflects current modal state (?modal=add_token, etc.)
- Tab and page params preserved when opening/closing modals
- Bookmarkable URLs restore exact modal state
Follows idiomatic Phoenix LiveView pattern using push_patch/2 and
handle_params/3 as documented in CLAUDE.md.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add comprehensive documentation for idiomatic Phoenix LiveView URL state
management to ensure browser back/forward buttons work correctly.
Changes:
- Add design document: docs/plans/2026-02-12-browser-navigation-fix-design.md
- Documents the problem (18 LiveViews missing handle_params/3)
- Provides idiomatic Phoenix patterns using push_patch/2
- Includes migration strategy for updating all LiveViews
- Contains testing protocols and success criteria
- Update CLAUDE.md: Add "Browser Navigation and URL State Management" section
- Documents the three core patterns: tabs, modals, filters
- Establishes critical rules for all future LiveView development
- Provides working code examples for each pattern
- Links to official Phoenix documentation
Key Principles:
- URL is single source of truth for visible state
- Use push_patch/2 in event handlers, never assign for URL-backed state
- Always implement handle_params/3 to validate and process URL params
- Use Phoenix built-ins (no custom helpers needed)
This ensures future LiveView development follows the correct pattern
and browser navigation works as users expect.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add TIER 3 MEDIUM priority optical power sensors for comprehensive AF-LTU link diagnostics.
Per-chain monitoring enables detection of imbalanced signals and fiber issues.
AirFiber AF-LTU additions:
- RX Power Chain 0: Received optical power chain 0 in dBm
OID: .1.3.6.1.4.1.41112.1.10.1.4.1.5
- RX Power Chain 1: Received optical power chain 1 in dBm
OID: .1.3.6.1.4.1.41112.1.10.1.4.1.6
- RX Ideal Power Chain 0: Ideal received power chain 0 in dBm
OID: .1.3.6.1.4.1.41112.1.10.1.4.1.7
- RX Ideal Power Chain 1: Ideal received power chain 1 in dBm
OID: .1.3.6.1.4.1.41112.1.10.1.4.1.8
- TX EIRP: Transmit effective isotropic radiated power in dBm
OID: .1.3.6.1.4.1.41112.1.10.1.2.6
Impact:
- Per-chain monitoring detects imbalanced signal levels across RF chains
- RX Ideal Power comparison identifies suboptimal alignment or hardware issues
- TX EIRP monitoring ensures regulatory compliance and proper power output
- Enables proactive fiber and optical component troubleshooting
Implementation:
- All sensors use dbm sensor type for dBm units
- OIDs match LibreNMS LibreNMS/OS/AirosAfLtu.php exactly
- No divisors needed (values already in dBm)
Priority: TIER 3 MEDIUM (6-8 hour effort, enhanced diagnostics)
Gap Status: AF-LTU parity 80% → 90%
Remaining: UniFi channel-to-frequency conversion (requires vendor module work)
Files:
- priv/profiles/os_discovery/airos-af-ltu.yaml
- CHANGELOG.txt
Add TIER 2 HIGH priority wireless metrics for comprehensive AirFiber point-to-point monitoring.
These sensors enable capacity planning, link budget analysis, and interference detection.
AirFiber AF60 additions:
- Frequency: Operating frequency in MHz (OID: .1.3.6.1.4.1.41112.1.11.1.1.2)
- Distance: Link distance in km, divisor 1000 (OID: .1.3.6.1.4.1.41112.1.11.1.3.1.15)
- TX Capacity: Transmit capacity in Mbps, divisor 1000 (OID: .1.3.6.1.4.1.41112.1.11.1.3.1.7)
- RX Capacity: Receive capacity in Mbps, divisor 1000 (OID: .1.3.6.1.4.1.41112.1.11.1.3.1.8)
- Local RSSI: Local received signal strength in dBm (OID: .1.3.6.1.4.1.41112.1.11.1.3.1.3)
- Remote RSSI: Remote received signal strength in dBm (OID: .1.3.6.1.4.1.41112.1.11.1.3.1.18)
- Local SNR: Local signal-to-noise ratio in dB (OID: .1.3.6.1.4.1.41112.1.11.1.3.1.4)
- Remote SNR: Remote signal-to-noise ratio in dB (OID: .1.3.6.1.4.1.41112.1.11.1.3.1.19)
AirFiber AF-LTU additions:
- Frequency: Operating frequency in MHz (OID: .1.3.6.1.4.1.41112.1.10.1.2.2)
- Distance: Link distance in km, divisor 1000 (OID: .1.3.6.1.4.1.41112.1.10.1.4.1.23)
- TX Capacity: Transmit capacity in Mbps, divisor 1000 (OID: .1.3.6.1.4.1.41112.1.10.1.4.1.3)
- RX Capacity: Receive capacity in Mbps, divisor 1000 (OID: .1.3.6.1.4.1.41112.1.10.1.4.1.4)
Impact:
- Distance enables link budget analysis and alignment verification
- RSSI/SNR enable interference detection and signal quality monitoring
- Capacity metrics enable throughput monitoring and bandwidth planning
- Frequency confirms correct channel selection
Implementation:
- All sensor definitions match LibreNMS exactly
- Divisors applied for proper unit conversion (1000 for distance/capacity)
- Source: LibreNMS LibreNMS/OS/AirosAf60.php, LibreNMS/OS/AirosAfLtu.php
Priority: TIER 2 HIGH (4-6 hour effort, capacity planning and troubleshooting)
Gap Status: AF60 parity 50% → 80%, AF-LTU parity 60% → 80%
Next: TIER 3 - UniFi channel-to-frequency conversion, AF-LTU optical power sensors
Files:
- priv/profiles/os_discovery/airos-af60.yaml
- priv/profiles/os_discovery/airos-af-ltu.yaml
- CHANGELOG.txt
Implemented all critical and nice-to-have Arista sensor improvements identified
in LibreNMS parity audit. Upgrades Arista EOS support from 85% to 100% parity.
Files Changed:
- lib/towerops/snmp/profiles/vendors/arista.ex (enhanced)
Added 4 major enhancements:
1. DOM power conversion (watts→dBm): Detects optical power sensors via regex,
converts using formula dBm = 10*log10(watts*1000), preserves original value
in metadata
2. Arista threshold discovery: Walks ARISTA-ENTITY-SENSOR-MIB threshold table
(OID 1.3.6.1.4.1.30065.3.3.1.1.1), applies 4 threshold types (low_critical,
low_warn, high_warn, high_critical), converts thresholds to dBm for optical
sensors
3. Smart grouping: Organizes sensors by SFPs, PSUs, Platform (chipsets), Power
Connectors, System for better UX
4. Description cleanup: Removes redundant "sensor" text, simplifies PSU naming,
cleans whitespace
- lib/towerops/snmp/profiles/dynamic.ex (integration)
Added apply_vendor_post_processing/3 to call Arista enhancements after sensor
discovery. Applies to both arista_eos and arista-mos profiles.
- test/towerops/snmp/profiles/vendors/arista_test.exs (comprehensive tests)
Added 23 new tests covering:
- DOM conversion (6 tests): Rx/Tx power, Xcvr power, non-DOM sensors, edge cases
- Threshold discovery (4 tests): Basic thresholds, dBm conversion, no thresholds,
SNMP failures
- Smart grouping (6 tests): SFPs, Xcvr, PSUs, power connectors, platform, system
- Description cleanup (5 tests): Trailing sensor, duplicate Sensor strings, PSU
naming, hotspot, whitespace
- End-to-end post-processing (1 test): All enhancements in correct order
All tests passing (30/30 in arista_test.exs, 6367/6367 total).
Result: Arista EOS support upgraded from 85% to 100% LibreNMS parity. All critical
gaps closed: optical power now displayed correctly in dBm, alerting enabled via
thresholds, sensors organized for better UX, descriptions cleaned up.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
MikroTik mtxrGaugeTable temperature sensors report values in whole
degrees Celsius, not tenths. The MIB defines the unit as 'celsius(1)'
without the 'deci-' prefix used for voltage (dV), current (dA), and
power (dW).
This matches LibreNMS behavior which does not apply a divisor to
temperature sensors.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>