Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
644bbcf
fix(snapshot): remove host Python sanitizer dependency
cv Sep 8, 2026
c4d1f2c
Merge branch 'main' into codex/remove-snapshot-python
cv Sep 8, 2026
49819af
fix(snapshot): package native sanitizer helper
cv Sep 8, 2026
c74ce67
fix(snapshot): complete native dependency handoff
cv Sep 8, 2026
4223ed2
test: exercise restored snapshot sanitizer artifact
cv Sep 8, 2026
646d00d
ci: pin artifact restore action with helper check
cv Sep 8, 2026
ffcf1be
fix: complete snapshot sanitizer artifact handoff
cv Sep 8, 2026
46abf0a
fix: surface snapshot sanitizer failures
cv Sep 8, 2026
0fafa29
fix: complete native sanitizer qualification
cv Sep 8, 2026
eca910a
test: restore snapshot sanitizer coverage
cv Sep 8, 2026
c159c99
fix: complete snapshot sanitizer handoff
cv Sep 8, 2026
71f87d2
ci: pin complete sanitizer artifact restore
cv Sep 8, 2026
7f29575
test: prove snapshot sanitizer limits
cv Sep 8, 2026
dd6a1ca
ci: select native sanitizer source changes
cv Sep 8, 2026
9eb4735
fix: preserve native probe cleanup recovery
cv Sep 8, 2026
e291dd3
test: refresh MCP restore transition digest
cv Sep 8, 2026
fbfb736
ci: select native sanitizer dependency changes
cv Sep 8, 2026
b10868c
merge: resolve conflicts with main
github-actions[bot] Sep 8, 2026
d338faa
Merge branch 'main' into codex/remove-snapshot-python
cv Sep 8, 2026
a615592
Merge branch 'main' into codex/remove-snapshot-python
cv Sep 9, 2026
90e5e1c
Merge branch 'main' into codex/remove-snapshot-python
cv Sep 9, 2026
26cf467
merge: resolve conflicts with main
github-actions[bot] Sep 9, 2026
7344a08
Merge branch 'main' into codex/remove-snapshot-python
cv Sep 9, 2026
b5748b0
fix(snapshot): restore compatibility and canonicalize native probes
sandl99 Sep 10, 2026
068fb00
merge: consume reviewed CI fixes and retire review report
sandl99 Sep 10, 2026
9cd0455
merge: resolve conflicts with main
github-actions[bot] Sep 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/e2e-standard-profile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ jobs:

- name: Restore exact-commit CLI artifact
if: ${{ inputs.restore_cli }}
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ inputs.cli_artifact_provenance }}

