Skip to content
LogoLogo

Architecture

Centaur accepts Slack and API requests, stores each turn, assigns an isolated runtime, exposes approved tools, injects credentials through a proxy, and keeps an event trail clients can replay.

Centaur architecture — ingress, durable control plane, isolated execution, tools, workflows, and controlled egress

Planes

PlaneResponsibilityMain components
IngressAccept user and client input.Slack Events API, Slackbot webhook, external API clients.
ControlPersist requests and coordinate runtime state.FastAPI, Postgres, execution worker.
ExecutionRun one assigned agent session per thread.Kubernetes sandbox pods.
CapabilitiesGive agents approved actions.Tool plugins, workflow engine, overlays.
Secrets and egressLet agents call third-party APIs without receiving raw keys.Kubernetes Secret, iron-proxy, per-sandbox proxy token mapping.

Durable API lifecycle

Clients do not manage containers or keep long-running processes alive. They call the API and follow the event stream.

StepEndpointWhat it saves
Start or reuse a sandboxPOST /agent/spawnThe thread's current sandbox assignment.
Persist inputPOST /agent/messageWrites the user turn and extracts large multimodal attachments.
Run the agentPOST /agent/executeA run row with status and final result.
Follow outputGET /agent/threads/{thread}/eventsTool calls, model output, status changes, and final text.
Clean upPOST /agent/threads/{thread}/releaseReleases the sandbox and can cancel running work.

Because each step is stored, a Slack reconnect, browser refresh, API restart, pod replacement, or worker failover does not erase the run. The event stream is the client contract; Slack and other clients should reconnect with after_event_id instead of trying to reconstruct state locally.

Slackbot ingress

Slack talks to Centaur through the Slack Events API. The public request URL is the Slackbot webhook, usually:

https://api.acme.com/api/webhooks/slack

The webhook does not use a Centaur API key. Slack signs every request with X-Slack-Signature and X-Slack-Request-Timestamp; the Slackbot validates that HMAC signature with SLACK_SIGNING_SECRET before it routes the event to the API. After validation, the Slackbot calls Centaur's agent API with SLACKBOT_API_KEY.

During a Slack delivery, the API owns the execution state while Slackbot owns Slack rendering: opening or updating the thread UI, streaming chunks, rendering steps, and posting the final answer. The landing page preview shows that Slack thread surface; the durable API lifecycle above is the system underneath it.

Execution path

Kubernetes is the active sandbox runtime path. The API creates or claims a sandbox pod, attaches to it, and runs the requested agent CLI. Do not plan a new deployment around a local-container backend; the Helm chart, warm pool, overlay mounting, and network policies all assume Kubernetes sandboxes.

HarnessAdapter behavior
AmpMaterializes image/document blocks to files and passes text plus file references.
Claude CodePasses the Anthropic-shaped content through directly.
Codex / pi-monoExtracts text blocks for CLIs that accept a plain prompt.

The pod receives the prompt files, CLI command, internal API URL, proxy CA, and proxy settings. It does not need Kubernetes credentials or long-lived third-party API keys.

Tool and workflow layer

Tools are Python plugin directories. Each public client method becomes a REST method at /tools/{name}/{method}. Agents discover tools when they start.

Use tools for search, Slack, GitHub, market data, calendars, internal systems, and deployment-specific APIs. Tool code should read credentials with secret("NAME") so the same code works locally and in production.

Workflows are Python handlers that save step results. When a worker restarts, the handler runs again, but ctx.step(...) returns cached results for completed work.

Use workflows for scheduled digests, monitoring loops, approval gates, jobs that sleep for minutes or days, and parent/child workflow trees.

Secrets and outbound requests

Agents and tools refer to credentials by name, such as OPENAI_API_KEY or secret("CRM_API_TOKEN"). The sandbox container only ever holds those placeholder names; the real values live on a per-sandbox iron-proxy pod, bound to specific upstream hosts and request locations, and substituted on the wire when an outbound request matches.

See Security for the full threat model and what it does and does not protect against.

Failure model

FailureExpected recovery
Client disconnectsReconnect to the event stream with after_event_id.
API restartsReload assignments, executions, and terminal state from Postgres.
Sandbox pod diesThe execution becomes terminal, the event trail remains in Postgres, and operators inspect GET /agent/executions/{execution_id} plus API/sandbox logs before retrying the turn.
Workflow worker restartsRe-run the handler and skip completed checkpoints.
Proxy restartsRebuild the key-injection map from the secret-manager cache.
Tool changesDiscovery reloads plugin metadata; agents see the updated methods.