#!/bin/bash # # Purpose : Set up sandboxed AI agents (see sandbox/agent): install or update # Anthropic's sandbox runtime (srt) and Hermes Agent, and turn on # Claude Code's built-in sandbox. Run by install.sh and on every # update (updates.d/72-sandbox.sh). COREDESKTOP="$HOME/.local/share/coredesktop" STATUS=0 # npm's global prefix is under /usr, so install to ~/.local; srt lands in ~/.local/bin echo "Installing the sandbox runtime (srt)..." npm install --global --prefix "$HOME/.local" --no-fund --no-audit @anthropic-ai/sandbox-runtime || STATUS=1 # --non-interactive skips Hermes' setup questions and its background gateway # service; `configure hermes` chooses a model provider afterwards if [ ! -x "$HOME/.local/bin/hermes" ]; then echo "Installing Hermes Agent..." curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash -s -- --non-interactive || STATUS=1 fi # Claude Code runs every command it starts in its own sandbox, also built on # bubblewrap and socat; it can't run inside srt because bubblewrap can't be # nested. Anything already in the sandbox section of your settings wins over # these defaults. CLAUDE_SETTINGS="$HOME/.claude/settings.json" if [ -f "$CLAUDE_SETTINGS" ]; then domains="$(jq '.network.allowedDomains' "$COREDESKTOP/sandbox/config/base.json")" tmp="$(mktemp "$CLAUDE_SETTINGS.XXXXXX")" if jq --argjson domains "$domains" \ '.sandbox = ({enabled: true, network: {allowedDomains: $domains}} * (.sandbox // {}))' \ "$CLAUDE_SETTINGS" > "$tmp"; then if cmp -s "$tmp" "$CLAUDE_SETTINGS"; then rm -f "$tmp" else echo "Turning on Claude Code's sandbox..." mv -f "$tmp" "$CLAUDE_SETTINGS" fi else echo "Couldn't read $CLAUDE_SETTINGS; Claude Code's sandbox was not turned on." rm -f "$tmp" STATUS=1 fi fi # Hermes' gateway acts on messages from outside this machine, so it runs in # the sandbox too: a systemd drop-in starts it through sandbox/bin/hermes. It # also needs to reach the Matrix server in Hermes' settings, which differs on # each machine; for other messaging services, add their domains to # ~/.config/coredesktop/sandbox/hermes.json. GATEWAY_UNIT="$HOME/.config/systemd/user/hermes-gateway.service" if [ -f "$GATEWAY_UNIT" ]; then if [ -x "$HOME/.local/bin/srt" ]; then homeserver="$(sed -nE "s#^MATRIX_HOMESERVER=[\"']?https?://([^/:\"']+).*#\1#p" "$HOME/.hermes/.env" 2> /dev/null | head -n 1)" dropin="$GATEWAY_UNIT.d/coredesktop-sandbox.conf" tmp="$(mktemp)" cat > "$tmp" << EOF # Written by CoreDesktop (sandbox/setup.sh): run Hermes' gateway in the sandbox [Service] Environment="COREDESKTOP_SANDBOX_DOMAINS=$homeserver" ExecStart= ExecStart=%h/.local/share/coredesktop/sandbox/bin/hermes gateway run EOF if cmp -s "$tmp" "$dropin"; then rm -f "$tmp" else echo "Running Hermes' gateway in the sandbox..." mkdir -p "$(dirname "$dropin")" && mv -f "$tmp" "$dropin" && chmod 644 "$dropin" \ && systemctl --user daemon-reload || STATUS=1 fi # The gateway reads its sandbox rules only when it starts, so restart it # when they or the drop-in change stamp="$HOME/.local/state/coredesktop/hermes-gateway-sandbox" rules="$(cat "$dropin" "$COREDESKTOP/sandbox/agent" "$COREDESKTOP"/sandbox/config/{base,hermes}.json \ "$HOME"/.config/coredesktop/sandbox/{base,hermes}.json 2> /dev/null | sha256sum)" if [ "$rules" != "$(cat "$stamp" 2> /dev/null)" ]; then systemctl --user try-restart hermes-gateway.service \ && mkdir -p "$(dirname "$stamp")" && echo "$rules" > "$stamp" || STATUS=1 fi else echo "Hermes' gateway isn't sandboxed yet: the sandbox (srt) isn't installed." STATUS=1 fi fi exit $STATUS