Standard library · Network

ssh

SSH connections, running a command on a server, and working with files over SFTP.

import ssh

This package is an SSH client written in Salam and needs neither OpenSSH nor any other library. Make a Config with the host and the user, pick the login method, and connect with Connect; then send a command with Exec, or move files with SftpOpen. It suits automating deployments and backups.

The host key is checked against the known_hosts file, the way ssh itself does. The buffer and reader functions build and read the data format of the SSH protocol; an ordinary program does not need them.

Types

Config

struct Config
The configuration of an SSH connection.
NameTypeDescription
hoststr = ""The host.
portint = 22The port; 22 by default.
userstr = ""The user name.
passwordstr = ""The password.
use_passwordbool = falseLog in with a password.
key_pemstr = ""The private key as text.
use_keybool = falseLog in with a key.
hostkey_modeint = 1The host key check mode.
known_hosts_pathstr = ""The path of the known_hosts file.
ssh-Config.salam
import ssh

func main:
    mut cfg := ssh.NewConfig("example.com", ssh.DefaultPort, "deploy")
    println cfg.host, cfg.port, cfg.user, cfg.hostkey_mode == ssh.HOSTKEY_MODE_ACCEPT_NEW
    ssh.WithPassword(cfg, "s3cret")
    println cfg.use_password, cfg.use_key
    ssh.WithKeyPEM(cfg, "-----BEGIN OPENSSH KEY-----")
    println cfg.use_key, cfg.key_pem.len() > 0
    println ssh.WithKeyFile(cfg, "/nonexistent/id_rsa")
    ssh.WithHostKeyMode(cfg, ssh.HOSTKEY_MODE_STRICT)
    ssh.WithKnownHostsPath(cfg, "/home/deploy/.ssh/known_hosts")
    println cfg.hostkey_mode, cfg.known_hosts_path
    println ssh.HOSTKEY_MODE_INSECURE, ssh.ClientVersion
end
Outputexample.com 22 deploy true true false true true false 2 /home/deploy/.ssh/known_hosts 0 SSH-2.0-Salam_1.0

Session

struct Session
One session over the connection: one command or one shell.
NameTypeDescription
chChannelThe SSH channel.
okbool = falseThe session opened.
ssh-Exec.salam
import ssh

func main:
    mut cfg := ssh.NewConfig("server.example.com", 22, "deploy")
    _ := ssh.WithKeyFile(cfg, "/home/deploy/.ssh/id_rsa")
    mut client := ssh.Connect(cfg)
    if not ssh.Ok(client):
        println "error:", ssh.Error(client)
        ret
    end
    println "host key:", ssh.HostKeyFingerprint(client)

    r := ssh.Exec(client, "uname -a")
    println r.ok, r.exit_code, r.stdout, r.stderr
    r2 := ssh.RunCommand(client, "df -h /")
    println r2.stdout

    mut tail := ssh.NewSession(client)
    _ := ssh.RequestPty(tail, client, "xterm", 120, 40)
    if ssh.Start(tail, client, "tail -f /var/log/app.log"):
        repeat 10:
            _ := ssh.ReadStep(tail, client)
            print ssh.DrainOutput(tail)
            print ssh.DrainStderrOutput(tail)
        end
    end
    ssh.CloseSession(tail, client)

    mut shell := ssh.NewSession(client)
    if ssh.Shell(shell, client):
        _ := ssh.WriteInput(shell, client, "echo hello\nexit\n")
        ssh.CloseInput(shell, client)
        code := ssh.Wait(shell, client)
        println code, ssh.ExitCode(shell), ssh.SessionClosed(shell)
        println ssh.Output(shell), ssh.StderrOutput(shell)
    end
    ssh.CloseSession(shell, client)

    probe := ssh.Dial(cfg)
    println ssh.Ok(probe)
    println ssh.Reconnect(client)
    println ssh.SelfTest()
    ssh.Close(client)
end

ExecResult

struct ExecResult
The result of running one command.
NameTypeDescription
stdoutstr = ""The output.
stderrstr = ""The error output.
exit_codeint = -1The exit code.
okbool = falseThe command ran.

The example for this item is shown with the ones above: ssh-Exec.salam

Sftp

struct Sftp
An SFTP session for working with the files on the server.
NameTypeDescription
chChannelThe channel.
okbool = falseIt opened.
next_idu32 = 1The number of the next request.
ssh-Sftp.salam
import ssh

