Versions: @tanstack/ai-sandbox-daytona 0.2.1, @daytona/sdk v0.191.0.
Summary
The sandbox documentation says that secrets never go into snapshots or the event log. On Daytona, secrets stay in two locations that the Daytona API can read: the sandbox env record, and the command string of each session command.
Mechanism
Secrets go into the sandbox on two paths. Both paths keep the values.
Path 1 — create-time env. ensure() puts all workspace secrets into SandboxCreateInput.env (packages/ai-sandbox/src/sandbox.ts). The adapter sends them as envVars to daytona.create (packages/ai-sandbox-daytona/src/provider.ts). Daytona keeps create-time env vars in the sandbox record. GET /sandbox/:id returns them in plain text. Each organization member with sandbox read access sees them in the dashboard, for the life of the sandbox.
Path 2 — per-command exports. bootstrapWorkspace also sends the same secrets to handle.env.set(). DaytonaHandle.withEnv() (packages/ai-sandbox-daytona/src/handle.ts) adds export KEY='value'; before each exec and each spawn command. The spawn path starts the harness CLI, which holds the agent API key (for example, ANTHROPIC_API_KEY).
Daytona keeps the full command string of a session command. The get-session-command endpoint returns it until the session is deleted. The adapter deletes the session only after the process ends. The daemon also writes the command to a cmd.sh file in the sandbox.
The git path shows the defect most clearly. The header of packages/ai-sandbox/src/git-exec.ts says that auth tokens must never go into argv. The code sends GIT_ASKPASS_TOKEN through opts.env. The Daytona handle writes opts.env into the command string. Thus the repo token goes into the clone command line.
Scope
Members of a different organization cannot see the values. The two surfaces are: API and dashboard readers in the same organization, and processes in the sandbox. The sandbox processes get the secrets by design. The problem is that the values stay in readable records after their use.
Fix direction
- Use Daytona Secrets (SDK v0.192.0) as the primary path. Create an organization Secret once (
daytona.secret.create({ name, value, hosts })). At sandbox create, map the env var to the Secret name with the secrets parameter. The sandbox record and the sandbox processes then hold an opaque placeholder (dtn_secret_<id>), not the value. Daytona puts the real value only into outbound requests to the Secret's allowed hosts. This fits the createSecrets() workspace contract directly. It requires a pin bump from ^0.191.0 to ^0.192.0.
- For values that must be literal in the process, send per-call env through the SDK parameter:
executeCommand(command, cwd, env, timeout). The daemon applies this env to the process and keeps no copy.
- For
spawn, write an env file once with fs.write, then source it in the command. The command record then contains a source line, not the values. (The session-execute request has no env field.)
- Keep only non-sensitive variables in create-time
envVars.
Versions:
@tanstack/ai-sandbox-daytona0.2.1,@daytona/sdkv0.191.0.Summary
The sandbox documentation says that secrets never go into snapshots or the event log. On Daytona, secrets stay in two locations that the Daytona API can read: the sandbox env record, and the command string of each session command.
Mechanism
Secrets go into the sandbox on two paths. Both paths keep the values.
Path 1 — create-time env.
ensure()puts all workspace secrets intoSandboxCreateInput.env(packages/ai-sandbox/src/sandbox.ts). The adapter sends them asenvVarstodaytona.create(packages/ai-sandbox-daytona/src/provider.ts). Daytona keeps create-time env vars in the sandbox record.GET /sandbox/:idreturns them in plain text. Each organization member with sandbox read access sees them in the dashboard, for the life of the sandbox.Path 2 — per-command exports.
bootstrapWorkspacealso sends the same secrets tohandle.env.set().DaytonaHandle.withEnv()(packages/ai-sandbox-daytona/src/handle.ts) addsexport KEY='value';before eachexecand eachspawncommand. Thespawnpath starts the harness CLI, which holds the agent API key (for example,ANTHROPIC_API_KEY).Daytona keeps the full command string of a session command. The get-session-command endpoint returns it until the session is deleted. The adapter deletes the session only after the process ends. The daemon also writes the command to a
cmd.shfile in the sandbox.The git path shows the defect most clearly. The header of
packages/ai-sandbox/src/git-exec.tssays that auth tokens must never go into argv. The code sendsGIT_ASKPASS_TOKENthroughopts.env. The Daytona handle writesopts.envinto the command string. Thus the repo token goes into the clone command line.Scope
Members of a different organization cannot see the values. The two surfaces are: API and dashboard readers in the same organization, and processes in the sandbox. The sandbox processes get the secrets by design. The problem is that the values stay in readable records after their use.
Fix direction
daytona.secret.create({ name, value, hosts })). At sandbox create, map the env var to the Secret name with thesecretsparameter. The sandbox record and the sandbox processes then hold an opaque placeholder (dtn_secret_<id>), not the value. Daytona puts the real value only into outbound requests to the Secret's allowed hosts. This fits thecreateSecrets()workspace contract directly. It requires a pin bump from^0.191.0to^0.192.0.executeCommand(command, cwd, env, timeout). The daemon applies this env to the process and keeps no copy.spawn, write an env file once withfs.write, then source it in the command. The command record then contains a source line, not the values. (The session-execute request has no env field.)envVars.