Control plane and data plane
AgentArea splits into two halves with different jobs and different threat exposure. The control plane decides: it owns workspaces, projects, task metadata, policy, quotas, routing, and audit records. The data plane executes: it runs commands, holds files, produces artifacts, and touches plaintext task data. The split is the reason execution can move into a customer’s own network while the platform stays where it is. It is also the reason certain things are deliberately awkward — the control plane is not allowed to know a pod name.The problem
Agent platforms handle two categories of information that want opposite treatment. Orchestration metadata — which task belongs to which workspace, what policy applies, who approved what — is small, needs to be durable and queryable, and benefits from central management. Task payload — the contents of a file the agent is analysing, the output of a command, the text a model produced — is large, frequently sensitive, and often subject to residency requirements that say it may not leave a jurisdiction or a customer network. Build these as one system and you get a platform that cannot be adopted by anyone with a data-residency obligation, because running the orchestrator means handing over the payload. The alternative most vendors choose is a fully on-premises installation, which shifts the entire operational burden to the customer. Separating the planes lets the two halves be hosted differently.How AgentArea approaches it
What each plane owns
Two constraints keep the boundary honest:
- The control plane must not depend on pod names, runner URLs, or provider-specific sandbox paths. Depending on any of these would couple it to one data-plane implementation and make a customer-hosted runner impossible.
- Raw stdout, large logs, files, and artifacts must not enter workflow history or control-plane event payloads. They are written to data-plane storage and referenced by handle.
The request path across the boundary
Execution is a durable job hand-off, not a synchronous call: The control-plane API is served by the Go MCP manager, which registers:POST /sandbox/executions only creates a pending record and publishes a request
event. It does not run anything. A data-plane runner claims the work from a Redis
Streams consumer group, executes it, and acknowledges the request message only
after terminal state is stored — so a runner that dies mid-command leaves the
message unacknowledged and the work reclaimable.
Events are CloudEvents-compatible JSON envelopes on two streams, named in
internal/sandboxcontrol/models.go:
agentarea.sandbox.execution.requestsagentarea.sandbox.execution.events
claimed, started, completed, failed, and output
artifact references. Agent shell and skill execution clients go through
/sandbox/executions and read completion from the execution record; they never
call a runner URL.
The contract is a protocol, not a URL
What makes a data plane replaceable is that the control plane depends on a set of operations rather than an address: create or resume a session · start an execution · read execution status · read logs by reference · collect artifacts by reference · cancel an execution · snapshot and restore a workspace · pause or terminate a session Any runtime implementing those can be substituted: the AgentArea Kubernetes runner, a customer-hosted runner claiming jobs outbound, or a provider adapter. The portable unit is a workspace snapshot, an artifact manifest, and session metadata. Live process memory is not portable, so a session cannot be migrated mid-command between runtimes.Deployment shapes
In hybrid mode the customer runner connects outbound to claim work. There is
no inbound path from AgentArea into the customer network, which is what makes the
mode deployable behind a firewall without an ingress exception.
Why not run everything in the customer’s network?
Fully self-hosted is supported and is the right answer for some organizations. It is not the default because it transfers the entire operational burden — upgrades, Temporal, Postgres, object storage, the authorization service, and the consequences of misconfiguring any of them — to the customer’s team. The hybrid split exists because the residency requirement is almost always about the payload, not the metadata. A customer who cannot let file contents leave their network is usually content for the record “task 91f3 ran and failed policy check X” to live in a managed control plane. Separating the planes lets that customer buy an operated platform and still keep the bytes.Why not one synchronous execute call?
A synchronous/sandbox/execute is simpler and does exist as a compatibility
path for the AgentArea-managed Kubernetes warm pool. It is not the production
contract, for three reasons.
Long work outlives HTTP. An agent command can run for many minutes. A
synchronous call ties completion to a socket surviving that long, through every
proxy and load balancer between the two planes.
It puts payload in the wrong place. Returning stdout in the response body
pulls output through the control plane, which is the thing the boundary exists to
prevent, and lands large blobs in workflow history.
It requires an inbound route. Calling a runner means addressing it, which
means the control plane knows a URL and the customer network accepts inbound
connections. The claim model inverts this: the runner reaches out, and the
control plane never learns where it is.
The cost is that the asynchronous path is harder to debug — a failure is
distributed across a record, a stream, and a runner log, rather than visible in
one response. SANDBOX_EXECUTOR_URL and other direct runner URLs are explicitly
not production extension points, precisely because they are the tempting shortcut
back to the synchronous model.
Limits
- Hosted mode is not data residency. If hosted workers, hosted sandbox runners, or a hosted LLM proxy receive plaintext task data, residency claims do not hold. The boundary only helps when the data-plane components are genuinely in your environment.
- Metadata is not nothing. Task names, file names, artifact paths, policy decisions, and audit records live in the control plane by design. If your sensitivity extends to identifiers and filenames, the hybrid split is insufficient.
- The synchronous path still exists. Deployments using the compatibility
/sandbox/executeroute do not get the durability or the payload separation described here. - Snapshots bound portability. Moving between runtimes means restoring a workspace snapshot. Anything not captured in the snapshot — running processes, in-memory state, unflushed buffers — does not survive.
- The control plane still schedules. It decides what runs, so a compromised control plane can direct a customer-hosted data plane to execute attacker-chosen work. The split protects confidentiality of payload at rest and in transit, not integrity of instruction.
Related
- Agentic networks — why some labels cannot be enforced across this boundary
- Why a sandbox — the threat model on the data-plane side
- Durable execution — how the workflow waits without holding a connection
- Open core — which deployment shapes are commercial