# Secrets in E2B sandboxes

E2B takes environment variables at creation and per command, so both shapes on
the [sandboxes overview](/docs/guides/sandboxes) work without any E2B-side
configuration.

## Inject at creation

```python
import os
import seekrit
from e2b import Sandbox

secrets = seekrit.Client(token=os.environ["SEEKRIT_TOKEN"]).resolve()

sandbox = Sandbox.create(
    envs={
        "OPENAI_API_KEY": secrets["OPENAI_API_KEY"],
        "TAVILY_API_KEY": secrets["TAVILY_API_KEY"],
    },
)
```

```ts
import { Sandbox } from 'e2b';
import { Seekrit } from '@seekrit/sdk';

const secrets = await new Seekrit({ token: process.env.SEEKRIT_TOKEN }).resolve();

const sandbox = await Sandbox.create({
  envs: {
    OPENAI_API_KEY: secrets.OPENAI_API_KEY,
    TAVILY_API_KEY: secrets.TAVILY_API_KEY,
  },
});
```

Named keys, not `envs=secrets`. The token can see the whole environment and the
sandbox has no business holding all of it — see
[Pick names, not the whole environment](/docs/guides/sandboxes#pick-names-not-the-whole-environment).

## Inject per command

Creation-time variables apply to everything the sandbox runs. If only one step
needs a credential, scope it to that step:

```python
sandbox = Sandbox.create()

sandbox.commands.run(
    "python fetch_and_summarize.py",
    envs={"OPENAI_API_KEY": secrets["OPENAI_API_KEY"]},
)

# This one gets no key at all.
sandbox.commands.run("python render_report.py")
```

> **Warning:** Per-command variables are narrower in *time*, not in secrecy. E2B says so plainly: they are "scoped to the command but are not private in the OS." Any other process in the sandbox can read `/proc/<pid>/environ` while the command runs. Use this to keep a key out of unrelated steps, not to hide it from code running alongside.

## Keep the credential outside the sandbox

When the code inside is model output or a coding agent, do not inject at all.
Run [`seekrit-proxy`](/docs/guides/agent-proxy) on the host and give the sandbox
a placeholder plus a base URL:

```toml
# seekrit-proxy.toml — on the host, not in the sandbox
listen = "0.0.0.0:8080"

[[route]]
prefix = "/openai"
upstream = "https://api.openai.com"
allow = ["OPENAI_API_KEY"]
methods = ["POST"]
paths = ["/v1/chat/completions", "/v1/embeddings"]
```

```python
sandbox = Sandbox.create(
    envs={
        # A placeholder, not a key. Worthless if it leaks.
        "OPENAI_API_KEY": "{{seekrit:OPENAI_API_KEY}}",
        "OPENAI_BASE_URL": "http://<host>:8080/openai",
    },
)
```

The agent's SDK sends `Authorization: Bearer {{seekrit:OPENAI_API_KEY}}`; the
proxy swaps in the real value and forwards it. What the sandbox holds is a
string that means nothing anywhere else, and the `paths` list means the key
cannot be spent on anything but chat completions and embeddings even from inside.

> **Note:** Bind the proxy where the sandbox can reach it, and nowhere else. `0.0.0.0` above is for the sandbox-to-host hop; put it behind a firewall or on a private network, because anything that can reach the proxy can spend the credential on the allowed operations. The default `127.0.0.1` is right when the proxy is a sidecar in the same network namespace.

## See also

- [Agent sandboxes](/docs/guides/sandboxes) — the two shapes and when each is right
- [Agent proxy](/docs/guides/agent-proxy) — the full proxy configuration
- [SDKs](/docs/guides/sdks) — `resolve()` in every language
