Skip to content

Releases: gitkodak/unifi-map

v0.11.0

Choose a tag to compare

@gitkodak gitkodak released this 11 Aug 05:28

Changed

  • Future releases are AGPL-3.0-only. This protects against closed distributed forks and modified versions operated solely as network services: operators must offer corresponding source to users who interact with a modified version over a network.
  • Earlier MIT and GPL-3.0-only releases remain under the licenses with which they were published. The bundled Panzoom library remains under its own MIT license.

In memoriam: Ian Murdock. You will always be remembered as long as I'm alive, my friend.

v0.10.2

Choose a tag to compare

@gitkodak gitkodak released this 11 Aug 05:13

Changed

  • The GPL-3.0-only commitment is now explicit in the README, contribution terms, project metadata, artwork documentation, and project site.
  • New distributed modified versions must remain GPLv3 source available. Panzoom's third-party MIT attribution and the historical MIT status of older releases remain accurately identified.

In memoriam: Ian Murdock. You will always be remembered as long as I'm alive, my friend.

v0.10.0

Choose a tag to compare

@gitkodak gitkodak released this 11 Aug 02:52

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.

v0.9.0

Choose a tag to compare

@gitkodak gitkodak released this 03 Aug 23:05

Added

  • An svg extra, for your own SVG override artwork. pip install 'unifi-map[svg]' rasterises a supplied SVG to a cached PNG as it is read, so
    it reaches every output format.

    Without it, Graphviz loads SVG artwork only for its own svg driver: png
    and pdf go through cairo, which has no SVG loader, so the icon is dropped
    from both. It also insists on an XML declaration and reports a file that
    plainly exists as missing when there is none. Rasterising sidesteps both, so
    a file exported by a drawing tool works untouched.

    Optional on purpose, and converting the file to PNG yourself does the
    same job with no dependency at all. Both routes are documented; the tool
    warns and names the file when an SVG is about to go missing from a format.
    Fetched artwork is unaffected: it is already PNG.

  • A warning when an SVG override will not reach png or pdf, naming the
    icons and the formats that will lack them. Graphviz's own message is No loadimage plugin for "svg:cairo", which names neither the file nor a way
    forward.

  • A warning when --theme dark is combined with drawio. draw.io
    re-themes on load and will render a dark-authored file light. See Known
    limitations
    below.

  • make build, producing a wheel and an sdist in dist/. Installing the
    wheel into a clean environment gives you a working unifi-map without a
    checkout, which is useful for putting it on a machine that should not carry
    the source.

    It needed almost no new machinery: the entry point and the build backend were
    already in pyproject.toml. What was missing was a documented way to invoke
    them, and dist/ and build/ in .gitignore so the artifacts cannot be
    swept into a commit.

    This is not a published package and does not promise one. There is no
    pip install unifi-map from PyPI, and whether there ever should be is
    deliberately still an open question, recorded in TODO.md. Building an
    artifact and publishing one are separate decisions, and only the second is a
    commitment that cannot be withdrawn.

  • UNIFI_CACHE_DIR, UNIFI_ASSET_CACHE and UNIFI_OUT_DIR, so the
    directories can be set once instead of passed on every command. A flag still
    wins over the variable, and the variable over the default. They can go in the
    credential file as well as the environment, which is the natural place, since
    that file is already the thing kept outside the project.

    The cache one is the point. A snapshot is a complete inventory of a network,
    the default puts it in the working directory, and for anyone working on this
    tool that directory is a git checkout. A cache.bak copy made before a risky
    fetch is not covered by a .gitignore entry for cache/; one sat untracked
    in this repository, one git add -A from being published. The ignore rule was
    widened in 0.8.0, and this removes the question rather than guarding it.

