SSH Way¶
Source: hooks/ways/softwaredev/environment/ssh/ssh.md
Frontmatter
| Field | Value |
|---|---|
description |
SSH remote access, key management, secure file transfer, non-interactive authentication |
vocabulary |
ssh remote key scp rsync bastion jumphost tunnel forwarding batchmode noninteractive |
pattern |
\bssh\b|remote.?server|remote.?host|sshpass |
commands |
^ssh\ |^scp\ |^rsync.*:|\bsshpass\b |
refire |
0.15 |
scope |
agent, subagent |
Core Principle¶
Claude cannot interact with password prompts. SSH commands must be non-interactive or they will hang indefinitely.
Before SSH'ing¶
Assume the user has intentional access configured. Quick validation:
# Check if ssh-agent has keys loaded
ssh-add -l
# Test connection (fails fast, no hang)
ssh -o BatchMode=yes -o ConnectTimeout=5 host exit && echo "OK"
If connection fails, ask before troubleshooting - don't assume setup is broken.
Non-Interactive Flags (Always Use)¶
ssh -o BatchMode=yes \
-o ConnectTimeout=10 \
-o StrictHostKeyChecking=accept-new \
user@host "command"
| Flag | Purpose |
|---|---|
BatchMode=yes |
Fail immediately if auth needs interaction |
ConnectTimeout=10 |
Don't hang on unreachable hosts |
StrictHostKeyChecking=accept-new |
Accept new hosts, reject changed keys |
Tiered Scenarios¶
Dev / Personal¶
Keys typically in ~/.ssh/, ssh-agent running. Straightforward:
Homelab / Internal¶
Multiple hosts, possibly jump hosts. Use SSH config:
# ~/.ssh/config
Host homelab-*
User admin
IdentityFile ~/.ssh/homelab_key
Host *.internal
ProxyJump bastion
Then simply: ssh homelab-web "systemctl status nginx"
Enterprise / Legacy¶
May require sshpass for password-based systems:
# Password from environment (not argument - visible in ps)
export SSHPASS="$PASSWORD"
sshpass -e ssh -o StrictHostKeyChecking=no legacy-server "command"
sshpass cautions:
- Password visible in environment (but not process list with -e)
- Prefer key-based auth when possible
- If password required, get from env var or file, never hardcode
If Setup Doesn't Exist¶
When user wants SSH but it's not configured, suggest:
# Generate key (if none exists)
ssh-keygen -t ed25519 -C "user@machine"
# Copy to remote (one-time, interactive OK for setup)
ssh-copy-id user@host
# Add to agent for session
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
Then retest with ssh -o BatchMode=yes host exit.
File Transfers¶
# scp (simple)
scp -o BatchMode=yes local.txt host:/path/
# rsync (better for large/repeated transfers)
rsync -avz -e "ssh -o BatchMode=yes" ./dir/ host:/path/
Command Patterns¶
# Single command
ssh host "command"
# Multiple commands
ssh host "cd /app && git pull && npm install"
# Heredoc for complex scripts
ssh host bash <<'EOF'
cd /var/log
grep ERROR app.log | tail -20
EOF
What NOT to Do¶
- Don't attempt interactive password entry
- Don't hardcode passwords in commands
- Don't skip BatchMode (will hang on unexpected prompts)
- Don't ignore ConnectTimeout (will hang on network issues)
- Don't assume broken setup - ask the user first