fix(shields): write an absent config hash before locking a config - #7995
Conversation
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
📝 WalkthroughWalkthroughAdds secure repair for missing configuration hashes before non-Hermes locking. Extends protected-parent handling to OpenClaw, Hermes, and LangChain Deep Agents Code. Adds lock, unlock, filesystem, race, and failure tests. ChangesSandbox shielding
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant ShieldLock
participant HashRepair
participant ConfigFilesystem
participant LockVerification
ShieldLock->>HashRepair: repair missing configuration hash
HashRepair->>ConfigFilesystem: validate and hash configuration
HashRepair->>ConfigFilesystem: create read-only .config-hash
ShieldLock->>ConfigFilesystem: lock configuration and protected parent
LockVerification->>ConfigFilesystem: verify ownership and permissions
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage in commit 2b0beb2 in the TypeScript / code-coverage/cliThe overall coverage in commit 2b0beb2 in the Show a code coverage summary of the most impacted files.
Updated |
PR Review Advisor — InformationalAdvisor assessment: Informational / low confidence Model lanes
Second-opinion terminology and E2E selections are advisory. They do not change the primary assessment or E2E / PR Gate. E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/lib/shields/seal.test.ts (1)
38-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSurface the spawn error so a missing
python3is diagnosable.
spawnSyncreports ENOENT throughresult.error, not throughstatusorstderr. If the interpreter is absent, every assertion fails with an unclear message.♻️ Proposed change to fail with the spawn error
const result = spawnSync(binary, args, { encoding: "utf-8" }); + if (result.error) throw result.error; return { status: result.status, stderr: (result.stderr ?? "").trim() };🤖 Prompt for 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. In `@src/lib/shields/seal.test.ts` around lines 38 - 39, Update the helper wrapping spawnSync to inspect result.error and surface its details when the Python process cannot be started, including the missing python3/ENOENT case. Preserve the existing status and trimmed stderr return behavior for successfully spawned processes.src/lib/shields/policy-transition.test.ts (1)
118-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSeed the emulated record so the lock steps stay observable.
The python3 branch inserts HASH_PATH already at mode
444and ownerroot:root. The assertion at Line 204 then passes even if the lock never appliedchmod 444orchown root:rootto the record. Create the record with a non-locked mode and owner so the assertion proves the lock steps ran.♻️ Proposed change to the repair emulation
if (head === "python3") { repairCalls.push(cmd); - entries.set(HASH_PATH, { mode: "444", owner: "root:root" }); + entries.set(HASH_PATH, { mode: "600", owner: "sandbox:sandbox" }); return ""; }Based on learnings about the path instruction "Flag copied production algorithms, broad mocks that bypass the behavior under test, and conditionals that make a test pass without exercising its claim".
🤖 Prompt for 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. In `@src/lib/shields/policy-transition.test.ts` around lines 118 - 122, Update the python3 branch of the repair emulation so the HASH_PATH record starts with a non-locked mode and owner, rather than mode 444 and root:root. Keep the existing repairCalls tracking and record creation flow, allowing the assertion to verify that the lock operations actually apply chmod 444 and chown root:root.Source: Path instructions
🤖 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 `@src/lib/shields/index.ts`:
- Around line 1582-1590: Validate custom BASE_IMAGE values used for python3
before proceeding with non-Hermes locking, ensuring the image provides the
python3 executable required by writeAbsentConfigHashNoSymlinkFollow. Add the
check in the existing Dockerfile/build validation flow for custom base images,
while preserving the standard-image behavior and rejecting invalid images with a
clear error.
In `@src/lib/shields/seal.ts`:
- Around line 52-56: Update the flag construction in the relevant seal path to
require O_NOFOLLOW from os and fail closed when it is unavailable, rather than
defaulting to zero; do this before any path open. Apply the same required-flag
guard to the creation flags constructed around the creation logic, while
preserving the existing directory and nonblocking flag behavior.
---
Nitpick comments:
In `@src/lib/shields/policy-transition.test.ts`:
- Around line 118-122: Update the python3 branch of the repair emulation so the
HASH_PATH record starts with a non-locked mode and owner, rather than mode 444
and root:root. Keep the existing repairCalls tracking and record creation flow,
allowing the assertion to verify that the lock operations actually apply chmod
444 and chown root:root.
In `@src/lib/shields/seal.test.ts`:
- Around line 38-39: Update the helper wrapping spawnSync to inspect
result.error and surface its details when the Python process cannot be started,
including the missing python3/ENOENT case. Preserve the existing status and
trimmed stderr return behavior for successfully spawned processes.
🪄 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: 4d14953c-6b4c-46a8-b6de-28acedecad9f
📒 Files selected for processing (4)
src/lib/shields/index.tssrc/lib/shields/policy-transition.test.tssrc/lib/shields/seal.test.tssrc/lib/shields/seal.ts
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
src/lib/shields/policy-transition.test.ts (1)
136-198: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueKey the fixture on the script, not only on
python3.The repair script and the unlock script are both invoked as
python3. The fixture dispatches onargv[0], so one handler serves both. A future flow that runs repair and unlock in the same test would silently receive the wrong model. Match on the script argument, or on a distinguishing argv position, so each handler stays bound to one script.Also applies to: 272-279
🤖 Prompt for 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. In `@src/lib/shields/policy-transition.test.ts` around lines 136 - 198, The commandHandlers fixture currently dispatches both repair and unlock invocations through the same “python3” handler. Update the “python3” dispatch in commandHandlers to distinguish the script argument or another stable argv position, and bind repair-specific state updates only to the repair script while preserving separate behavior for the unlock script.src/lib/shields/verify-lock.test.ts (1)
205-228: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd the passing direction for the Deep Agents target.
This case proves only that a replaceable parent is reported. Add a case with
/sandboxat1775 root:sandboxand assert that noparent dirissue is produced. That pins both directions of the new allowlist entry.🤖 Prompt for 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. In `@src/lib/shields/verify-lock.test.ts` around lines 205 - 228, Extend the Deep Agents coverage near deepAgentsTarget with a passing case using /sandbox mode 1775 and owner root:sandbox while retaining the existing target and sensitive-file setup. Invoke verifyShieldsLockState with parent protection enabled and assert that issues contains no entries matching “parent dir”, covering the accepted allowlist configuration.src/lib/shields/seal.ts (1)
185-231: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConfirm the intended behavior when an existing record holds a stale digest.
inspect_hash_recordreturns the existing record, and the flow then skips hashing and creation. The record content is never compared to the current configuration file.src/lib/shields/seal.test.tsLines 222-232 assert this preservation, so the behavior looks deliberate. State the reason in a comment so a later reader does not treat this as a missing verification step.🤖 Prompt for 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. In `@src/lib/shields/seal.ts` around lines 185 - 231, Add a concise comment near the existing-record branch in the hash repair flow, around inspect_hash_record and the existing is None check, stating that an existing hash record is intentionally preserved without recomputing or comparing the current configuration digest, including when stale. Do not alter the control flow or verification behavior covered by the tests.src/lib/shields/seal.test.ts (1)
50-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winProve that the injected race actually fired.
racePlantWrapperpatchesos.statandos.openand keys on the literal name.config-hashand onflags & os.O_EXCL. If the repair script later opens the record by a different name or withoutO_EXCL, the patches never fire. The test then still passes, because the pre-planted symlink alone makesinspect_hash_recordfail with "not a regular file".Make the harness record that both hooks fired, and assert that signal in the test. For example, write a marker to stderr from
raced_openand assert it inoutcome.stderr.As per path instructions: "Flag copied production algorithms, broad mocks that bypass the behavior under test, and conditionals that make a test pass without exercising its claim."
Also applies to: 250-265
🤖 Prompt for 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. In `@src/lib/shields/seal.test.ts` around lines 50 - 80, Update the raced_stat and raced_open hook functions in racePlantWrapper to write distinguishing markers to stderr when they fire the injected conditions. Then add assertions in the test to verify both markers appear in outcome.stderr, ensuring the test exercises the actual race condition hooks rather than just relying on the pre-planted symlink to cause failure.Source: Path instructions
🤖 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 `@src/lib/shields/index.ts`:
- Around line 2131-2134: Replace the inline verifyParentProtection predicate
with the existing requiresProtectedSandboxParent helper, passing the relevant
target and sandbox path inputs so its protected-agent and /sandbox/ conditions
are reused. Apply the same helper to the duplicate predicate around the other
referenced logic, removing repeated agent-name checks while preserving the
current behavior.
In `@src/lib/shields/seal.ts`:
- Around line 141-158: The production protected-parent decision in
src/lib/shields/seal.ts lines 141-158 must no longer depend on
NEMOCLAW_TEST_PROTECT_CONFIG_PARENT; retain the ownership and
writable-without-sticky checks for all non-production-condition parents while
preserving fail-closed behavior. Update src/lib/shields/seal.test.ts lines
184-220 to exercise the protected-parent path using a fixture matching the
production condition or an explicit test-only argv flag that production callers
never pass, rather than the environment variable.
---
Nitpick comments:
In `@src/lib/shields/policy-transition.test.ts`:
- Around line 136-198: The commandHandlers fixture currently dispatches both
repair and unlock invocations through the same “python3” handler. Update the
“python3” dispatch in commandHandlers to distinguish the script argument or
another stable argv position, and bind repair-specific state updates only to the
repair script while preserving separate behavior for the unlock script.
In `@src/lib/shields/seal.test.ts`:
- Around line 50-80: Update the raced_stat and raced_open hook functions in
racePlantWrapper to write distinguishing markers to stderr when they fire the
injected conditions. Then add assertions in the test to verify both markers
appear in outcome.stderr, ensuring the test exercises the actual race condition
hooks rather than just relying on the pre-planted symlink to cause failure.
In `@src/lib/shields/seal.ts`:
- Around line 185-231: Add a concise comment near the existing-record branch in
the hash repair flow, around inspect_hash_record and the existing is None check,
stating that an existing hash record is intentionally preserved without
recomputing or comparing the current configuration digest, including when stale.
Do not alter the control flow or verification behavior covered by the tests.
In `@src/lib/shields/verify-lock.test.ts`:
- Around line 205-228: Extend the Deep Agents coverage near deepAgentsTarget
with a passing case using /sandbox mode 1775 and owner root:sandbox while
retaining the existing target and sensitive-file setup. Invoke
verifyShieldsLockState with parent protection enabled and assert that issues
contains no entries matching “parent dir”, covering the accepted allowlist
configuration.
🪄 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: df9a27f0-8854-4e95-887e-31df9df30624
📒 Files selected for processing (6)
src/lib/shields/index.tssrc/lib/shields/policy-transition.test.tssrc/lib/shields/seal.test.tssrc/lib/shields/seal.tssrc/lib/shields/verify-lock.test.tssrc/lib/shields/verify-lock.ts
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
senthilr-nv
left a comment
There was a problem hiding this comment.
Reviewed exact head 0146c7b against current main 5cd29cc. Product scope PASS: fixes accepted issue #7977 for the existing Deep Agents shields-up surface. Security review PASS across secrets, input/path validation, authorization, dependencies, error handling, cryptography, configuration, security tests, and system/TOCTOU controls; no actionable findings. Focused tests 50/50, complete Shields suite 272/272, CLI typecheck, and repository checks pass locally. Cross-issue sweep found no adjacent fixes or contradictions; all CodeRabbit threads are resolved; independent documentation review passed no-docs-needed. Approval is based on the reviewed PR code per maintainer direction; GitHub remains separately blocked by an unrelated repeated timeout in mcp-bridge-status-state.test.ts.
<!-- markdownlint-disable MD041 --> ## Summary Follow-up review of merged #7995 and #7847 found two independent state-handling gaps. Shields recovery now remains bound to its original configuration target. Migration preparation now installs only verified, credential-filtered configuration bytes through a pinned directory descriptor. ## Changes - Follow-up to #7995: - Persist the agent name, config path, and config directory in each Shields timer marker. - Use that persisted target for detached-timer and expired-marker recovery when registry state is unavailable or resolves to a different target. - Give Deep Agents a canonical protected-file set and a descriptor-sealed config-lock transaction. - Fail closed when the transaction cannot restore or confirm a protected posture. - Document recovery from a critical Deep Agents config-lock failure. - Follow-up to #7847: - Exclude the copied `openclaw.json` from the general recursive copy. - Read the copied config through the descriptor-bound snapshot scanner. - Strip credential fields and contextual secret assignments in memory. - Install the sanitized config with exclusive, no-follow creation at mode `0600`. - Verify the installed inode, link count, size, and digest before accepting it. - Reject unsafe config path segments that could modify object prototypes. - Pin the plugin contextual secret patterns to the canonical CLI patterns with a parity test. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [x] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [x] Tests added or updated for changed behavior - [ ] Existing tests cover changed behavior — justification: - [ ] Tests not applicable — justification: - [x] Docs updated for user-facing behavior changes - [ ] Docs not applicable — justification: - [x] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [x] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: Independent nine-category security review passed for commit SHA `662d4ad020369dfcad3a21da225090cce507b111` against base SHA `d6ac4027b75b15b8acd1456984acbbfb623cb231`. No findings in secrets and credentials; input validation and data sanitization; authentication and authorization; dependencies and third-party libraries; error handling and logging; cryptography and data protection; configuration and security headers; security testing; or system security. The three addressed review threads are resolved. - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Documentation Writer Review - [x] Documentation writer subagent reviewed the completed changes - Result: `docs-updated` - Evidence: Independent review passed for commit SHA `662d4ad020369dfcad3a21da225090cce507b111` against base SHA `d6ac4027b75b15b8acd1456984acbbfb623cb231`. Reviewed `docs/reference/commands.mdx`, `docs/reference/troubleshooting.mdx`, and changed explanatory text against the NemoClaw Writing Guide, Controlled Word List, documentation guidance, implementation, tests, and Deep Agents variant routing. Required checks `docs-review-receipt`, `cli-parity`, and `preview` pass for this commit. No blocking finding remains. - Agent: Codex Desktop <!-- docs-review-head-sha: 662d4ad --> <!-- docs-review-agents-blob-sha: 3dd7c24 --> ## DGX Station Hardware Evidence - [ ] Tested on DGX Station - Tested commit: - Station profile/scenario: - Result: - Supporting evidence: ## 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 validate:pr` passed after refreshing `origin/main` when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set — Required check `checks` concluded `SUCCESS` for commit SHA `662d4ad020369dfcad3a21da225090cce507b111`. - [ ] Applicable broad gate passed — Required check `E2E / PR Gate` is running for commit SHA `662d4ad020369dfcad3a21da225090cce507b111`. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [x] `npm run docs` builds without warnings (documentation changes only) — Required documentation checks pass for commit SHA `662d4ad020369dfcad3a21da225090cce507b111`. - [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) --- Signed-off-by: Carlos Villela <cvillela@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added fail-closed protection for Deep Agents configuration-lock failures. * Improved automatic recovery by preserving agent and configuration details. * Added secure snapshot-based configuration recovery and verification. * Expanded detection of embedded credential patterns. * **Bug Fixes** * Prevented unsafe repairs, symlink swaps, hard-link mutations, and incomplete rollbacks. * Improved handling of invalid or empty configuration files. * **Documentation** * Added guidance for diagnosing, recovering from, and verifying critical configuration-lock failures. * **Tests** * Expanded coverage for security, recovery, locking, rollback, and timer behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Carlos Villela <cvillela@nvidia.com>
<!-- markdownlint-disable MD041 --> ## Summary Adds the canonical dated `v0.0.101` changelog entry that was missing when the release tag was cut. This post-release recovery records the shipped behavior on current `main` without changing or replacing the existing tag. ## Changes - Add `docs/changelog/2026-08-03.mdx` with the exact `## v0.0.101` heading, release summary, detailed behavior changes, support boundaries, and links to durable documentation. - [#7317](#7317) -> `docs/changelog/2026-08-03.mdx`: Records experimental OpenClaw Google Chat support and its restricted credential and webhook boundary. - [#7715](#7715) -> `docs/changelog/2026-08-03.mdx`: Records strict onboarding recovery state and authoritative resume identity. - [#7749](#7749) -> `docs/changelog/2026-08-03.mdx`: Records the provider-neutral policy seam and unchanged runtime support boundary. - [#7817](#7817) -> `docs/changelog/2026-08-03.mdx`: Records preserved Hermes home-channel assignments across rebuilds. - [#7820](#7820) -> `docs/changelog/2026-08-03.mdx`: Records the SSH-session status field correction. - [#7847](#7847) -> `docs/changelog/2026-08-03.mdx`: Records fail-closed credential filtering for migration and rebuild backups. - [#7870](#7870) -> `docs/changelog/2026-08-03.mdx`: Records sandbox-qualified in-sandbox host command hints. - [#7875](#7875) -> `docs/changelog/2026-08-03.mdx`: Records Microsoft Teams stop and start E2E coverage. - [#7885](#7885) -> `docs/changelog/2026-08-03.mdx`: Records Hermes managed gateway detection in status. - [#7889](#7889) -> `docs/changelog/2026-08-03.mdx`: Records policy-authenticated HTTPS Pin Runtime route revocation. - [#7891](#7891) -> `docs/changelog/2026-08-03.mdx`: Records default fallback for negative timeout and polling overrides. - [#7993](#7993) -> `docs/changelog/2026-08-03.mdx`: Records correct sibling detection during uninstall. - [#7995](#7995) -> `docs/changelog/2026-08-03.mdx`: Records absent configuration-hash handling before shields lock. - [#8001](#8001) -> `docs/changelog/2026-08-03.mdx`: Records the dormant atomic managed workload replacement foundation. - [#8029](#8029) -> `docs/changelog/2026-08-03.mdx`: Records repository terminology review in PR Review Advisor. - [#8031](#8031) -> `docs/changelog/2026-08-03.mdx`: Records provider-neutral managed snapshot authority. - [#8032](#8032) -> `docs/changelog/2026-08-03.mdx`: Records immutable managed clone handoff contracts. - [#8034](#8034) -> `docs/changelog/2026-08-03.mdx`: Records the dormant provider-owned clone transaction surface. - [#8035](#8035) -> `docs/changelog/2026-08-03.mdx`: Records the dormant Hermes managed clone broker boundary. - [#8036](#8036) -> `docs/changelog/2026-08-03.mdx`: Records the dormant transactional managed bootstrap boundary. - [#8037](#8037) -> `docs/changelog/2026-08-03.mdx`: Records dormant Docker bootstrap primitives and the unchanged provider support boundary. - [#8070](#8070) -> `docs/changelog/2026-08-03.mdx`: Records consolidated sandbox resource-limit E2E coverage. - [#8071](#8071) -> `docs/changelog/2026-08-03.mdx`: Records escaped and bounded CLI validation diagnostics. - [#8081](#8081) -> `docs/changelog/2026-08-03.mdx`: Records bounded linear snapshot Base64 validation. - [#8085](#8085) -> `docs/changelog/2026-08-03.mdx`: Records commit-bound workflow approval for eligible same-repository maintainers. - [#8088](#8088) -> `docs/changelog/2026-08-03.mdx`: Records Hermes managed-policy E2E selection. - [#8090](#8090) -> `docs/changelog/2026-08-03.mdx`: Records pinned CI search-tool provisioning. - [#8106](#8106) -> `docs/changelog/2026-08-03.mdx`: Records fallback from failed managed OpenShell gateway startup. - [#8107](#8107) -> `docs/changelog/2026-08-03.mdx`: Records Hermes adapter lifecycle E2E selection. - [#8128](#8128) -> `docs/changelog/2026-08-03.mdx`: Records the dormant transactional Docker bootstrap adapter and rollback authority. - [#8140](#8140) -> `docs/changelog/2026-08-03.mdx`: Records Slack conflict scope across independent OpenShell gateways. - [#8147](#8147) -> `docs/changelog/2026-08-03.mdx`: Records completion of durable v0.0.100 documentation audit follow-ups. ## 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 - [ ] Existing tests cover changed behavior — justification: - [x] Tests not applicable — justification: This documentation-only recovery does not change executable behavior. - [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: ## Documentation Writer Review - [x] Documentation writer subagent reviewed the completed changes - Result: `docs-updated` - Evidence: Independently reviewed `docs/changelog/2026-08-03.mdx` at commit `0bebe1f568e3dc85cf410aac1dfb8f8830070b85`. Its blob is `82887920f9720eafd75db6b2271c35f7477edb9b`. The entry follows the writing guide, controlled terminology, changelog structure, MDX SPDX format, literal CLI-name rule, and root-absolute route requirements. It accurately records the `v0.0.100...v0.0.101` release range, Announcement #8162, accepted scope boundaries, and shipped security behavior. There are no code samples. Focused changelog tests and the documentation build pass for this commit. - Agent: Codex Desktop independent documentation writer <!-- docs-review-head-sha: 0bebe1f --> <!-- docs-review-agents-blob-sha: 3dd7c24 --> ## Security Review - Result: `PASS` - Reviewed commit: `0bebe1f568e3dc85cf410aac1dfb8f8830070b85` - Base commit: `643a4ab8b5f583d8555192a37927268b26022c51` - Findings: None. - Secrets and credentials: `PASS`. No credential values or secret files are present. - Input validation and data sanitization: `PASS`. No executable input path changes. - Authentication and authorization: `PASS`. No identity or permission logic changes. - Dependencies and third-party libraries: `PASS`. No dependency changes. - Error handling and logging: `PASS`. No runtime path changes; diagnostic-security claims are precise. - Cryptography and data protection: `PASS`. No implementation changes. - Configuration and security controls: `PASS`. No configuration, container, port, or HTTP changes. - Security testing: `PASS`. No coverage is removed; the entry records shipped test and security behavior. - System security: `PASS`. No runtime control changes; dormant and non-activation boundaries are explicit. - Agent: Codex Desktop independent security reviewer ## Verification - [ ] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub — verification is pending after commit `0bebe1f568e3dc85cf410aac1dfb8f8830070b85` is pushed. - [ ] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run validate:pr` passed after refreshing `origin/main` when hooks were skipped or unavailable — commit hooks passed; pre-push is pending. - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — tests are not applicable to this documentation-only recovery. - [x] Applicable broad gate passed — not applicable to this documentation-only recovery. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, credentials, or private keys are added by this diff. - [ ] `npm run docs` builds without warnings (doc changes only) — GitHub documentation checks are pending. - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) — independent documentation review passed. - [x] 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. GitHub CI is authoritative. Focused changelog tests and `npm run docs` passed after the merge refresh. --- Signed-off-by: Apurv Kumaria <akumaria@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added experimental Google Chat support. * Improved runtime and session status visibility. * Added onboarding recovery and persistence safeguards. * Added snapshot validation and dormant managed-workload support. * **Bug Fixes** * Improved backup sanitization, route handling, and gateway reliability. * **Documentation** * Added the v0.0.101 changelog and related updates. * **Tests** * Expanded end-to-end coverage and strengthened trusted CI validation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Apurv Kumaria <akumaria@nvidia.com> Signed-off-by: Carlos Villela <cvillela@nvidia.com> Co-authored-by: Carlos Villela <cvillela@nvidia.com> Co-authored-by: Senthil Ravichandran <senthilr@nvidia.com>
Summary
shields upfailed on a Deep Agents sandbox because the lock and the verifier both treat<config dir>/.config-hashas a protected file, while the Deep Agents image never writes one. The lock now writes an absent hash record before it applies the locked permissions, so agents whose image ships no record reach the same locked posture as OpenClaw and Hermes.Related Issue
Fixes #7977
Changes
src/lib/shields/seal.tsaddsCONFIG_HASH_REPAIR_NOFOLLOW_SCRIPTandbuildConfigHashRepairCommand. The helper opens the config directory withO_NOFOLLOWandO_DIRECTORY, inspects the record name withlstat, and creates it withO_CREAT | O_EXCLat mode0444and thesha256sumrecord shape. A non-regular file at the record name, a symlinked config file, and a config path outside the config directory each fail the lock instead of redirecting a privileged write. The helper lives here because this module already owns the seal's on-disk contract, and a separate module would raise thesrc/lib/shields/index.tsfan-out past itsci/source-architecture-budget.jsonlimit.src/lib/shields/index.tscalls the helper from the lock path used by agents other than OpenClaw and Hermes, which run their own root-only config guards. Those guards already synthesize an absent record; this path had no equivalent step, so the followingchmod 444andchown root:rootfailed and the verifier reportedConfig not locked: … .config-hash stat failed.src/lib/shields/seal.test.tsruns the helper against real directories for the absent, stale, planted-symlink, planted-directory, symlinked-config, and outside-the-directory cases.src/lib/shields/policy-transition.test.tscovers the lock wiring for a Deep Agents target: the record is repaired before the protected files are locked, and a repair failure leaves the config unlocked.Type of Change
Quality Gates
docs/reference/commands.mdxalready documentsshields upas an agent-independent command with no Deep Agents exception. The command now behaves as documented, so no documented contract changes.0146c7b450b9650e0fa540e10ba6638f951cf029; exact diff fingerprint7b2bcd4a56f8ea1ee0d49412af4cc84bc8499d92ca5bcf65754ca78383471868. The current-main sync leaves the feature diff unchanged.Documentation Writer Review
no-docs-neededDGX Station Hardware Evidence
Verification
Signed-off-by:line and every commit appears asVerifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed, ornpm run validate:prpassed after refreshingorigin/mainwhen hooks were skipped or unavailable0146c7b45, focused repair/transition/verifier coverage passed 50/50 and the complete CLI shields suite passed 272/272;npm run build:cli,npm run validate:pr, and normal pre-push CLI/type/version gates passed.npm 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