Removed

  • The UDM_* environment variable names. UDM_HOST, UDM_API_KEY,
    UDM_SITE and UDM_VERIFY_TLS are no longer read. They existed only because
    that is what the author had called things before this tool did, and they have
    warned since 0.7.0.

    If you still use them, rename them to the UNIFI_* spellings. Nothing
    subtle happens if you do not: the tool reports the missing variable by name
    and exits, exactly as on a fresh install. UDM_USER and UDM_PASS were
    already dead, unread since password authentication was removed, and are worth
    deleting from any credential file that still carries them.

    One deliberate asymmetry: layout.py still strips UDM_API_KEY from the
    environment Graphviz runs with. We stopped reading it, which does nothing
    about somebody who still exports one, and an unread variable holding a real
    key is exactly as worth withholding from a child process as a read one.

Changed

  • Graphviz's warnings are no longer discarded. Graphviz warns on stderr and
    still exits 0, and that output was thrown away on every successful run, so
    every warning it has ever emitted was invisible. That is how an icon could
    vanish from a PNG in silence. Surfaced whole rather than filtered: deciding
    which of its messages matter is how the last one stayed hidden.

  • Artwork resolution and output writing moved out of cli.py, into
    artwork.py and output.py. No behaviour changes. The reason is layering
    rather than length: neither is a command-line concern, and the tell was a
    rendering test having to import a private function from unifi_map.cli to
    exercise the renderer. cli.py keeps argument parsing, credential resolution,
    logging setup and the cmd_* functions that sequence a run, and drops from
    1367 lines to 1027.

    Only relevant to you if you import from unifi_map.cli directly, which is not
    a supported interface and which nothing is known to do.

Fixed

  • A repeated -f is refused instead of silently honouring the last one.
    -f takes several values, so -f svg -f png overwrote rather than appended
    and wrote png only, with nothing said. That reads as a format that failed to
    render. Pass them together: -f svg pdf png.

    Refused rather than made to append, because an error states what happened
    whereas appending would quietly change what an existing invocation produces.
    If you have a script using the repeated form, it will now stop with an
    error naming the fix.

  • draw.io connection lines no longer run through unrelated devices. The
    edges carried only their two endpoints, so draw.io routed them with its own
    router and drew a long run straight through whatever the layout had placed in
    between. Graphviz had already computed a route and it was being discarded;
    those waypoints are now written into the file. Safe to pass through unchanged
    because both layouts use ortho or polyline splines, never a bezier, so the
    reported points are corners rather than control points.

  • draw.io node captions no longer land on the node below. The label was
    positioned outside the cell, while Graphviz had sized that cell to hold the
    artwork and the text. The box carried dead space and the caption fell onto
    whatever was underneath, so on a dense column every icon wore its neighbour's
    caption. The label now renders inside the box the layout was computed for.

  • An SVG with only a viewBox is accepted. Explicit width and height
    were required, and most drawing tools export a viewBox instead. Graphviz
    renders those perfectly well and preserves the ratio, checked by rendering
    both through dot rather than by reading a spec. Only the ratio is used
    downstream. Explicit dimensions still win where a file has both.

  • A refused icon says which rule it broke. "Could not read artwork at
    <path>" on a file the reader can plainly open is a shrug rather than an
    error, and SVG has requirements that are not guessable from the outside.

  • The README implied you could run unifi-map before installing it. The
    quick-look section ran make demo and then unifi-map all, as though the
    first had prepared an environment for the second. It had not: make demo
    calls the venv's copy by full path and puts nothing on your PATH.

  • Nothing anywhere told you to activate the virtual environment. Install
    ended at .venv/bin/pip install -e ., which puts the command at
    .venv/bin/unifi-map and nowhere else, while the README and every page under
    docs/ invoked a bare unifi-map. Anyone following the instructions
    literally got command not found on their first real command. Install now
    ends with source .venv/bin/activate and says why, which makes those examples
    correct rather than editing each of them.

  • all now says what it means. It is fetch then render, both stages,
    and reads to a newcomer as "all output formats". It writes the same default
    two files any render would. The help text says so and docs/usage.md has
    the incantation for the other five.

  • Documentation said Lucid's .drawio import had not been tried. It has, and
    it does not work.
    Lucid reads one cell of the file and stops, a different
    cell each time. Neither stripping the embedded artwork nor writing the payload
    in draw.io's own compressed form changes it, so it is not a size or an
    encoding problem. docs/output.md now says so and points at the workarounds,
    which are to export from draw.io or to import the svg or pdf output.
    draw.io itself remains confirmed working. Nothing about the generated file is
    being reshaped to suit a second tool's parser.