func main:
    mut cfg := ssh.NewConfig("files.example.com", 22, "ali")
    ssh.WithPassword(cfg, "s3cret")
    mut client := ssh.Connect(cfg)
    mut sftp := ssh.SftpOpen(client)
    if not sftp.ok:
        println ssh.Error(client)
        ret
    end
    println ssh.MkdirAll(client, sftp, "/backup/2026/09")
    println ssh.Upload(client, sftp, "report.pdf", "/backup/2026/09/report.pdf")
    println ssh.UploadDir(client, sftp, "site", "/var/www/site")
    if ssh.Exists(client, sftp, "/backup/2026/09/report.pdf"):
        info := ssh.Stat(client, sftp, "/backup/2026/09/report.pdf")
        println info.name, info.size, info.is_dir, info.permissions
    end
    each info in ssh.ListDir(client, sftp, "/backup"):
        println info.name, info.is_dir
    end
    println ssh.IsLink(client, sftp, "/current"), ssh.Lstat(client, sftp, "/current").name
    _ := ssh.Download(client, sftp, "/etc/hostname", "hostname.txt")
    _ := ssh.DownloadDir(client, sftp, "/var/log/app", "logs")
    _ := ssh.Rename(client, sftp, "/tmp/a", "/tmp/b")
    _ := ssh.Remove(client, sftp, "/tmp/b")
    _ := ssh.Mkdir(client, sftp, "/tmp/x")
    _ := ssh.Rmdir(client, sftp, "/tmp/x")
    _ := ssh.RemoveAll(client, sftp, "/tmp/old")
    ssh.SftpClose(client, sftp)
    ssh.Close(client)
end

SftpFileInfo

struct SftpFileInfo
The information about one file.
NameTypeDescription
namestr = ""The name.
sizei64 = 0The size in bytes.
is_dirbool = falseIt is a directory.
permissionsu32 = 0The permissions as a Unix number.

The example for this item is shown with the ones above: ssh-Sftp.salam

Client

struct Client
One SSH connection.
NameTypeDescription
transportTransportThe encrypted layer.
cfgConfigThe configuration.
connectedbool = falseIt is connected.
errmsgstr = ""The error message.

The example for this item is shown with the ones above: ssh-Exec.salam

Constants and variables

DefaultPort

const DefaultPort: i32 = 22
The default SSH port: 22.

The example for this item is shown with the ones above: ssh-Config.salam

ClientVersion

const ClientVersion: str = "SSH-2.0-Salam_1.0"
The version string the client sends at the start of the connection.

The example for this item is shown with the ones above: ssh-Config.salam

HOSTKEY_MODE_INSECURE

const HOSTKEY_MODE_INSECURE: i32 = 0
Host key mode: do not check it; the connection is then open to a man in the middle attack.

The example for this item is shown with the ones above: ssh-Config.salam

HOSTKEY_MODE_ACCEPT_NEW

const HOSTKEY_MODE_ACCEPT_NEW: i32 = 1
Host key mode: accept a new host and record it in known_hosts, but reject a changed key; the default.

The example for this item is shown with the ones above: ssh-Config.salam

HOSTKEY_MODE_STRICT

const HOSTKEY_MODE_STRICT: i32 = 2
Host key mode: only hosts already in known_hosts.

The example for this item is shown with the ones above: ssh-Config.salam

Functions

NewConfig

func NewConfig(host: str, port: int, user: str): Config
A new configuration for a host, port and user.

The example for this item is shown with the ones above: ssh-Config.salam

WithPassword

func WithPassword(cfg &: Config, password: str)
Sets up login with a password.

The example for this item is shown with the ones above: ssh-Config.salam

WithKeyFile

func WithKeyFile(cfg &: Config, path: str): bool
Reads the private key from a file; false when it could not be read.

The example for this item is shown with the ones above: ssh-Config.salam

WithKeyPEM

func WithKeyPEM(cfg &: Config, pem: str)
Takes the private key from text.

The example for this item is shown with the ones above: ssh-Config.salam

WithHostKeyMode

func WithHostKeyMode(cfg &: Config, mode: int)
Sets the host key check mode.

The example for this item is shown with the ones above: ssh-Config.salam

WithKnownHostsPath

func WithKnownHostsPath(cfg &: Config, path: str)
Uses another known_hosts file.

The example for this item is shown with the ones above: ssh-Config.salam

SelfTest

func SelfTest(): int
Runs the internal tests of the package; returns the number of failures.

