Part 6

Architecture side by side

This page names the binaries, ports, and stores in each project, so you can see which process answers which request and where each one keeps its data.

After this page you can:

  • Name the control plane process of each project and the port it listens on.
  • Say which component runs inside each sandbox.
  • List every store each install depends on, including the ones Substrate brings.

Two control planes on one substrate

Both projects schedule their workloads as Actors on Agent Substrate, which is described on the Substrate page. Above that shared layer they look different. ax has one gRPC server that keeps its state in Redis and reconciles directly with Substrate.2 kagent has a controller that watches Kubernetes custom resources, keeps runtime state in PostgreSQL, and exposes gRPC, A2A, and MCP APIs to clients.8

Figure: ax routes every request through one gRPC server backed by Redis, while kagent splits configuration into Kubernetes and runtime state into PostgreSQL behind a controller on port 8083.

Control plane and entrypoints

The table below covers the processes a request passes through, from the client to the code that runs inside the sandbox.

Processes and ports in each project
Componentaxkagent
Control plane processax: ax-server, a gRPC API on port 8080 with GET /healthz on the same port.2kagent: the controller. Port 8083 serves native gRPC, gRPC-Web, A2A, authenticated MCP, and health.10
Client toolsax: the ax CLI, which applies manifests, watches resources, and tunnels to the cluster.2 No web UI is documented.1kagent: a web UI and a CLI, both of which manage agents and tools and chat with agents.8
Process inside the sandboxax: ax-task-runner runs as PID 1, serves metadata on port 80, prepares workspaces, then starts spec.command.5kagent: the runtime selected by the Harness (Go or Python ADK, Codex, Claude, or a custom runtime). Actor A2A gRPC is private on port 80 and readiness is private HTTP /readyz on port 8081.810
Substrate endpointax: expects the Substrate Control API at api.ate-system.svc.cluster.local:443.1kagent: ate-api defaults to dns:///api.ate-system.svc:443.10

Stores and install footprint

Each project names one store of its own. Substrate adds more, because the Substrate install includes PostgreSQL and rustfs alongside its own components.12 An ax install therefore runs Redis plus the Substrate PostgreSQL, and a kagent install runs its own PostgreSQL plus the Substrate one.

Stores, namespaces, and packaging
Concernaxkagent
Configuration storeax: Redis, which holds resources, locks, and PubSub.2kagent: the Kubernetes API, as custom resources.9
Runtime and history storeax: Redis.2kagent: PostgreSQL, which holds Sessions, tasks, and conversation history.8
Private sandbox stateax: the workspace paths mounted into the Task.3kagent: a Substrate DurableDir mounted at /data in every Actor.10
Install methodax: make deploy into the ax-system namespace, building images with ko.1kagent: a Helm chart, helm/kagent in the repository.21
Substrate packagingax: expects Substrate to be installed first, in ate-system.1kagent: the Helm chart declares Substrate as an optional subchart, enabled with substrate.enabled.21

How a request reaches the sandbox

Neither project gives a sandbox its own Kubernetes Service. An ax Task is reached through the Substrate atenet-router Service in ate-system, which selects the target with the ate-target-actor header and resumes a suspended Actor before proxying.7 kagent clients never receive Actor addresses. The kagent gateway derives and dials them through the same private router.10

The difference is what sits in front. An ax caller talks to whatever the container serves. A kagent caller talks to the public A2A gateway in the controller, which resolves the conversation to a Session and authorizes the call before routing it.9