fix: reduce SSH interactive lag (GSSAPIAuthentication + TCPKeepAlive) (#1795)

* fix: reduce SSH interactive lag with GSSAPIAuthentication=no and TCPKeepAlive=no

GSSAPIAuthentication causes latency on every SSH interaction when
the server doesn't support Kerberos (i.e. always for our VMs).
TCPKeepAlive is redundant with ServerAliveInterval and can cause
retransmission issues through NAT/firewalls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use SSH_INTERACTIVE_OPTS for all interactive sessions

The reconnect (cmdConnect) and agent launch (cmdEnterAgent) paths
were using bare SSH with only StrictHostKeyChecking, missing all
performance flags. Now they use SSH_INTERACTIVE_OPTS which includes:

- GSSAPIAuthentication=no (skip Kerberos timeout)
- TCPKeepAlive=no (avoid NAT retransmission issues)
- ServerAliveInterval=15 (encrypted keepalives)
- Compression=yes (reduce latency on slow/distant links)
- IPQoS=lowdelay (mark packets for low-latency treatment)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
A 2026-02-23 00:20:49 -08:00 committed by GitHub
parent 61fc36c557
commit 180b19d9f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 43 additions and 7 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openrouter/spawn",
"version": "0.7.9",
"version": "0.7.10",
"type": "module",
"bin": {
"spawn": "cli.js"

View file

@ -53,6 +53,7 @@ import {
import { destroyServer as awsDestroyServer, ensureAwsCli, authenticate as awsAuthenticate } from "./aws/aws.js";
import { destroyServer as daytonaDestroyServer, ensureDaytonaToken } from "./daytona/daytona.js";
import { destroyServer as spriteDestroyServer, ensureSpriteCli, ensureSpriteAuthenticated } from "./sprite/sprite.js";
import { SSH_INTERACTIVE_OPTS } from "./shared/ssh.js";
// ── Schemas ──────────────────────────────────────────────────────────────────
@ -2758,13 +2759,12 @@ async function cmdConnect(connection: VMConnection): Promise<void> {
// Handle SSH connections
p.log.step(`Connecting to ${pc.bold(connection.ip)}...`);
const sshCmd = `ssh -o StrictHostKeyChecking=accept-new ${connection.user}@${connection.ip}`;
const sshCmd = `ssh ${connection.user}@${connection.ip}`;
return runInteractiveCommand(
"ssh",
[
"-o",
"StrictHostKeyChecking=accept-new",
...SSH_INTERACTIVE_OPTS,
`${connection.user}@${connection.ip}`,
],
"SSH connection failed",
@ -2874,9 +2874,7 @@ async function cmdEnterAgent(connection: VMConnection, agentKey: string, manifes
return runInteractiveCommand(
"ssh",
[
"-t",
"-o",
"StrictHostKeyChecking=accept-new",
...SSH_INTERACTIVE_OPTS,
`${connection.user}@${connection.ip}`,
"--",
`bash -lc '${remoteCmd}'`,

View file

@ -20,9 +20,47 @@ export const SSH_BASE_OPTS: string[] = [
"-o",
"ServerAliveCountMax=3",
"-o",
"GSSAPIAuthentication=no",
"-o",
"TCPKeepAlive=no",
"-o",
"BatchMode=yes",
];
/**
* SSH options for interactive sessions (user-facing TTY).
*
* Differences from SSH_BASE_OPTS:
* - No BatchMode (interactive sessions need TTY prompts to work)
* - StrictHostKeyChecking=accept-new instead of =no (safer for reconnects)
* - Compression=yes (reduces latency on slow/distant links)
* - IPQoS=lowdelay (mark packets for low-latency QoS treatment)
* - RequestTTY=yes (force TTY allocation for the session)
*/
export const SSH_INTERACTIVE_OPTS: string[] = [
"-o",
"StrictHostKeyChecking=accept-new",
"-o",
"UserKnownHostsFile=/dev/null",
"-o",
"LogLevel=ERROR",
"-o",
"ConnectTimeout=10",
"-o",
"ServerAliveInterval=15",
"-o",
"ServerAliveCountMax=3",
"-o",
"GSSAPIAuthentication=no",
"-o",
"TCPKeepAlive=no",
"-o",
"Compression=yes",
"-o",
"IPQoS=lowdelay",
"-t",
];
// ─── Helpers ─────────────────────────────────────────────────────────────────
/** Async sleep — shared across all cloud providers. */