The example for this item is shown with the ones above: ssh-Exec.salam

NewSession

func NewSession(client &: Client): Session
Opens a new session.

The example for this item is shown with the ones above: ssh-Exec.salam

RequestPty

func RequestPty(session &: Session, client &: Client, term: str, cols: int, rows: int): bool
Asks for a pseudo terminal of the given type and size; for commands that need a terminal.

The example for this item is shown with the ones above: ssh-Exec.salam

Start

func Start(session &: Session, client &: Client, cmd: str): bool
Starts a command in the session and does not wait.

The example for this item is shown with the ones above: ssh-Exec.salam

Shell

func Shell(session &: Session, client &: Client): bool
Starts the shell of the user.

The example for this item is shown with the ones above: ssh-Exec.salam

Wait

func Wait(session &: Session, client &: Client): int
Waits for the command to finish and returns the exit code.

The example for this item is shown with the ones above: ssh-Exec.salam

ReadStep

func ReadStep(session &: Session, client &: Client): bool
Reads whatever data has arrived, once; for reading the output as it comes.

The example for this item is shown with the ones above: ssh-Exec.salam

Output

func Output(session: Session): str
All the output so far.

The example for this item is shown with the ones above: ssh-Exec.salam

StderrOutput

func StderrOutput(session: Session): str
All the error output so far.

The example for this item is shown with the ones above: ssh-Exec.salam

DrainOutput

func DrainOutput(session &: Session): str
Returns the new output and takes it out of the buffer.

The example for this item is shown with the ones above: ssh-Exec.salam

DrainStderrOutput

func DrainStderrOutput(session &: Session): str
Returns the new error output and takes it out.

The example for this item is shown with the ones above: ssh-Exec.salam

ExitCode

func ExitCode(session: Session): int
The exit code; -1 when it has not finished yet.

The example for this item is shown with the ones above: ssh-Exec.salam

SessionClosed

func SessionClosed(session: Session): bool
The session has been closed.

The example for this item is shown with the ones above: ssh-Exec.salam

WriteInput

func WriteInput(session &: Session, client &: Client, data: str): bool
Writes to the input of the command.

The example for this item is shown with the ones above: ssh-Exec.salam

CloseInput

func CloseInput(session &: Session, client &: Client)
Closes the input, so the command knows the data has ended.

The example for this item is shown with the ones above: ssh-Exec.salam

CloseSession

func CloseSession(session &: Session, client &: Client)
Closes the session.

The example for this item is shown with the ones above: ssh-Exec.salam

Exec

func Exec(client &: Client, cmd: str): ExecResult
Runs a command and waits for it to finish.

The example for this item is shown with the ones above: ssh-Exec.salam

SftpOpen

func SftpOpen(client &: Client): Sftp
Opens the SFTP subsystem over the connection.

The example for this item is shown with the ones above: ssh-Sftp.salam

SftpClose

func SftpClose(client &: Client, sftp &: Sftp)
Closes SFTP.

The example for this item is shown with the ones above: ssh-Sftp.salam

Upload

func Upload(client &: Client, sftp &: Sftp, local_path: str, remote_path: str): bool
Uploads a local file.

The example for this item is shown with the ones above: ssh-Sftp.salam

Download

func Download(client &: Client, sftp &: Sftp, remote_path: str, local_path: str): bool
Downloads a file from the server.

The example for this item is shown with the ones above: ssh-Sftp.salam

Mkdir

func Mkdir(client &: Client, sftp &: Sftp, path: str): bool
Makes a directory.

The example for this item is shown with the ones above: ssh-Sftp.salam

Remove

func Remove(client &: Client, sftp &: Sftp, path: str): bool
Deletes a file.

The example for this item is shown with the ones above: ssh-Sftp.salam

Rmdir

func Rmdir(client &: Client, sftp &: Sftp, path: str): bool
Deletes an empty directory.

The example for this item is shown with the ones above: ssh-Sftp.salam

Rename

func Rename(client &: Client, sftp &: Sftp, old_path: str, new_path: str): bool
Renames or moves.

The example for this item is shown with the ones above: ssh-Sftp.salam

Stat

func Stat(client &: Client, sftp &: Sftp, path: str): SftpFileInfo
The information about a file; it follows links.

The example for this item is shown with the ones above: ssh-Sftp.salam

Exists

func Exists(client &: Client, sftp &: Sftp, path: str): bool
The file or directory is there.

The example for this item is shown with the ones above: ssh-Sftp.salam

