From fa34d29b7eecbcd68ce3871d1a46721eafa67d72 Mon Sep 17 00:00:00 2001 From: A <258483684+la14-1@users.noreply.github.com> Date: Sun, 22 Feb 2026 21:11:59 -0800 Subject: [PATCH] fix: explicitly pass SSH identity file for DigitalOcean connections (#1784) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DigitalOcean SSH was failing with "Permission denied (publickey)" because the SSH client was not explicitly told which identity file to use. When users have multiple SSH keys or an SSH agent with different keys loaded, SSH may try the wrong key first and fail — especially with BatchMode=yes which suppresses interactive fallbacks. The fix adds `-i ~/.ssh/id_ed25519` to SSH_OPTS (matching AWS's approach) and passes sshKeyPath to the shared waitForSsh utility, ensuring the correct key is always used for both the handshake wait and all subsequent SSH/SCP commands. Fixes #1783 Agent: code-health Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.5 --- cli/package.json | 2 +- cli/src/digitalocean/digitalocean.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cli/package.json b/cli/package.json index e868103c..7363eff0 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openrouter/spawn", - "version": "0.7.3", + "version": "0.7.4", "type": "module", "bin": { "spawn": "cli.js" diff --git a/cli/src/digitalocean/digitalocean.ts b/cli/src/digitalocean/digitalocean.ts index 494a112d..85271338 100644 --- a/cli/src/digitalocean/digitalocean.ts +++ b/cli/src/digitalocean/digitalocean.ts @@ -841,7 +841,8 @@ async function waitForDropletActive(dropletId: string, maxAttempts = 60): Promis // ─── SSH Execution ─────────────────────────────────────────────────────────── -const SSH_OPTS = SSH_BASE_OPTS; +const SSH_KEY_PATH = `${process.env.HOME}/.ssh/id_ed25519`; +const SSH_OPTS = [...SSH_BASE_OPTS, "-i", SSH_KEY_PATH]; export async function waitForCloudInit(ip?: string, _maxAttempts = 60): Promise { const serverIp = ip || doServerIp; @@ -849,6 +850,7 @@ export async function waitForCloudInit(ip?: string, _maxAttempts = 60): Promise< host: serverIp, user: "root", maxAttempts: 36, + sshKeyPath: SSH_KEY_PATH, }); // Stream cloud-init output so the user sees progress in real time