Skip to content
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
7a3ed92
feat(runtime): implement durable Docker state mutation
ericksoa Aug 9, 2026
01ec592
Merge current main into feat/docker-state-mutation-consumer
ericksoa Aug 9, 2026
b0c481d
merge: resolve conflicts with main
github-actions[bot] Aug 10, 2026
b7ed19f
fix(runtime): stabilize Docker mutation authority
ericksoa Aug 10, 2026
2e42b6e
Merge current main into feat/docker-state-mutation-consumer
ericksoa Aug 10, 2026
3d85534
fix(inference): refresh Docker publish authority
ericksoa Aug 10, 2026
70beb5c
chore: merge concurrent PR branch update
ericksoa Aug 10, 2026
9625d4e
fix(test): stabilize release cleanup and runtime stubs
ericksoa Aug 10, 2026
aa2b03a
merge: resolve conflicts with main
github-actions[bot] Aug 10, 2026
4380c70
chore: merge current main into B4-C2 stabilization
ericksoa Aug 10, 2026
b419c89
chore: merge concurrent PR branch update
ericksoa Aug 10, 2026
6a88a83
chore: merge latest main into B4-C2 stabilization
ericksoa Aug 10, 2026
a73c121
fix(installer): preserve runtime authority through shim
ericksoa Aug 10, 2026
2335b16
test(shields): tolerate contended compatibility setup
ericksoa Aug 10, 2026
3c4ba5a
chore: merge current main into B4-C2 stabilization
ericksoa Aug 10, 2026
4f01f04
merge: resolve conflicts with main
github-actions[bot] Aug 10, 2026
dbf7919
chore: merge current main into B4-C2 stabilization
ericksoa Aug 10, 2026
0fb2152
merge: resolve conflicts with main
github-actions[bot] Aug 10, 2026
5e5e2fa
chore: reconcile concurrent PR branch update
ericksoa Aug 10, 2026
64a3a53
chore: merge current main into B4-C2 stabilization
ericksoa Aug 10, 2026
e9d7640
merge: resolve conflicts with main
github-actions[bot] Aug 10, 2026
10408b3
test(shields): tolerate contended provider resolution
ericksoa Aug 10, 2026
acd8817
chore: reconcile concurrent PR branch update
ericksoa Aug 10, 2026
997b9f2
merge: resolve conflicts with main
github-actions[bot] Aug 10, 2026
831d619
chore: merge current main into B4-C2 stabilization
ericksoa Aug 10, 2026
03bd912
chore: reconcile concurrent PR branch update
ericksoa Aug 10, 2026
85c28d7
chore: merge current main into docker state mutation slice
ericksoa Aug 10, 2026
d4271fa
chore: merge current main into docker state mutation slice
ericksoa Aug 10, 2026
ad7a8cc
chore: merge current main into docker state mutation slice
ericksoa Aug 10, 2026
0ed6c0e
fix(shields): stabilize mutation recovery checks
ericksoa Aug 10, 2026
df73f62
Merge remote-tracking branch 'origin/main' into feat/docker-state-mut…
ericksoa Aug 10, 2026
a9bcb9d
fix(runtime): defer lifecycle process identity probe
ericksoa Aug 10, 2026
6f72a02
fix(runtime): close state mutation review gaps
ericksoa Aug 10, 2026
c8ddedf
Merge remote-tracking branch 'origin/main' into feat/docker-state-mut…
ericksoa Aug 10, 2026
c9f7bc3
fix(runtime): make release recovery type honest
ericksoa Aug 10, 2026
92e7c96
test: clean Hermes prelude fixtures on failure
ericksoa Aug 10, 2026
773768d
fix(e2e): align protected supervisor argv
ericksoa Aug 11, 2026
2cd934e
test(e2e): exercise protected bootstrap boundary
ericksoa Aug 11, 2026
91de332
merge: resolve conflicts with main
github-actions[bot] Aug 11, 2026
9f59d44
Merge branch 'main' into feat/docker-state-mutation-consumer
ericksoa Aug 11, 2026
0991dd0
test(ci): align base-image path expectation
ericksoa Aug 11, 2026
0d75cbc
Merge branch 'main' into feat/docker-state-mutation-consumer
ericksoa Aug 11, 2026
cc718f0
test(ci): follow base-image contingency revert
ericksoa Aug 11, 2026
bc3ae09
fix(installer): sanitize admission diagnostics
ericksoa Aug 11, 2026
647ec59
Merge branch 'main' into feat/docker-state-mutation-consumer
ericksoa Aug 11, 2026
601d143
merge: resolve conflicts with main
github-actions[bot] Aug 11, 2026
cab140d
Merge branch 'main' into feat/docker-state-mutation-consumer
cv Aug 11, 2026
b0bfb67
Merge branch 'main' into feat/docker-state-mutation-consumer
cv Aug 11, 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
23 changes: 19 additions & 4 deletions agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ COPY agents/hermes/mcp-config-transaction.py /usr/local/lib/nemoclaw/hermes-mcp-
COPY agents/hermes/cron-restore-control.py /usr/local/lib/nemoclaw/hermes-cron-restore-control.py
COPY src/lib/actions/sandbox/openshell-child-visible-credentials.v0.0.101.json /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.101.json
COPY scripts/state-dir-guard.py /usr/local/lib/nemoclaw/state-dir-guard.py
COPY scripts/runtime-state-mutation-control.py /usr/local/lib/nemoclaw/runtime-state-mutation-control.py
COPY scripts/runtime-state-mutation-startup-gate.py /usr/local/lib/nemoclaw/runtime-state-mutation-startup-gate.py
COPY scripts/runtime_state_mutation_hermes_publisher.py /usr/local/lib/nemoclaw/runtime_state_mutation_hermes_publisher.py
COPY agents/hermes/state-lock-plan.json /usr/local/share/nemoclaw/state-lock-plan.json
COPY agents/hermes/runtime-state-mutation-publisher-v1.json /usr/local/share/nemoclaw/runtime-state-mutation-publisher-v1.json
COPY nemoclaw-blueprint/scripts/*.js /usr/local/lib/nemoclaw/preloads/

FROM scratch AS hermes-wrapper-payload
Expand Down Expand Up @@ -400,7 +404,10 @@ RUN managed_runtime_assertion_failed() { \
&& test -f /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh \
&& test ! -L /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh \
&& test "$(stat -c '%u:%g:%a' /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh)" = '0:0:444' \
&& install -d -o root -g root -m 0755 /run/nemoclaw
&& install -d -o root -g root -m 0755 /run/nemoclaw /var/lib/nemoclaw \
&& install -d -o root -g root -m 0711 \
/run/nemoclaw/runtime-state-mutation-startup \
/var/lib/nemoclaw/runtime-state-mutation

# Ensure sandbox user can read blueprint files copied as root
RUN chmod -R a+rX /opt/nemoclaw-blueprint/
Expand All @@ -415,12 +422,14 @@ RUN chmod -R a+rX /opt/nemoclaw-blueprint/
# minimum supported Hermes sandbox base tag guarantees those artifacts and
# test/sandbox-rlimit-hooks.test.ts covers that base.
RUN chmod 755 /usr/local/bin/nemoclaw-start /usr/local/bin/nemoclaw-managed-startup-hold /usr/local/bin/nemoclaw-managed-bootstrap /usr/local/lib/nemoclaw/sandbox-init.sh /usr/local/lib/nemoclaw/validate-hermes-env-secret-boundary.py /usr/local/lib/nemoclaw/patch-hermes-session-list-preview.py /usr/local/lib/nemoclaw/patch-hermes-sqlite-temp-store.py /usr/local/lib/nemoclaw/patch-hermes-discord-recovery-permissions.py /usr/local/lib/nemoclaw/patch-hermes-profile-policy-defaults.py /usr/local/lib/nemoclaw/seed-hermes-dashboard-config.py /usr/local/lib/nemoclaw/hermes-runtime-config-guard.py /usr/local/lib/nemoclaw/finalize-tirith-marker.py /usr/local/lib/nemoclaw/hermes-mcp-config-transaction.py \
&& chown root:root /usr/local/bin/nemoclaw-gateway-control /usr/local/lib/nemoclaw/gateway-supervisor.sh /usr/local/lib/nemoclaw/state-dir-guard.py /usr/local/share/nemoclaw/state-lock-plan.json /usr/local/lib/nemoclaw/managed-gateway-control.py /usr/local/lib/nemoclaw/build-hermes-mcp-digest.py /usr/local/lib/nemoclaw/hermes-cron-restore-control.py /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.101.json \
&& chown root:root /usr/local/bin/nemoclaw-gateway-control /usr/local/lib/nemoclaw/gateway-supervisor.sh /usr/local/lib/nemoclaw/state-dir-guard.py /usr/local/lib/nemoclaw/runtime-state-mutation-control.py /usr/local/lib/nemoclaw/runtime-state-mutation-startup-gate.py /usr/local/lib/nemoclaw/runtime_state_mutation_hermes_publisher.py /usr/local/share/nemoclaw/state-lock-plan.json /usr/local/share/nemoclaw/runtime-state-mutation-publisher-v1.json /usr/local/lib/nemoclaw/managed-gateway-control.py /usr/local/lib/nemoclaw/build-hermes-mcp-digest.py /usr/local/lib/nemoclaw/hermes-cron-restore-control.py /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.101.json \
&& chmod 700 /usr/local/bin/nemoclaw-gateway-control /usr/local/lib/nemoclaw/hermes-cron-restore-control.py \
&& chmod 500 /usr/local/lib/nemoclaw/state-dir-guard.py /usr/local/lib/nemoclaw/managed-gateway-control.py \
&& chmod 444 /usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh /usr/local/share/nemoclaw/state-lock-plan.json /usr/local/lib/nemoclaw/gateway-supervisor.sh /usr/local/lib/nemoclaw/build-hermes-mcp-digest.py /usr/local/lib/nemoclaw/managed_policy.py \
&& chmod 500 /usr/local/lib/nemoclaw/state-dir-guard.py /usr/local/lib/nemoclaw/runtime-state-mutation-control.py /usr/local/lib/nemoclaw/runtime_state_mutation_hermes_publisher.py /usr/local/lib/nemoclaw/managed-gateway-control.py \
&& chmod 555 /usr/local/lib/nemoclaw/runtime-state-mutation-startup-gate.py \
&& chmod 444 /usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh /usr/local/share/nemoclaw/state-lock-plan.json /usr/local/share/nemoclaw/runtime-state-mutation-publisher-v1.json /usr/local/lib/nemoclaw/gateway-supervisor.sh /usr/local/lib/nemoclaw/build-hermes-mcp-digest.py /usr/local/lib/nemoclaw/managed_policy.py \
&& chmod 444 /usr/local/lib/nemoclaw/patch-hermes-langfuse-credentials.mts \
&& chmod 444 /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.101.json \
&& /opt/hermes/.venv/bin/python3 -I -c 'import runpy, yaml; assert yaml.safe_load("ready: true")["ready"] is True; runpy.run_path("/usr/local/lib/nemoclaw/runtime-state-mutation-control.py", run_name="nemoclaw_runtime_state_mutation_control_probe"); runpy.run_path("/usr/local/lib/nemoclaw/runtime_state_mutation_hermes_publisher.py", run_name="nemoclaw_runtime_state_mutation_publisher_probe"); runpy.run_path("/usr/local/lib/nemoclaw/runtime-state-mutation-startup-gate.py", run_name="nemoclaw_runtime_state_mutation_gate_probe"); runpy.run_path("/usr/local/lib/nemoclaw/hermes-runtime-config-guard.py", run_name="nemoclaw_runtime_config_guard_probe")' \
&& if [ -d /usr/local/lib/nemoclaw/preloads ]; then \
chown -R 0:0 /usr/local/lib/nemoclaw/preloads \
&& find /usr/local/lib/nemoclaw/preloads -type f -exec chmod 444 {} + \
Expand Down Expand Up @@ -1243,6 +1252,12 @@ RUN check_metadata() { \
&& check_metadata /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh 'root:root 444' \
&& check_metadata /usr/local/bin/nemoclaw-gateway-control 'root:root 700' \
&& check_metadata /usr/local/share/nemoclaw/state-lock-plan.json 'root:root 444' \
&& check_metadata /usr/local/lib/nemoclaw/runtime-state-mutation-control.py 'root:root 500' \
&& check_metadata /usr/local/lib/nemoclaw/runtime-state-mutation-startup-gate.py 'root:root 555' \
&& check_metadata /usr/local/lib/nemoclaw/runtime_state_mutation_hermes_publisher.py 'root:root 500' \
&& check_metadata /var/lib/nemoclaw/runtime-state-mutation 'root:root 711' \
&& check_metadata /run/nemoclaw/runtime-state-mutation-startup 'root:root 711' \
&& check_metadata /usr/local/share/nemoclaw/runtime-state-mutation-publisher-v1.json 'root:root 444' \
&& check_metadata /usr/local/lib/nemoclaw/hermes-cron-restore-control.py 'root:root 700' \
&& check_metadata /usr/local/lib/nemoclaw/preloads/sandbox-safety-net.js 'root:root 444' \
&& check_metadata /usr/local/lib/nemoclaw/hermes-wrapper.py 'root:root 755' \
Expand Down
1 change: 1 addition & 0 deletions agents/hermes/runtime-state-mutation-publisher-v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":1,"protocol":"nemoclaw-runtime-state-mutation-publisher-v1","agent":"hermes","providerId":"docker","stateRoot":"/sandbox/.hermes","planSchemaVersion":2,"entrypoint":"/usr/local/lib/nemoclaw/runtime_state_mutation_hermes_publisher.py"}
109 changes: 109 additions & 0 deletions agents/hermes/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,105 @@ set -euo pipefail

# SECURITY: Lock down PATH before resolving or sourcing root startup helpers.
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
NEMOCLAW_RUNTIME_STATE_MUTATION_RETRY_ARGV=("$@")

# The provider gate is a fixed root-owned file beneath a search-only directory
# outside /sandbox, so the sandbox identity cannot rename either the gate or
# its parent. Run the immutable validator as this shell's direct child before
# sourcing helpers or reading mutable state; its permit is bound to this exact
# process identity. Invalid or uninspectable gate state is a hold, never an
# availability-to-integrity downgrade.
readonly NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_PYTHON="/opt/hermes/.venv/bin/python3"
readonly NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_HELPER="/usr/local/lib/nemoclaw/runtime-state-mutation-startup-gate.py"
readonly NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_SETPRIV="/usr/bin/setpriv"

if [ ! -x "$NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_PYTHON" ] \
|| [ ! -f "$NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_HELPER" ] \
|| [ -L "$NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_HELPER" ] \
|| { [ "$EUID" -eq 0 ] && [ ! -x "$NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_SETPRIV" ]; }; then
printf '%s\n' '[SECURITY] Required runtime state mutation startup gate is unavailable.' >&2
exit 1
fi

nemoclaw_runtime_state_mutation_gate() {
local action="$1"
if [ "$EUID" -eq 0 ]; then
"$NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_SETPRIV" \
--reuid=sandbox --regid=sandbox --init-groups -- \
"$NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_PYTHON" -I \
"$NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_HELPER" "$action" >/dev/null
return
fi
"$NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_PYTHON" -I \
"$NEMOCLAW_RUNTIME_STATE_MUTATION_GATE_HELPER" "$action" >/dev/null
}

nemoclaw_runtime_state_mutation_retry_exec() {
local status
if nemoclaw_runtime_state_mutation_gate restart; then
status=0
else
status=$?
fi
if [ "$status" -eq 12 ]; then
exec /usr/local/bin/nemoclaw-start \
"${NEMOCLAW_RUNTIME_STATE_MUTATION_RETRY_ARGV[@]}"
fi
printf '%s\n' '[SECURITY] Runtime state mutation retry was not authenticated; holding startup.' >&2
kill -STOP "$$"
}
trap nemoclaw_runtime_state_mutation_retry_exec USR2

while :; do
if nemoclaw_runtime_state_mutation_gate admit; then
break
else
_nemoclaw_runtime_state_mutation_gate_status=$?
fi
case "$_nemoclaw_runtime_state_mutation_gate_status" in
10) break ;;
75)
printf '%s\n' '[SECURITY] Hermes startup held by an active runtime state mutation.' >&2
/bin/sleep 1 || true
;;
*)
printf '%s\n' '[SECURITY] Runtime state mutation startup gate failed.' >&2
exit 1
;;
esac
done
unset _nemoclaw_runtime_state_mutation_gate_status
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Publish a candidate only after the complete gateway topology is healthy.
# The shell then stops itself until the root controller has independently
# authenticated the candidate, frozen the exact process tree, and published a
# release receipt. Calling this with no active mutation is a cheap no-op.
nemoclaw_runtime_state_mutation_checkpoint() {
local status
if nemoclaw_runtime_state_mutation_gate checkpoint; then
return 0
else
status=$?
fi
if [ "$status" -ne 11 ]; then
printf '%s\n' '[SECURITY] Runtime state mutation startup checkpoint was refused; holding startup.' >&2
kill -STOP "$$"
return 1
fi
kill -STOP "$$"
if nemoclaw_runtime_state_mutation_gate resume; then
return 0
else
status=$?
fi
if [ "$status" -eq 12 ]; then
exec /usr/local/bin/nemoclaw-start \
"${NEMOCLAW_RUNTIME_STATE_MUTATION_RETRY_ARGV[@]}"
fi
printf '%s\n' '[SECURITY] Runtime state mutation release receipt was not authenticated; holding startup.' >&2
kill -STOP "$$"
return 1
}

# managed-entrypoint-env-wrapper begin
_NEMOCLAW_ENTRYPOINT_ENV_WRAPPER="/usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh"
Expand Down Expand Up @@ -127,6 +226,7 @@ exec > >(tee -a "$_START_LOG") 2> >(tee -a "$_START_LOG" >&2)
drop_capabilities /usr/local/bin/nemoclaw-start "$@"

NEMOCLAW_CMD=("$@")
NEMOCLAW_RUNTIME_STATE_MUTATION_RETRY_ARGV=("${NEMOCLAW_CMD[@]}")

_chat_ui_url_port() {
[ -n "${CHAT_UI_URL:-}" ] || return 1
Expand Down Expand Up @@ -2908,6 +3008,11 @@ handle_hermes_gateway_control_request() {
return 1
fi
refresh_hermes_supervised_child_pids
nemoclaw_runtime_state_mutation_checkpoint || {
stop_hermes_gateway_fail_closed
gateway_control_fail internal "$old_pid"
return 1
}
gateway_control_complete ok "$old_pid" "$GATEWAY_PID"
}

Expand Down Expand Up @@ -3136,6 +3241,7 @@ recover_hermes_gateway_current_user() {
return 1
fi
refresh_hermes_supervised_child_pids
nemoclaw_runtime_state_mutation_checkpoint || return 1
return 0
fi
echo "[gateway] Hermes auxiliary repair failed; retrying while the exact gateway remains healthy" >&2
Expand Down Expand Up @@ -3228,6 +3334,7 @@ bootstrap_hermes_gateway_current_user() {
return 1
fi
refresh_hermes_supervised_child_pids
nemoclaw_runtime_state_mutation_checkpoint || return 1
return 0
fi

Expand All @@ -3243,6 +3350,7 @@ bootstrap_hermes_gateway_current_user() {
sleep 2 || true
recover_hermes_gateway_current_user || return 1
refresh_hermes_supervised_child_pids
nemoclaw_runtime_state_mutation_checkpoint || return 1
}

# ── Main ─────────────────────────────────────────────────────────
Expand Down Expand Up @@ -3371,6 +3479,7 @@ if ! "$_HERMES_PYTHON" -I "$_HERMES_RUNTIME_CONFIG_GUARD" publish-startup-ready
echo "[gateway-control] failed to publish Hermes startup readiness" >&2
exit 1
fi
nemoclaw_runtime_state_mutation_checkpoint || exit 1
print_dashboard_urls

# PID 1 remains alive even when Hermes stops its gateway. Host recovery uses
Expand Down
10 changes: 8 additions & 2 deletions docs/inference/set-up-llama-cpp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ The default recipe serves NVIDIA Nemotron 3 Nano 30B-A3B through the OpenAI Chat
<Note>
This path is an experimental implementation.
It does not establish a supported agent, model, and runtime tuple until the protected qualification and activation gates pass.
The protected qualification runner requires Docker Engine `28.3.3` or newer and the trusted daemon's default protected NAT and firewall behavior; it re-queries the live daemon and consumes a fresh single-use authority immediately before each temporary loopback publication.
That requirement supersedes older Docker 27 qualification evidence.
Ordinary managed onboarding does not publish a Docker port: it retains its no-publication container contract and uses the host-owned private bridge, so this runner-specific version floor does not apply to onboarding.
</Note>

Before you start, confirm these prerequisites:
Expand Down Expand Up @@ -135,7 +138,7 @@ Onboarding performs these actions:
- Reuses a verified GGUF from the shared `~/.cache/huggingface/` cache, or acquires the exact file through the existing Hugging Face mechanism when it is absent or invalid.
- Verifies the GGUF size and SHA-256 digest before it starts the runtime.
- Runs the authenticated container as the current non-root user with one NVIDIA GPU and no CPU fallback.
- Starts a private host bridge on `127.0.0.1:8081` and connects the container to a Docker internal network without publishing a container port.
- Starts a host-owned private bridge on `127.0.0.1:8081` and connects the container to a Docker internal network without a Docker-published port.
- Registers `llama-cpp-local` and routes agent traffic through `https://inference.local/v1`.

The runtime cannot download a model and has no egress on its internal Docker network.
Expand All @@ -147,14 +150,17 @@ Selecting either runtime fails before model acquisition or runtime mutation and

## Verify the Managed Runtime

Run the managed runtime and route checks:
Run the managed runtime, route, and host-port checks:

```bash
$$nemoclaw my-assistant status
$$nemoclaw my-assistant doctor
docker inspect --format '{{json .HostConfig.PortBindings}}' nemoclaw-llama-cpp
```

Accept the result when `status` reports `Managed llama.cpp: running`, the inference route is `reachable`, and `doctor` exits with status `0`.
The Docker inspection output must be `{}`, confirming that the container has no Docker-published port.
The host-owned private bridge still listens on loopback.
These checks do not establish agent and model qualification.

`status` reports the recipe ID, model digest, image reference, endpoint, and lifecycle state without exposing the API key.
Expand Down
28 changes: 27 additions & 1 deletion docs/manage-sandboxes/runtime-controls.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,33 @@ NemoClaw binds the write to the SHA-256 digest of the matching read, temporarily
`shields up` also publishes fresh config, environment, and compatibility-hash inodes so a descriptor opened before lockdown cannot retain write authority.
Shields up keeps `/sandbox/.hermes/profiles/dashboard-home/` sandbox-owned at mode `0700` so the dashboard can update its isolated profile.
Other Hermes profiles remain read-only during lockdown.
The same root-only mutation lock stays held through Hermes config writes, the full `shields up` or `shields down` filesystem transition and verification, and lifecycle recovery that needs to seal those paths.
The root-only mutation lock stays held through every Hermes host-side config write.
On the sealed-plan and compatibility Shields paths, it also stays held through the full `shields up` or `shields down` filesystem transition and verification, and lifecycle recovery that needs to seal those paths.

Current NemoClaw-managed Hermes images on the Docker driver use the `provider-state-mutation-v2` runtime provider state mutation contract for Shields filesystem transitions.
NemoClaw selects this contract only when all these conditions apply:

- The registered sandbox is a managed Hermes image.
- The sandbox registry records its lifecycle generation.
- The image exposes the exact root-owned capability installed by NemoClaw.

An older managed image uses the sealed-plan transition only after NemoClaw proves that the capability is absent.
A custom image, including a legacy Dockerfile workflow, remains on its existing sealed-plan or compatibility contract and cannot opt in by adding a similarly named file.
An invalid capability that is present fails closed and requires a rebuild with a current managed image.

The Docker provider binds each transition to the registered lifecycle generation, exact container, qualified Docker engine authority, mount namespace, `/sandbox/.hermes` inode, AgentDefinition-derived plan and projection, target posture, rollback posture, and fresh nonce.
Before the provider fence becomes active, NemoClaw drains any earlier privileged sandbox execution lease.
While the fence is active, new direct-container, SSH, and OpenShell command transports are rejected before a sandbox command starts.
The installed root-owned controller and Hermes publisher apply and verify the exact target posture, then the provider starts only the bound Hermes entrypoint and proves the replacement gateway and startup checkpoint before it releases the fence.

The owner-only ledger under `~/.nemoclaw/state/runtime-provider-lifecycle/` survives a host controller restart and remains authoritative for recovery of the exact target.
The next Shields command, including `shields status`, must recover a retained fence before it can report or change posture.
Hermes startup also checks the image-owned startup gate before it reads mutable state and remains held when the runtime provider state mutation is active or cannot be authenticated.
While recovery is incomplete, do not:

- Delete the ledger.
- Kill a held entrypoint.
- Use a manual container command to bypass the fence.

If another host mutation is active, the command reports `Hermes config mutation is already in progress`.
If another lifecycle request owns the supervisor, it reports `SUPERVISOR_BUSY`.
Expand Down
Loading
Loading