Shift-left carbon analysis for Terraform — suggests lower-carbon cloud regions in pull requests.
Project description
🌱 CarbonGate
Shift-left carbon analysis for Terraform. Suggests lower-carbon cloud regions right in your Pull Requests.
CarbonGate is a deterministic, zero-token, zero-latency CLI tool that analyzes Terraform infrastructure diffs and recommends AWS regions with lower carbon intensity — before you merge.
The Sweet Spot
You give away the software (free, open-source, runs on their own CI runner). You sell the data (fresh Parquet feed with real-time carbon intensity from emissions.dev). No dashboards. No LLM hallucinations. Just a YAML file, a ruthless parser, a DuckDB engine faster than electricity, and a gate (the data) that you control.
For buyers & sales calls
One-page pitch (FR), demo script, pricing summary, objections: docs/SALES_ONE_PAGER.md · product sheet: docs/FICHE_PRODUIT.md.
For executives (FR)
IMPACT© methodology — audit framework, ROI ranges, CSRD scope, 3× fee-back guarantee.
IMPACT Pre-Scan — qualification 1 repo + call 30 min → livrable 1 page (gratuit sous conditions).
Case study #0 — public terraform-aws-vpc demo (~86 % modeled carbon savings eu-west-1 → eu-north-1; not a paying client).
Trigger events playbook — CSRD / FinOps signals, CFO outreach, CHASE prioritization.
Executive audit template: gtm/templates/audit_report_exec.md · KPI Shield: CFO / CTO.
How It Works
Developer pushes Terraform on GitHub
│
▼
GitHub Action triggers (ubuntu-latest)
│
├── 1. Parse diff → extract AWS regions (regex, <0.1s)
├── 2. Query DuckDB over Parquet → find lowest-carbon alternative in same zone (0.01s)
├── 3. If savings ≥ 30%, format Markdown PR comment
└── 4. Post comment via GitHub API
No API call per PR run. The intelligence is baked into the Parquet file. Your compute cost: $0.00.
Quick Start
pip install carbongate
carbongate --diff-file path/to/pr.diff
carbongate --hcl-file main.tf --tfvars-file terraform.tfvars # optional
carbongate --plan-json plan.json # terraform plan -json
carbongate --plan-json plan.json --json # machine-readable output
Install with MCP support: pip install carbongate[mcp]
Agent-native usage (MCP)
CarbonGate exposes an MCP server for AI agents (Claude, Cursor, etc.):
carbongate serve
Configure your MCP client:
{
"mcpServers": {
"carbongate": {
"command": "carbongate",
"args": ["serve"]
}
}
}
Tools exposed:
| Tool | Input | Description |
|---|---|---|
| analyze_plan | plan_json_path |
Analyze a terraform plan -json file for carbon savings |
| analyze_diff | diff_path + tfvars_path? + threshold_pct? |
Analyze a git diff file for carbon savings |
| suggest_region | region |
Get the best lower-carbon alternative for an AWS region |
JSON output is versioned (schema_version: 1) for agent workflow stability.
JSON Schema available for automated validation.
MCP example: analyze a diff
# Agent calls via MCP — no shell required
analyze_diff(
diff_path="/path/to/terraform.diff",
tfvars_path="/path/to/terraform.tfvars", # optional: resolves var.region
threshold_pct=20.0 # optional: lower threshold
)
# → {"schema_version": 1, "suggestions": [{"current_region": "us-east-1", ...}]}
From source (dev):
git clone https://github.com/carbongate/carbongate.git && cd carbongate
pip install -e ".[dev]"
carbongate --diff-file tests/fixtures/sample.diff
Regenerate carbon data (optional): EMISSIONS_DEV_API_KEY=em_live_xxxx python data_packer.py
Output Example
## 🌱 CarbonGate Shift-Left Analysis
⚠️ **Optimization detected** for your infrastructure deployment:
- **Current Region:** `us-east-1` (Carbon: 322.0 gCO₂eq/kWh)
- **Suggested Region:** `us-west-2` (Carbon: 122.5 gCO₂eq/kWh)
💰 **Estimated Carbon Savings:** `62.0%`
📉 **Spot Price Multiplier:** `0.8x` (Lower is cheaper)
> 💡 **Action:** Consider updating your Terraform config to use the suggested
> region(s) and reduce both carbon footprint and cloud costs.
Architecture
carbongate/
├── data_packer.py # Polars → Fetches real CI from emissions.dev, writes Parquet
├── query_engine.py # DuckDB in-process: get_best_alternative(region) → dict
├── tf_parser.py # Deterministic regex + plan JSON: extract AWS regions
├── judge.py # Decision engine: ≥30% savings threshold, Markdown + JSON
├── carbongate_cli.py # Typer CLI entry point (carbongate analyze / serve)
├── mcp_server.py # MCP server for AI agent integration (3 tools)
├── entrypoint.sh # GitHub Action entrypoint (fetches diff via API)
├── Dockerfile # python:3.12-slim, ready for GitHub Actions
├── action.yml # GitHub Action definition
├── pyproject.toml # Package metadata (pip install .)
└── data/
├── eu/eu_carbon.parquet # EU region carbon data
└── us/us_carbon.parquet # US region carbon data
Module Dependency Graph
┌─────────────┐
│ carbongate_cli │ Typer CLI, GitHub API integration
└──────┬──────┘
│
┌──────▼──────┐
diff.txt ────────►│ judge.py │ Decision engine (≥30% threshold)
└──┬──────┬───┘
│ │
┌────────▼──┐ ┌─▼───────────┐
│ tf_parser │ │query_engine │ DuckDB over Parquet
└───────────┘ └──────┬───────┘
│
┌────────▼──────────┐
│ data_packer.py │ Polars → Parquet
│ emissions.dev │ Real CI data
└───────────────────┘
Data Flow
emissions.dev API (real CI data)
│
▼
data_packer.py ─── Polars DataFrame ───► data/{eu,us}/*.parquet
│
▼
query_engine.py ─── DuckDB read_parquet() ───► get_best_alternative()
│
▼
tf_parser.py ─── extract regions from diff ──► judge.py ──► Markdown PR comment
Region coverage
| Detected in PR diff | Notes |
|---|---|
Literal region = "eu-west-1" |
provider, resources, locals |
availability_zone |
Mapped to region when possible |
region = var.region |
Resolved if terraform.tfvars / --tfvars-file sets region or aws_region |
| Auto tfvars | terraform.tfvars, *.auto.tfvars, *.tfvars next to input file |
Not covered (yet): unresolved var.region without tfvars, GCP/Azure, hourly carbon scheduling.
See Infracost integration, PyPI publish checklist, and sales one-pager (demo commands, pricing, objections).
Supported Regions
| AWS Region | Country | Data Source | Best EU/US pair (feed) | Typical savings |
|---|---|---|---|---|
eu-west-1 |
Ireland | emissions.dev (IE) | → eu-north-1 |
~86% |
eu-west-2 |
United Kingdom | emissions.dev (GB) | → eu-north-1 |
~84% |
eu-west-3 |
France | emissions.dev (FR) | → eu-north-1 |
~17% (below threshold) |
eu-central-1 |
Germany | emissions.dev (DE) | → eu-north-1 |
~90% |
eu-north-1 |
Sweden | emissions.dev (SE) | already optimal | — |
us-east-1 |
Virginia | eGRID 2023 (×0.92) | → us-west-2 |
~62% |
us-east-2 |
Ohio | eGRID 2023 (×1.25) | → us-west-2 |
~72% |
us-west-1 |
California | eGRID 2023 (×0.55) | → us-west-2 |
~36% |
us-west-2 |
Oregon | eGRID 2023 (×0.35) | already optimal | — |
Savings are vs the lowest-carbon region in the same zone (EU or US), using the bundled Parquet feed. See query_engine.get_best_alternative().
30% savings threshold
CarbonGate only posts a PR comment when estimated carbon savings are ≥ 30% (SAVINGS_THRESHOLD_PCT in judge.py, overridable with --threshold). This avoids noisy suggestions for marginal gains.
Silent output is expected, not a bug, when:
- The region is already near-optimal in its zone (e.g.
eu-west-3at ~42 gCO₂eq/kWh vseu-north-1at ~35 → ~17% savings, below threshold). - No resolvable region in the diff (e.g.
var.regionwithout tfvars, or only removed lines).
Example — eu-west-3 (no suggestion):
carbongate --diff-file tests/fixtures/eu-west-3-only.diff
🔍 Analyzing diff for carbon optimization opportunities...
✅ No carbon optimization detected (all regions within 30% of optimal).
Example — eu-west-1 (suggestion emitted):
carbongate --diff-file gtm/audits/terraform-aws-vpc/audit_input.diff
## 🌱 CarbonGate Shift-Left Analysis
...
💰 **Estimated Carbon Savings:** `86.4%`
CLI options (Phase 0)
| Flag | Description |
|---|---|
analyze --diff-file |
Git diff from PR |
analyze --hcl-file |
Static .tf scan (synthetic diff) |
analyze --plan-json |
terraform plan -json / terraform show -json output |
analyze --tfvars-file |
Explicit tfvars (repeatable); also auto-discovered |
analyze --threshold |
Min savings % to comment (default 30) |
analyze --api-key |
Fresh Parquet from emissions.dev |
analyze --json |
Machine-readable JSON output (all input modes) |
serve |
Start MCP server for AI agent integration |
Configuration
| Variable | Required | Description |
|---|---|---|
EMISSIONS_DEV_API_KEY |
No | emissions.dev API key (free: 500 req/month). Without it, uses realistic mock data. |
Using the Action (published vs local)
Published (after release on GitHub):
- uses: carbongate/carbongate@v0.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
api-key: ${{ secrets.CARBONGATE_API_KEY }}
Same repository (development):
- uses: ./
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
See GitHub Marketplace checklist.
GitHub Action Setup
Add to .github/workflows/carbongate.yml (or use cost + carbon with Infracost):
name: CarbonGate Analysis
on:
pull_request:
paths:
- '**/*.tf'
- '**/*.tfvars'
jobs:
carbongate:
runs-on: ubuntu-latest
steps:
- uses: carbongate/carbongate@v0.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
api-key: ${{ secrets.CARBONGATE_API_KEY }}
Tech Stack
| Component | Technology | Why |
|---|---|---|
| Data processing | Polars | Zero-copy, columnar, faster than Pandas |
| Query engine | DuckDB | In-process OLAP, reads Parquet in <1ms |
| CLI | Typer | Type-safe Click wrapper |
| Data format | Apache Parquet | Compressed columnar, 2MB per zone |
| Data source | emissions.dev | Free tier, annual CI per country + cloud region |
| Packaging | Docker / pip | GitHub Action + PyPI ready (85 pytest) |
Performance
- Parse diff: <0.1s (regex)
- Query DuckDB: <0.01s (in-memory Parquet)
- End-to-end: <0.2s
- API calls per PR run: 0 (data pre-baked in Parquet)
- Compute cost per PR: $0.00
License
MIT
CarbonGate — Analyse Carbone Shift-Left pour Terraform
🇫🇷 Français
CarbonGate est un outil CLI déterministe, zéro-token, zéro-latence qui analyse les diffs d'infrastructure Terraform et recommande des régions AWS avec une intensité carbone plus faible — avant le merge.
Le Sweet Spot
Tu donnes le logiciel (gratuit, open-source, tourne sur leur propre runner CI). Tu vends la donnée (feed Parquet frais avec l'intensité carbone temps réel d'emissions.dev). Pas de dashboards. Pas d'hallucinations LLM. Juste un fichier YAML, un parser impitoyable, un moteur DuckDB plus rapide que l'électricité, et une porte d'entrée (la donnée) que tu contrôles.
Comment ça marche
Le développeur pousse du Terraform sur GitHub
│
▼
GitHub Action se déclenche (ubuntu-latest)
│
├── 1. Parse le diff → extrait les régions AWS (regex, <0.1s)
├── 2. Interroge DuckDB sur le Parquet → trouve l'alternative la moins carbonée dans la même zone (0.01s)
├── 3. Si économies ≥ 30%, formate un commentaire Markdown PR
└── 4. Poste le commentaire via l'API GitHub
Aucun appel API par exécution de PR. L'intelligence est pré-cuite dans le fichier Parquet. Ton coût compute : 0,00 €.
Démarrage Rapide
git clone https://github.com/carbongate/carbongate.git
cd carbongate
pip install duckdb polars requests typer
export EMISSIONS_DEV_API_KEY=em_live_xxxx # Gratuit sur https://emissions.dev
python data_packer.py
carbongate --diff-file chemin/vers/terraform.diff
Architecture
carbongate/
├── data_packer.py # Polars → Récupère CI réelle d'emissions.dev, écrit Parquet
├── query_engine.py # DuckDB in-process : get_best_alternative(region) → dict
├── tf_parser.py # Regex déterministe : extrait les régions AWS du diff git
├── judge.py # Moteur de décision : seuil ≥30%, formatage Markdown
├── carbongate_cli.py # Point d'entrée CLI Typer (analyze / serve)
├── mcp_server.py # Serveur MCP pour intégration agent IA (3 tools)
├── entrypoint.sh # Entrypoint GitHub Action
├── Dockerfile # python:3.12-slim, prêt pour GitHub Actions
├── action.yml # Définition GitHub Action
├── pyproject.toml # Métadonnées package (pip install .)
└── data/
├── eu/eu_carbon.parquet
└── us/us_carbon.parquet
Régions supportées
| Région AWS | Pays | Source données | Meilleur pair (feed) | Économie typique |
|---|---|---|---|---|
eu-west-1 |
Irlande | emissions.dev (IE) | → eu-north-1 |
~86 % |
eu-west-2 |
Royaume-Uni | emissions.dev (GB) | → eu-north-1 |
~84 % |
eu-west-3 |
France | emissions.dev (FR) | → eu-north-1 |
~17 % (sous seuil) |
eu-central-1 |
Allemagne | emissions.dev (DE) | → eu-north-1 |
~90 % |
eu-north-1 |
Suède | emissions.dev (SE) | déjà optimal | — |
us-east-1 |
Virginie | eGRID 2023 (×0.92) | → us-west-2 |
~62 % |
us-east-2 |
Ohio | eGRID 2023 (×1.25) | → us-west-2 |
~72 % |
us-west-1 |
Californie | eGRID 2023 (×0.55) | → us-west-2 |
~36 % |
us-west-2 |
Oregon | eGRID 2023 (×0.35) | déjà optimal | — |
Seuil 30 %
CarbonGate ne commente une PR que si l'économie carbone estimée est ≥ 30 % (SAVINGS_THRESHOLD_PCT dans judge.py). Exemple : eu-west-3 (~42 gCO₂eq/kWh) vs eu-north-1 (~35) → ~17 % → sortie silencieuse (« No carbon optimization detected »), pas un bug.
AI / LLM / Agent Usage
For AI Agents (Claude Code, Cursor, Copilot, etc.)
CarbonGate is designed to be operated by AI agents as well as humans. The codebase follows these agent-friendly conventions:
- Single-file modules: each
.pyfile is a self-contained module with a clear single responsibility - Type-safe: all public functions use
TypedDictor explicit return types - Deterministic: zero LLM calls in the pipeline; everything is regex + SQL
- Silent on success: functions return
Nonewhen there's nothing to report; no spam - Idempotent:
data_packer.pycan be re-run safely;query_engine.pyreads latest data - Mock fallback: works without API keys for dev/testing
For LLM Context Windows
When an AI agent reads this project, the optimal files to load are:
| Priority | File | Purpose |
|---|---|---|
| 1 | README.md |
This file — architecture overview |
| 2 | pyproject.toml |
Dependencies and entry points |
| 3 | cli.py |
Understand the CLI surface |
| 4 | judge.py |
Decision logic + Markdown format |
| 5 | query_engine.py |
DuckDB query patterns |
| 6 | tf_parser.py |
Region extraction regex |
| 7 | data_packer.py |
Data pipeline (only if modifying data sources) |
llms.txt
# CarbonGate — Shift-left carbon analysis for Terraform
# Architecture (7 files, 1019 lines)
## Core Pipeline
carbongate_cli.py: Typer CLI (--diff-file, --hcl-file, --plan-json, --json, --threshold, serve)
judge.py: Decision engine (≥30% savings threshold), Markdown + JSON formatter
tf_parser.py: hcl2 + regex + plan JSON recursive; tfvars; var.region resolution
query_engine.py: DuckDB in-process query engine over Parquet carbon data
## Agent Integration
mcp_server.py: FastMCP server — 3 tools: analyze_plan, analyze_diff, suggest_region
Entry point: carbongate serve or carbongate-serve
JSON output: schema_version: 1 envelope
## Data Pipeline
data_packer.py: Polars-based ETL. Fetches real CI from emissions.dev API, falls back to mock data.
Reads EMISSIONS_DEV_API_KEY from env. Writes data/{eu,us}/*.parquet.
## Deployment
action.yml: GitHub Action definition (docker-based)
entrypoint.sh: Extracts PR diff via GitHub API, runs carbongate (supports INPUT_PLAN_JSON_FILE)
Dockerfile: python:3.12-slim with curl, jq, duckdb, polars
pyproject.toml: Package metadata, dependencies, entry points (carbongate, carbongate-serve)
## Data
data/eu/eu_carbon.parquet: EU region carbon intensity (5 regions × latest timestamp)
data/us/us_carbon.parquet: US region carbon intensity (4 regions × latest timestamp)
## External APIs
emissions.dev /v1/electricity/grid: country-level grid carbon intensity (free tier: 500 req/month)
GitHub REST API: POST /repos/{owner}/{repo}/issues/{pr_number}/comments
## Key Design Decisions
- Annual average CI (not real-time): structural advantage matters more than hourly fluctuations for infra decisions
- No LLM calls in pipeline: purely deterministic (regex + SQL)
- Data pre-baked as Parquet: zero API calls per PR run, $0 compute cost for user
- AWS region → country mapping: static table, no runtime lookups
- 30% savings threshold: avoids noisy suggestions for marginal improvements
Competitor Landscape (2026)
| Tool | Position | CarbonGate Advantage |
|---|---|---|
| Infracost + InfraCarbon | PR comments, closed backend | Open-source CLI, Parquet data feed, no dashboard lock-in |
| CarbonAware | Scheduling, K8s/Airflow | Pre-merge analysis, not post-deploy |
| Cloud Carbon Footprint | Post-deploy reporting dashboard | Shift-left: catch it before merge |
| Climatiq | General carbon API | Deprecated cloud endpoint (Sep 2026) |
Built with ❤️ and DuckDB.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file carbongate-0.5.4.tar.gz.
File metadata
- Download URL: carbongate-0.5.4.tar.gz
- Upload date:
- Size: 36.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97c71f88d240a201a150b8915d1566b2cb87a4c641cc5ed672833d162941f8ab
|
|
| MD5 |
639eb798aa43cbf82a7c17cfd99e524c
|
|
| BLAKE2b-256 |
cd71fd41ec77cc47a487ea20aa901a467b001e8c792e96a99979f76689fa3458
|
File details
Details for the file carbongate-0.5.4-py3-none-any.whl.
File metadata
- Download URL: carbongate-0.5.4-py3-none-any.whl
- Upload date:
- Size: 40.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f88355b90849a002f380563e09878e32fb1c731b5f305f7e83234b5a4dc743c
|
|
| MD5 |
3f1fe5fa98454a1907933c93fb0c78ee
|
|
| BLAKE2b-256 |
314e27a4aa1ca08d8f48ab1d21a8f75d854f184e0aa362e54a41621e4944e2dc
|