Skip to content

Secrets and credentials

Secrets are not ordinary configuration. A sandbox can print them, copy them, persist them in snapshots, or expose them through files and logs if the workflow is careless. Treat credential delivery as a policy decision with a reviewable grant.

StepRule
StoreStore a named secret reference with the local secret store.
DeclareReference the secret by name in a manifest, declaration, or SDK request.
GrantBind the reference to a workload, command, or tool operation.
DeliverPrefer host-mediated release paths over raw guest-visible values.
RedactScrub stdout, stderr, logs, errors, receipts, and model-facing results.
RetainTreat snapshots, cold state, volumes, and copied files as secret-bearing until proven otherwise.

The secure default is reference-first. Raw values should not appear in source files, examples, command history, model prompts, or long-lived logs.

Store a named reference:

Terminal window
mvmctl secret put openai-api-key

Declare the reference in a workload manifest or SDK declaration, then run under the policy that grants that reference to the workload. Avoid passing secrets through argv or --env; those paths are easy to leak through process listings, shell history, debug output, and crash reports.

env={
"OPENAI_API_KEY": mvm.secret("openai-api-key", var="OPENAI_API_KEY"),
}

The declaration records the reference. It should not contain the credential value.

The runtime SDK target should preserve the same shape:

from mvm import Sandbox, SecretRef
with Sandbox.create(
image="nix:./flake#agent-runtime",
secrets={"OPENAI_API_KEY": SecretRef("openai-api-key")},
) as sandbox:
sandbox.commands.run(["python", "/work/tool.py"], timeout_seconds=30)

Check Operations cookbook and Lifecycle matrix before treating a helper as shipped in a language SDK.

Some workloads still need file-shaped secrets, certificates, or config bundles. When using a secrets drive:

  • mount the narrowest directory possible;
  • keep files read-only inside the guest;
  • use non-secret test data in docs and fixtures;
  • do not share the same mounted directory across unrelated workloads;
  • treat snapshots and cold state as secret-bearing after the mount has been visible to a guest;
  • remove or rotate credentials after a workflow that allowed generated code to read them.

Prefer managed secret references for credentials. Use file mounts for formats that cannot reasonably be expressed as references.

Models should never invent secret names or values. The application should map a tool capability to an allowed reference:

{
"tool": "web_fetch",
"secrets": {
"OPENAI_API_KEY": {
"ref": "openai-api-key"
}
}
}

Rules for model-facing tools:

  • grant secrets per operation;
  • never echo resolved values to the model;
  • redact command output before it becomes model context;
  • reject requests that ask to print, exfiltrate, or persist credentials;
  • record the grant decision with the audit/run identifier.

Every secret-bearing path should have a failure behavior:

PathFailure behavior
Missing referenceFail closed before VM launch or command execution.
Unauthorized referenceReturn policy denial, not a generic runtime failure.
Guest prints a secretRedact before logs or model-facing responses leave the app.
Cleanup failsReport cleanup failure separately and mark retained state as sensitive.
Snapshot retainedAttach retention metadata and delete or rotate when no longer needed.

Do not collapse secret failures into generic command errors. Operators need to know whether a workload failed because of policy, missing credentials, guest behavior, or cleanup.