AWS Builder Center

LeakLedger @ Environmental Hacks by WeMakeDevs

LeakLedger is a water reconciliation and repair-verification system for facilities with hierarchical water meters. The main idea is simple: LeakLedger does not guess where water went. It reconciles it.

Building LeakLedger: Accounting for Water Loss Instead of Guessing

LeakLedger reconciles water across a meter hierarchy, pauses when the evidence is incomplete, and only marks a repair as verified when new meter readings prove it.
A facility can know that it used more water than expected and still have very little idea what to do next.
A campus might have a main water meter, separate meters for hostels and common facilities, and smaller meters for individual floors or branches. A dashboard can tell the facilities team that consumption increased, but that still leaves the harder questions unanswered: where did the imbalance begin, do we have enough measurements to trust that conclusion, and did the repair actually fix anything?
Those were the questions I wanted LeakLedger to answer.
Instead of treating the water network as a collection of charts, I treated it as a ledger. Every interval should either reconcile or have a reason why it does not. If something remains unexplained, the system should narrow the investigation only as far as the available meters allow. If those measurements disappear, it should admit that the evidence is no longer sufficient. And if somebody reports a repair, the system should wait for new meter data before deciding that the problem is actually gone.

What LeakLedger actually does

LeakLedger is a water reconciliation and repair-verification control plane for facilities that already have hierarchical water meters.
The demo operates on fifteen-minute reconciliation intervals. For each metered boundary, LeakLedger compares the amount of water entering that boundary with everything it can account for downstream during the same period.
Here, reconciliation simply means comparing measured inflow with measured downstream consumption, known unmetered usage, and any storage change.
The calculation is straightforward:
Plain text
Unexplained water
= inflow
- measured downstream flow
- known unmetered use
- storage change

One of the main examples in the demo is Hostel B:
Plain text
3.85 m³ entered Hostel B Main
- 3.40 m³ measured downstream
- 0.10 m³ known unmetered use
- 0.00 m³ storage change
= 0.35 m³ unexplained

That final 0.35 m³ is the residual: water that the current measurements and configured assumptions cannot explain.
Importantly, a single abnormal interval does not immediately trigger an incident. LeakLedger also checks whether the evidence is complete enough to trust and whether the residual persists. In the demo, the anomaly must survive three distinct valid intervals before an incident opens.
That persistence rule matters because one bad interval may simply be noise, delayed data, or a temporary operational change. Three consecutive anomalous intervals with valid evidence are much harder to dismiss.
The UI can also translate the current residual into a daily projection. A 0.35 m³ residual over fifteen minutes corresponds to 1.4 m³ per hour, or around 33.6 m³ per day if the same rate continues. The wording "projected if sustained" is deliberate: a short observation should not be presented as a guaranteed daily loss.

Localising the imbalance without pretending to know too much

The fictional demo site is Northbridge University Campus.
Its meter topology contains the campus main meter, Hostel A, Hostel B, a mess or kitchen, a sports block, and smaller downstream branches.
Reconciliation runs at each metered node independently.
This means the campus root can still reconcile while an internal branch does not. That distinction became important during the demo: the campus-level balance may look fine, while Hostel B repeatedly shows an unexplained residual.
LeakLedger follows that imbalance through the meter hierarchy. If an anomalous child can be identified with sufficient evidence, the investigation boundary moves down to that child and the process repeats.
In the hidden-loss scenario, the search ends at:
 HOSTEL-B-MAIN 
The UI calls this the deepest trustworthy boundary.
I chose that wording because LeakLedger is not supposed to claim an exact pipe location when the instrumentation cannot support one.
Hostel B contains an explicitly unmetered common branch. Since not everything below Hostel B Main is measured, claiming a more precise location would be dishonest. The useful answer is therefore not "the leak is exactly here." It is:
This is the deepest point in the topology where the current meter evidence still supports the conclusion.
That still gives a maintenance team a much smaller area to investigate, while keeping the distinction between a measured conclusion and a guess.

What happens when the evidence disappears

Localisation is only useful if the system also knows when it can no longer trust it.
LeakLedger tracks evidence properties such as meter coverage, reading completeness, freshness, time alignment, and whether the required reading pairs exist for the same physical interval.
A water balance depends on those pairs.
If a parent meter reports but one of its required downstream meters disappears, the remaining arithmetic can become misleading. Subtracting only the meters that happened to report may produce a large number, but that number is not a complete water balance.
The missing-meter scenario was built specifically to test this.
Once a required reading disappears, LeakLedger changes the incident status to:
 EVIDENCE INSUFFICIENT 
