Remote Access/en: Unterschied zwischen den Versionen

Aus expecco Wiki (Version 25.x)
Zur Navigation springen Zur Suche springen
Inhalt gelöscht Inhalt hinzugefügt
Sv (Diskussion | Beiträge)
Die Seite wurde neu angelegt: „ '''Remote access''' is the ability to drive a remote computer or network from this expecco image — opening shells, running commands, moving files, or driving a test target. Three protocol families are supported, listed in current-recommended order: * '''SSH and SFTP''' (recommended) — encrypted shell + secure file transfer over an SSH-2 tunnel. Pure-Smalltalk implementation in <code>exept:libcrypt/ssh</code>; no external dependency on OpenSS…“
 
Sv (Diskussion | Beiträge)
Fold the Automatic-Refresh bullets onto single physical lines
 
(6 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
{{Languages|Remote Access/en|label=English}}


'''Remote access''' is the ability to drive a remote computer or
'''Remote access''' is the ability to drive a remote computer or
Zeile 5: Zeile 6:
supported, listed in current-recommended order:
supported, listed in current-recommended order:


* '''SSH and SFTP''' (recommended) — encrypted shell + secure
* '''SSH and SFTP''' (recommended) — encrypted shell + secure file transfer over an SSH-2 tunnel. Pure-Smalltalk implementation in <code>exept:libcrypt/ssh</code>; no external dependency on OpenSSL or libssh. Use this for anything that touches credentials or sensitive payloads.
* '''Local Command Shell''' — fork + exec on the local machine. Used for local-tool integration and for the local end of a remote workflow that bridges via another protocol.
file transfer over an SSH-2 tunnel. Pure-Smalltalk implementation
* '''Telnet''' (legacy) — plain-text terminal session. No encryption, passwords on the wire in clear. Use only when the target hardware has no other option.
in <code>exept:libcrypt/ssh</code>; no external dependency on
OpenSSL or libssh. Use this for anything that touches credentials
or sensitive payloads.
* '''Local Command Shell''' — fork + exec on the local machine.
Used for local-tool integration and for the local end of a remote
workflow that bridges via another protocol.
* '''Telnet''' (legacy) — plain-text terminal session. No
encryption, passwords on the wire in clear. Use only when the
target hardware has no other option.


= SSH and SFTP =
= SSH and SFTP =
Zeile 25: Zeile 18:


* '''<code>SSH::Client</code>''' — programmatic SSH access (remote
* '''<code>SSH::Client</code>''' — programmatic SSH access (remote
<code>exec</code>, TTY shell, agent forwarding, ProxyJump bastion).
<code>exec</code>, TTY shell, agent forwarding, ProxyJump bastion).
* '''<code>SSH::SftpFilename</code>''' — a <code>Filename</code>
* '''<code>SSH::SftpFilename</code>''' — a <code>Filename</code> subclass that lets the rest of ST/X treat a remote SFTP path the same way it treats a local file.
subclass that lets the rest of ST/X treat a remote SFTP path the
same way it treats a local file.


The rest of this section is organised user-task-first: what the user
The rest of this section is organised user-task-first: what the user
Zeile 52: Zeile 43:
<code>/</code>.
<code>/</code>.


The '''Refresh''' button in the toolbar (the round-arrow icon
The Tools menu offers three SSH-related actions, all gated on the
between ''Forward'' and ''DirectoryUp'') re-reads both the
SSH library being loaded:
directory tree and the contents pane on demand. Works uniformly
for local and SFTP paths; for SFTP it also flushes the per-file
STAT cache, so changes made directly on the remote side become
visible immediately rather than waiting for the 5-second cache TTL
to expire.


The small arrow next to the Refresh icon opens a dropdown with a
* '''Generate SSH Key Pair...''' — opens the same key-generation
single checkbox, '''Automatic Refresh''', controlling the
dialog described under [[#Generating an SSH key pair]] below.
background polling task that walks every expanded tree item to
* '''SSH Connect...''' — opens an interactive VT100 terminal to a
detect external changes. The default depends on the current root:
remote host.

* '''SFTP Connect...''' — points this browser tab at a remote
* Local filesystem &rarr; '''on''' (10-second cycle, matches the long-standing behaviour).
filesystem via SFTP.
* SFTP &rarr; '''off'''. Each cycle costs one STAT round-trip per child, which is fine for a handful of local directories but painful over the network. Click Refresh manually when you need to pick up remote changes.

When you navigate between local and SFTP roots the toggle flips
automatically &mdash; but only if you haven't overridden it for
the previous root. An explicit user choice is preserved across
navigations.

The Tools menu offers four browser actions, three of them gated on
the SSH library being loaded:

* '''Generate SSH Key Pair...''' — opens the same key-generation dialog described under [[#Generating an SSH key pair]] below.
* '''SSH Connect...''' — opens an interactive VT100 terminal to a remote host.
* '''SFTP Connect...''' — points this browser tab at a remote filesystem via SFTP.
* '''Filesystem Info...''' — shows size, free space and usage of the filesystem holding the currently displayed directory. Works uniformly for local paths and SFTP paths; for SFTP it requires the server to advertise the <code>statvfs@openssh.com</code> extension (every modern OpenSSH does). Sizes are reported in IEC binary units (MiB, GiB, TiB) — the largest unit yielding a value ≥ 1 is chosen, so a TB-scale volume reads as ''X TiB'' rather than ''10240 GiB''.


== From expecco actions ==
== From expecco actions ==
Zeile 68: Zeile 78:
actions to the expecco action palette:
actions to the expecco action palette:


* '''CmdShell - Open SSH Remote Connection''' — opens an SSH session
* '''CmdShell - Open SSH Remote Connection''' — opens an SSH session via the platform's <code>ssh</code> binary (PuTTY's
via the platform's <code>ssh</code> binary (PuTTY's
<code>plink</code> on Windows).
* '''CmdShell - Open SSH Remote Connection and PublicKey''' — same but with explicit public-key authentication.
<code>plink</code> on Windows).
* '''CmdShell - Open SSH Remote Connection and PublicKey''' — same
but with explicit public-key authentication.


To run these you need a configured keypair (private key on this
To run these you need a configured keypair (private key on this
Zeile 90: Zeile 98:


* '''Comment''' — embedded in the generated key (defaults to
* '''Comment''' — embedded in the generated key (defaults to
<code>stx@&lt;hostname&gt;</code>).
<code>stx@&lt;hostname&gt;</code>).
* '''Storage''':
* '''Storage''':
** ''Save to disk file only'' — writes <code>~/.ssh/id_ed25519_stx</code>
** ''Save to disk file only'' — writes <code>~/.ssh/id_ed25519_stx</code> (or wherever) plus a matching <code>.pub</code> companion.
** ''Save to disk AND load into ssh-agent'' — writes the file and also hands the key to the running ssh-agent.
(or wherever) plus a matching <code>.pub</code> companion.
** ''Save to disk AND load into ssh-agent'' — writes the file and
** ''Load into ssh-agent only'' — key lives in agent memory only; gone on agent restart.
also hands the key to the running ssh-agent.
** ''Load into ssh-agent only'' — key lives in agent memory only;
gone on agent restart.
* '''Private key file''' — full path; disabled in agent-only mode.
* '''Private key file''' — full path; disabled in agent-only mode.
* '''Passphrase / Confirm''' — empty leaves the on-disk file
* '''Passphrase / Confirm''' — empty leaves the on-disk file unencrypted (agent-only mode ignores the passphrase, since the OpenSSH agent wire protocol carries only the decrypted key).
unencrypted (agent-only mode ignores the passphrase, since the
OpenSSH agent wire protocol carries only the decrypted key).


On '''Generate''', the public-key line (the same
On '''Generate''', the public-key line (the same
Zeile 234: Zeile 237:


# Open '''Services''' (<code>services.msc</code>) as Administrator.
# Open '''Services''' (<code>services.msc</code>) as Administrator.
# Find '''OpenSSH Authentication Agent''', set Startup Type to
# Find '''OpenSSH Authentication Agent''', set Startup Type to '''Automatic''', click '''Start'''.
'''Automatic''', click '''Start'''.
# In PowerShell: <code>ssh-add $HOME\.ssh\id_ed25519</code>.
# In PowerShell: <code>ssh-add $HOME\.ssh\id_ed25519</code>.
# Verify: <code>ssh-add -l</code>.
# Verify: <code>ssh-add -l</code>.
Zeile 283: Zeile 285:
Alternative agents:
Alternative agents:


* '''PuTTY pageant''' — uses its own protocol; NOT supported by
* '''PuTTY pageant''' — uses its own protocol; NOT supported by ST/X's SSH::Agent. Migrate the keys to OpenSSH.
ST/X's SSH::Agent. Migrate the keys to OpenSSH.
* '''Git for Windows ssh-agent''' — works; point
* '''Git for Windows ssh-agent''' — works; point
<code>SSH_AUTH_SOCK</code> at the socket it publishes.
<code>SSH_AUTH_SOCK</code> at the socket it publishes.
* '''WSL 2''' — a ST/X inside WSL sees WSL's Linux agent normally;
* '''WSL 2''' — a ST/X inside WSL sees WSL's Linux agent normally; a ST/X on the Windows side does not. Bridging needs a helper like <code>npiperelay</code> + <code>socat</code>.
a ST/X on the Windows side does not. Bridging needs a helper
like <code>npiperelay</code> + <code>socat</code>.


Verify in the Remote Access settings page
Verify in the Remote Access settings page
Zeile 347: Zeile 346:
<code>SFTP/pool</code>. Right-click a row:
<code>SFTP/pool</code>. Right-click a row:


* '''Copy Waiters Stack to Clipboard''' — dumps the last-owner's
* '''Copy Waiters Stack to Clipboard''' — dumps the last-owner's walkback plus each waiter's, formatted as plain text. Use when a process is wedged in <code>readWait</code> inside
<code>withSftpClientDo:</code> and you need to see which SFTP
walkback plus each waiter's, formatted as plain text. Use when
request it is on.
a process is wedged in <code>readWait</code> inside
* '''Copy List to Clipboard''' — the whole table, for an email-this-to-someone diagnosis.
<code>withSftpClientDo:</code> and you need to see which SFTP
* '''Detect Deadlocks''' — DFS over the wait-for graph, reports cycles.
request it is on.
* '''Copy List to Clipboard''' — the whole table, for an
email-this-to-someone diagnosis.
* '''Detect Deadlocks''' — DFS over the wait-for graph, reports
cycles.


=== Logger ===
=== Logger ===
Zeile 363: Zeile 358:
* <code>warning:</code> on auto-reconnect after a dead connection.
* <code>warning:</code> on auto-reconnect after a dead connection.
* <code>warning:</code> when a pool entry is idle-evicted.
* <code>warning:</code> when a pool entry is idle-evicted.
* <code>warning:</code> when an SSH key file cannot be parsed
* <code>warning:</code> when an SSH key file cannot be parsed (e.g. legacy PEM, encrypted-without-agent) — the file is skipped, others tried.
(e.g. legacy PEM, encrypted-without-agent) — the file is skipped,
others tried.


== Limitations ==
== Limitations ==


* '''SFTP v3 only.''' No SETSTAT (no remote chmod / chown / utime),
* '''SFTP v3 only.''' No SETSTAT (no remote chmod / chown / utime), no SSH_FXP_READLINK exposed (<code>#isSymbolicLink</code> always
<code>false</code>, <code>#linkInfo</code> returns the regular
no SSH_FXP_READLINK exposed (<code>#isSymbolicLink</code> always
stat info). Several SFTPv5+ niceties are nevertheless picked up
<code>false</code>, <code>#linkInfo</code> returns the regular
via OpenSSH SSH_FXP_EXTENDED requests — see
stat info). SFTPv5+ features (atomic-overwrite rename via
[[#OpenSSH SFTP extensions]] below.
<code>SSH_FXF_OVERWRITE</code>) not supported.
* '''Per-host serialisation.''' Two concurrent operations on the
* '''Per-host serialisation.''' Two concurrent operations on the same host queue through the host mutex. See [[#Future work]].
* '''<code>#renameTo:</code> fallback has a TOCTOU window.''' On servers that advertise <code>posix-rename@openssh.com</code> (every modern OpenSSH does), overwrite is atomic; on the rare server that does not, the receiver is emulated as delete-then-rename and another process can race in between.
same host queue through the host mutex. See [[#Future work]].
* '''<code>#isNonEmptyDirectory</code> is heuristic.''' Always returns <code>#isDirectory</code> (the accurate answer would cost three round-trips per directory icon, which made the original tree expansion unbearably slow).
* '''<code>#renameTo:</code> has a TOCTOU window.''' POSIX-style
overwrite is emulated as delete-then-rename; another process can
race in between.
* '''<code>#isNonEmptyDirectory</code> is heuristic.''' Always
returns <code>#isDirectory</code> (the accurate answer would cost
three round-trips per directory icon, which made the original tree
expansion unbearably slow).


== Implementation details ==
== Implementation details ==
Zeile 412: Zeile 399:
heartbeat, SSH_MSG_DISCONNECT.
heartbeat, SSH_MSG_DISCONNECT.
|}
|}

=== OpenSSH SFTP extensions ===

SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal.
OpenSSH ships an open-ended extension mechanism: the server lists
extension names it understands in its <code>SSH_FXP_VERSION</code>
reply, and the client invokes them via
<code>SSH_FXP_EXTENDED(200)</code> packets carrying the extension
name as the first string. Each extension is feature-detected via
<code>SSH::SftpClient&gt;&gt;supportsExtension:</code>; callers fall
back when the server doesn't advertise it.

The stack uses four of the OpenSSH extensions today:

* <code>posix-rename@openssh.com</code> — atomic rename-with-overwrite. Picked up automatically by
<code>SftpFilename&gt;&gt;renameTo:</code>; the delete-then-rename
fallback only fires on servers that lack it.
* <code>hardlink@openssh.com</code> — create a POSIX hard link. Exposed as <code>SftpFilename&gt;&gt;createHardLinkAs:</code>.
* <code>statvfs@openssh.com</code> — POSIX
<code>statvfs(3)</code>-shape filesystem stats. Exposed as
<code>SftpFilename&gt;&gt;fileSystemInfo</code>; the result is
shape-compatible with <code>OperatingSystem getDiskInfoOf:</code>
so callers can treat local and remote uniformly. Drives the
'''Tools &rarr; Filesystem Info...''' menu entry described at the
top of this page.
* <code>fsync@openssh.com</code> — flush server-side write buffer to disk on an open handle. Available on the low-level
<code>SftpClient&gt;&gt;fsyncHandle:</code>; not yet plumbed
into a Filename-level "durable write" API.

The remaining OpenSSH extensions
(<code>lsetstat@openssh.com</code>, <code>fstatvfs@openssh.com</code>)
are recognised in the advertised-extensions list but not wrapped at
Filename level — there's no Filename-side caller for them yet.


=== Connection pooling ===
=== Connection pooling ===
Zeile 421: Zeile 441:
<code>ConnectionPoolMutex</code>:
<code>ConnectionPoolMutex</code>:


* '''Lazy bring-up''' — TCP + KEX + userauth + SFTP INIT happens
* '''Lazy bring-up''' — TCP + KEX + userauth + SFTP INIT happens on the first SFTP operation, not on <code>forUrl:</code>.
* '''Per-host serialisation''' — SFTP requests on a given host are serialised through a <code>RecursionLock</code> named
on the first SFTP operation, not on <code>forUrl:</code>.
<code>SFTP/&lt;user@host:port&gt;</code> (visible in
* '''Per-host serialisation''' — SFTP requests on a given host
SemaphoreMonitor).
are serialised through a <code>RecursionLock</code> named
<code>SFTP/&lt;user@host:port&gt;</code> (visible in
SemaphoreMonitor).
* '''Idle eviction''' — unused for longer than
* '''Idle eviction''' — unused for longer than
<code>idleEvictionSeconds</code>, the entry is proactively
<code>idleEvictionSeconds</code>, the entry is proactively
closed + reopened on the next access.
closed + reopened on the next access.
* '''Auto-reconnect''' — a transport-level failure (broken pipe,
* '''Auto-reconnect''' — a transport-level failure (broken pipe, EOF, MNU on nil socket) evicts the dead pool entry, opens a fresh client, retries the request '''once'''. Application-level SFTP STATUS errors propagate immediately.
EOF, MNU on nil socket) evicts the dead pool entry, opens a
fresh client, retries the request '''once'''. Application-level
SFTP STATUS errors propagate immediately.


== Future work ==
== Future work ==
Zeile 439: Zeile 454:
Tracked but not yet implemented:
Tracked but not yet implemented:


* '''Multi-channel parallelism per host''' — today one TCP + one SFTP channel per host means N concurrent requests serialise. Pipelining over multiple SshClients in the pool (preferred), or a transport-level reader process demultiplexing to per-channel inboxes, would let the tree pane keep listing while the content pane reads a large file.
* '''Multi-channel parallelism per host''' — today one TCP +
* '''Accurate <code>#isNonEmptyDirectory</code>''' via OPEN_DIR + READ_DIR (first batch only) + CLOSE — three RTTs per probe; needs SftpClient to pipeline requests before this pays off.
one SFTP channel per host means N concurrent requests
* '''SFTP v5/v6 negotiation''' for extended attrs and FTP-style canonicalisation. (Atomic-overwrite rename is already handled via the OpenSSH <code>posix-rename@openssh.com</code> extension; see [[#OpenSSH SFTP extensions]].)
serialise. Pipelining over multiple SshClients in the pool
(preferred), or a transport-level reader process demultiplexing
to per-channel inboxes, would let the tree pane keep listing
while the content pane reads a large file.
* '''Accurate <code>#isNonEmptyDirectory</code>''' via OPEN_DIR
+ READ_DIR (first batch only) + CLOSE — three RTTs per probe;
needs SftpClient to pipeline requests before this pays off.
* '''SFTP v5/v6 negotiation''' for atomic-overwrite rename,
extended attrs, FTP-style canonicalisation.


= Command Shell =
= Command Shell =
Zeile 488: Zeile 495:
= See also =
= See also =


* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent forwarding, ProxyJump).
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of this stack.
forwarding, ProxyJump).
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack for its HTTPS transport.
this stack.
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack
for its HTTPS transport.
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]

Aktuelle Version vom 26. Mai 2026, 18:52 Uhr

Language: English

Remote access is the ability to drive a remote computer or network from this expecco image — opening shells, running commands, moving files, or driving a test target. Three protocol families are supported, listed in current-recommended order:

  • SSH and SFTP (recommended) — encrypted shell + secure file transfer over an SSH-2 tunnel. Pure-Smalltalk implementation in exept:libcrypt/ssh; no external dependency on OpenSSL or libssh. Use this for anything that touches credentials or sensitive payloads.
  • Local Command Shell — fork + exec on the local machine. Used for local-tool integration and for the local end of a remote workflow that bridges via another protocol.
  • Telnet (legacy) — plain-text terminal session. No encryption, passwords on the wire in clear. Use only when the target hardware has no other option.

SSH and SFTP

The SSH stack covers the full SSH-2 protocol (RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) plus OpenSSH's chacha20-poly1305 transport cipher and the SFTP v3 file-transfer subsystem (draft-ietf-secsh-filexfer-02). Two layers:

  • SSH::Client — programmatic SSH access (remote

exec, TTY shell, agent forwarding, ProxyJump bastion).

  • SSH::SftpFilename — a Filename subclass that lets the rest of ST/X treat a remote SFTP path the same way it treats a local file.

The rest of this section is organised user-task-first: what the user sees and does, the expecco-library hooks below that, then the implementation detail at the end for the curious.

From the FileBrowserV2

Open the location dropdown and paste an sftp:// URL. The browser tab populates as if it were a local path. Tree expansion, column sort (name / size / mtime), preview, and double-click-to-open-in-editor all behave normally. The first click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent clicks reuse the pooled connection.

URL syntax:

sftp://[user@]host[:port]/remote/path

User defaults to the local login name, port to 22, path to /.

The Refresh button in the toolbar (the round-arrow icon between Forward and DirectoryUp) re-reads both the directory tree and the contents pane on demand. Works uniformly for local and SFTP paths; for SFTP it also flushes the per-file STAT cache, so changes made directly on the remote side become visible immediately rather than waiting for the 5-second cache TTL to expire.

The small arrow next to the Refresh icon opens a dropdown with a single checkbox, Automatic Refresh, controlling the background polling task that walks every expanded tree item to detect external changes. The default depends on the current root:

  • Local filesystem → on (10-second cycle, matches the long-standing behaviour).
  • SFTP → off. Each cycle costs one STAT round-trip per child, which is fine for a handful of local directories but painful over the network. Click Refresh manually when you need to pick up remote changes.

When you navigate between local and SFTP roots the toggle flips automatically — but only if you haven't overridden it for the previous root. An explicit user choice is preserved across navigations.

The Tools menu offers four browser actions, three of them gated on the SSH library being loaded:

  • Generate SSH Key Pair... — opens the same key-generation dialog described under #Generating an SSH key pair below.
  • SSH Connect... — opens an interactive VT100 terminal to a remote host.
  • SFTP Connect... — points this browser tab at a remote filesystem via SFTP.
  • Filesystem Info... — shows size, free space and usage of the filesystem holding the currently displayed directory. Works uniformly for local paths and SFTP paths; for SFTP it requires the server to advertise the statvfs@openssh.com extension (every modern OpenSSH does). Sizes are reported in IEC binary units (MiB, GiB, TiB) — the largest unit yielding a value ≥ 1 is chosen, so a TB-scale volume reads as X TiB rather than 10240 GiB.

From expecco actions

The Expecco RemoteAccess plugin (Expecco::RemoteAccessImportPlugin) exposes the following test actions to the expecco action palette:

  • CmdShell - Open SSH Remote Connection — opens an SSH session via the platform's ssh binary (PuTTY's

plink on Windows).

  • CmdShell - Open SSH Remote Connection and PublicKey — same but with explicit public-key authentication.

To run these you need a configured keypair (private key on this machine, public key in the remote host's ~/.ssh/authorized_keys). Generate one via the dialog below or via ssh-keygen.

The plugin also adds a settings page at Extras → Settings → Plugins → Remote Access — SSH Keys carrying a single Generate SSH Key Pair... button that opens the same dialog.

Generating an SSH key pair

The dialog (FileBrowserV2 / settings page)

The dialog asks for all parameters in one form:

  • Comment — embedded in the generated key (defaults to

stx@<hostname>).

  • Storage:
    • Save to disk file only — writes ~/.ssh/id_ed25519_stx (or wherever) plus a matching .pub companion.
    • Save to disk AND load into ssh-agent — writes the file and also hands the key to the running ssh-agent.
    • Load into ssh-agent only — key lives in agent memory only; gone on agent restart.
  • Private key file — full path; disabled in agent-only mode.
  • Passphrase / Confirm — empty leaves the on-disk file unencrypted (agent-only mode ignores the passphrase, since the OpenSSH agent wire protocol carries only the decrypted key).

On Generate, the public-key line (the same ssh-ed25519 AAAA... comment string ssh-keygen emits) is copied to the system clipboard for pasting into the remote host's ~/.ssh/authorized_keys.

From a workspace

For headless deployments, sandboxed builds, or scripts, SSH::Client exposes a pure-Smalltalk key generator that produces output bit-compatible with ssh-keygen -t ed25519:

| seed comment priv |
seed    := SSH::Client generateEd25519Seed.
comment := 'stx@', OperatingSystem getHostName.

"/ Save passphrase-encrypted to disk"
priv := (Filename homeDirectory / '.ssh' / 'id_ed25519_stx') pathName.
SSH::Client
    saveOpenSshEd25519Seed:seed
    toFile:priv
    comment:comment
    passphrase:'choose-something-long'.

"/ AND load into the running agent"
SSH::Client addEd25519SeedToAgent:seed comment:comment.

"/ Print the public-key line to paste into authorized_keys"
Transcript showCR:
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).

Keys generated this way are interoperable with OpenSSH's own tooling (ssh-keygen -y -f ... re-derives the public key, ssh-keygen -p -f ... changes the passphrase, etc.).

Using the shell tools instead

The traditional path also works:

ssh-keygen -t ed25519 -C "stx@your.host"
ssh-copy-id user@remotehost
ssh-add ~/.ssh/id_ed25519

Preparing ssh-agent

The agent path is strongly preferred over reading raw keyfiles: it keeps encrypted private keys unlocked once per session, and handles identities (hardware-token-backed keys, KeePassXC entries) that ST/X should never see directly.

ST/X picks the agent path automatically when $SSH_AUTH_SOCK is set in the process environment at the time stx is launched. Setting it later from a workspace does not help.

Linux / macOS

Most desktop distributions launch an agent automatically as part of the session (gnome-keyring on GNOME, ssh-agent.service on systemd, KWallet on KDE). Verify in a terminal:

echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar
ssh-add -l             # lists loaded identities
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded

If no agent runs at all, add this snippet to your shell rc:

# ~/.bashrc or ~/.zshrc
if [ -z "$SSH_AUTH_SOCK" ]; then
    eval "$(ssh-agent -s)" > /dev/null
fi

ST/X must be launched from a shell that has seen this rc — a desktop launcher started from the file manager does NOT inherit the variable. Wrap the stx start command in a small script under ~/.local/bin/ that sources the rc first.

The Remote Access settings page (Extras → Settings → Plugins → Remote Access — SSH Keys) shows whether the running image sees an agent.

Permanent setup via systemd

For a truly cross-session agent (survives desktop logouts, comes up automatically at next login), enable the per-user systemd unit shipped with most distros' openssh-clients package:

systemctl --user enable --now ssh-agent.service

Then point SSH_AUTH_SOCK at the user-service socket in your shell rc (this replaces the eval $(ssh-agent -s) snippet above):

export SSH_AUTH_SOCK="${XDG_RUNTIME_DIR}/ssh-agent.socket"

Auto-loading keys on first use

To skip the manual ssh-add step, let OpenSSH load keys into the agent automatically the first time they are needed. Add to ~/.ssh/config:

Host *
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519

The first SSH connection then prompts for the key passphrase once and hands the unlocked key to the agent; subsequent connections use the cached identity without prompting.

Windows

Windows 10+ ships native OpenSSH including an agent service. One-time setup:

  1. Open Services (services.msc) as Administrator.
  2. Find OpenSSH Authentication Agent, set Startup Type to Automatic, click Start.
  3. In PowerShell: ssh-add $HOME\.ssh\id_ed25519.
  4. Verify: ssh-add -l.

The Windows OpenSSH agent listens on a named pipe (\\.\pipe\openssh-ssh-agent), not a Unix socket. ST/X supports both transports, but Windows ssh-add does not set SSH_AUTH_SOCK for you. Add it manually:

  1. Press Vorlage:Key → type "environment" → "Edit the system environment variables".
  2. Environment Variables → under User variables, New.
  3. Name: SSH_AUTH_SOCK
  4. Value: \\.\pipe\openssh-ssh-agent
  5. Log out and back in (or restart stx) so the new env propagates.

PowerShell quick-setup

The same setup from an elevated PowerShell prompt, for scripts or unattended provisioning:

# Start the agent now AND on every reboot (permanent).
Set-Service -Name ssh-agent -StartupType Automatic
Start-Service ssh-agent

# Persist SSH_AUTH_SOCK for the user (survives reboots).
[Environment]::SetEnvironmentVariable(
    'SSH_AUTH_SOCK',
    '\\.\pipe\openssh-ssh-agent',
    'User')

# Load a key (prompts for the passphrase if the file is encrypted).
ssh-add $HOME\.ssh\id_ed25519

For a one-shot agent start without making it persistent (e.g. single-session test), drop the Set-Service line and just run Start-Service ssh-agent. The env-var line can also be omitted if SSH_AUTH_SOCK is only needed in the current shell — use $env:SSH_AUTH_SOCK = '...' instead for that session-local form.

On stripped-down Windows installs the ssh-agent service may not be present. Add it once via Settings → Apps → Optional features → OpenSSH Client.

Alternative agents:

  • PuTTY pageant — uses its own protocol; NOT supported by ST/X's SSH::Agent. Migrate the keys to OpenSSH.
  • Git for Windows ssh-agent — works; point

SSH_AUTH_SOCK at the socket it publishes.

  • WSL 2 — a ST/X inside WSL sees WSL's Linux agent normally; a ST/X on the Windows side does not. Bridging needs a helper like npiperelay + socat.

Verify in the Remote Access settings page (Extras → Settings → Plugins → Remote Access — SSH Keys) — the agent indicator there reports whether the running image sees the agent.

Auto-loading keys on first use

Windows OpenSSH does not persist agent-loaded keys across agent restarts. To avoid running ssh-add manually after each reboot, add the same lazy-load configuration to %USERPROFILE%\.ssh\config:

Host *
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519

OpenSSH then loads the key into the agent on first use (prompts for the passphrase once) and reuses it for the rest of the session.

Configuration

All tunables are class-side on SSH::SftpFilename:

Accessor Default What it controls
#idleEvictionSeconds: 240 (4 min) How long a pooled

connection sits idle before the next access proactively closes + reopens it. Just under typical sshd ClientAliveInterval × ClientAliveCountMax so we recycle before the server TCP-RESETs us. Pass nil to restore the default.

#attrsCacheTtlSeconds: 5 Max age (s) of a

cached STAT before #ensureAttrs refetches. Parent listDir always re-stamps fresh attrs onto children, so navigating an open directory does not pay the TTL. Set to 0 to disable caching.

#closeAllConnections (action) Tears down every

pooled connection. Useful after a known-bad network event, before a deliberate identity swap, or as part of a clean image shutdown.

Diagnostics

SemaphoreMonitor

Open SemaphoreMonitor from the Launcher's "Status" sub-menu. Per-host SFTP mutex appears as SFTP/<user@host:port>; the pool-wide mutex as SFTP/pool. Right-click a row:

  • Copy Waiters Stack to Clipboard — dumps the last-owner's walkback plus each waiter's, formatted as plain text. Use when a process is wedged in readWait inside

withSftpClientDo: and you need to see which SFTP request it is on.

  • Copy List to Clipboard — the whole table, for an email-this-to-someone diagnosis.
  • Detect Deadlocks — DFS over the wait-for graph, reports cycles.

Logger

The SSH stack logs interesting events:

  • warning: on auto-reconnect after a dead connection.
  • warning: when a pool entry is idle-evicted.
  • warning: when an SSH key file cannot be parsed (e.g. legacy PEM, encrypted-without-agent) — the file is skipped, others tried.

Limitations

  • SFTP v3 only. No SETSTAT (no remote chmod / chown / utime), no SSH_FXP_READLINK exposed (#isSymbolicLink always

false, #linkInfo returns the regular stat info). Several SFTPv5+ niceties are nevertheless picked up via OpenSSH SSH_FXP_EXTENDED requests — see #OpenSSH SFTP extensions below.

  • Per-host serialisation. Two concurrent operations on the same host queue through the host mutex. See #Future work.
  • #renameTo: fallback has a TOCTOU window. On servers that advertise posix-rename@openssh.com (every modern OpenSSH does), overwrite is atomic; on the rare server that does not, the receiver is emulated as delete-then-rename and another process can race in between.
  • #isNonEmptyDirectory is heuristic. Always returns #isDirectory (the accurate answer would cost three round-trips per directory icon, which made the original tree expansion unbearably slow).

Implementation details

For readers wanting the architecture. Five classes, top-down:

Class Role
SSH::SftpFilename Filename subclass; the public

API. Maps sftp://... URLs to remote files; exposes directoryContents, readingFileDo:, renameTo: etc.

SSH::SftpClient SFTP-v3 protocol

(request/response codec, listDir, stat, open, read, write, mkdir). Driven by SftpFilename.

SSH::Channel SSH channel multiplexer

(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST). One logical session per Channel instance.

SSH::Client High-level SSH client: opens the

transport, runs KEX, host-key check, userauth, then dispenses Channels.

SSH::Transport Wire layer. Banner + KEXINIT

exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq, heartbeat, SSH_MSG_DISCONNECT.

OpenSSH SFTP extensions

SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal. OpenSSH ships an open-ended extension mechanism: the server lists extension names it understands in its SSH_FXP_VERSION reply, and the client invokes them via SSH_FXP_EXTENDED(200) packets carrying the extension name as the first string. Each extension is feature-detected via SSH::SftpClient>>supportsExtension:; callers fall back when the server doesn't advertise it.

The stack uses four of the OpenSSH extensions today:

  • posix-rename@openssh.com — atomic rename-with-overwrite. Picked up automatically by

SftpFilename>>renameTo:; the delete-then-rename fallback only fires on servers that lack it.

  • hardlink@openssh.com — create a POSIX hard link. Exposed as SftpFilename>>createHardLinkAs:.
  • statvfs@openssh.com — POSIX

statvfs(3)-shape filesystem stats. Exposed as SftpFilename>>fileSystemInfo; the result is shape-compatible with OperatingSystem getDiskInfoOf: so callers can treat local and remote uniformly. Drives the Tools → Filesystem Info... menu entry described at the top of this page.

  • fsync@openssh.com — flush server-side write buffer to disk on an open handle. Available on the low-level

SftpClient>>fsyncHandle:; not yet plumbed into a Filename-level "durable write" API.

The remaining OpenSSH extensions (lsetstat@openssh.com, fstatvfs@openssh.com) are recognised in the advertised-extensions list but not wrapped at Filename level — there's no Filename-side caller for them yet.

Connection pooling

Every SftpFilename instance pointing at the same user@host:port triple shares one SSH::Client plus one SSH::SftpClient. Pool is class-side, guarded by a single ConnectionPoolMutex:

  • Lazy bring-up — TCP + KEX + userauth + SFTP INIT happens on the first SFTP operation, not on forUrl:.
  • Per-host serialisation — SFTP requests on a given host are serialised through a RecursionLock named

SFTP/<user@host:port> (visible in SemaphoreMonitor).

  • Idle eviction — unused for longer than

idleEvictionSeconds, the entry is proactively closed + reopened on the next access.

  • Auto-reconnect — a transport-level failure (broken pipe, EOF, MNU on nil socket) evicts the dead pool entry, opens a fresh client, retries the request once. Application-level SFTP STATUS errors propagate immediately.

Future work

Tracked but not yet implemented:

  • Multi-channel parallelism per host — today one TCP + one SFTP channel per host means N concurrent requests serialise. Pipelining over multiple SshClients in the pool (preferred), or a transport-level reader process demultiplexing to per-channel inboxes, would let the tree pane keep listing while the content pane reads a large file.
  • Accurate #isNonEmptyDirectory via OPEN_DIR + READ_DIR (first batch only) + CLOSE — three RTTs per probe; needs SftpClient to pipeline requests before this pays off.
  • SFTP v5/v6 negotiation for extended attrs and FTP-style canonicalisation. (Atomic-overwrite rename is already handled via the OpenSSH posix-rename@openssh.com extension; see #OpenSSH SFTP extensions.)

Command Shell

Local command shell on this expecco machine. Typical applications: local command-line, running a local helper tool, bridging a remote workflow to a local utility.

The Expecco RemoteAccess plugin exposes:

  • CmdShell - Open
  • CmdShell - Close

No credentials, no network — runs as the expecco process's own user. Output streams to expecco's log.

Telnet

Warning Telnet is a legacy protocol with no encryption. Passwords are transmitted in plain text on the wire; anyone on the network path can read them. Use Telnet ONLY when the target device has no other option (typically: old industrial controllers, lab instruments, embedded measurement equipment without an SSH stack). For everything else use #SSH and SFTP.

The expecco plugin exposes:

  • Telnet - Open Remote Connection With Login
  • Telnet - Execute Remote Command
  • Example - Remote Device Control via Telnet (internal demo)

The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream over TCP, with in-band control sequences for terminal options. A connection is established to a target host:port; after optional in-band login, both sides can send data.

See also



Copyright © 2014-2024 eXept Software AG