Mirror of r420/sauer/claude-ssh-setup
Go to file
2026-04-16 22:15:00 +10:00
README.md Initial commit: plink/PowerShell SSH setup notes 2026-04-16 22:15:00 +10:00

claude-ssh-setup

How Claude Code on this Windows machine reaches the sae-engineering server (10.0.2.1, user sauer) over SSH.

TL;DR

OpenSSH on Windows can't load the .ppk key (NTFS permissions block it from Git Bash, and the OpenSSH copy of the key fails the same way). PuTTY's plink reads the PuTTY keystore natively and the host key is already trusted in the Windows registry. cmd.exe mangles user@host (treats it as an AT job), so invoke plink through powershell.exe instead.

Connection details

Field Value
Host 10.0.2.1
User sauer
Key C:\Users\richa\.ssh\id_ed25519.ppk (PuTTY)
Plink C:\Program Files\PuTTY\plink.exe
pscp C:\Program Files\PuTTY\pscp.exe

Single command

powershell.exe -Command "& 'C:\Program Files\PuTTY\plink.exe' -i 'C:\Users\richa\.ssh\id_ed25519.ppk' -batch -l sauer 10.0.2.1 '<command>'"

-batch disables interactive prompts so the call fails fast in automation instead of hanging.

Multiple commands

Chain with ; and avoid single quotes inside the remote command (the outer single quotes are already in use):

powershell.exe -Command "& 'C:\Program Files\PuTTY\plink.exe' -i 'C:\Users\richa\.ssh\id_ed25519.ppk' -batch -l sauer 10.0.2.1 '/bin/cmd1; /bin/cmd2'"

For anything more complex, write a script locally and run it with the file-transfer recipe below.

File transfer (pscp)

"/c/Program Files/PuTTY/pscp.exe" -i "C:/Users/richa/.ssh/id_ed25519.ppk" -batch \
  localfile sauer@10.0.2.1:/remote/path

This is also the workaround when a remote command needs literal quotes or braces — PowerShell + plink eats them. Write the payload to a local file, pscp it across, then have plink consume it (e.g. curl --data-binary @file).

"Ubuntu Server Mode"

When the user says "Ubuntu Server Mode" (or "server mode"), Claude treats every shell command as if it were running on 10.0.2.1 as sauer — wrap each command with the plink invocation transparently and follow server-side conventions (sudo docker compose, Caddy reload paths, etc.). The server has its own memory at ~/.claude/projects/-home-sauer/memory/MEMORY.md — read that index before making changes. Stay in this mode until told to exit ("back to local", "exit server mode").

Notifications (server side)

claude-notify "message"   # send to Telegram bridge
claude-inbox --pop        # read responses