ListDir

func ListDir(client &: Client, sftp &: Sftp, path: str): Vector<SftpFileInfo>
The contents of a directory, without . and ...

The example for this item is shown with the ones above: ssh-Sftp.salam

MkdirAll

func MkdirAll(client &: Client, sftp &: Sftp, path: str): bool
Makes the directory and every directory above it.

The example for this item is shown with the ones above: ssh-Sftp.salam

RemoveAll

func RemoveAll(client &: Client, sftp &: Sftp, path: str): bool
Deletes the directory and everything in it.

The example for this item is shown with the ones above: ssh-Sftp.salam

DownloadDir

func DownloadDir(client &: Client, sftp &: Sftp, remote_path: str, local_path: str): bool
Downloads a directory with everything in it.

The example for this item is shown with the ones above: ssh-Sftp.salam

UploadDir

func UploadDir(client &: Client, sftp &: Sftp, local_path: str, remote_path: str): bool
Uploads a local directory with everything in it.

The example for this item is shown with the ones above: ssh-Sftp.salam

Lstat

func Lstat(client &: Client, sftp &: Sftp, path: str): SftpFileInfo
The information about the link itself, without following it.

The example for this item is shown with the ones above: ssh-Sftp.salam

func IsLink(client &: Client, sftp &: Sftp, path: str): bool
It is a symbolic link.

The example for this item is shown with the ones above: ssh-Sftp.salam

NewBuf

func NewBuf(cap0: int): Buf
An empty buffer for building SSH protocol messages.
ssh-Wire.salam
import ssh

func main:
    mut b := ssh.NewBuf(64)
    ssh.BufPutByte(b, 20)
    ssh.BufPutUint32(b, 7 as u32)
    ssh.BufPutUint64(b, 9 as u64)
    ssh.BufPutBool(b, true)
    ssh.BufPutStringStr(b, "ssh-ed25519,rsa-sha2-256")
    ssh.BufPutString(b, "abc" as void*, 3)
    ssh.BufPutBytes(b, "xy" as void*, 2)
    ssh.BufPutZeros(b, 2)
    ssh.BufPutMpintBytes(b, "\x80" as void*, 1)
    println b.len

    mut r := ssh.ReaderFromBuf(b)
    println ssh.RGetByte(r), ssh.RGetUint32(r), ssh.RGetUint64(r), ssh.RGetBool(r)
    names := ssh.RGetNameList(r)
    println names, ssh.NameListContains(names, "rsa-sha2-256")
    println ssh.RGetStringAsStr(r)
    ssh.RSkip(r, 4)
    println ssh.RRemaining(r)
    n := ssh.RGetMpintView(r)
    println n.len, ssh.RRemaining(r)

    println ssh.NegotiateFirstMatch("aes256-ctr,aes128-ctr", "aes128-ctr,aes256-ctr")
    println ssh.ConstantTimeEqual("abcd" as void*, "abcd" as void*, 4), ssh.ConstantTimeEqual("abcd" as void*, "abce" as void*, 4)

    mut b2 := ssh.NewBuf(16)
    ssh.BufPutStringBuf(b2, b)
    ssh.BufPutMpintBuf(b2, b)
    ssh.BufPutBuf(b2, b)
    mut r2 := ssh.NewReader(b2.p, b2.len)
    view := ssh.RGetStringView(r2)
    println view.len == b.len
    mut copied := ssh.RGetStringCopy(ssh.ReaderFromBuf(b2))
    mut number := ssh.RGetMpintCopy(r2)
    println copied.len, number.len, ssh.RRemaining(r2)
    ssh.WipeBuf(b)
    ssh.FreeBuf(b)
    ssh.FreeBuf(b2)
    ssh.FreeBuf(copied)
    ssh.FreeBuf(number)
end
Output59 20 7 9 true ssh-ed25519,rsa-sha2-256 true abc 6 1 0 aes256-ctr true false true 59 59 59

FreeBuf

func FreeBuf(b &: Buf)
Frees the buffer.

The example for this item is shown with the ones above: ssh-Wire.salam

WipeBuf

func WipeBuf(b &: Buf)
Zeroes the contents of the buffer.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutByte

func BufPutByte(b &: Buf, v: int)
Adds one byte.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutBytes

func BufPutBytes(b &: Buf, p: void*, n: int)
Adds bytes from memory.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutBuf

func BufPutBuf(b &: Buf, src: Buf)
Adds the contents of another buffer.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutZeros

