Architecture
Service topology and request flow for self-hosted ctxpipe.
Self-hosted ctxpipe uses the same product architecture as hosted ctxpipe, but the operator owns the runtime, network boundary, secrets, and stateful services. Keep the browser, REST API, auth callbacks, and MCP endpoint on the same public backend origin.
Request flow
- The browser opens your public
AUTH_BASE_URL. - The backend serves API, auth, webhook, and MCP routes from that origin.
- The backend proxies UI requests to the
uiservice. - Auth sessions, repository records, connections, and workflow state live in Postgres.
- Repository ingestion calls
codesearch, which clones repositories and maintains Zoekt indexes. Codesearch is a single replica: Zoekt shards, git clone cache, and SCIP artifacts live on one process and one volume. Extra worker replicas do not raise ingest throughput unless codesearch memory (andOPENWORKFLOW_CONCURRENCY/ indexer caps) also grow. - The worker runs durable jobs for ingestion, connector setup, scoped source mirrors, Slack thread capture, and Forge provisioning.
- Extracted claims and relationships are projected into the configured graph database.
- Chat and MCP tools retrieve from Postgres, codesearch, and the graph database before calling your configured model provider.
Services
| Service | Default port | Stateful | Purpose |
|---|---|---|---|
backend | 3000 | No | Public API, auth, MCP, callbacks, UI proxy |
ui | 3002 | No | Product web app, proxied by backend |
worker | internal | No | Background workflows and connector jobs |
codesearch | 3001 | Yes | Repository clone cache and Zoekt index. Stay at one replica. |
postgres | 5432 internal (no host port in the deploy profile) | Yes | Primary relational state |
falkordb | 6379 Redis, 7687 Bolt, internal | Yes | Default graph database |
dind (Docker Compose) | 2376 internal | No | Docker daemon for Workspace chat sandboxes |
Chat sandbox network policy
The same policy holds on Docker Compose and AWS:
- A chat sandbox holds no credential. It has no GitHub token, no model proxy credential, and no tool bridge token in its environment, files, or Git configuration. Agent Vault (open source) runs next to the sandboxes and adds each credential to the requests that need it.
- Sandboxes reach the network only through the Agent Vault proxy. On Compose,
rules in the
dindservice enforce this; on AWS,DOCKER-USERrules on the sandbox host do. HTTP and HTTPS stay open: agents can clone, fetch, push, call the GitHub CLI, install packages, and read documentation on the internet. The proxy forwards hosts that have no credential rule as they are. - All other traffic from a sandbox is blocked: a direct connection that does not use the proxy, any non-HTTP protocol (SSH, raw TCP, UDP), and DNS. Sandboxes do not resolve names; the proxy resolves them.
- Private addresses through the proxy: on Compose, only the backend's fixed
address on the
sandboxnetwork. On AWS, only the backend's subnets; the host also rejects traffic from Agent Vault to the host itself. Agent Vault never reaches its own management API. Data stores are never exposed to sandboxes: Compose never publishes Postgres or FalkorDB, and AWS admits only the app's security group to them. - Sandboxes cannot reach cloud instance metadata (
169.254.169.254). - Each agent port needs a password, and sandboxes cannot reach each other over the Docker bridge.
- Each chat turn gets its own Agent Vault vault and proxy session. The backend deletes the vault when the turn ends, and the session stops with it. The backend also revokes the turn's GitHub token.
- If Agent Vault does not answer, Docker chat fails closed with a clear error. The backend never gives a sandbox a credential instead.
The deployment generates the Agent Vault passwords. You do not set them. On Compose, the master password is on a volume that only Agent Vault mounts, and the owner password is on a volume that only the backend and the worker mount. On AWS, the stack keeps both in Secrets Manager; only the host reads the master password.
Agent Vault owner password
The backend registers the owner password on its first login to an Agent Vault
with no owner. After that, Agent Vault refuses another password, and Docker
chat fails closed with an error that names this section. To change the
password, change it in Agent Vault first (agent-vault account change-password,
or its web UI on port 14321 inside the deployment), then give the backend the
new value. To start over, remove Agent Vault's data (Compose: the
agent_vault_data volume; AWS: replace the sandbox host). Run vaults are
temporary, so no data is lost.
Tools in a sandbox must use the standard proxy variables (HTTPS_PROXY,
HTTP_PROXY) and trust the proxy's certificate authority. The sandbox
environment sets both. A tool that ignores the proxy variables cannot reach
the network.
Chat sandboxes on Docker Compose
Compose runs each Workspace conversation's agent in its own container inside
dind, a stock Docker-in-Docker service. dind, agent-vault, backend, and
worker share a separate sandbox network (172.30.99.0/24):
| Component | Port | Reached by | Purpose |
|---|---|---|---|
| Docker API (mutual TLS) | 2376 | backend, worker | Create, stop, resume, and remove sandboxes |
| Sandbox agent ports (password per conversation) | 32768–60999 on dind | backend | The backend talks to the agent in each sandbox |
| Agent Vault API | 14321 | backend, worker | One vault per chat turn |
| Agent Vault proxy | 14322 | Sandboxes (the only address they reach) | Adds credentials and forwards HTTP(S) |
| Backend model proxy and tool bridges | 3000 and per-run ports on backend.sandbox.ctxpipe.internal | Agent Vault, for sandboxes | Model calls and tool calls go back to the backend |
Chat sandbox host on AWS
On AWS, @ctxpipe/aws-cdk adds one EC2 Docker host next to the Fargate
services. Each Workspace conversation's agent runs in its own container there.
| Component | Port | Reached by | Purpose |
|---|---|---|---|
| Docker API (mutual TLS) | 2376 | backend, worker | Create, stop, resume, and remove sandboxes |
| Sandbox agent ports (password per conversation) | 32768–60999 | backend | The backend talks to the agent in each sandbox |
| Agent Vault API | 14321 | backend, worker | One vault per chat turn |
| Agent Vault proxy | 14322 (on the host) | Sandboxes (the only address they reach) | Adds credentials and forwards HTTP(S) |
| Backend model proxy and tool bridges | 3000, 32768–60999 | Agent Vault, for sandboxes | Model calls and tool calls go back to the backend task that runs the turn, at its VPC DNS name |
DOCKER-USER rules on the host let sandboxes reach only the Agent Vault proxy.
Agent Vault reaches the internet through NAT and may dial private addresses
only in the VPC. Security groups keep Postgres, Neptune, EFS, codesearch, the
UI, and the worker away from the host. The host also blocks sandboxes from the
host itself. See
AWS: Chat sandbox host.
Public surface
Expose the backend origin publicly. Keep internal services private unless you have a specific operational need.
| Route or endpoint | Public? | Notes |
|---|---|---|
/ | Yes | Backend proxies UI requests |
/.auth/* | Yes | Better Auth routes and provider callbacks |
/api/v1/* | Yes | REST API and connector callbacks |
/:orgSlug/api/v1/* | Yes | Organization-scoped REST API |
/mcp | Yes | Streamable HTTP MCP endpoint. Tool calls name the organization in org |
/.status | Optional | Basic backend status endpoint |
ui:3002 | No | Internal service behind backend. Docker Compose binds it to 127.0.0.1 only. |
codesearch:3001 | No | Internal service called by backend and workflows. Docker Compose binds it to 127.0.0.1 only. |
postgres, falkordb | No | Stateful infrastructure. Never published. |
Stateful data
Back up anything you cannot rebuild quickly.
| Data | Location | Recovery note |
|---|---|---|
| App database | Postgres. On Compose, an anonymous volume unless you mount a named one (see Docker). | Required. Contains users, sessions, repositories, connections, workflow state, and encrypted connector metadata. |
| Graph data | FalkorDB or another graph database. On Compose, the writable layer of the falkordb container unless you mount a named volume (see Docker). | Back up for fast recovery. Some graph data may be regenerated by reindexing, but that can be slow and may not cover all operational state. |
| Repository cache | repo_cache volume in Compose, EFS /data on AWS CDK, a Railway volume on Terraform | Rebuildable by recloning, but useful to retain. |
| Zoekt index | zoekt_index_deploy volume in Compose, EFS /data on AWS CDK, a Railway volume on Terraform | Rebuildable by reindexing, but can be expensive for large repositories. |
| Chat sandboxes | sandbox_docker volume in Compose, Docker volume on the AWS sandbox host | No backup needed. Conversations keep their work in their git branch, and a lost sandbox is created again from it. |
| Secrets | Deployment secret store or .env files | Required to decrypt sessions and connector credentials. Protect and version changes carefully. |