SSH Admin
Caution
Every destructive command needs sign-off before it runs. A subagent has no way to pause and ask — AskUserQuestion is stripped from every subagent at runtime — so MODIFY, DELETE, and PRIVILEGE operations come back as an ## APPROVAL REQUIRED envelope in the final return instead of touching the remote host. READ and non-destructive CREATE run freely.
Caution
One agent per host. A multi-server, multi-environment or multi-service job is split per target — the agent refuses to loop over your whole fleet in a single run. Ask for “check prod-1” and “check prod-2” as separate tasks, or accept the split proposal it returns.
Tip
Run /brewtools:ssh first to configure server connections. The skill writes CLAUDE.local.md with your server inventory — ssh-admin reads it automatically on every task start. No inventory file? The agent stops and returns the missing connection details as a ## NEEDS-INPUT block — it never guesses a host.
Quick reference
| Field | Value |
|---|---|
| Model | inherit — runs on the session’s model |
| Tools | Read, Write, Edit, Bash, Glob, Grep, WebFetch, WebSearch |
| Triggers | ”ssh admin”, “server management”, “deploy to server”, “docker on server” |
| Inventory | Reads CLAUDE.local.md in project root |
| Permission | default — destructive ops return as an APPROVAL REQUIRED envelope |
| Scope | One bounded unit per run; one agent per host |
What's new in 6.2.0
- Agent prompt tightened for density — shorter prose, more tables, no behavior change.
Return Contractnow opens the agent definition instead of closing it — the model reads the output shape before anything else.- Every guardrail is unchanged: the six-level safety classification, the
## APPROVAL REQUIREDenvelope fields, and the approval-token rule read the same as before. - No new capability in this pass — wording and table conversion only.
Approval contract
A subagent cannot ask, confirm, or wait for input mid-run — AskUserQuestion is stripped from every
subagent at runtime, even when it’s listed in the agent’s tools:. ssh-admin therefore never runs a
destructive command on its own judgement:
- It performs all non-destructive work and gathers full evidence first.
- Every MODIFY-or-above operation becomes one envelope in its final return, ids
A1..AN:
## APPROVAL REQUIRED
### A1
COMMAND: <exact command, copy-pasteable>
HOST: <server alias / user@host>
EFFECT: <what changes, incl. downtime>
ROLLBACK: <exact reverse command, or NONE>
EVIDENCE: <the read-only output that proves it is needed>
PRECONDITION: <what must still hold at execution time>
- It stops — nothing in that block runs. Nothing destructive to report → the literal line
APPROVAL REQUIRED: none.
The caller (main session, which does have AskUserQuestion) reviews the envelope and either runs
the command itself or re-spawns ssh-admin with APPROVED: A1 A3 in the prompt. That token
authorizes only the ids it names, exactly as worded — not a similar command, not a retry with
different arguments.
Scope guard
The agent sizes the task before it touches a server.
| Situation | What the agent does |
|---|---|
| Task fits one bounded unit — one deliverable, ~5 files, ~10 steps | Runs it |
| Bigger, or several independent deliverables | Stops before starting, returns a split proposal: 2-N bounded subtasks, each with scope and suggested owner |
| Several hosts / environments / services in one request | Split per target — one agent per host, per environment, per service. Never one agent looping over all of them |
| Scope grows mid-flight | Stops at the next clean boundary, reports done / remaining / how to split |
| Brief missing GOAL, SCOPE, CONTEXT, CONSUMER or acceptance | States the assumption in the report, or asks once. Never invents scope |
How this changes your request: instead of “restart the app on all three prod nodes”, phrase it as three tasks (one per node) — or send the broad request, get the split proposal back, and approve it. The result is delivered for the CONSUMER who picks it up next, not for the literal wording.
When to use
- Server health check — disk, memory, CPU, running containers, failed services
- Docker deployment — pull image, write compose file, start/update/stop services
- Firewall changes — UFW rules, port allow/deny, rule review
- Reverse proxy setup — Caddy or Nginx virtual hosts with auto-TLS
- Security hardening — SSH config, fail2ban, user/key management
- Disk cleanup — prune unused Docker layers, locate large directories
Examples
"Check health on prod-server — disk, memory, running containers"
The agent connects via SSH, runs diagnostics (uptime, free -h, df -h, docker ps), and returns a summary table.
"Deploy a Redis container on staging-server, port 6379, with a persistent volume"
Writes a compose file, transfers it via scp, starts the service, verifies the container is running.
"Open port 443 and block all other inbound traffic on web-server"
Reviews current UFW rules, proposes changes, returns them as an APPROVAL REQUIRED envelope, applies once approved, verifies with ufw status verbose.
Flow
- Load inventory
Reads
CLAUDE.local.mdin the project root for host aliases, IP addresses, SSH users, and key paths. If the file is missing, stops and returns the missing connection details as a## NEEDS-INPUTblock — it never guesses a host. - Verify SSH
Runs
ssh -o ConnectTimeout=10 -o BatchMode=yes USER@HOST ‘echo OK’to confirm connectivity. If BatchMode fails (password required), reports the issue and suggests key-based auth setup instead of blocking. - Gather state
Collects a baseline snapshot — uptime, memory, disk, Docker containers, open ports, failed systemd units. Read-only, no confirmation needed.
- Classify and envelope
Every planned command is classified: READ (free), CREATE (free if non-destructive), MODIFY / SERVICE / DELETE / PRIVILEGE (returned as an
## APPROVAL REQUIREDenvelope in the final response — the agent cannot pause mid-run to ask). Nothing executes until the caller re-spawns it with anAPPROVED:token. - Execute and verify
Runs the confirmed commands. Config changes are validated before apply (
caddy validate,nginx -t). Services are restarted and re-checked. Credentials are never hardcoded in commands or files. - Report
Returns a structured summary: server, task description, actions taken, verification results, and final status (Success / Partial / Failed).
Safety rules & internals
Safety classification
| Class | Examples | Action |
|---|---|---|
| READ | ls, cat, df, docker ps, systemctl status, ufw status | Free |
| CREATE | mkdir, touch, docker pull | Free if non-destructive |
| MODIFY | chmod, chown, sed, config edits | Envelope |
| SERVICE | restart, reload, docker compose up | Envelope |
| DELETE | rm, docker rm, docker volume rm, drop | Always envelope |
| PRIVILEGE | sudo, su, firewall rules, user management | Always envelope |
Before any MODIFY / SERVICE / DELETE / PRIVILEGE command on a remote server, the agent returns an
## APPROVAL REQUIRED envelope in its final response instead of running the command — it has no
mid-run question channel. The caller reviews it and either runs the command or re-spawns the agent
with APPROVED: <ids>.
Operational guards
| Guard | Rule |
|---|---|
| Non-interactive output | --no-pager on journalctl/systemctl, bounded log reads (-n 50, --tail 100) — a pager on a remote shell hangs the run |
| Lockout | Before ufw enable or any sshd/port change: verify the current SSH port is allowed and keep an open session until the new config is proven |
| Data destruction | docker system prune -af --volumes and rsync --delete are DELETE level — always confirmed, with an explicit statement of what gets removed |
SSH connection patterns
| Pattern | Command |
|---|---|
| Non-interactive | ssh -o ConnectTimeout=10 -o BatchMode=yes USER@HOST "command" |
| Multi-command | ssh -o ConnectTimeout=10 -o BatchMode=yes USER@HOST 'cmd1 && cmd2' |
| File transfer | scp -o ConnectTimeout=10 FILE USER@HOST:/path/ |
| Interactive | Instruct user: ! ssh USER@HOST in Claude Code prompt |
Docker Compose resource limits (Non-Swarm)
services:
app:
image: myapp:latest
mem_limit: 512m
cpus: 0.5
restart: unless-stoppedUse mem_limit/cpus — never deploy.resources.* (Swarm-only syntax).
Quick health script
echo "=== Server Health ===" && \
uptime && echo "---" && \
free -h | grep Mem && echo "---" && \
df -h | grep -E '^/dev' && echo "---" && \
docker ps --format 'table {{.Names}}\t{{.Status}}' 2>/dev/null && echo "---" && \
systemctl --failed --no-pagerSSH hardening checklist (/etc/ssh/sshd_config)
| Setting | Value | Why |
|---|---|---|
PermitRootLogin | no | Prevent root SSH |
PasswordAuthentication | no | Key-only access |
MaxAuthTries | 3 | Brute-force limit |
Port | Custom (e.g. 2222) | Reduce scan noise |
Return Contract
Verdict first, <=30 lines, path:line — never raw command output, journalctl/docker logs dumps, or config file bodies pasted into the reply. Per host: what changed, service state after (active / failed / unchanged), and anything left pending confirmation. A config edit cites the changed path:line, not the file; a health check cites the one abnormal number, not the whole dump. Full logs and health output go to .claude/reports/YYYYMMDD-HHMMSS_ssh-admin/, returned as a path.
/brewtools:agent-return-setup enforces this at ~1000 / ~2500 est-tokens when installed; the contract itself ships unconditionally.
SSH Skill
Configure server inventory and SSH connections before spawning ssh-admin.
Deploy Admin
GitHub Actions, workflows, GHCR releases, and CI/CD — pairs with ssh-admin for full deploy pipelines.
GitHub source
Agent definition, safety rules, and SSH patterns.
Brewtools overview
All brewtools skills and agents in one place.
Updating plugins
/brewtools:plugin-update to check and update the brewcode plugin suite in one command.
See the FAQ for details.