Prerequisites
- A local Kubernetes cluster. kind is the recommended default (
kind create cluster --name fracta). Docker Desktop Kubernetes, minikube, and k3d are also supported for image loading. kubectl,make,op(1Password CLI) on PATHpsqlfor event queries (optional)
Architecture
deployment/k8s/manifests/.
Quick Start
1. Initialize fracta in your project
From the root of any git repository:fracta.yaml and deployment/k8s/manifests/ (namespace, RBAC, postgres, falkordb, controlplane, gateway, auth-helpers ConfigMap stub, agent job template).
2. Apply the manifests
3. Verify pods are running
1/1 Running:
postgres-0falkordb-0fracta-controlplane-*fracta-gateway-*
4. Reach the control plane Service from your host
The control plane is thefracta-controlplane Service on port 9090 inside the cluster. For local dev clusters:
localhost:9090 → fracta-controlplane Service.
Port-forward is the canonical path on kind: kind’s LoadBalancer Services stay <pending> because there’s no cloud provisioner. For Docker Desktop, a LoadBalancer Service may publish directly without port-forward; for non-dev clusters, expose via an Ingress. In all cases, update control_plane_api.url in fracta.yaml to match. The rest of the flow is identical.
5. Connect via MCP (golden path)
The scaffoldedfracta.yaml is the host-side thin-client config. Configure your AI CLI to run fracta serve from your project root:
Configuration
Two config files
| File | Purpose | Used by |
|---|---|---|
fracta.yaml (project root) | Host-side thin-client config pointing at control plane API | fracta serve on your machine |
deployment/k8s/manifests/fracta-controlplane.yaml | In-cluster controlplane config (ConfigMap) | Controlplane pod |
deployment/k8s/manifests/fracta-gateway.yaml | In-cluster gateway config (ConfigMap) | Gateway pod |
Key differences
| Setting | Host-side (fracta.yaml) | In-cluster (controlplane / gateway ConfigMaps) |
|---|---|---|
| Control plane API | http://localhost:9090 | Pod-local service access |
| State/queue | Not configured in thin client | Postgres in cluster |
| FalkorDB | Not configured in thin client | falkordb.fracta.svc:6379 |
| Runtime backend | kubernetes (with extra_volumes) | kubernetes |
MCP backend transports
Each MCP backend in the gateway config needs an explicit transport. For example, if you’ve added an Elasticsearch MCP container as a Service in the cluster:streamable-http (default if omitted), sse.
Secrets
You’ll need at minimum a postgres secret. Create it manually:fracta-auth-helpers ConfigMap from deployment/auth-helpers/ — see the auth helpers section of the K8s configuration docs.
For MCP backend services you add (Elasticsearch, internal services, etc.), create their secrets the same way and reference them via secretKeyRef in the corresponding Deployment manifest.
How Agent Pods Work
When you spawn an agent (viafracta_spawn or fracta spawn), fracta prepares a per-agent workspace and the K8s runtime runs it:
- The in-cluster control plane worker resolves the selected runtime (
claude,codex, oropencode) and writes runtime-specific workspace files into the configured staging directory. - The K8s backend packages those files into a ConfigMap and creates either a batch Job (
fracta-agent-<task>) or, for stream mode, a persistent Pod (fracta-stream-<task>). - A
workspace-initinit container copies the ConfigMap files into the runtime workdir, normally/workspace/agents/<task>. - An optional auth Secret provides the host-seeded bearer token for the agent runtime.
- The main container runs
entrypoint.sh, starts the strategy sidecar, and execs the selected runtime command. - The runtime reads its workspace config and connects to the fracta gateway via the agent-scoped HTTP MCP endpoint.
- The agent discovers fracta, elastic, and vendor tools through the gateway. On completion, fracta records output and events.
| Runtime | Workspace files |
|---|---|
| Claude | .mcp.json, .claude/settings.json, .fracta/user-settings.json, CLAUDE.md |
| Codex | .codex/config.toml, AGENTS.md |
| OpenCode | opencode.json, AGENTS.md |
Runtime MCP Config Formats
Claude uses.mcp.json:
"type": "http" field is required — Claude CLI uses it to select HTTP MCP transport.
Codex uses .codex/config.toml:
opencode.json:
docs/runtime-configuration.md for the full multi-runtime configuration details.
Images
The scaffolded manifests referenceghcr.io/darkquasar/fracta:latest (the published fracta image) with imagePullPolicy: IfNotPresent. For local clusters that can pull from public registries, no extra setup is needed. For air-gapped clusters or fracta-development workflows where you’ve built a local image, load the image into your cluster runtime and edit the image: and imagePullPolicy: fields in deployment/k8s/manifests/fracta-controlplane.yaml and fracta-gateway.yaml accordingly:
image: and set imagePullPolicy: Never on the relevant Deployments.
After image changes, restart the Deployments:
Observability
Events in Postgres
Kubernetes Events
Gateway logs
Agent pod logs (while pod exists)
Sidecar logs (inside agent pod)
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
ErrImagePull on locally-built pods | Image not loaded into the local cluster runtime | Load the image via your cluster’s loader (kind load docker-image, minikube image load, k3d image import) and set imagePullPolicy: Never on the relevant manifests |
ErrImagePull on a public-image pod | Cluster cannot reach the registry | Check local cluster network and registry access; for air-gapped clusters, mirror the image |
no MCP transport configured | MCP server entry is missing both remote.url and local.command | Add a remote entry to the gateway ConfigMap |
| MCP tools not discovered in agent | Runtime workspace config missing gateway endpoint or auth fields | Check generated .mcp.json, .codex/config.toml, or opencode.json |
| OpenCode MCP tool call is auto-rejected | Concrete OpenCode MCP permission key missing, for example fracta_list | Ensure opencode.json expands fracta tool permissions |
unexpected status code: 404 on MCP backend | Wrong SSE/HTTP endpoint path | Check transport + URL path (/sse vs /mcp) |
timeout waiting for endpoint | SSE endpoint returns EOF | Backend may need /sse path explicitly |
| Agent output parse error | Sidecar stdout contamination | Check entrypoint.sh redirects to log file |
config_skew event | Host and worker config differ | Expected if configs diverge; informational |
| Agents table empty after completion | Reaper cleans terminal queued agents | By design; check missions table instead |
Authorization header is missing | CLAUDE_CODE_SIMPLE in credential profile env blocks apiKeyHelper loading | Remove CLAUDE_CODE_SIMPLE from credential profile env (enforced by forbid_env assertion) |
Authentication failed: API Key is valid | Missing AWS_REGION in credential profile env | Add AWS_REGION: "ap-southeast-2" to auth.credentials.profiles.bedrock.env (enforced by require_env assertion) |
model identifier is invalid | Stale/wrong Bedrock model ID | Use global.anthropic.claude-sonnet-4-6 (not us.anthropic.* or old dated IDs) |
configmaps is forbidden (in-cluster mode) | Controlplane pod using default service account | Set serviceAccountName: fracta-agent on controlplane deployment |
bedrock-auth-helper: executable file not found (in-cluster mode) | host_fallback credential source (scope: host_edge) runs bedrock-auth-helper which doesn’t exist in-cluster | In-cluster config should not include host_fallback source; the credential planner annotates it as unavailable. Pods self-auth via corporate proxy source |
Teardown
fracta namespace and persistent volumes.
