Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
aa7461e
fix(mcp): reconcile Hermes runtime state
ericksoa Jul 3, 2026
0d8046a
test(mcp): keep startup harness linear
ericksoa Jul 3, 2026
107cef1
fix(mcp): harden reconciliation boundaries
ericksoa Jul 4, 2026
d2b027e
fix(mcp): bind inspection to authenticated snapshot
ericksoa Jul 4, 2026
2cf484a
test(mcp): prove Hermes failure boundaries
ericksoa Jul 4, 2026
7bd1098
fix(mcp): preserve Hermes integrity hash state
ericksoa Jul 4, 2026
c76a741
fix(mcp): address CodeRabbit feedback
ericksoa Jul 4, 2026
b54135e
Merge main into fix/mcp-v075-followups
ericksoa Jul 4, 2026
d17364b
Merge main into fix/mcp-v075-followups
ericksoa Jul 4, 2026
4e088f7
fix(mcp): close Hermes reconciliation blockers
ericksoa Jul 4, 2026
9fd8580
fix(hermes): restore startup integrity checks
ericksoa Jul 4, 2026
4fea4e5
test(security): eliminate shell interpolation
ericksoa Jul 4, 2026
2f81d34
fix(hermes): harden startup integrity proof
ericksoa Jul 4, 2026
b2ae469
fix(mcp): close final review blockers
ericksoa Jul 4, 2026
42f3602
fix(mcp): reject cross-agent reconciliation
ericksoa Jul 4, 2026
8db5342
test(mcp): cover final integrity boundaries
ericksoa Jul 4, 2026
3c6b92d
test(mcp): keep manifest fixture linear
ericksoa Jul 4, 2026
da4442c
Merge remote-tracking branch 'origin/main' into review/pr6261
ericksoa Jul 4, 2026
8503875
fix(hermes): trust atomic MCP state commit
ericksoa Jul 4, 2026
11ba1a3
fix(hermes): stabilize MCP relay recovery
ericksoa Jul 4, 2026
c6f2762
fix(hermes): preserve supervised relays on reload
ericksoa Jul 6, 2026
44195b5
fix(hermes): redact config from snapshot repr
ericksoa Jul 6, 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
16 changes: 11 additions & 5 deletions agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ COPY scripts/managed-gateway-control.py /usr/local/lib/nemoclaw/managed-gateway-
COPY agents/hermes/validate-env-secret-boundary.py /usr/local/lib/nemoclaw/validate-hermes-env-secret-boundary.py
COPY agents/hermes/seed-dashboard-config.py /usr/local/lib/nemoclaw/seed-hermes-dashboard-config.py
COPY agents/hermes/runtime-config-guard.py /usr/local/lib/nemoclaw/hermes-runtime-config-guard.py
COPY agents/hermes/build-mcp-digest.py /usr/local/lib/nemoclaw/build-hermes-mcp-digest.py
COPY agents/hermes/mcp-config-transaction.py /usr/local/lib/nemoclaw/hermes-mcp-config-transaction.py
COPY src/lib/actions/sandbox/openshell-child-visible-credentials.v0.0.72.json /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.72.json
COPY scripts/state-dir-guard.py /usr/local/lib/nemoclaw/state-dir-guard.py
Expand All @@ -138,10 +139,10 @@ COPY nemoclaw-blueprint/scripts/*.js /usr/local/lib/nemoclaw/preloads/
# 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/lib/nemoclaw/sandbox-init.sh /usr/local/lib/nemoclaw/validate-hermes-env-secret-boundary.py /usr/local/lib/nemoclaw/seed-hermes-dashboard-config.py /usr/local/lib/nemoclaw/hermes-runtime-config-guard.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/lib/nemoclaw/managed-gateway-control.py /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.72.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/managed-gateway-control.py /usr/local/lib/nemoclaw/build-hermes-mcp-digest.py /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.72.json \
&& chmod 700 /usr/local/bin/nemoclaw-gateway-control \
&& chmod 500 /usr/local/lib/nemoclaw/state-dir-guard.py /usr/local/lib/nemoclaw/managed-gateway-control.py \
&& chmod 444 /usr/local/lib/nemoclaw/gateway-supervisor.sh \
&& chmod 444 /usr/local/lib/nemoclaw/gateway-supervisor.sh /usr/local/lib/nemoclaw/build-hermes-mcp-digest.py \
&& chmod 444 /usr/local/lib/nemoclaw/openshell-child-visible-credentials.v0.0.72.json \
&& if [ -d /usr/local/lib/nemoclaw/preloads ]; then \
chown -R 0:0 /usr/local/lib/nemoclaw/preloads \
Expand Down Expand Up @@ -483,16 +484,21 @@ RUN set -eu; \
&& chown sandbox:sandbox /sandbox/.hermes/.hermes_history \
&& chmod 660 /sandbox/.hermes/.hermes_history

# Pin config hash at build time for integrity verification at startup.
# Pin config hash at build time for integrity verification at startup. Invoke
# the installed runtime guard's `_canonical_mcp_servers_digest` directly so
# image sealing and runtime verification cannot drift onto different JSON
# canonicalization contracts.
RUN mkdir -p /etc/nemoclaw \
&& sha256sum /sandbox/.hermes/config.yaml /sandbox/.hermes/.env \
> /etc/nemoclaw/hermes.config-hash \
&& mcp_digest="$(/opt/hermes/.venv/bin/python -I /usr/local/lib/nemoclaw/build-hermes-mcp-digest.py --guard /usr/local/lib/nemoclaw/hermes-runtime-config-guard.py --config /sandbox/.hermes/config.yaml)" \
&& printf '# nemoclaw-hermes-mcp-state-v1 intended=%s applied=%s\n' "$mcp_digest" "$mcp_digest" \
>> /etc/nemoclaw/hermes.config-hash \
&& chown root:root /etc/nemoclaw/hermes.config-hash \
&& chmod 444 /etc/nemoclaw/hermes.config-hash

# Backward-compatible marker for host-side shields logic on older sandboxes.
RUN sha256sum /sandbox/.hermes/config.yaml /sandbox/.hermes/.env \
> /sandbox/.hermes/.config-hash \
RUN cp /etc/nemoclaw/hermes.config-hash /sandbox/.hermes/.config-hash \
&& chmod 640 /sandbox/.hermes/.config-hash \
&& chown sandbox:sandbox /sandbox/.hermes/.config-hash

Expand Down
39 changes: 39 additions & 0 deletions agents/hermes/build-mcp-digest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Compute the image seal with the runtime guard's canonical MCP function."""

from __future__ import annotations

import argparse
import importlib.util
import sys
from pathlib import Path
from types import ModuleType
from typing import Callable, cast


def _load_guard(path: Path) -> ModuleType:
spec = importlib.util.spec_from_file_location("hermes_runtime_config_guard", path)
if spec is None or spec.loader is None:
raise RuntimeError("Hermes runtime config guard cannot be loaded")
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module


def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--guard", required=True, type=Path)
parser.add_argument("--config", required=True, type=Path)
args = parser.parse_args()

guard = _load_guard(args.guard)
canonicalizer = cast(Callable[[str], str], guard._canonical_mcp_servers_digest)
print(canonicalizer(args.config.read_text(encoding="utf-8")))
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading
Loading