feat(state): segregate host state root per gateway port - #6711
Conversation
A non-default NEMOCLAW_GATEWAY_PORT now resolves its own host state root at ~/.nemoclaw/gateways/<port>/, covering the sandbox registry, credentials, snapshots, onboard session, and inference adapter state, so two gateways on one host stay fully segregated. The default port keeps the shared ~/.nemoclaw/ location, so existing single-gateway hosts are byte-identical. Route the registry, credentials, config-io, onboard, inference, share, and diagnostics paths through one nemoclawStateRoot resolver keyed off GATEWAY_PORT. Keep the CLI source clone and the managed_swap marker host-shared. Uninstall targets the port-scoped state root, always preserves the gateways/ subtree so other environments survive even --destroy-user-data, and no longer tears down the host-shared swap file while other gateway-port environments remain. Fixes #3053 Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (5)
📝 WalkthroughWalkthroughThis change introduces port-scoped host state, legacy-state migration, host-wide registry validation, gateway-scoped installer and uninstall behavior, symlink-safe state writers, CLI migration gating, and corresponding tests and documentation. ChangesGateway-port state segregation
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant CLI as public-dispatch
participant Migration as migrateLegacyPortState
participant SharedRoot as Shared NemoClaw state
participant GatewayRoot as Port-scoped state
CLI->>Migration: migrate stateful invocation
Migration->>SharedRoot: read and classify legacy state
Migration->>GatewayRoot: move selected registry and files
Migration->>SharedRoot: rewrite remaining registry
Migration-->>CLI: migration result
sequenceDiagram
participant User
participant Uninstall as runUninstallPlan
participant Registry as gateway registries
participant OpenShell
participant Docker
User->>Uninstall: uninstall selected gateway
Uninstall->>Registry: inspect sibling environments
Registry-->>Uninstall: scope and sandbox names
Uninstall->>OpenShell: remove selected gateway resources
Uninstall->>Docker: remove selected gateway containers
Uninstall-->>User: cleanup result
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage remains at 96%, unchanged from the TypeScript / code-coverage/cliThe overall coverage in the Show a code coverage summary of the most impacted files.
Updated |
|
🌿 Preview your docs: https://nvidia-preview-pr-6711.docs.buildwithfern.com/nemoclaw |
PR Review Advisor — Blocking findings reportedAdvisor assessment: Blockers require maintainer review Model lanes
Nemotron output stays in workflow artifacts and does not change the assessment above. E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: 1 optional E2E recommendation
Blockers
|
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/manage-sandboxes/lifecycle.mdx`:
- Around line 154-157: Update the agent-specific lifecycle documentation block
to replace the invalid $$nemoclaw uninstall command with the literal Deep Agents
CLI command nemo-deepagents uninstall, while preserving the surrounding
gateway-state behavior and wording.
- Line 150: Update the lifecycle documentation near the NEMOCLAW_GATEWAY_PORT
state-root description to replace “fully segregated” with a narrower claim about
gateway-scoped state. Mention that the CLI source clone and managed_swap marker
remain shared under ~/.nemoclaw/ if describing shared host-level artifacts,
while preserving the separate registry, credentials, snapshots, and uninstall
behavior.
In `@docs/reference/architecture.mdx`:
- Line 311: Update the preceding ~/.nemoclaw/sandboxes.json documentation row to
state explicitly that it is the registry for the default gateway port only;
retain the separate non-default registry location described by the
gateways/<port> entry.
In `@src/lib/actions/uninstall/run-plan-gateway-segregation.test.ts`:
- Around line 70-73: Remove the conditional inside the mock run handler and
record every invoked command unconditionally in swapCommands. Update the related
assertions to verify unwanted commands with not.toContain, preserving the test’s
intent without adding an if statement.
In `@src/lib/onboard/usage-notice.ts`:
- Line 56: Update the usage-notice path construction to pass the active gateway
port to nemoclawStateRoot instead of relying on its default-port behavior.
Preserve the existing HOME fallback and usage-notice.json filename while
ensuring non-default gateways resolve their port-scoped state root.
In `@src/lib/share-command.ts`:
- Line 64: The defaultShareMountDir resolver currently ignores gateway identity,
causing all gateways to use the same mount root. Update defaultShareMountDir to
accept the active gateway port and include that port when deriving the mount
directory, then update every caller to pass the corresponding gateway port so
non-default gateways use isolated mount state.
In `@src/lib/state/onboard-session.ts`:
- Around line 48-50: Update the onboarding session path definitions around
SESSION_DIR, SESSION_FILE, and LOCK_FILE to derive the directory using the
active gateway port, rather than a single module-global nemoclawStateRoot value.
Ensure each non-default gateway receives distinct session and lock paths while
preserving the existing filenames.
In `@src/lib/state/state-root.test.ts`:
- Around line 37-41: Update the getNemoclawStateRoot default-port test to
explicitly control NEMOCLAW_GATEWAY_PORT, clearing it before module
initialization or using the explicit-port helper to assert default behavior.
Ensure the test remains isolated from the host environment and preserves the
expected shared root assertion.
In `@src/lib/state/state-root.ts`:
- Around line 19-24: Update resolveHome to use an empty-value fallback, matching
the credentials store behavior, so empty HOME values fall back to os.homedir()
before getNemoclawStateRoot constructs the state path. Keep the existing
getNemoclawStateRoot and nemoclawStateRoot flow unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 3fa4a17d-c76f-46ab-82a5-a14de9de2fd0
📒 Files selected for processing (21)
docs/manage-sandboxes/lifecycle.mdxdocs/reference/architecture.mdxsrc/lib/actions/uninstall/run-plan-gateway-segregation.test.tssrc/lib/actions/uninstall/run-plan.tssrc/lib/credentials/store.tssrc/lib/domain/uninstall/paths.tssrc/lib/inference/local-adapter-lifecycle.tssrc/lib/inference/local.tssrc/lib/onboard/docker-gpu-patch.tssrc/lib/onboard/model-router.tssrc/lib/onboard/sandbox-create-failure.tssrc/lib/onboard/usage-notice.tssrc/lib/share-command.tssrc/lib/state/config-io.tssrc/lib/state/onboard-session.tssrc/lib/state/paths.tssrc/lib/state/registry-port-segregation.test.tssrc/lib/state/registry.tssrc/lib/state/sandbox.tssrc/lib/state/state-root.test.tssrc/lib/state/state-root.ts
Fixes a regression the port-keyed state root introduced in a pre-existing CLI status test that temporarily overrides NEMOCLAW_GATEWAY_PORT to probe for a gateway-port conflict, closes the empty-HOME relative-path edge case in the state-root resolver, makes the default-port unit test control the environment variable it claims to test, corrects two doc wording issues (an overstated segregation claim and a Deep Agents command name), and drops a conditional from the new uninstall test to satisfy the codebase-growth guardrail. Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/repro-2666-silent-list-status.test.ts`:
- Line 210: Strengthen the port-specific scenario setup around seedRegistry so
the default ~/.nemoclaw registry cannot satisfy the test: clear it before the
scenario or seed it with distinct sentinel data, then assert the selected result
comes only from nemoclawStateRoot(home, port). Apply the same correction to the
corresponding occurrence.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 54a56463-3080-4275-8500-99fb477832c9
📒 Files selected for processing (6)
docs/manage-sandboxes/lifecycle.mdxdocs/reference/architecture.mdxsrc/lib/actions/uninstall/run-plan-gateway-segregation.test.tssrc/lib/state/state-root.test.tssrc/lib/state/state-root.tstest/repro-2666-silent-list-status.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- src/lib/state/state-root.ts
- src/lib/state/state-root.test.ts
- docs/manage-sandboxes/lifecycle.mdx
- src/lib/actions/uninstall/run-plan-gateway-segregation.test.ts
- docs/reference/architecture.mdx
The port-specific #3271 scenario seeded identical my-assist data under both the default and port-scoped registries, so a regression that kept reading the default root under a non-default NEMOCLAW_GATEWAY_PORT would still pass. Clear the default registry before that scenario so the test only succeeds when the CLI actually reads the port-scoped root. Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
…ate-root Signed-off-by: Tinson Lai <tinsonl@nvidia.com> # Conflicts: # src/lib/onboard/docker-gpu-patch.ts
Every production call site relied on nemoclawStateRoot's default gatewayPort parameter, which resolves to the correct value only because GATEWAY_PORT happens to be a process-wide constant computed once at module load. That's easy to misread as always resolving the default port. Thread GATEWAY_PORT through all 18 call sites explicitly so each one is self-evidently correct without tracing into core/ports.ts. No behavioural change — GATEWAY_PORT is the exact value the default parameter already supplied. Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
E2E Target Results —
|
| Test | Result |
|---|---|
| cloud-onboard | |
| concurrent-gateway-ports | |
| credential-sanitization | |
| inference-routing | ✅ success |
| network-policy | |
| onboard-repair | |
| onboard-resume | |
| security-posture |
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
E2E Target Results — ❌ Some tests failedRun: 29265796896
|
# Conflicts: # src/lib/actions/maintenance.ts
E2E Target Results — ❌ Some tests failedRun: 29266737670
|
|
Exact-head live-E2E diagnosis for the failing |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/manage-sandboxes/run-sandboxes.mdx`:
- Line 61: Reformat the affected documentation paragraphs so every sentence
begins on its own source line, including the sentences around the gateway
state-root and uninstall behavior. Preserve all wording and paragraph structure;
only add line breaks at sentence boundaries.
In `@docs/manage-sandboxes/uninstall-nemoclaw.mdx`:
- Line 35: Revise the uninstall behavior description so it no longer promises
deleting every entry under ~/.nemoclaw/ when no sibling gateways remain.
Document only the specific shared resources eligible for final-environment
cleanup, including managed_swap as established by the gateway segregation
behavior, while preserving the selected gateway’s resource and port-scoped
cleanup details.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 476cb23d-5784-43a7-aae9-d5fa12da9015
📒 Files selected for processing (3)
docs/manage-sandboxes/run-sandboxes.mdxdocs/manage-sandboxes/uninstall-nemoclaw.mdxdocs/reference/commands.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/reference/commands.mdx
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
cv
left a comment
There was a problem hiding this comment.
Maintainer deep review complete for commit 0cba390. Gateway-scoped state migration, registry validation, collision handling, uninstall preservation, security boundaries, DCO, and the focused/live test coverage are sound. The remaining blockers are the requested re-review and refreshed E2E gate.
Superseded by later verified commits that address gateway-scoped state pairing, migration validation and collision handling, fail-closed registry inspection, and uninstall preservation, with focused regressions. All 54 current checks are green.
<!-- markdownlint-disable MD041 --> ## Summary Adds the canonical `docs/changelog/2026-07-15.mdx` entry with the exact `## v0.0.84` heading for the release candidate range from `v0.0.83` through `710d2b36b9eebcb6bca3c2b2f796a1bdb69c3a31`. Fills two owner-page gaps for model-aware local inference health and pre-write OpenClaw candidate validation. ## Changes - Add the complete shared Fern changelog entry for `v0.0.84`, with literal CLI names and root-absolute OpenClaw and Hermes routes. - Document that sandbox status and doctor compare the configured Ollama or vLLM model with provider inventory without issuing a completion. - Document that host-side OpenClaw `config set` validates the complete candidate before replacing live config or reaching gateway restart. - Reconcile the `v0.0.84` release label with the commit range. PR #6773 is already contained in `v0.0.83` and remains documented there; CI, test-harness, docs-infrastructure, and `.js` to `.mts` migration-only changes require no additional user guidance. ### Source summary - [#6882](#6882) -> `docs/manage-sandboxes/backup-restore.mdx`, `docs/changelog/2026-07-15.mdx`: Explain that OpenClaw runtime identity and pairing state are excluded from snapshots and ignored during restore. - [#6873](#6873) -> `docs/inference/set-up-ollama.mdx`, `docs/changelog/2026-07-15.mdx`: Record the Ollama requested-model environment fallback and interactive default. - [#6835](#6835) -> `docs/changelog/2026-07-15.mdx`: Include the sandbox name in the documented rebuild resume-recovery behavior. - [#6886](#6886) -> `docs/inference/custom-endpoint-security.mdx`, `docs/inference/set-up-openai-compatible-endpoint.mdx`, `docs/changelog/2026-07-15.mdx`: Explain the exact-host trusted-private endpoint opt-in and retained SSRF boundaries. - [#6887](#6887) -> `docs/reference/commands.mdx`, `docs/changelog/2026-07-15.mdx`: Document Telegram channel health verdicts, summary behavior, and exit status. - [#6863](#6863) -> `docs/manage-sandboxes/lifecycle.mdx`, `docs/changelog/2026-07-15.mdx`: Add the missing model-inventory behavior for local status and doctor checks. - [#6902](#6902) -> `docs/manage-sandboxes/runtime-controls.mdx`, `docs/changelog/2026-07-15.mdx`: Add the missing pre-write OpenClaw candidate-validation contract. - [#6916](#6916) -> `docs/changelog/2026-07-15.mdx`: Preserve the failed-session fresh-install recovery correction in the release entry. - [#6934](#6934) -> `docs/reference/commands.mdx`, `docs/reference/troubleshooting.mdx`, `docs/security/credential-storage.mdx`, `docs/changelog/2026-07-15.mdx`: Summarize completed-prompt checkpointing and validated credential reuse during OpenClaw resume. - [#6898](#6898) -> `docs/inference/switch-models.mdx`, `docs/inference/switch-providers.mdx`, `docs/reference/troubleshooting.mdx`, `docs/changelog/2026-07-15.mdx`: Explain Hermes dashboard convergence after in-place inference changes. - [#6711](#6711) -> `docs/manage-sandboxes/run-sandboxes.mdx`, `docs/manage-sandboxes/uninstall-nemoclaw.mdx`, `docs/reference/architecture.mdx`, `docs/reference/commands.mdx`, `docs/changelog/2026-07-15.mdx`: Summarize port-scoped host state and uninstall preservation. - [#6767](#6767) -> `docs/inference/configure-model-limits.mdx`, `docs/inference/set-up-ollama.mdx`, `docs/reference/troubleshooting.mdx`, `docs/changelog/2026-07-15.mdx`: Record the Hermes `64000`-token Ollama floor and unchanged OpenClaw floor. - [#6862](#6862) -> `docs/get-started/quickstart.mdx`, `docs/inference/verify-inference-route.mdx`, `docs/changelog/2026-07-15.mdx`: Explain retryable not-ready finalization for unhealthy inference routes. - [#6766](#6766) -> `docs/security/tcb-boundary.mdx`, `docs/changelog/2026-07-15.mdx`: Document definitive stale transition-lock recovery and fail-closed ambiguous cases. - [#6948](#6948) -> `docs/manage-sandboxes/manage-mcp-servers.mdx`, `docs/changelog/2026-07-15.mdx`: Include Hermes MCP apply-state race recovery in the release entry without changing the established user workflow. - [#6964](#6964) -> `docs/reference/troubleshooting.mdx`, `docs/changelog/2026-07-15.mdx`: Record complete agent-specific fresh-install and resume recovery commands. - [#6883](#6883) -> `docs/get-started/quickstart.mdx`, `docs/inference/set-up-vllm.mdx`, `docs/reference/platform-support.mdx`, `docs/changelog/2026-07-15.mdx`: Summarize the DGX Station Nemotron Ultra express path and pinned managed-vLLM recipe. - [#6985](#6985) -> `docs/inference/set-up-vllm.mdx`, `docs/reference/commands.mdx`, `docs/changelog/2026-07-15.mdx`: Capture the final automated and interactive storage-warning behavior. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [x] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [ ] Tests added or updated for changed behavior - [x] Existing tests cover changed behavior — `test/changelog-docs.test.ts` validates the dated-entry structure, exact version heading, and preserved history. - [ ] Tests not applicable — justification: - [x] Docs updated for user-facing behavior changes - [ ] Docs not applicable — justification: - [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [ ] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run check:diff` passed when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — `npx vitest run test/changelog-docs.test.ts` (6 passed) - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — not run for this doc-only change. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) — completed with 0 errors; Fern reported the unchanged unauthenticated redirect-check and light-theme contrast warnings. - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) — the native changelog entry uses the required parser-safe MDX SPDX comment and intentionally has no frontmatter. --- Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added the v0.0.84 changelog entry covering setup, endpoint onboarding, model handling, sandbox readiness, recovery, channel status, and configuration safeguards. * Clarified that sandbox health checks validate configured models against local Ollama and vLLM provider inventories without generating completions or consuming tokens. * Documented that invalid runtime configuration changes are rejected while preserving the existing working configuration. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
A non-default
NEMOCLAW_GATEWAY_PORTpreviously shared the single~/.nemoclaw/host state root, so its sandbox registry, legacy credential-migration files, snapshots, and onboarding state collided with the default gateway's. This routes the host state root through onenemoclawStateRootresolver keyed off the existing gateway port, so a non-default port now owns a separate~/.nemoclaw/gateways/<port>/root while the default port stays byte-identical at~/.nemoclaw/.Related Issue
Fixes #3053
Changes
src/lib/state/state-root.ts—nemoclawStateRoot(home, gatewayPort)returns~/.nemoclawfor the default gateway port and~/.nemoclaw/gateways/<port>otherwise. It is keyed off the existingGATEWAY_PORTconstant so the state root and the gateway name (nemoclaw/nemoclaw-<port>) always agree.sandboxes.json), legacy credential-migration path, config-io permission-heal and remediation, onboard session, rebuild backups, blueprint cache, model router, local-inference adapter state and Ollama proxy token, share mounts, usage notice, and onboard-failure diagnostics.sourceclone and themanaged_swapmarker at the shared~/.nemoclaw/root — one CLI binary and one host swap file exist per machine, not per gateway.gateways/subtree so other environments survive even--destroy-user-data, prints the resolved state directory in its plan output, and skips host-swap teardown while other gateway-port environments remain.docs/manage-sandboxes/lifecycle.mdxanddocs/reference/architecture.mdx.This adds one resolver abstraction because the
~/.nemoclawroot was inlined at every state-path site with no single lever; segregation needs a single keyed source of truth. Its current consumer is #3053 (two segregated NemoClaw environments on one host). The default-port byte-identical contract is protected bysrc/lib/state/state-root.test.ts; the end-to-end per-port fork bysrc/lib/state/registry-port-segregation.test.ts; and the uninstall preservation plus host-swap guard bysrc/lib/actions/uninstall/run-plan-gateway-segregation.test.ts.Type of Change
Quality Gates
Verification
Verifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed, ornpm run check:diffpassed when hooks were skipped or unavailablenpm run test:changed— 154 files and 1,541 tests passed;npm run checks— passed;npm run typecheck:cli— 0 errorsnpm testfor broad runtime/test-harness changes;npm run checkfor repo-wide validation/coverage changes — command/result:npm run docsbuilds without warnings (doc changes only)Signed-off-by: Tinson Lai tinsonl@nvidia.com
Summary by CodeRabbit
~/.nemoclaw/gateways/<port>/(separate registries/snapshots/state, mounts, diagnostics, model-router state, and adapter/Ollama token paths).NEMOCLAW_GATEWAY_PORTdescription.