The classes
Desktop, Android and iOS have no backend in this build. They are surfaced with their status, their allocation fails closed with a reason, and nothing about them is presented as working. The fleet did not change that: it stands behind
code and browser and may not be named for the other three.
This page describes how an environment is resolved and what it enforces. It is not a statement that a run completes: as of 2026-09-10 one does not, for the reasons in Overview.
Read runnable, not the label
string
required
available, beta, waitlist or unavailable. What Obol offers as a product.string
required
configured or not_configured. Whether this deployment has a backend for it.boolean
required
Whether the workspace’s plan includes the class.
boolean
required
The conjunction of the three above. This is the answer. A class can be
available and not runnable because no backend is configured, and a surface that reads availability alone will offer a start button that fails.string
required
e2b, docker_dev, demo or none. none is the absence of an implementation, and it can never report itself ready.string
required
none, development_only or vendor_attested_microvm, ordered weakest first. The order is part of the contract, so two claims can be compared. There is deliberately no production member: no value here names an isolation boundary Obol has verified.string
required
allocates or fails_closed.reason, backend_readiness_reason and entitlement_reason carry the sentence to show a person.
What the backends are
Code and browser can be stood behind one of four backends. Which one you get is a fact about your deployment, not about your plan, and control asserts none of them by default: a deployment that has configured nothing reportsbackend: "none", backend_readiness: "not_configured" and runnable: false for every class.
The Obol-operated E2B fleet — e2b
Linux microVMs for code, and browser through Playwright baked into a reviewed template. Obol operates the fleet on its own infrastructure account, and that account’s credential buys compute and nothing else: it is never placed inside an environment, it never carries e2b.* connector traffic, and it appears in no receipt, snapshot or log. It is not a pooled vendor account, and it is a different object from the customer-facing e2b connector pack, which uses your own key.
Two operating rules are worth knowing because they are visible in behaviour. A guest is never publicly addressable — the vendor’s default is the opposite, and for a class that can hold a login that default would put a logged-in browser on the open internet. And a sandbox is killed rather than paused, so checkpoint is unsupported on this backend: a paused guest at this vendor has no time to live and is never reaped, and a snapshot would leave run state there after the run that produced it ended, under no retention promise.
The local Docker development backend — docker_dev
A development backend on a trusted host: shared kernel, no hypervisor boundary, no hardened host baseline, no attested image supply chain, no proven tenant reset. It is fit for development and demonstration. It is not production isolation, and its isolation_claim reads development_only rather than naming a stronger boundary.
The demo backend — demo
Deterministic and Obol-owned. A demo run touches no customer credential and no real vendor, its estimate is a known zero rather than an unavailable one, and it never bills. Its claim is development_only too.
When a class cannot run
Allocation is checked before anything chargeable starts, and the refusal is typed:409
set_by: "control" is on every one of these. A failure reason is control’s fact, never model prose.
The same
environment_class_unavailable reason is currently returned when an unsupported capability is the real problem, because the closed failure taxonomy has no capability_unsupported variant yet. The what and why still describe what happened; the machine-readable reason is broader than the cause. This is recorded in docs/workers/2026-09-09-self-serve-follow-ups.md.Sandboxed commands on code
A code Worker can run commands in its isolated sandbox: env_code_exec, env_code_file_read, env_code_file_write. No credential is placed in the guest. Each command is a fresh bash -c, at most 240 seconds, with no background processes. Network scope for those commands is derived from the lease (deny_all → none; dependency provisioning or an allowlist → declared; anything else fails closed as undeclared). Control re-derives the same value and evaluates the stricter of the claimed and derived scope.
In safe mode, and in test mode, a sandboxed command whose network is none or declared is admitted without an approval hold. Other non-read environment commands still wait or are refused. The Read only preset permits no sandbox commands.
GitHub’s API is not reachable from the sandbox. Clone public repositories over https://github.com/… and publish through gateway Git Data tools (github.get_ref, github.create_tree, github.create_commit, github.create_ref, then github.create_pull). Private repositories are not supported. A Worker acts in one environment class per run; code and browser together cannot run yet. Browser environments on Browserbase are unchanged.
Command output is an observation, not evidence of a vendor-side effect.
Networking
A code environment denies egress by default except for dependency provisioning; a browser environment uses an allowlist. Widening it is a reviewed change to the Worker’s limits, and a run that reaches a destination outside the list fails withnetwork_destination_denied carrying a widen_network_policy fix.
Where the environment runs on the fleet, that policy is enforced in two places rather than one. deny_all is no internet access at the fabric, outside the guest. An allowlist is address rules whose only permitted destination is a SOCKS5 proxy Obol operates, and every name-based decision is taken there, where Obol can also enforce the scheme rule, refuse private addresses, pin the address it resolved and re-evaluate a redirect. The proxy fails closed when it is unreachable, which means its availability is your run’s availability.
The vendor’s own domain rules are not used at all, and that is deliberate rather than an omission: using any domain entry there permanently allows a public nameserver that cannot be removed, which would be a DNS channel to a third party inside an environment you have been told is restricted. With no domain entry the guest has no name resolution of its own, and the name it wanted arrives at the proxy for Obol to resolve and check. The per-command destination check still runs as well, because the vendor cannot express scheme restriction, private-address refusal, address pinning or redirect re-evaluation.