and pauses localisation.
It also explains why. The UI identifies the missing meter or missing reading pair rather than simply replacing a high-confidence badge with a vague lower score.
This behavior is what I mean by failing closed: when the evidence required to justify a conclusion is missing, the system withholds the stronger conclusion instead of continuing under invalid assumptions.
That principle became one of the most important parts of the project:
Missing evidence should reduce certainty. It should never create false certainty.
When valid readings begin arriving again, reconciliation resumes and the system can once again localise based on fresh evidence.

A repair report is not proof

I applied the same reasoning to maintenance.
A technician can record that a repair was performed, but LeakLedger does not immediately change the incident to resolved.
Instead, the incident status moves to:
 VERIFYING 
The system then waits for three new, distinct, and valid post-repair reconciliation intervals:
 1 / 3 
 2 / 3 
 3 / 3 
Only after the full sequence remains healthy does the incident become  REPAIR VERIFIED  and resolve.
If the residual continues to violate the expected balance, the repair can instead be marked as failed.
The distinction is simple but important:
  • "A technician says the work is complete" is a maintenance event.
  • "The unexplained residual disappeared afterward" is evidence that the work solved the problem.
I did not want the same click that records a repair to also prove that repair.
That is why the verification sequence is one of the parts of LeakLedger I consider most important.

The AWS architecture

The deployed version is built around an asynchronous AWS pipeline:
Plain text
Meter reading
↓
API Gateway HTTP API
↓
API Lambda — Python 3.12
↓
S3 raw event archive
↓
EventBridge custom bus
↓
SQS reconciliation queue
↓
Reconciliation worker Lambda — Python 3.12
↓
DynamoDB

The API Lambda validates incoming readings, normalizes units, stores the original event in S3, and publishes a  MeterReadingReceived  event.
Reconciliation does not run inside the original HTTP request.
That separation was intentional. Meter ingestion should not have to wait for the full reconciliation engine, and SQS gives the worker a durable buffer when multiple readings arrive close together. The queue also has a dead-letter queue (DLQ), and the worker can report partial failures without forcing every successful message in the batch to re-run.
The worker loads the persisted site state, reconciles the appropriate interval, updates balances and incidents, and writes the results back to DynamoDB.
Every incoming reading also has an  event_id . Before processing, the worker claims that event in DynamoDB. This is how the pipeline provides idempotency: sending the same event twice should not change the final state twice.
For observability, the deployed runtime uses structured CloudWatch logging and metrics for events such as readings processed, reconciliations completed, balance violations, incidents opened, repairs verified, repair failures, and ignored duplicates.
The stack also creates an operational dashboard and a DLQ alarm, and both application Lambdas have X-Ray tracing enabled.
The infrastructure includes a CloudFront path for the static frontend, but CloudFront could not be enabled during the hackathon because the AWS account was still subject to an account-level verification restriction.
The deployed application therefore serves the same static export through the API Lambda behind API Gateway HTTPS.

Replay Lab: making the whole lifecycle reproducible

Waiting for real fifteen-minute water-meter intervals would make a three-minute hackathon demonstration fairly impractical.
So I built Replay Lab.
Replay Lab creates deterministic cumulative meter readings for several controlled scenarios and sends them through the same ingestion and reconciliation path as normal application data.
It is not a frontend animation that directly sets an incident to  OPEN  or  RESOLVED .
The readings go through the event pipeline, and the normal domain rules decide what happens.
The scenarios include:
  • a balanced system,
  • a hidden Hostel B loss,
  • missing meter data,
  • successful repair verification,
  • failed repair verification,
  • and meter counter/reset edge cases.
That made the entire incident lifecycle reproducible while keeping the backend behavior identical to the normal ingestion path.

The bugs that appeared when I stopped running everything slowly

