# Secrets in Daytona sandboxes

Daytona takes environment variables at creation and — unusually — lets you change
them on a running sandbox, which makes it the one provider here where a rotated
secret can reach a live sandbox without recreating it.

## Inject at creation

```ts
import { Daytona, type CreateSandboxFromSnapshotParams } from '@daytonaio/sdk';
import { Seekrit } from '@seekrit/sdk';

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

const daytona = new Daytona();
const sandbox = await daytona.create({
  envVars: {
    OPENAI_API_KEY: secrets.OPENAI_API_KEY,
    TAVILY_API_KEY: secrets.TAVILY_API_KEY,
  },
} satisfies CreateSandboxFromSnapshotParams);
```

```python
import os
import seekrit
from daytona import Daytona, CreateSandboxFromSnapshotParams

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

daytona = Daytona()
sandbox = daytona.create(
    CreateSandboxFromSnapshotParams(
        env_vars={
            "OPENAI_API_KEY": secrets["OPENAI_API_KEY"],
            "TAVILY_API_KEY": secrets["TAVILY_API_KEY"],
        },
    )
)
```

Note the casing difference between the SDKs: `envVars` in TypeScript,
`env_vars` in Python. Named keys, not the whole dict — see [Pick names, not the
whole environment](/docs/guides/sandboxes#pick-names-not-the-whole-environment).

## Update a running sandbox

`updateEnv` replaces what later processes see, which is how a rotation lands
without recreating the sandbox:

```ts
// After rotating OPENAI_API_KEY in seekrit.
const fresh = await new Seekrit({ token: process.env.SEEKRIT_TOKEN }).resolve();

await sandbox.updateEnv(
  { OPENAI_API_KEY: fresh.OPENAI_API_KEY },
  { unset: ['TAVILY_API_KEY'] },
);
```

> **Warning:** **Only newly started processes see the change.** Daytona is explicit: "Newly spawned processes, sessions and PTYs inherit the change; already-running processes keep their environment." A long-lived agent process started before the update keeps the old key until it restarts — so an `updateEnv` after a rotation is not, on its own, a revocation. Revoke at the provider too.

## Keep the credential outside the sandbox

Daytona sandboxes are commonly handed to coding agents, which is exactly the case
for not injecting anything. Run [`seekrit-proxy`](/docs/guides/agent-proxy) on
the host and give the sandbox a placeholder and a URL:

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

[[route]]
prefix = "/anthropic"
upstream = "https://api.anthropic.com"
allow = ["ANTHROPIC_API_KEY"]
methods = ["POST"]
paths = ["/v1/messages"]
```

```ts
const sandbox = await daytona.create({
  envVars: {
    // A placeholder, not a key.
    ANTHROPIC_API_KEY: '{{seekrit:ANTHROPIC_API_KEY}}',
    ANTHROPIC_BASE_URL: 'http://<host>:8080/anthropic',
  },
});
```

The value in the sandbox means nothing anywhere except through the proxy, and the
`paths` list bounds what the real key can be spent on. This also removes the
rotation problem above entirely: the proxy re-resolves on its own
(`[secrets] refresh_interval`), so a rotated key reaches even a long-running
agent process without touching the sandbox.

## 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
- [Rotation](/docs/guides/rotation) — what a rotation does and does not revoke
