Apache Software Foundation logo

Skill

magpie-setup-isolated-setup-doctor

diagnose secure agent setup restrictions

Covers Security Operations Engineering Debugging

Description

Probe the secure-agent setup for in-session functional restrictions that block legitimate workflows. Three live probes — SSH agent / Yubikey reachability, localhost port binding, docker / podman runtime socket — each pointing the user at the matching numbered troubleshooting entry and its settings.json remediation (see body). Read-only — never modifies settings.json, never invokes the sandbox bypass.

SKILL.md

setup-isolated-setup-doctor

The diagnostic layer over the secure agent setup. Complements the existing setup skills:

  • setup-isolated-setup-install installs the secure setup.
  • setup-isolated-setup-verify answers "is the secure setup installed correctly?" — static checks on settings.json shape, hook wiring, pinned tool versions. Catches drift and missing pieces.
  • setup-isolated-setup-update surfaces drift against the framework's latest.
  • setup-isolated-setup-doctor (this skill) answers "are common workflows functionally blocked by the current sandbox?" — live probes of SSH agent, port binding, docker / podman socket. Catches over-restrictive allowlists.

Run verify first when the install is in question (fresh machine, recent framework upgrade, sandbox-state surprise). Run doctor when the install is known good but a workflow fails in a sandbox-shaped way — agent unreachable, socket error, port permission error.

Every probe maps to a numbered entry in docs/setup/sandbox-troubleshooting.md; the doctor's job is to identify which entry applies right now, not to re-explain the remediation. If a fail surfaces a failure mode not catalogued there, propose appending a new entry per the catalog's Adding a new entry section.

Golden rules

  • Read-only. Each probe runs a small, deterministic, side-effect-free check. The skill never edits any settings file, never runs a command with dangerouslyDisableSandbox, never installs anything. If a check fails, surface the failure and point at the catalog entry; do not auto-fix.
  • Run every probe, even on early failure. Do not stop at the first ✗. The value of the report is in the full picture — a user may have one of three independent restrictions, or all three, and discovering them one re-run at a time is annoying.
  • Distinguish ✗ (failing) from ⊘ (not applicable). ✗ means the probe ran and the sandbox blocked it. ⊘ means the probe was skipped because the prerequisite is absent (e.g. no docker / podman on PATH → docker probe ⊘, not ✗).
  • Surface evidence. Each report line names the probe command, the exit code, and the relevant stderr snippet. "Looks blocked" is not a useful report; "ssh-add -l → rc=2 → Could not open a connection to your authentication agent" is.
  • Map each ✗ to a catalog entry. The fail report includes a direct link to the matching section of docs/setup/sandbox-troubleshooting.md. Do not paraphrase the remediation — the catalog is the single source of truth.

The 3 probes

The current set covers the three failure modes the catalog documents. New probes are added when new entries land in the catalog; the two stay in lock-step.

Probe 1 — SSH agent / Yubikey reachable

Tests whether ssh-agent is reachable from inside the sandbox. Failure mode: SSH_AUTH_SOCK is passed through claude-iso's env whitelist but the socket file is not in sandbox.filesystem.allowRead, so the agent's ssh / git push subprocesses cannot connect(2) to the socket.

Command:

if [ -z "$SSH_AUTH_SOCK" ]; then
  echo "PROBE: ssh-agent → ⊘ (SSH_AUTH_SOCK not set in env)"
elif [ ! -S "$SSH_AUTH_SOCK" ]; then
  echo "PROBE: ssh-agent → ✗ (socket file at SSH_AUTH_SOCK not stat-able from inside sandbox)"
  echo "       SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
else
  ssh-add -l > /tmp/ssh-add.out 2>&1; rc=$?
  case "$rc" in
    0) echo "PROBE: ssh-agent → ✓ ($(wc -l < /tmp/ssh-add.out | tr -d ' ') identities listed)" ;;
    1) echo "PROBE: ssh-agent → ✓ (agent reachable, no identities configured)" ;;
    2) echo "PROBE: ssh-agent → ✗ (agent unreachable: $(head -1 /tmp/ssh-add.out))" ;;
    *) echo "PROBE: ssh-agent → ⚠ (unexpected rc=$rc: $(head -1 /tmp/ssh-add.out))" ;;
  esac
fi

Interpretation:

ResultStatusMeaning
✓ N identities listedPassssh-add -l returned the key list.
✓ agent reachable, no identitiesPassssh-add -l returned rc=1 (the documented "no keys" exit).
✗ socket not stat-ableFailSandbox blocks stat(2) on the socket file.
✗ agent unreachableFailSandbox blocks connect(2) to the socket.
⊘ SSH_AUTH_SOCK not setSkipEither the user does not run ssh-agent, or claude-iso's env whitelist dropped it (separate bug — verify).

On ✗ → remediation:docs/setup/sandbox-troubleshooting.md — SSH agent / Yubikey appears unreachable from inside the sandbox.

Probe 2 — Localhost port bind

Tests whether a process inside the sandbox can bind to a loopback port AND then talk to itself over loopback. The failure mode the catalog documents is the second half (egress proxy blocks 127.0.0.1).

Command:

