# How the CubeSandbox Credential Vault Prevents API Keys from Entering the Sandbox

> Discover how the CubeSandbox credential vault protects API keys by storing them in the egress proxy policy layer and injecting them at the network edge, keeping them out of your sandbox.

- Repository: [Tencent Cloud/CubeSandbox](https://github.com/TencentCloud/CubeSandbox)
- Tags: internals
- Published: 2026-07-09

---

**CubeSandbox isolates secrets by storing them in the egress proxy's policy layer and injecting them at the network edge, ensuring API keys never touch the sandbox filesystem, environment variables, or process logs.**

The TencentCloud CubeSandbox project implements a **credential vault** pattern that fundamentally separates sensitive authentication tokens from untrusted code execution environments. Instead of passing API keys into the sandbox through environment variables or mounted files, the system stores secrets centrally within the CubeEgress proxy and injects them only when requests leave the network perimeter.

## Policy-Driven Secret Injection

The CubeSandbox credential vault stores raw secret values inside **L7 egress policies** rather than inside the sandbox instance. According to the source code in [`CubeEgress/lua/policy.lua`](https://github.com/TencentCloud/CubeSandbox/blob/main/CubeEgress/lua/policy.lua), each `EgressRule` can contain an `inject` field that carries the secret inline within the policy object itself.

The policy structure stores these injection rules in a shared Lua dictionary (`policy_store`) that lives in the CubeEgress proxy memory space. Each inject entry follows the format `{header, secret, format}`, where the `secret` field contains the actual API key value. Because this policy object resides in the egress proxy—not the sandbox VM—the secret remains physically separate from the execution environment.

```lua
-- CubeEgress/lua/policy.lua
-- Policies are validated and stored in policy_store
-- The inject field contains: {header = "x-api-key", secret = "sk-...", format = "${SECRET}"}

```

## Proxy-Side Injection at the Access Phase

When outbound traffic matches a rule containing injection directives, the CubeEgress proxy performs the header manipulation during the **access phase** defined in [`CubeEgress/lua/access_phase.lua`](https://github.com/TencentCloud/CubeSandbox/blob/main/CubeEgress/lua/access_phase.lua). The system executes the `inject_gates` function to validate safety constraints—including scheme verification, host validation, and secret size limits (under 64 KiB)—before any credential touches the wire.

If validation passes, the `apply_injects` function renders the final header value by substituting the `${SECRET}` placeholder with the actual secret, then assigns it via `ngx.header[header] = value`. This operation occurs **only in the proxy**, meaning the header materializes on the outgoing request but never exists inside the sandbox's network stack or filesystem.

```lua
-- CubeEgress/lua/access_phase.lua (simplified)
local function apply_injects(decision, inject_list)
    for _, inj in ipairs(inject_list) do
        local ok, reason = inject_gates({inject=inj})
        if ok then
            ngx.header[inj.header] = render_inject(inj.format or "${SECRET}", inj.secret)
        else
            decision.inject_dropped = reason  -- Audit only, request proceeds
        end
    end
end

```

## SDK Integration and Vault-Flavor Usage

The CubeSandbox SDK exposes the `Inject` struct (defined in [`sdk/go/policy.go`](https://github.com/TencentCloud/CubeSandbox/blob/main/sdk/go/policy.go)) to enable developers to configure credential injection without handling secrets inside sandbox code. The Go type definition confirms the structure contains `Header`, `Secret`, and optional `Format` fields.

When using the Python SDK as shown in [`docs/guide/integrations/pi-agent.md`](https://github.com/TencentCloud/CubeSandbox/blob/main/docs/guide/integrations/pi-agent.md), developers create rules that inject headers such as `x-api-key` while the actual key remains in the host process memory only. Inside the sandbox, environment variable inspection reveals only placeholders—confirming the real credential never entered the VM.

```python
from cubesandbox import Sandbox, Rule, Match, Action, Inject

ANTHROPIC_API_KEY = "sk-deadbeef..."  # Exists only in host process

rules = [
    Rule(
        name="allow_anthropic",
        match=Match(scheme="https", sni="api.anthropic.com"),
        action=Action(
            allow=True,
            audit="metadata",
            inject=[
                Inject(header="x-api-key", secret=ANTHROPIC_API_KEY, format="${SECRET}"),
                Inject(header="anthropic-version", secret="2023-06-01")
            ],
        ),
    )
]

sandbox = Sandbox.create(
    template="my-template-id",
    allow_internet_access=False,
    network={"rules": rules},
)

```

## Audit-Safe Secret Handling

The credential vault implements defense-in-depth for observability by preventing secret leakage through logs. As implemented in the policy validation layer, the system redacts secret values in audit trails—replacing them with `***REDACTED***`—and records only a short fingerprint (`fp-xxxx`) derived from the secret's SHA-256 hash.

This design ensures that even if an attacker gains access to CubeSandbox audit logs, they cannot reconstruct the API keys, while administrators retain the ability to correlate policy usage with specific credential versions via the fingerprint.

## Summary

- **Secrets live in the proxy**: API keys are stored inline within `EgressRule` policy objects in the CubeEgress proxy's Lua dictionary (`policy_store`), never in the sandbox filesystem or environment.
- **Injection happens at the edge**: The `apply_injects` function in [`access_phase.lua`](https://github.com/TencentCloud/CubeSandbox/blob/main/access_phase.lua) adds authentication headers only during the proxy's access phase, just before the request leaves the network.
- **Validation gates prevent leakage**: The `inject_gates` function enforces strict validation (scheme, host, size limits) and drops injection attempts that fail safety checks without exposing the secret.
- **Audit trails remain safe**: Secrets are redacted in logs and identified only by SHA-256 fingerprints, preventing accidental credential exposure through observability data.

## Frequently Asked Questions

### How does CubeSandbox store API keys if not in the sandbox?

API keys are stored inline within the `inject` field of egress policies that reside in the CubeEgress proxy's shared Lua dictionary (`policy_store`). This keeps secrets in the proxy's memory space while the sandbox operates in a separate execution environment that never receives the raw values.

### What happens if an injection rule fails validation?

The `inject_gates` function in [`CubeEgress/lua/access_phase.lua`](https://github.com/TencentCloud/CubeSandbox/blob/main/CubeEgress/lua/access_phase.lua) performs safety checks on every injection attempt. If validation fails—for example, if the secret exceeds size limits or the host is invalid—the injection is dropped and recorded in the decision log, but the request may still proceed to the destination without credentials rather than exposing the error to the sandbox.

### Can sandbox code access the injected headers?

No. The sandbox code cannot read the injected headers because the injection occurs in the ** CubeEgress proxy after the request leaves the sandbox**. The sandbox sees only its own outbound request without the authentication headers; the proxy adds them encrypted on the wire, making them invisible to the sandbox process, its filesystem, and any core dumps or memory dumps.

### How are secrets protected in audit logs?

The system replaces secret values with `***REDACTED***` in all audit logs and stores only a short fingerprint (`fp-xxxx`) calculated from the SHA-256 hash of the secret. This allows operators to track which credentials were used for specific requests without risking credential leakage through log aggregation systems or SIEM platforms.