Skip to content

Outer loop agent should not need to have access to secrets #8

@mrjf

Description

@mrjf

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

  1. Outer agent only handles secret names, never values. The envSecrets dictionary in exec.ts becomes envSecretRefs: string[].
  2. 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.
  3. 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.
  4. 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)?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions