Skip to content

v0.10.0

Choose a tag to compare

@gitkodak gitkodak released this 11 Aug 02:52
· 34 commits to main since this release

Added

  • --report, saying how much of the map to trust. A map drawn from a
    complete fetch and one drawn from a thin one look equally authoritative. This
    prints where every part of it came from: how many nodes and links came from
    stat/device, from stat/sta, from the controller's own topology graph, from
    an overrides file, or from nowhere at all. Then, where something needs
    attention, it names the devices rather than only counting them: clients that
    could not be placed, clients with no address from any source, networks a
    client claims to be on that the controller does not list, and artwork matches
    refused as ambiguous.

    It also says what the snapshot carried and what each missing piece costs. An
    optional endpoint that fails is logged once at fetch time and never mentioned
    again, so a snapshot cached before an app was installed renders thinner every
    time with nothing saying why.

    Underneath it, every node and link now records which endpoint or fallback
    produced it, at the point that decision is made rather than reconstructed
    afterwards. The tool has always refused to guess; until now that refusal was
    invisible, because a client the controller reported directly and one recovered
    from a second endpoint were drawn identically and with equal apparent
    authority. The diagram itself gains the same distinction below.

    It is not safe to share, and says so at the top. It names your devices,
    addresses and networks by design. unifi-map shape remains the one built from
    an allowlist for pasting into a bug report. A device is named only where
    something is wrong with it, so a healthy map produces a report with no names
    in it and a short report is a good sign.

    It describes the map as drawn, running after overrides and after
    --obfuscate, so pairing it with --obfuscate gives a report carrying the
    same placeholders as the shared diagram.

  • A client placed via the controller's own topology graph, rather than its
    own reported uplink, is now marked on the diagram itself.
    That link gets a
    small hollow-circle arrowhead at the child end, in every backend that draws
    the network as a picture rather than as text or data (svg, pdf, png,
    drawio, and html, which embeds the same marked SVG) — the one
    distinction --report surfaced in text but the map still drew like any
    other link. Every other provenance value turned out to already have a
    channel: node role through Kind, an override through the existing dotted
    style, offline through dashed. Composes with the wireless dashed style
    rather than competing with it, and a legend row explains the marker when the
    map uses one.

  • An interactive HTML viewer, -f html. A single self-contained file:
    scroll or drag to pan, pinch or Ctrl+scroll to zoom, search to dim
    everything that doesn't match, click a client to trace its path back to the
    gateway, and click a switch or AP to collapse the clients hanging off it.
    That last one is the actual point — a switch with thirty clients is
    unreadable in every static format, which is the problem this exists to
    solve.

    Pan and zoom is a vendored copy of Panzoom
    (MIT, zero dependencies of its own), not hand-rolled and not pulled from a
    CDN: a small permissively-licensed file checked into the repo is a
    different kind of "vendoring" than the rule against committing Ubiquiti's
    artwork, which is about somebody else's copyright rather than about
    third-party code existing at all. Node and edge correlation between the SVG
    and the topology is computed in Python and stamped onto the SVG as data-*
    attributes, so the JavaScript never has to reverse-engineer anything. The
    topology payload is embedded base64-encoded rather than as a JSON literal,
    because a label can come from a controller or a support file, both hostile
    input by this project's own rule, and a literal </script> inside one
    would end the block early no matter how the JSON around it was escaped.

  • Controller responses are size-capped, like the CDN artwork always was.
    fetch used to read each response whole with no ceiling, while every
    download from Ubiquiti's CDN stops at a limit. That was backwards from how it
    looked: the defended path is the untrusted CDN and the undefended one is the
    controller, which is the endpoint people are told it is ordinary to reach
    with UNIFI_VERIFY_TLS=false. Both now stream through the same capped-read
    guard, which moved out of assets.py into a shared module so the two cannot
    drift apart again. A response past 64 MiB is refused with a message; nothing
    a real network returns comes near that.

  • A cached snapshot is now an atomic generation. Each payload used to be
    written as its own file with stale ones deleted afterwards, so a fetch cut
    off mid-write, or two fetches running at once, could leave a mixture of old
    and new that a later render read without complaint: a map built from two
    moments, indistinguishable from one built from one. Each fetch now writes a
    complete set into its own directory and switches a pointer to it as the last
    step, so a reader only ever sees one complete generation. An interrupted
    fetch leaves the previous snapshot intact and readable, and an old flat cache
    keeps working and is migrated by the next fetch.

  • pip install works straight from GitHub, two ways, with no PyPI account.
    pip install git+https://github.com/gitkodak/unifi-map.git@v0.10.0 builds
    from a tag at install time and needed no changes at all. From this version
    on, a release's wheel and sdist are also attached to its GitHub Release, so
    pip install <wheel-URL> installs with no build step and no git either.
    Neither is a PyPI decision: no account, no owned name, nothing that cannot
    be withdrawn. See docs/install-from-github.md.

    The man page now reaches a wheel install too. Shipped as installed data
    at share/man/man1/unifi-map.1, so man unifi-map resolves with no
    MANPATH configuration once the virtual environment is on PATH — both
    macOS's and GNU man-db's man search a venv's share/man automatically.
    Previously the committed unifi-map.1 only worked as man ./unifi-map.1
    from a checkout.

Changed

  • A controller is only ever contacted over HTTPS. UNIFI_HOST could
    previously carry an explicit http:// prefix, which was honoured, so a
    mistyped credential file sent the API key over the wire in clear with nothing
    said about it. An http:// prefix is now upgraded to HTTPS rather than
    obeyed, and a host with no scheme at all still gets HTTPS as before.

    No opt-out is offered, because the case people reach for plaintext to solve
    is a bare IP address with a self-signed certificate, and UNIFI_VERIFY_TLS
    already answers that without giving up encryption.

Fixed

  • The permission repair for pre-0.9.0 SVG caches no longer follows symlinks.
    It restores private modes to rasters an earlier build left world-readable, and
    did so through Path.chmod(), which follows links: a symlink planted at
    user-svg/ or at one of its cached PNGs redirected the change onto whatever
    it pointed at. Nothing was disclosed, since access was removed rather than
    granted, but an unrelated path could lose group or world access, and in a
    cache directory writable by somebody else that is a local denial-of-service
    primitive.

    Now done through a descriptor opened O_NOFOLLOW, so a link fails the open
    outright and the mode is applied to the thing that was inspected rather than
    to whatever the name resolves to a moment later.