python3 - <<'PY' 2>&1 || true
import socket, urllib.request, threading, http.server, sys

# 1. Can we bind?
try:
    s = socket.socket()
    s.bind(("127.0.0.1", 0))
    port = s.getsockname()[1]
    s.listen(1)
except OSError as e:
    print(f"PROBE: localhost-bind → ✗ (bind: {e})")
    sys.exit(0)

# 2. Can we GET from our own server over loopback?
class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200); self.end_headers(); self.wfile.write(b"ok")
    def log_message(self, *_): pass

server = http.server.HTTPServer(("127.0.0.1", 0), Handler)
threading.Thread(target=server.serve_forever, daemon=True).start()
try:
    with urllib.request.urlopen(f"http://127.0.0.1:{server.server_port}/", timeout=5) as r:
        body = r.read()
    print(f"PROBE: localhost-bind → ✓ (bound + loopback GET → HTTP {r.status}, body={body!r})")
except Exception as e:
    print(f"PROBE: localhost-bind → ✗ (bind ok, loopback GET: {type(e).__name__}: {e})")
finally:
    server.shutdown()
    s.close()
PY

Interpretation:

ResultStatusMeaning
✓ bound + loopback GET → HTTP 200PassBoth bind and loopback HTTP work.
✗ bind: ...FailThe sandbox blocks bind(2) on 127.0.0.1. Rare.
✗ bind ok, loopback GET: ...FailBind works but the sandbox egress proxy refuses 127.0.0.1 as a destination. Common shape.

On ✗ → remediation:docs/setup/sandbox-troubleshooting.md — Test cannot bind to a localhost port.

Probe 3 — Docker / Podman runtime socket

Tests whether the runtime CLI can talk to its daemon. Run for each of docker / podman that is on PATH; ⊘ each that is not installed (this is not a sandbox failure, just an absent prerequisite).

Command:

for rt in docker podman; do
  if ! command -v "$rt" > /dev/null 2>&1; then
    echo "PROBE: ${rt}-runtime → ⊘ ($rt not on PATH)"
    continue
  fi
  out=$("$rt" info > /dev/null 2>&1 && echo ok || echo "fail:$?")
  case "$out" in
    ok)
      echo "PROBE: ${rt}-runtime → ✓ (${rt} info returned)" ;;
    fail:*)
      err=$("$rt" info 2>&1 >/dev/null | head -2 | tr '\n' ' ')
      echo "PROBE: ${rt}-runtime → ✗ ($out: $err)"
      ;;
  esac
done

Interpretation:

ResultStatusMeaning
✓ <rt> info returnedPassThe CLI reached the daemon successfully.
✗ fail:1: Cannot connect to the Docker daemon …FailDaemon socket not readable from inside the sandbox.
✗ fail:1: connect: permission deniedFailSame root cause, different stderr (Linux variant).
⊘ <rt> not on PATHSkipRuntime not installed; not a sandbox restriction.

On ✗ → remediation:docs/setup/sandbox-troubleshooting.md — Docker / Podman command fails with a socket error.

After the report

If every probe is ✓ or ⊘:

All three probes pass (or are not applicable). The sandbox is not currently blocking the known failure modes catalogued in docs/setup/sandbox-troubleshooting.md. If you hit a different sandbox-shaped failure, follow the catalog's Adding a new entry section and (optionally) extend this skill with a fourth probe so future runs catch the same shape automatically.

If any probe is ✗:

  1. Surface every fail in one report (do not stop at the first).
  2. For each fail, print the troubleshooting-doc anchor link from the probe's On ✗ → remediation row above.
  3. Suggest the user open the catalog entry to read the symptom → root cause → fix shape, then apply the settings.json widening themselves. Do not propose to apply the widening from this skill — settings.json widenings are sandbox-bypass-adjacent and need an explicit user-driven edit.
  4. After the user has applied the widening (in a separate flow), re-run setup-isolated-setup-doctor to confirm the probe now passes.

If a probe surfaces a fail shape not catalogued in docs/setup/sandbox-troubleshooting.md:

  1. Report the fail with the literal probe command + exit code + stderr.
  2. Suggest the user add a new entry to the catalog per its Adding a new entry section (symptom verbatim, root cause, fix, notes).
  3. Once the catalog has the new entry, extend this skill with a matching probe in the same shape so the next doctor run catches it automatically.

Extending the skill with a new probe

When the catalog grows a new entry, add a matching probe section following the shape above:

  1. Command — a short, deterministic, side-effect-free one-liner (or short Python heredoc) that triggers the failure mode reliably.
  2. Interpretation — a 3–5-row table mapping result strings to ✓ / ✗ / ⊘ / ⚠.
  3. On ✗ → remediation — a direct link to the matching section of docs/setup/sandbox-troubleshooting.md.

Keep probes narrowly scoped: each probe tests one failure mode, not a bundle. A probe that conflates two restrictions makes the report ambiguous when the result is ✗.

© 2026 YourAI.tools. Every skill from an identity-verified publisher.

Independent catalog. Not affiliated with, endorsed by, or sponsored by Anthropic or any listed publisher. All trademarks belong to their respective owners.