Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1f748de
chore(policy): checkpoint Shields MCP reconciliation
jyaunches Jul 31, 2026
467259e
fix(shields): preserve managed MCP policies
jyaunches Jul 31, 2026
8c7c0a2
fix(snapshot): honor Shields deadline gate
jyaunches Jul 31, 2026
dd0a63e
test(state): isolate non-live fixture state
jyaunches Jul 31, 2026
7581fe0
test(shields): linearize regression fixtures
jyaunches Jul 31, 2026
b820678
fix(shields): harden recovery authority
jyaunches Jul 31, 2026
6a182f2
docs(shields): clarify recovery timing
jyaunches Jul 31, 2026
d61bcbc
test(shields): assert retry revocation
jyaunches Jul 31, 2026
4fa5cbd
fix(shields): close lifecycle recovery gaps
jyaunches Jul 31, 2026
dc6de57
test(shields): linearize recovery assertions
jyaunches Jul 31, 2026
719023f
docs(shields): clarify containment recovery
jyaunches Jul 31, 2026
4f124e6
test(shields): allow coverage boundary runtime
jyaunches Jul 31, 2026
948ed64
test(state): attribute lifecycle lock coverage
jyaunches Jul 31, 2026
c650528
test(state): cover lifecycle lock acquisition
jyaunches Jul 31, 2026
db0494e
test(state): clarify deadline recovery assertion
jyaunches Jul 31, 2026
f31b034
test(shields): avoid marker check-use race
jyaunches Jul 31, 2026
21d8786
chore(ci): retry trusted e2e gate
jyaunches Jul 31, 2026
f65c804
fix(shields): clear stale restore warnings
jyaunches Jul 31, 2026
f731be0
fix(shields): timestamp restore retries accurately
jyaunches Jul 31, 2026
27d1056
fix(shields): keep failed containment closed
jyaunches Jul 31, 2026
eb8e4c0
test(shields): linearize containment fixtures
jyaunches Jul 31, 2026
594934e
Merge branch 'main' into codex/fix-shields-mcp-policy
jyaunches Aug 1, 2026
46e43e5
test(e2e): stay within MCP bridge size budget
jyaunches Aug 1, 2026
7d0aedf
merge: resolve conflicts with main
github-actions[bot] Aug 2, 2026
5f08381
merge: update PR 7980 with main
jyaunches Aug 2, 2026
7d3e33e
docs: refresh documentation review receipt
jyaunches Aug 2, 2026
1413419
merge: resolve conflicts with main
github-actions[bot] Aug 2, 2026
02eb8bd
Merge remote-tracking branch 'origin/main' into codex/fix-shields-mcp…
jyaunches Aug 3, 2026
93c32f5
chore(ci): ratchet runner fan-in budget
jyaunches Aug 3, 2026
0f09878
fix(backup): preserve current mutation lock scope
jyaunches Aug 3, 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
8 changes: 8 additions & 0 deletions ci/env-var-doc-allowlist.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@
"name": "NEMOCLAW_TEST_NO_SLEEP",
"reason": "Test sentinel that bypasses real-time sleep() calls in onboard inference probes. Set to '1' only by Vitest tests; never user-set."
},
{
"name": "NEMOCLAW_TEST_BASE_HOME",
"reason": "Internal Vitest-only baseline used with NEMOCLAW_TEST_STATE_DIR so tests that explicitly replace HOME retain their fixture paths. Never user-set in production."
},
{
"name": "NEMOCLAW_TEST_STATE_DIR",
"reason": "Internal Vitest-only state root that keeps lifecycle locks and Shields artifacts out of the caller's real NemoClaw state. The production resolver honors it only while Vitest is active."
},
{
"name": "NEMOCLAW_TELEGRAM_STARTUP_GRACE_MS",
"reason": "Internal Vitest-only override that shortens the Telegram diagnostics startup-grace timer. Production uses the built-in default."
Expand Down
2 changes: 1 addition & 1 deletion ci/source-architecture-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"src/lib/inference/web-search.ts": 21,
"src/lib/messaging/channels/index.ts": 26,
"src/lib/onboard/gateway-binding.ts": 48,
"src/lib/runner.ts": 89,
"src/lib/runner.ts": 88,
"src/lib/security/redact.ts": 51,
"src/lib/state/onboard-session.ts": 36,
"src/lib/state/registry.ts": 101,
Expand Down
5 changes: 5 additions & 0 deletions ci/source-shape-test-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,11 @@
"test": "wires cleanup into root and standalone plugin test runs",
"category": "compatibility"
},
{
"file": "test/vitest-temp-root.test.ts",
"test": "isolates stateful non-live projects without redirecting live E2E state",
"category": "security"
},
{
"file": "test/vitest-watch-triggers.test.ts",
"test": "registers the focused mappings at the root configuration boundary (#6692)",
Expand Down
9 changes: 6 additions & 3 deletions docs/manage-sandboxes/backup-restore.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,10 @@ NemoClaw computes versions (`v1`, `v2`, through `vN`) from timestamp order, so `
`snapshot create` requires shields to be down.
Snapshot creation and restore share the per-sandbox transition lock with the shields auto-restore timer.

If a timed shields-down window expires during snapshot work, auto-restore can interrupt the operation and restore lockdown instead of allowing state or policy changes to continue past the deadline.
Retry the snapshot in a new shields-down window if the deadline interrupts it.
If a timed shields-down window expires during snapshot work, auto-restore closes the per-sandbox lifecycle deadline gate and waits for the recorded live owner to finish.
New mutations remain blocked until the snapshot owner releases its exact lock generation and auto-restore restores lockdown.
If the owner exits before releasing that generation, NemoClaw leaves Shields down, records permanent containment, and reports operator recovery guidance.
Stop all NemoClaw processes for that sandbox, then follow the exact lock-generation guidance before retrying the snapshot.

Tag a snapshot with a human-readable label:

Expand Down Expand Up @@ -210,7 +212,8 @@ If a sandbox is not running and its container cannot be started this way, start
When Shields are UP for an eligible sandbox, `backup-all` opens a 30-minute shields-down window before it creates that sandbox's snapshot.
A sandbox that starts with Shields down remains down.
An unlock failure marks that sandbox as failed, and `backup-all` continues with the next sandbox.
Because the timer does not defer to the backup process, it can restore lockdown when the 30-minute deadline expires.
`backup-all` does not hold the lifecycle mutation lock while it copies sandbox state.
The timer can restore lockdown if the 30-minute deadline expires during that copy.
NemoClaw always attempts to restore Shields lockdown before it processes the next sandbox, including when the backup fails.
If lockdown cannot be restored, `backup-all` stops and does not process the remaining sandboxes.
Correct the reported issue, run the printed `$$nemoclaw <name> shields up` command, and rerun `$$nemoclaw backup-all`.
Expand Down
27 changes: 25 additions & 2 deletions docs/manage-sandboxes/runtime-controls.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,33 @@ Run `$$nemoclaw <name> shields down` before the change, then restore lockdown wi
NemoClaw serializes host-side config and inference writes, snapshot mutation, sandbox destruction, and shields transitions for each sandbox.
When `shields down --timeout` is active, each mutation binds to that exact timer generation so a replaced or expired timer cannot race a later command or a new sandbox that reuses the same name.

If the timeout expires while a mutation is still changing sandbox state, auto-restore can stop that exact process tree, reclaim the transition, and restore the restrictive policy and config posture.
If the timeout expires while a mutation is still changing sandbox state, auto-restore closes the per-sandbox lifecycle deadline gate.
The gate blocks new mutations and lets the recorded live owner finish without sending it a signal.
After that owner releases its exact lock generation, auto-restore restores the restrictive policy and config posture.
The ownership check includes both the process ID and process start identity so PID reuse does not grant control over an unrelated process.

Retry a command that the auto-restore deadline interrupts after you open a new shields-down window.
Before a manual Shields transition replaces a policy, NemoClaw requires exact Model Context Protocol (MCP) agreement among the sandbox registry, generated-policy record, and live gateway policy.
`shields down` carries the proven managed MCP policy entries into the relaxed policy.
Restoration removes snapshot-time managed MCP entries before it overlays current exact entries.
If exact agreement is absent, a manual Shields transition refuses the replacement policy.
At an expired deadline, auto-restore omits unproven managed MCP policy entries, restores lockdown, and records the omission count in its audit entry.
An MCP server removed during the shields-down window stays removed.
A surviving server keeps its recorded endpoint and address pins while its policy ownership remains exact.

When an interactive command takes over an expired timer, NemoClaw makes up to 7 restoration attempts over an additional 30-second completion-grace window while the deadline gate stays closed.
If the command cannot complete and commit restoration within that window, NemoClaw converts the deadline gate into durable containment.
When the containment write succeeds, NemoClaw commits the record before it returns the failure, so new mutations remain blocked until operator resolution.
If NemoClaw cannot commit that record after its bounded write retry budget, the command returns an operator-resolution error with the last state-directory write failure and keeps every exact deadline or main lifecycle lock generation that it owns.
Correct the reported state-directory write failure, then retry the command once.
After the retained owner exits, the retry records durable containment for the stale timer-bound generation and returns exact-generation operator-resolution instructions.
Complete those instructions before running another sandbox mutation.
For an ordinary lifecycle lock, NemoClaw reclaims only an exact, structurally valid stale generation for your sandbox.
NemoClaw records durable containment for a stale main owner bound to a Shields timer instead of reclaiming it as an ordinary stale generation.
Before removal, NemoClaw rechecks the generation under an exclusive stale-lock reaper gate.
If a deadline owner expires or a reaper is interrupted, NemoClaw records durable containment because it cannot rule out surviving descendants.
NemoClaw fails closed for corrupt, non-regular, wrong-sandbox, ambiguous, foreign, and live identities, and it leaves those generations in place.
Stop all NemoClaw processes for that sandbox, then follow the exact lock-generation recovery guidance in the reported error or audit entry.
Do not remove a recorded lifecycle lock while any NemoClaw process for that sandbox is running.

## Related Topics

Expand Down
32 changes: 28 additions & 4 deletions docs/reference/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1161,8 +1161,30 @@ If `shields up` reports that the config remains unlocked or drifted, confirm tha
If the retry still fails, rebuild a known-good baseline with `$$nemoclaw <name> rebuild --yes`.

Host-side config and inference writes, snapshot mutation, sandbox destruction, and shields transitions serialize per sandbox.
When a timed shields-down window reaches its deadline, auto-restore can interrupt the exact process tree holding that transition and restore lockdown.
Retry an interrupted command in a new shields-down window.
When a timed shields-down window reaches its deadline, auto-restore closes the per-sandbox lifecycle deadline gate.
The gate blocks new mutations and waits for the recorded live owner to release its exact lock generation before auto-restore restores lockdown.
NemoClaw does not signal that process because portable process inspection cannot prove that all descendants are contained.
When an interactive command takes over an expired timer, NemoClaw makes up to 7 restoration attempts over an additional 30-second completion-grace window while the deadline gate stays closed.
If the command cannot complete and commit restoration within that window, NemoClaw converts the deadline gate into durable containment.
When the containment write succeeds, NemoClaw commits the record before it returns the failure, so new mutations remain blocked until operator resolution.
If NemoClaw cannot commit that record after its bounded write retry budget, the command returns an operator-resolution error with the last state-directory write failure and keeps every exact deadline or main lifecycle lock generation that it owns.
Correct the reported state-directory write failure, then retry the command once.
After the retained owner exits, the retry records durable containment for the stale timer-bound generation and returns exact-generation operator-resolution instructions.
Complete those instructions before running another sandbox mutation.
For an ordinary lifecycle lock, NemoClaw reclaims only an exact, structurally valid stale generation for the same sandbox.
NemoClaw records durable containment for a stale main owner bound to a Shields timer instead of reclaiming it as an ordinary stale generation.
It rechecks the generation under an exclusive stale-lock reaper gate before removal.
An expired deadline owner or interrupted reaper still records durable containment because surviving descendants cannot be ruled out.
NemoClaw fails closed for corrupt, non-regular, wrong-sandbox, ambiguous, foreign, and live identities instead of deleting those generations.
Stop all NemoClaw processes for that sandbox, then follow the exact lock-generation guidance before retrying.

Before a manual Shields transition replaces a policy, NemoClaw requires exact agreement among the sandbox registry, generated-policy record, and live gateway policy.
`shields down` carries the proven managed MCP policy entries into the relaxed policy.
Restoration removes snapshot-time managed MCP entries before it overlays current exact entries.
If exact agreement is absent, a manual Shields transition refuses the replacement policy.
At an expired deadline, auto-restore omits unproven managed MCP policy entries, restores lockdown, and records the omission count in its audit entry.
An MCP server removed during the shields-down window stays removed.
A surviving server keeps its recorded endpoint and address pins while its policy ownership remains exact.

<AgentOnly variant="openclaw,hermes">

Expand Down Expand Up @@ -2921,7 +2943,8 @@ A skipped sandbox's uncommitted state is not included in its last successful bac
Create a timestamped snapshot of sandbox state.
Snapshots are stored in `~/.nemoclaw/rebuild-backups/<name>/`.
The command requires shields to be down and keeps the shields check and backup under one per-sandbox transition.
An expired auto-restore timer can interrupt a long-running backup and restore lockdown.
When the timer expires during a long-running backup, the lifecycle deadline gate blocks new mutations and waits for the recorded backup owner to release its exact lock generation before restoring lockdown.
If the owner exits first, NemoClaw leaves Shields down and reports the permanent-containment recovery guidance.
When the sandbox has active baseline exclusions, successful output lists their keys and repeats that excluded egress leaves dependent agent features unsupported for that sandbox.

```bash
Expand Down Expand Up @@ -2955,7 +2978,8 @@ If no selector is provided, the latest snapshot is used.
Restore removes files added after the snapshot only from state directories selected for cleanup.
It preserves directories that exist only in the target manifest or whose backup failed.
The state replacement, mutable-config permission repair, and policy reconciliation run under the same per-sandbox transition.
An expired auto-restore timer can interrupt that work and restore lockdown.
When the timer expires during that work, the lifecycle deadline gate blocks new mutations and waits for the recorded restore owner to release its exact lock generation before restoring lockdown.
If the owner exits first, NemoClaw leaves Shields down and reports the permanent-containment recovery guidance.

The selector accepts any of:

Expand Down
2 changes: 1 addition & 1 deletion scripts/checks/openshell-policy-mutation-read.mts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const MUTATION_READS: readonly AuditedMutationRead[] = [
},
{
relativePath: "src/lib/shields/index.ts",
expectedReadCalls: 1,
expectedReadCalls: 3,
baseCommand: "runCapture(buildPolicyGetCommand(sandboxName))",
unsafeBaseCommand: "runCapture(buildPolicyGetCommand(sandboxName), {",
fullCommand: "runCapture(buildPolicyGetFullCommand(sandboxName))",
Expand Down
Loading
Loading