Thanks to visit codestin.com
Credit goes to github.com

Skip to content

agentsystems/agent-control-plane

Agent Control Plane

GitHub stars CI codecov

Note

Pre-Release Software - AgentSystems is in active development. Join our Discord for updates and early access. ⭐ Star the main repository to show your support!

This is the gateway and orchestration layer for AgentSystems. See the main repository for platform overview and documentation.

The Agent Control Plane (ACP) is the HTTP gateway and core services layer that fronts every AgentSystems deployment.

  • Repo: agentsystems/agent-control-plane
  • Image: ghcr.io/agentsystems/agent-control-plane:<tag> (publicly available)
  • Part of the multi-repository platform – see the AgentSystems docs.

Contents

Path Purpose
cmd/gateway/ FastAPI gateway & reverse-proxy (modularized components)
├── main.py Core FastAPI application and endpoints
├── database.py PostgreSQL connection pooling and job management
├── docker_discovery.py Docker container discovery and agent registration
├── egress.py Egress allowlist configuration and management
├── lifecycle.py Container idle timeout and lifecycle management
├── proxy.py HTTP CONNECT proxy server for agent egress
├── models.py Pydantic data models
└── exceptions.py Common HTTP exception patterns
Dockerfile Container image build instructions
tests/ Unit tests for gateway functionality
.coveragerc Test coverage configuration
CHANGELOG.md Version history and changes

How ACP fits into the platform

graph LR
  C((Client)) -- 18080 --> G[Gateway]
  subgraph "agents-int"
    G -- 8080 --> A1((hello-world-agent))
    G --> A2((your-agent))
  end
  G --> PG[(Postgres)]

Loading
  1. Gateway discovers containers with labels agent.enabled=true & agent.port=<port>.
  2. Auth (Bearer token placeholder) → forward to agent.
  3. Writes an append-only audit row (hash-chained).

Endpoint details: Gateway API.


Quick start

Using AgentSystems SDK (Recommended)

# Install the SDK
pipx install agentsystems-sdk

# Initialize a deployment
agentsystems init

# Start the platform
cd agent-platform-deployments
agentsystems up

• API Gateway: http://localhost:18080 • List agents: curl http://localhost:18080/agents -H "Authorization: Bearer demo"

Using Docker directly

# Pull the public image
docker pull ghcr.io/agentsystems/agent-control-plane:latest

# Run with Docker Compose (see agent-platform-deployments repo)
docker run -d \
  --name agent-control-plane \
  -p 18080:8080 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  ghcr.io/agentsystems/agent-control-plane:latest

Local dev (hot reload)

cd agent-control-plane
python -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
# override port via env if desired
ACP_BIND_PORT=8088 uvicorn cmd.gateway.main:app --reload --port ${ACP_BIND_PORT:-8080}

Running tests

# Run all tests with coverage
pytest --cov=cmd --cov-report=term-missing

# Run specific test file
pytest tests/test_gateway.py -v

Docker Images

Using the public image (Recommended)

# Pull the latest published image from GitHub Container Registry
docker pull ghcr.io/agentsystems/agent-control-plane:latest

# Specific version
docker pull ghcr.io/agentsystems/agent-control-plane:0.3.3

Building locally

# Clone and build for development
git clone https://github.com/agentsystems/agent-control-plane.git
cd agent-control-plane
docker build -t my-custom-acp:dev .

If you built a custom image, update the tag in your docker-compose.yml or agentsystems-config.yml.


File Uploads & Artifacts

The gateway supports file uploads to agents and manages a shared artifacts volume.

Upload Endpoint

Send multipart requests to any agent:

# Upload single file with JSON payload
curl -X POST http://localhost:18080/invoke/agent-name \
  -H "Authorization: Bearer token" \
  -F "[email protected]" \
  -F 'json={"sync": true, "format": "csv"}'

# Upload multiple files
curl -X POST http://localhost:18080/invoke/agent-name \
  -H "Authorization: Bearer token" \
  -F "[email protected]" \
  -F "[email protected]" \
  -F 'json={"sync": true}'

Artifacts Volume Management

The gateway automatically:

  1. Creates thread directories: /artifacts/{thread-id}/{in,out}/ for each request
  2. Saves uploaded files: Files go to /artifacts/{thread-id}/in/{filename}
  3. Sets permissions: Attempts to configure agents (UID 1001) for file access
  4. Configured with limits: Default 200MB upload limit (configurable via ACP_MAX_UPLOAD_MB)

Thread-Centric Structure

/artifacts/
├── {thread-id-1}/
│   ├── in/          # Files uploaded by client
│   │   ├── data.csv
│   │   └── config.json
│   └── out/         # Files created by agent
│       └── result.txt
└── {thread-id-2}/
    ├── in/
    └── out/