Known limitations

  • --theme dark does not survive into a .drawio file. draw.io re-themes
    a diagram on load, inverting it to contrast with its own appearance setting,
    because its dark mode assumes diagrams are authored light. A file authored
    dark is inverted a second time and displays light; a file authored light is
    correct in both of draw.io's modes.

    Nothing is corrupted when this happens. The inversion is holistic, so cells,
    text and artwork flip together and the file stays coherent; it simply reads as
    the theme you did not ask for.

    For now the tool warns and renders what you asked for. Use --theme light
    for the .drawio, or set the appearance in draw.io explicitly rather than
    leaving it on Automatic. docs/output.md covers both.

    When this is fixed, the behaviour will be:

    • It will always warn when drawio is among the requested formats and the
      theme is dark, because the interaction is worth knowing about either way.
    • With other formats alongside, the .drawio will be authored light,
      so it displays dark like everything else in the run.
    • With only drawio requested (--theme dark -f drawio), it will be
      authored dark as asked, with a warning that draw.io may not display it
      the way you expect and a pointer to the documentation. ...
Read more

v0.8.0

Choose a tag to compare

@gitkodak gitkodak released this 03 Aug 03:27

If you keep an overrides file, read this before upgrading. A file
containing wireless = "false", hide = "false", a fractional port, or a
misspelled key used to render; it now stops the run with an error naming the
problem. Those files were never doing what they said, which is why this changed,
but the failure is new and it is at the point of use. The fix in every case is
in the error message.

