computers.sandbox.warmPool.enabled=true renders a SandboxWarmPool, grants the service account create and delete on sandboxclaims, and holds two pods ready. Nothing ever claims from it. The server has no code path that creates a SandboxClaim, so a Bot's first action still waits for a brand-new Sandbox to schedule and for Chromium to boot — the exact wait the pool exists to remove, now paid for twice.
What the cold start actually does
Every request the sandbox provider makes when a Bot has no computer yet, captured through the provider's own injectable fetchImpl:
provider.warm implemented? undefined
methods: name, isolation, locate, status, stop, reset, list, sessionOf
GET /apis/agents.x-k8s.io/v1beta1/namespaces/openbot/sandboxes/bot-knowledge-19lp3wl 404
POST /apis/agents.x-k8s.io/v1beta1/namespaces/openbot/sandboxes
GET /apis/agents.x-k8s.io/v1beta1/namespaces/openbot/sandboxes/bot-knowledge-19lp3wl
Three calls, none of them a claim. The POST creates a fresh Sandbox from scratch while two warm ones sit idle beside it.
Where the wiring stops
The interface declares the hook, optionally:
server/src/computer/provider.ts:85
/** Prepare provider resources before the first computer request. */
warm?(): Promise<void>;
And the server calls it at server/src/index.ts:206:
if (computerProvider?.warm) {
void computerProvider.warm();
}
The guard is never true. createComputerProvider returns one of three shapes — docker, shared, sandbox — and typeof provider.warm is undefined for all three. The string sandboxclaim appears nowhere under server/. Because warm?() is optional, TypeScript is content and the if reads as deliberate rather than unfinished.
This is a gap between the chart and the code that consumes it, not a typo. Claiming is what the upstream controller keys on claim.Spec.WarmPoolRef to do; without a claim, a warm pool is only pods.
Reproduction
-
Render the chart with the pool on and the CRDs declared:
helm template ob charts/openbot \
--set computers.mode=sandbox \
--set computers.sandbox.warmPool.enabled=true \
--api-versions agents.x-k8s.io/v1beta1/Sandbox \
--api-versions extensions.agents.x-k8s.io/v1beta1/SandboxWarmPool
kind: SandboxWarmPool renders with replicas: 2, and the Role gains sandboxclaims with get, list, create, delete.
-
Build a sandbox provider with a recording fetchImpl, as server/tests/computer-sandbox.test.ts already does, and call locate for a Bot with no computer. The three requests above are all of them.
-
grep -rn sandboxclaim server/ returns nothing.
Why it matters
The pool is off by default, so this costs nobody who leaves it alone. The people it does reach are the ones who read the values comment, decided the first-action wait was worth paying for, and turned it on. They get the bill — two idle browsers, their nodes and their volumes — and none of the latency they bought it for. Nothing logs a warning, the pool reports healthy, and the only visible symptom is that cold starts feel exactly as slow as before.
The RBAC grant makes it read as finished. A reviewer checking whether claims are permitted finds that they are.
Three ways to close it
Implement claiming: have the sandbox provider create a SandboxClaim and adopt what the controller hands back, instead of posting a fresh Sandbox. That is what the chart was written for and clearly the right end state.
Or delete the switch — warmpool.yaml, sandbox-template.yaml, the sandboxclaims grant and the unreachable warm?() hook — so nothing offers an option that does not do anything.
Or, in between, make the chart refuse to render with the pool on and say why, keeping every template for when claiming lands. That is the treatment computers.mode: sandbox already gets on a cluster with no Sandbox CRD, and the argument is stronger here: the CRD case at least fails at the first browser action, while this one never fails at all.
I have the third written and tested, because it is the one I can prove without guessing at a SandboxClaim schema that this repository does not describe. Happy to send either of the others instead if claiming is close, or if the feature should simply go.
Severity
Low to moderate. Nothing breaks and the default is off. It is money spent for no effect, on an option whose whole purpose is the thing it fails to deliver.
computers.sandbox.warmPool.enabled=truerenders aSandboxWarmPool, grants the service accountcreateanddeleteonsandboxclaims, and holds two pods ready. Nothing ever claims from it. The server has no code path that creates aSandboxClaim, so a Bot's first action still waits for a brand-new Sandbox to schedule and for Chromium to boot — the exact wait the pool exists to remove, now paid for twice.What the cold start actually does
Every request the sandbox provider makes when a Bot has no computer yet, captured through the provider's own injectable
fetchImpl:Three calls, none of them a claim. The
POSTcreates a fresh Sandbox from scratch while two warm ones sit idle beside it.Where the wiring stops
The interface declares the hook, optionally:
server/src/computer/provider.ts:85And the server calls it at
server/src/index.ts:206:The guard is never true.
createComputerProviderreturns one of three shapes — docker, shared, sandbox — andtypeof provider.warmisundefinedfor all three. The stringsandboxclaimappears nowhere underserver/. Becausewarm?()is optional, TypeScript is content and theifreads as deliberate rather than unfinished.This is a gap between the chart and the code that consumes it, not a typo. Claiming is what the upstream controller keys on
claim.Spec.WarmPoolRefto do; without a claim, a warm pool is only pods.Reproduction
Render the chart with the pool on and the CRDs declared:
kind: SandboxWarmPoolrenders withreplicas: 2, and the Role gainssandboxclaimswithget, list, create, delete.Build a sandbox provider with a recording
fetchImpl, asserver/tests/computer-sandbox.test.tsalready does, and calllocatefor a Bot with no computer. The three requests above are all of them.grep -rn sandboxclaim server/returns nothing.Why it matters
The pool is off by default, so this costs nobody who leaves it alone. The people it does reach are the ones who read the values comment, decided the first-action wait was worth paying for, and turned it on. They get the bill — two idle browsers, their nodes and their volumes — and none of the latency they bought it for. Nothing logs a warning, the pool reports healthy, and the only visible symptom is that cold starts feel exactly as slow as before.
The RBAC grant makes it read as finished. A reviewer checking whether claims are permitted finds that they are.
Three ways to close it
Implement claiming: have the sandbox provider create a
SandboxClaimand adopt what the controller hands back, instead of posting a freshSandbox. That is what the chart was written for and clearly the right end state.Or delete the switch —
warmpool.yaml,sandbox-template.yaml, thesandboxclaimsgrant and the unreachablewarm?()hook — so nothing offers an option that does not do anything.Or, in between, make the chart refuse to render with the pool on and say why, keeping every template for when claiming lands. That is the treatment
computers.mode: sandboxalready gets on a cluster with no Sandbox CRD, and the argument is stronger here: the CRD case at least fails at the first browser action, while this one never fails at all.I have the third written and tested, because it is the one I can prove without guessing at a
SandboxClaimschema that this repository does not describe. Happy to send either of the others instead if claiming is close, or if the feature should simply go.Severity
Low to moderate. Nothing breaks and the default is off. It is money spent for no effect, on an option whose whole purpose is the thing it fails to deliver.