Skip to content

Control Plane

The Joch control plane is a domain-specific control plane for AI agent fleets. It owns desired state — agents, models, tools, MCP servers, policies, deployments, environments — and reconciles that state into runtime configuration. It does not own the agent loop; that lives in whichever SDK built the agent.

Services

The control plane is a small set of well-bounded services:

joch-apiserver           REST + gRPC + Watch API for resources
joch-resource-store      durable storage (SQLite | Postgres | Kubernetes CRDs)
joch-admission           validates and defaults resource specs on apply
joch-policy-engine       evaluates policies against decision contexts
joch-controller-manager  reconciles desired state into runtime artifacts
joch-compiler            compiles agent records into CompiledAgentManifests
joch-scheduler           assigns executions to workers
joch-approval-service    routes approval requests; records decisions
joch-budget-service      tracks cost and enforces caps
joch-eval-service        runs evals on a schedule, on demand, on promotion
joch-agbom-service        generates and refreshes per-agent AgBOM
joch-secret-broker       resolves Secret references at the right boundary

In small deployments these collapse into a single joch-server binary. In large deployments they split out for independent scaling.

Resource store

The store is pluggable. Joch ships three backends:

SQLite           local mode, single-process developer experience
Postgres         shared resource store for Docker / VM / SaaS deployments
Kubernetes CRDs  stores resources as native CRDs for kubectl-friendly use

The same resource catalog (Resources) works against all three backends. Operators choose by context, not by changing the spec.

Apply, validate, reconcile

An apply pipeline is identical across backends:

joch apply -f agent.yaml
joch-apiserver  (auth, RBAC, audit)
joch-admission  (schema, defaults, cross-references, policy preflight)
joch-resource-store  (versioned put)
joch-controller-manager  (reconcile)
joch-compiler  (CompiledAgentManifest)
runtime adapter  (Local | Docker | Kubernetes | …)

If the apply fails policy preflight (e.g., a referenced model is denied for the target environment), the apply rejects with a clear diagnostic. Nothing is partially-applied.

Compiled agent manifests

Specs are human-authored. The runtime consumes a deterministic, fully-resolved manifest:

apiVersion: internal.joch.dev/v1
kind: CompiledAgentManifest
metadata:
  name: support-triage-g14
spec:
  agent:
    name: support-triage
    generation: 14
  framework:
    adapter: openai-agents-sdk
    adapterImage: ghcr.io/peasantsai/joch-adapter-openai:1.0.0
  modelRoute:
    primary: openai:gpt-5-thinking
    fallback:
      - anthropic:claude-sonnet
  tools:
    - name: zendesk.search
      gatewayUrl: http://joch-tool-gateway
      sideEffect: read_only
      schemaSha: sha256:abc...
    - name: slack.send
      gatewayUrl: http://joch-tool-gateway
      sideEffect: external_write
      requiresApproval: true
  mcpServers:
    - name: github
      gatewayUrl: http://joch-mcp-gateway
      pinnedVersion: 1.2.0
      trustScore: 0.9
  memories:
    working:
      endpoint: http://joch-memory
      namespace: support-platform
      name: support-triage-working
  policies:
    resolved:
      - no-customer-data-exfiltration@v3
      - external-send-requires-approval@v2
  observability:
    traceEndpoint: http://joch-trace
    abomEndpoint: http://joch-agbom

Workers run from the manifest, not from raw YAML. The compiler is the single place where references are resolved and capability checks happen.

Authentication and authorization

The control plane authenticates clients via OIDC, with optional integrations for Kubernetes service accounts. Authorization uses roles bound to teams / namespaces. Every API call lands in the audit log with the operator identity, the resource touched, and the effective policy.

Multi-tenancy

Joch's multi-tenant model is Team / Namespace plus Environment:

  • Team / Namespace — ownership boundary; roles are bound here.
  • Environment — promotion boundary (dev, staging, prod).
  • Policy selectors — bind policies to namespaces or environments.

For SaaS deployments, an additional Tenant boundary wraps namespaces. See Trust and Security Model.

API surface

POST   /v1/resources/apply
GET    /v1/namespaces/{ns}/agents
GET    /v1/namespaces/{ns}/agents/{name}
GET    /v1/namespaces/{ns}/agents/{name}/agbom
GET    /v1/namespaces/{ns}/executions
POST   /v1/namespaces/{ns}/executions
GET    /v1/namespaces/{ns}/toolcalls
GET    /v1/namespaces/{ns}/approvals
POST   /v1/namespaces/{ns}/approvals/{id}/decide
POST   /v1/eval/{name}/run
POST   /v1/promote
WATCH  /v1/resources

The full API is published as OpenAPI in the peasantsai/joch-spec repository.

What the control plane does not do

  • It does not run model calls. Those go through the Model Router.
  • It does not run tool calls. Those go through the Tool Gateway.
  • It does not store conversation transcripts. Those live in the Conversation resource backed by the data plane.
  • It does not run agent loops. Those live in the SDK runtime via a framework adapter.

This separation keeps the control plane small, deterministic, and easy to audit.