Skip to content

Guides

Secrets and credentials

Use secret references, grants, redaction, and narrow delivery paths for sandboxed workloads.

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.

For owned cleartext request paths, mvm can mediate caller-visible content in addition to managed secret refs:

StepBehavior
DetectMatch declared secrets plus structured PII such as email, us_ssn, credit_card, e164_phone, and iban.
ReplaceRewrite matched bytes to request-scoped opaque tokens before they leave the runtime-owned path.
ReinjectRestore the original bytes only when the exact token returns on an owned cleartext response path and policy allows plaintext reinjection.
FallbackIf the path is unowned, transformed, expired, unauthorized, or uncorrelatable, do not restore; fall back to redaction, denial, or pass-through per policy.

This is an exact-token contract, not semantic recovery. If a model paraphrases, splits, reformats, or partially copies the sensitive value, the runtime should not guess. Reinjection is for exact opaque-token round trips only.

Cloud deployments should keep reinjection policy load-bearing at the control plane: which classes are handled, which sinks may receive replacements, which callers may receive plaintext restoration, and how long correlation state is retained.

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", type="bearer", hosts=["api.openai.com"], 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:

import mvm
from mvm import Sandbox
with Sandbox.create(
image="nix:./flake#agent-runtime",
env={
"OPENAI_API_KEY": mvm.secret(
"openai-api-key", type="bearer", hosts=["api.openai.com"]
)
},
) 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;
  • keep PII handling explicit alongside secret grants;
  • never echo resolved values to the model;
  • treat restored plaintext as a separate permission from outbound replacement;
  • 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.
Structured PII detected on an owned outbound pathReplace with an opaque token when policy enables it; otherwise redact or deny per policy.
Guest prints a secretRedact before logs or model-facing responses leave the app.
Exact opaque token returns on an owned response pathReinject only when correlation and permissions are still valid.
Token returns from an unowned or transformed pathDo not reinject; keep the replacement or redact the value.
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, PII mediation, reinjection authorization, or cleanup.