VeriCanopy — forest carbon credits that prove themselves

Live app API status Base Sepolia MCP package
Release MIT

Live app · API docs · MCP server · Architecture · Benchmarks · Limitations · Launch film

Forest carbon credits that prove themselves. Satellite AI scores every forest against a matched twin, a zero-knowledge proof puts the score on-chain, every record is signed post-quantum, and the credit keeps watching the forest after it is sold.

The whole VeriCanopy system drawn as one diagram


1 · The problem

Today a forest credit is verified once, by someone you have to trust, against a baseline the developer wrote. Then nobody looks at the forest again.

How a credit is made today: developer-chosen baseline, never re-derived from pixels

What happens today What it costs
① Baseline The developer picks the reference region and the deforestation rate. That one number decides how many credits exist. Nobody re-derives it from pixels. Independent investigations found more than 90 % of some rainforest offsets to be phantom reductions (West et al., Science 2023; Guardian / SourceMaterial 2023).
② Time The credit is issued, sold and resold. Nothing re-scores the forest after issuance. On our Manavgat pilot area a single fire year removed 1.2 MtCO₂e. The registry would never notice.
③ Score Ratings come from a private model and a letter grade. No proof the model ran, no proof which data it saw.

Manavgat: Sentinel-2 2019 vs 2021 fire scar Private model, letter grade, no proof

This is real satellite data, not a mock-up. Both cards below are Sentinel-2 L2A composites rendered by Google Earth Engine for the exact polygons registered on Base Sepolia:

Manavgat, Türkiye — 2019 vs 2021 fire, project frozen

Tapajós, Brazil — 2019 vs 2024, score 71.9 to 3.9

2 · The solution: from pixels to proof

VeriCanopy: from pixels to proof

Gap Question VeriCanopy's mechanism
Input Was the right land and data used? Is this land sold twice? Earth Engine evidence for the project and a matched twin forest; reproducible ML-DSA-65-signed manifest (datasets, dates, geometry hash, code commit); H3 cell exclusivity in ProjectRegistry; staked challenge game
Computation Did the AI really run the registered model? EZKL/Halo2 proof verified on-chain by Halo2Verifier; the score is decoded from the proof's public instances, never trusted from the oracle
Time Is the credit still true today? Living integrity score per period, automatic buffer burns, freeze on shortfall, holder notifications

Step 1 · Evidence from pixels, against a twin forest

Every period the oracle pulls Sentinel-2, Sentinel-1, Hansen GFC 2025, GEDI L4B biomass, MODIS burned area and Dynamic World for the project polygon and a matched twin nearby: 776 project pixels against 2,396 twin pixels. The twin is the counterfactual, not the developer's spreadsheet.

Sentinel-2 tile with project and twin polygons and the six datasets Twin-forest chart from real evidence data

Step 2 · An integrity score that follows the forest

Sixteen features feed a 417-parameter audited network (MAE 0.0125 against the transparent expert rule, zero monotonicity violations). Tapajós scored 71.9 in 2023; after 8,080 t of detected loss it scores 3.9.

16 features, 417-parameter network, score 71.9 to 3.9

Step 3 · A zero-knowledge proof the oracle cannot fake

Inference runs inside a Halo2 circuit (EZKL 23.0.5): about 6 s, a 4,416-byte proof. Halo2Verifier on Base Sepolia checks it inside submitScore; a wrong proof reverts. The manifest is signed with ML-DSA-65 (FIPS 204) and the key is registered on-chain.

zk circuit to proof.bin ML-DSA-65 seal

On-chain receipt. The real 2025 Tapajós submission, verified by the deployed verifier: 0xba0aaa…7ff4db (Base Sepolia, block 49,603,029, status Success). The event log carries period 7, project 1 and the oracle address as indexed topics.

Basescan: IntegrityOracle submitScore transaction Basescan: ScoreSubmitted event log

Step 4 · A living credit

Credits carry the live score. Detected loss burns buffer credits first; a shortfall freezes the project, blocks transfers and notifies every holder. Anyone can challenge a score with a bond; two of three independent resolvers recompute it.

Buffer burn, freeze, challenge with 2-of-3 resolvers

flowchart LR
  A[Sentinel-1/2 · Hansen GFC · GEDI L4B · MODIS · Dynamic World] --> B[Twin-forest matching<br/>776 vs 2,396 px]
  B --> C[16-feature vector fv1]
  C --> D[417-param MLP<br/>integrity score]
  D --> E[EZKL Halo2 proof<br/>4,416 B · ~6 s]
  E --> F[IntegrityOracle.submitScore<br/>Halo2Verifier · Base Sepolia]
  C --> G[Manifest signed ML-DSA-65]
  G --> F
  F --> H[VeriCredit ERC-1155<br/>buffer · freeze · challenges · market]

3 · Use it today (live on Base Sepolia)

Every clip below is a real recording of vericanopy.vercel.app, driven click by click.

Landing page

