Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Charter

Build and operate production-safe agents that run in your own environment.

The Charter console: the fleet with an agent parked on an approval, the decision waiting on a human, and the policy and run history behind it

Charter is an open-source platform for running AI agents in production. You define an agent and its policies in YAML. Charter runs it on your compute and governs it from a persistent control plane.

Why Charter

  • Durable execution. A run parks for a human and resumes days later on another worker, with its state intact.
  • Policy that acts. Set thresholds on the metrics an agent produces. One that crosses a threshold pauses, cools down, or rolls back to the version that worked.
  • Declared authority. The model sees only the tools you list, gated tools require human approval, and budgets cap what a task may spend.
  • Fleet operations. Every agent's operational state, run history, metrics and open decisions, from the CLI or the console.
  • Audit and traces. Every approval, rejection and policy action is recorded with who took it and why. Model and tool calls export as OpenTelemetry GenAI traces, which Jaeger, Tempo, Datadog and Langfuse can read.
  • Your network, your data. Workers run in your environment, so agents reach internal services and databases directly. Model keys and prompts never reach the control plane, and the agent's conversation, files and traces stay in stores you run. Self-host the control plane and use a local model to run Charter fully air-gapped.

DESIGN.md documents every field.

Status

Charter is pre-1.0, so the configuration format and CLI can still change between releases. Feedback is welcome.

Quickstart

Run your first agent in 5 minutes.

pip install boundflow-charter          # add [ui] for the console, [otel] for traces
pip install --pre boundflow-charter    # or whatever main is, published every green build

A control plane

Charter needs one to run agents against. To run one locally:

curl -sSLO https://raw.githubusercontent.com/boundflow/charter/main/deploy/local.compose.yml
docker compose -f local.compose.yml up -d --wait
docker compose -f local.compose.yml run --rm server -mode=provision -name=me

That prints an API key. With it:

export BOUNDFLOW_API_KEY=<the key it printed>
export BOUNDFLOW_SERVER_ADDRESS=http://localhost:50051
export BOUNDFLOW_WORKER_ADDRESS=http://localhost:50052
export CHARTER_STORE_URL=postgres://charter:charter@localhost:5434/charter
export ANTHROPIC_API_KEY=<your model key>   # or another provider's, see worker.yaml below

Remove it with docker compose -f local.compose.yml down -v. Cloning the repo works too, and gets you the examples alongside it.

For production you have two options. Run the BoundFlow backend yourself, following its deployment docs. Or use BoundFlow Cloud, which is managed and in early access (request access): it gives you an API key and the two addresses, and you export those instead of the local ones. The worker still runs wherever you put it, so CHARTER_STORE_URL stays yours.

Either way the control plane never sees your model key or its traffic.

Your first agent

charter init triage

That writes two files. triage/v1.yaml, the agent:

apiVersion: charter/v1
kind: AgentConfig

name: triage
version: 1
model: claude-haiku-4-5

objective: |
  Triage this support ticket and say what should happen to it:

  {{ inputs.ticket }}

inputs:
  ticket: { type: string, required: true }

response_format:
  category:
    type: string
    description: billing, bug, account, or other.
  next_step:
    type: string
    description: What a person should do about it, in one sentence.

and worker.yaml beside it, the deployment:

apiVersion: charter/v1
kind: Worker

control_plane:
  endpoint: ${BOUNDFLOW_SERVER_ADDRESS}
  worker_endpoint: ${BOUNDFLOW_WORKER_ADDRESS}
  api_key: ${BOUNDFLOW_API_KEY}
  tenant: default

llm:
  # Any provider LangChain can build. Name it here, put its key in the variable
  # below, and install its package: pip install 'boundflow-charter[openai]'
  provider: anthropic
  api_key: ${ANTHROPIC_API_KEY}

store:
  url: ${CHARTER_STORE_URL}

agents_dir: ./
serves:
  - agent: triage
    versions: [1]

It calls no tools and sets no budget. Both are optional, and the sections below add them.

Run it

charter tenant create default        # once per control plane
charter agent create triage          # prints an instance id
charter apply .                      # arm config and policy
charter worker .                     # leave this running, it is the process

Then, from another terminal, charter agents confirms it exists and carries the instance id every command needs:

AGENT   INSTANCE  VER  STATUS  ACTIVITY
triage  d9811374  v1   active  active

Run a task against it:

charter run triage --instance d9811374 --ticket "card declined twice, tried a new one"  # prints a task id
charter status <task-id>

status prints what the agent returned, in the shape response_format declared:

task      f683f822-d8f4-40a7-b528-8db1a576140c
outcome   successful
took      11s

inputs
  ticket   card declined twice, tried a new one

result
  category    billing
  next_step   Verify if the new card payment processed successfully and contact
              the customer to resolve any ongoing payment issues.

The console shows the same thing in a browser, for all agents:

charter ui

Approvals and policy

Tools can be gated on human approval. Behaviour is versioned, so adding one means writing a new version file:

mcp:
  - name: stripe
    url: https://mcp.stripe.com
    env: [STRIPE_API_KEY]
    tools:
      - tool: get_charge
      - tool: create_refund
        approval: always

Charter stops the task and shows a person the call it wants to make and the reasoning behind it:

charter approve apr_01J8Z --actor dana --reason "third dispute this month"

