agentctl is a Kubernetes control plane for fleets of conformant AI agents: it provisions, configures, scales, observes, secures, and exposes agents declaratively, through Custom Resources. This document is the reference for how the pieces fit together — the control-plane components, the Custom Resources and the workloads they render to, the capability planes, the end-to-end request flows, and the data stores.
agentctl depends on a published Agent Control Contract (ACC), never on a
specific agent binary. Any agent that conforms is managed unchanged. The
reference agent (agentd) is one implementation; every interaction with an agent
in this document — the management surface, A2A, the work.* claim client, the
downward-API environment, /metrics — is an ACC surface, so a conformant agent
wires in identically. The control plane is implemented in Rust.
Two facts shape everything below:
- The network is the substrate. Agents are ordinary pods on the pod network. They serve a management/A2A surface over mTLS-gated HTTPS, and they dial the control-plane gateways over TLS. There is no on-node bridge, no host socket, no vsock.
- Identity is cryptographic. Inbound to an agent, the caller presents a verified mTLS client certificate that authenticates it as the Management origin. Outbound from an agent, the gateways attest the caller by its source IP (its pod IP, resolved to the pod through the Kubernetes API). Agents are secret-free: the gateways hold and inject every provider and tool credential off-pod.
Eight control-plane Deployments cooperate. External actors (kubectl, A2A peers, producers, providers, Prometheus) enter through the edges; the data plane is your agent pods.
flowchart LR
subgraph ext[External / users]
kubectl[kubectl / kube-apiserver]
peer[A2A clients / peer agents]
prod[Work producers]
prov[Model + tool providers]
prom[Prometheus]
end
subgraph cp["Control plane (agentctl-system)"]
op[operator + PKI]
api[apiserver aggregated]
adm[admission webhooks]
gw[gateway A2A]
mg[modelgateway]
mcpg[mcpgateway]
coord[coordination]
scaler[scaler]
keda[KEDA]
cm[cert-manager]
pg[(Postgres)]
end
subgraph dp[Data plane: your agents]
agent["agent pod<br/>serves mTLS HTTPS :8443 /mcp"]
end
kubectl -->|apply CRs| api
kubectl -->|management verbs| api
api -->|mutate then validate| adm
op -->|watch CRs, apply workload| agent
api -->|admin verbs over mTLS /mcp| agent
peer -->|A2A HTTP + SSE| gw
gw -->|forward to pod: mTLS /mcp| agent
gw <-->|durable tasks| pg
mg <-->|usage + reservations| pg
agent -->|infer: keyless TLS| mg
agent -->|tools: keyless TLS| mcpg
mg -->|provider call: key injected| prov
mcpg -->|tool call: credential injected| prov
prod -->|work.submit MCP| coord
agent -->|work.claim/ack MCP| coord
keda -->|IsActive / GetMetrics gRPC| scaler
scaler -->|work.stats| coord
keda -->|scale 0..N| agent
prom -->|scrape /metrics| agent
cm -.->|certs + caBundle| api
cm -.->|per-workload serving cert + per-ns CA| agent
Solid arrows are request/data paths; dashed arrows are certificate/out-of-band paths.
All four CRDs are served under the API group agentctl.dev, version
v1alpha1. Each carries CEL validation rules, a status subresource, and printer
columns. The manifests live under deploy/crds/ and
charts/agentctl/crds/; worked examples are in
deploy/examples/.
An Agent is one logical agent workload. The load-bearing field is mode, which
selects the rendered workload kind.
| Field | Meaning |
|---|---|
mode |
once | loop | reactive | schedule | workflow — the run shape. |
image |
The conformant-agent image. Optional — falls back to operator.defaultAgentImage when omitted. |
instruction |
The agent's inline instruction (required for non-reactive modes). |
model.id |
Declared model id (metadata + printer column). |
model.pool |
The ModelPool this agent binds for inference. |
mcpServers |
MCPServerSets (by name) this agent binds for tools. |
subscribe |
Reactive-mode MCP resource URIs the agent wakes on. |
schedule |
{ cron, timezone } for mode: schedule. |
workflow |
The workflow graph (inline or configMapKeyRef) for mode: workflow. |
limits |
{ maxTokens, maxDepth, maxSteps } bounding box. |
surfaces |
Which control-plane surfaces to expose (management/metrics/a2a). |
access |
A2A caller policy: oidc (JWT verify + claim authz). |
capabilities.exec / capabilities.egress / capabilities.secrets |
Declared privileged capabilities (the lethal-trifecta legs). |
substrate |
Substrate tier (see Substrate). |
capabilities.exec, capabilities.egress, and capabilities.secrets are declared capabilities the admission
webhook gates: requesting all three at once (the "lethal trifecta") requires an
explicit opt-in annotation.
The operator's pure rendering core (crates/agentctl-operator/src/render.rs)
maps mode deterministically to a Kubernetes workload:
mode |
Rendered workload | Notes |
|---|---|---|
once |
Job |
backoffLimit: 0, restartPolicy: Never; run to a terminal status, then exit. |
workflow |
Job |
Supervised one-shot driving the workflow graph (--mode workflow --workflow <file>). |
schedule |
CronJob |
Fires the Job on schedule.cron; concurrencyPolicy: Forbid. |
loop |
Deployment |
Single replica; re-enters on a cadence. |
reactive |
Deployment |
Single replica; idles and wakes on subscribed resources. |
Every rendered pod is identical in its wiring, regardless of kind:
- Serves its management/A2A surface mTLS-gated on
:8443(--serve-mcp https://0.0.0.0:8443), presenting its own cert-manager-issued serving identity (--serve-cert/--serve-key), and trusting cluster-CA client certs (--serve-client-ca) — holders of which are the Management origin. - Dials the model gateway keyless via
INTELLIGENCE=https://…, trusting the same cluster CA for the hop (--tls-ca). No provider token is ever rendered into the pod. - Exposes
/readyzand/metricson a separate listener (:9090,AGENT_METRICS_ADDR), used as the readiness probe and scraped directly. - Carries the downward-API identity env (
AGENT_POD_NAME,AGENT_POD_UID,AGENT_POD_NAMESPACE,AGENT_NODE_NAME). - Is hardened: nonroot, no privilege escalation, all capabilities dropped,
read-only root filesystem (with a writable
/tmpemptyDir), no auto-mounted ServiceAccount token,RuntimeDefaultseccomp — satisfying therestrictedPod Security Standard. The only key material in the pod is its own serving identity, which the agent hot-reloads on cert-manager rotation without a restart.
Bound MCP servers are appended as --mcp <name>=<mcpgateway>/s/<name> (plus
--mcp-tags); a workflow is mounted as a ConfigMap and passed as --workflow.
An AgentFleet is a replicated, autoscaled set of workers, optionally fronted by
a coordinator ("main agent").
| Field | Meaning |
|---|---|
template |
The per-replica worker AgentSpec. |
scaling |
{ mode: claim | shard, minReplicas/maxReplicas (claim), shards N (shard), target }. |
work.source |
The shared work source (an MCP resource URI). |
replicas |
Claim-mode replica count; the target of the scale subresource (KEDA owns it in steady state). |
coordinator |
The optional main agent: { template, replicas, distribution }. |
budget |
Per-fleet token cap (maxTokens), enforced alongside the pool budget. |
work |
{ source?, maxAttempts (dead-letter), claimTtl (lease TTL) }. |
The worker template's mode is coerced to reactive (a fleet member is a
long-lived worker), then the scaling regime selects the workload:
flowchart TB
fleet[AgentFleet] --> mode{scaling.mode}
mode -->|claim| dep["Deployment<br/>(.spec.replicas OMITTED — KEDA owns it)"]
mode -->|shard| sts["StatefulSet<br/>(replicas = N; headless Service; --shard auto/N)"]
dep --> so["ScaledObject (keda.sh/v1alpha1)<br/>external trigger → scaler → coordination backlog"]
fleet -->|spec.coordinator set| coord["Deployment <fleet>-coordinator<br/>label fleet-role: coordinator"]
- Claim mode renders a
Deploymentwith.spec.replicasdeliberately omitted — a KEDAScaledObjectis the sole owner of the replica count. The ScaledObject's external trigger points at thescaler, which reads the coordination backlog, giving elastic scale from zero.minReplicaCountdefaults to 0. - Shard mode renders a
StatefulSetofscaling.shards(N) fixed partitions with a headless Service for stable per-shard identity. Each pod gets--shard auto/N; the agent derives its own partitionKfrom the ordinal inAGENT_POD_NAME. Shard mode is never KEDA-driven — the partition count is fixed, so no ScaledObject is emitted. - Coordinator. When
spec.coordinatoris set, the operator renders a second ownedDeploymentnamed<fleet>-coordinator(labeledagentctl.dev/fleet-role: coordinator, replicas default 1, coerced toreactive). Itsdistributioncontrols fan-out:queue(default) wires it as a producer on the fleetwork.source(injected asAGENT_FLEET_WORKSOURCE);a2aappends--a2a-peer worker=<gateway>/fleets/<ns>/<fleet>so it delegates point-to-point through the gateway.
The fleet exposes the scale subresource (.spec.replicas /
.status.replicas / .status.selector), so kubectl scale agentfleet and an
HPA can read and drive it.
A ModelPool configures the intelligence plane — a pool of model access the
modelgateway brokers. Agents hold no provider secret.
| Field | Meaning |
|---|---|
provider |
Provider id (free string, e.g. mock, anthropic, openai). |
endpoint |
Provider base URL. |
credentialSecretRef |
{ name, key } of the Secret holding the provider API key. |
models / defaultModel |
Allowed model ids and the default. |
budget.maxTokens |
Optional total token budget for the pool. |
status.usedTokens reports running consumption against the budget.
An MCPServerSet is a reusable bundle of MCP tool servers the mcpgateway
brokers. Agents hold no tool-server credential.
Field (per servers[] entry) |
Meaning |
|---|---|
name |
Server name — the agent's --mcp key and the gateway facade path segment (/s/<name>). |
endpoint |
The remote MCP server URL (Streamable HTTP). The agent never dials this. |
auth |
{ mode: none | staticToken, tokenSecretRef, header } — the credential the gateway attaches upstream, held off-pod. |
tags |
Per-tool trifecta capability tags. |
budget.maxTokens |
Optional per-server call budget. |
Each component is a Deployment with its own container image (eight in total). All
control-plane TLS is issued by cert-manager. Every component exposes Prometheus
/metrics.
| Component | Role | Reached by | Talks to |
|---|---|---|---|
| operator | Reconciles CRs into workloads; issues per-workload PKI; reconciles NetworkPolicies + KEDA ScaledObjects. | (no inbound API — watches the kube-apiserver) | kube-apiserver, cert-manager, agent workloads |
| apiserver | Aggregated API for management verbs. | kube-apiserver aggregator (front-proxy mTLS) | agent pods (mTLS /mcp), SubjectAccessReview |
| admission | Validating + mutating webhooks. | kube-apiserver (HTTPS webhook) | kube-apiserver (reads ModelPool existence) |
| gateway | Public A2A HTTP/JSON-RPC + SSE surface. | A2A clients (:8080 HTTP; optional :8443 mTLS) |
agent pods (mTLS /mcp), Postgres, push webhooks |
| modelgateway | Intelligence broker (secret-free inference). | agent pods (keyless TLS + plaintext :8080) |
model providers, Postgres, kube-apiserver (attest) |
| mcpgateway | Tools broker (secret-free MCP). | agent pods (keyless TLS + plaintext :8080) |
remote MCP servers, kube-apiserver (attest) |
| coordination | Work-distribution backbone (work.* MCP). |
agents/producers (:8080; optional mTLS) |
in-memory or Postgres store |
| scaler | KEDA external scaler (scale from zero). | KEDA (gRPC :9100) |
coordination (work.stats) |
The operator (crates/agentctl-operator) is a
level-triggered controller. For each Agent/AgentFleet it: (1) renders the CR
to its workload with the pure render core; (2) server-side-applies that workload
(owner-referenced, so garbage collection reclaims it when the CR is deleted); and
(3) patches status with the conditions taxonomy (Validated, Rendered,
Ready, Draining, Degraded), observedGeneration, and a curated projection
of the agent's live capabilities manifest. A RenderError becomes a
Validated=False condition rather than a hard failure.
Alongside reconcile it owns three cross-cutting duties:
- Workload PKI — for every workload it ensures a cert-manager
Certificateminting the serving identity into the Secret the render mounts, and anagentctl-caConfigMap per agent namespace carrying the cluster CA public cert (the agent's client-CA and outbound trust anchor). - NetworkPolicies — on each reconcile it ensures the three per-namespace agent
policies (default-deny, egress only to DNS and the control-plane gateways,
ingress only from the control-plane namespace). Gated by
NETWORK_POLICIES_ENABLED; enforced only by a policy-capable CNI. - KEDA wiring — it applies a
ScaledObjectper claim fleet (best-effort; a cluster without KEDA simply never gets the object).
It is leader-elected over a coordination.k8s.io Lease so it is safe to run
at replicas > 1. Readiness is not gated on leadership — every replica serves
/healthz, /readyz, and /metrics — so a rolling upgrade never deadlocks.
The operator also drives the guarded shard-resize choreography: when a shard
fleet's N changes, it quiesces the live StatefulSet to 0 pods (draining the
old-N partitions), then flips the applied template to the new N and scales back
up — a stop-the-world rebalance so no key is served by two partitions across the
seam.
The aggregated apiserver (crates/agentctl-apiserver)
registers as an APIService for management.agentctl.dev and serves the
management connect verbs drain, lame-duck, cancel, pause, resume on
both agents and agentfleets. It sits behind the kube-aggregator's front-proxy
trust: rustls requires a client cert verified against the
requestheader-client-ca (so only the kube-apiserver reaches it), it trusts the
proxied X-Remote-User/-Group identity, and it authorizes each verb with a
SubjectAccessReview before acting. It resolves the target to a Running pod IP
and dials the agent directly at its mTLS /mcp, presenting the control-plane
client cert (= Management). A fleet verb fans out to all Running replicas.
The admission plane (crates/agentctl-admission)
adds the two concerns the CRDs' CEL rules cannot express:
- Validating (
POST /validate): the image-registry allow-list, cross-objectModelPoolexistence, the lethal-trifecta opt-in gate, and OIDC-policy well-formedness. These run against both anAgent(spec.*) and a fleet'sspec.template.*andcoordinator.template.*, so a fleet cannot smuggle a disallowed image or an ungated trifecta. - Mutating (
POST /mutate): secure defaults — standard labels, a conservativemode, and a minimal-exposuresurfacesset. It deliberately does not hard-defaultsubstrate; leaving it unset resolves tostock-unix, the only rendered tier today (kata-hybrid/sidecar-emptydirare roadmap tiers, rejected at render until implemented).
The A2A gateway (crates/agentctl-gateway) is the
public agent-to-agent surface. It:
- projects a JWS-signed Agent Card at
/agents/{ns}/{name}/.well-known/agent-card.json(and/fleets/…), fetching the agent's capabilities from its pod over mTLS and signing with an Ed25519 key published at/.well-known/jwks.json; - bridges JSON-RPC at
POST /agents/{ns}/{name}, translating the spec method (message/send,tasks/get, …) to the reference method (a2a.SendMessage, …) and forwarding to the agent's mTLS/mcp;message/streampipes the agent's SSE byte-stream straight back astext/event-stream; - persists tasks in Postgres (so
tasks/getsurvives the agent andtasks/listreturns history) and owns push-notification webhooks (SSRF-guarded — see A2A flow); - serves a mesh registry at
GET /agents.
Inbound auth has three modes: per-agent OIDC (spec.access.oidc), a
trusted-proxy mTLS identity (a fronting API gateway asserts an
edge-verified identity over an authenticated channel), or a coarse bearer
token (AGENTCTL_API_TOKEN). A fleet is one addressable endpoint: for
/fleets/{ns}/{name} the gateway routes to the coordinator (front door) when the
fleet declares one, else load-balances round-robin across worker replicas, with
task affinity — a live op on an existing task returns to the pod that owns it.
This includes a workflow gate-reply: a message/send carrying message.taskId
resumes a run paused at the non-terminal INPUT_REQUIRED state, and is routed back
to the member that owns that task (rather than round-robined to a fresh worker).
The model gateway (crates/agentctl-modelgateway)
brokers inference secret-free. An agent dials it keyless
(POST /v1/infer, with /v1/chat/completions as an alias). The gateway
attests the caller by source IP, selects the agent's ModelPool, enforces
the budget, injects the pool's provider credential (read from the referenced
Secret), forwards to the provider, and meters the tokens consumed into
Postgres. It serves a keyless server-auth TLS listener (agents dial the rendered
https:// URL, trusting the cluster CA) plus a plaintext :8080 for
health/metrics.
Budget enforcement is atomic (no check-then-act race): a request first
reserves a conservative upper-bound estimate under a per-pool advisory lock —
admitted only if committed + outstanding-reserved + estimate <= budget — then
reconciles the reservation to the true token count on success (or releases it on
error). A leaked reservation is excluded from the budget after a TTL and swept.
The pool-wide cap and a per-fleet cap (keyed by (namespace, pool, fleet)) are
both enforced this way.
The MCP gateway (crates/agentctl-mcpgateway)
is the tools-plane analogue of the model gateway. An agent dials
…/s/<server> keyless; the gateway attests the caller by source IP,
scopes it to only the servers of the MCPServerSets its Agent binds,
injects the server's credential (from a Secret, held off-pod) onto the
upstream hop, and forwards the MCP JSON-RPC transparently — the
Streamable-HTTP session and SSE flow straight through, so no MCP state is
terminated at the gateway.
The coordination server (crates/agentctl-coordination)
is the work-distribution backbone and the single serializing point that makes
exactly-one-owner hold across replicas. It serves the stable work.*
contract over MCP JSON-RPC on :8080:
| Method | Purpose |
|---|---|
work.submit |
Enqueue an item; returns a work_id. |
work.claim |
Atomic grant of a pending item to one racer ({item, ttl_ms} + claim key). |
work.renew |
Extend a live, owned lease. |
work.ack |
Terminal settle; records the result and dedupes the claim key. |
work.release |
Return an item to pending (re-claimable). |
work.stats |
The off-pod backlog snapshot (also work://pending) — the scale-from-zero signal. |
work.result |
Correlate a submitted work_id to its outcome. |
work.deadletter |
Inspect/manage the dead-letter queue (dlq://items). |
The store sits behind a ClaimStore trait: an in-memory store is the default
(single replica; monotonic-clock leases) and a durable Postgres backend
(store: postgres) makes grant-one hold across >1 replica and survive
restarts via a single conditional UPSERT on claim_key. Attested-ownership mode
binds each claim to the caller's source-IP identity so one tenant cannot
ack/release another's lease.
The scaler (crates/agentctl-scaler) serves KEDA's
ExternalScaler gRPC on :9100. At replica 0 there is no pod to scrape, so it
reads the coordination backlog (work.stats → pending) and maps it onto KEDA's
RPCs: GetMetricSpec/GetMetrics drive the replica count toward
ceil(pending / threshold), and IsActive (pending > activationThreshold) is
the scale-from-zero gate. A coordination read failure returns the last known
value rather than flapping the fleet to 0.
Each capability plane is a composition of the components above.
| Plane | Built on | What it gives you |
|---|---|---|
| Provisioning | operator + CRDs + admission | Declarative agents/fleets rendered to hardened workloads with per-workload PKI. |
| Intelligence | modelgateway + ModelPool |
Secret-free, metered, budgeted inference. |
| Tools | mcpgateway + MCPServerSet |
Secret-free, scoped MCP tool access. |
| Scaling | coordination + scaler + KEDA (claim); StatefulSet partitioning (shard) | Elastic scale-from-zero for claim fleets; keyed/ordered partitioning for shard fleets. |
| A2A | gateway | Agents and fleets as authenticated A2A endpoints, plus agent-to-agent delegation. |
| Management | apiserver | drain/lame-duck/cancel/pause/resume via kubectl, RBAC-gated. |
| Observability | every component + agent | Prometheus /metrics scraped directly; OTLP tracing when OTEL_EXPORTER_OTLP_ENDPOINT is set. |
sequenceDiagram
actor U as kubectl
participant API as kube-apiserver
participant ADM as admission
participant OP as operator
participant AG as agent pod
U->>API: apply Agent (image, mode, model.pool, caps)
API->>ADM: mutate (defaults) then validate (trifecta + registry + ModelPool)
ADM-->>API: patched + admitted
OP->>API: watch Agents
OP->>OP: render workload; ensure PKI (serving cert + per-ns CA)
OP->>API: server-side-apply Job/Deployment/StatefulSet (restricted PSS, zero pod creds)
API-->>AG: scheduled + started
AG->>AG: serve mgmt/A2A on mTLS :8443; expose /readyz + /metrics on :9090
OP->>API: patch Agent.status (conditions, observedGeneration, contract)
sequenceDiagram
participant AG as agent
participant MG as modelgateway
participant KW as kube (source IP → pod)
participant SEC as ModelPool Secret
participant P as provider
participant DB as Postgres
Note over AG: secret-free — dials INTELLIGENCE=https://…/v1/infer keyless
AG->>MG: POST /v1/infer (identity = source IP)
MG->>KW: resolve source IP → namespace / agent
MG->>DB: reserve estimate under per-pool lock
alt within budget (pool + per-fleet)
MG->>SEC: read provider credential
MG->>P: forward with key injected
P-->>MG: completion + usage
MG->>DB: reconcile reservation → true tokens
MG-->>AG: completion
else over budget
MG-->>AG: HTTP 429
end
sequenceDiagram
participant AG as agent
participant MCP as mcpgateway
participant KW as kube (source IP → pod)
participant SEC as MCPServerSet Secret
participant SRV as remote MCP server
AG->>MCP: POST /s/<server> (MCP JSON-RPC, keyless)
MCP->>KW: attest source IP → (ns, agent)
MCP->>MCP: scope to servers of the agent's bound MCPServerSets
alt server in scope
MCP->>SEC: read server credential (off-pod)
MCP->>SRV: forward with Authorization injected (Streamable HTTP + SSE passthrough)
SRV-->>MCP: tool result
MCP-->>AG: tool result
else out of scope
MCP-->>AG: 403
end
sequenceDiagram
participant PR as producer
participant CO as coordination
participant SC as scaler
participant KE as KEDA
participant FL as fleet (0..N)
PR->>CO: work.submit(item) → work_id
loop poll
KE->>SC: IsActive / GetMetrics (gRPC)
SC->>CO: work.stats → pending
end
KE->>FL: scale 0 → N (scale from zero)
FL->>CO: work.claim(item, ttl) — N racers
CO-->>FL: granted to exactly ONE; others see the holder
FL->>FL: process (infer + tools)
FL->>CO: work.renew at ttl/3; work.ack on success
Note over CO: claim key deduped; expired lease re-offers on crash; maxAttempts → dead-letter (dlq://items)
SC->>CO: work.stats → 0
KE->>FL: scale N → 0
Distribution is pull/claim, not push: the only assignment is the atomic claim picking one winner of N racers. Producers submit references; the payload bytes live in your own store.
sequenceDiagram
participant C as A2A client / peer
participant GW as gateway
participant DB as Postgres
participant AG as agent
participant WH as push webhook
C->>GW: GET /agents/{ns}/{name}/.well-known/agent-card.json
GW->>AG: read capabilities (mTLS /mcp)
GW-->>C: JWS-signed Agent Card
C->>GW: message/send (auth: OIDC / trusted-proxy / bearer)
GW->>DB: persist Task (durable)
GW->>AG: a2a.SendMessage over mTLS /mcp (client cert = Management)
AG-->>GW: {"task": …} result (or SSE frames for message/stream)
GW->>DB: update Task → completed
opt push registered
GW->>WH: POST result (SSRF-guarded: https + public-IP only)
end
GW-->>C: result / SSE
Push webhooks are validated at registration and re-validated on delivery: only
https:// URLs that resolve to globally-routable addresses are accepted
(loopback, RFC1918/CGNAT, link-local incl. the metadata IP, and IPv6
ULA/link-local are rejected), and the connection is pinned to the validated
address.
A fleet can be a "main agent + workers" system. The coordinator decomposes a
request and fans subtasks to the elastic worker pool — over the work fabric
(default queue) or via A2A delegation (a2a).
flowchart TB
client[A2A client] -->|message/send| gw[gateway]
gw -->|front door| coord["coordinator (main agent)"]
subgraph fabric[Work fabric — default queue distribution]
coord -->|work.submit| co[coordination]
co -->|work.claim| w0[worker 0]
co -->|work.claim| w1[worker 1]
co -->|work.claim| wn[worker N]
w0 -->|work.ack result| co
w1 -->|work.ack result| co
wn -->|work.ack result| co
coord -->|work.result correlate| co
end
coord -.->|a2a distribution: --a2a-peer worker=…/fleets/ns/name| gw
work.submit returns a work_id; work.ack records a result; work.result
correlates the outcome; an item is dead-lettered after work.maxAttempts
redeliveries. A fleet's budget is enforced by the modelgateway alongside the
pool budget. Coordinator replicas (>1) are peers, not shards: they coordinate
through the work fabric like any other producer.
Durable state lives in Postgres so the gateways stay replicated, stateless front
ends; the coordination server keeps its serializing ledger in memory by default.
Postgres is optional and bundled by the chart (postgres.mode: bundled |
external); the in-memory paths are the single-replica defaults.
| Store | Owner | Tables | Contents |
|---|---|---|---|
| A2A tasks | gateway | a2a_tasks, a2a_push_configs |
Durable task records + push-webhook configs. |
| Intelligence usage | modelgateway | intelligence_usage, intelligence_reservation |
Committed token ledger + in-flight budget reservations. |
| Coordination work | coordination (store: postgres) |
work_items |
Work items keyed by claim_key (grant-one across replicas). |
The bundled Postgres hop is plain NoTls in-cluster by default (NetworkPolicy-
scoped); in-cluster TLS to the database is opt-in hardening
(postgres.bundled.tls). When store: memory, run one coordination replica —
its claim ledger is a single in-process Mutex, and losing it collapses the
serializing point for dependent fleets.
The full model is in security.md; in brief:
- Inbound identity to an agent is a verified mTLS client certificate authenticating the caller as the Management origin — the only origin allowed to drive management/A2A verbs on the agent. Server-name verification is skipped on these hops (the agent is addressed by dynamic pod IP); the CA is the trust anchor.
- Outbound identity from an agent is its attested source IP. A confined pod
drops
CAP_NET_RAWand so cannot spoof it. If a request also carries a self-assertedX-Agent-*identity that disagrees, the attested identity wins. - Agents are secret-free: the gateways hold and inject every provider/tool credential off-pod.
- Pods are hardened to the
restrictedPSS (nonroot, no privilege escalation, all caps dropped, read-only root fs, no auto-mounted SA token). - Tenant isolation is enforced by default-deny NetworkPolicies (egress only to DNS + the control-plane gateways; ingress only from the control-plane namespace), shipped by the chart and reconciled per-namespace by the operator.
- Admission enforces an image-registry allow-list and gates the lethal trifecta (exec + egress + secrets) behind an explicit annotation.
- All control-plane TLS is issued by cert-manager; management access is RBAC-gated via SubjectAccessReview.
spec.substrate selects the isolation tier. The rendering core recognizes three
tier ids — stock-unix, kata-hybrid, sidecar-emptydir — but stock-unix
is the only tier the renderer currently emits: it renders ordinary hardened
Kubernetes pods. A CR requesting kata-hybrid or sidecar-emptydir is rejected
with an UnsupportedSubstrate render error (surfaced as Validated=False) until
those tiers are wired. Leaving substrate unset resolves to stock-unix.
- operations.md — install, day-2 runbooks, tuning.
- security.md — the full trust and isolation model.
- benchmarks.md — throughput, latency, and density numbers.