Gateway Environment Variables

Var Default Purpose
ACP_MAX_UPLOAD_MB 200 Maximum file upload size in MB
ACP_BIND_PORT 8080 Gateway listen port inside container
ACP_AUDIT_DSN postgresql://... Postgres connection for audit logs
ACP_PROXY_PORT 3128 HTTP CONNECT proxy port for agent egress
ACP_IDLE_TIMEOUT_MIN 15 Default idle timeout for containers (minutes)
AGENTSYSTEMS_CONFIG_PATH /etc/agentsystems/agentsystems-config.yml Path to agent configuration file

Accessing Artifacts

View uploaded files and agent outputs:

# List all active threads
docker exec gateway ls -la /artifacts/

# Read specific files
docker exec gateway cat /artifacts/{thread-id}/out/result.json

Configuration

Continuous Integration (GitHub Actions)

Every pull request triggers ci.yml which now performs:

  1. Pre-commit hooks (ruff, black, shellcheck, hadolint).
  2. docker build of the gateway image.
  3. Run the container on localhost:8800 (internal port 8080).
  4. Poll http://localhost:8800/health (30 × 2 s) and fail if not 200 OK.
  5. Always remove the container in a cleanup step.

The gateway detects when the host Docker socket is absent (e.g. CI) and gracefully disables agent discovery, emitting the log line docker_unavailable. The health endpoint still reports OK, so the build remains deterministic.


Environment variables (excerpt)

Var Default Purpose
PG_HOST localhost Postgres host
PG_DB agent_cp Postgres database name
PG_USER agent Postgres user
PG_PASSWORD agent Postgres password

Agent discovery labels

Label Example Meaning
agent.enabled true Opt-in to gateway routing.
agent.port 8000 TCP port the agent listens on.

Architecture

Module Structure

The gateway is organized into focused modules:

  • main.py: FastAPI application, request routing, and API endpoints
  • docker_discovery.py: Container discovery via Docker labels, maintains agent registry
  • database.py: PostgreSQL operations with automatic fallback to in-memory storage
  • egress.py: Loads and manages per-agent egress allowlists from config
  • proxy.py: HTTP CONNECT proxy server for controlled agent outbound requests
  • lifecycle.py: Monitors agent activity and stops idle containers
  • models.py: Shared Pydantic models for request/response validation
  • exceptions.py: Standardized HTTP exception factories

Key Features

  1. Auto-discovery: Finds agents by Docker labels (agent.enabled=true)
  2. Lazy start: Automatically starts stopped agent containers on first request
  3. Idle management: Stops containers after configurable idle timeout
  4. Egress control: HTTP proxy configured to restrict agent outbound requests to allowlisted URLs
  5. File uploads: Handles multipart uploads with automatic artifact management
  6. Async by default: Non-blocking invocations with status polling
  7. Type safety: Full type hints for better IDE support

Contributing

See CONTRIBUTING.md for guidelines on contributing to this project.

Development Guidelines

  • Add type hints to all new functions
  • Include docstrings for public APIs
  • Update CHANGELOG.md for notable changes
  • Run tests before submitting PRs: pytest

gateway[Gateway]

subgraph "Docker network: agents-int" gateway -- 8080 --> agent1((some-agent)) gateway --> agent2((another-agent)) end gateway --> pg[(Postgres)] gateway --> lf[Langfuse]


1. **Gateway** discovers containers on the `agents-int` internal network via Docker/Kubernetes labels `agent.enabled=true` and `agent.port=<port>`.
2. Requests are authenticated (Bearer token placeholder for now) and forwarded to the agent container.
3. Each round-trip is appended to the **audit** table (hash-chained rows) and optionally mirrored to Langfuse.

See the [Gateway API reference](../docs/reference/gateway-api) for endpoints.

## Quick start (compose)

The fastest path is the deployment repo:

