Download the PHP package drakelid/librenms-netbox-context without Composer
On this page you can find all versions of the php package drakelid/librenms-netbox-context. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download drakelid/librenms-netbox-context
More information about drakelid/librenms-netbox-context
Files in drakelid/librenms-netbox-context
Package librenms-netbox-context
Short Description LibreNMS NetBox Context - NetBox device, interface, cabling and circuit context inside LibreNMS device and port views
License GPL-3.0-or-later
Homepage https://github.com/Drakelid/netboxcontext
Informations about the package librenms-netbox-context
LibreNMS NetBox Context
NetBox device, interface, cabling and circuit context, shown inside the LibreNMS pages an engineer is already looking at — and compared against what LibreNMS actually observes.
NetBox is the source of truth for intent: identity, location, ownership, interface configuration, cabling, VLANs and circuits. LibreNMS is the source of truth for reality: availability, observed speed, administrative and operational state, MTU as configured on the box. This plugin puts the two side by side and says plainly where they disagree.
Table of contents
- Overview
- What it looks like
- Architecture
- Requirements
- Supported LibreNMS versions
- Supported NetBox versions
- Installation
- Development installation
- NetBox API token permissions
- Configuration
- Initial synchronisation
- Scheduler
- Device matching
- Interface matching
- Manual mappings
- Device context panel
- Interface context tab
- Custom fields
- Cache and staleness behaviour
- Troubleshooting
- Performance
- Security
- Upgrading
- Uninstalling
- LibreNMS daily.sh and upgrade persistence
- Using the context from another plugin
- Development
Overview
The plugin adds three things to LibreNMS:
- a NetBox Device Context panel on the device overview page, including the device's documented cabling and how it compares with what LibreNMS discovered;
- a NetBox Interface Context tab on the port page;
- its own pages under NetBox Context in the navigation — status, device and interface mappings, estate-wide topology drift, and settings.
All of them read from a local cache that a scheduled job keeps up to date. Opening a device page never calls NetBox, so a NetBox outage degrades the panel to "here is what we last knew, and how old it is" instead of slowing down or breaking LibreNMS.
Beyond mirroring NetBox fields, it compares the two systems and flags:
- interface speed, MTU, description and administrative-state differences;
- device hostname, management IP, serial, hardware and platform differences;
- cabling that disagrees with the neighbours LibreNMS discovered;
- interfaces present in LibreNMS but not documented in NetBox;
- devices that cannot be matched to NetBox at all;
- matches that are ambiguous — reported, never guessed;
- cached data that has gone stale.
Version 1 is strictly read-only. It never writes to NetBox and never changes LibreNMS devices or ports.
What it looks like
Device overview panel:
Port tab:
Status indicators are always a symbol and a word, so state is never carried by colour alone.
Architecture
Two properties hold throughout:
- UI code makes no decisions. Blade templates render a prepared DTO. All matching, comparison and formatting happens in services that are testable on their own.
- UI requests never call NetBox. Every page render reads local, indexed tables.
Directory layout:
Requirements
- PHP 8.2 or later
- LibreNMS 24.9.0 or later (see below)
- A reachable NetBox instance and a read-only API token
- Nothing else: no additional services, no queue worker, no frontend framework
Supported LibreNMS versions
The plugin uses LibreNMS's package-plugin API (librenms/plugin-interfaces),
which first shipped in LibreNMS 24.9.0. It was developed and reviewed
against 26.7.0.
The device overview panel uses the same panel markup LibreNMS 26.x uses for its own overview panels. On older releases the panel still renders correctly; it simply inherits that release's default styling.
No LibreNMS core file is modified.
One documented deviation
LibreNMS decides whether to show the port page's Plugins tab with
but renders that tab's content with
Package plugins publish against the interface — as LibreNMS's own
PluginProvider does for local plugins too — so the tab renders but the menu
entry never appears (verified in 26.7.0).
Rather than patch core, the plugin publishes a second, deliberately empty hook
(Hooks\PortTabMenu) under the abstract class purely to satisfy the menu
check. It renders nothing; the panel itself still comes from the interface
registration, so there is no duplication if a future LibreNMS calls both. It is
only registered when that abstract class exists, and it honours the same
authorisation rule as the tab, so the menu entry does not appear for a user who
would see nothing under it.
Supported NetBox versions
Designed against the NetBox REST API as it exists in 3.5 through 4.x.
Rather than targeting one point release, all NetBox JSON passes through a normalisation layer that:
- accepts both
role(NetBox ≥ 3.6) anddevice_role(older) on devices; - tolerates any nested object being
nullor absent; - accepts choice fields as either
{"value": …, "label": …}or a bare string; - treats a missing optional field as "not documented", never as an error;
- reads
connected_endpoints(the traced path) in preference tolink_peers(the immediate cable peer), and falls back cleanly when a version does not populate one of them.
If NetBox does not expose an endpoint (for example the circuits module is unused), the connection test reports it and that feature degrades; the rest of the plugin keeps working.
Installation
Production installation is through Composer, using LibreNMS's plugin manager:
Then:
- Open Overview → Plugins (or
/plugins) in LibreNMS and enable netbox-context if it is not already enabled. - Open its Settings page.
- Enter the NetBox URL and API token, save, and press Test connection.
- Run the first synchronisation (see Initial synchronisation).
Verify the exact plugin-manager syntax against your LibreNMS version with
./lnms list plugin.plugin:addhas existed since LibreNMS 22.2.0.
Development installation
Use a Composer path repository so edits take effect without a release:
Enable plugin error reporting while developing, so a broken hook shows the error instead of silently disabling the plugin:
NetBox API token permissions
Read-only is sufficient. Do not grant write permissions. The plugin never
issues anything but GET.
The token needs read access to:
| NetBox object | Endpoint | Used for |
|---|---|---|
| Status | /api/status/ |
connection test, version detection |
| Devices | /api/dcim/devices/ |
device context and matching |
| Interfaces | /api/dcim/interfaces/ |
interface context, cabling, matching |
| IP addresses | /api/ipam/ip-addresses/ |
addresses assigned to interfaces |
| Circuits | /api/circuits/circuits/ |
circuit context (optional) |
Interface payloads carry their cable, VLAN and VRF information inline, so no separate cable or VLAN permission is required.
In NetBox, create a token with Write enabled unchecked, and — if you use
object permissions — grant view on dcim.device, dcim.interface,
ipam.ipaddress and circuits.circuit.
Configuration
Settings resolve in this order, highest first:
- Environment variables (URL and token only)
- Plugin settings stored in
netbox_context_settings config/netbox-context.phpdefaults
Environment
Setting these keeps credentials out of the database entirely, and means a database restore into another environment does not carry NetBox access with it. When the token comes from the environment, the settings page says so.
Connecting NetBox from the UI
The plugin has its own settings page at /plugin/netbox-context/settings,
reachable from the Settings tab next to Status and the mapping pages, and
from the plugin's entry in the LibreNMS Plugins menu.
Enter the NetBox URL and a read-only API token, then press Save and test — it stores the credentials and immediately runs the full connection test, reporting reachability, TLS, authentication and read access to each endpoint separately. Nothing is synchronised until that succeeds.
The same connection form also appears on LibreNMS's own plugin-admin page (Plugins → netbox-context → Settings), so either route works.
Both require the netbox-context.admin ability, which LibreNMS administrators
and holders of plugin.admin inherit.
All settings
Under either settings screen:
- Connection — URL, API token, TLS verification, connect and request timeouts, API page size, retry attempts.
- Intervals — device, interface and circuit sync intervals; stale threshold; database chunk size; maximum tagged VLANs shown.
- Matching — which device and interface matching signals are enabled.
- Comparison — which fields are compared, and how descriptions are normalised before comparison.
- Display — which panels and sections are shown.
- Custom fields — which NetBox custom fields to display.
The API token field is always blank when the page loads. Leaving it blank keeps the stored token; there is a separate Remove token button. The token is never rendered, never returned to the browser, and is stored encrypted with Laravel's application key.
NetBox-side filters
On a shared NetBox you can limit what is synchronised, in
config/netbox-context.php:
These are passed to NetBox as query parameters, so the filtering happens server-side.
Initial synchronisation
Or everything at once:
A sync automatically runs the matching pass afterwards unless you pass
--no-match. To re-run matching alone:
Expect the first interface sync of a large NetBox to take several minutes; it is paginated and chunked, and subsequent runs only write objects that changed.
Scheduler
The plugin registers its own schedule with Laravel:
--due consults the configured per-dataset intervals (devices every 5 minutes,
interfaces every 15, circuits every 30 by default) and runs only what is
actually due — so the five-minute tick is cheap.
This requires LibreNMS's own scheduler cron entry, which a standard install already has:
If you prefer explicit cron entries instead, disable nothing and simply add:
Concurrent runs are prevented by an expiring cache lock, so an overlapping manual run is skipped rather than doubling the API load.
Device matching
Matching runs in a fixed order and stops at the first step that identifies exactly one NetBox device.
| # | Step | Confidence | Notes |
|---|---|---|---|
| 1 | Manual or locked mapping | 100 | An operator decision. Never overridden. |
| 2 | Persisted mapping | 100 | The previous answer, re-verified against the cache. |
| 3 | Exact hostname | 95 | LibreNMS hostname vs NetBox name, case-insensitive, trailing dot ignored. |
| 4 | Primary IP | 90 | Management address vs NetBox primary IPv4/IPv6, canonicalised, prefix stripped. |
| 5 | Unique short hostname | 80 | sw01 matches sw01.example.net — only if exactly one candidate. |
| 6 | Serial number | 75 | Off by default. Enable only if your serials are trustworthy. |
Rules that hold at every step:
- Ambiguity is never resolved by guessing. If a step finds more than one
candidate, the mapping is recorded as
ambiguous, the candidates are stored, and matching stops — it does not fall through to a weaker signal. - DNS is never consulted. A name that resolves somewhere is not evidence of identity.
- Every mapping records the method, the confidence and a human-readable reason:
Interface matching
The parent device must be mapped first. Candidates only ever come from that NetBox device — interfaces are never matched across devices.
| # | Step | Confidence | Notes |
|---|---|---|---|
| 1 | Manual or locked mapping | 100 | Never overridden. |
| 2 | Persisted mapping | 100 | Re-verified against the device's interfaces. |
| 3 | Exact name | 95 | LibreNMS ifName (or ifDescr when there is no ifName) vs NetBox name, case-insensitive. |
| 4 | Canonical name | 85 | Vendor abbreviation expansion, when enabled. |
Canonicalisation expands a known leading abbreviation and leaves the numeric part alone:
What it deliberately does not do:
- no substring or fuzzy matching;
- no matching on descriptions — two uplinks routinely share a description, and a port mapped that way would attribute one circuit's context to another;
- no rewriting of unknown prefixes: Juniper's
ae0,xe-0/0/0andirb.100pass through untouched.
The abbreviation table is not claimed to be universal. Extend or override it in
config/netbox-context.php:
A canonical name that is not unique on the device is reported as ambiguous.
Mapping states
Manual mappings
Plugins → netbox-context → Settings → Device mappings / Interface mappings, or directly:
/plugin/netbox-context/mappings/devices/plugin/netbox-context/mappings/interfaces
Both pages filter by state, so "show me everything ambiguous" is one click. Panels for unmatched or ambiguous objects link straight to them.
To map by hand, enter the NetBox object id and press Set. Optionally tick lock.
- Manual mappings are never repointed by the automatic matcher.
- Locked mappings are never repointed either, including mappings that were originally automatic — useful for pinning a correct match on a device whose hostname keeps changing.
- Clear manual mapping drops the flags and lets matching decide again; the row itself is kept.
- An interface may only be mapped to an interface on the device's own mapped NetBox device. Anything else is refused.
Mapping changes require the netbox-context.admin ability and are logged with
the username.
Device context panel
The panel arrives fully folded. A device page is scanned in seconds, so every section — device details, connections, topology, custom data and the comparison — is a fold that starts closed, and an engineer opens the one they came for:
Every closed header carries a count, so folding hides detail and never hides
a disagreement — a shut comparison section still says ⚠ 2 mismatches. The
folds are native <details> elements: no JavaScript, no Bootstrap collapse,
keyboard and screen-reader behaviour straight from the browser, and the content
stays in the document so find-in-page still reaches it. Open/closed state is not
remembered between page loads.
Unmatched, ambiguous and error states are never folded — when something is wrong, the panel says so on arrival.
Shows only fields NetBox actually has — no rows of dashes. Includes status, site, location, role, tenant, manufacturer, model, serial, asset tag, rack and position, primary and OOB IP, platform, tags, selected custom fields, an Open in NetBox link, and the consistency table.
Device comparison covers hostname, management IP, serial number, hardware model and platform. It is deliberately tolerant of harmless differences:
- one system holding the FQDN and the other the short name is a match;
Cisco C9300L-24P-4GagainstC9300L-24P-4Gis a match (the manufacturer prefix is normalised away);- model and platform differences are raised at notice severity, not as warnings — a catalogue naming difference is not an incident.
When there is no match the panel says so and shows what was looked for:
Connections
Below the context table, every cabled interface of the device as NetBox documents it — the device's physical neighbours on one screen, without opening a port at a time:
Details that matter:
- The far end links where it is most useful. A neighbour this LibreNMS also monitors links to its LibreNMS device page; one it does not links out to NetBox. An ambiguous mapping links to neither — an unresolved match must not become a confident link to one of its candidates.
- Only cabled interfaces appear. An access switch with four uplinks shows four rows, not forty-eight.
- Non-interface terminations are described, not flattened. A patch panel is named as a front or rear port, exactly as on the port tab.
- Cable peers are distinguished from traced paths. When NetBox reports only
this cable's peer rather than tracing the whole path, the row says
Cable peer — path not traced by NetBox, because that endpoint is the near side of a patch and not the far device. - The list is capped at
display.max_connections(default 100) and states the total when it truncates:Showing 100 of 384 documented connections. - It costs three queries regardless of port count — the interfaces, then
one batched lookup each for the far-end NetBox devices and their LibreNMS
mappings. Set
display.connectionsto off to remove the section entirely.
Topology drift
The cabling NetBox documents, held against the neighbours LibreNMS actually discovered over LLDP or CDP. It appears on the device panel and, for the whole estate, on the plugin's Topology page.
The five verdicts:
| State | Meaning | Severity |
|---|---|---|
| ✓ Match | Both systems describe the same far end | — |
| ⚠ Wrong neighbour | Both describe a far end and they disagree | warning |
| ⚠ Not in NetBox | LibreNMS discovered a neighbour NetBox has no cable for | warning |
| — Not discovered | NetBox documents a cable LibreNMS has not seen | notice |
| — Not comparable | The far end could not be identified on both sides | — |
The categories are deliberately asymmetric, because the evidence is. A neighbour LibreNMS has seen is proof a link exists. LibreNMS not seeing one proves almost nothing: LLDP and CDP only reveal peers that speak them, so a perfectly correct cable to a server, to a host with discovery disabled, or to a device this LibreNMS does not poll looks exactly like a missing one. That is why "not discovered" is a notice and carries its caveat in the UI, while "not in NetBox" is a warning.
Rules that keep the output trustworthy:
- Every finding is anchored to a LibreNMS port with a resolved NetBox interface mapping. Without that anchor there is no honest way to say "NetBox has no cable here" — only "we do not know which NetBox interface this is", which is a mapping problem the plugin already reports on the mapping pages. Cabled NetBox interfaces with no mapped port are counted and stated ("2 cabled NetBox interfaces could not be compared…"), never silently dropped.
- Far ends are compared by device id where possible, falling back to hostname only when one side is a device LibreNMS does not monitor. Two devices sharing a hostname prefix do not become a match.
- Interface names are compared canonically, so
Te1/1/2againstTenGigabitEthernet1/1/2is agreement, not drift. - Cables to patch panels and circuit terminations are not compared at all. No discovery protocol could ever confirm a rear port.
- Inactive link rows are ignored. LibreNMS keeps them as history; they are not a current observation, so they cannot contradict NetBox.
- Whole pages of devices cost a fixed set of queries, not a set per device.
Set display.topology_drift to off to remove the section and the page's
findings entirely.
Interface context tab
Under the port page's Plugins tab. Shows the NetBox interface, its description, type, configured speed, MTU, enabled state, 802.1Q mode, VLANs, addresses, VRF and custom fields.
Connection. When NetBox traces the path to another device interface, both ends and the cable are named. When it does not — a front port, rear port, patch panel or circuit termination — the tab says what the termination actually is:
Circuit. When the path terminates on a circuit: circuit ID, provider, type, status, tenant, committed rate and description.
Comparison. Speed, MTU, description and administrative state.
A few details that matter in practice:
- Speeds are normalised to bit/s before comparison. NetBox stores kbit/s;
LibreNMS stores
ifSpeedin bit/s andifHighSpeedin Mbit/s.ifSpeedsaturates at 4 294 967 295 on anything faster than ~4.29 Gbit/s, soifHighSpeedis preferred and a saturatedifSpeedis treated as unknown rather than as a measurement. Without this, every 10G port would report a false mismatch. - A missing value on either side is
Unknown, not a mismatch. NetBox not documenting an MTU is not a discrepancy. - Administrative state is compared against
ifAdminStatusonly. An interface that is admin up and oper down is an outage, which LibreNMS already alerts on — not a documentation problem. - Description comparison is configurable: trim, collapse repeated whitespace, optional case-insensitivity. Nothing is ever stripped from the middle of a description.
Custom fields
NetBox custom fields are fetched dynamically — no field name is hard-coded. Choose which to display in the settings page (comma-separated), or leave the list empty to show everything NetBox returns.
Values are formatted for display:
| NetBox value | Shown as |
|---|---|
| string | the string |
| number | the number |
| boolean | Yes / No |
| list | comma-separated |
| object reference | its display / name |
| URL string | a link, only for http/https |
| null or empty | the row is omitted entirely |
Nothing NetBox returns is ever rendered as HTML. A value containing markup is
escaped and shown as text, and a javascript: or data: "URL" is rendered as
text rather than becoming a link.
Cache and staleness behaviour
Every panel states how old its data is. Past the configured threshold (60 minutes by default) it says so prominently:
Freshness is measured from the last successful sync, not the last attempt.
Reconciliation is only ever done from a complete run
This is the most important behaviour in the plugin.
A sync marks objects NetBox did not return as stale only if it traversed its entire dataset successfully. If it fails on page 37 of 50:
- the run is recorded as
partial, withauthoritative = false; - what was fetched is still written to the cache;
- nothing is marked stale, because the run knows nothing about the pages it never reached.
Cached objects are never deleted by a sync, only flagged. An object that comes back next run simply clears its flag. Mapping rows — especially manual ones — are never touched by synchronisation at all.
Sync outcomes:
| Status | Meaning |
|---|---|
success |
Whole dataset traversed; reconciliation performed |
partial |
Some data arrived, traversal incomplete; no reconciliation |
failed |
Nothing usable arrived |
Troubleshooting
Start here:
The status page shows connection details, NetBox version, cache counts, mapping tallies and the last 20 synchronisation runs with their counters and errors.
| Symptom | Likely cause and fix |
|---|---|
| Panel says "not matched to NetBox" | Compare the diagnostics against NetBox. Usually the hostname differs; enable short-hostname fallback or map manually. |
| Panel says "ambiguous" | Two NetBox devices share the name. Map manually — the candidates are listed. |
| "not documented in NetBox" on a port | The interface really is absent from NetBox, or its name differs beyond normalisation. Check the interface mappings page. |
| Everything is stale | Synchronisation is not running. Check the LibreNMS scheduler cron, then run netbox-context:sync --devices by hand. |
403 Forbidden in the test |
The token lacks read access to that object type. See token permissions. |
401 Unauthorized |
Wrong or revoked token. Re-enter it; it is not retried, by design. |
404 on circuits |
The circuits module is not in use. Turn off circuit sync in the settings. |
| "stored NetBox token could not be decrypted" in the log | APP_KEY changed. Re-enter the token. |
Sync says partial repeatedly |
NetBox is timing out or rate limiting. Increase the request timeout, reduce the API page size. |
| Cross-host redirect refused | NetBox is redirecting to a different hostname. Configure the URL NetBox actually serves on. |
Plugin log lines are written to LibreNMS's own log (logs/librenms.log) and are
prefixed netbox-context:. Synchronisation logs one aggregate line per run, not
one per object:
Performance
Designed for 5 000+ devices and 250 000+ interfaces.
Rendering. A device panel is three indexed queries (freshness, mapping, cached device); a port tab is at most six, and only reaches that when the interface is both cabled to a known device and terminated on a circuit. Neither grows with the size of the cache, and neither calls NetBox. The scale suite asserts these bounds directly.
Synchronisation. Paginated API access, one page normalised and written at a time, bounded memory. Each chunk costs three statements regardless of chunk size: read current hashes, upsert what differs, stamp the rest as seen. Objects whose content hash is unchanged are not rewritten — on a settled network a sync of 183 000 interfaces updates a few hundred rows.
Matching. Ports are matched against a per-device hash index built from one indexed query, so the cost is linear in ports. No interface is ever compared with another device's interfaces.
Indexes. Mappings are unique on device_id / port_id and indexed on the
NetBox side; interfaces carry composite indexes on
(netbox_device_id, name_normalized) and (netbox_device_id, name_canonical),
which is exactly what the matcher queries.
Tuning knobs: connection.page_size (API page size), sync.chunk_size
(database chunk size), and the per-dataset intervals.
Security
Credentials. The API token is read from the environment first, otherwise
from the plugin's own settings table where it is stored encrypted with
Laravel's application key — not in LibreNMS's plaintext plugin-settings JSON.
It is never rendered, never logged, and never sent to the browser. The settings
page shows only •••••••••••••••• configured. An empty token field on save
means "keep the current token"; removal is a separate, explicit action.
Transport. TLS verification is on by default; disabling it is an
administrator action with the consequence stated next to the checkbox. Only
http and https are accepted, and credentials embedded in the URL are
rejected. Connect and request timeouts are enforced.
Redirects. Never followed automatically. A same-host redirect is re-issued
by hand; a redirect to another host is refused outright, so the Authorization
header is never handed to a host the operator did not configure.
Request targets. The plugin only ever requests paths from a fixed set of endpoint constants, built against the configured base URL. No browser-supplied value reaches the HTTP client. Object links shown in the UI are constructed from the base URL and a numeric id, not from anything NetBox returned.
Note that private (RFC1918) NetBox URLs are explicitly supported — NetBox normally lives on the management network, and an SSRF filter that blocked private ranges would break every real deployment. The control is that only a plugin administrator can set the base URL.
Authorisation.
- All plugin routes require authentication.
- Plugin pages require
netbox-context.view. - Settings, sync and mapping changes require
netbox-context.admin, which LibreNMS administrators and holders ofplugin.admininherit. - The device panel and port tab defer to LibreNMS's own device and port policies. A user who cannot see a device in LibreNMS learns nothing about it here.
- State-changing requests go through the
webmiddleware group and carry CSRF protection.
Output. Every NetBox-derived value is escaped by Blade. Nothing NetBox
returns is rendered as HTML, and only http/https values become links.
Logging. Errors are scrubbed by a redaction helper that removes known token
values and anything shaped like an Authorization header before the message
reaches a log, an exception or a page.
Upgrading
Schema changes always arrive as new migrations; released migrations are
never edited. Check CHANGELOG.md for breaking changes before a major version.
Cached NetBox data and mappings survive upgrades. If a new version adds cached fields, run a full sync afterwards to populate them:
Uninstalling
Disabling or removing the plugin does not destroy anything. Cached data and mappings — including every manual mapping — are left in place, so re-enabling restores the previous state.
Step 3 is never performed automatically. If you only want to drop the API token, use Remove token on the settings page instead.
LibreNMS daily.sh and upgrade persistence
The plugin is a Composer package, not files copied into a directory LibreNMS overwrites. Nothing it owns lives inside the LibreNMS source tree.
After daily.sh or any normal LibreNMS upgrade:
| Survives | Why |
|---|---|
| The package itself | It is a Composer dependency; daily.sh runs composer install, which reinstalls it. |
| Plugin registration | Laravel package auto-discovery reads extra.laravel.providers from the package's own composer.json. |
| Database tables | Plugin-owned migrations; daily.sh runs artisan migrate, which is additive. |
| Configuration | Stored in netbox_context_settings (a plugin table) and/or the environment. |
| API token | Same, encrypted; or in the environment, which upgrades do not touch. |
| Mappings | netbox_context_device_mappings / _interface_mappings — never truncated by a sync or an upgrade. |
| Scheduled sync | Registered by the service provider through Laravel's scheduler, not by a cron file that could be replaced. |
No LibreNMS core file is modified, so there is nothing for an upgrade to conflict with.
For a path-repository development install, note that composer install during
daily.sh will keep resolving the symlink — keep the checkout in place, or
switch to the Packagist version for production.
Using the context from another plugin
The correlation this plugin maintains is deliberately reusable. Two published interfaces are bound in the container:
Both answer from the local cache and never call NetBox, so they are safe to use in a page render. Implementations must keep that guarantee.
Development
The suite runs standalone on Orchestra Testbench, so the matchers, comparison
engine, API client and sync services can be exercised without a LibreNMS
install. Set LIBRENMS_ROOT — or keep a LibreNMS checkout beside this package —
and the same tests run against the real LibreNMS models instead of the stubs in
tests/Stubs/librenms.php.
Coverage worth knowing about:
tests/Unit/DeviceMatcherTest.php,InterfaceMatcherTest.php— the full matching matrix, including ambiguity, locked mappings and "no false match".tests/Unit/ComparisonServiceTest.php— including the 100GifSpeedsaturation case and admin-vs-oper state.tests/Unit/NormalizerTest.php— NetBox schema variations, null nested objects, non-interface cable terminations.tests/Unit/SecurityTest.php— token redaction, URL scheme rejection, cross-host redirect detection.tests/Feature/DeviceSyncTest.php— partial-sync safety: a run that fails halfway must not declare anything deleted.tests/Feature/NetBoxApiClientTest.php— pagination, retries, 401/403/404/ 429/500, malformed JSON, redirect handling.tests/Feature/ContextPanelTest.php— both panels, including escaping of NetBox-supplied markup and authorisation denial.tests/Feature/AdminRoutesTest.php— authorisation, token-preservation semantics, cross-device mapping rejection.tests/Scale/MatchingScaleTest.php— linear matching and bounded query counts.
Licence
GPL-3.0-or-later. See LICENSE.