The calculation behind LeakLedger is not especially complicated.
The harder part turned out to be keeping that calculation meaningful when events were delayed, duplicated, or delivered out of order.
One of the more serious bugs only appeared when I stopped stepping through the replay slowly and started sending several intervals through the pipeline close together.
The original engine relied too heavily on the latest two readings for a meter. That worked when everything arrived in the exact order I expected.
EventBridge and SQS, however, do not give the application that convenience.
A parent reading could be paired across one physical interval while one of its children was effectively being compared across another. That produced balances that were mathematically calculated but physically meaningless. In one case, it even generated a negative residual because downstream usage exceeded the inflow being compared against it.
The fix was to change the reconciliation model rather than hide the strange number.
Reconciliation now targets a particular interval, pairs each meter with the immediately preceding interval boundary, and treats missing pairs as incomplete evidence.
The worker also allows the preceding interval more time to settle during burst delivery before declaring the readings genuinely absent.
A second bug appeared around evidence restoration.
Suppose an incident pauses because a meter is missing at a newer interval. If an older, healthy interval arrives late afterward, that old information cannot prove that the newer gap has disappeared.
Earlier logic could still allow that older interval to restore the incident.
LeakLedger now records the interval on which the current incident state is based. Evidence can only restore localisation if it belongs to that interval or a newer one.
Repair verification had a similar problem. Incrementing a counter whenever healthy events arrived meant that the streak could depend on processing order.
Verification is now stored by interval, and the trailing valid sequence is recomputed chronologically. Three healthy intervals therefore mean the same thing regardless of the order in which the queue delivers them.
There were smaller frontend issues, too.
One example was the Replay Lab scenario selector. Navigating away and back could show a default scenario even though the backend was still running another one. The fix was to make the backend replay state the source of truth instead of relying on a local default value.
These bugs ended up being useful. They forced the application to deal with the difference between correct arithmetic and correct event-driven behavior.

Validation

The current backend suite contains 30 tests, along with 5 frontend scenario tests.
The tests are built around behavior rather than just endpoint responses.
They cover cases such as:
  • a healthy site remaining balanced,
  • persistent hidden loss opening an incident at Hostel B,
  • incomplete meter evidence failing closed,
  • duplicate events not changing state twice,
  • one physical interval not advancing persistence more than once,
  • valid evidence restoration,
  • successful repair requiring three distinct healthy intervals,
  • and failed repair not being falsely resolved.
The same important scenarios were also run through the deployed AWS environment.
During the final validation passes, the hidden-loss scenario opened an incident at  HOSTEL-B-MAIN  with the expected 0.35 m³ residual, the missing-meter scenario moved into evidence-insufficient behavior, repair verification progressed through 1/3, 2/3, and 3/3, and duplicate ingestion was counted only once.
The queue returned to zero and the DLQ stayed empty.

The finished workflow

Putting those pieces together, the complete workflow looks like this:
  1. A normal interval reconciles.
  1. A hidden 0.35 m³ residual begins appearing below Hostel B. It persists across three valid intervals, so LeakLedger opens an incident at the deepest trustworthy boundary:  HOSTEL-B-MAIN .
  1. The system does not descend further because an unmetered common branch leaves a narrower conclusion unsupported.
  1. Then a required downstream meter stops reporting.
  1. The evidence becomes insufficient, so localisation pauses rather than strengthening the old claim.
  1. When complete readings return, reconciliation resumes.
  1. Maintenance reports a repair.
  1. The incident becomes  VERIFYING  rather than  RESOLVED .
  1. Three new healthy reconciliation intervals arrive.
  1. Only then is the repair verified and the incident closed.
That sequence captures the principle behind the project:
Every stronger conclusion should require stronger evidence.

Tradeoffs and limitations

LeakLedger is still a hackathon project, and I did not want to hide that behind the demo.
  • The current scenarios use deterministic simulated meter readings rather than a real campus meter network.
  • The system localises losses to metered boundaries, not physical pipe coordinates.
  • An unmetered branch limits how precisely LeakLedger can localise an imbalance.
  • The three demo identities are used for attribution and workflow demonstration rather than full role-based access control.
  • The core decision process also contains no machine-learning model.
Reconciliation, persistence, evidence gating, localisation, and repair verification are deterministic. For this problem, I preferred conclusions that could be traced directly back to meter readings and configured rules.
I also deliberately left monetary-loss estimates out of the current product. Water tariffs vary by site, and I did not want to invent a universal conversion just to make the dashboard look more impressive.
The next meaningful validation step would be to run LeakLedger against a real facility, where meter quality, timing, and topology are less cooperative than a simulator.

Closing

The part of LeakLedger I care about most is not that it can raise an incident. Plenty of systems can produce alerts.
The harder problem is knowing when the available measurements actually justify a conclusion, when they do not, and whether later measurements prove that the problem was fixed.
LeakLedger does not guess where water went.
It reconciles it.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article