Expand Down
26 changes: 13 additions & 13 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2559,7 +2559,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -2837,7 +2837,7 @@ jobs:
"${test_evidence_dir}/dcode-base-image.json"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -3020,7 +3020,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -3326,7 +3326,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -3402,7 +3402,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -3693,7 +3693,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -3850,7 +3850,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -4123,7 +4123,7 @@ jobs:
# The restore action executes the candidate CLI for its final identity
# check. Candidate-controlled state starts with dependency preparation.
- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -5311,7 +5311,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -5484,7 +5484,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -5766,7 +5766,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -5909,7 +5909,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down Expand Up @@ -6007,7 +6007,7 @@ jobs:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@17759906bd7f80319c58af759dd60cfb893109bf
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c159c99ed5e7d683aa6db4c152c42e0b05803801
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/managed-images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ on:
- "ci/reviewed-npm-audit.json"
- "nemoclaw/**"
- "nemoclaw-blueprint/**"
- "package-lock.json"
- "package.json"
- "scripts/**"
- "src/lib/actions/sandbox/openshell-child-visible-credentials.v*.json"
- "src/lib/actions/sandbox/mcp-bridge-*.ts"
Expand All @@ -53,7 +55,9 @@ on:
- "src/lib/core/ports.ts"
- "src/lib/messaging/**"
- "src/lib/onboard/**"
- "src/lib/security/credential-filter.ts"
- "src/lib/security/credential-hash.ts"
- "src/lib/security/snapshot-sanitizer.ts"
- "src/lib/state/paths.ts"
- "src/lib/state/state-root.ts"
- "test/e2e/live/mcp-bridge*.ts"
Expand Down Expand Up @@ -1217,6 +1221,19 @@ jobs:
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22.19.0
- name: Validate the candidate snapshot sanitizer on the native architecture
shell: bash
run: |
set -euo pipefail
[[ "$(git rev-parse --verify HEAD)" == "$SOURCE_REVISION" ]] || {
echo "ERROR: snapshot sanitizer checkout does not match the exact PR head" >&2
exit 1
}
npm ci --ignore-scripts --no-audit --no-fund
npm run build:cli
npx --no-install vitest run \
--project package-contract \
test/package-contract/snapshot-sanitizer-boundary.test.ts
# Pi has no published base alias yet, so the candidate lane builds its own
# base on the target architecture. The activation change adds the shared
# published-base contract with the rest of the release cohort.
Expand Down
12 changes: 6 additions & 6 deletions docs/get-started/prerequisites.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ If you cannot add memory, configure at least 8 GB of swap to work around the iss
|------------|----------------------------------|
| Node.js | 22.19 or later |
| npm | 10 or later |
| Python | Python 3.10 through 3.13 for Model Router onboarding; Python 3 for direct blueprint-runner `snapshots prune` and `snapshots delete` operations on POSIX hosts |
| SHA-256 tool | `sha256sum` or `shasum`, required only when the installer must install or upgrade Node.js through nvm |
| Python | Python 3 at a trusted system location, with POSIX descriptor-relative filesystem support |
| Container runtime | Docker Engine, Docker Desktop, or Colima on a tested platform (default), or rootless Podman on qualified native Linux and Windows WSL2 paths |
| Platform | Refer to [Platforms](#platforms) below |

Expand All @@ -50,11 +50,11 @@ Before selecting it, review its qualified host requirements and limitations in [
Managed llama.cpp and Windows-host Ollama remain Docker Desktop-only on WSL. Managed local vLLM install/start remains Docker-backed and is unavailable when Docker is absent; attaching an already-running local vLLM server remains a separate path.
The portable experimental profile is separate and still requires a Docker-compatible `docker` command.

NemoClaw uses an isolated `python3` helper for descriptor-relative migration snapshot sanitization and deletion.
NemoClaw does not resolve this credential-bearing helper through the host `PATH`.
It accepts a verified executable at `/usr/bin/python3`, `/usr/local/bin/python3`, `/opt/homebrew/bin/python3`, `/opt/local/bin/python3`, or beside the Node.js executable.
The sanitizer fails closed before it reads snapshot content if no candidate passes its ownership, permission, and executable checks.
Supported Linux, macOS, and WSL environments provide the required POSIX filesystem operations.
NemoClaw uses the native filesystem support installed with its Node.js package for migration snapshot sanitization.
The sanitizer fails closed if that platform package is missing or cannot load.
Snapshot creation and rebuild sanitization do not require a host Python installation.
Model Router onboarding requires Python 3.10 through 3.13 on the host.
Direct blueprint-runner snapshot deletion and pruning still require Python 3 on POSIX hosts.
Native Windows is not a supported execution path; use WSL.

If you choose the native Linux Ollama install path, the onboard wizard also requires `zstd` for Ollama archive extraction.
Expand Down
2 changes: 1 addition & 1 deletion docs/manage-sandboxes/backup-restore.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Before NemoClaw marks a snapshot complete, it strips recognized credential value
- URL user information.
- A credential-bearing query parameter.

Dependency names in lockfile maps do not count as credential fields. NemoClaw also preserves valid, credential-free `node_modules/**/package.json` manifests byte for byte because dependency names can match credential field names. It omits an installed package manifest when the file contains invalid JSON, a credential or authentication field, a provider-shaped secret, or a credential-bearing URL. It continues to sanitize configuration and `.env` files inside installed dependency trees. It preserves OpenShell credential placeholders so rebuild can reattach the host-side provider. If NemoClaw cannot sanitize a copied configuration or environment file, it omits that file from the snapshot. If it cannot remove the unsafe file, snapshot creation returns an error. It deletes the incomplete backup when cleanup succeeds and reports when the backup remains. This sanitization uses an isolated `python3` helper on POSIX hosts to keep reads, replacements, and removals anchored to opened directory descriptors. If a copied file or parent directory changes identity during the operation, snapshot creation fails closed instead of following the changed path.
Dependency names in lockfile maps do not count as credential fields. NemoClaw also preserves valid, credential-free `node_modules/**/package.json` manifests byte for byte because dependency names can match credential field names. It omits an installed package manifest when the file contains invalid JSON, a credential or authentication field, a provider-shaped secret, or a credential-bearing URL. It continues to sanitize configuration and `.env` files inside installed dependency trees. It preserves OpenShell credential placeholders so rebuild can reattach the host-side provider. If NemoClaw cannot sanitize a copied configuration or environment file, it omits that file from the snapshot. If it cannot remove the unsafe file, snapshot creation returns an error. It deletes the incomplete backup when cleanup succeeds and reports when the backup remains. This sanitization uses NemoClaw's installed native filesystem support to keep reads and mutations contained within the snapshot root. If a copied file or parent directory changes identity during the operation, snapshot creation fails closed instead of following the changed path.

<AgentOnly variant="hermes">
A previous release sanitized dependency lockfiles and installed package manifests.
Expand Down
5 changes: 3 additions & 2 deletions docs/reference/host-files-and-state.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Run `$$nemoclaw onboard` to register a sandbox again.
Before NemoClaw retains a migration snapshot, it recursively sanitizes the copied OpenClaw state and every configured external root.
It preserves empty or comment-only YAML files and omits copied JSON, YAML, or `.env` files that it cannot sanitize.
If NemoClaw cannot remove an unsafe copied artifact, snapshot creation fails and attempts to delete the incomplete snapshot directory.
Sanitization requires `python3` on a POSIX host so every traversal and mutation can remain anchored to opened directory descriptors.
Sanitization uses the native filesystem support installed with NemoClaw's Node.js package.
It fails closed if a copied file or parent directory changes identity during sanitization.

The direct blueprint runner accepts these action arguments for migration snapshots:
Expand All @@ -135,7 +135,8 @@ An integration that invokes the direct runner can use `snapshots list` first to
`snapshots delete` accepts only one timestamped directory directly under `~/.nemoclaw/snapshots/`.
Both deletion commands are irreversible: they do not modify a running sandbox, but they remove host state that could otherwise be used for rollback or restore.

Snapshot sanitization and deletion require `python3` on a POSIX host.
Snapshot sanitization does not require a host Python installation.
Direct blueprint-runner `snapshots prune` and `snapshots delete` operations still require Python 3 on POSIX hosts.
Listing works on native Windows, but migration snapshot creation and deletion do not; use WSL for those operations on Windows.

## Uninstall Behavior
Expand Down
20 changes: 6 additions & 14 deletions docs/reference/troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1574,31 +1574,23 @@ $$nemoclaw <name> snapshot create
$$nemoclaw <name> rebuild
```

### Snapshot Sanitization Requires a Verified Python Interpreter
### Snapshot Sanitization Reports Missing Native Support

The CLI operations listed below stop when NemoClaw cannot resolve a verified interpreter. The error begins with this text:
The CLI operations listed below stop when NemoClaw cannot load its native filesystem support. The error begins with this text:

```text
python3 is required for snapshot sanitization; install python3 and rerun
Native snapshot sanitization support is unavailable; reinstall NemoClaw with optional dependencies and rerun
```

NemoClaw removes credentials from copied state with an isolated `python3` helper. The operation fails closed when that helper cannot run. These operations can report the error:
NemoClaw removes credentials from copied state with native filesystem operations provided by its Node.js package. The operation fails closed when the platform package is missing or cannot load. These operations can report the error:

- `$$nemoclaw <name> snapshot create`
- `$$nemoclaw <name> rebuild`, including rebuilds started by `$$nemoclaw upgrade-sandboxes`
- `$$nemoclaw backup-all`, including the installer's pre-upgrade backup and an eligible stopped Docker-driver sandbox

A host that already has `python3` can still report this message. NemoClaw does not search `PATH` for this credential-bearing helper. NemoClaw accepts `python3` only at these locations:
Reinstall NemoClaw without an option such as `--omit=optional` that excludes platform packages. Then rerun the complete command. For the supported Node.js and host requirements, refer to [Prerequisites](../get-started/prerequisites).

- `/usr/bin/python3`
- `/usr/local/bin/python3`
- `/opt/homebrew/bin/python3`
- `/opt/local/bin/python3`
- A `python3` executable beside the canonical Node.js executable

NemoClaw rejects a candidate that fails its ownership, permission, or executable checks. For more information about the interpreter requirement, refer to [Prerequisites](../get-started/prerequisites).

If NemoClaw reports that it removed the incomplete snapshot, install or repair `python3` at an accepted location. Then rerun the complete command.
If the error reports `native-probe-failed` and a retained temporary file, remove only the exact file path in that error. Confirm that the file is absent before you rerun the complete command.

If cleanup fails, treat the reported directory as retained until you confirm that it is absent.

Expand Down
Loading
Loading