AgentPlane Control
The shared control plane for agent traces and spend.
Chronicle and TokenOps talk HTTP. Only this process owns SQLite. Agents never open the database file.
One UI, one SQLite: budgets and policies on the left, a run halted in-path by progress_guard on the right. Chronicle traces live in the next tab.
AgentPlane Control is the HTTP plane Chronicle and TokenOps share. Sidecars post envelopes and register runs; the plane stores them; Admin, Chronicle, and TokenOps tabs read the same file. No NFS, no two processes fighting over WAL, no "which DB did that agent open."
Why · Architecture · Install · Quick start · Sidecars · Comparison · Design
Why AgentPlane Control
- SQLite lives here. Nowhere else. Chronicle and TokenOps are HTTP clients. They never receive a DB path.
- One pane for both products. Admin keys, Chronicle waterfalls, TokenOps budgets / policies / breaches — same process, same file.
- Ingest is a contract, not a dump. Chronicle writes
POST /v1/envelopes:batch(batch_size=1= flush now) and replaysGET /v1/traces/{id}/envelopes. TokenOps registers runs, ledger, and governance over/v1/*. - Auth when you need it. Empty key table = local anonymous (all scopes, tenant
local). Create sidecar keys in Admin, or seedCONTROL_PLANE_API_KEYS. - pip in, serve, done. No Postgres, no login screen, no separate dashboard server.
Architecture
The plane is one FastAPI process. Agents stay agents.
flowchart LR
subgraph AGENTS["Agent processes"]
C["Chronicle sidecar<br/>RemoteStore"]
T["TokenOps sidecar<br/>HttpStore"]
end
subgraph PLANE["Control plane (:8800)"]
API["HTTP /v1"]
UI["Admin · Chronicle · TokenOps"]
DB[("SQLite CONTROL_PLANE_DB")]
API --> DB
UI --> API
end
C -->|"POST /v1/envelopes:batch"| API
C -->|"GET /v1/traces/{id}/envelopes"| API
T -->|"POST /v1/runs · ledger · governance"| API
| Piece | Owns | Does not own |
|---|---|---|
This plane (control-plane serve) |
SQLite, HTTP API, HTML UI | Agent loops, LLM calls, record-and-replay, in-path halt |
| Chronicle sidecar | Boundaries, envelopes, fixtures | The database file |
| TokenOps sidecar | tokenops_run, wrap_complete, ledger client |
The database file |
Design notes: docs/DESIGN.md.
Chronicle tab: newest traces first. Click a row for the time-based waterfall.
Install
pip install agentplane-control-plane
Prerequisites: Python 3.10+. Sidecars are separate packages: agent-chronicle>=0.4.0, agent-tokenops>=0.2.0.
PyPI name is agentplane-control-plane; import is control_plane; CLI is control-plane.
See RELEASING.md for releases.
From a clone:
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
Quick start
control-plane start # runs in the background; safe to re-run (no-op if already up)
control-plane status # pid, port, version, /health
control-plane stop
Open http://127.0.0.1:8800/ — Admin, Chronicle, TokenOps. No login.
One managed instance per machine user; start/stop/status track it via a small
state file (platformdirs user-state dir) and a PID — stop never touches a process
it didn't start. Logs go to a file next to the state (path printed by start), since
the process is detached from your terminal.
Prefer the foreground, un-managed form for scripting, containers, or when you want Ctrl-C to stop it:
control-plane serve --port 8800 --db control_plane.db
control-plane ui is a pointer, not a second server: the HTML UI is served with the API.
Docker
For hosting (a shared team plane, a demo environment) rather than a local dev loop:
docker compose up -d # builds the image, starts on :8800, persists SQLite in a volume
docker compose logs -f
docker compose down # add -v to also drop the data volume
Or without Compose:
docker build -t agentplane-control-plane .
docker run -d --name control-plane -p 8800:8800 -v control-plane-data:/data agentplane-control-plane
The image's entrypoint is control-plane serve (foreground, PID 1) — not start.
Docker/Kubernetes is already the process supervisor here (restart policy, health
checks via the built-in HEALTHCHECK hitting /health, log collection from
stdout/stderr); start/stop/status are for running the plane directly on a
developer's machine, where nothing else is supervising the process. Don't run them
inside the container — a background/detached mode would exit PID 1 as soon as it
spawned its child, and the container would exit with it.
Set CONTROL_PLANE_API_KEYS before exposing the container beyond localhost — the
default (empty) is anonymous, all-scopes access.
Sidecars
Point both libraries at the same origin. Do not set a SQLite path on the agents.
export CONTROL_PLANE_URL=http://127.0.0.1:8800
# TokenOps also honors TOKENOPS_URL; leave TOKENOPS_EMBEDDED unset
Chronicle — flush every envelope to the plane:
import os
import chronicle
from chronicle import RemoteStore
store = RemoteStore(os.environ["CONTROL_PLANE_URL"], batch_size=1)
with chronicle.record("my-run", store=store):
...
TokenOps — register runs and share the ledger over HTTP:
from tokenops import ControlPlaneClient, tokenops_run
client = ControlPlaneClient.from_env() # CONTROL_PLANE_URL or TOKENOPS_URL
with tokenops_run(client=client) as bound:
...
Auth off until you create a key. Then:
export CONTROL_PLANE_API_KEYS='chron:local:ingest+read,tops:local:ingest+read,admin:local:read+admin'
export CONTROL_PLANE_API_KEY=chron # Chronicle / TokenOps sidecar
Or create keys in the Admin tab (secret shown once).
API callers
Every route has one caller class. Agents do not scrape the UI.
| Route | Caller |
|---|---|
POST /v1/envelopes:batch |
Chronicle sidecar (only ingest API) |
GET /v1/traces/{id}/envelopes |
Chronicle sidecar (fixture replay) and Chronicle waterfall |
GET /v1/traces |
Chronicle UI search |
POST /v1/runs, ledger, governance, run-records |
TokenOps sidecar |
| segments / budgets / policies, admin keys | UI |
How it compares
This is not a gateway, not a SaaS, and not a replacement for Chronicle or TokenOps. It is the shared store + UI those two already assume.
| AgentPlane Control | TokenOps embedded SQLite | Langfuse / Phoenix | |
|---|---|---|---|
| Primary focus | Shared plane (traces + spend) | Governance in one process | Observe / traces |
| Agents open the DB file | No | Yes (same TOKENOPS_DB) |
N/A (hosted or collector) |
| Chronicle + TokenOps one UI | Yes | TokenOps UI only | No |
| In-path halt / mutate | Via TokenOps sidecar | Yes | No (analytics) |
| Record-and-replay fixtures | Via Chronicle sidecar | No | No |
| Requires hosted SaaS | No | No | Often |
What this does not do: call models, wrap complete, record boundaries, or host a multi-tenant cloud for you. Fail-closed auth is opt-in (create keys). The HTTP contract is still 0.x.
Environment variables
Command and env reference
| Variable | Purpose |
|---|---|
CONTROL_PLANE_DB |
SQLite path (or control-plane serve --db) |
CONTROL_PLANE_URL |
Sidecar base URL (http://127.0.0.1:8800) |
CONTROL_PLANE_API_KEYS |
Seed keys: name:tenant:scope+scope |
CONTROL_PLANE_API_KEY |
Bearer the sidecar sends |
CONTROL_PLANE_CONFIG |
Governance YAML seed (else packaged default.yaml) |
TOKENOPS_URL |
TokenOps alias for the same origin |
TOKENOPS_API_KEY |
TokenOps alias for the Bearer |
TOKENOPS_EMBEDDED |
Must be unset when using this plane |
control-plane serve [--host 127.0.0.1] [--port 8800] [--db PATH] [--reload]
Roadmap
The plane is early (0.x). Near-term:
- Harden SQLite under concurrent sidecar writes (
busy_timeout, WAL discipline). - Admin: persist the browser key so a refresh does not 401.
- Keep the HTTP contract stable enough for Chronicle 0.4 and TokenOps 0.2.
Ideas welcome via GitHub issues.
Compatibility
| agentplane-control-plane | tokenops | agent-chronicle | notes |
|---|---|---|---|
| 0.1.x | ≤ 0.2.1 | ≥ 0.3.0 | single-op /v1/ledger/*, PUT /v1/run-records |
| 0.2.x | ≤ 0.2.1 and <next> |
≥ 0.3.0 | additive — old clients keep working; adds precheck / events:batch, run_state, data_scope |
| 0.3.x | <next>+ only |
≥ 0.3.0 | breaking — drops run_registrations, PUT /v1/run-records, legacy ledger wrappers |
Breaking changes
- 0.2.0 → 0.3.0:
run_registrationsfolded intorunsand dropped;PUT /v1/run-records(create_run) removed;PATCH /v1/run-recordsrejectssteps/cost_micros(0.2.x only ignores them); legacy/v1/ledger/halt/*and single-op/v1/ledger/{spent,inflight}/*writes removed. Runs as an automaticPRAGMA user_versionv3 migration. Released only aftertokenops <next>stops callingcreate_run.
Full contract: docs/api-contract.md.
Documentation
- API contract — the TokenOps ⇄ control-plane wire spec
- Design — storage, callers, scopes, keys
- Releasing — Trusted Publishing to PyPI
- Changelog
- Chronicle · TokenOps
Contributing
Issues and PRs are welcome.
pip install -e ".[dev]"
ruff check control_plane tests
pytest -v
Contributors
Thanks to everyone who has contributed.
If this is the missing box between your agents and the database, please ⭐ star the repo so more people can find it.
Built by Susheem Koul and Tisha Chawla
Metadata
Release files for agentplane-control-plane 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agentplane_control_plane-0.2.0.tar.gz | 563.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agentplane_control_plane-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 736.9 kB
Release files / agentplane_control_plane-0.2.0.tar.gz
| Download URL | agentplane_control_plane-0.2.0.tar.gz |
|---|---|
| Size | 563.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
75583439b1a2a89221464f62f3c63c4b20fb61a9029b11786659aa1bbe52f37a
|
|
BLAKE2b-256 checksum How to use checksums |
f73643ad9e539ce15c6993812fa1ad30a284a44e32902cd6017d20c31b65399b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency logRelease files / agentplane_control_plane-0.2.0-py3-none-any.whl
| Download URL | agentplane_control_plane-0.2.0-py3-none-any.whl |
|---|---|
| Size | 173.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5820b966766f0e4e261b5fdc680a02b4af2a5c30e93dc75efc64304e25218330
|
|
BLAKE2b-256 checksum How to use checksums |
fca0ed5afb68975f87524a7d2c8f5ec8d27ce61dae32d50e45efd6095fb58f8a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency log