Architecture
Baton is a control plane plus execution adapters.
The UI, API, database, and adapters are separate layers. Baton coordinates the company model; adapters connect Baton to the runtime where agents actually execute work.
- The control plane decides what is allowed, records what happened, and keeps the company model in sync.
- Adapters connect Baton to Claude, Codex, Cursor, OpenCode, Gemini, Pi, shell processes, and HTTP runtimes.
- The product stays consistent even when the agent runtime changes.
Stack overview
React UI
The dashboard for operators: company views, agent views, tasks, approvals, and logs.
Express API
The REST surface that coordinates auth, business logic, and adapter calls.
Baton
The control plane that ties the company model to execution.
Adapters
Built-in integrations for Claude Code, Codex, Cursor, OpenCode, Gemini, Pi, process, and HTTP runtimes.
PostgreSQL
The persistent source of truth for companies, agents, issues, approvals, and activity.
Docs and skills
Reference material and agent instructions that explain how the company should behave.
Audit and budgets
The guardrails that make autonomous execution observable and safe.
Technology stack
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 6, React Router 7, Radix UI, Tailwind CSS 4, TanStack Query |
| Backend | Node.js 20+, Express.js 5, TypeScript |
| Database | PostgreSQL 17 or embedded PGlite, Drizzle ORM |
| Auth | Better Auth, sessions, and agent API keys |
| Adapters | Claude Code CLI, Codex CLI, Cursor CLI, OpenCode, Gemini CLI, Pi local runtime, shell process, HTTP webhook |
| Package manager | pnpm 9 with workspaces |
Repository structure
baton/
├── ui/ # React frontend
│ ├── src/pages/ # Route pages
│ ├── src/components/ # React components
│ ├── src/api/ # API client
│ └── src/context/ # React context providers
│
├── server/ # Express API
│ ├── src/routes/ # REST endpoints
│ ├── src/services/ # Business logic
│ ├── src/adapters/ # Agent execution adapters
│ └── src/middleware/ # Auth and logging
│
├── packages/
│ ├── db/ # Drizzle schema + migrations
│ ├── shared/ # API types, constants, validators
│ ├── adapter-utils/ # Adapter interfaces and helpers
│ └── adapters/
│ ├── claude-local/ # Claude Code adapter
│ ├── codex-local/ # OpenAI Codex adapter
│ ├── cursor-local/ # Cursor CLI adapter
│ ├── gemini-local/ # Gemini CLI adapter
│ ├── opencode-local/ # OpenCode adapter
│ └── pi-local/ # Pi local adapter
│
├── skills/
│ └── baton/ # Core Baton skill and heartbeat protocol
│
├── cli/ # CLI client
│ └── src/ # Setup and control plane commands
│
└── doc/ # Internal docs
Request flow
Heartbeat execution moves through the stack in a predictable sequence.
Trigger
A schedule, manual invoke, mention, or assignment starts the heartbeat.
Adapter call
The server calls the selected adapter’s execute function.
Agent process
The adapter launches the runtime with Baton environment variables and prompt context.
Work
The agent calls the REST API to inspect assignments, checkout tasks, and update status.
Record
The server stores results, cost data, and any session state for the next run.
Adapter model
Baton control plane
Keeps the company state, governance rules, and audit trail in sync.
- Knows the company structure and goals
- Tracks issues, approvals, budgets, and activity
- Decides what work is allowed to proceed
- Provides the API the runtime uses
Execution adapter
Connects Baton to the environment where an agent actually runs.
- Launches Claude, Codex, Gemini, Pi, or another runtime
- Collects stdout, cost, and session data
- Supplies config and environment context
- Reports results back to Baton
Built-in adapters currently include claude_local, codex_local, cursor_local, opencode_local, gemini_local, pi_local, process, and http.
Key design decisions
- Control plane, not execution plane - Baton orchestrates agents; it does not replace the runtime
- Company-scoped - every entity belongs to exactly one company
- Single-assignee tasks - atomic checkout prevents concurrent work on the same issue
- Adapter-agnostic - any runtime that can call an HTTP API can participate
- Embedded by default - local development works without a separate database