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:
| Scope | Use case |
|---|---|
tenant | Your organization’s company-wide credentials |
workspace | Credentials shared by everyone in one workspace |
user | An individual MIOSA user’s personal credentials |
external_user | A white-label end-user’s credentials (see White-label) |
external_workspace | A 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.