Table of Contents

Class SshGameHost

Namespace
Valheim.Testing.GameSessions
Assembly
Valheim.Testing.GameSessions.dll

A machine reached through OpenSSH (ssh from PATH; on Windows the inbox client, or Git for Windows' client when the runner itself is in an SSH session) with key or agent authentication only: BatchMode is always on, so ssh never prompts, and nothing here accepts a password. The destination is user@host, ssh://user@host:port or an ssh-config alias; the host key must already be known (or pass StrictHostKeyChecking=accept-new). A bash host needs a POSIX login shell; a Windows host's pwsh works under any login shell, including Windows' cmd.exe. ssh exit 255 without the host's exit report is a transport failure.

public sealed class SshGameHost : ScriptedGameHost, IGameHost
Inheritance
object
SshGameHost
Implements
Inherited Members

Constructors

SshGameHost(string, string, HostShell, int, IEnumerable<string>?, TimeSpan?, string)

public SshGameHost(string name, string destination, HostShell shell, int port = 0, IEnumerable<string>? sshOptions = null, TimeSpan? connectTimeout = null, string sshExecutable = "ssh")

Parameters

name string
destination string

user@host, ssh://user@host:port or an ssh-config alias. Never a password.

shell HostShell
port int

The ssh port; 0 leaves it to the destination or the ssh config.

sshOptions IEnumerable<string>

Extra -o options as Name=value, for example IdentityFile=/path/key; the value is taken literally and may contain spaces, quotes and backslashes (User=Some Name, IdentityFile=C:\Users\Some Name.ssh\key). ssh reads a -o value like a config line, so such a value is passed as Name="value", escaped the way ssh reads it. The value must not be empty or contain a control character such as a tab or a line break. The space-separated Name value form is not accepted. A command (ProxyCommand=ssh -W %h:%p jump) is passed as written: ssh runs it with the user's shell. RemoteCommand is refused.

connectTimeout TimeSpan?

ssh's ConnectTimeout; 10 s by default.

sshExecutable string

The OpenSSH client. The default is ssh from PATH, which on Windows is the inbox client and reaches keys held by the Windows ssh-agent service. When the runner itself is inside an SSH session on Windows, the default is instead Git for Windows' matching ssh/scp pair, because the inbox ssh can hang there when a .NET process sends its script through redirected stdin. Git's msys ssh cannot open the named pipe that carries a forwarded agent in a Win32-OpenSSH session, so a host reached from such a session needs a key file (IdentityFile=). Anything but the bare default name, a path above all, is an explicit choice and always wins. On Windows ssh, ssh.exe and any capitalisation of either are the default, so forcing the inbox client from inside an SSH session takes its full path. A run's provenance records which client was chosen and why.

Properties

ConnectTimeout

public TimeSpan ConnectTimeout { get; }

Property Value

TimeSpan

Destination

public string Destination { get; }

Property Value

string

Kind

public override GameHostKind Kind { get; }

Property Value

GameHostKind

Port

public int Port { get; }

Property Value

int

Methods

OpenCliTunnelAsync(int, TimeSpan, int, CancellationToken)

Forwards a local loopback port to the host's loopback-only ValheimCLI port (ssh -N -L 127.0.0.1:local:127.0.0.1:host with ExitOnForwardFailure). localPort 0 picks a free port; a port already in use is refused before ssh starts. Ready means a local connection succeeded within readyTimeout: ssh listens here. Whether the host's port answers is the next check, made through the tunnel. Neither end ever listens on a non-loopback address: ssh would also open the LocalForward, RemoteForward and DynamicForward entries an ssh config gives this host (ClearAllForwardings would clear the tunnel's own forward too), so a host whose effective config (ssh -G) has any is refused.

The free-port check and ssh's own bind are separate steps: another process can take the port in between. ssh then fails to bind and exits (ExitOnForwardFailure), which fails the tunnel, but a readiness probe made before ssh has exited could reach that other listener. Pass a localPort reserved for tests on a shared machine.

public override Task<CliTunnel> OpenCliTunnelAsync(int hostPort, TimeSpan readyTimeout, int localPort = 0, CancellationToken cancellation = default)

Parameters

hostPort int
readyTimeout TimeSpan
localPort int
cancellation CancellationToken

Returns

Task<CliTunnel>