# Architecture

Ozma splits **control plane** (discovery, auth, billing, provider ops) from **data plane** (proxied API calls). Both run on Cloudflare Workers; state lives in Neon Postgres with Drizzle ORM.

## Planes

| Component | URL | Role |
|-----------|-----|------|
| API worker | `https://api.ozma.app` | Catalog, keys, billing webhooks, provider CRUD, merit crons |
| Gateway worker | `https://gateway.ozma.app` | Auth, spend counters, credential injection, proxy, usage enqueue |
| MCP worker | `https://mcp.ozma.app` | MCP tools + OAuth 2.1 |
| Web | `https://ozma.app` | Next.js + OpenNext Cloudflare Workers (SSR/ISR) marketplace, dashboard, docs |

## Gateway hot path

`ALL /v1/proxy/:apiSlug/*`:

1. Validate API key (`proxy:call`)
2. Resolve live endpoint + price
3. Check required provider terms (428 if missing)
4. SpendCounter DO — budget / daily / per-call
5. Decrypt upstream credentials (AES-GCM); inject into outbound request
6. Forward to provider `base_url`
7. On success — record `usage_events`, update key spend, queue metering
8. On failure — refund reservation

Stripe meter emission and Connect payouts run asynchronously via queues and scheduled crons on the API worker.

## Agent surfaces

REST is the source of truth. **`@ozma_app/sdk`**, **`@ozma_app/cli`**, and **MCP** wrap the same endpoints. The web app runs **OpenNext SSR/ISR** on Cloudflare Workers; live panels (market ticker, dashboard) poll the API from the browser. Site SEO sitemap is ISR at `/sitemap.xml`; the API also exposes `GET /v1/catalog/sitemap.xml` for machines.

Shared packages: `@ozma_app/core` (envelope, crypto), `@ozma/db`, `@ozma/merit`, `@ozma_app/sdk`.

## Merit & health

Cron jobs roll up provider stats, refresh leaderboards, compute OzmaScores, probe live API health, and finalize scheduled price changes. Suspended APIs are excluded from public catalog merit.

## Learn more

- [REST API reference](/docs/reference/api)
- [Gateway reference](/docs/reference/gateway)
- [Security model](/docs/concepts/security)