```bash
# clone side-by-side
mkdir agents && cd agents
for repo in agent-control-plane agent-platform-deployments agent-template; do
  git clone https://github.com/agentsystems/$repo.git
done

cd agent-platform-deployments
make up        # docker compose up -d (gateway + Postgres + example agent)

Browse: • Gateway Swagger UI → http://localhost:18080/hello-world-agent/docs • List agents & states → curl http://localhost:18080/agents -H "Authorization: Bearer demo" Returns JSON array of objects like { "name": "hello-world-agent", "state": "running" }

Local development (hot reload)

cd agent-control-plane
python -m venv .venv && source .venv/bin/activate
pip install -e .[dev]
# override port via env if desired
ACP_BIND_PORT=8088 uvicorn cmd.gateway.main:app --reload --port ${ACP_BIND_PORT:-8080}

Point the deployment’s compose file at host.docker.internal:8080 or run an agent locally on port 8000 and the gateway will pick it up.

Building the container

This repo is intended to be built into a container image and then orchestrated via the manifests in agent-platform-deployments.

# clone
 git clone https://github.com/agentsystems/agent-control-plane.git
 cd agent-control-plane

# build image (adjust tag as needed)
 docker build -t agentsystems/agent-control-plane:<tag> .

Push the image to your registry of choice and update the image tag in the deployment repo’s Compose / Helm charts.

 && source .venv/bin/activate
pip install -e .[dev]
# override port via env if desired
ACP_BIND_PORT=8088 uvicorn cmd.gateway.main:app --port ${ACP_BIND_PORT:-8080}

But day-to-day you will spin it up via the deployment bundle, e.g.:

# in agent-platform-deployments

# clone
git clone https://github.com/agentsystems/agent-control-plane.git
cd agent-control-plane

# create venv & install
 && source .venv/bin/activate
pip install -e .[dev]

# run gateway
# override port via env if desired
ACP_BIND_PORT=8088 uvicorn cmd.gateway.main:app --reload --port ${ACP_BIND_PORT:-8080}

(either with Docker or uvicorn agent.main:app) and the gateway auto-registers when the container is labeled agent.enabled=true and exposes the configured port (once health checks pass).

(gateway + Postgres + example agent, etc.) use the agent-platform-deployments repo:

# in a separate clone
cd agent-platform-deployments
docker compose -f compose/local/docker-compose.yml up -d

Configuration & conventions

Environment variables (excerpt):

Var Default Purpose
ACP_BIND_PORT 8080 Gateway listen port inside the container.
ACP_AUDIT_DSN postgresql://user:pw@postgres:5432/acp Audit Postgres connection.
ACP_ALLOWED_ORIGINS * CORS origins.

Agent discovery labels:

Label Example Meaning
agent.enabled true Opt-in to gateway routing.
agent.port 8000 Container port to forward to.

Release checklist

  1. Bump version in pyproject.toml.
  2. Build & push Docker image: docker build -t agentsystems/agent-control-plane:<tag> ..
  3. Create Git tag and release notes.
  4. Update Compose / Helm charts in agent-platform-deployments with the new <tag>.

© 2025 AgentSystems

  • (includes Compose v2)

       # repo root
    

docker compose build # build agents + gateway docker compose up -d # start stack (detached) curl http://localhost:18080/agents | jq . # → { "agents": [ {"name": "hello-world-agent", "state": "running"}, ... ] }


Swagger for any agent:
<http://localhost:18080/my_agent/docs>





Invoke an agent and poll status until it completes:

```bash
# 1. Start the job (returns thread_id and helper URLs)
resp=$(curl -s -X POST http://localhost:18080/invoke/my_agent \
     -H "Content-Type: application/json" \
     -d '{"today":"2025-06-13"}')
thread_id=$(echo "$resp" | jq -r .thread_id)

# 2. Poll lightweight status endpoint (state + progress only)
curl -s http://localhost:18080/status/$thread_id | jq .
# { "state": "running", "progress": { ... } }

# 3. Fetch the final result when state == completed
curl -s http://localhost:18080/result/$thread_id | jq .
# { "result": { ... } }
docker compose down        # stop containers, keep images
docker system prune -f     # optional: clear build cache
# copy an existing folder
cp -R my_agent my_fourth_agent

# edit YAML metadata
sed -i '' 's/name:.*/name: my_fourth_agent/' my_fourth_agent/agent.yaml

# (optional) tweak greeting
sed -i '' 's/Hello!/Howdy from agent four!/' my_fourth_agent/main.py

Append this to docker-compose.yml:

my_fourth_agent:
  build: ./my_fourth_agent
  expose: ["8000"]
  labels:
    - agent.enabled=true
    - agent.port=8000

Then:

docker compose build my_fourth_agent
docker compose up -d my_fourth_agent
curl http://localhost:18080/agents | jq .   # shows `{ "name": "my_fourth_agent", "state": "running" }`
curl -X POST http://localhost:18080/invoke/my_fourth_agent \
     -H "Content-Type: application/json" \
     -d '{"today":"2025-06-13"}'
  • label discovery (/gateway) Agents → FastAPI apps in my_*_agent/, read their own agent.yaml Labels → agent.enabled=true & agent.port=8000 tell the gateway to route -->

License

Licensed under the Apache-2.0 license.

About

Gateway and orchestration for AI agent deployment

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages