A standing monitor over signed sensor readings. It sweeps a building's smoke sensors and sorts every reading into one of three states — clear, tampered, or unknown — and, when a reading is tampered, it computes which connected rooms can no longer be vouched for.
This is a reference implementation on provenance-core, the same trust primitive behind the other repos in this set. Where one applies it to an agent that acts and another to a good that is checked at a gate, this one applies it to a sensor that reports.
It pairs with the essay An Alarm Is Not a Map. The essay makes the argument; this is the working thing behind it.
A smoke detector tells you there is smoke. It does not tell you which room, how far it spread, or what you can still trust. For one room that is enough. For a building it is the beginning of the problem, not the end.
Most integrity tooling ships the smoke detector: a green checkmark that turns red when a signature fails. That is an alarm. This repo is about the two things an alarm leaves out.
A break is not a point — it is a watershed. When a reading is tampered, the doubt does not stop at that sensor. The rooms connected to it can no longer be vouched for on the strength of it. So a finding here does not just say "sensor X failed" — it names the affected zone: the connected rooms now in doubt. That map is what a manager can act on at 2am; the alarm alone is not.
Not all red is the same red. The monitor can fail in two completely different ways, and it never paints them the same colour:
- TAMPERED — a reading was altered after it was signed, or the log's hash chain broke. This is an accusation, and the cryptography stands behind it. It is red, and it persists.
- UNKNOWN — the monitor could not complete the check: the sensor was unreachable, or its reading was missing, or the device had been decommissioned. This is not an accusation. Nothing has been shown to be wrong. It is amber, and it says so.
Conflating those two is how a dashboard becomes theatre. If "I couldn't reach the sensor" wears the same red as "someone edited the reading," operators learn that red means nothing and stop looking — and then the one time red means tampered, nobody is watching. Keeping the three states apart is the whole point of this repo.
overall: tampered
TAMPERED sensor_tampered room=Server Room blast=[Corridor]
UNKNOWN sensor_unreachable room=Kitchen (severity: warning)
Every non-clear reading becomes one finding, with a state, a kind, a severity, a plain-language detail, and — for a tampered reading — a blast radius. Clear readings produce nothing; the sweep returns only what warrants attention.
Findings can be pushed to a sink. A ConsoleSink (the safe default) prints
them; a WebhookSink (opt-in via BUILDING_INTEGRITY_WEBHOOK_URL) POSTs each as
JSON. Email, Slack, and paging are deliberately not here — the webhook is the
seam you wire those to. "Trustworthy detection and honest classification;
connect it to your own ops" is the right boundary for a reference
implementation.
You need Python 3.12+. Full step-by-step (including a no-terminal-experience walkthrough) is in RUNNING.md. The short version:
make dev # install the vendored core, then this package
make test # 11 tests, a throwaway database, no network
make run # then open http://localhost:8000/The UI has three columns: pick a situation on the left (alter a reading, rewrite the log, or lose contact with a sensor), watch the findings appear in the middle, and see the actual checks the monitor ran — with real values — on the right.
For a terminal-only tour, with the server running: make demo.
A trust project that overstates itself is undermining its own point, so:
-
The blast radius is flat — the first ring only. When a room is compromised, the monitor returns the rooms directly connected to it, computed from the real room topology (not narrated). It does not yet walk the full transitive closure — every room reachable through the graph, in propagation order. That graph-aware impact analysis is the honest next layer, and the finding says
depth: 1so the first ring is never mistaken for the whole watershed. -
Reachability is modeled, not probed. Whether a sensor is "reachable" during a sweep is an injected flag, and the demo's tampering is applied through clearly-marked
demo_*affordances. This keeps the demo deterministic and runnable with no services. The detection is real — real signatures, a real hash chain, a real adjacency query — but the world around it is a sandbox. -
The domain is deliberately shallow. There is no fire physics, no smoke model, no real detection logic. A "reading" is a shallow record. The subject of this repo is the signature, the tamper-evident log, and the honest three-way classification — not building safety engineering.
-
It records and proves; it does not actuate. No alarms sound, no sprinklers trip. This is the receipt, not the siren.
-
Single node, SQLite. One file, zero infrastructure, so it clones and runs in minutes. A real deployment would use a proper datastore; the interface is small so that swap stays mechanical.
Apache-2.0 · SurroundApps. Built on provenance-core.