Nothing waits in your terminal. The task ends at the gate and resumes when someone answers, which can be days later on a different worker.

Limits are policy rather than behaviour, so they sit outside the version. runtime.yaml holds what one task may spend and what the agent may reach:

apiVersion: charter/v1
kind: RuntimePolicy
agent: triage

per_run:
  max_cost_usd: 0.50
  max_llm_calls: 20
  max_seconds: 300
  max_parallel_subagents: 3
  capability_call_limits:
    - { capability: write, max_calls: 10 }

limits:
  max_call_seconds: 60
  max_tool_seconds: 30

authority:
  allowed_capabilities: [read, write]
  approval_timeout_seconds: 3600

lifecycle.yaml acts on the agent over time. When a metric crosses a threshold the control plane can pause it, cool it down, or roll it back to an earlier version:

apiVersion: charter/v1
kind: LifecyclePolicy
agent: triage

rules:
  - when: { metric: num_failures, threshold: 3 }
    then: { pause: { window: 5 } }

  - when: { metric: approval_rejections, threshold: 2 }
    then: { cooldown: { window: 10, seconds: 3600 } }

  - when: { metric: cost, threshold: 2.00 }
    then: { set_version: { target: 1 } }

Both are re-applied on every charter apply, so a ceiling can be lowered without cutting a release.

Architecture

charter apply compiles your configuration into workflows and policy on the BoundFlow control plane. A Charter worker runs the agent in your environment and talks to your MCP servers with credentials that stay there. Each worker registers the agents it can run, listed under serves in its worker.yaml, and any worker registered for an agent can pick up its work. If the worker running a task crashes or stops, another continues it from its last checkpoint, and a run parked for a person resumes on whichever worker is free when they answer.

The agent loop itself is deepagents, so its tools, subagents, filesystem and skills work here unchanged. Charter makes that loop durable and governed: it checkpoints the run, turns the harness's interrupts into approvals a person can answer tomorrow, and holds it to the limits your config declares.

                     BoundFlow
                   Control Plane
            state • policy • lifecycle
                         │
                        RPC
             ┌───────────┴───────────┐
             ▼                       ▼
  ┌────────────────────┐  ┌────────────────────┐
  │ Charter worker     │  │ Charter worker     │
  │                    │  │                    │
  │ model ↔ agent loop │  │ model ↔ agent loop │
  │          │         │  │          │         │
  │      MCP tools     │  │      MCP tools     │
  └────────────────────┘  └────────────────────┘
                 Your environment

Charter adds no database or service of its own. Deployed agents keep running through their workers and the control plane whether or not the CLI is installed.

Documentation

  • DESIGN.md: every field of every file, and the decisions behind them
  • deploy/: running workers as containers, and a control plane locally
  • examples/: two agents over a toy support system. One gates a refund and pauses itself when too many are turned down, the other rolls itself back to an earlier version. They run with nothing but a model key

Development

python -m venv .venv
.venv/bin/pip install -e '.[dev,ui,otel]'
.venv/bin/pytest

boundflow comes from PyPI. Add --pre --upgrade boundflow to track its main, which is what CI's second unit job does.

End-to-end tests need a control plane, and skip themselves without one. The compose file CI uses runs the published image:

docker compose -f deploy/local.compose.yml up -d --wait
key=$(docker compose -f deploy/local.compose.yml run --rm server \
        -mode=provision -name=dev | awk '/^api_key/{print $NF}')

export BOUNDFLOW_API_KEY=$key
export BOUNDFLOW_SERVER_ADDRESS=http://localhost:50051
export BOUNDFLOW_WORKER_ADDRESS=http://localhost:50052
export CHARTER_STORE_URL=postgres://charter:charter@localhost:5434/charter
pytest tests/e2e

They use a real control plane, a real MCP subprocess and real governance gates. Only the model is faked, so the suite stays deterministic and free.

Metadata

Release files for boundflow-charter 0.4.0.dev3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for boundflow-charter 0.4.0.dev3
File Size Uploaded
boundflow_charter-0.4.0.dev3.tar.gz 177.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for boundflow-charter 0.4.0.dev3
File Interpreter ABI Platform
boundflow_charter-0.4.0.dev3-py3-none-any.whl Python 3 none any Details

Total release size: 316.3 kB

Release files / boundflow_charter-0.4.0.dev3.tar.gz

Download URL boundflow_charter-0.4.0.dev3.tar.gz
Size 177.6 kB
Tags Source
SHA-256 checksum
How to use checksums
1650e410a71c45cc1efc6c167f44e3ff7f8fa9e8399b251dfb7ef6869346057e
BLAKE2b-256 checksum
How to use checksums
cd8a51fc4ccb743e768b93e63a790ab03d58a7e94a5b77395e1be05a3d05eb23
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

Release files / boundflow_charter-0.4.0.dev3-py3-none-any.whl

Download URL boundflow_charter-0.4.0.dev3-py3-none-any.whl
Size 138.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8a81d5314b1ff2c9ebc0c17233137f506a6dd8b4b99eff1483fbfb7a981bf1bc
BLAKE2b-256 checksum
How to use checksums
c131efa6cfd9375cc4aa6c3379593152dcb4c380ed6345c66d96cf69a7be8be3
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
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page