Skip to main content
Org Secrets are encrypted key=value pairs owned by an org. A machine sees each one as an environment variable whose value is an opaque placeholder (ibsec_...). Your app sends that placeholder in its usual header, to its usual HTTPS URL, and the outbound proxy swaps in the real value for the hosts the Secret allows. The real key never lands on the machine’s disk or in its snapshots.

Save a Secret and give it to a machine

1

Save a Secret

To save a Secret, you need to be an org owner or admin.
The CLI reads the value from stdin, or from a prompt without echo.
2

Give it to a machine

3

Use it as usual

The app reads SERVICE_API_KEY and sends it as it always would, such as Authorization: Bearer $SERVICE_API_KEY or X-Api-Key: $SERVICE_API_KEY. There is no separate retrieval API.
New machines made in the dashboard receive all current and future proxy Secrets of their workspace, once Secrets is enabled for your account and on the server. Existing machines keep their selections. To change those, use the CLI or the HTTP API, below. A machine uses Secrets from its own org only.

Scope a model provider key to its API

--ai scopes a model provider key to its API. The proxy puts the key into x-api-key or Authorization only for that host.
You can repeat --ai and combine it with --host.

Choose the hosts a Secret may go to

Each Secret lists the exact hosts it may be sent to. Without --host, it may go to every public HTTPS destination (*). Partial wildcards such as *.example.com are not supported.
With *, any process in the machine can send the Secret to any public HTTPS host, including one it controls. When the credential must stay with one service, use exact hosts.

Change a running machine’s Secrets

  • --secrets all includes Secrets created later. --secrets none blocks the machine from all Secrets.
  • Rotating a value keeps its placeholder, so apps need no restart.
  • If you remove a Secret and recreate it with the same name, an explicit grant to the old one is not restored.

Use a Secret for git over HTTPS

Git sends its token as Basic auth, which the proxy handles, so git clone and git push to private repos work with a placeholder. Add github.com (for git) to the Secret’s hosts next to api.github.com (for gh). Then give git a credential helper that returns the placeholder, such as gh auth setup-git for GH_TOKEN.

Import a .env file

The whole file is validated and written at once, and every entry shares that host scope. Importing does not give any machine the Secrets. Select them with instabox secrets select, as above.

Turn off QUIC in browsers

The proxy sees HTTPS over TCP port 443 only. HTTP/3 (QUIC, on UDP 443) bypasses it, so the API receives the placeholder and usually refuses it, for example with 401. curl and most HTTP libraries use TCP, but Chromium may still pick HTTP/3. In a Chromium-based browser that sends Secrets, turn QUIC off:
In Playwright or Puppeteer, pass the same flag: chromium.launch({ args: ['--disable-quic'] }) or puppeteer.launch({ args: ['--disable-quic'] }). The browser must also trust the sandbox CA with normal certificate checks. Do not turn verification off.

Put the real value in the machine with a direct Secret

A direct Secret (--direct in the CLI, delivery: "env" in the API) puts the real value in the machine, as a variable or a file. Use one when the proxy cannot carry the credential.
Any code in the machine, including an agent or a package it installs, can read and copy a direct Secret. Prefer the proxy for HTTPS API keys. To revoke a direct credential, rotate it at its service, because copies already read stay.
As an owner or admin, save the Secret as direct. Then reference it from a machine as a variable, a home file, or both:
  • --secret-env VAR=SECRET, or --secret-env SECRET for the same name
  • --secret-file PATH=SECRET
Who may reference direct Secrets, and the rules for names and paths, are under Reference below.

Select Secrets over the HTTP API

Select Secrets when you create a machine with POST /v1/machines:
After that, use /v1/machines/{id}/secrets:
  • PATCH sets a new selection. Explicit null selections are rejected.
  • GET returns metadata and status.
  • POST refreshes delivery now.
directSecrets replaces the machine’s direct references. "envSecrets": ["DATABASE_URL"] is the same-name shorthand. Send one or the other, not both. "secrets": "all" never includes env (direct) Secrets, which reach a machine only through envSecrets or directSecrets. For one command, POST /v1/machines/{id}/exec takes "secretEnvs": {"DATABASE_URL": "PROD_PG"}. To save a Secret over HTTP, see “Save a Secret” under Reference. Full schemas are in the API reference.

How it works

The proxy carries proxy Secrets, and files and variables carry direct ones. The details below say what the proxy changes in a request, when a change reaches a machine, and which processes see the values.
The proxy checks the machine’s authorization, the host scope, and the upstream TLS certificate. Then it replaces the placeholder inside a header value. It supports HTTPS on port 443 only, and it does not substitute URL parameters or request bodies.
  • For HTTP Basic auth in Authorization, the proxy decodes the credential, replaces the placeholder inside user:password, and re-encodes it. Any other encoding or signing of the placeholder prevents replacement.
  • The proxy intercepts all supported HTTPS hosts, even without a placeholder. Clients must trust the sandbox CA and cannot pin certificates.
  • The proxy closes intercepted connections to private addresses.
  • WebSockets work. The proxy replaces placeholders in the handshake headers, not in messages.
  • The proxy drops Alt-Svc from the responses it relays. Chromium may still choose HTTP/3 from DNS, or from an earlier visit saved in its profile.
