Inspiration

A carbon credit is a promise that one tonne of CO₂ stayed out of the air. The promise only means something if it is spent once. A 2024 study in Nature Communications looked at 2,346 projects and found that 84% of their credits did not reflect real emission cuts, and in June 2025 Verra put a cookstove project on hold after double-counting claims. Most checks look at one project at a time, but double counting lives between projects. I wanted to see if a graph and an AI agent could do the first pass, so a human reviewer starts with a short list instead of a pile of spreadsheets.

What it does

EcoGraph finds three ways one tonne gets counted twice:

  1. Same land, two projects. Two active projects claim the same parcel, sometimes on different registries.
  2. One credit, two buyers. A credit is retired by more than one company.
  3. A circle of sales. One credit is sold from holder to holder until it returns to one it already passed through.

It groups related findings into cases, scores each from named signals, and shows the exact query behind every answer. The agent can only recommend. A person freezes, refers or dismisses each case, and the decision goes back into the graph.

Each case is scored as

$$\text{score} = 1 - \prod_i (1 - w_i)$$

where each $w_i$ is the weight of a signal (double retirement 0.90, registry duplicate 0.80, overlap 0.60, trading loop 0.55, no audit 0.15). It is a rule of thumb, not a probability, and the app says so.

How I built it

  • FalkorDB holds the ledger as a graph: 1,900 nodes and 4,125 edges (projects, land parcels, registries, credits, buyers). Every detector is a Cypher pattern, algo.WCC turns 7 findings into 5 cases, and algo.betweenness ranks brokers in the middle of trading.
  • An AI agent (Gemini) reaches the graph only through 11 MCP tools. Ten read, and one writes a note. The rule is enforced by the tool list, not a prompt, and a test checks it.
  • A Flutter app for iOS, Android and web, plus a desktop land-map dashboard that plots every parcel and marks shared land in red.
  • A small Python backend, deployed on Render with Docker.

All data is synthetic. I planted 7 frauds and 6 look-alike decoys. The detectors found 7 of 7 with 0 false alarms, and a full scan takes about 0.15 seconds.

Challenges

  • A query that never finished. My first loop detector asked for any closed walk of three to six sales. On 121 traders it had not finished after two minutes. Adding {credit: cid} to the traversal, so every hop carries the same credit, made it return in under a second and fixed a false-positive bug too.
  • Deployment. The embedded FalkorDB build needs glibc 2.39 or newer, so my Docker image had to move to Debian 13.
  • A sleeping free server. My app showed a pulsing dot while the server woke up. I added a clear "Connecting" message and an uptime monitor.
  • "Can't reach server" on Android. The release build needed the live server address baked in at build time.
  • Keys. The Gemini key lives only in a git-ignored .env file and Render's environment settings, and a script scans the repo for key-shaped strings before every push.

What I learned

A graph database earns its place when the question is about paths, not rows. Every one of my three fraud types is a path. I also learned to keep the agent's power small and enforce it in code, and that shipping (deployment, keys, cold starts) takes as long as the detection logic.

What's next

EcoGraph detects double counting that already happened. The natural next step is to prevent it: an on-chain retirement registry that rejects a second claim on the same credit, with EcoGraph's decisions anchored as tamper-evident hashes. I would also replace the synthetic ledger with public registry data, and scan each new issuance, sale and retirement as it happens.

Built with AI, openly

Claude wrote much of the code, tests and documentation under my direction, and I reviewed and ran it. The running product uses a language model only for the Ask agent.

Links: Code · Live demo · Land map · Blog · Demo video

Built With

Share this project:

Updates

Submission history