Changed

  • The documentation is split into docs/, and links into the README have
    moved.
    It had reached 1233 lines, which is past the point anybody reads,
    and is now 220: what the tool is, what it produces, how to install it, and
    how to see a map without touching your network. Everything else is a page of
    its own, indexed from the README.

    If you linked to a README anchor, it has moved. Every #section that is
    now a page is a different address:

    Was Now
    README.md#usage, #reading-the-diagram, #flag-reference docs/usage.md
    README.md#credentials, #unifi_api_key docs/credentials.md
    README.md#mapping-from-a-support-file docs/support-files.md
    README.md#json-for-programs, #mermaid-for-documentation docs/output.md
    README.md#sharing-a-map---obfuscate docs/sharing.md
    README.md#artwork-licensing-and-attribution docs/artwork.md
    README.md#how-it-works, #caveats docs/verification.md
    README.md#manual-overrides docs/overrides.md, which already existed

    Pages are organised by why somebody opens them rather than by what they are
    about, and each was then checked against what its own opening sentence
    promises. That found the artwork page describing where pictures come from with
    the answer two files away, the output page covering two formats of seven, and
    three pages repeating their own title as their first section. All were the
    same artefact: splitting on top-level headings moves text correctly and lands
    it by accident.

    The README's ## Manual overrides and ## Also planned sections are gone
    rather than moved, because docs/overrides.md and TODO.md already held the
    same material and the README was carrying second copies to drift against.

    The guards were widened before the split rather than after. Every link used to
    be a same-file #anchor; most are now docs/artwork.md#something, which can
    fail two ways a browser renders happily: the file may be missing, or present
    without the heading. The link check now resolves cross-file targets, the flag
    and command checks read every document rather than the README alone, and the
    generated flag reference lives in docs/usage.md. It caught fourteen links
    the split broke silently.

  • The documentation says which UniFi applications this has been run against,
    in the introduction rather than buried: Network for everything, and one
    Protect endpoint read purely to tell a camera from an Access reader. Devices
    from Access, Talk or a UNAS already draw, since they are clients or UniFi
    hardware like anything else, so the gap is narrower than "unsupported": what
    is missing is the second source that would let an ambiguous match resolve.
    CONTRIBUTING.md asks for those environments alongside the other things
    nobody here has.

  • Two released sections described one change twice. 0.7.0 had two entries for
    unifi-map shape, written days apart; 0.6.0 had two for RELEASING.md that
    contradicted each other, one saying a fix had been claimed and never made and
    the other making that same claim. Both are merged. A test now fails when a
    release describes the same subject twice, with genuinely separate changes to
    one thing listed as exceptions rather than the rule loosened.

  • The artwork page lists user-supplied artwork among its sources. An icon in
    an overrides file is where a picture comes from when none of Ubiquiti's
    catalogues has one, and it is the only source that works under --offline.

  • Both generated references were wrong in the same two ways, and neither
    staleness check could have noticed.
    docs/usage.md and the man page each
    printed a synopsis reading {fetch,render,all}, two commands behind, and the
    man page listed only those three under COMMANDS. Neither said that
    unifi-map overrides requires a check argument, because the introspection
    walked option_strings and a positional has none. All three lists are now
    derived from the parser.

    The two existing checks regenerate the file and fail on a diff, which catches
    an author who forgot to run make docs and cannot catch a generator holding a
    hardcoded list: it produces the same wrong file every time and compares equal
    to itself forever. A separate test now asserts that both documents name every
    subcommand and every positional. A document claiming it cannot drift from
    --help is worse than a hand-written one when it does, so the claim is tested
    rather than trusted.

  • overrides check now validates against the same topology render will
    build.
    It passed include_offline=True unconditionally, which is more
    permissive than the default render, so a selector naming a device the
    controller merely remembers passed the check and then failed the render it had
    just been checked for. That is the one outcome the command exists to prevent.
    --show-offline is now shared between the two subcommands rather than
    belonging to render alone.

  • An override that displaces a link the controller reported now says so.
    [[link]] and [[hosted]] both detach a node from its current parent before
    attaching the stated one, which is necessary and, for [[hosted]], the entire
    point: reparenting a VM under its hypervisor displaces a real observation by
    design. But the code assumed the displaced edge was always the "uplink not
    reported" placeholder, and nothing enforced that, so contradicting the
    controller was silent. It warns now, naming both ends. Tidying the placeholder
    stays quiet, since warning on the documented case is how a warning stops being
    read.

  • The runtime hint about unplaceable clients pointed at "Manual overrides in the
    README", a section the documentation split moved to docs/overrides.md.

  • The man page omitted exit code 3 (Graphviz not installed), and its
    description listed neither Mermaid nor JSON among the output formats.

  • Several documented behaviours did not match the code, all found by an external
    review reading the split documentation against the source. Each is a fix to
    the document except where noted:

    • docs/output.md said nothing is ever written to stdout; unifi-map shape
      writes its report there, which is what makes it pipeable.
    • It listed dot among the formats needing Graphviz. dot, mermaid and
      json are all written directly and work without it; only svg, pdf and
      png need it. Verified by rendering with an empty PATH.
    • Its JSON example named version 0.6.0 and omitted title and networks,
      the latter promised two paragraphs above it.
    • Its Mermaid example claimed to be the shipped demo while showing a direction
      and header no documented command produced. The example is now byte-identical
      to --layout tree output, with the one edit stated.
    • docs/overrides.md called [[device]].kind required; it defaults to
      unknown. It also never documented what note does, which differs per
      block: an edge label for [[link]] and [[hosted]], nothing at all for
      [[device]] and [[node]]. And it did not mention overrides check.
    • docs/usage.md said fetch downloads the icon font only when missing; it
      replaces any cached copy every time.
    • docs/support-files.md said omitting --fetch-fingerprints leaves clients
      without product artwork. The flag governs the download, not the lookup: a
      database already cached is read either way.
    • docs/verification.md said "all five output formats" when there are seven,
      and ended with a BEGIN GENERATED FLAGS marker that had no END and no
      content, left behind when the split moved the flag reference to
      docs/usage.md.
    • .env.example pointed at a Credentials section of the README that is now
      docs/credentials.md.
    • The README said Access readers, Talk phones and a UNAS "all still appear".
      docs/verification.md correctly calls the UNAS case inference, none of the
      three having been seen here, and the README now matches it.
  • Support-file operational guidance was on the artwork page. Site selection,
    the four archive limits, the compression-bomb defence and the slow-walk
    warning all sat inside a section about client icons, while
    docs/support-files.md covered none of them. Moved, and site selection and
    limits now come before the artwork asides, since a multi-site archive stops
    the run before artwork is reached.

  • CLAUDE.md proposed the JSON export, overrides check and the Mermaid export
    as future work, all three having shipped, and still described removing the
    sane layout alias in 0.6.0, which 0.6.0 did. The entries are rewritten to
    keep the constraints that still bind rather than deleted. The issue template
    for feature requests pointed contributors at that file's planned-work section,
    naming two shipped features; it points at TODO.md, which exists precisely
    because CLAUDE.md is written for agents.

  • Two defects in the override-displacement warning added earlier in this same
    unreleased cycle
    , both found by an external review before either reached a
    release:

    • It leaked under --obfuscate. Overrides are applied before obfuscation, so
      an ordinary obfuscated render logged the node's real label, its old parent's
      real label and the selector, contradicting the promise that log output is
      scrubbed too. The scrubbed diagram was the whole point, and a terminal
      beside it naming the nodes defeats it. Displacements are now carried out on
      ...