instabox secrets show reports proxy authorization and placeholder setup separately. A proxy update is pending until acknowledged, and an acknowledged lease expires within at most 60 seconds without renewal. Requests already admitted, streams included, may finish.Servers with per-sandbox usage show each Secret’s proxied and denied request counts in secrets show and GET /v1/machines/{id}/secrets.
Secrets are written to /run/instabox/secrets/env (tmpfs, mode 0600, owned by the machine’s user) and loaded by /etc/profile.d/zz-instabox-secrets.sh. Login shells get them: SSH, instabox exec, and the exec API.Processes that skip the profile do not get them, such as systemd units, cron, and bash -c without -l. Running processes keep their old environment until restarted.
Variables go to /run/instabox/secrets/env. A file’s value is written to /run/instabox/secrets/files/ (tmpfs, mode 0600, owned by the machine’s user), and the home path becomes a symlink to it. So the disk, disk snapshots, and forks never hold the value.If the path already holds a file of the machine’s, the file is left alone and secrets show reports the error. Move the file and run secrets push. A tool that rewrites its login file replaces the link with a real file on the disk, which is then the machine’s own.
A new value reaches running machines within about 10 seconds. Files change in place and new shells get the new variable, but running processes keep the old one until restarted. Stopped and paused machines get the current value when they start or resume. Deleting the Secret or the reference removes the variable and the file at the next delivery.Forks copy the source’s Secret selection, both proxy and direct, and resolve the values again. A machine created from a snapshot takes only its own selection.

Reference

A new Secret answers 201 Created, and a replacement 200 OK. Both carry metadata and never the value: id, orgId, name, delivery, hosts, version, size, createdAt, and updatedAt.POST /v1/orgs/{id}/secrets/import takes {"values":{...},"delivery":...,"hosts":[...]}, with the same rules for every entry. A missing delivery there answers 400 delivery must be proxy or env. The CLI saves proxy unless you pass --direct.This example saves a proxy Secret in your personal org. It keeps the token header in a mode 0600 file and sends the body on stdin, so neither appears in the process list:
A heredoc you type still lands in shell history. For a real credential, build the body from a file with jq -Rs '{value: rtrimstr("\n"), delivery: "proxy", hosts: ["api.example.com"]}' <key.txt, and pipe it into the same curl. For an env Secret, send {"value":"postgresql://...","delivery":"env"} to .../secrets/DATABASE_URL.
  • Manage values (create, import, change, delete): an org owner or admin, signed in, or the workspace’s own workspace:admin API key sent as X-API-Key. After instabox login, the CLI’s access token works, and instabox whoami refreshes it. A personal org’s key and narrower keys get 403 missing_scope. Management reads never return values.
  • Read Secret metadata and machine selections: keys with machines:read.
  • Change selections and refresh delivery: keys with machines:write.
To work headless, put the workspace:admin key in INSTABOX_API_KEY. Org commands then default to the key’s org, and secrets rm needs --yes without a terminal.
Only an org owner or admin can save a Secret as direct. Anyone who can change a machine (machines:write) in the org can reference the org’s direct Secrets. A proxy Secret can never be referenced as direct, and a Secret of another org cannot be referenced at all.
The name is the variable the machine exports, so it follows the rule for Environment variables. A name that env refuses is refused here too (400 bad_request), including BASH_ENV, ENV, and IFS.A Secret saved under a name before the name was reserved is not delivered. The rest of the selection is delivered, secrets show reports not delivered: <names>, and secrets push answers 400. To fix it, save the value under another name, select it, delete the old Secret, and run secrets push.
Variable names follow the Secret name rule. They cannot be PATH, HOME, BASH_ENV, IFS, or the CA variables, and they cannot start with INSTABOX_ or SSH_.Paths are relative to the machine’s home directory: write .ssh/id_ed25519, not ~/.ssh/id_ed25519. The shell turns an unquoted ~/ into your own home, and the CLI takes that back as home-relative. A machine has at most 64 direct references.No API returns a value. secrets show and GET /v1/machines/{id}/secrets list directSecrets as names and paths. Each reference (create, select, fork, exec) is audited by name.
Vault (per-user) and Org Secrets are independent stores, with no automatic migration. Existing Vault env values, file delivery, and metadata selectors keep working, and a machine can use both. For a same-named variable, an explicitly selected Secret wins.
  • Omitting secrets on create keeps the Vault behavior without enrolling the machine in Secrets.
  • --no-vault --secrets none disables delivery from both stores on a new machine.
  • --no-vault, and the SDK option vault: false, affect only Vault. Clearing Secret selections affects only Secrets. Where an SDK does not expose Secret selection fields, use the CLI or HTTP API.
  • instabox vault export NAME --out recovered.json remains for items kept by an earlier explicit migration. Its output contains the real value.

Environment variables

Plain variables, and the names a machine refuses.

API keys

Scopes, including workspace:admin.