Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6d08d7a
fix(onboard): skip Docker bridge probe for VM driver
ericksoa May 13, 2026
c98d291
fix(onboard): keep bridge probe patch entrypoint-neutral
ericksoa May 13, 2026
26b66cd
fix(onboard): wait for VM startup output before detaching
ericksoa May 13, 2026
a458b2f
fix(onboard): keep VM startup gate out of entrypoint
ericksoa May 13, 2026
d3fbfeb
fix(hermes): keep macos vm startup mutable
ericksoa May 13, 2026
3869623
fix(onboard): reuse stored messaging channels
ericksoa May 13, 2026
cef0079
fix(connect): avoid legacy dns repair for vm sandboxes
ericksoa May 13, 2026
8343311
fix: monkeypatch macos vm dns for inference
ericksoa May 13, 2026
696be2a
fix: allow discord guild users for hermes
ericksoa May 13, 2026
db6b0d4
refactor: keep onboard entrypoint net neutral
ericksoa May 13, 2026
aed17c3
Merge remote-tracking branch 'origin/main' into fix/macos-vm-skip-doc…
ericksoa May 13, 2026
11cc7f3
fix: allow discord regional websocket gateways
ericksoa May 13, 2026
0f54432
fix: address messaging reuse review feedback
ericksoa May 13, 2026
46ab1f6
Merge remote-tracking branch 'origin/main' into fix/macos-vm-skip-doc…
ericksoa May 13, 2026
2b290c0
fix: flush sandbox create tail before ready recovery
ericksoa May 13, 2026
3a9f58f
fix: keep VM DNS monkeypatch best-effort
ericksoa May 13, 2026
242a624
fix(onboard): address messaging reuse feedback
ericksoa May 13, 2026
32056fa
fix(macos): harden VM DNS monkeypatch
ericksoa May 13, 2026
d7a3b25
Merge branch 'main' into fix/macos-vm-skip-docker-bridge-probe
cv May 13, 2026
793666c
fix(macos): address VM DNS review feedback
ericksoa May 13, 2026
70a4887
fix(macos): special-case only VM DNS repair
ericksoa May 13, 2026
cd513ea
Merge branch 'main' into fix/macos-vm-skip-docker-bridge-probe
cv May 13, 2026
5a84abb
Merge remote-tracking branch 'origin/main' into fix/macos-vm-skip-doc…
cv May 13, 2026
2646709
Merge remote-tracking branch 'origin/main' into fix/macos-vm-skip-doc…
ericksoa May 14, 2026
4c36539
fix(connect): probe VM inference after route reapply
ericksoa May 14, 2026
55296c4
merge: main into fix/macos-vm-skip-docker-bridge-probe
cv May 14, 2026
69698b7
fix(onboard): satisfy entrypoint budget
cv May 14, 2026
7a22871
Merge branch 'main' into fix/macos-vm-skip-docker-bridge-probe
ericksoa May 14, 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
5 changes: 5 additions & 0 deletions agents/hermes/config/messaging-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export function buildMessagingEnvLines(
const discordAllowedUsers = collectDiscordAllowedUsers(allowedIds, discordGuilds);
if (discordAllowedUsers.length > 0) {
envLines.push(`DISCORD_ALLOWED_USERS=${discordAllowedUsers.join(",")}`);
} else if (
enabledChannels.has("discord") &&
Object.keys(discordGuilds).filter((guildId) => guildId.trim()).length > 0
) {
envLines.push("DISCORD_ALLOW_ALL_USERS=true");
}
if (allowedIds.telegram?.length) {
envLines.push(`TELEGRAM_ALLOWED_USERS=${allowedIds.telegram.map(String).join(",")}`);
Expand Down
8 changes: 8 additions & 0 deletions agents/hermes/policy-additions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ network_policies:
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: "*.discord.gg"
port: 443
protocol: websocket
enforcement: enforce
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: cdn.discordapp.com
port: 443
protocol: rest
Expand Down
8 changes: 8 additions & 0 deletions agents/hermes/policy-permissive.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ network_policies:
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: "*.discord.gg"
port: 443
protocol: websocket
enforcement: enforce
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: cdn.discordapp.com
port: 443
protocol: rest
Expand Down
7 changes: 6 additions & 1 deletion agents/hermes/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,12 @@ if [ "$(id -u)" -ne 0 ]; then
export HOME=/sandbox
export HERMES_HOME="${HERMES_DIR}"

if ! verify_config_integrity "${HERMES_DIR}" "${HERMES_HASH_FILE}"; then
# macOS VM startup currently runs this entrypoint as the sandbox user and
# remaps rootfs ownership to the host uid. In that mode the strict /etc hash
# cannot remain a root-owned trust anchor, so use the same locked-aware
# mutable-default verifier as OpenClaw. The root path below keeps strict
# verification against /etc/nemoclaw/hermes.config-hash.
if ! verify_config_integrity_if_locked "${HERMES_DIR}"; then
echo "[SECURITY] Config integrity check failed — refusing to start (non-root mode)" >&2
exit 1
fi
Expand Down
15 changes: 13 additions & 2 deletions agents/openclaw/policy-permissive.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,20 @@ network_policies:
access: full
- host: gateway.discord.gg
port: 443
protocol: rest
protocol: websocket
enforcement: enforce
access: full
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: "*.discord.gg"
port: 443
protocol: websocket
enforcement: enforce
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: cdn.discordapp.com
port: 443
protocol: rest
Expand Down
8 changes: 8 additions & 0 deletions nemoclaw-blueprint/policies/openclaw-sandbox-permissive.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ network_policies:
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: "*.discord.gg"
port: 443
protocol: websocket
enforcement: enforce
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: cdn.discordapp.com
port: 443
protocol: rest
Expand Down
8 changes: 8 additions & 0 deletions nemoclaw-blueprint/policies/presets/discord.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ network_policies:
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: "*.discord.gg"
port: 443
protocol: websocket
enforcement: enforce
websocket_credential_rewrite: true
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: WEBSOCKET_TEXT, path: "/**" }
- host: cdn.discordapp.com
port: 443
protocol: rest
Expand Down
65 changes: 64 additions & 1 deletion src/lib/actions/sandbox/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import type { SandboxEntry } from "../../state/registry";
import { ROOT } from "../../runner";
import { runSetupDnsProxy } from "../dns";
import { ensureLiveSandboxOrExit } from "./gateway-state";
import {
applyOpenShellVmDnsMonkeypatch,
shouldApplyVmDnsMonkeypatch,
} from "./vm-dns-monkeypatch";
import {
createSystemDeps as createSessionDeps,
getActiveSandboxSessions,
Expand Down Expand Up @@ -155,13 +159,72 @@ function isSandboxInferenceRouteHealthy(sandboxName: string): boolean {
return probe.status === 0 && /^OK\s+[0-9]{3}\b/.test(probe.output.trim());
}

function shouldUseLegacyDnsProxyRepair(sb: SandboxEntry | null): boolean {
return sb?.openshellDriver !== "vm";
}

function reapplyVmInferenceRoute(sandboxName: string, sb: SandboxEntry | null): boolean {
if (!sb?.provider || !sb.model) return false;
runOpenshell(
["inference", "set", "--provider", sb.provider, "--model", sb.model, "--no-verify"],
{ ignoreError: true },
);
return isSandboxInferenceRouteHealthy(sandboxName);
}

function repairSandboxInferenceRouteIfNeeded(
sandboxName: string,
sb: SandboxEntry | null,
{ quiet = false }: { quiet?: boolean } = {},
): boolean {
if (process.env.NEMOCLAW_DISABLE_INFERENCE_ROUTE_REPAIR === "1") return false;
if (isSandboxInferenceRouteHealthy(sandboxName)) return false;

if (!shouldUseLegacyDnsProxyRepair(sb)) {
if (shouldApplyVmDnsMonkeypatch(sb)) {
if (!quiet) {
console.log("");
console.log(
` inference.local is unavailable inside '${sandboxName}'. Applying OpenShell VM DNS monkeypatch...`,
);
}
const patch = applyOpenShellVmDnsMonkeypatch(sandboxName, sb);
if (patch.ok && isSandboxInferenceRouteHealthy(sandboxName)) {
if (!quiet) {
console.log(" inference.local route repaired.");
}
return true;
}
if (!quiet) {
if (!patch.ok && patch.reason) {
console.error(
` Warning: OpenShell VM DNS monkeypatch did not apply: ${patch.reason}`,
);
} else if (patch.ok) {
console.error(
" Warning: OpenShell VM DNS monkeypatch completed but inference.local is still unavailable.",
);
}
}
}

if (!quiet) {
console.log("");
console.log(` inference.local is unavailable inside '${sandboxName}'. Reapplying OpenShell inference route...`);
}
const healthy = reapplyVmInferenceRoute(sandboxName, sb);
if (!quiet) {
if (healthy) {
console.log(" inference.local route repaired.");
} else {
console.error(
` Warning: inference.local is still unavailable through the OpenShell ${sb?.openshellDriver || "non-legacy"} gateway path.`,
);
}
}
return healthy;
}

if (!quiet) {
console.log("");
console.log(` inference.local is unavailable inside '${sandboxName}'. Repairing sandbox DNS proxy...`);
Expand Down Expand Up @@ -219,7 +282,7 @@ function ensureSandboxInferenceRoute(
);
}
}
repairSandboxInferenceRouteIfNeeded(sandboxName, { quiet });
repairSandboxInferenceRouteIfNeeded(sandboxName, sb, { quiet });
}
} catch {
/* non-fatal — don't block connect on inference route repair */
Expand Down
Loading
Loading