Architecture
Marshal gives every AI coding agent session its own on-demand, isolated Kubernetes environment. This page explains how that’s actually built: what runs all the time versus what exists only for the length of a session, how one session is kept apart from another and from the node it runs on, how credentials reach a tool without ever reaching the agent, who can see and control what, and where state lives. If you’re evaluating Marshal for security or architecture fit, this is the page to read closely.
Solid arrows are always-live connections; the dashed arrow (supervisor → mcps) is optional, present only when a session’s manifest asks for MCP servers. Full detail on each component is below.
Control plane vs. session plane
Marshal is split into two very different lifecycles.
The control plane is a small set of always-on services, deployed once by the Helm chart and running for as long as the installation exists:
| Component | Role |
|---|---|
api | Auth, session lifecycle, org/RBAC, credential vault, pod provisioning |
gateway | Terminal/WebSocket routing — bridges a browser or CLI connection to the right session pod |
web | The dashboard — manage orgs, credentials, sessions, settings |
marketplace | In-cluster catalog service — merges the upstream tool/agent/MCP catalog with any customer-added packages |
These four are ordinary Kubernetes Deployments. They scale independently, have their own resource budgets, and are the only things a customer’s platform team needs to operate day to day.
The session plane is everything else, and it’s ephemeral by design. Each
active coding session gets its own pod, created by the api at session-start
and deleted (or stopped) when the session ends:
- a supervisor container — the pod’s PID 1, and the only thing in the pod that ever touches real credentials or unfiltered network egress
- an agent container — where the actual coding agent (Claude Code, or another supported agent) runs
- an optional mcps sidecar, if the session’s manifest asks for MCP servers
A session pod exists only while that session is active. Stop the session and the pod is gone; there is no persistent per-session compute sitting idle between uses. This split — a handful of long-lived control-plane services versus a growing and shrinking population of session pods — is the shape of the whole system.
Isolation model
Every session pod is isolated from every other session pod, and from the control plane, on three layers:
Pod and namespace scoping. Session pods run in a dedicated namespace
(<release>-spaces), separate from the namespace the control-plane
Deployments run in. Each pod is labeled with its session, org, and user IDs,
and the api’s Kubernetes access to that namespace is a namespace-scoped
Role/RoleBinding — not a ClusterRole — so operating Marshal never
requires cluster-admin.
Network policy. Each session pod gets its own NetworkPolicy scoped to
that pod’s label selector. By default the agent container’s egress is
locked down to DNS; all other outbound traffic — HTTP, HTTPS, tool
connections — is forced through the supervisor, which enforces an allowlist
and logs what it sees. One session’s agent cannot reach into another
session’s pod, and cannot reach the internet except through its own
supervisor’s mediated path.
Shared nodes, minimally-scoped node identity. For efficient bin-packing, session pods from different sessions (and different orgs, on a multi-tenant install) commonly land on the same Kubernetes node. That’s a deliberate scheduling choice, not an oversight — but it has a consequence Marshal’s design accounts for explicitly: any pod on a node can, in principle, reach that node’s own service-account identity (for example via the cloud metadata endpoint), so the node’s service account must never carry more privilege than the node itself strictly needs. Marshal keeps node-level service accounts minimal and puts anything more privileged — like the supervisor’s object-storage access for durable session volumes — behind a dedicated identity that’s bound only to the supervisor container, not ambient on the node. The practical implication for you: don’t grant the underlying node’s cloud identity broad roles “for convenience” in your own cluster config, since any session’s code effectively shares that node.
Credential flow
This is the architectural decision most evaluators care about most, so it’s worth stating plainly:
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. Every credential a tool needs is injected at the moment of use, by the supervisor, into that tool’s own subprocess — and nowhere else.
Concretely: 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 isn’t
invoking the real binary directly. It’s invoking a thin wrapper that talks to
the supervisor over a Unix socket local to the pod. The supervisor looks up
what that tool call needs, execs the real binary itself, and injects the
credential into that subprocess’s environment — a process the supervisor
owns, in a part of the pod the agent container cannot introspect. Output
streams back to the agent normally; the credential itself never crosses into
agent-owned memory, filesystem, or environment. The same pattern covers
outbound HTTP: the agent’s traffic is transparently routed through a
supervisor-side proxy, which is the only place in the pod where a
credential-bearing header could be added.
The rationale is blast-radius containment. The agent container runs a general-purpose AI coding agent driven by an LLM interpreting instructions from a repository, a task, and whatever it reads along the way — it’s the most exposed, least predictable part of the pod. By construction, even a fully compromised or manipulated agent process has no credential to exfiltrate, because it was never handed one.
Underlying all of this, credentials at rest are envelope-encrypted in the control plane’s vault: each credential has its own per-credential encryption key, which is itself wrapped by a KMS (cloud KMS or, for self-hosted deployments without one, HashiCorp Vault’s Transit engine). The vault API never returns a stored credential’s value once it’s written — not to the dashboard, not to any API caller, regardless of role.
Access control
Marshal’s authorization model is deliberately narrower than “anyone in my
org can see everything.” Org membership grants a role — owner, admin, or
member — but a session (a “space”) belongs to the member who created it,
not to the org at large. By default, a member only ever sees and controls
their own sessions; owner and admin get org-wide read visibility (for
accountability — knowing what’s running) but not the ability to act on
someone else’s session or see the value of someone else’s stored credential.
Org-wide visibility into other members’ resources is available to
owner/admin roles, but it’s an explicit opt-in on specific endpoints, never
the default view — opening the dashboard never surfaces another member’s
sessions just because you happen to hold an admin role.
Datastores
Marshal uses two datastores, and they play very different roles:
- PostgreSQL is the durable source of truth. Sessions, orgs and their membership, encrypted credentials, manifests, and the audit trail all live here. This is the data you cannot afford to lose.
- Redis is a pure ephemeral cache. WebSocket session tokens, live SSE event streams, the registry mapping a session to its current pod address, and in-flight OAuth state all live here — and nothing stored in Redis is unique. If Redis were lost entirely, every one of those things could be reconstructed or would simply be re-issued on the next request.
The operational takeaway: back up Postgres. Redis needs no backup story at all — losing it costs you some in-flight session tokens and live connections that reconnect on their own, not data.
Deployment model
Marshal ships as a single Helm chart, and the chart’s pinned version is the product version — one number that pins an exact, tested set of component images (the control-plane workloads and the runtime images stamped into session pods alike). What you install is what was tested; upgrades and rollbacks are one-command operations against that same pinned set. The full mechanics — registry access, the Bill of Materials, secrets providers, and step-by-step installation on EKS, GKE, or a single node — are covered in Installing Marshal.