Read more

v0.7.2

Choose a tag to compare

@gitkodak gitkodak released this 03 Aug 03:27

Fixed

  • unifi-map shape says when its artwork counts are measuring an empty cache
    rather than the network. Resolved against a cold cache it reported 0 of 19
    for any network at all, and that section exists precisely so somebody else's
    numbers can tell us how well the fingerprint joins work. The same snapshot
    gives 0 of 19 or 19 of 19 depending only on whether artwork has ever been
    fetched, so reading it as a property of their network was a wrong conclusion
    waiting to be drawn.
  • SECURITY.md covers unifi-map shape. A command whose entire purpose is
    producing something to hand to a stranger was absent from the document about
    what this tool discloses, which is the one place somebody assessing it would
    look.

v0.7.1

Choose a tag to compare

@gitkodak gitkodak released this 03 Aug 03:27

Fixed

  • The README's ## Output table lists mermaid and json. It had not, since
    both were added, so the first place a reader looks to find out what the tool
    can produce named five formats out of seven. A test now checks the table
    against ALL_FORMATS: the existing guard covers flag names, not the values a
    flag accepts, which is how the table fell behind twice without failing.

v0.7.0

Choose a tag to compare

@gitkodak gitkodak released this 03 Aug 03:27

Fixed

  • SOURCE_DATE_EPOCH fixes the time stamped into the title block, following the
    reproducible-builds convention. Without it the committed demo screenshots
    changed on every regeneration, by exactly the thirty pixels of their
    timestamp, which made a real rendering change indistinguishable from the clock
    ticking. make demo-images sets it; a real map still stamps the time it was
    drawn.

  • The first screenshot's caption said most clients "fall back to plain shapes".
    They have not for some time: eleven of them draw the console's own generic
    glyphs, which is what the image shows. The caption now describes the image,
    and says that those glyphs need an icon font only a controller serves, so a
    fresh clone genuinely does see shapes there. The two statements were both
    true of different machines, which is the confusing kind of wrong.

