Architecture
Marshal gives every AI agent its own on-demand, isolated Kubernetes environment, called a space. This page shows how that is built: what runs all the time and what exists only while a space is active, how one space is kept apart from another and from the node it runs on, how credentials reach a tool without reaching the agent, who can see and control what, and where state lives. If you are evaluating Marshal for security or architecture fit, start here.
The control plane is always on, one per install:
Each space gets its own pod. api creates and stops it, and gateway carries its terminal I/O:
Every request from a browser or the CLI enters through web. It serves the dashboard and
passes API calls to api and terminal connections to gateway. The dashed arrow is the
optional mcps sidecar, present only when the space’s manifest asks for MCP servers.
Control plane and space plane
Marshal has two very different lifecycles.
The control plane is a small set of always-on services. The Helm chart deploys them once, and they run for as long as the installation exists:
| Component | Role |
|---|---|
api | Sign-in, space lifecycle, organizations and roles, the credential vault, pod provisioning |
gateway | Terminal routing: connects a browser or CLI terminal to the right space pod |
web | The dashboard for organizations, credentials, spaces and settings. Also the entry point that routes API and terminal traffic |
marketplace | In-cluster catalog service. It merges the Marshal catalog of tools, agents and MCP servers with any packages you add |
These four are ordinary Kubernetes Deployments. They scale independently, have their own resource budgets, and are the only services your platform team operates day to day.
The space plane is everything else, and it is ephemeral by design. Each active space gets
its own pod. The api creates it when the space starts and removes it when the space stops.
The pod holds:
- a supervisor container: the only part of the pod that touches real credentials or unfiltered network egress
- an agent container: where the coding agent runs, such as Claude Code or another agent from the marketplace
- an optional mcps sidecar, when the space’s manifest asks for MCP servers
A space pod exists only while the space is active. Stop the space and the pod is gone, so no compute sits idle between uses. A handful of long-lived control-plane services next to a growing and shrinking set of space pods: that is the shape of the whole system.
Isolation model
Every space pod is isolated from every other space pod, and from the control plane, on three layers.
Pod and namespace scoping. Space pods run in their own namespace (<release>-spaces by
default), separate from the control-plane namespace. Each pod is labeled with its space,
organization and member IDs. Every write the api makes to Kubernetes is limited to that
namespace and the control-plane namespace, through namespace-scoped Role and RoleBinding
objects. The only cluster-wide grant is a read-only ClusterRole (<release>-node-reader)
that reads nodes, pods and storage classes for capacity views. Operating Marshal never
requires cluster-admin.
Network policy. Each space pod gets its own Kubernetes NetworkPolicy, scoped to that
pod. A space looks up names through the cluster’s own DNS resolver and can send DNS to no other
server. All other outbound traffic (HTTP, HTTPS,
tool connections) goes through the supervisor, which enforces the space’s
network policy and records what it sees. One space’s agent cannot
reach into another space’s pod, and it reaches the internet only through its own supervisor.
Shared nodes, minimal node identity. Space pods from different spaces share nodes, which keeps the cluster efficiently packed. Any pod on a node can, in principle, reach that node’s own service-account identity (for example through the cloud metadata endpoint). So Marshal keeps node-level service accounts minimal. Anything more privileged, such as the supervisor’s object-storage access for saved workspaces, uses a dedicated identity bound only to the supervisor, never ambient on the node. In your own cluster, keep the node’s cloud identity free of broad roles, since code from any space shares that node.
Credential flow
The agent process never holds a real credential. Not in its environment, not in a file it can read, not anywhere in its own container. The supervisor injects each credential at the moment of use, into the process of the tool that needs it, and nowhere else.
When the agent runs a tool that needs a credential (a git push, a cloud CLI call, an API
request signed with a token), it does not call the real binary. It calls a thin wrapper that
talks to the supervisor over a Unix socket inside the pod. The supervisor looks up what that
call needs, runs the real binary itself, and injects the credential into that subprocess
only. The agent container cannot inspect that process. Output streams back to the agent as
normal, and the credential never enters agent-owned memory, files or environment.
Outbound HTTP works the same way. The agent’s traffic passes through a supervisor-side proxy. For an API key sent over HTTP, the agent sees only a placeholder, and the proxy swaps in the real value for the hosts the package declared. See Credentials and connections.
The reason is blast-radius containment. The agent container runs a general-purpose AI agent that follows instructions from a repository, a task, and whatever it reads along the way. It is the most exposed part of the pod. Even a fully manipulated agent process has no credential to leak, because it never had one.
At rest, credentials are envelope-encrypted in the control plane’s vault. Each credential has its own encryption key, wrapped by a KMS: your cloud KMS, or the Transit engine of HashiCorp Vault on installs without one. Once a credential is saved, the vault never returns its value: not to the dashboard, and not to any API caller, whatever their role.
Access control
Organization membership grants a role: owner, admin or member. A space belongs to the
member who created it, not to the organization at large. A member sees and controls their own
spaces. Owners and admins can read what runs across the organization, for accountability,
but they cannot act inside someone else’s space or see the value of anyone’s stored
credential. That org-wide view is an explicit choice on specific pages, never the default:
opening the dashboard shows your own spaces, whatever your role. See
Owners, teams and scope and
Roles and permissions.
Datastores
Marshal uses two datastores, with very different roles.
- PostgreSQL is the durable source of truth. Spaces, organizations and their members, encrypted credentials, manifests and the audit log all live here. This is the data to protect.
- Redis is an ephemeral cache. Terminal connection tokens, live event streams, the map from a space to its current pod address, and in-flight OAuth state live here. Nothing in Redis is unique. If Redis is lost, each of these is rebuilt or issued again on the next request.
Back up Postgres. Redis needs no backup: losing it costs some in-flight tokens and live connections, which reconnect on their own.
Deployment model
Marshal ships as a single Helm chart, and the chart version is the product version. One number pins an exact, tested set of component images: the control-plane services and the runtime images used in space pods. What you install is what was tested, and upgrades and rollbacks are one command against that same pinned set. Registry access, secrets providers, and step-by-step installation on EKS, GKE or a single node are in Installing Marshal.