v0.10.0
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, fromstat/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 shaperemains 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--obfuscategives 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, andhtml, which embeds the same marked SVG) — the one
distinction--reportsurfaced in text but the map still drew like any
other link. Every other provenance value turned out to already have a
channel: node role throughKind, 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 asdata-*
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.
fetchused 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
withUNIFI_VERIFY_TLS=false. Both now stream through the same capped-read
guard, which moved out ofassets.pyinto 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 laterrenderread 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 installworks straight from GitHub, two ways, with no PyPI account.
pip install git+https://github.com/gitkodak/unifi-map.git@v0.10.0builds
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 nogiteither.
Neither is a PyPI decision: no account, no owned name, nothing that cannot
be withdrawn. Seedocs/install-from-github.md.The man page now reaches a wheel install too. Shipped as installed data
atshare/man/man1/unifi-map.1, soman unifi-mapresolves with no
MANPATHconfiguration once the virtual environment is onPATH— both
macOS's and GNU man-db'smansearch a venv'sshare/manautomatically.
Previously the committedunifi-map.1only worked asman ./unifi-map.1
from a checkout.
Changed
-
A controller is only ever contacted over HTTPS.
UNIFI_HOSTcould
previously carry an explicithttp://prefix, which was honoured, so a
mistyped credential file sent the API key over the wire in clear with nothing
said about it. Anhttp://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, andUNIFI_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 throughPath.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.