How the CubeSandbox Credential Vault Prevents Secrets from Entering Sandbox Memory

The CubeSandbox credential vault injects secrets at the L7 proxy layer (CubeEgress) rather than inside the micro-VM, ensuring sensitive tokens never reside in sandbox memory.

CubeSandbox isolates untrusted user code inside micro-VMs. When that code needs to authenticate with external APIs, the credential vault architecture guarantees that secrets remain outside the sandbox boundary, eliminating the risk of memory dumps or side-channel leaks exposing sensitive credentials.

The L7 Injection Architecture

The credential vault operates by intercepting outbound HTTPS traffic at the host level and injecting authorization headers only after the request has exited the sandbox. This creates a strict security boundary where the micro-VM never possesses the raw secret value.

The request flow works as follows:

  1. Sandbox user code issues an HTTP request without any Authorization header
  2. CubeVS (the kernel eBPF layer) allows the outbound connection and tags the flow for L7 processing
  3. CubeEgress (the OpenResty/Lua proxy) receives the request and matches it against per-sandbox policies containing action.inject rules
  4. Credential Vault (host-side storage, typically under /root/.pi/agent or integrated with a secret manager) supplies the real secret via the vault:get lookup
  5. Injection occurs as CubeEgress substitutes the secret into the header (e.g., Authorization: {{SECRET}}) before forwarding to the external service
  6. Response returns through the proxy unchanged to the sandbox

In CubeEgress/lua/policy.lua, the injection logic validates the rule before performing the lookup:

-- CubeEgress Lua policy (simplified)
if r.action.inject then
    for _, inj in ipairs(r.action.inject) do
        -- `inj.secret` is the identifier; the proxy looks it up in the vault
        local secret = vault:get(inj.secret)   -- never exposed to sandbox
        ngx.req.set_header(inj.header, secret)
    end
end

Configuring Secret Injection Policies

Policies are serialized via network-agent/internal/cubeegress/wire.go and translated by the SDK into action.inject configurations. The Python SDK (sdk/python/cubesandbox/_policy.py) converts high-level declarations into the wire format expected by CubeEgress.

Python SDK Example:

from cubesandbox import CubeEgress

policy = CubeEgress(
    rules=[
        {
            "match": {"host": "api.openai.com"},
            "action": {"allow": True, "inject": [{"header": "Authorization", "secret": "OPENAI_API_KEY"}]},
        }
    ]
)

sandbox = CubeSandbox()
sandbox.run(
    code="import requests; requests.get('https://api.openai.com/v1/models')",
    egress_policy=policy
)   # No key in the sandbox code

Go SDK Example:

policy := []EgressRule{
    {
        Match: Match{Host: "api.openai.com"},
        Action: Action{
            Allow:  true,
            Inject: []Inject{{Header: "Authorization", Secret: "OPENAI_API_KEY"}},
        },
    },
}
sandbox := cubesandbox.New()
sandbox.Run(context.Background(), "curl https://api.openai.com/v1/models", policy)

The examples/pi-agent-integration/run_pi_agent.py file demonstrates this pattern explicitly, noting that "the key never enters the VM" while still allowing authenticated outbound requests.

Security Guarantees and Observability

The architecture provides three critical security guarantees:

  • Zero-copy of secrets – The secret value exists only in the CubeEgress host process; the sandbox’s memory and network buffers never contain the raw credential
  • Policy-driven validation – Injection only occurs for explicitly allowed destinations validated by CubeEgress/lua/policy.lua against the per-sandbox policy
  • Automatic log redaction – CubeMaster sanitizes all logs to prevent accidental exposure. In cubelog/logger.go, secret values are replaced with ***REDACTED*** before any output reaches operators or logging systems

This design ensures that even if an attacker gains full control of the sandbox micro-VM, they cannot extract secrets from memory or network traffic because the credentials materialize only at the host-side proxy layer.

Summary

  • Secrets are injected at the CubeEgress L7 proxy layer, never reaching sandbox RAM or storage
  • Policy-driven injection via action.inject rules validated in CubeEgress/lua/policy.lua restricts secrets to specific HTTPS destinations
  • Host-side credential vault lookups occur outside the micro-VM boundary, maintaining strict isolation
  • Automatic redaction in cubelog/logger.go prevents accidental secret exposure in operational logs
  • The examples/pi-agent-integration/run_pi_agent.py reference implementation demonstrates production-ready secret isolation

Frequently Asked Questions

Where does the credential vault store the actual secret values?

The credential vault operates as a host-side store, typically located at /root/.pi/agent or integrated with external secret managers like AWS Secrets Manager or HashiCorp Vault. When CubeEgress evaluates an action.inject rule, it calls vault:get(inj.secret) to retrieve the credential from this host-side storage, ensuring the lookup never crosses into the sandbox boundary.

How does the system prevent a compromised sandbox from extracting arbitrary secrets?

The CubeEgress/lua/policy.lua enforces strict destination matching before injection occurs. Secrets are only injected for HTTPS requests matching specific host patterns defined in the per-sandbox policy. A sandbox cannot request injection for arbitrary domains because the proxy validates the rule against the whitelist before performing the vault lookup, preventing credential exfiltration to attacker-controlled endpoints.

Can sandbox code detect that secret injection is occurring?

No. From the sandbox's perspective, it sends unauthenticated HTTP requests and receives authenticated responses transparently. The injection happens at the network layer (CubeEgress) after the request leaves the micro-VM. The sandbox process sees no Authorization headers in its memory, no credential files on disk, and no environment variables containing secrets, making the injection mechanism completely opaque to the running code.

What happens if the credential vault is temporarily unavailable?

If the vault lookup fails or the secret is missing, CubeEgress will not substitute the placeholder (e.g., {{SECRET}}) with a real value. Depending on the policy configuration, the request will either proceed without the header (likely causing the external service to reject it) or be dropped by the proxy, but in neither case will an invalid or empty secret leak into the sandbox memory.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →