You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Ubuntu 24.04][Sandbox] Upgrading across #8677 orphans every pre-existing Docker sandbox on the first gateway restart: records are purged and baked sandbox tokens are rejected #8740
fix(uninstall): isolate scoped gateway shutdown #8677 (fa2b57746) gives each gateway a state-scoped Docker sandbox_namespace and adds a hash suffix to gateway_id. Containers created before it carry openshell.ai/sandbox-namespace=default and a sandbox JWT whose iss is the unsuffixed gateway id.
The generated TOML is rewritten when the gateway is started, so the break lands on the first gateway restart after the upgrade — the normal path after a host reboot. Commands that find a live gateway (list, <name> status, doctor) change nothing.
Reproduced by construction: onboard on 04e28aa2a (the parent commit), upgrade to dfef83e26, restart the gateway. The resume sweep reports missing_backend=1, and 303 s later (ORPHAN_GRACE_PERIOD = 300 s) the record is removed from the store. openshell sandbox list then returns none and nemoclaw <name> status reports the sandbox as registered locally but absent from the live gateway.
The container also crash-loops on Policy fetch failed after 5 attempts: ... invalid token: InvalidIssuer, because the JWT baked at /etc/openshell/auth/sandbox.jwt is non-expiring (exp: 0, fix(onboard): make local docker-driver gateway JWT non-expiring #6304) and still names the old issuer.
Recovery is not guaranteed. Pinning the legacy gateway_id and sandbox_namespace back into the generated TOML brought three pre-existing sandboxes on this machine fully back (records recreated at the gateway restart, Ready, workspaces intact). On the purpose-built sandbox the same restoration only fixed the token half: InvalidIssuer became sandbox not found, and the record did not come back, through a gateway restart and a reconcile interval. I did not trace what decides between the two outcomes.
Description
What happened
On an installation whose sandboxes were created before #8677, the first gateway start on current main silently unregisters every sandbox and leaves its container unable to fetch policy.
Two independent effects of the same commit:
A — the store record is purged. The regenerated TOML sets sandbox_namespace to the new gateway id. The Docker driver's list / find paths filter on the openshell.ai/sandbox-namespace label (crates/openshell-driver-docker/src/lib.rs:1217-1257 at v0.0.101; the filter uses all(true), so container state is not the factor), and pre-existing containers are labeled default — the driver's own default when the field is absent (lib.rs:148). The sandbox becomes invisible, prune_missing_sandbox re-checks, finds nothing, and deletes the record (crates/openshell-server/src/compute/mod.rs:2348-2412).
B — the sandbox token stops validating.gatewayIdForStateDir now appends a UID+state-root hash, and the gateway mints and verifies sandbox JWTs against that id. The token written into the container at creation time still carries the old one, so every policy fetch fails with InvalidIssuer and the container crash-loops.
Effect A is what a user sees as "registered locally, but is not present in the live OpenShell gateway", with the CLI suggesting rebuild --yes or destroy — both destructive, for sandboxes whose containers and workspaces are intact on disk.
What I expected
An upgrade that changes the namespace and the gateway identity should carry existing sandboxes across: adopt containers labeled with the legacy namespace (relabel or match both during a transition), and accept the legacy issuer for tokens already minted, rather than purging records and rejecting the tokens.
Not yet released
fa2b57746 is on main and is not contained in any tag (git tag --contains returns nothing; latest tag v0.0.105), so this is reachable today only by source installs.
Related work I checked first
Several issues share the user-facing message but not this cause, so I am filing separately rather than commenting on them:
fix(sandbox): repair legacy gateway upgrade recovery #8735 repairs legacy gateway upgrade recovery by reusing a validated installer backup for stale Ready / Running sandboxes. That is the recovery path after a sandbox has been orphaned, and it is fail-closed when no validated backup exists. It does not adopt the containers, which in this report are intact and running on disk with their workspaces.
I re-ran the trigger on v0.0.105-96-gacf810d9a (current main at the time of filing) against a gateway still holding the legacy values, and it still breaks: the gateway start rewrote gateway_id and sandbox_namespace to the suffixed form, openshell sandbox list returned No sandboxes found. while the container was running, status printed the "not present in the live OpenShell gateway" message, and the container failed with invalid token: InvalidIssuer. The store record had already been purged on the earlier run, so the purge lines quoted below are from the dfef83e26 reproduction, not the re-run. No commit between the two builds touches sandbox_namespace, gateway_id, or issuer validation.
Reproduction Steps
Docker driver, dedicated gateway port so nothing else is involved.
Build the parent of the commit and onboard a sandbox:
State after onboarding: sandbox Ready, dashboard /health returns 200, container label openshell.ai/sandbox-namespace=default, generated TOML has gateway_id = "nemoclaw-openshell-docker-gateway-18900"
and no sandbox_namespace line, and the in-container JWT claims iss = openshell-gateway:nemoclaw-openshell-docker-gateway-18900.
Upgrade and confirm nothing breaks while the gateway keeps running:
git switch --detach dfef83e26 # current main
npm run build:cli
NEMOCLAW_GATEWAY_PORT=18900 node bin/nemoclaw.js list
NEMOCLAW_GATEWAY_PORT=18900 node bin/nemoclaw.js nsrepro status
NEMOCLAW_GATEWAY_PORT=18900 node bin/nemoclaw.js doctor
The TOML is unchanged and the sandbox stays Ready.
Restart the gateway, as a host reboot would:
kill "$(cat <state-dir>/openshell-docker-gateway-18900/openshell-gateway.pid)"
NEMOCLAW_GATEWAY_PORT=18900 node bin/nemoclaw.js doctor
Observed from there:
# TOML, rewritten by the gateway start
gateway_id = "nemoclaw-openshell-docker-gateway-18900-1be7ec74403d"
sandbox_namespace = "nemoclaw-openshell-docker-gateway-18900-1be7ec74403d"
# gateway log
WARN Cannot resume sandbox: backend resource is missing sandbox_name=nsrepro
INFO Sandbox resume sweep complete resumed=0 missing_backend=1 failed=0
INFO reconcile{backend_count=0 store_count=1}: Removing sandbox from store after it
disappeared from the compute driver snapshot sandbox_name=nsrepro age_secs=303
# openshell sandbox list -g nemoclaw-18900
No sandboxes found.
# nemoclaw nsrepro status
Sandbox 'nsrepro' is registered locally, but is not present in the live OpenShell gateway.
# container log, after docker start
Error: Policy fetch failed after 5 attempts: code: 'The request does not have valid
authentication credentials', message: "invalid token: InvalidIssuer"
The same sequence hit three sandboxes that predated the upgrade on this machine; there the records were gone by the end of the startup sweep, without the 300 s wait I saw on nsrepro. I did not trace what accounts for the difference.
Partial recovery, without destroying anything
Editing the generated TOML back to the legacy values and restarting the gateway re-adopted the
three pre-existing sandboxes on this machine and restored Ready, with workspaces intact. On the
sandbox built for this report the same edit only restored token validity — the purged record did
not return — so treat this as a recovery worth trying, not a reliable one:
Two caveats: it reinstates the cross-gateway coupling #8677 fixed (stopping one gateway then stops the other's containers), and any later doctor / onboard / connect rewrites the file and restarts the process, because the runtime marker check treats the restored process as legacy (src/lib/onboard/host-gateway-process.ts:241).
Environment
OS: Ubuntu 24.04.4 LTS on WSL2 (kernel 6.6.87.2-microsoft-standard-WSL2), x86_64 workstation
Node.js: v24.18.0
Docker: Engine 29.2.1
NemoClaw: v0.0.105-91-gdfef83e26 (source build) for the by-construction reproduction; pre-upgrade build 04e28aa2a; trigger re-verified on v0.0.105-96-gacf810d9a
OpenShell: 0.0.101 (gateway, CLI, sandbox), the version the current blueprint pins
Driver: docker
Debug Output
nemoclaw debug --quick on this host includes the machine hostname, internal DNS servers and
local paths, so I have not pasted it wholesale. The evidence specific to this bug is quoted
above: the regenerated TOML, the gateway log lines for the sweep and the purge, the openshell sandbox list result, the CLI message, and the container's InvalidIssuer failure.
Happy to provide a redacted bundle if you want the full collection.
Checklist
I confirmed this bug is reproducible
I searched existing issues and this is not a duplicate
Investigation Summary
fa2b57746) gives each gateway a state-scoped Dockersandbox_namespaceand adds a hash suffix togateway_id. Containers created before it carryopenshell.ai/sandbox-namespace=defaultand a sandbox JWT whoseissis the unsuffixed gateway id.list,<name> status,doctor) change nothing.04e28aa2a(the parent commit), upgrade todfef83e26, restart the gateway. The resume sweep reportsmissing_backend=1, and 303 s later (ORPHAN_GRACE_PERIOD= 300 s) the record is removed from the store.openshell sandbox listthen returns none andnemoclaw <name> statusreports the sandbox as registered locally but absent from the live gateway.Policy fetch failed after 5 attempts: ... invalid token: InvalidIssuer, because the JWT baked at/etc/openshell/auth/sandbox.jwtis non-expiring (exp: 0, fix(onboard): make local docker-driver gateway JWT non-expiring #6304) and still names the old issuer.gateway_idandsandbox_namespaceback into the generated TOML brought three pre-existing sandboxes on this machine fully back (records recreated at the gateway restart,Ready, workspaces intact). On the purpose-built sandbox the same restoration only fixed the token half:InvalidIssuerbecamesandbox not found, and the record did not come back, through a gateway restart and a reconcile interval. I did not trace what decides between the two outcomes.Description
What happened
On an installation whose sandboxes were created before #8677, the first gateway start on current
mainsilently unregisters every sandbox and leaves its container unable to fetch policy.Two independent effects of the same commit:
A — the store record is purged. The regenerated TOML sets
sandbox_namespaceto the new gateway id. The Docker driver'slist/findpaths filter on theopenshell.ai/sandbox-namespacelabel (crates/openshell-driver-docker/src/lib.rs:1217-1257atv0.0.101; the filter usesall(true), so container state is not the factor), and pre-existing containers are labeleddefault— the driver's own default when the field is absent (lib.rs:148). The sandbox becomes invisible,prune_missing_sandboxre-checks, finds nothing, and deletes the record (crates/openshell-server/src/compute/mod.rs:2348-2412).B — the sandbox token stops validating.
gatewayIdForStateDirnow appends a UID+state-root hash, and the gateway mints and verifies sandbox JWTs against that id. The token written into the container at creation time still carries the old one, so every policy fetch fails withInvalidIssuerand the container crash-loops.Effect A is what a user sees as "registered locally, but is not present in the live OpenShell gateway", with the CLI suggesting
rebuild --yesordestroy— both destructive, for sandboxes whose containers and workspaces are intact on disk.What I expected
An upgrade that changes the namespace and the gateway identity should carry existing sandboxes across: adopt containers labeled with the legacy namespace (relabel or match both during a transition), and accept the legacy issuer for tokens already minted, rather than purging records and rejecting the tokens.
Not yet released
fa2b57746is onmainand is not contained in any tag (git tag --containsreturns nothing; latest tagv0.0.105), so this is reachable today only by source installs.Related work I checked first
Several issues share the user-facing message but not this cause, so I am filing separately rather than commenting on them:
sandbox_namespace/sandbox-namespaceand found no occurrence.Ready/Runningsandboxes. That is the recovery path after a sandbox has been orphaned, and it is fail-closed when no validated backup exists. It does not adopt the containers, which in this report are intact and running on disk with their workspaces.hermes rebuildon an older LKG commit.I re-ran the trigger on
v0.0.105-96-gacf810d9a(currentmainat the time of filing) against a gateway still holding the legacy values, and it still breaks: the gateway start rewrotegateway_idandsandbox_namespaceto the suffixed form,openshell sandbox listreturnedNo sandboxes found.while the container was running,statusprinted the "not present in the live OpenShell gateway" message, and the container failed withinvalid token: InvalidIssuer. The store record had already been purged on the earlier run, so the purge lines quoted below are from thedfef83e26reproduction, not the re-run. No commit between the two builds touchessandbox_namespace,gateway_id, or issuer validation.Reproduction Steps
Docker driver, dedicated gateway port so nothing else is involved.
Build the parent of the commit and onboard a sandbox:
State after onboarding: sandbox
Ready, dashboard/healthreturns 200, container labelopenshell.ai/sandbox-namespace=default, generated TOML hasgateway_id = "nemoclaw-openshell-docker-gateway-18900"and no
sandbox_namespaceline, and the in-container JWT claimsiss = openshell-gateway:nemoclaw-openshell-docker-gateway-18900.Upgrade and confirm nothing breaks while the gateway keeps running:
The TOML is unchanged and the sandbox stays
Ready.Restart the gateway, as a host reboot would:
Observed from there:
The same sequence hit three sandboxes that predated the upgrade on this machine; there the records were gone by the end of the startup sweep, without the 300 s wait I saw on
nsrepro. I did not trace what accounts for the difference.Partial recovery, without destroying anything
Editing the generated TOML back to the legacy values and restarting the gateway re-adopted the
three pre-existing sandboxes on this machine and restored
Ready, with workspaces intact. On thesandbox built for this report the same edit only restored token validity — the purged record did
not return — so treat this as a recovery worth trying, not a reliable one:
Two caveats: it reinstates the cross-gateway coupling #8677 fixed (stopping one gateway then stops the other's containers), and any later
doctor/onboard/connectrewrites the file and restarts the process, because the runtime marker check treats the restored process as legacy (src/lib/onboard/host-gateway-process.ts:241).Environment
v0.0.105-91-gdfef83e26(source build) for the by-construction reproduction; pre-upgrade build04e28aa2a; trigger re-verified onv0.0.105-96-gacf810d9aDebug Output
nemoclaw debug --quickon this host includes the machine hostname, internal DNS servers andlocal paths, so I have not pasted it wholesale. The evidence specific to this bug is quoted
above: the regenerated TOML, the gateway log lines for the sweep and the purge, the
openshell sandbox listresult, the CLI message, and the container'sInvalidIssuerfailure.Happy to provide a redacted bundle if you want the full collection.
Checklist