Added

  • -f json writes the normalised topology: nodes, edges, networks and counts,
    rather than the controller's payloads. The model is the stable thing here and
    UniFi's schemas are not, so this is what to build an inventory check or an
    integration against, and it is a far smaller disclosure than a snapshot. It
    honours --obfuscate, overrides and --per-network like the diagram does.
    Carries a schema number: fields may be added and will not be removed.

  • -f mermaid writes a .mmd that GitHub, GitLab and most wikis draw in
    place. It reaches the one destination the other formats cannot: a page that
    renders the diagram itself, with no file to open and no colour scheme to
    guess. Artwork is lost, necessarily, since Mermaid draws boxes and text; node
    kind is carried by shape and link meaning by line style, so nothing depends on
    colour. The README embeds a live one of the demo.

  • unifi-map overrides check applies an overrides file against the cached
    snapshot and reports, without rendering anything. Overrides fail loudly by
    design, so the only way to find a stale selector used to be producing a whole
    map. Exits non-zero on the first selector matching nothing or several things,
    which makes it usable in a hook or in CI. A missing file is an error rather
    than a pass.

  • unifi-map shape, which prints a short plain-text description of the shape
    of a network: counts, fan-out, which field names the controller returns, and
    versions. Meant for a bug report or for the features that are stuck waiting on
    a network nobody here has.

    Built from an allowlist rather than by redacting: every line is a counted
    integer, a boolean, or a field name from a list written in advance. A filter
    that strips identifying values can be incomplete, and UniFi's own does exactly
    that and was observed leaving unredacted tokens in a real support file. A list
    that only ever adds cannot leak by omission.

    It asks before producing anything, printing what it does and does not collect,
    and --yes skips that once read. A non-interactive run without --yes
    refuses rather than assuming: a cron job has nobody to consent on behalf of.

    It reports counts and fan-out, topology depth, artwork resolution rates, the
    Graphviz version and the offline device count. The artwork numbers were
    already computed and thrown away as log lines, and they measure the most
    fragile join in the tool; depth because fan-out alone does not distinguish a
    flat network from a daisy chain; the Graphviz version because layout differs
    between them.

    Pointed at an archive with --support-file it reads that directly rather
    than the cache, and adds how much there was to walk, how many entries, and
    how many sites it holds. Those are the numbers behind the support-file limits
    (set from a single archive) and the untested multi-site handling. Sites are
    counted, never named: the keys carrying those names are user-chosen.

    CONTRIBUTING.md asked people to gather this sort of thing by hand and now
    points at the command instead.

    Named shape rather than report because a diagnostic --report is planned
    and does the opposite job: it describes your map for your benefit and may
    freely name your devices, since it never leaves your terminal. This one
    describes a network for somebody else and is constrained so it can be shared.
    One name for both would have guaranteed the constraint eventually leaked.

    Two tests hold the promise up. One renders a snapshot built entirely of
    identifying values and searches the output for every one. The other is
    stronger and matches the design: it asserts the report's whole vocabulary is
    closed, so a value arriving by a route nobody predicted fails even though no
    test knew to look for it.

  • A small rendering flourish, off unless an environment variable asks for it.
    Not in --help, not in the README, and not described further here. It is
    emitted straight into the DOT rather than added to the topology, so it cannot
    reach counts, filtering, obfuscation or the report; a test holds that. Harmless
    and cosmetic, and findable by anyone who already knows to look.

v0.6.0

Choose a tag to compare

@gitkodak gitkodak released this 03 Aug 03:27

Removed

  • --layout sane, and the make sane target. Renamed to tree in 0.5.0, where
    it was deprecated with this version named in the warning, the code, the tests
    and the changelog. --layout sane is now an argparse error listing the valid
    choices, and Style(layout="sane") raises. One release of overlap was the
    whole point of promising a version rather than leaving it open-ended, which is
    still the right call for the UDM_* environment names.

Added

  • TODO.md, the planned work in one contributor-facing place, since the
    alternatives were a context file written for AI agents and a Jira instance
    needing an account. Two tests keep it honest: it may not plan work for a
    released version, and its commitment section must name the same version the
    deprecation warning tells users.
  • --transparent draws no canvas, so a map can be dropped onto a page that
    already has a background. Covers SVG, PDF, PNG and draw.io. The theme still
    applies and still matters: with the default icon set, labels have no card
    behind them, so on a transparent canvas they land straight on the destination
    page and a light map is near-invisible on a dark one.
  • Every demo screenshot is committed in both themes rather than only dark, so
    the documentation shows what the default actually produces instead of
    describing it.

Fixed

  • RELEASING.md describes the process as it now is. Four things in it were
    wrong. It said to rename ## Unreleased away at release, which would delete
    the section CONTRIBUTING.md tells contributors to use. It never mentioned
    make docs, which is mandatory now that the man page carries the version. It
    said CI could not be checked from here, which stopped being true once gh was
    installed; the instruction now runs gh run watch and says to read the
    per-job output, because Dependency advisories is continue-on-error and
    reports success having failed inside. And it counted how many times things had
    happened, where two of the counts were wrong and none told a reader anything
    the sentence did not.
  • CLAUDE.md said the sane layout alias goes in 0.5.0. It goes in 0.6.0,
    which the code, the tests and the changelog all said; that one heading was
    missed when the promise moved.

