Skip to content

Releases: dsmorgan/patchbay

0.17.1

Choose a tag to compare

@dsmorgan dsmorgan released this 25 Sep 02:13

Fixed

  • A switch-to-switch cable that both LibreNMS (LLDP) and the UniFi
    controller report drew twice when the controller stored the operator's
    port label where SNMP reported the ifName. The normalizer now also
    matches unifi links to lldp links by device pair, keeping genuine
    parallel cables (#56, Sam). The pair pass runs after the weaker-source
    passes, so a retired unifi row still claims its port against MAC-table
    inference and ghost switches.

0.17.0

Choose a tag to compare

@dsmorgan dsmorgan released this 24 Sep 23:19

Added

  • Topology: undo and group moves (#55, from Sam / @slmingol). Ctrl+Z
    or Cmd+Z steps back up to ten node moves. Shift+drag on the background
    draws a lasso; dragging any selected node moves the group together,
    spacing kept, and one undo restores the whole group. Escape or a
    shift-click on the background clears the selection. Landed with two
    fixes: undo redraws the node even after the simulation has cooled, and
    it restores the node's saved state instead of turning a settled node's
    hold into a pin.

0.16.0

Choose a tag to compare

@dsmorgan dsmorgan released this 24 Sep 17:17

Added

  • Load view: UniFi switch ports carry throughput (#53, from Sam /
    @slmingol). The controller reports a rolling byte rate per switch port,
    the same figure its own UI shows, and the collector now stores it as the
    port's in/out rate and as a rate_history sample. Switch-to-switch and
    switch-to-AP legs on UniFi-managed switches color in the load view and
    get a 24-hour peak, where before only SNMP-polled ports did. An AP's
    uplink carries the same figures and is stored too. A down device keeps
    its last good reading, as it does for port status and speed.

  • Load view: the busier end of a cable wins. Where both ends of a
    link report a rate, the edge used to show whichever end sorted first by
    name. The two ends read the same wire through different windows, a
    UniFi rolling rate against a five-minute SNMP average, so the edge now
    shows the higher reading, for the current figure and the 24-hour peak
    separately. A burst one poller caught no longer hides behind the
    other's average.

Changed

  • Deployment guide: verify snapshot delivery before you need it (#25).
    A new step walks through mounting the off-host share into both patchbay
    services, triggering one snapshot, checking the share from another
    machine for the renamed copy and no leftover .part file, waiting for
    the sync, and opening the off-host copy with the stack down.

Fixed

  • Rate samples age out whatever the source. The seven-day
    rate_history prune lived in the LibreNMS collector, so a site whose
    rates came from another source would have kept every sample forever.
    The normalizer's housekeeping pass prunes now, every cycle, beside the
    raw-payload expiry.

  • The test suite passes under bare pytest (#54, from Sam / @slmingol).
    Two test modules import helpers from a sibling through the tests
    package, which resolves only with the repository root on the path.
    python -m pytest puts the working directory there and plain pytest
    does not, so the same tree passed on one machine and failed eight tests
    on another. The pytest config adds the root now.

0.15.0

Choose a tag to compare

@dsmorgan dsmorgan released this 23 Sep 21:57

Changed

  • Agent instructions live in AGENTS.md, the convention shared across
    AI coding tools, instead of CLAUDE.md. CLAUDE.md and
    CLAUDE.local.md are gitignored for personal notes (Sam's suggestion
    in #51). Claude Code 2.1.277 or later reads AGENTS.md natively.

Fixed

  • Topology: a filter toggle no longer re-flows the map (#52, from
    Sam / @slmingol). Every toggle restarts the simulation, and on a map
    without saved positions that moved every node the operator hadn't
    dragged — by a hundred pixels or more for core only. Now the first
    settle is the arrangement: the nodes that took part in it are held where
    they landed, and a node a toggle reveals later flows into the gaps
    between them. Drag still pins and saves, shift-click frees, reload
    re-settles. A map whose nodes all carry saved positions is unchanged.

  • pfSense: a 404 note says which kind of absence it is (#51, from
    Sam / @slmingol). The OpenVPN and IPsec status endpoints also answer 404
    when that feature is not configured on the firewall, pfrest installed
    and current, and the old note sent operators after the package. Those
    two calls are optional now and their note names the feature; a core
    endpoint's 404 still means pfrest is missing or too old, and says so.

0.14.0

Choose a tag to compare

@dsmorgan dsmorgan released this 17 Sep 23:10

Added

  • Routed view: every router draws (#50, ADR-0002 Decision 4). The
    routing tier holds them all: routers whose lanes don't overlap share a
    column, overlapping ones — an HA pair, a core router behind the edge
    firewall — take successive columns toward the internet, and whoever
    holds a default route stands last beside its cloud (a multi-egress site
    gets a cloud per default route). Router boxes are open frames now, so a
    lane bound for a further router visibly passes the nearer one; a lane
    lists every router that claims it (all gateways in the tooltip); a
    tunnel leaves the router that terminates it. A plain router wears the
    router glyph and color, a firewall the firewall's, as on the Overview.
    Scenario tests cover the HA pair, the inner router, and two egress
    routers — no live site yet exercises them, so they are the contract.

  • Routed view: Load mode (#50, ADR-0002 Decision 3). The router's
    per-network legs are the only edges on this view with counters — its
    VLAN interfaces, when the firewall is a polled device — so Load
    heat-tints a segment where each lane meets the router, the number
    beside it, and the default route's drop from the WAN interface, on the
    topology's palette with the same now / 24h peak select
    (load=peak). Busier direction over capacity; a declared service
    capacity beats the port speed. Lanes and attachments go quiet; grey
    means no measurement. The demo seeds firewall counters so the public
    snapshot shows it.

  • Routed view: Protocol mode (#50, ADR-0002 Decision 3). The third
    radio paints address families: IPv4 lanes teal, IPv6 lanes amber, a
    dual-stack lane teal with an amber dash riding it, and every attachment
    dot — a host's leg, the router's gateway — colored by the families it
    actually holds there. The select narrows to one family and dims
    whatever lacks it (proto=4 / proto=6; the default is both). A leg
    now lists every address a device holds on a network, and a rail knows
    its IPv4 and IPv6 gateways separately (both in the tooltip).

  • Routed view in the snapshot (#50). The break-glass file now carries
    the L3 picture under the topology map, from the same builder and
    template as /routed — never a second implementation. Its state lives in
    memory there (the URL belongs to the topology map on the same page),
    the vertical rails chip redraws in place instead of reloading, and
    double-click jumps to the device's section or the VLAN's row.

  • Topology: derived zones (#47). A translucent hull now sits behind
    each hypervisor and the guests that are nodes on the map (a virtualized
    firewall, a router VM), and a fainter one around the whole cluster when
    the hypervisors share a vSphere — named after the vSphere server, like
    the routed view's box. Zones are computed from the parent relationship
    patchbay already holds, never drawn by hand; a grouping force pulls
    members together, the VM-on-host edge hides inside a drawn zone, and
    zones draw under everything and take no clicks. In the tiers layout a
    zone keeps only the members in its hypervisor's band, so a hull never
    wraps the fabric between an Edge-band guest and its host. The zones
    chip (zones=0) turns them off. The demo's hypervisors gained specs
    so the public snapshot shows the #44 line.

  • Topology: node detail panel (#45). Clicking a node no longer
    leaves the map: a panel floats over the map's right edge with the
    node's role, status and how long ago it was seen, address, hardware,
    OS, specs, host, VLANs, and every link with the port at each end, the
    speed, utilization, and who reported it. A peer's name in the list
    selects that node; Escape or × closes; the selection rides the URL as
    sel=. The device page is the secondary action — double-click, or
    ⌘/Ctrl-click for a new tab — the routed view's contract. The panel
    reads the graph JSON, so the snapshot has it too (its link jumps to
    the device's section). Read-only by design: edits belong on /ops.

  • Topology: node card refresh (#44). Every card node now shares one
    anatomy: a rounded-square icon chip anchors the left edge, the status
    LED pins the top-right corner, and the name and subtitle read from a
    fixed inset — so a mixed row of switches, hypervisors, APs, and hosts
    scans as one kind of thing. Hypervisors gain a third line of hardware
    mini-specs (24c · 192 GB), recorded by the vSphere collector from
    vCenter's hardware summary into two new devices columns (cpus,
    mem_bytes, migrated on start) and printed on the device page and
    Overview card too; the line only appears when the data does. Role color
    stays the stroke and the glyph, status stays the LED — no glow channel.

Fixed

  • The routed view remembers your view options. A bare /routed — the
    rail's link — restores the last-used mode, family and load choice,
    visibility toggles, and axis from the browser's storage by rewriting the
    URL on arrival, the way /topology has since #16. The two maps now
    follow one rule: an explicit URL always wins outright, focus is never
    remembered, and the snapshot is exempt.

  • Device merge dropped the new specs columns. A hypervisor that
    LibreNMS also polls folds its vSphere row into the SNMP-owned primary,
    and the merge's identity field list must name every column or the
    specs vanish on every poll (the way ip6 once did). Named, with a
    regression test.

  • Topology: speed and VLAN chips on edges (#43). The bare mid-edge
    speed text is now a pill on the edge, and under it a second pill names
    the link's VLANs when there are three or fewer — an access link says
    VLAN 20, a trunk carrying twelve stays quiet (the tooltip lists
    them). Chips carry facts, not provenance: card fill and a line border,
    so edge color stays the reporter's alone; the speed text still warns
    amber at ≤100M and red at ≤10M, and the load view appends the
    utilization. The edge chips chip (chips=0) hides them.

  • Topology: port names on hover (#48). Edges no longer print their
    interface names permanently — on a dense map neighboring labels
    overlapped into noise. Hovering an edge (its hit area is now a wide
    invisible twin of the line, which also carries the tooltip) reveals the
    names at both ends; hovering a device reveals the names on every one of
    its links — the "what is plugged into this switch" question. Revealed
    names paint above every node. The port names chip (ports=1) shows
    them all, for the old always-on picture.

  • Device totals in the nav rail (#46). Every page's rail carries how
    many devices patchbay knows and how they split: up, down, and stale — a
    device no source has reported for two hours counts as stale whatever
    its last status said, the same window that ages out inferred links.
    Inferred unmanaged switches are a guess, not a device, and don't count.
    Collapsed, the numbers stack under their dots; the block links to the
    Overview. The snapshot header carries the same totals, frozen at
    generation beside the data ages.

0.13.0

Choose a tag to compare

@dsmorgan dsmorgan released this 09 Sep 03:53

Added

  • Routed view: networks-as-lanes layout (default; the vertical rails
    chip turns the same drawing on its side). VLANs are horizontal lanes
    reading edge → internet, left to right: lane labels with subnets in a
    left gutter, loose single-homed chips at the edge, one logical
    wireless container holding every AP's clients (per-AP attribution in
    tooltips), hypervisor slabs spanning their lanes with tenants inside,
    the router spanning everything it routes, and the internet cloud plus
    tunnels at the far right. Height is fixed by network count, width grows
    with devices — landscape-native, made to read at a distance.
  • Routed view: one virtualization box. All hypervisors fold into a
    single box — the wireless container's sibling — named after the vSphere
    server when the guest-aware collector owns a hypervisor row. The routed
    view is logical, so which physical host a VM runs on becomes tooltip
    detail, and a router that runs as a guest draws inside the box.
  • Routed view: one renderer, two axes. The vertical-rails view is the
    lanes drawing turned upright — same containers, boxes, chips, and
    rules, edge at the bottom and the internet at the top — instead of the
    earlier tiered layout. Lanes sit tighter, multi-homed
    hosts share a column when their spans don't overlap, and VPN tunnels
    sit beside the internet cloud so the transport leaves the router
    straight from its edge. The viewBox hugs the drawing, so fit means
    the whole map.
  • Routed view: click focuses, a page is the secondary action. A plain
    click on a network, host, box, or router now focuses it in place —
    highlight what's attached, dim the rest, focus= in the URL — and a
    second click or a click on the background clears it. Double-click opens
    the thing's page; ⌘/Ctrl-click or middle-click opens it in a new tab.
    Navigating away on a plain click felt like falling through the map.
  • Routed view: the shell's last-polled indicator and auto-refresh, which
    every other page already had.
  • Routed view: Evidence mode, and with it the segmented view control
    from ADR-0002 Decision 3. Each lane takes the color of its strongest
    reporter — a firewall interface (it routes it), a switch carrying the
    VLAN, the controller, a hypervisor port group, IPAM alone (dashed:
    documented, nothing carries it), a route learned through a tunnel —
    the tag carries a badge per reporter, and everything attached goes
    quiet. view=evidence in the URL. Load, Protocol, multi-router sites,
    and snapshot embedding are tracked in #50; ADR-0002 carries an
    amendment recording the lanes-era decisions. The "still settling"
    banner is gone.

Fixed

  • Powered-off VMs and down devices no longer count on the routed view.
    A VM that is off still has legs on paper (its port group VLAN, a
    documented address, a cached guest IP) and was counted in the VM chips;
    now only active devices count, unknown status still counts, and the
    virtualization box lists the sleeping guests in its tooltip. The
    builder gained scenario tests for typical sites — ARP-only flat
    networks, IPv6-only sightings, ARP plus learned-VLAN fusion without
    IPAM, mixed-case MACs, addresses outside every network, wireless
    clients across APs, an empty database, and a router with no addresses.
  • Merging a re-duplicated device no longer drops its addresses. A
    device LibreNMS re-creates under its FQDN every poll merged into the
    fresher row, and colliding port rows on the older duplicate were
    discarded wholesale — so the firewall's interface addresses (which only
    the firewall collector writes) vanished whenever that collector was
    skipped or failed, and with them the routed view's gateway exclusion,
    which let dnsmasq's "gateway" ARP rows draw as a phantom host spanning
    every network. Identity facts (ip, ip6, mac, description, ifindex) now
    fill the primary's gaps whatever their age; liveness (status, speed,
    rates) still follows the fresher row. Separately, an ARP or IPAM row
    carrying a MAC some device's interface owns is that device, never a
    host, whatever name the address wears.

Changed

  • IPAM is identity, never liveness. phpIPAM no longer writes endpoint
    rows (it re-stamped last_seen every poll, so documented-but-gone
    hosts never aged out and ghost hosts haunted the routed view); it now
    only lends hostnames to endpoints real observers saw, and legacy
    doc-rows are retired on the next poll. On the routed view, IPAM
    addresses matching an observed host add "ipam" legs — including
    networks nothing can observe, like an isolated storage VLAN — but IPAM
    alone never draws a host. UniFi clears stale AP attribution for
    clients gone from the controller's station list, so ex-wireless hosts
    stop counting as clients. A tunnel route whose destination contains
    local networks (WireGuard allowed-ips for the home supernet) is the
    tunnel's source side: named on hover, never drawn as a network.
  • Switch MAC tables discover hosts, but a bare MAC is not a host. A
    MAC learned on a pure access port counts as a sighting on that port's
    VLAN (trunks and mirror destinations excluded), named by its hostname
    or, failing that, its address from any endpoint or IPAM row — a
    WAN-side neighbor with an address outside every documented subnet
    shows as that address. A MAC with neither is usually a bond member or
    kernel port of a host already drawn, so it is counted in the lane's
    tooltip and never listed as a host.
  • The switch MAC table keeps the VLAN a MAC was learned in. LibreNMS
    reports it per entry (platforms that don't leave it 0), and fdb rows
    are now keyed by it, so a trunked host — a storage box with a VLAN
    interface per network on one 10G port — is placed on every network it
    talks in, from real switch evidence, no alias needed. Legs name the
    address that belongs on that network, and a second address on the
    same network (bond plus trunk sub-interface on mgmt) rides the leg in
    the tooltip. Databases from before the change migrate in place.
  • Guests are placed by what ARP saw their NICs do. A VM's collector
    reports NIC MACs but no guest addresses, and an untagged port group
    never reaches the VLAN tags, so a two-NIC guest drew as single-homed.
    A device's NIC seen by ARP (or documented in IPAM) with an address is
    now a leg on that address's network; mgmt_ip is the last resort.
    Routers still claim networks from their own interface config only.
  • phpIPAM lends names by exact address first, MAC second. One NIC can
    carry several documented addresses; matching by MAC alone handed the
    wrong row's name to whichever came first. Names an earlier MAC-only
    lend got wrong are corrected on the next poll.

0.12.0

Choose a tag to compare

@dsmorgan dsmorgan released this 04 Sep 00:54

Added

  • Firewall VPN tunnels are first-class, type-labeled objects
    (#42). WireGuard,
    OpenVPN, and IPsec tunnels from OPNsense (peer/session/SA status
    endpoints) and pfSense (OpenVPN/IPsec status; the WireGuard VPN gateway,
    previously dropped, becomes tunnel health) land in a new tunnels
    table — prune-per-type with empty-response guards, and no key material
    stored, not even public keys. On the topology they draw as dashed
    purple egress nodes hung off their firewall, labeled with type and
    peer; on the routed view they sit beside the internet cloud, and a
    subnet reachable through a tunnel rails off the tunnel node — drawn
    even when nothing local claims it, because reachability through the
    tunnel is its participation. WireGuard liveness derives from handshake
    age (up / idle / down); tunnel interfaces stay out of the port model,
    exactly as before. The demo network gains a site-b WireGuard peer so
    both maps show the feature out of the box. The OPNsense API user needs
    the VPN page privileges — optional, everything else degrades cleanly
    without them. The terminating firewall's device page lists its tunnels
    in their own section, separate from the port table.

  • Routed view redesign: rails spread to a computed gap (wider for
    more networks, capped for few); hypervisors and APs draw as spanning
    boxes in tiers of their own, with guest VMs grouped inside their
    hypervisor and wireless clients inside their AP — every host counts in
    exactly one place. Dual-homed hosts known only from ARP now fuse by
    canonical hostname into real host boxes (rail gateway addresses are
    excluded, so dnsmasq's per-VLAN "gateway" rows can't invent a phantom
    host). Boxes carry the topology's role icons and colors, and hovering
    a network now dims isolated (gray) rails too.

Fixed

  • OPNsense 403 poll notes now name the exact privilege to grant (and note
    that a Status sub-privilege covers the VPN reads) instead of "the
    matching page privilege".
  • Brocade/Ruckus FastIron ports no longer show the long-form port name
    ("GigabitEthernet1/1/10") as their description — that is the vendor
    echoing the ifName when no comment is set, not documentation.
  • Snapshot polish: per-source data ages are humanized ("16 h", not
    "967m"), VM cards drop the "· ?" when no hardware string exists, and
    VPN tunnels now appear in the snapshot both on the map and as a table
    under the terminating firewall.
  • Hypervisor device pages split vmk* kernel interfaces into their own
    section — they carry the management addresses but no cable ends on
    one, so they no longer pad the physical port list.
  • The configs page shows the firewall's management IP on its API-sourced
    row, the patch-panel section header is just the panel's declared name,
    and the topology toolbar controls share one height.

0.11.2

Choose a tag to compare

@dsmorgan dsmorgan released this 01 Sep 15:47

A data-quality release: the liveness and pruning gaps from the post-0.10.0 audit (#41), and the mid-poll duplicate-cable flicker (#38).

Fixed

  • The transient duplicate AP↔switch cable right after a poll starts is gone (#38). Normalize now runs inside the same transaction as each collector, so the web UI never reads fresh-but-unnormalized state. A normalize failure rolls back to a savepoint and the source's data still lands; per-source atomicity is unchanged.
  • The audit's liveness and pruning gaps (#41) — the family where nothing lies, things just never leave:
    • vSphere no longer writes a VM's cached powerState (with a fresh timestamp) while the owning host is not responding — a virtualized firewall's own status report wins again. Placement still lands.
    • UniFi no longer writes port oper/admin/speed from a down or heartbeat-missed device's cached port_table; the last good values stand, the same gate temperature already had.
    • vnic_vlans is refreshed by replace: a port group moved to untagged, or a deleted VM's MAC, no longer re-emits phantom 802.1Q membership forever.
    • Devices removed from LibreNMS or the UniFi controller are retired instead of haunting every page with frozen status (each collector's own rows only, guarded on a non-empty listing).
    • Endpoint observations (ARP, leases, controller clients, the IPAM address book) age out after a week unrefreshed, so /drift stops treating months-old rows as live sightings.
    • VLANs that vanish from every switch config and SNMP table get the same claim-aware prune phpIPAM's rows got in 0.11.1.
    • The oxidized per-device port_vlans rewrite is scoped to its own rows, like the port_roles delete beside it always was.

Ten new regression tests cover the round (suite at 230).

Upgrade: docker compose pull patchbay patchbay-poller && docker compose up -d patchbay patchbay-poller. No schema or configuration changes.

0.11.1

Choose a tag to compare

@dsmorgan dsmorgan released this 31 Aug 21:01

Quick fix round on top of 0.11.0:

  • phpIPAM prunes VLANs deleted from IPAM — the collector's own rows only, guarded on a real listing, and never a VLAN a device still claims (that one just loses its IPAM documentation and reads "no subnet documented in IPAM"). Previously a deleted VLAN sat on /vlans forever.
  • The routed view carries a visible still-settling note linking to the issue tracker.

Full details in the changelog.

0.11.0

Choose a tag to compare

@dsmorgan dsmorgan released this 31 Aug 20:33

The routed view finishes its real-site shakedown, and a security audit hardens every store.

Highlights

  • The routed view discovers the internet uplink — the default route's exit interface resolves to its VLAN's rail: drawn green, named on the drop line ("via VLAN 299"), hover-linked to the cloud. Appliance-style WAN ports keep the plain cloud-to-router drawing; no configuration either way.
  • Rails must participate — IPAM-only supernets, aggregates, and unclaimed VLANs stay on /vlans; the map shows the networks that exist. ADR-0002 is accepted and #17 closed.
  • UniFi device temperature (#40, thanks @slmingol) — the controller's reading lands as a device fact on the detail page, gated on a real sensor and an up device.
  • Security hardening — raw payloads strip credential fields (LibreNMS device rows carry SNMPv3 secrets); firewall-config redaction matches secret tags as substrings and catches OTP seeds; the routed graph JSON is script-escaped; pfSense/phpIPAM keep last good data through failed or empty polls.
  • Targeted deletion — snapshots and stored firewall config revisions can be deleted from the UI.
  • The break-glass snapshot embeds the latest firewall config revision (double-scrubbed), PATCHBAY_SNAPSHOT_AT is editable on /ops, and /configs shows canonical device names.

Upgrading note: stored firewall revisions captured before 0.11.0 predate the wider redaction — use "delete all stored revisions" on the device's config page and let the next poll re-capture.

Full details in the changelog.