Skip to content

Commit f256f54

Browse files
fix(web): use %USERPROFILE% (not $HOME) in generated Windows ssh commands
The wizard generated Windows ssh commands with a PowerShell-style $HOME in the identity-file path (e.g. `ssh -i $HOME\.ssh\acfs_ed25519 ...`). In the Windows Terminal profile `commandline` context (and cmd.exe), $HOME is not expanded before ssh launches, so OpenSSH gets the literal path, can't find the key, and falls back to password auth: Warning: Identity file $HOME\.ssh\acfs_ed25519 not accessible: ... Switched to %USERPROFILE%, which Windows expands in these contexts (and which the windows-terminal-setup page already uses for the starting directory). Fixed at the shared source (commandBuilder SSH_KEY_PATH_WINDOWS) plus the per-page hardcoded literals across the wizard and the troubleshooting snippets, and updated the commandBuilder unit test. Left the genuine PowerShell snippets in generate-ssh-key (Test-Path/New-Item/ ssh-keygen) on $HOME, where interactive PowerShell does expand it correctly. Closes #302 Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 462eba1 commit f256f54

8 files changed

Lines changed: 21 additions & 13 deletions

File tree

apps/web/app/wizard/launch-onboarding/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ export default function LaunchOnboardingStep() {
323323
<h3 className="font-medium">Connect to your VPS</h3>
324324
<CommandCard
325325
command={`ssh -i ~/.ssh/acfs_ed25519 ${userTarget}`}
326-
windowsCommand={`ssh -i $HOME\\.ssh\\acfs_ed25519 ${userTarget}`}
326+
windowsCommand={`ssh -i %USERPROFILE%\\.ssh\\acfs_ed25519 ${userTarget}`}
327327
runLocation="local"
328328
/>
329329
<p className="text-sm text-muted-foreground">Open your terminal and SSH in.</p>
@@ -608,7 +608,7 @@ export default function LaunchOnboardingStep() {
608608
<h3 className="font-medium">2. Connect to your VPS</h3>
609609
<CommandCard
610610
command={`ssh -i ~/.ssh/acfs_ed25519 ${userTarget}`}
611-
windowsCommand={`ssh -i $HOME\\.ssh\\acfs_ed25519 ${userTarget}`}
611+
windowsCommand={`ssh -i %USERPROFILE%\\.ssh\\acfs_ed25519 ${userTarget}`}
612612
runLocation="local"
613613
/>
614614
</div>

apps/web/app/wizard/reconnect-ubuntu/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export default function ReconnectUbuntuPage() {
6969
const userTarget = formatSshTarget(effectiveUsername, vpsIP);
7070
const userPrompt = `${effectiveUsername}@`;
7171
const sshCommand = `ssh -i ~/.ssh/acfs_ed25519 ${userTarget}`;
72-
const sshCommandWindows = `ssh -i $HOME\\.ssh\\acfs_ed25519 ${userTarget}`;
72+
const sshCommandWindows = `ssh -i %USERPROFILE%\\.ssh\\acfs_ed25519 ${userTarget}`;
7373
const userKeyRepairCommand = buildUserKeyRepairCommand(effectiveUsername, vpsIP);
7474
const rootKeyRepairCommand = buildRootKeyRepairCommand(effectiveUsername, vpsIP);
7575

@@ -143,7 +143,7 @@ export default function ReconnectUbuntuPage() {
143143
</code>{" "}
144144
part — or on Windows{" "}
145145
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">
146-
-i $HOME\\.ssh\\acfs_ed25519
146+
-i %USERPROFILE%\\.ssh\\acfs_ed25519
147147
</code>
148148
) instead of a password. The installer set this up for you.
149149
</p>

apps/web/app/wizard/status-check/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ export default function StatusCheckPage() {
146146
const effectiveSSHUsername = sshUsername.trim() || "ubuntu";
147147
const reconnectTarget = formatSshTarget(effectiveSSHUsername, effectiveVpsIP);
148148
const reconnectCommand = `ssh -i ~/.ssh/acfs_ed25519 ${reconnectTarget}`;
149-
const reconnectWindowsCommand = `ssh -i $HOME\\.ssh\\acfs_ed25519 ${reconnectTarget}`;
149+
const reconnectWindowsCommand = `ssh -i %USERPROFILE%\\.ssh\\acfs_ed25519 ${reconnectTarget}`;
150150
const codexTunnelCommand = `ssh -i ~/.ssh/acfs_ed25519 -L 1455:localhost:1455 ${reconnectTarget}`;
151-
const codexTunnelWindowsCommand = `ssh -i $HOME\\.ssh\\acfs_ed25519 -L 1455:localhost:1455 ${reconnectTarget}`;
151+
const codexTunnelWindowsCommand = `ssh -i %USERPROFILE%\\.ssh\\acfs_ed25519 -L 1455:localhost:1455 ${reconnectTarget}`;
152152
const reinstallCommand = buildInstallCommand(
153153
installModeLoaded ? installMode : "vibe",
154154
acfsRefLoaded ? acfsRef : null,

apps/web/app/wizard/verify-key-connection/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export default function VerifyKeyConnectionPage() {
6363
const userTarget = formatSshTarget(effectiveUsername, vpsIP);
6464
const userPrompt = `${effectiveUsername}@`;
6565
const sshKeyCommand = `ssh -i ~/.ssh/acfs_ed25519 ${userTarget}`;
66-
const sshKeyCommandWindows = `ssh -i $HOME\\.ssh\\acfs_ed25519 ${userTarget}`;
66+
const sshKeyCommandWindows = `ssh -i %USERPROFILE%\\.ssh\\acfs_ed25519 ${userTarget}`;
6767
const userKeyRepairCommand = buildUserKeyRepairCommand(effectiveUsername, vpsIP);
6868
const rootKeyRepairCommand = buildRootKeyRepairCommand(effectiveUsername, vpsIP);
6969

apps/web/app/wizard/windows-terminal-setup/page.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,11 @@ function WindowsTerminalSetupContent() {
9898
const displayIP = vpsIP || "YOUR_VPS_IP";
9999
const effectiveUsername = sshUsername.trim() || "ubuntu";
100100
const ubuntuTarget = formatSshTarget(effectiveUsername, displayIP);
101-
const sshCommandLine = `ssh -i $HOME\\.ssh\\acfs_ed25519 ${ubuntuTarget}`;
101+
// Use %USERPROFILE% (not $HOME): the Windows Terminal profile `commandline`
102+
// context does not expand PowerShell's $HOME before launching ssh, so the key
103+
// path would be passed literally and OpenSSH would fall back to password auth
104+
// (#302). %USERPROFILE% is expanded by Windows in this context.
105+
const sshCommandLine = `ssh -i %USERPROFILE%\\.ssh\\acfs_ed25519 ${ubuntuTarget}`;
102106

103107
const handleCopy = useCallback(async () => {
104108
const copiedOk = await copyTextToClipboard(sshCommandLine);
@@ -332,7 +336,7 @@ function WindowsTerminalSetupContent() {
332336
<p className="text-sm text-muted-foreground">
333337
Make sure your SSH key file exists at{" "}
334338
<code className="rounded bg-muted px-1 py-0.5 font-mono text-xs">
335-
$HOME\.ssh\acfs_ed25519
339+
%USERPROFILE%\.ssh\acfs_ed25519
336340
</code>
337341
. If you used a different key name, update the command line accordingly.
338342
</p>
@@ -348,7 +352,7 @@ function WindowsTerminalSetupContent() {
348352
<p className="text-sm text-muted-foreground">
349353
This can happen if you rebuilt your VPS. You may need to remove the old key from{" "}
350354
<code className="rounded bg-muted px-1 py-0.5 font-mono text-xs">
351-
$HOME\.ssh\known_hosts
355+
%USERPROFILE%\.ssh\known_hosts
352356
</code>
353357
.
354358
</p>

apps/web/components/connection-check.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function ConnectionCheck({
3939
? `ssh -i ~/.ssh/acfs_ed25519 ${sshTarget}`
4040
: `ssh ${sshTarget}`;
4141
const windowsSshCommand = useIdentityFile
42-
? `ssh -i $HOME\\.ssh\\acfs_ed25519 ${sshTarget}`
42+
? `ssh -i %USERPROFILE%\\.ssh\\acfs_ed25519 ${sshTarget}`
4343
: `ssh ${sshTarget}`;
4444

4545
return (

apps/web/lib/commandBuilder.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ describe("buildCommands", () => {
182182

183183
expect(installer?.command).toContain('TARGET_USER="admin"');
184184
expect(sshUser?.command).toContain("admin@10.20.30.40");
185-
expect(sshUser?.windowsCommand).toBe("ssh -i $HOME\\.ssh\\acfs_ed25519 admin@10.20.30.40");
185+
expect(sshUser?.windowsCommand).toBe("ssh -i %USERPROFILE%\\.ssh\\acfs_ed25519 admin@10.20.30.40");
186186
});
187187

188188
test("falls back to ubuntu when the username input is invalid", () => {

apps/web/lib/commandBuilder.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ const INSTALL_SCRIPT_BASE_URL =
2020
"https://raw.githubusercontent.com/Dicklesworthstone/agentic_coding_flywheel_setup";
2121
const DEFAULT_INSTALL_REF = "main";
2222
const SSH_KEY_PATH_UNIX = "~/.ssh/acfs_ed25519";
23-
const SSH_KEY_PATH_WINDOWS = "$HOME\\.ssh\\acfs_ed25519";
23+
// Use %USERPROFILE% rather than PowerShell's $HOME: the Windows Terminal profile
24+
// `commandline` context (and cmd.exe) does not expand $HOME before launching ssh,
25+
// so the key path would be passed literally and OpenSSH would fall back to
26+
// password auth (#302). %USERPROFILE% is expanded by Windows in these contexts.
27+
const SSH_KEY_PATH_WINDOWS = "%USERPROFILE%\\.ssh\\acfs_ed25519";
2428

2529
export interface CommandBuilderInputs {
2630
ip: string;

0 commit comments

Comments
 (0)