Skip to main content
Two independent capabilities, usually used together. Secret keeps the credential on the host: sandbox code writes a placeholder such as <BOXLITE_SECRET:openai>, and the host proxy substitutes the real value on the way out. SecurityOptions hardens the OS-level sandbox around the VM — jailer, seccomp, resource limits.

Quick Example

Inject a credential (Secret)

The following calls an external API from inside the sandbox: the in-sandbox script only knows the placeholder <BOXLITE_SECRET:demo>, while the real credential <YOUR_API_KEY> stays on the host and is injected by the proxy on egress.
Note: api.example.com above is a placeholder host with no /v1/ping endpoint, so running this as-is yields a non-zero exit_code. To verify that injection works, replace both hosts and the request target with a real, reachable HTTPS service that echoes request headers, and enable network egress. The following is a minimal verification (using httpbin.org/headers to echo the Authorization header):
Output excerpt (httpbin echo; you can see the placeholder has been replaced by the real value):
Note: the guest also gets an environment variable BOXLITE_SECRET_TESTKEY=<BOXLITE_SECRET:testkey> injected automatically (the placeholder, not the real value), which you can verify with printenv BOXLITE_SECRET_TESTKEY. The real value is not found in env, confirming that the real credential never enters the guest. The variable key is derived from the secret name as BOXLITE_SECRET_{NAME} (uppercased, with non-alphanumeric characters replaced by _).

Tighten isolation (SecurityOptions)

For untrusted workloads, use the maximum() preset to enable all isolation. Note: security must be passed through advanced=AdvancedBoxOptions(...); there is no top-level security= keyword, and AdvancedBoxOptions is exported only under the boxlite.boxlite submodule.

Combining the two (credentials plus isolation)

Node equivalent

In the Node SDK, secrets and security are both direct fields of SimpleBoxOptions (there is no advanced wrapper layer), and SecurityOptions is expressed as a plain object (there is no maximum() static preset).

Parameters and Returns

Secret(name, value, hosts=[], placeholder=None)

Constructor signature: see sdks/python/src/options.rs. Injection entry point: BoxOptions(secrets=[Secret(...)]), or passed through via SimpleBox(..., secrets=[...]). Node uses { name, value, hosts?, placeholder? }.

SecurityOptions (Python)

Preset static methods (sdks/python/src/advanced_options.rs):
Note: there is no SecurityOptions.minimum(); the weak-isolation preset is named development().
Resource limits set by maximum(): max_open_files=1024, max_file_size=1 GiB, max_processes=100 (max_memory/max_cpu_time are left to the VM configuration). Custom constructor parameters (defaults per the source signature):

Attaching SecurityOptions to a box

AdvancedBoxOptions(security=None, health_check=None): exported only under the boxlite.boxlite submodule; import boxlite; boxlite.AdvancedBoxOptions at the top level is not available.

box.exec(...) return value (ExecResult)

Troubleshooting

AttributeError: module 'boxlite' has no attribute 'AdvancedBoxOptions'

AdvancedBoxOptions lives in the boxlite.boxlite submodule, not the top level.

AttributeError: type object 'SecurityOptions' has no attribute 'minimum'

There is no minimum() preset. Use SecurityOptions.development() for weak isolation, and standard() / maximum() otherwise.

Passing security= at the top level errors out

BoxOptions / SimpleBox do not have a security= keyword. Security options must be wrapped in advanced=AdvancedBoxOptions(security=...). Passing security= directly reports a TypeError due to the unknown keyword argument.

The secret was not injected (the request still contains the raw placeholder / authentication fails)

  • The proxy substitutes placeholders in request headers (typically Authorization), the URL query string, and the request body. It does not touch the URL path — a placeholder there is forwarded literally.
  • hosts must match the actual outbound target host. For example, requesting api.openai.com with hosts=["openai.com"] does not match: use the exact host or the *.openai.com wildcard.
  • Egress must be HTTPS and pass through the BoxLite proxy; if the sandbox network is fully disabled, injection cannot occur.

seccomp / jailer behave differently on macOS

seccomp_enabled, new_pid_ns, new_net_ns, and chroot are Linux-only. Setting them on macOS does not error — the runtime logs a warn and ignores them. seccomp_enabled takes effect only on Linux (standard()/maximum() already guard it internally with cfg!(target_os = "linux")). On macOS, even setting it to True does not enable seccomp; this is a platform difference. The jailer is available on both Linux and macOS.

Isolation is blocking your program and you cannot tell which layer

Downgrade temporarily to confirm it, then put it back — do not ship with isolation off:
Once the interference is confirmed, return to standard() or maximum() and narrow the specific field instead. Which field touches which layer is on Security and isolation.

The box fails to start (RuntimeError) — virtualization unavailable

BoxLite depends on hardware virtualization:
  • Linux: requires KVM (/dev/kvm accessible); WSL2 requires KVM enabled and the user in the kvm group.
  • macOS: uses Apple’s Hypervisor.framework and does not need /dev/kvm.
  • Without virtualization support, the box fails to start and raises a standard RuntimeError (the process stays alive and can be caught with try/except).
A network hiccup during image pull also raises a RuntimeError, which can be caught and retried.

See also