Root on the VM is not a boundary
The agent runs as themurmur user with passwordless sudo — root on its own VM.
That’s deliberate. The VM is ephemeral, single-tenant, and holds nothing the agent
can’t already read: its own unsealed developer profile and its own identity token.
An in-VM privilege wall would fence nothing, so Murmur doesn’t pretend it’s one.
All real isolation is enforced server-side — the API authenticates every call with
the agent’s identity token, the Temporal proxy admits only the VM’s own task queue,
and per-tenant KMS keys never touch the VM. What root buys you is self-provisioning:
an agent that needs a system package installs it at runtime (sudo apt-get install,
or npx playwright install --with-deps chromium on an image with Node.js) instead
of stranding on a missing dependency.
The honest cost is defense-in-depth on the murmur-vm supervisor: a root agent
could tamper with it and misreport its own lifecycle events. Those events carry
only the VM’s own identity — they can’t cross a tenant or touch another agent — and
durable state is re-derived from GitHub, not from what the VM says about itself. If
your policy requires a non-root agent anyway, remove /etc/sudoers.d/murmur in
your own recipe bake.
Leg 1 — Secret access
You decide which credentials an agent can reach, and Murmur guarantees those credentials never leave the VM in usable form. What you control:- Scope secrets to where they’re needed. Tenant secrets
are only injected into the workspaces whose
secret_refslist them — keep a secret out of a workspace and no agent there ever sees it. Pass one-off credentials as spawn-time secrets (murmur spawn -e) that live only for that single agent. - Run agents under a narrowed identity. A service profile gives an automated agent its own bot credentials and its own access grants, so a Flight or bot gets exactly the access it needs instead of inheriting a developer’s full scope.
- Secret values are write-only. The API never returns a stored secret value —
murmur secret lsshows names only, and a value can’t be read back once set. RBAC governs who can manage secrets (secret.create,secret.edit,secret.delete) through roles and bindings; which agents actually receive a secret is set by the workspace’ssecret_refs, above.
- End-to-end encryption to the specific VM. Credentials are encrypted at rest under your tenant’s KMS key, re-sealed with NaCl box to the target VM’s ephemeral key, decrypted only in memory, and destroyed with the VM. See Encryption.
- Per-tenant isolation. Each tenant has its own KMS key, and AAD binding makes a secret encrypted for one tenant (or developer) impossible to decrypt for another.
-
Secret scrubbing on the way out. As a backstop,
murmur-vmredacts any session-event field or task response that exactly equals an unsealed profile secret, replacing it with[REDACTED]before it leaves the VM — so an agent that is tricked into echoing a credential can’t ship it back through the control plane.
Leg 2 — Trusted input
A coding agent has to read untrusted content — that’s the job — so you can’t stop injected content from reaching it. What you can control is who is allowed to steer the agent: whose instructions it will act on. Murmur gives you two layers of this.Who in your org can drive an agent (RBAC)
Steering a running agent — queueing follow-ups, updating its task list, killing it — requires theagent.edit permission; acting as an agent requires
agent.assume. You grant these through roles, groups, and
tenant-bindings, and you can scope them with name
patterns so a developer drives only their own agents. An org member without
agent.edit on an agent simply cannot send it instructions.
Which external identities can steer an agent (steering policies)
The riskier input path is events from outside your org — a PR comment, a review, or an issue/PR that opens and triggers a Flight. Murmur gates these with steering policies, so a stranger commenting on a public PR can’t drive your agent. A steering policy sets a minimum tier of trust for the event’s author, plus optional allowlists of specific usernames:
You attach a policy in two places, and the narrower of the two always wins:
- Per repo — set a steering policy on a repo’s config to
govern events coming from that repo. If you set nothing, Murmur auto-selects by
repo visibility: public repos default to
COLLABORATORS, private repos toOPEN(every commenter is already org-vetted). - Per service profile — set a policy on a service profile to gate every agent that runs under it, regardless of which repo the event came from. A locked-down profile can’t be loosened by a permissive repo.
dependabot[bot] — without
opening the tier for everyone.
Enforcement is fail-closed and auditable: an event whose author fails the
effective policy is recorded on the agent’s timeline as BLOCKED and is never
delivered — no follow-up, no checklist task, no wake, no Flight spawn. The agent’s
own owner can always steer their own agent.
Leg 3 — Egress control
Egress control — restricting where an agent VM can send traffic — is the highest-leverage leg, because it’s the one you can cut on a coding agent without taking away its job. In Murmur, egress control is a feature of customer placements. When you run agents in a customer placement, each VM boots in your own GCP project or AWS account, on your subnet — so the network the VM lives in is yours, and you apply whatever native egress controls your cloud provides. There’s nothing Murmur-specific to learn:- VPC firewall rules / security groups that default-deny outbound and allow only the destinations you choose
- A forced egress proxy, NAT gateway, or VPN
- DNS-resolution allowlists and SNI/Host filtering at a managed firewall
- Cloud-native network policy, flow logs, and your own incident response