API (@tedos/api)
The API is the Fastify backend for TED OS — the server-side home for the product endpoints, the
Plan Engine API, ingress webhooks, and the MCP server. Stack: Fastify + Drizzle + Postgres +
pg-boss (ADR-001). Deployed to Fly as the app tedos-api.
What it exposes
| Surface | Routes | Notes |
|---|---|---|
| Product API | /projects, /p/:id/* | Behind the Clerk auth/tenancy guard. See Product SoR |
| Engine | /engine/* | The last-mile engine — goals / jobs / decision. See Engine |
| Plan Engine | /plan/* | The dev-workflow graph API + 9 plan_* MCP tools. See Plan Engine |
| Webhooks | /webhooks/github | HMAC-verified ingress (GitHub → graph; the ingestion pattern) |
| Health | /healthz | Used by Fly health checks |
Process split (web + worker)
One image, two Fly process groups:
| Process | Runs | Public HTTP |
|---|---|---|
web | Fastify (the routes above) | yes |
worker | the pg-boss handler + cron (mirror jobs, refresh jobs) | no |
The web process starts pg-boss only to enqueue (the webhook needs it); the worker process
owns the handler/cron — no double-registration. See Deploy topology.
Two databases (a hard rule)
The API touches two separate databases, never one:
| Connection | Database | Owner | Migrations |
|---|---|---|---|
| (dev-workflow DB env) | the dev-workflow / Plan Engine graph (+ pg-boss tables) | api + Plan Engine | apps/api/drizzle/ |
| (product DB env) | the product System of Record (product schema) | @tedos/domain | packages/domain/drizzle/ |
A single release command runs both migration sets before any new machine serves traffic, and aborts (fail-closed) if either database's connection env var is unset. The two databases are logically separate — tenant/product data must never enter the graph DB. See Decisions (ADR-006).
The API's configuration (database connections, Clerk keys, the model layer, the GitHub token) lives in environment variables. This documentation refers to those variables by name only and never includes their values.
The shared contract
The API and the admin both import the frozen @tedos/shared contract
(zod-as-source + inferred TypeScript types), so request/response shapes match by construction.
The model layer
The engine's model provider is selected by environment: a local model (Ollama), a hosted vLLM (OpenAI-compatible), or a deterministic mock with no network. See Engine.