Registry. Every project on a map with its live integrity status, H3 cells, credits issued, loss detected and buffer coverage.
Project page. Real Sentinel-2 before/after, the twin-forest chart with the fire year, integrity history and claim vs reality.
Year slider, fire watch, leakage ring. Scrub Sentinel-2 composites 2017–2025, NASA FIRMS detections for the last 30 days, and Hansen loss in a 10 km ring around the parcel.
Proofs & signatures. Model hash, feature commitment, manifest CID, the on-chain tx, ML-DSA-65 verification, and a signed auditor report (PDF + JSON).
Register. Draw or paste a polygon; H3 overlap is pre-checked and the contract refuses land that is already registered (OverlapDetected).
Market. Escrowed fixed-price listings with the integrity score on the price tag and a price guide by integrity band. Frozen projects cannot trade.
Ask. Deterministic, model-faithful explanation of any score: per-feature attribution, data sources, proof and signature status. No language model in the loop.
Status. Database, RPC head, indexer lag, Earth Engine, signer, keeper balance and all ten contracts, refreshed every 30 s.
Verify. Re-check hash ↔ chain, key ↔ registry, signature and proof mode for any manifest.

Also: Challenges with bonds and 2-of-3 resolver votes · Retirements with printable certificates · Alerts as JSON and RSS · Model registry page that compares the on-chain model hash with the published ONNX artifact.

4 · For agents: the VeriCanopy MCP server, working in claude.ai

VeriCanopy is a Model Context Protocol server. Below is a real claude.ai session: the connector added under Customize → Connectors, then one prompt. Claude listed the projects, explained the 3.9 score feature by feature, found the cheapest listing and prepared an unsigned buy transaction for the user's wallet. The server holds no keys.

claude.ai using the VeriCanopy MCP connector

claude.ai connector settings: VeriCanopy, 10 tools Claude calling list projects, explain score, list listings, prepare buy tx Unsigned buy transaction returned by Claude

Connect it

# Claude Code, remote HTTP
claude mcp add --transport http vericanopy https://vericanopy-api.onrender.com/mcp/

# Any stdio client (Claude Desktop, Cursor) via the GitHub package
echo "@lsudoko:registry=https://npm.pkg.github.com" >> ~/.npmrc
npx -y @lsudoko/vericanopy-mcp

claude.ai: Customize → Connectors → Add custom connector → https://vericanopy-api.onrender.com/mcp/, no sign-in.

Tools: list_projects · get_project · get_submissions · get_evidence · explain_score · list_listings · prepare_buy_tx · prepare_retire_tx · get_challenge · verify_manifest. Package: @lsudoko/vericanopy-mcp (source). Details: docs/MCP.md.

The live product as one wall of recordings

Measured, not promised

Every number is produced by code in this repository (scripts/write_benchmarks.py, forge test --gas-report, pytest). Full table in docs/BENCHMARKS.md.

Metric Value
Contract tests (incl. a real EZKL proof verified by the deployed Halo2Verifier, fuzz + invariants) 55
Backend tests (indexer idempotency, verify binding, market, feeds, MCP, reports) 31
Web smoke tests against production 11
Proof size / proving time (mean of 20) 4,416 B / 6.02 s
Halo2Verifier deployed bytecode 19,817 B (limit 24,576)
On-chain verify inside submitScore ≈1.08 M gas
Model 16-16-8-1 MLP, 417 params, MAE 0.0125, 0 monotonicity violations
ML-DSA-65 sign / verify (pure Python) ≈118 ms / ≈19 ms
Satellite datasets via Earth Engine 10

Pilot areas and data provenance

Loreto, Peru — Sentinel-2 before and after

Two kinds of results exist on purpose:

  • Live submissions on Base Sepolia (provider: earthengine in the manifest): features extracted from real Hansen GFC 2025, Sentinel-1/2, GEDI L4B, MODIS burned-area and Dynamic World pixels for the pilot polygons, proven with EZKL and verified on-chain. The pilot polygons (Tapajós riverine block, Loreto Marañón floodplain, Manavgat pine forest) were drawn by us for monitoring; they are not issued carbon projects, so their real-data scores are low (Tapajós/2025 = 3.9, Manavgat/2021 = 0.2 with 1.2 MtCO2e of real 2021 loss). That is the system working as intended.
  • The first three submissions (provider: fixture) were made before Earth Engine access existed and use deterministic labelled fixture samples; they remain on-chain, labelled as such.

Deployed addresses