func BufPutZeros(b &: Buf, n: int)
Adds n zero bytes.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutUint32

func BufPutUint32(b &: Buf, v: u32)
A 32 bit big-endian number.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutUint64

func BufPutUint64(b &: Buf, v: u64)
A 64 bit big-endian number.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutBool

func BufPutBool(b &: Buf, v: bool)
A boolean as one byte.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutString

func BufPutString(b &: Buf, p: void*, n: int)
An SSH string: a 4 byte length and then the bytes.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutStringBuf

func BufPutStringBuf(b &: Buf, src: Buf)
Adds a buffer as an SSH string.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutStringStr

func BufPutStringStr(b &: Buf, s: str)
Adds text as an SSH string.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutMpintBytes

func BufPutMpintBytes(b &: Buf, p: void*, n: int)
A big number, mpint, from big-endian bytes.

The example for this item is shown with the ones above: ssh-Wire.salam

BufPutMpintBuf

func BufPutMpintBuf(b &: Buf, src: Buf)
An mpint from a buffer.

The example for this item is shown with the ones above: ssh-Wire.salam

NewReader

func NewReader(p: void*, n: int): Reader
A reader over memory.

The example for this item is shown with the ones above: ssh-Wire.salam

ReaderFromBuf

func ReaderFromBuf(b: Buf): Reader
A reader over a buffer.

The example for this item is shown with the ones above: ssh-Wire.salam

RRemaining

func RRemaining(r: Reader): int
The bytes not yet read.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetByte

func RGetByte(r &: Reader): int
Reads one byte.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetBool

func RGetBool(r &: Reader): bool
Reads a boolean.

The example for this item is shown with the ones above: ssh-Wire.salam

RSkip

func RSkip(r &: Reader, n: int)
Skips n bytes.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetUint32

func RGetUint32(r &: Reader): u32
Reads a 32 bit number.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetUint64

func RGetUint64(r &: Reader): u64
Reads a 64 bit number.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetStringView

func RGetStringView(r &: Reader): Reader
An SSH string without copying, as a reader over the same memory.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetStringCopy

func RGetStringCopy(r &: Reader): Buf
Copies an SSH string into a new buffer.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetStringAsStr

func RGetStringAsStr(r &: Reader): str
An SSH string as text.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetMpintView

func RGetMpintView(r &: Reader): Reader
An mpint without copying and without the leading zero.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetMpintCopy

func RGetMpintCopy(r &: Reader): Buf
Copies an mpint.

The example for this item is shown with the ones above: ssh-Wire.salam

RGetNameList

func RGetNameList(r &: Reader): str
A list of names, such as algorithms, separated by commas.

The example for this item is shown with the ones above: ssh-Wire.salam

NameListContains

func NameListContains(list: str, name: str): bool
The name list holds this name.

The example for this item is shown with the ones above: ssh-Wire.salam

NegotiateFirstMatch

func NegotiateFirstMatch(mine: str, theirs: str): str
The first name in your own list that the other side also has; the algorithm negotiation rule of SSH.

The example for this item is shown with the ones above: ssh-Wire.salam

ConstantTimeEqual

func ConstantTimeEqual(a: void*, b: void*, n: int): bool
Compares two pieces of memory in constant time.

The example for this item is shown with the ones above: ssh-Wire.salam

Dial

func Dial(cfg: Config): Client
Connects to the server and checks the host key, without logging in.

The example for this item is shown with the ones above: ssh-Exec.salam

Connect

func Connect(cfg: Config): Client
Connects and logs in.

The example for this item is shown with the ones above: ssh-Exec.salam

Ok

func Ok(client: Client): bool
The connection and login succeeded.

The example for this item is shown with the ones above: ssh-Exec.salam

Error

func Error(client: Client): str
The error message.

The example for this item is shown with the ones above: ssh-Exec.salam

HostKeyFingerprint

func HostKeyFingerprint(client: Client): str
The SHA-256 fingerprint of the host key, the one ssh shows.

The example for this item is shown with the ones above: ssh-Exec.salam

Close

func Close(client &: Client)
Closes the connection.

The example for this item is shown with the ones above: ssh-Exec.salam

Reconnect

func Reconnect(client &: Client): bool
Connects again with the same configuration.

The example for this item is shown with the ones above: ssh-Exec.salam

RunCommand

func RunCommand(client &: Client, cmd: str): ExecResult
The same as Exec.

The example for this item is shown with the ones above: ssh-Exec.salam