What it actually is
SG/Compute launches an isolated environment, runs the declared work, and destroys it. That happens at two different scales with two different lifetimes, and almost every first reading of this platform collapses them into one. Separating them is the most useful thing this page can do.
An ephemeral browser
Lives for one request. Fresh Playwright runtime, fresh Chromium, fresh context, torn down before the response returns.
milliseconds to secondsAn ephemeral EC2 instance
Lives for a workload. Minted key, composed user-data, health-polled, then self-terminates on a timer it was launched with.
one hour by defaultLayer 1 — the browser, per request
From the source, verbatim:
“Layer-3 multi-step execution. Stateless by design: every call launches a freshsync_playwright+ Chromium, runs the declared step list, and tears both down intry/finallybefore returning.”
And, separately: “Each call to launch() starts a fresh sync_playwright() Node subprocess AND a fresh Browser” — so there is zero cross-request state, not merely a cleared one.
What makes that an architectural rule rather than a convention is that it has a mechanism behind it. Page__Factory is the single sanctioned path to a Page, and a CI guard fails the build if any raw browser.new_context( appears anywhere else. That is rarer than it should be: most codebases state this rule in a style guide and then discover the exceptions in production.
The request lifecycle
parse steps -> reject duplicate ids -> validate -> launch fresh Chromium -> new context + page -> apply credentials -> iterate steps against a deadline # remaining steps marked SKIPPED on halt -> derive COMPLETED / FAILED / PARTIAL -> try/finally ALWAYS tears down -> return, with a full timings block
Note the deadline behaviour: a halt does not discard the run. Steps that did not execute are returned marked SKIPPED, and the overall status distinguishes a partial run from a failed one. That distinction is what makes the surface usable by an agent that has to decide whether to retry.
Sessions — the exception to statelessness, and why it exists
Statelessness is the default, not a religion. A held session gets one dedicated OS thread, owning the Playwright runtime, browser and page for the session's whole life. The rationale is a real bug, documented in the source:
“Playwright's sync API is greenlet-based… Callingpage.goto/page.locator(...)from a DIFFERENT thread leads to undefined behaviour (deadlocks, silent hangs, or ‘navigate failed’ with noerror_message— the exact symptomsession_actwas hitting in CI).”
So thread affinity is not an implementation detail that leaked into the design; it is the design, and it was paid for.
| Surface | Default TTL | Refreshed by |
|---|---|---|
POST /session/open | 300,000 ms — 5 minutes | every access refreshes the full TTL |
POST /desktop/browser | 3,600,000 ms — 1 hour |
Both are capped at capabilities.max_session_lifetime_ms. Expiry is swept lazily — at the start of every registry operation and from service setup — so any request to any endpoint sweeps idle sessions and there is no background thread. Teardown runs on the worker thread, for the same Playwright affinity reason. The idle cost, from the source: “the worker blocks on queue.get() — 0% CPU, ~few KB of RAM.”
That design is genuinely elegant and it has never been documented publicly before this page. It is also the reason the concurrency question in Q2 is open rather than answered: the ceiling here is memory and browser processes, not the web framework.
The watchdog — the best engineering story in the repository
Request__Watchdog runs a daemon thread. If any in-flight request exceeds max_request_ms, it calls:
os._exit(2)
Not a graceful shutdown. Not an exception. The process dies.
The interesting part is the reasoning, which is in the source rather than in a design document. time.sleep releases the GIL, so the watchdog keeps ticking even through a main-thread deadlock — which is precisely the state you need to recover from. And os._exit bypasses cleanup, finally blocks and GIL contention, which is exactly what a graceful shutdown cannot do from inside a deadlock: it would queue behind the thing that is stuck. The Lambda Web Adapter sees the process die, and AWS hands the next invocation a fresh container.
It was written against a real production deadlock. A platform that publishes the failure and the mechanism reads very differently from one that publishes a feature list.
Layer 2 — the EC2 node
sg_compute/platforms/Platform.py is the abstraction — create_node, list_nodes, get_node, delete_node — carrying name: str = '' # 'ec2' | 'k8s' | 'gcp' | 'local'. Only EC2__Platform exists. Whether that comment describes an abstraction or an aspiration is Q3, published unresolved.
Request
POST /api/nodes, or sg <spec> create.
Key mint
api_key = secrets.token_urlsafe(32), commented “per-node random key; never reused”, written to SSM before launch so cloud-init can read it. And also written to an EC2 tag — the one trade-off this site publishes.
Provision
run_instances with composed user-data sections: Base, Docker, Sidecar, Nginx, Env__File, GPU_Verify, NVIDIA_Container_Toolkit, Ollama, VLLM, SGit_Venv, Claude_Code__Firstboot, Shutdown. A spec composes the sections it needs; nothing is templated by hand.
Address
The EC2 tags are the registry. sg:stack-name and sg:purpose; instance states map running→READY, pending→BOOTING, shutting-down|stopping→TERMINATING. There is no separate database to fall out of sync.
Wait
Health__Poller, two-phase: EC2 reports running, then an HTTP probe succeeds. A node is not ready because AWS says it started.
Execute
Pods managed over the host-control sidecar; Sidecar__Client sends {'X-API-Key': self.api_key}. The sidecar's port is unreachable from outside the instance.
Teardown — three independent paths
The timer, an explicit delete, and idle reconciliation. Below.
Teardown is the part worth publishing
systemd-run --on-active={seconds}s /sbin/shutdown -h now
paired with InstanceInitiatedShutdownBehavior=terminate. Halt means terminate, not stop — the instance does not linger in a stopped state accruing EBS charges and waiting for someone to notice it.
- Default
max_hours = 1across every spec.vault_appsupports fractional values (0.1= six minutes);max_hours=0disables the timer entirely. - Explicit
delete, at any time. - Idle reconciliation —
SG_Edge__Fleet__ReconcilerwithIDLE_TEARDOWN_THRESHOLD = 3: three consecutive zero-vault idle checks at a five-minute cadence, so roughly fifteen minutes.
And a detail that shows the code was written by someone who had been bitten: on spot instances the shutdown flag is deliberately skipped, with the reasoning inline — “spot non-hibernation instances always terminate on OS shutdown; skip the flag (it's silently ignored).”
How artefacts come back — correct the likely assumption
Enum__Artefact__Sink declares four sinks: VAULT, INLINE, LOCAL_FILE, S3. The default is INLINE.
Only INLINE and LOCAL_FILE are implemented. Both write_bytes_to_vault and write_bytes_to_s3 raise NotImplementedError.
So today you get base64 in the JSON body (20 MB cap — a source comment notes that a 64 KB default was rejecting real screenshot PNGs), raw PNG bytes from POST /browser/screenshot, or a local file path in development. No S3. No presigned URLs. No vault writes.
The gap matters more than it looks, because two specs exist whose declared capability is literally vault-writes. The capability is real at the spec layer and missing at the platform layer — Q4.
Isolation boundaries
Each row is a boundary with a named mechanism, not a policy statement.
| Boundary | Mechanism |
|---|---|
| Job ↔ job, same node | Fresh Playwright subprocess + fresh Browser + fresh BrowserContext per request, try/finally teardown |
| Session ↔ session | Dedicated OS thread, own browser process, registry keyed by Session_Id |
| Node ↔ node | Separate EC2 instances, per-node security group, per-node API key, never reused |
| Untrusted JavaScript | JS__Expression__Allowlist — deny by default, exact match. evaluate is rejected until an operator populates the allowlist |
| Network | Browser traffic through the mitmproxy sidecar; “isolation is Docker network”; the sidecar's :8080 is unreachable from outside the EC2 instance |
| Cookies | SET_COOKIE applies to the per-request context — “stateless — the context is fresh per request and discarded after; no session persistence” |