Tools
Free tools for reading a building's network, its paperwork, and a release before it ships
Fourteen small programs, given away because they cost nothing to give.
Three of them read a network: one sweeps it for BACnet devices and dumps a device's
points, one watches an MQTT broker and prints its topic tree, one runs a LoRaWAN
vendor's payload decoder and reports what a station would get back from it. Nine read
what Niagara itself will do with a setting, a point file, a signed module, a
gateway's object type, an alarm's route, a recipient whose far end is down or a
schedule's next change, before any of it reaches a station. One
more is for the spreadsheet a business runs on: it opens an .xlsx as the
zip of XML it is and reports what the sheet encodes. And one is for a
website on its way to production: it reads the same list of paths on two hosts and
reports every field that moved, so a release cannot change what a client's rankings
rest on without somebody having written it down. All
fourteen are read-only, all fourteen are one file, and none of them needs anything
installed beyond the runtime already on the machine.
- Read-only
- Python 3 · Node 18
- No dependencies
- One file
- MIT licensed
- No account
Why these exist
The first hour on an unfamiliar site is always the same hour. What is on this network. What is it called. Is that value real. The tools that answer those questions well are either a vendor's, or licensed to a laptop that is not yours, or excellent and free and installed on a machine you are not allowed to install anything on.
So these are deliberately small. One file each, no package to install, nothing to
register, and no ability to change anything in the building. Copy the file onto
whatever laptop you have, run it, read the output, delete it. Thirteen of them are
Python 3 and standard library only; decoder-check is Node 18 or
newer and uses nothing outside the Node standard library either.
Another nine answer a different first hour. Not what is on the network, but what this framework will quietly do with what you are about to give it — the register a Modbus point really asks for, the columns an EDE import replaces with a default and does not mention, which verification mode refuses the module you signed, whether a write goes through a priority array or straight to present-value, which duplicate alarm the queue discards and reports as delivered, what a recipient does when the far end is down, and how far ahead a schedule can see. Each reads the framework's own code out of a Niagara installation rather than repeating a manual, so the answer is the one your version will actually give.
And one answers a question nobody asks about a control
system, because the thing it reads is not one. The spreadsheet a department runs on
is undocumented software with no version control, no tests, and nobody left who
knows why a cell holds the number it holds. workbook-scan opens it as
the zip of XML it is and reports what the file encodes, which is the only honest
first step before anybody offers to replace it.
And one is not about a building at all. Asked what gets an
outsourced developer dropped, an agency owner who buys development that way
answered that it is not bad code, it is silent changes — a URL pattern that
moved, a dropped canonical, an h1 turned into a styled div, a
noindex that never came off, an analytics tag quietly not firing. The
site works perfectly afterwards and the rankings slide three weeks later.
release-diff is the instrument that same person named: read the same
paths on both sides of a release, report everything that moved, and fail on
anything that moved and was not declared. A launch checklist cannot do it, because
a checklist only catches what somebody already thought to write down.
Thirteen of the fourteen are a file you download and run.
ede-check is also hosted here: the
same checks, running in your browser, on an EDE file that is never uploaded. It is
the only page on this site that executes something for you, and the reason it can
be offered at all is that there is nowhere for the file to go.
They are not replacements for the tools you already use. They are narrower than all of them on purpose: a sweep and a dump, a tap and a tree. Where a general-purpose explorer is better, use the explorer — each page says so.
How they are licensed, and where the source is
Every file here is MIT
(LICENSE.txt), and each tool's repository
carries the same licence as its own LICENSE file. In practice that
means you can read it, change it, keep the changed copy, put it on a site laptop
that is not yours, or ship it inside something you sell. The one condition is that
the copyright line and the permission notice travel with the copy, and since
2026-10-04 they do on their own: the licence block is inside each script, as an
SPDX-License-Identifier line and the copyright, so a single file
downloaded on its own still says what it is. The fuller text is in
LICENSE.txt and in every repository.
This page claimed that for weeks before it was true - no served script carried a
notice until the day it was found - so check.py now holds the
sentence to the served bytes in both directions.
Each one also has a public repository, holding the same file plus everything
needed to re-run the checks its page relies on — the fixtures and suites the
claims rest on, rather than a description of them:
bacnet-sweep · mqtt-tap · decoder-check · modbus-address-scan · ede-check · module-sign-scan · bacnet-priority-scan · alarm-route-scan · alarm-recipient-scan · schedule-scan · workbook-scan · poll-scheduler-scan · tuning-stale-scan · release-diff.
The download here and the file in the repository are the same bytes; each page
publishes the size and SHA-256, so that is checkable rather than asserted.
Issues and pull requests are read.
There is also nothing to activate. No key, no account, no registration, no phone-home — the scripts make exactly the network connections their pages describe and no others.
All free
Three that read the network
bacnet-sweep
Broadcasts a BACnet/IP Who-Is, tables the devices that answer, and dumps a named device's object list — object name, present value and units — to a table or to CSV. It can encode two BACnet services and no others: Who-Is and ReadProperty.
- BACnet/IP
- Who-Is
- CSV out
mqtt-tap
Subscribes to a broker and prints what is actually on it: the topic tree with a count, a rate, a payload-type guess and the last value per topic, plus the retained topics that stopped updating. It sends five packet types and none of them is PUBLISH.
- MQTT
- Topic tree
- Retained
decoder-check
Runs a LoRaWAN device vendor's payload decoder against your frames in a sealed vm context and reports what a station would actually get back: crashes on a short frame, types that change between uplinks, units glued into values, keys a station has to escape. It reads frames and nothing else - no network, no broker, no network server.
- LoRaWAN
- Decoder
- Sandboxed
Also free
Nine that read what Niagara will do with your file
No network, no station and no import: they read the shipped jars and report what the framework is built to do with the settings, the file and the module you already have.
modbus-address-scan
Reads modbusCore-rt.jar out of a Niagara installation with javap and prints the register a point of each address format actually asks for — including the four band boundaries that all resolve to the same one.
- Modbus
- Shipped jars
- No station
ede-check
Reads the EDE import rules out of bacnetEDE-wb.jar with javap, then reports line by line what the shipped parser would reject in your point file and what it would silently default.
- EDE
- Point lists
- Line by line
module-sign-scan
Reads the verification code out of a Niagara installation and prints the four modes, the signature state each one accepts or refuses, and the exact log line a station writes — including the warning that only becomes a refusal when a certificate expires.
- Module signing
- Shipped jars
- No station
bacnet-priority-scan
Reads bacnet-rt.jar with javap and prints the object types Niagara writes through the priority array without asking, the ones it probes with a single ReadProperty, and what a failed probe does to the point for the life of that configuration.
- BACnet
- Shipped jars
- No station
alarm-route-scan
Reads alarm-rt.jar and baja.jar with javap and prints what happens to an alarm between the source and the recipient: one queue, one worker thread, the coalesce key that decides which duplicate is dropped, and why the invocation that lost that collision still reports success.
- Alarms
- Shipped jars
- No station
alarm-recipient-scan
Reads the recipient side of alarm-rt.jar with javap and prints why returning false from sendAlarm drops the alarm silently, what throwing does instead, how long the retry loop runs, and which four properties are the only evidence a site can send you.
- Alarms
- Retry
- No station
schedule-scan
Reads schedule-rt.jar with javap and prints the 90-day scanLimit horizon that turns a far-off change into no change at all, why nextCov steps over a boundary whose value matches, and the one serial uncapped queue every control schedule shares.
- Schedules
- Shipped jars
- No station
poll-scheduler-scan
Reads the poll scheduler out of driver-rt.jar and prints the arithmetic: three rate defaults, one point polled per pass, and the bucket size at which the thread stops sleeping and the real cycle time stretches.
- Niagara
- javap
- Read-only
tuning-stale-scan
Reads the tuning policy and the stale branch out of the shipped jars: the default staleTime of zero, the clock it measures, and why an unsolicited device can sit dead with an ok status for as long as the station runs.
- Niagara
- javap
- Read-only
Also free
One for the spreadsheet a business runs on
Not a control system at all. A workbook a department depends on is undocumented software too, and the first honest step is reading what it already does.
workbook-scan
Opens an .xlsx as the zip of XML it is and reports what is in the bytes: formulas saved holding an error, links into files that may be gone, saved queries to one person's mapped drive, approximate VLOOKUPs, hidden sheets, and rules nothing protects. It reads the old binary .xls too.
- Excel
- No install
- JSON out
Also free
One for a release on its way to production
Not a control system either. A website release can change what a client's search rankings rest on without anybody being able to see it on the page, and the only instrument that finds that is a comparison of the two renderings.
release-diff
Reads the same paths on two hosts and compares eleven fields per page — status, redirect chain, title, h1 and whether there is one, canonical, robots, description, the internal link graph, which measurement tags are present, and the JSON-LD types. Anything that moved and was not declared fails the run.
- Staging vs production
- 11 fields
- Exit 3
What "read-only" means here
It means something mechanical in both cases, not a promise about intent.
bacnet-sweep can encode two BACnet services and no others, Who-Is and
ReadProperty, and a gate function raises before any packet leaves if anything else
is ever passed to the request builder. mqtt-tap has no PUBLISH encoder
in it at all; every outbound byte goes through one function that refuses any packet
type outside CONNECT, SUBSCRIBE, PUBACK, PINGREQ and DISCONNECT, and
mqtt-tap.py --self-check makes that refusal happen in front of you.
Both files are one file, so anyone who wants to check rather than believe it can.
decoder-check touches no network at all: it runs a decoder inside a
node:vm context with no require, no process,
no filesystem, no timers and no sockets, and its own self-check proves that a
decoder trying to reach any of them fails rather than succeeds. The nine scanners
open jars and a CSV for reading, and workbook-scan opens a zip and an
OLE compound file the same way: none of them makes a network connection at all, and
there is nothing in any of those files that can reach a station, which is why none
of them needs one.
Read-only is not the same as no effect. A Who-Is makes every device on a subnet answer at once, and reading a large controller's object list is hundreds of requests to a device that also has a building to run. A new MQTT client that takes an identifier already in use gets the existing client disconnected, which is a way to cause an incident without publishing a byte. Both tools are built to avoid the second and to pace the first, and both pages say where it still matters.
What each one has actually been run against
This is the honest headline and it belongs above the fold rather than in a
footnote. bacnet-sweep has never spoken to a real BACnet device;
mqtt-tap has never connected to a real broker product. Each was
proved against a fixture written from the protocol specification and run on
loopback — 198 assertions for one, 130 for the other — and every
terminal transcript on these pages is real output from that fixture, not from a
site. A real controller or a real broker will differ. Each page has a section
saying exactly what was and was not covered, and it is worth reading before you
rely on either of them for anything that matters.
The nine scanners are measured rather than fixtured, and the measurement is of one
machine: a Niagara 4.15.5.22 installation, read with the
javap that ships with it. Every transcript on their pages is that run.
The controller this work usually targets runs 4.14.0.162, and none of
the nine has been run on a JACE. A different Niagara version is a different answer,
which is why each one prints the installation it read and each page says to run it
against yours.
decoder-check is the exception, and in the useful direction: it has
been run against real published decoders — the JavaScript that device
vendors ship for The Things Network and ChirpStack — and it found real
defects in them. Those went to the maintainers privately, with a patch, which is
why no vendor is named here or on its page. What it has never done is talk to a
network server: it reads frames you give it, and nothing else.
Also free, and written for an agent rather than a person
obix-mcp
(repository) is
an MCP server that lets an AI agent read a Niagara station over
oBIX — the lobby, points, folders, histories, and live
values through server-side watches. It is read-only by default: the write tool is
not merely refused when writes are off, it is absent from the tool list, and
turning it on takes two separate decisions, one for the deployment and one for
the subtree it may write to.
What makes it worth a look even if you never run it is
EVIDENCE.md:
every protocol decision cited to the class and method it was read out of in a
Niagara 4.15 install, with the gaps stated as gaps rather than filled in from
the specification. Three of those citations change what a client has to do —
the export branch is mounted as continuousControl and not
export; HTTP Basic is not enabled on a station by default, so a 401
is usually a missing scheme rather than a wrong password; and a 403 means either
an unlicensed server or a refused object, with only the response body to tell
them apart. Same caveat as the tools above: it has been proved against a fixture
written from that evidence, and has never been pointed at a controller.
If you want the findings without the code, they are written up as notes: what a Niagara oBIX server actually exposes covers the surface, and letting an agent write to a control system covers the part that needs gates.
Also free, and not a program
The Niagara 5 module
scan is the same idea pointed at a different problem: send a listing of a
station's modules folder and a per-module readiness table comes back.
That one we run rather than give away, because it needs a Java 25 JDK and a
rule set rather than a laptop.
Next step
Found something you now have to fix?
Send the CSV, the topic list or the decoder report, and what the site is. You will get a written scope and a fixed price against it — or, if the honest answer is that this is a setting rather than a project, that instead.