Component Where
Web app https://vericanopy.vercel.app (Vercel)
API, indexer, workers, MCP https://vericanopy-api.onrender.com (Render, Docker, GET /api/v1/status)
Database Neon Postgres (Alembic-managed)
Contracts Base Sepolia (84532), contracts/deployments/84532.json
Contract Address
ProjectRegistry 0x40Fc737bc985f8f75689a4B4E95D625a1c701e4a
ModelRegistry 0x36691AdE60cEc5A02B32690B159AC92C4Ec5ac9d
PQKeyRegistry 0xd760B9004F5ACF3814Ea833E601a23b060B922c2
IntegrityOracle 0xa3a203519f86DdeeCA51771F43ca0840e51387b6
Halo2Verifier 0x333c45Cbbb7AB746244767c199551e69A4Fa4965
ChallengeManager 0x1AA138691E23a3d35FDfB09CA7ABf7065987a14d
VeriCredit (ERC-1155) 0x69DEfd5a594542D4d1e514d4CD9d8CE1a448cE3f
BufferPool 0xa536e7C93c32179747d6eD83fA5138B2b7879700
RetirementRegistry 0x1D042FddCA42542D51CA4c33aB55db1835b010a9
Marketplace 0x305a35aAf5347c98C02930d35854e9a9B1c2e481

First zk-verified submissions: #1, #2, #3.

Architecture

Layer Stack Lives in
Contracts Solidity 0.8.26, OpenZeppelin v5, Foundry; EZKL-generated Halo2Verifier contracts/
AI pipeline Python 3.11, Earth Engine, numpy, ONNX, EZKL 23.0.5, dilithium-py (ML-DSA-65) ai/
Backend FastAPI, SQLAlchemy + Alembic (Neon Postgres), web3.py indexer, oracle + finalizer workers, content-addressed blob store, webhooks, MCP (FastMCP) backend/
Web Next.js 16, wagmi/viem, RainbowKit, MapLibre, GSAP + Lenis web/
Ops Vercel, Render (Docker), Neon, GitHub Actions CI + release workflow, ghcr.io image .github/, render.yaml

Details: docs/ARCHITECTURE.md · feeds, certificates and monitoring: docs/FEATURES.md · runbook: docs/DEPLOYMENT.md.

Quick start

# prerequisites: foundry, uv, node 22 + pnpm, python 3.11 (uv downloads it)
make setup          # venv + forge deps + pnpm install
make zk-setup       # EZKL circuit, keys, SRS, Solidity verifier (one-off, ~1 min + SRS download)
make chain          # terminal 1: anvil
make deploy         # terminal 2: Halo2Verifier + full stack → contracts/deployments/31337.json
make seed           # register the pilot areas (one overlapping parcel is rejected on purpose)
cd backend && ../.venv/bin/uvicorn app.main:app --reload &     # API + MCP on :8000
cd backend && ../.venv/bin/python -m app.indexer.run &         # indexer
cd backend && ../.venv/bin/python -m app.workers.scheduler &   # finalizer loop
cd web && pnpm dev                                             # http://localhost:3000
make submit P=1 Y=2025   # oracle: pipeline → proof → ML-DSA sign → submitScore
make e2e                 # the whole story on a local chain, every step asserted
make test                # forge test + pytest (ai, backend) + vitest/lint/build (web)

Copy .env.example to .env. With EE_SERVICE_ACCOUNT_JSON and EE_PROJECT the pipeline uses live Earth Engine data; without them it uses labelled fixture samples (see docs/LIMITATIONS.md).

Honesty: what is and is not proven

Claim Covered by
The registered model was used, unmodified zk proof (fixed params; verifier bound to the circuit) + ModelRegistry
The score follows from the committed features zk proof (Poseidon commitment of the 16 features is a public instance)
The features came from the stated satellite data not the zk proof → reproducible manifest + challenge game
The scoring rule is scientifically right not cryptography → open methodology + governance
Element Quantum-safe?
Manifest attestations and auditor reports (ML-DSA-65) ✅
Hash commitments (SHA-256 / keccak256) ✅ with sufficient output size
Ethereum transaction signatures (ECDSA) ❌ ecosystem dependency (PQ account abstraction)
zk proofs (Halo2 + KZG) ❌ pairing-based (hash-based STARK proofs on the roadmap)

Repository

ai/          Earth Engine extraction, matching, fv1 features, MLP, EZKL circuit, ML-DSA-65
backend/     FastAPI API, indexer, oracle/finalizer workers, MCP server, Alembic migrations
contracts/   Foundry project, tests, deployment scripts, generated Halo2Verifier
web/         Next.js app
scripts/     deploy, seed, e2e, benchmarks, media
docs/        architecture, benchmarks, limitations, MCP, features, submission kit, launch film
brand/       logo and brand guide

Contributing: CONTRIBUTING.md · security: SECURITY.md · changes: CHANGELOG.md.

References

Data attributions

Hansen/UMD Global Forest Change, ESA Copernicus Sentinel-1/2, NASA GEDI L4B, NASA FIRMS and MODIS MCD64A1, SRTM, ESA WorldCover, Google Dynamic World (via Earth Engine). Basemap © OpenStreetMap contributors.

Licence

MIT — see LICENSE. Built for the IEEE ClimateChain Global Hackathon 2026, Carbon Markets & Emissions Transparency track. Testnet only; nothing here assesses any real carbon project.

Built With

  • carbon-credit
  • earth-engine
  • mcp
  • post-quantum
  • sattelite-imaginary
Share this project:

Updates

Submission history