Changed

  • --site's help text no longer reads as though multi-site support files are
    refused outright. They are refused only when you do not say which site you
    want, which the sentence did say, twenty words earlier, in a table cell. It
    now states the requirement rather than the refusal. The README prose leads
    with the same.
  • Dependabot auto-merge holds anything whose update type it cannot identify,
    rather than merging it. The condition asked "is this not a major bump", which
    treats an empty or unrecognised value as safe, so anything leaving
    update-type unpopulated made the step an unconditional merge. Not
    hypothetical: fetch-metadata resolved update-type to null for Python pull
    requests until v3.1.0, and Python dependencies are what this repository
    tracks. It now asks "is this a minor or patch bump", so an unknown value
    merges nothing.
  • A test asserts the man page header carries a real date. The date comes from
    the changelog entry for the current version, so bumping __version__ before
    dating that section produced an empty one, and the regenerate-and-compare
    check could not see it because both sides were generated the same wrong way.

v0.5.0

Choose a tag to compare

@gitkodak gitkodak released this 03 Aug 03:27

Deprecated

  • --layout sane is renamed to --layout tree, and sane will be removed in
    0.6.0
    . It still works and still selects the same layout, but it is hidden
    from --help and warns once, naming the replacement and the version.

    The old name implied the other layout was not sane, and borrowed a clinical
    word as a judgement. tree describes what the layout is: top down, leaf
    staggered, with port numbers on the links.

    A version is promised here, unlike the open-ended UDM_* deprecation, because
    this is one flag value that anyone using it can change in seconds, and an
    indefinite alias would keep the word in --help indefinitely. The target is
    0.6.0 rather than 0.5.0 because 0.5.0 is the release that introduces tree;
    removing the old name in the same version it is deprecated would leave nobody
    a release to migrate in.

Fixed

  • A support file holding more than one site is refused until --site says
    which, instead of mapping whichever site had the most devices and warning.
    The old behaviour was the only place this tool guessed: an ambiguous override
    selector is a loud error, an ambiguous product name resolves to nothing, and
    an unreported uplink gets a placeholder rather than a plausible parent. It was
    also the worst place to guess, because the result is a complete and entirely
    ordinary looking map, and nothing about the diagram says it is the wrong
    network. A single-site archive still needs no flag.
  • Every directory this tool creates is restricted, not only the last one.
    --out-dir out/private/maps created three directories and locked down one,
    leaving the other two at the umask. Output filenames come from network names,
    so a listable parent disclosed the network layout even though the files
    themselves are 0600. An existing directory is still left exactly as it is.
  • Overrides that make a loop are refused, naming the loop. Nothing crashed:
    Graphviz draws a cycle without complaint, since DOT is a digraph. It is
    refused because a switch cannot be its own uplink, so such a map asserts
    hardware that cannot exist while looking as authoritative as any other.

Changed

  • A man page, unifi-map.1, generated from the argument parser and committed
    so it works from a clone with man ./unifi-map.1. make docs regenerates it
    and make check fails when it is stale, the same guard the README flag
    reference has. It carries the sections a parser cannot supply: ENVIRONMENT,
    FILES, EXAMPLES, exit status, and the warning about support files.
  • Atomic writes live in one module, fsio.py. Three copies had grown apart:
    two called fsync before the rename and one did not, and only two set the
    file mode before putting it in place. None of the differences were intended.
  • _fetch returns a small Fetched object rather than a requests.Response
    with two private attributes assigned by hand. The body is streamed through a
    size cap rather than read by requests, which is why the response had to be
    doctored; callers only ever used three fields.
  • The example unmanaged switch is described as "unmanaged" rather than "dumb",
    in examples/overrides.toml and the README. "Unmanaged" is also the accurate
    term: it names the absent management plane, which is why such a device has to
    be declared by hand in the first place.
  • make sane is now make tree, and docs/images/example-sane-dark.png is now
    example-tree-dark.png. The old make target still works and says it is going.