On this page

Secrets - Quickstart

Store an API key once in MIOSA. Every sandbox using it sees an opaque placeholder env var. Rotate in one place, every sandbox picks up the new value automatically.

Add an API key (30 seconds)

From the dashboard

1. Open your Sandbox or Computer in the MIOSA dashboard
2. Click the "Secrets" tab
3. Click "+ Add Secret"
4. Fill in:
     Name:        openai_key
     Value:       sk-proj-...  (paste your real key)
     Expose as:   OPENAI_API_KEY
5. Save

Inside the sandbox, process.env.OPENAI_API_KEY is now an opaque token. Use it in your code exactly as you would the real key - the swap happens on egress.

From the SDK

How the placeholder works

Inside the sandbox after binding:

$ echo $OPENAI_API_KEY
miosa-tok-7f2a8c1d4b2e6f0a...

When your code makes an outbound call:

requests.post(
    "https://api.openai.com/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"},
    json={...},
)

…the MIOSA proxy on your VM’s host sees the request, finds miosa-tok-7f2a8c1d4b2e6f0a in the Authorization header, and swaps it with your real sk-proj-... before forwarding to OpenAI. OpenAI sees the real key. Your sandbox never did.

The placeholder is unique per binding - there is no global table of placeholders an attacker could brute-force.

Rotate a key

sandbox.secrets.rotate("openai_key", new_value="sk-proj-newvalue...")

Every sandbox using this secret picks up the new value on its next outbound request. No restart. No redeploy. The placeholder token doesn’t change.

List + delete

secrets = sandbox.secrets.list()
# Returns metadata only  -  name, type, preview (last 4 chars), last_used_at.
# Never returns the real value.

sandbox.secrets.delete("openai_key")

Scope levels

A secret can be scoped at five levels - the proxy picks the most specific match for the resource making the request:

ScopeUse case
tenantYour organization’s company-wide credentials
workspaceCredentials shared by everyone in one workspace
userAn individual MIOSA user’s personal credentials
external_userA white-label end-user’s credentials (see White-label)
external_workspaceA white-label workspace’s credentials

Default scope when using sandbox.secrets.set is determined by the API key making the call. Tenant admins can create tenant-wide secrets; individual users create user-scoped ones for their own resources.

What the proxy can do beyond a string swap

For OAuth-managed credentials (see Connect Accounts), the proxy also:

  • Refreshes expired tokens automatically using the stored refresh token
  • Surfaces token revocation (when the upstream side invalidates the access) as a 401 to your code, exactly as if you’d held the token yourself

You don’t write any of that logic.

All 5 SDKs

Real-world example - multi-service agent with OpenAI and Stripe

An agent that drafts payment summaries by calling both OpenAI and the Stripe API. Two secrets, one sandbox, no credentials in the VM.

Troubleshooting

The sandbox env var is empty - echo $OPENAI_API_KEY prints nothing. The secret was saved at scope="tenant" or scope="workspace" but the sandbox doesn’t belong to that scope. Re-save with scope="sandbox" (the default when using sandbox.secrets.set).

secrets.list() shows the secret but the upstream API returns 401. The proxy is not seeing the placeholder in the request. Confirm the library you’re using puts the token in Authorization: Bearer .... Check the audit row’s request_transforms.secrets.swapped - if empty, the proxy never matched.

I rotated the key but nothing changed. Rotation is instant. The upstream API call succeeding after rotation means the new key is already working. Verify the old key is actually invalid by revoking it on the provider side.

I deleted a secret but the env var still has a value inside the sandbox. The sandbox process caches env vars at startup. Restart the process or open a new exec session - the proxy checks the vault on each request, but the env var in the process’s memory reflects the last value it read.

For more issues, see the Security troubleshooting hub.

Frequently asked

Can I read a secret’s real value via the SDK after I save it? No. secrets.list returns metadata only (name, type, preview, last-used time). The value is sealed.

What if I want to keep using .env files? Use them. MIOSA Secrets is purely additive; .env files continue to work. Use Secrets for credentials you want centrally managed.

Will this slow my sandbox down? About 1-3 ms per outbound request. Trivial vs typical API latency.

Does it work with curl, requests, fetch, axios, gRPC, …? Yes. The proxy operates at the network layer; the swap is invisible to the language runtime.

Was this helpful?