-
-
361,903 carbon credits counted more than once, found across six registries that do not reconcile against each other.
-
Thirteen confirmed findings. Kielty leads: two registries credited the same vintage year, 147,508 credits on both sides.
-
Every finding links to both registry records, so the claim can be checked at its source rather than taken from this page.
-
The browser fetches the sealed Solana account, rebuilds the fingerprint, walks the Merkle proof, and compares. No trust needed.
-
What Tally refuses to claim: 977,904 pairs compared, eight left awaiting human review and excluded from the attested result.
Track 1 — Carbon Markets & Emissions Transparency
The problem that started this
A carbon credit represents one tonne of CO2 that was not emitted. The buyer uses it to offset their own emissions. The whole market rests on one rule: each tonne is counted once.
Six registries issue credits independently — Verra, Gold Standard, American Carbon Registry, Climate Action Reserve, ART and Isometric. Each keeps its own database, in its own format, with no link to the others. None of them can see what the others issued.
So nothing stops one physical project from registering twice and earning credits in both for the same reduction. A 2025 systematic review in the Annual Review of Environment and Resources names double counting among the most severe failure modes in carbon markets, alongside non-additionality, impermanence and leakage.
What made me pick this one: of the six failures that review lists, double counting is the only one detectable from public data alone. Judging whether a forest would have been cut anyway requires field measurement and years. Determining whether two registries credited the same project for the same year is arithmetic over records that are already published — and nobody was doing it.
What it does
Tally reads the complete public record of all six registries: 11,856 projects and 541,290 credit-level events, and finds credits claimed more than once.
361,903 credits counted more than once. Thirteen projects credited for the same vintage year by two registries, plus one project whose own serial numbers were retired across three overlapping events.
The largest case is Kielty Abandoned Mine Methane Recovery. The ACR issued 266,038 credits for it; the CAR issued 147,508. Vintage 2025 appears in full on both sides. The CAR side has already been retired by a buyer. The ACR side is still available for sale.
Each finding is classified by what became of both copies: both still available, one already claimed, or both claimed by different buyers. 13,252 credits fall in the last category — two different buyers offset their emissions against the same tonne.
What it clears
This is the part I care most about. 56 projects appear in two registries with no overlapping vintage. Those are legitimate registry migrations, and Tally publishes them as cleared. Six more cases are the same transaction exported twice by a registry — a data quality problem, not double counting. Eight further candidate pairs, worth 2.4 million credits, are in a review queue and deliberately excluded from the attested result because a name match is not proof.
A tool that only ever accuses cannot be checked.
How I built it
The pipeline is Python: pandas for the workbooks, SQLite for the normalised data, scikit-learn and RapidFuzz for record linkage. 96 tests, including every published figure.
Linkage compares 977,904 cross-registry pairs, blocked by country. The decisive logic is negative: a field populated on both sides that disagrees rejects the pair outright; an empty field never rejects, because absence is not evidence.
Identity markers come from the corpus, not from a hand-written list. A token appearing in under 1% of projects identifies one; a token occurring in exactly one country is a place name at any frequency. That last rule is what separates a Gujarat wind project from a Karnataka one when every other word in both names is identical.
And a name match alone is never a finding. Two registries must have credited the same vintage year — the year the reduction physically occurred. 69 projects issue in two registries; only 13 share a vintage.
No large language model is used anywhere. Record linkage here is classical machine learning, which runs on CPU at no marginal cost and, more importantly, is reproducible by a third party. A model behind an API would make the result unverifiable, which defeats the entire purpose.
The attestation is an append-only Solana program written in Rust with Anchor, 8 tests. The SHA-256 of both source workbooks is written before the result, so the commitment covers the inputs as well as the output. Findings must arrive in order, the run seals only when complete, and once sealed nothing can be added, changed or removed.
The interface is a static Next.js site with no backend. Press "Run the check" on any finding and your browser fetches the account from Solana, rebuilds the finding's fingerprint from the fields displayed on the page, walks the Merkle proof to the root, and compares. The browser verification is a second, independent implementation of the hashing in TypeScript — if it disagreed with the Python pipeline it would fail visibly instead of quietly agreeing with itself.
Why a blockchain is necessary here
The parties whose records are being audited are the registries themselves. Storing audit results in a database that one operator controls would ask readers to trust exactly the kind of party the tool exists to check.
That is the whole argument. An append-only account, seeded by dataset version and sealed when complete, means a result cannot be silently revised — including by me.
Challenges
The data lies about its own shape. The source spreadsheet has 170 columns, not the 45 my first inspection found: four adjacent blocks of year columns whose headers are bare numbers. Reading them as one block compared issued credits against retired ones. The bug announced itself as an overlap reported as "2018-2018 (6 years)".
Floating point broke the totals. The Isometric registry reports removals to three decimal places. Summing the same column three ways gave 2,704,143,472, then ...473, then ...472.766. For a tool whose entire claim is "recompute this and compare", three possible answers is fatal. Every amount is now an integer in thousandths.
My best finding was wrong. 4.5 million credits retired for compliance with no corresponding adjustment looked major. Research showed corresponding adjustment applies to internationally transferred outcomes; California's programme is subnational with domestic projects and buyers, so its absence is expected. I discarded the figure before publishing it.
A filter I designed would have destroyed a real finding. I built a consistency check on year-over-year credit ratios, assuming one physical project metered twice would keep a steady ratio. Measuring it first showed it would have rejected a confirmed finding whose ratios vary 34-fold. The variable stayed as context and never rejects anything.
Name matching nearly ruined the project. An early version produced 681 "findings" worth 69 million credits. Nearly all were false: five different county landfills in five different states matched each other because "county landfill gas project" is the name of the category, not the project. Getting from 681 noisy matches to 13 defensible ones was most of the work.
What I learned
Measure before deciding. Four separate times, a rule that seemed obviously right was wrong, and only checking it against the 13 cases I already understood revealed that.
A verification tool is only as credible as its willingness to publish what it did not find. The 56 cleared projects and the 8 unreviewed candidates do more for the project's standing than a bigger headline number would.
And documenting your own errors is not a weakness in an audit tool — it is the only thing that gives it standing to report anyone else's. All four errors above are written into SECURITY.md in the repository.
What the data shows but Tally will not claim
Over 7.1 million credits from Mexican projects were retired through a US registry by predominantly foreign buyers — every one recording the Article 6 corresponding adjustment as "No". Verra publishes no such field at all, while Shell and Eni alone retired over 46 million tonnes from projects in Indonesia, Peru, the DRC and Zambia.
Tally does not claim those tonnes were double counted. It reports that the public record does not contain the information needed to rule the question out, for cross-border transfers measuring in the tens of millions of tonnes. That disclosure gap is directly within the scope of COP 31, and closing it would cost registries nothing — the ACR and Gold Standard already publish the field.
What's next
Each quarterly release of the source database becomes a new sealed attestation keyed by version, so results accumulate as a history rather than overwriting each other. The eight queued candidates get human review. Geographic coordinates, where registries publish them, would raise the ceiling on linkage beyond what names allow.
And the program's upgrade authority gets revoked — after which the attestation rules cannot be changed by anyone, including me.
Built With
- anchor
- blockchain
- carbon-markets
- data-integrity
- machine-learning
- merkle-tree
- nextjs
- pandas
- pytest
- python
- rapidfuzz
- react
- record-linkage
- rust
- scikit-learn
- sha-256
- smart-contracts
- solana
- sqlite
- tailwindcss
- typescript
- vercel
- web-crypto-api
Log in or sign up for Devpost to join the conversation.