A coding agent on a VM is risky for a specific, well-understood reason. To do its
job it needs three things at once: access to your secrets and source, the ability
to read untrusted content (issues, PRs, web pages, dependency docs), and the
ability to reach the network. Any one of those is fine. All three together is
the problem — a prompt-injected agent can read a secret, be told to leak it, and
have a channel to send it out.
This is Simon Willison’s lethal trifecta:
an agent that simultaneously has (a) access to private data, (b) exposure to
untrusted content, and (c) the ability to communicate externally is exposed to
data theft, and prompt-injection detection is too unreliable to depend on. Remove
any one leg and the data-theft class collapses. Meta’s
Agents Rule of Two
makes it a rule: an agent session should satisfy at most two of these three —
processes untrusted input, accesses sensitive data, or can communicate externally;
if it genuinely needs all three, keep a human in the loop. Google DeepMind’s
CaMeL (paper: Defeating
Prompt Injections by Design) pushes the same principle into a capability-enforcing
interpreter.
This page maps each leg to the controls you have in Murmur — what you decide,
what you configure, and what the platform enforces underneath so you don’t have to.
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_refs
list 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 ls shows 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’s secret_refs,
above.
What the platform enforces for you:
-
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-vm redacts 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.
Scrubbing is exact-match only — it catches a string equal to a secret in its
entirety, not a secret embedded in a larger string or transformed (e.g.
base64-encoded). It is a conservative backstop, not a guarantee, and it does not
inspect traffic the agent sends directly to the network — that’s the egress leg.
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 the agent.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:
| Tier | Who may steer |
|---|
OPEN | Any author |
COLLABORATORS | Owners, members, and repo collaborators (write/triage) |
MEMBERS | Org owners and members |
ALLOWLIST_ONLY | Only the named allowlists (plus a user-owned agent’s own owner) |
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 to
OPEN (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.
Allowlists (named sets of usernames, scoped by identity provider) let you admit
specific trusted outsiders or approved bots — e.g. 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
Because the VM is in your account, you own the network path end to end — your
rules, your routes, your egress, your logs. If a VM misbehaves it’s in your
project: you can capture logs, snapshot the disk, or shut it down directly. See
Customer placements for setup and
Placements for the concept.
On the default platform placements, agent VM egress is not restricted — VMs
can reach the public internet. If your threat model requires cutting the egress
leg, run your agents in a customer placement and apply your own network controls
there.