Currently, we pass the secrets in to the banded VM from the shell environment of the orchestrating agent. This means the outer loop agent has access to the secrets.
It would be better if the secrets were injected by a separate service.
Proposal: Brokered Secrets
Introduce a secret broker — a separate privileged process that owns secret resolution end-to-end. The outer agent never touches plaintext secret values.
Current flow (vulnerable)
Outer agent process (has process.env access)
→ exec.ts reads process.env[SECRET_NAME] ← agent can see values
→ passes plaintext in execReq.secrets
→ band-server injects into VM
Proposed flow
Outer agent process
→ exec.ts passes only secret NAMES (references)
→ executor contacts secret broker (socket/API)
→ broker resolves names → values from a store the outer agent cannot access
→ broker injects secrets directly into the VM (or returns them only to band-server inside the VM)
→ ExecutorResult returned to outer agent (no secrets)
Key design points
- Outer agent only handles secret names, never values. The
envSecrets dictionary in exec.ts becomes envSecretRefs: string[].
- Secret store is inaccessible to the outer agent. Options: a Unix socket with UID-based access control, a file readable only by the broker's user, or a vault API with a token the outer agent doesn't have.
- Broker validates requests against the band's declared
env.secrets. The broker reads the band config independently and only injects secrets the band is authorized to receive — the outer agent can't request arbitrary secrets.
- Transport between broker and VM is direct. The broker either writes secrets into the VM's workdir (mounted volume) or posts them to the band-server's
/exec endpoint over a channel the outer agent can't intercept (e.g., a VM-only network interface).
Implementation sketch
- New component:
packages/runtime/src/secret-broker.ts — a lightweight server (Unix socket) that accepts { bandId, secretRefs } and returns nothing to the caller (injects directly into the VM).
- Modify
exec.ts: replace process.env[key] resolution with a call to the broker.
- Modify
band-server.ts: accept secrets from the broker channel instead of the executor request body.
- Strip secrets from
process.env before the outer agent's LLM session starts (or run the outer agent in a restricted environment that doesn't have the secret env vars at all).
Open questions
- Should the broker be a long-running sidecar or started on-demand per execution?
- How does the broker authenticate the executor's identity (to prevent a compromised outer agent from impersonating the broker channel)?
- Should we support pluggable backends (env, file, HashiCorp Vault, 1Password CLI, cloud KMS)?
Currently, we pass the secrets in to the banded VM from the shell environment of the orchestrating agent. This means the outer loop agent has access to the secrets.
It would be better if the secrets were injected by a separate service.
Proposal: Brokered Secrets
Introduce a secret broker — a separate privileged process that owns secret resolution end-to-end. The outer agent never touches plaintext secret values.
Current flow (vulnerable)
Proposed flow
Key design points
envSecretsdictionary inexec.tsbecomesenvSecretRefs: string[].env.secrets. The broker reads the band config independently and only injects secrets the band is authorized to receive — the outer agent can't request arbitrary secrets./execendpoint over a channel the outer agent can't intercept (e.g., a VM-only network interface).Implementation sketch
packages/runtime/src/secret-broker.ts— a lightweight server (Unix socket) that accepts{ bandId, secretRefs }and returns nothing to the caller (injects directly into the VM).exec.ts: replaceprocess.env[key]resolution with a call to the broker.band-server.ts: accept secrets from the broker channel instead of the executor request body.process.envbefore the outer agent's LLM session starts (or run the outer agent in a restricted environment that doesn't have the secret env vars at all).Open questions