architecture
How ebb-ai is built.
A small, deliberately scoped system. One scheduler. One MCP surface. Grid feeds in front, provider adapters behind, carbon receipts in between.
System diagram
Where each component sits and how the data flows.
The MCP server is a thin protocol surface in front of the scheduler. The scheduler core does the work and is the same implementation used by the in-process TypeScript library and the Python port (same algorithms, same receipt schema, two host languages).
Data sources
Grid intensity feeds come from government / TSO authorities — the same sources that ISO/RTO grid operators and DOE energy-usage studies cite. ebb-ai does no data collection of its own; it routes dispatch decisions through public-sector telemetry.
- U.S. Energy Information Administration (EIA). Hourly carbon-intensity for the six major US ISOs: CAISO, ERCOT, ISO-NE, PJM, NY-ISO, MISO. Free, key-required.
- UK National Grid ESO Carbon Intensity API. Key-less. Always live for GB region. The reference for UK demand-response programs.
- ENTSO-E Transparency Platform. European zones (FR, DE, ES, IT, NL). Operated by European Network of Transmission System Operators for Electricity. Free, key-required.
- Electricity Maps. Universal fallback for any other zone. Free tier sufficient for a single-user dashboard.
Every per-task carbon receipt carries the exact intensity value (and source identifier) used at scoring time, so the audit trail is reproducible — operators can later verify any individual dispatch against the public data feed it relied on.
Data flow, end to end
- Ingest. A task arrives via
defer(task, deadline)(library) orschedule_task(prompt, deadline)(MCP). The task body is captured (closure, JSON-serialized request, or a provider-adapter call descriptor) and persisted to SQLite with a UUID task_id. For non-committal planning — when the agent wants to see when the cleanest window falls before deciding whether to commit — there'srecommend_window(deadline, region), which returns the recommended window, top-3 alternatives, and a savings-vs-now number without ever touching the queue. - Validate. Deadline must parse as ISO-8601 and be in the future. If not, the call fails fast with
InvalidDeadlineError. A taskId, if caller-supplied, must be unique. - Forecast. The scheduler asks the configured grid feed for the next N hours of carbon intensity for the task's region. With an Electricity Maps API key, this hits the live API (5-second timeout). Without one, the feed falls back to a deterministic UTC-based sinusoidal mock so the whole stack still runs end-to-end.
- Score. Each hour inside the deadline window is scored. The cheapest-carbon window wins. If a
carbonBudgetGwas supplied, windows that exceed the budget are dropped first; if no window survives, the task fails withCarbonBudgetExceededError. - Wait. A timer is set for the chosen window. Long horizons are chained to avoid Node's 32-bit-millisecond
setTimeoutoverflow. - Dispatch. When the timer fires, the scheduler calls the task body. If the body is a provider-adapter descriptor (Anthropic / OpenAI), and the deadline is more than 24 hours out, the adapter routes through the provider's Batch API for the 50% discount. Otherwise it uses the standard sync endpoint.
- Receipt. The dispatch writes a carbon receipt: timestamp, region, intensity (g CO₂/kWh) from the forecast entry used to score the window (not a freshly-fetched value — the audit trail is honest about what was scored), tokens in/out, duration, dollar cost saved versus peak.
- Return. The result resolves the caller's promise (library) or becomes visible via
check_queue_status(task_id)(MCP). The receipt persists.
What's deliberately not in v0.8
- Cross-provider routing. ebb-ai picks the cheapest carbon window inside one provider. It doesn't yet weigh "Anthropic vs OpenAI vs Gemini" on a per-task basis. Planned for v0.9+ once the per-provider adapters have stabilized.
- WattTime marginal-emissions feed. The honest climate signal for time-shifting is marginal emissions, not average. Electricity Maps / EIA / ENTSO-E give average intensity today; WattTime integration is queued for v0.9.
- Local hardware routing. "If your laptop is plugged in and the grid is clean, use Ollama" is a real product idea, deferred to v0.9+.
- Multi-region failover. If your default region's grid is unusually dirty for a sustained window, ebb-ai could route through a different provider region. v0.9+.
- Global aggregate leaderboard. Per-user impact is local-only today (privacy-by-default). A signed opt-in telemetry endpoint and aggregate counters are designed for v0.9 — see
docs/spec/proposal/v09-leaderboard.md.