<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://doc.expecco.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Sv</id>
	<title>expecco Wiki (Version 25.x) - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://doc.expecco.de/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Sv"/>
	<link rel="alternate" type="text/html" href="https://doc.expecco.de/wiki/Spezial:Beitr%C3%A4ge/Sv"/>
	<updated>2026-06-09T08:20:09Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.44.2</generator>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31405</id>
		<title>Remote Access/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31405"/>
		<updated>2026-06-08T14:26:29Z</updated>

		<summary type="html">&lt;p&gt;Sv: password authentication: URL form, code API, auth order; launcher menu entries&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access/en|label=English}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Remote access&#039;&#039;&#039; is the ability to drive a remote computer or&lt;br /&gt;
network from this expecco image — opening shells, running commands,&lt;br /&gt;
moving files, or driving a test target.  Three protocol families are&lt;br /&gt;
supported, listed in current-recommended order:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH and SFTP&#039;&#039;&#039; (recommended) — encrypted shell + secure file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on OpenSSL or libssh.  Use this for anything that touches credentials or sensitive payloads.&lt;br /&gt;
* &#039;&#039;&#039;Local Command Shell&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (legacy) — plain-text terminal session.  No encryption, passwords on the wire in clear.  Use only when the target hardware has no other option.&lt;br /&gt;
&lt;br /&gt;
= SSH and SFTP =&lt;br /&gt;
&lt;br /&gt;
The SSH stack covers the full SSH-2 protocol (RFC 4251–4254,&lt;br /&gt;
RFC 5656, RFC 8709, RFC 8731) plus OpenSSH&#039;s chacha20-poly1305&lt;br /&gt;
transport cipher and the SFTP v3 file-transfer subsystem&lt;br /&gt;
(draft-ietf-secsh-filexfer-02).  Two layers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatic SSH access (remote&lt;br /&gt;
&amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY shell, agent forwarding, ProxyJump bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — a &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt; subclass that lets the rest of ST/X treat a remote SFTP path the same way it treats a local file.&lt;br /&gt;
&lt;br /&gt;
The rest of this section is organised user-task-first: what the user&lt;br /&gt;
sees and does, the expecco-library hooks below that, then the&lt;br /&gt;
implementation detail at the end for the curious.&lt;br /&gt;
&lt;br /&gt;
== From the FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Open the location dropdown and paste an &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt; URL.&lt;br /&gt;
The browser tab populates as if it were a local path.  Tree&lt;br /&gt;
expansion, column sort (name / size / mtime), preview, and&lt;br /&gt;
double-click-to-open-in-editor all behave normally.  The first&lt;br /&gt;
click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent&lt;br /&gt;
clicks reuse the pooled connection.&lt;br /&gt;
&lt;br /&gt;
URL syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user[:password]@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User defaults to the local login name, port to 22, path to&lt;br /&gt;
&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.  The optional &amp;lt;code&amp;gt;:password&amp;lt;/code&amp;gt; piece is the&lt;br /&gt;
RFC-3986-style userinfo password &amp;amp;mdash; see&lt;br /&gt;
[[#Password authentication]] for when this is appropriate and what&lt;br /&gt;
the security trade-offs are.  When present, it is used as a USERAUTH&lt;br /&gt;
fallback after the normal publickey/agent attempts and is &#039;&#039;&#039;never&#039;&#039;&#039;&lt;br /&gt;
re-emitted by the printable URL form, so logging the filename does&lt;br /&gt;
not leak the credential.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Refresh&#039;&#039;&#039; button in the toolbar (the round-arrow icon&lt;br /&gt;
between &#039;&#039;Forward&#039;&#039; and &#039;&#039;DirectoryUp&#039;&#039;) re-reads both the&lt;br /&gt;
directory tree and the contents pane on demand.  Works uniformly&lt;br /&gt;
for local and SFTP paths; for SFTP it also flushes the per-file&lt;br /&gt;
STAT cache, so changes made directly on the remote side become&lt;br /&gt;
visible immediately rather than waiting for the 5-second cache TTL&lt;br /&gt;
to expire.&lt;br /&gt;
&lt;br /&gt;
The small arrow next to the Refresh icon opens a dropdown with a&lt;br /&gt;
single checkbox, &#039;&#039;&#039;Automatic Refresh&#039;&#039;&#039;, controlling the&lt;br /&gt;
background polling task that walks every expanded tree item to&lt;br /&gt;
detect external changes.  The default depends on the current root:&lt;br /&gt;
&lt;br /&gt;
* Local filesystem &amp;amp;rarr; &#039;&#039;&#039;on&#039;&#039;&#039; (10-second cycle, matches the long-standing behaviour).&lt;br /&gt;
* SFTP &amp;amp;rarr; &#039;&#039;&#039;off&#039;&#039;&#039;.  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.&lt;br /&gt;
&lt;br /&gt;
When you navigate between local and SFTP roots the toggle flips&lt;br /&gt;
automatically &amp;amp;mdash; but only if you haven&#039;t overridden it for&lt;br /&gt;
the previous root.  An explicit user choice is preserved across&lt;br /&gt;
navigations.&lt;br /&gt;
&lt;br /&gt;
The Tools menu offers four browser actions, three of them gated on&lt;br /&gt;
the SSH library being loaded:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — opens the same key-generation dialog described under [[#Generating an SSH key pair]] below.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — opens an interactive VT100 terminal to a remote host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — points this browser tab at a remote filesystem via SFTP.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — 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 &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; 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 &#039;&#039;X TiB&#039;&#039; rather than &#039;&#039;10240 GiB&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== From the Launcher ==&lt;br /&gt;
&lt;br /&gt;
The Launcher&#039;s &#039;&#039;&#039;Workspace&#039;&#039;&#039; submenu carries two stand-alone&lt;br /&gt;
entries (gated on the SSH package being loaded):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH Terminal&#039;&#039;&#039; &amp;amp;mdash; prompts for a target in the form &amp;lt;code&amp;gt;user[:password]@host[:port]&amp;lt;/code&amp;gt; and opens a top-level VT100 terminal connected to it.  No FileBrowser involved &amp;amp;mdash; this is the launcher analogue of the FileBrowserV2 &#039;&#039;&#039;Tools &amp;amp;rarr; SSH Connect...&#039;&#039;&#039; entry.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connection&#039;&#039;&#039; &amp;amp;mdash; prompts for an SFTP target (same URL grammar as the FileBrowserV2 location bar, see [[#From the FileBrowserV2]]) and opens a fresh FileBrowserV2 already navigated to that path.&lt;br /&gt;
&lt;br /&gt;
Both entries honour the URL-embedded password syntax described in&lt;br /&gt;
[[#Password authentication]].&lt;br /&gt;
&lt;br /&gt;
== From expecco actions ==&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) exposes the following test&lt;br /&gt;
actions to the expecco action palette:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — opens an SSH session via the platform&#039;s &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; binary (PuTTY&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; on Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — same but with explicit public-key authentication.&lt;br /&gt;
&lt;br /&gt;
To run these you need a configured keypair (private key on this&lt;br /&gt;
machine, public key in the remote host&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;).  Generate one via the dialog&lt;br /&gt;
below or via &amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The plugin also adds a settings page at &#039;&#039;&#039;Extras → Settings →&lt;br /&gt;
Plugins → Remote Access — SSH Keys&#039;&#039;&#039; carrying a single&lt;br /&gt;
&#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; button that opens the same dialog.&lt;br /&gt;
&lt;br /&gt;
== Generating an SSH key pair ==&lt;br /&gt;
&lt;br /&gt;
=== The dialog (FileBrowserV2 / settings page) ===&lt;br /&gt;
&lt;br /&gt;
The dialog asks for all parameters in one form:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — embedded in the generated key (defaults to&lt;br /&gt;
&amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — writes &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (or wherever) plus a matching &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt; companion.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — writes the file and also hands the key to the running ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — key lives in agent memory only; gone on agent restart.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — full path; disabled in agent-only mode.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — empty leaves the on-disk file unencrypted (agent-only mode ignores the passphrase, since the OpenSSH agent wire protocol carries only the decrypted key).&lt;br /&gt;
&lt;br /&gt;
On &#039;&#039;&#039;Generate&#039;&#039;&#039;, the public-key line (the same&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt; string ssh-keygen&lt;br /&gt;
emits) is copied to the system clipboard for pasting into the&lt;br /&gt;
remote host&#039;s &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From a workspace ===&lt;br /&gt;
&lt;br /&gt;
For headless deployments, sandboxed builds, or scripts,&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; exposes a pure-Smalltalk key generator&lt;br /&gt;
that produces output bit-compatible with&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Save passphrase-encrypted to disk&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ AND load into the running agent&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Print the public-key line to paste into authorized_keys&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keys generated this way are interoperable with OpenSSH&#039;s own&lt;br /&gt;
tooling (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; re-derives the public&lt;br /&gt;
key, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; changes the passphrase,&lt;br /&gt;
etc.).&lt;br /&gt;
&lt;br /&gt;
=== Using the shell tools instead ===&lt;br /&gt;
&lt;br /&gt;
The traditional path also works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparing ssh-agent ==&lt;br /&gt;
&lt;br /&gt;
The agent path is strongly preferred over reading raw keyfiles: it&lt;br /&gt;
keeps encrypted private keys unlocked once per session, and handles&lt;br /&gt;
identities (hardware-token-backed keys, KeePassXC entries) that&lt;br /&gt;
ST/X should never see directly.&lt;br /&gt;
&lt;br /&gt;
ST/X picks the agent path automatically when&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is set in the process environment&lt;br /&gt;
&#039;&#039;&#039;at the time stx is launched&#039;&#039;&#039;.  Setting it later from a&lt;br /&gt;
workspace does not help.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Most desktop distributions launch an agent automatically as part of&lt;br /&gt;
the session (gnome-keyring on GNOME, ssh-agent.service on systemd,&lt;br /&gt;
KWallet on KDE).  Verify in a terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar&lt;br /&gt;
ssh-add -l             # lists loaded identities&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If no agent runs at all, add this snippet to your shell rc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc or ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X must be launched from a shell that has seen this rc — a&lt;br /&gt;
desktop launcher started from the file manager does NOT inherit&lt;br /&gt;
the variable.  Wrap the stx start command in a small script under&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt; that sources the rc first.&lt;br /&gt;
&lt;br /&gt;
The Remote Access settings page (&#039;&#039;&#039;Extras → Settings → Plugins&lt;br /&gt;
→ Remote Access — SSH Keys&#039;&#039;&#039;) shows whether the running image&lt;br /&gt;
sees an agent.&lt;br /&gt;
&lt;br /&gt;
==== Permanent setup via systemd ====&lt;br /&gt;
&lt;br /&gt;
For a truly cross-session agent (survives desktop logouts, comes&lt;br /&gt;
up automatically at next login), enable the per-user systemd&lt;br /&gt;
unit shipped with most distros&#039; &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
package:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then point &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the user-service socket&lt;br /&gt;
in your shell rc (this replaces the &amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;&lt;br /&gt;
snippet above):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
To skip the manual &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; step, let OpenSSH load&lt;br /&gt;
keys into the agent automatically the first time they are needed.&lt;br /&gt;
Add to &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first SSH connection then prompts for the key passphrase&lt;br /&gt;
once and hands the unlocked key to the agent; subsequent&lt;br /&gt;
connections use the cached identity without prompting.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ ships native OpenSSH including an agent service.&lt;br /&gt;
One-time setup:&lt;br /&gt;
&lt;br /&gt;
# Open &#039;&#039;&#039;Services&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) as Administrator.&lt;br /&gt;
# Find &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039;, set Startup Type to &#039;&#039;&#039;Automatic&#039;&#039;&#039;, click &#039;&#039;&#039;Start&#039;&#039;&#039;.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Windows OpenSSH agent listens on a named pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), not a Unix socket.  ST/X&lt;br /&gt;
supports both transports, but Windows ssh-add does &#039;&#039;&#039;not&#039;&#039;&#039; set&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; for you.  Add it manually:&lt;br /&gt;
&lt;br /&gt;
# Press {{Key|Win}} → type &amp;quot;environment&amp;quot; → &amp;quot;Edit the system environment variables&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Environment Variables&#039;&#039;&#039; → under &#039;&#039;&#039;User variables&#039;&#039;&#039;, &#039;&#039;&#039;New&#039;&#039;&#039;.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Value: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Log out and back in (or restart stx) so the new env propagates.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell quick-setup ====&lt;br /&gt;
&lt;br /&gt;
The same setup from an &#039;&#039;&#039;elevated&#039;&#039;&#039; PowerShell prompt, for&lt;br /&gt;
scripts or unattended provisioning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Start the agent now AND on every reboot (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# Persist SSH_AUTH_SOCK for the user (survives reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Load a key (prompts for the passphrase if the file is encrypted).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a one-shot agent start without making it persistent (e.g.&lt;br /&gt;
single-session test), drop the &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt; line and&lt;br /&gt;
just run &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt;.  The env-var line&lt;br /&gt;
can also be omitted if &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is only needed&lt;br /&gt;
in the current shell — use &amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
instead for that session-local form.&lt;br /&gt;
&lt;br /&gt;
On stripped-down Windows installs the ssh-agent service may not&lt;br /&gt;
be present.  Add it once via &#039;&#039;&#039;Settings → Apps → Optional&lt;br /&gt;
features → OpenSSH Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative agents:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — uses its own protocol; NOT supported by ST/X&#039;s SSH::Agent.  Migrate the keys to OpenSSH.&lt;br /&gt;
* &#039;&#039;&#039;Git for Windows ssh-agent&#039;&#039;&#039; — works; point&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the socket it publishes.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — a ST/X inside WSL sees WSL&#039;s Linux agent normally; a ST/X on the Windows side does not.  Bridging needs a helper like &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify in the Remote Access settings page&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
the agent indicator there reports whether the running image sees&lt;br /&gt;
the agent.&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
Windows OpenSSH does &#039;&#039;&#039;not&#039;&#039;&#039; persist agent-loaded keys across&lt;br /&gt;
agent restarts.  To avoid running &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; manually&lt;br /&gt;
after each reboot, add the same lazy-load configuration to&lt;br /&gt;
&amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH then loads the key into the agent on first use (prompts&lt;br /&gt;
for the passphrase once) and reuses it for the rest of the&lt;br /&gt;
session.&lt;br /&gt;
&lt;br /&gt;
== Password authentication ==&lt;br /&gt;
&lt;br /&gt;
Public-key (with or without ssh-agent) is the recommended path.&lt;br /&gt;
Password authentication is supported as a fallback for the cases&lt;br /&gt;
where keys are not available: legacy hosts, on-the-fly access to a&lt;br /&gt;
test box, scripted access to an account whose owner refuses to add&lt;br /&gt;
your public key.  Keep in mind:&lt;br /&gt;
&lt;br /&gt;
* The plaintext password lives in memory on the ST/X side for the lifetime of the &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; &amp;amp;mdash; treat it like any other in-memory secret.&lt;br /&gt;
* The server still controls which methods it accepts.  If &amp;lt;code&amp;gt;sshd_config&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;PasswordAuthentication no&amp;lt;/code&amp;gt;, no Smalltalk-side configuration can override that.&lt;br /&gt;
* Wire-level the handshake is RFC 4252 §8: the password is sent inside the encrypted SSH transport, never in clear over the network.&lt;br /&gt;
&lt;br /&gt;
=== From a URL ===&lt;br /&gt;
&lt;br /&gt;
Both the FileBrowserV2 location bar and the launcher&#039;s&lt;br /&gt;
&#039;&#039;&#039;Workspace &amp;amp;rarr; SFTP Connection&#039;&#039;&#039; / &#039;&#039;&#039;Workspace &amp;amp;rarr; SSH&lt;br /&gt;
Terminal&#039;&#039;&#039; prompts accept an inline password in the standard&lt;br /&gt;
RFC-3986 userinfo position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://alice:s3cret@host.example.com/srv/data&lt;br /&gt;
ssh   alice:s3cret@host.example.com:2222&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parser splits on the &#039;&#039;&#039;last&#039;&#039;&#039; &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; so passwords&lt;br /&gt;
containing &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; still parse; the first &amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;&lt;br /&gt;
inside the userinfo separates user from password.  Passwords&lt;br /&gt;
containing &amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt; are not supported in this form &amp;amp;mdash;&lt;br /&gt;
use the programmatic API below.&lt;br /&gt;
&lt;br /&gt;
The password is stripped from the printable URL: every place that&lt;br /&gt;
ends up logging or displaying the filename (status line,&lt;br /&gt;
&amp;lt;code&amp;gt;printOn:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nameString&amp;lt;/code&amp;gt;, the breadcrumb path)&lt;br /&gt;
shows the credential-free form&lt;br /&gt;
&amp;lt;code&amp;gt;sftp://alice@host.example.com/...&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From code ===&lt;br /&gt;
&lt;br /&gt;
Two equivalent ways to attach a password to an&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;/ literal -- simplest, password is just an ivar&lt;br /&gt;
client := SSH::Client newToHost:&#039;host.example.com&#039; port:22 user:&#039;alice&#039;.&lt;br /&gt;
client password:&#039;s3cret&#039;.&lt;br /&gt;
client connect.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;/ callback -- lazy; the password never lives on the client.&lt;br /&gt;
&amp;quot;/ Useful with an interactive prompt, a keychain lookup, or a&lt;br /&gt;
&amp;quot;/ vault-fetched secret that you do not want long-lived in memory.&lt;br /&gt;
client passwordCallback:[ Dialog requestPassword:&#039;SSH password&#039; ].&lt;br /&gt;
client connect.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The callback wins if both are set.  Both are evaluated lazily&lt;br /&gt;
during &amp;lt;code&amp;gt;#connect&amp;lt;/code&amp;gt;, after publickey/agent attempts have&lt;br /&gt;
been rejected, so a working key always wins over a password when&lt;br /&gt;
both are configured.&lt;br /&gt;
&lt;br /&gt;
=== Authentication order ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;amp;gt;&amp;amp;gt;authenticate&amp;lt;/code&amp;gt; walks every available&lt;br /&gt;
credential in this order, returning on the first one the server&lt;br /&gt;
accepts:&lt;br /&gt;
&lt;br /&gt;
# ssh-agent identities, if &amp;lt;code&amp;gt;#useAgent&amp;lt;/code&amp;gt; was called and the agent has loaded keys.&lt;br /&gt;
# ed25519 private-key seed, if &amp;lt;code&amp;gt;#privateKeyFromFile:&amp;lt;/code&amp;gt; picked one up.&lt;br /&gt;
# RSA private key.&lt;br /&gt;
# ECDSA private key.&lt;br /&gt;
# Password, if &amp;lt;code&amp;gt;#password:&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;#passwordCallback:&amp;lt;/code&amp;gt; was set &#039;&#039;&#039;and&#039;&#039;&#039; the server still advertises &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt; in its USERAUTH_FAILURE methods list.&lt;br /&gt;
&lt;br /&gt;
Each step is skipped when the server&#039;s last&lt;br /&gt;
&amp;lt;code&amp;gt;USERAUTH_FAILURE&amp;lt;/code&amp;gt; reply dropped the relevant method&lt;br /&gt;
from its allowed list &amp;amp;mdash; we do not bang on&lt;br /&gt;
&amp;lt;code&amp;gt;publickey&amp;lt;/code&amp;gt; after the server stops offering it.  The&lt;br /&gt;
password attempt is only fired when the server still wants&lt;br /&gt;
&amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt;, so a misconfigured client password against a&lt;br /&gt;
key-only server raises a clean&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::AuthenticationError&amp;lt;/code&amp;gt; without an extra useless&lt;br /&gt;
round-trip.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All tunables are class-side on &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Default !! What it controls&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 min) || How long a pooled&lt;br /&gt;
connection sits idle before the next access proactively closes +&lt;br /&gt;
reopens it.  Just under typical sshd&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; so we recycle&lt;br /&gt;
before the server TCP-RESETs us.  Pass &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; to restore&lt;br /&gt;
the default.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Max age (s) of a&lt;br /&gt;
cached STAT before &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; refetches.  Parent&lt;br /&gt;
listDir always re-stamps fresh attrs onto children, so navigating&lt;br /&gt;
an open directory does not pay the TTL.  Set to &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to&lt;br /&gt;
disable caching.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (action) || Tears down every&lt;br /&gt;
pooled connection.  Useful after a known-bad network event, before&lt;br /&gt;
a deliberate identity swap, or as part of a clean image shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
Open &amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; from the Launcher&#039;s &amp;quot;Status&amp;quot;&lt;br /&gt;
sub-menu.  Per-host SFTP mutex appears as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;; the pool-wide mutex as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Right-click a row:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — dumps the last-owner&#039;s walkback plus each waiter&#039;s, formatted as plain text.  Use when a process is wedged in &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; inside&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; and you need to see which SFTP&lt;br /&gt;
request it is on.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — the whole table, for an email-this-to-someone diagnosis.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS over the wait-for graph, reports cycles.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
The SSH stack logs interesting events:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; on auto-reconnect after a dead connection.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when a pool entry is idle-evicted.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when an SSH key file cannot be parsed (e.g. legacy PEM, encrypted-without-agent) — the file is skipped, others tried.&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SFTP v3 only.&#039;&#039;&#039;  No SETSTAT (no remote chmod / chown / utime), no SSH_FXP_READLINK exposed (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; always&lt;br /&gt;
&amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; returns the regular&lt;br /&gt;
stat info).  Several SFTPv5+ niceties are nevertheless picked up&lt;br /&gt;
via OpenSSH SSH_FXP_EXTENDED requests — see&lt;br /&gt;
[[#OpenSSH SFTP extensions]] below.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation.&#039;&#039;&#039;  Two concurrent operations on the same host queue through the host mutex.  See [[#Future work]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; fallback has a TOCTOU window.&#039;&#039;&#039;  On servers that advertise &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; (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.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; is heuristic.&#039;&#039;&#039;  Always returns &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (the accurate answer would cost three round-trips per directory icon, which made the original tree expansion unbearably slow).&lt;br /&gt;
&lt;br /&gt;
== Implementation details ==&lt;br /&gt;
&lt;br /&gt;
For readers wanting the architecture.  Five classes, top-down:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename subclass; the public&lt;br /&gt;
API.  Maps &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt; URLs to remote files; exposes&lt;br /&gt;
&amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3 protocol&lt;br /&gt;
(request/response codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Driven by SftpFilename.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH channel multiplexer&lt;br /&gt;
(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST).  One logical&lt;br /&gt;
session per Channel instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-level SSH client: opens the&lt;br /&gt;
transport, runs KEX, host-key check, userauth, then dispenses&lt;br /&gt;
Channels.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Wire layer.  Banner + KEXINIT&lt;br /&gt;
exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq,&lt;br /&gt;
heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH SFTP extensions ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal.&lt;br /&gt;
OpenSSH ships an open-ended extension mechanism: the server lists&lt;br /&gt;
extension names it understands in its &amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;&lt;br /&gt;
reply, and the client invokes them via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt; packets carrying the extension&lt;br /&gt;
name as the first string.  Each extension is feature-detected via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;; callers fall&lt;br /&gt;
back when the server doesn&#039;t advertise it.&lt;br /&gt;
&lt;br /&gt;
The stack uses four of the OpenSSH extensions today:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomic rename-with-overwrite.  Picked up automatically by&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt;; the delete-then-rename&lt;br /&gt;
fallback only fires on servers that lack it.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — create a POSIX hard link. Exposed as &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-shape filesystem stats.  Exposed as&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;; the result is&lt;br /&gt;
shape-compatible with &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;&lt;br /&gt;
so callers can treat local and remote uniformly.  Drives the&lt;br /&gt;
&#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; menu entry described at the&lt;br /&gt;
top of this page.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — flush server-side write buffer to disk on an open handle.  Available on the low-level&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt;; not yet plumbed&lt;br /&gt;
into a Filename-level &amp;quot;durable write&amp;quot; API.&lt;br /&gt;
&lt;br /&gt;
The remaining OpenSSH extensions&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
are recognised in the advertised-extensions list but not wrapped at&lt;br /&gt;
Filename level — there&#039;s no Filename-side caller for them yet.&lt;br /&gt;
&lt;br /&gt;
=== Connection pooling ===&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt; instance pointing at the same&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; triple shares one&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; plus one &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Pool is class-side, guarded by a single&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy bring-up&#039;&#039;&#039; — TCP + KEX + userauth + SFTP INIT happens on the first SFTP operation, not on &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation&#039;&#039;&#039; — SFTP requests on a given host are serialised through a &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; named&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; (visible in&lt;br /&gt;
SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle eviction&#039;&#039;&#039; — unused for longer than&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt;, the entry is proactively&lt;br /&gt;
closed + reopened on the next access.&lt;br /&gt;
* &#039;&#039;&#039;Auto-reconnect&#039;&#039;&#039; — a transport-level failure (broken pipe, EOF, MNU on nil socket) evicts the dead pool entry, opens a fresh client, retries the request &#039;&#039;&#039;once&#039;&#039;&#039;.  Application-level SFTP STATUS errors propagate immediately.&lt;br /&gt;
&lt;br /&gt;
== Future work ==&lt;br /&gt;
&lt;br /&gt;
Tracked but not yet implemented:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-channel parallelism per host&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Accurate &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (first batch only) + CLOSE — three RTTs per probe; needs SftpClient to pipeline requests before this pays off.&lt;br /&gt;
* &#039;&#039;&#039;SFTP v5/v6 negotiation&#039;&#039;&#039; for extended attrs and FTP-style canonicalisation.  (Atomic-overwrite rename is already handled via the OpenSSH &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; extension; see [[#OpenSSH SFTP extensions]].)&lt;br /&gt;
&lt;br /&gt;
= Command Shell =&lt;br /&gt;
&lt;br /&gt;
Local command shell on this expecco machine.  Typical applications:&lt;br /&gt;
local command-line, running a local helper tool, bridging a&lt;br /&gt;
remote workflow to a local utility.&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No credentials, no network — runs as the expecco process&#039;s own&lt;br /&gt;
user.  Output streams to expecco&#039;s log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warning]] &#039;&#039;&#039;Telnet is a legacy protocol&lt;br /&gt;
with no encryption.&#039;&#039;&#039; Passwords are transmitted in plain text on&lt;br /&gt;
the wire; anyone on the network path can read them.  Use Telnet&lt;br /&gt;
ONLY when the target device has no other option (typically: old&lt;br /&gt;
industrial controllers, lab instruments, embedded measurement&lt;br /&gt;
equipment without an SSH stack).  For everything else use&lt;br /&gt;
[[#SSH and SFTP]].&lt;br /&gt;
&lt;br /&gt;
The expecco plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (internal demo)&lt;br /&gt;
&lt;br /&gt;
The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream&lt;br /&gt;
over TCP, with in-band control sequences for terminal options.&lt;br /&gt;
A connection is established to a target host:port; after optional&lt;br /&gt;
in-band login, both sides can send data.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent forwarding, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of this stack.&lt;br /&gt;
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack for its HTTPS transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Network]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31404</id>
		<title>Remote Access</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31404"/>
		<updated>2026-06-08T14:26:01Z</updated>

		<summary type="html">&lt;p&gt;Sv: password authentication: URL form, code API, auth order; launcher menu entries&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access|label=Deutsch}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere Dateiübertragung über einen SSH-2-Tunnel.  Reine Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen Maschine.  Für die Anbindung lokaler Werkzeuge und für die lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff (entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung, ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
&amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user[:password]@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.  Das&lt;br /&gt;
optionale &amp;lt;code&amp;gt;:password&amp;lt;/code&amp;gt;-Segment ist das RFC-3986-Userinfo-Passwort&lt;br /&gt;
&amp;amp;mdash; wann es angebracht ist und welche Sicherheitsabwägungen&lt;br /&gt;
damit verbunden sind, beschreibt [[#Passwort-Authentifizierung]].&lt;br /&gt;
Wenn vorhanden, dient es als USERAUTH-Fallback nach den üblichen&lt;br /&gt;
Publickey-/Agent-Versuchen und wird &#039;&#039;&#039;niemals&#039;&#039;&#039; wieder von der&lt;br /&gt;
druckbaren URL-Form ausgegeben &amp;amp;mdash; das Protokollieren des&lt;br /&gt;
Dateinamens leakt also nicht das Credential.&lt;br /&gt;
&lt;br /&gt;
Die Schaltfläche &#039;&#039;&#039;Refresh&#039;&#039;&#039; in der Symbolleiste&lt;br /&gt;
(Pfeil-Kreis-Symbol zwischen &#039;&#039;Forward&#039;&#039; und &#039;&#039;DirectoryUp&#039;&#039;) liest&lt;br /&gt;
Baum und Inhalts-Panel auf Anforderung neu ein.  Funktioniert&lt;br /&gt;
einheitlich für lokale und SFTP-Pfade; bei SFTP wird zusätzlich der&lt;br /&gt;
per-Datei-STAT-Cache geleert, sodass Änderungen, die direkt auf&lt;br /&gt;
der Gegenseite gemacht wurden, sofort sichtbar werden — ohne auf&lt;br /&gt;
den Ablauf der 5-Sekunden-Cache-TTL zu warten.&lt;br /&gt;
&lt;br /&gt;
Der kleine Pfeil neben dem Refresh-Symbol öffnet ein Aufklappmenü&lt;br /&gt;
mit einem einzelnen Kontrollkästchen, &#039;&#039;&#039;Automatic Refresh&#039;&#039;&#039;, das&lt;br /&gt;
den Hintergrund-Task an- bzw. abschaltet, der alle expandierten&lt;br /&gt;
Baumeinträge auf externe Änderungen prüft.  Die Vorgabe richtet&lt;br /&gt;
sich nach der aktuellen Wurzel:&lt;br /&gt;
&lt;br /&gt;
* Lokales Dateisystem &amp;amp;rarr; &#039;&#039;&#039;an&#039;&#039;&#039; (10-Sekunden-Zyklus, entspricht dem bisherigen Verhalten).&lt;br /&gt;
* SFTP &amp;amp;rarr; &#039;&#039;&#039;aus&#039;&#039;&#039;.  Jeder Zyklus kostet einen STAT-Roundtrip pro Kind — für eine Handvoll lokaler Verzeichnisse harmlos, über das Netz schmerzhaft.  Bei Bedarf manuell auf Refresh klicken, um Änderungen zu sehen.&lt;br /&gt;
&lt;br /&gt;
Beim Wechsel zwischen lokalen und SFTP-Wurzeln wird der Schalter&lt;br /&gt;
automatisch umgelegt &amp;amp;mdash; aber nur, sofern man ihn für die&lt;br /&gt;
vorherige Wurzel nicht selbst verstellt hat.  Eine explizite&lt;br /&gt;
Benutzerwahl bleibt über Navigationen hinweg erhalten.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet vier Aktionen — die&lt;br /&gt;
drei SSH-spezifischen sind nur bei geladener SSH-Bibliothek&lt;br /&gt;
sichtbar:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den Schlüsselerzeugungs-Dialog, siehe [[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP auf ein entferntes Dateisystem.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — zeigt Größe, freien Platz und Belegung des Dateisystems, das das aktuell angezeigte Verzeichnis enthält. Funktioniert einheitlich für lokale und SFTP-Pfade; bei SFTP setzt der Aufruf voraus, daß der Server die Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; ankündigt (jedes moderne OpenSSH&lt;br /&gt;
tut das).  Größen werden in IEC-Binäreinheiten ausgegeben (MiB,&lt;br /&gt;
GiB, TiB) — gewählt wird die größte Einheit, die einen Wert ≥ 1&lt;br /&gt;
liefert, damit ein TB-großes Volume als &#039;&#039;X TiB&#039;&#039; statt&lt;br /&gt;
&#039;&#039;10240 GiB&#039;&#039; erscheint.&lt;br /&gt;
&lt;br /&gt;
== Aus dem Launcher ==&lt;br /&gt;
&lt;br /&gt;
Das Untermenü &#039;&#039;&#039;Workspace&#039;&#039;&#039; im Launcher enthält zwei eigenständige&lt;br /&gt;
Einträge (nur sichtbar bei geladenem SSH-Paket):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH Terminal&#039;&#039;&#039; &amp;amp;mdash; fragt nach einem Ziel der Form &amp;lt;code&amp;gt;user[:password]@host[:port]&amp;lt;/code&amp;gt; und öffnet ein VT100-Terminal als eigenständiges Fenster.  Das ist das Launcher-Pendant zum FileBrowserV2-Eintrag &#039;&#039;&#039;Tools &amp;amp;rarr; SSH Connect...&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connection&#039;&#039;&#039; &amp;amp;mdash; fragt nach einem SFTP-Ziel (gleiche URL-Grammatik wie in der FileBrowserV2-Adresszeile, siehe [[#Aus dem FileBrowserV2]]) und öffnet einen frischen FileBrowserV2, der bereits dorthin navigiert ist.&lt;br /&gt;
&lt;br /&gt;
Beide Einträge unterstützen die in [[#Passwort-Authentifizierung]]&lt;br /&gt;
beschriebene URL-Form mit eingebettetem Passwort.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary (PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet (Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf &#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs- variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;, &#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Passwort-Authentifizierung ==&lt;br /&gt;
&lt;br /&gt;
Empfohlen ist Public-Key-Authentifizierung (mit oder ohne&lt;br /&gt;
ssh-agent).  Passwort-Authentifizierung ist als Fallback gedacht,&lt;br /&gt;
wenn keine Schlüssel verfügbar sind: Altsysteme, Ad-hoc-Zugriff&lt;br /&gt;
auf einen Testserver, Skripte gegen ein Konto, dessen Besitzer den&lt;br /&gt;
Public Key nicht hinterlegen möchte.  Zu beachten:&lt;br /&gt;
&lt;br /&gt;
* Das Klartext-Passwort liegt auf ST/X-Seite für die Lebensdauer des &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; im Speicher &amp;amp;mdash; entsprechend behandeln wie jedes andere In-Memory-Geheimnis.&lt;br /&gt;
* Der Server entscheidet, welche Methoden er akzeptiert.  Steht in &amp;lt;code&amp;gt;sshd_config&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;PasswordAuthentication no&amp;lt;/code&amp;gt;, kann keine Smalltalk-seitige Einstellung das überschreiben.&lt;br /&gt;
* Auf Drahtebene entspricht der Vorgang RFC 4252 §8: das Passwort wandert innerhalb der verschlüsselten SSH-Transportschicht, niemals im Klartext über das Netz.&lt;br /&gt;
&lt;br /&gt;
=== Aus einer URL ===&lt;br /&gt;
&lt;br /&gt;
Sowohl die FileBrowserV2-Adressleiste als auch die Launcher-Dialoge&lt;br /&gt;
&#039;&#039;&#039;Workspace &amp;amp;rarr; SFTP Connection&#039;&#039;&#039; / &#039;&#039;&#039;Workspace &amp;amp;rarr; SSH&lt;br /&gt;
Terminal&#039;&#039;&#039; akzeptieren ein eingebettetes Passwort an der&lt;br /&gt;
standardmäßigen RFC-3986-Userinfo-Position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://alice:s3cret@host.example.com/srv/data&lt;br /&gt;
ssh   alice:s3cret@host.example.com:2222&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Parser splittet am &#039;&#039;&#039;letzten&#039;&#039;&#039; &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;, sodass&lt;br /&gt;
Passwörter mit enthaltenem &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; dennoch korrekt geparst&lt;br /&gt;
werden; der erste &amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt; im Userinfo-Teil trennt Benutzer&lt;br /&gt;
und Passwort.  Passwörter, die selbst ein &amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt; enthalten,&lt;br /&gt;
werden in dieser Form nicht unterstützt &amp;amp;mdash; dafür die&lt;br /&gt;
programmatische API unten verwenden.&lt;br /&gt;
&lt;br /&gt;
Das Passwort wird aus der druckbaren URL entfernt: jede Stelle, die&lt;br /&gt;
am Ende den Dateinamen ausgibt (Statuszeile,&lt;br /&gt;
&amp;lt;code&amp;gt;printOn:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nameString&amp;lt;/code&amp;gt;, die Breadcrumb-Leiste)&lt;br /&gt;
zeigt die credential-freie Form&lt;br /&gt;
&amp;lt;code&amp;gt;sftp://alice@host.example.com/...&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Aus Code ===&lt;br /&gt;
&lt;br /&gt;
Zwei äquivalente Wege, einem &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; ein Passwort&lt;br /&gt;
mitzugeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;/ Literal -- am einfachsten, Passwort ist nur ein Ivar&lt;br /&gt;
client := SSH::Client newToHost:&#039;host.example.com&#039; port:22 user:&#039;alice&#039;.&lt;br /&gt;
client password:&#039;s3cret&#039;.&lt;br /&gt;
client connect.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;/ Callback -- lazy; das Passwort lebt nie auf dem Client.&lt;br /&gt;
&amp;quot;/ Praktisch für interaktive Abfrage, Keychain-Lookup oder ein&lt;br /&gt;
&amp;quot;/ Vault-gespeichertes Geheimnis, das nicht langlebig im Speicher&lt;br /&gt;
&amp;quot;/ liegen soll.&lt;br /&gt;
client passwordCallback:[ Dialog requestPassword:&#039;SSH-Passwort&#039; ].&lt;br /&gt;
client connect.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sind beide gesetzt, gewinnt der Callback.  Beide werden lazy&lt;br /&gt;
während &amp;lt;code&amp;gt;#connect&amp;lt;/code&amp;gt; ausgewertet, nachdem die&lt;br /&gt;
Publickey-/Agent-Versuche abgelehnt wurden &amp;amp;mdash; ein&lt;br /&gt;
funktionierender Schlüssel schlägt also immer ein parallel&lt;br /&gt;
konfiguriertes Passwort.&lt;br /&gt;
&lt;br /&gt;
=== Reihenfolge der Authentifizierungsversuche ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;amp;gt;&amp;amp;gt;authenticate&amp;lt;/code&amp;gt; geht jedes verfügbare&lt;br /&gt;
Credential der Reihe nach durch und kehrt beim ersten zurück, das&lt;br /&gt;
der Server akzeptiert:&lt;br /&gt;
&lt;br /&gt;
# ssh-agent-Identitäten, sofern &amp;lt;code&amp;gt;#useAgent&amp;lt;/code&amp;gt; aufgerufen wurde und der Agent Schlüssel geladen hat.&lt;br /&gt;
# ed25519-Privatschlüssel-Seed, sofern &amp;lt;code&amp;gt;#privateKeyFromFile:&amp;lt;/code&amp;gt; einen geladen hat.&lt;br /&gt;
# RSA-Privatschlüssel.&lt;br /&gt;
# ECDSA-Privatschlüssel.&lt;br /&gt;
# Passwort, sofern &amp;lt;code&amp;gt;#password:&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;#passwordCallback:&amp;lt;/code&amp;gt; gesetzt ist &#039;&#039;&#039;und&#039;&#039;&#039; der Server &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt; in seiner USERAUTH_FAILURE-Methodenliste weiterhin anbietet.&lt;br /&gt;
&lt;br /&gt;
Jeder Schritt wird übersprungen, wenn die letzte&lt;br /&gt;
&amp;lt;code&amp;gt;USERAUTH_FAILURE&amp;lt;/code&amp;gt;-Antwort des Servers die jeweilige&lt;br /&gt;
Methode aus der erlaubten Liste gestrichen hat &amp;amp;mdash; auf&lt;br /&gt;
&amp;lt;code&amp;gt;publickey&amp;lt;/code&amp;gt; wird also nicht weiter herumgehämmert,&lt;br /&gt;
sobald der Server damit aufhört.  Der Passwortversuch läuft nur,&lt;br /&gt;
wenn der Server &amp;lt;code&amp;gt;password&amp;lt;/code&amp;gt; noch verlangt; ein falsch&lt;br /&gt;
konfiguriertes Passwort gegen einen reinen Schlüssel-Server erzeugt&lt;br /&gt;
also einen sauberen &amp;lt;code&amp;gt;SSH::AuthenticationError&amp;lt;/code&amp;gt; ohne&lt;br /&gt;
zusätzlichen nutzlosen Roundtrip.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback des letzten Eigners samt aller Waiter als Text in die Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
&amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes chmod / chown / utime), kein SSH_FXP_READLINK exponiert (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  Einige&lt;br /&gt;
SFTPv5+-Annehmlichkeiten werden dennoch über OpenSSH-spezifische&lt;br /&gt;
SSH_FXP_EXTENDED-Aufrufe nutzbar — siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]] weiter unten.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt;-Fallback hat ein TOCTOU-Fenster.&#039;&#039;&#039; Bei Servern, die &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; ankündigen (jedes moderne OpenSSH tut das), ist das Überschreiben atomar. Beim seltenen Server, der das nicht tut, wird auf Delete-dann-Rename ausgewichen und ein anderer Prozess kann sich dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039; Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH-SFTP-Erweiterungen ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (Entwurf draft-ietf-secsh-filexfer-02) ist bewusst&lt;br /&gt;
minimal gehalten.  OpenSSH bringt einen offenen&lt;br /&gt;
Erweiterungsmechanismus mit: der Server listet im&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;-Reply die Erweiterungsnamen auf, die&lt;br /&gt;
er versteht, und der Client ruft sie über&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt;-Pakete mit dem&lt;br /&gt;
Erweiterungsnamen als erstem String auf.  Jede Erweiterung wird&lt;br /&gt;
über &amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;&lt;br /&gt;
feature-detektiert; Aufrufer fallen zurück, wenn der Server sie&lt;br /&gt;
nicht ankündigt.&lt;br /&gt;
&lt;br /&gt;
Der Stack nutzt heute vier OpenSSH-Erweiterungen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomares rename-mit-Überschreiben.  Wird automatisch von&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt; aufgegriffen; die&lt;br /&gt;
Delete-dann-Rename-Fallback-Variante kommt nur bei Servern zum&lt;br /&gt;
Einsatz, die die Erweiterung nicht haben.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — Erzeugt einen POSIX-Hardlink. Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX-&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-typische Dateisystem-Statistik.&lt;br /&gt;
Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
das Ergebnis ist form-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;, sodass Aufrufer&lt;br /&gt;
lokale und entfernte Pfade einheitlich behandeln können.&lt;br /&gt;
Treibt den Menü-Eintrag &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; an,&lt;br /&gt;
der am Anfang dieser Seite beschrieben ist.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — schreibt den serverseitigen Schreibpuffer eines geöffneten Handles auf Platte.  Liegt als&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt; bereit; noch nicht&lt;br /&gt;
in eine &amp;quot;Durable-Write&amp;quot;-API auf Filename-Ebene eingebunden.&lt;br /&gt;
&lt;br /&gt;
Die verbleibenden OpenSSH-Erweiterungen&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
werden in der angekündigten Liste erkannt, aber nicht auf&lt;br /&gt;
Filename-Ebene gekapselt — es gibt dafür noch keinen&lt;br /&gt;
Filename-seitigen Aufrufer.&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene (Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den Pool-Eintrag, öffnet einen frischen Client und wiederholt die Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet eine TCP- plus eine SFTP-Verbindung pro Host, dass N gleichzeitige Anfragen serialisieren.  Pipelining über mehrere SshClients im Pool (bevorzugt) oder ein transport-seitiger Reader-Prozess, der eingehende Pakete in Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel erlauben, weiter aufzulisten, während das Inhalts-Panel eine große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für erweiterte Attribute und FTP-artige Kanonisierung.  (Atomares Überschreibungs-rename ist bereits über die OpenSSH-Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; abgedeckt; siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]].)&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY, Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31335</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31335"/>
		<updated>2026-05-29T07:39:47Z</updated>

		<summary type="html">&lt;p&gt;Sv: add performance entry for log-archive load speedup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q2 2026) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Feature: &#039;&#039;&#039;NEW&#039;&#039;&#039; [[KI Coding Plugin|&#039;&#039;&#039;AI Coding&#039;&#039;&#039;]] plugin (chat-assistant integration for the activity editor and class browser):&lt;br /&gt;
** supports both &#039;&#039;&#039;Anthropic Claude&#039;&#039;&#039;, &#039;&#039;&#039;OpenAI ChatGPT&#039;&#039;&#039; or &#039;&#039;&#039;Llama&#039;&#039;&#039; as backends, switchable from the settings dialog (Plugins → AI Coding);&amp;lt;br&amp;gt;API keys are stored per provider so you can flip between them without re-entering&lt;br /&gt;
** Toolbar / class-browser menu adapts to the active provider — reads &amp;quot;&#039;&#039;Ask Claude&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;Ask ChatGPT&#039;&#039;&amp;quot;, updates live when the provider is switched&lt;br /&gt;
** Menu actions: Explain code/method, Suggest improvement, Generate test, Generate doc-comment (fills the Documentation tab and pin comments), Find bugs, Custom prompt; &amp;quot;[Apply]&amp;quot; can install proposed code directly into the activity body or compile a proposed helper method into a class&lt;br /&gt;
** Chat window streams responses live (Server-Sent Events) and shows running token count + estimated cost in the title; supports image attachments (screenshots / PNG-JPG files)&lt;br /&gt;
** model, API key, endpoint and max-tokens are configurable via the Claude settings dialog&lt;br /&gt;
* Feature: SSL1.3 support (without external SSL library)&lt;br /&gt;
* Feature: SSL support for C, Python, NodeJS, Ruby, Dart and Smalltalk bridges (combined cert+key PEM files supported)&lt;br /&gt;
* Feature: SSH builtin, plus SSH and SFTP clients (both via [[Tools_FileBrowser/en|FileBrowser]])&lt;br /&gt;
* Feature: public/private keypair generation via a [[Tools_FileBrowser/en|FileBrowser]] menu (for easy SSH setup)&lt;br /&gt;
* Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;br /&gt;
* Feature: Improved/Fixed the [[Number_API_Functions|Number]] stack (see also in [[Numeric_Limits/en| Numeric Limits]]):&lt;br /&gt;
** Enhanced multiprecision numbers (eg. &amp;lt;float&amp;gt;q, &amp;lt;float&amp;gt;Q constants in freeze values)&lt;br /&gt;
** Float32 numbers (&amp;lt;float&amp;gt;f)&lt;br /&gt;
** Integer freezeValues in exponential notation (eg. 1e5)&lt;br /&gt;
** Recognize type specific infinities eg. &amp;quot;inf.0&amp;quot;, &amp;quot;inf.0f&amp;quot;, &amp;quot;inf.0q&amp;quot; etc. and type specific NaNs eg. &amp;quot;nan.0&amp;quot;, &amp;quot;nan.0f&amp;quot;, &amp;quot;nan.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** fixed/added missing trigonometric functions for multiprecision floats and complex numbers (eg. arcTan). Now all test cases produce a result within the precision limits of their type&lt;br /&gt;
** inspector (and activitylog as a consequence) show the type of a float (suffix &#039;f&#039;, &#039;q&#039;, &#039;Q&#039; etc.)&lt;br /&gt;
&lt;br /&gt;
* Feature: Workflow editor — improved orthogonal routing of connections:&lt;br /&gt;
** connections now detour around blocks, freeze values and annotation boxes instead of cutting through them&lt;br /&gt;
** connections from a compound block&#039;s input-pin descriptions are bundled into a bus column next to the source pin&lt;br /&gt;
** end-stub avoidance no longer fires on near-misses (strict overlap check, no clearance margin)&lt;br /&gt;
** routing prefers the source-side bend when the source step has multiple sibling pins&lt;br /&gt;
* Feature: Workflow editor — improved naïve autolayout: added horizontal and vertical expansion passes that spread adjacent blocks apart for clearer connection routing&lt;br /&gt;
* Feature: file transfer (getFile/putFile) and makeDirectory for all bridges (C, Python, NodeJS, Ruby, Dart, Smalltalk)&lt;br /&gt;
* Feature: defineFunction/callFunction support for NodeJS, Ruby and Smalltalk bridges&lt;br /&gt;
* Feature: OLE for 64 bit architectures&lt;br /&gt;
* Feature: optional HTTPS for the AIDYMO and license server — drop a PEM cert+key into &amp;lt;code&amp;gt;--workDir&amp;lt;/code&amp;gt; (combined &amp;lt;code&amp;gt;server.pem&amp;lt;/code&amp;gt;, or split&amp;lt;code&amp;gt;fullchain.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;privkey.pem&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;cert.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;key.pem&amp;lt;/code&amp;gt;) and the service binds TLS automatically; informational hostname is derived from the certificate (SAN-aware, wildcard- and multi-SAN-safe)&lt;br /&gt;
* Feature: more search options in the [[How_to_Program/en#MethodFinder:_Find_Functions_by_Example | MethodFinder]]. &lt;br /&gt;
* Fix: many fixes related to DPI scaling. I.e. when multiple monitors are configured with different scaling (especially different from 100%). Includes scaling of fonts, bitmap and UI components (widgets).&lt;br /&gt;
* Fix: display of very long lines in a text editor/inspector (workaround a Windows 16bit line limit)&lt;br /&gt;
* Fix: due to a bug in enumeration datatypes, the size of &amp;quot;.ets&amp;quot; files grew over time to huge sizes (some information was redundantly and identically written twice). This had no effect on the execution, but made load/save times almost unacceptably long by storing/reloading unneeded data. When loaded and saved again, this will fix those ets files automatically (there is also a patch for older versions)&lt;br /&gt;
* Performance: execution of elementary Smalltalk and JavaScript actions tuned for speed (Jitter improvements)&lt;br /&gt;
* Performance: speedup of cryptographic algorithms (affects key generation and other)&lt;br /&gt;
* Performance: tuned loading of stored ets/elf files (30-40% speedup)&lt;br /&gt;
* Performance: further speedup when loading test-result archives that embed large execution logs — typical load is roughly a third faster again&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=FAQ_on_Bridges/en&amp;diff=31331</id>
		<title>FAQ on Bridges/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=FAQ_on_Bridges/en&amp;diff=31331"/>
		<updated>2026-05-27T13:48:26Z</updated>

		<summary type="html">&lt;p&gt;Sv: update SSL section: client cert in prefs, load/inspect/clear UI, chain validation on load&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Why Bridged Actions and not Script Actions? ==&lt;br /&gt;
&lt;br /&gt;
Script actions will start the language interpreter anew for every such action. Thus, no internal state can be held and transferred from one such action to another. Especially, it is not possible to open a communication channel on one script action, pass it back to expecco, and use it with different followup script actions (i.e. actions like &amp;quot;&#039;&#039;open-port&#039;&#039;&amp;quot; / &amp;quot;&#039;&#039;send message&#039;&#039;&amp;quot; / &amp;quot;&#039;&#039;receive message&#039;&#039;&amp;quot; / &amp;quot;&#039;&#039;close port&#039;&#039;&amp;quot; etc.).&lt;br /&gt;
&lt;br /&gt;
For this, it is required to call individual API-entries inside the bridge code, and return handles from the bridge to expecco, which can later be passed to other API entries.&amp;lt;br&amp;gt;This of course only makes sense, if the interpreter stays active during the lifetime of the connection.&lt;br /&gt;
&lt;br /&gt;
On the other hand, script actions are easier to use if a self contained program or app is to be started, or which performs a single processing action or which stays alive for longer, serving a socket or database. In any such case, there is no need to exchange individual data objects (such as handles) with expecco. It may however interact with expecco through other communication channels, such as sockets, RPC calls, SOAP interface or stdin/stdout text ports.&lt;br /&gt;
&lt;br /&gt;
Thus, you will typically use script actions to start server applications and bridged actions to call individual functions of a framework.&lt;br /&gt;
&lt;br /&gt;
== Which Languages are Supported ==&lt;br /&gt;
&lt;br /&gt;
By the time this chapter was written, bridged code execution is supported for:&lt;br /&gt;
* Java (using either Groovy or Jython as &amp;quot;&#039;&#039;glue&#039;&#039;&amp;quot; or directly using proxy objects in expecco)&lt;br /&gt;
* Python (both Python 2.7 and Python 3.x)&lt;br /&gt;
* .NET (using IronPython as &amp;quot;&#039;&#039;glue&#039;&#039;&amp;quot; or directly via proxy objects)&lt;br /&gt;
* NodeJS (i.e. JavaScript)&lt;br /&gt;
* C/C++ (Root/Cling is being developed currently)&lt;br /&gt;
* Smalltalk (Smalltalk/X and VisualWorks are currently supported; VisualAge may come in the near future, if there is sufficient customer interest)&lt;br /&gt;
* Ruby&lt;br /&gt;
Plans are to support Dart and Scheme in the future.&lt;br /&gt;
&lt;br /&gt;
== How are Bridges Started ==&lt;br /&gt;
&lt;br /&gt;
Bridges on the local machine are usually automatically started by expecco itself, when the first bridged action is executed (and stays alive, until expecco is terminated, or bridge connections are closed via the menu or via an expecco action block). Some language interpreters may take a few seconds to come up, so the first such action will execute relatively slow compared to later actions. You can add a dummy (empty) bridged action to your startup actions (project&#039;s postLoad action) to prepare the bridged interpreter and have it startup in parallel. &lt;br /&gt;
&lt;br /&gt;
Bridges on other machines, or additional bridges on the local machine must be started by executing a bridge start action block or simply by executing a shell/batch action to call the corresponding language interpreter (python, node, etc.). On Windows, a powershell action can be used for remote execution (given that your Windows security settings allow that). On Unix, a shell action executin a remote shell / ssh command could be used.&lt;br /&gt;
&lt;br /&gt;
== Who Initiates the Connection ==&lt;br /&gt;
&lt;br /&gt;
Normally, expecco opens a port and tells the bridge (via command line argument) to which port to connect. Depending on the setup of your network and firewall, it may sometimes be required to reverse this, and let the bridge open a port and expecco connect to it. &lt;br /&gt;
&lt;br /&gt;
For this, some bridges support command line arguments (&amp;quot;--server&amp;quot; and &amp;quot;--port&amp;quot;) to specify this behavior. Details are found in the individual bridge documentation pages.&lt;br /&gt;
&lt;br /&gt;
== Ports Used for Bridge Communication ==&lt;br /&gt;
&lt;br /&gt;
Unless ports are configured in the expecco settings,&lt;br /&gt;
the following default ports are used (bridges typically listen on or connect to this port, unless an explicit &amp;quot;--port&amp;quot; argument is given):&lt;br /&gt;
&lt;br /&gt;
 Python      8677 , 8678 (debug) &lt;br /&gt;
   Python2     8679 , 8680 (debug)&lt;br /&gt;
   Python3     8681 , 8682 (debug)&lt;br /&gt;
 Jython      8777 , 8778 (debug)&lt;br /&gt;
 IronPython  8787 , 8788 (debug)&lt;br /&gt;
 Node        8577 , 8578 (debug)&lt;br /&gt;
 Electron    8588 , 8589 (debug)&lt;br /&gt;
 [[Embedded_Systems_C_Bridge_API |CBridge]]     8855 (also for Root/Cling)&lt;br /&gt;
 Smalltalk   8877&lt;br /&gt;
 VASmalltalk 8879&lt;br /&gt;
 VWSmalltalk 8881&lt;br /&gt;
 Dart        8885&lt;br /&gt;
 Ruby        8977&lt;br /&gt;
 Scheme      8599&lt;br /&gt;
&lt;br /&gt;
Note: for some bridges, two connections are used, the debug port being used by the debugger to inspect data and to support breakpoints, single stepping etc. &lt;br /&gt;
&amp;lt;br&amp;gt;Note: expecco will try successive alternative ports, if the default port happens to be already in use.&lt;br /&gt;
&lt;br /&gt;
Debugging is not supported on all bridges (eg. the C-bridge does not).&lt;br /&gt;
&lt;br /&gt;
== What are Proxy Objects ==&lt;br /&gt;
These are placeholder objects inside expecco, which will look like regular Smalltalk (or JavaScript) objects inside expecco, but will forward any call via the socket ower to the bridge partner. &lt;br /&gt;
&lt;br /&gt;
Thus, they can be used transparently inside Smalltalk and JavaScript elementary actions, performing whatever is implemented in the remote side.&lt;br /&gt;
&lt;br /&gt;
For example, given a Java bridge connection named &amp;quot;bridge&amp;quot;, you can instantiate a remote object there from within your local JavaScript code with:&lt;br /&gt;
 var remoteArrayListClass, myList;&lt;br /&gt;
 remoteArrayListClass = bridge.resolveType(&amp;quot;java.util.ArrayList&amp;quot;);&lt;br /&gt;
 myList = new remoteArrayListClass(10);&lt;br /&gt;
 myList.add(&amp;quot;hello&amp;quot;);&lt;br /&gt;
 ...&lt;br /&gt;
in a Smalltalk action, the code is similar:&lt;br /&gt;
 |remoteArrayList myList|&lt;br /&gt;
 remoteArrayListClass := bridge resolveType:&#039;java.util.ArrayList&#039;.&lt;br /&gt;
 myList := remoteArrayListClass new:10.&lt;br /&gt;
 myList add:&#039;hello&#039;.&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
This works without any remote code injection and is typically a bit faster than via Groovy. However, the language syntax is the local expecco syntax, which might be inconvenient to hard-core Java programmers (and the same is true for proxies to other language&#039;s bridge objects).&lt;br /&gt;
&lt;br /&gt;
Remote proxy objects are also returned by the &amp;quot;&amp;lt;code&amp;gt;makeRef&amp;lt;/code&amp;gt;&amp;quot; API as described in the API documentation, and when remote objects are inspected in the expecco data inspector window.&lt;br /&gt;
&lt;br /&gt;
== What can I do if a Bridge is Dead(locked) ==&lt;br /&gt;
&lt;br /&gt;
The code executed in the bridge may run into an endless loop (for example, if your elementary code contains a bug), or when it calls a function which does not return.&lt;br /&gt;
As a consequence, the corresponding elementary action will not finish.&lt;br /&gt;
&lt;br /&gt;
Try to stop or abort the execution via the corresponding toolbar function (in the expecco editor page).&lt;br /&gt;
Even though the corresponding expecco action has now finished (i.e. expecco will no longer wait for the action&#039;s return value and status), a problem might be reported when expecco is about to call another action inside that bridge: the bridge will still be in its loop, and the bridge will therefore not respond and expecco will report that the bridge is no longer answering.&lt;br /&gt;
&lt;br /&gt;
You now have to kill the bridge, and start a new one, with the undesired consequence of loosing any state inside the previous bridge incarnation. Especially, any connections in the previous bridge are now lost.&lt;br /&gt;
&lt;br /&gt;
To kill the bridge, perform one of the following:&lt;br /&gt;
* click the &amp;quot;&#039;&#039;Terminate Bridge&#039;&#039;&amp;quot; toolbar button in the &amp;quot;Test/Demo&amp;quot; tab.&amp;lt;br&amp;gt;This will terminate that language&#039;s bridges&lt;br /&gt;
* select &amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Debugging&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Close all Bridge Connections&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;This will terminate all running bridges (maybe this is not what you want)&lt;br /&gt;
* select &amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Debugging&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;OS Process Monitor&#039;&#039;&amp;quot; to get a list of all OS processes which have been started by expecco, select the one to terminate and choose &amp;quot;&#039;&#039;Terminate&#039;&#039;&amp;quot; from the context menu.&lt;br /&gt;
* select and terminate the bridge process in the Task Manager (Windows) or find its processID with the &amp;quot;ps&amp;quot; command and terminate it via the &amp;quot;kill&amp;quot; command (Unix)&lt;br /&gt;
&lt;br /&gt;
If the bridge was not originally started by expecco (eg. if it is a remote bridge, which was started automatically or by a remote command), you have to log into that remote machine and terminate the bridge there.&lt;br /&gt;
&lt;br /&gt;
In a production system, you may add time limits to actions which might suffer from locked bridges, and add a bridge-terminate action (found in the Standard Library) to be triggered via the exception pin of the time-controlled action.&lt;br /&gt;
&lt;br /&gt;
== I got an Object (Reference) from a Bridge but Access to its Fields is Slow ==&lt;br /&gt;
Be reminded ([[#What_are_Proxy_Objects |see above]]), that when an object (reference) is returned from a bridge to expecco, a proxy (placeholder object) is created inside expecco, which is what appears at the output pin.&lt;br /&gt;
This proxy object will intercept any calls to it and forward them to the real object inside the bridge, then wait for a response (the return value) and encode that again as a proxy.&lt;br /&gt;
&lt;br /&gt;
Thus, every such operation involves a round trip which is typically in the order of milliseconds (if the bridge is on a remote machine, and additional routers/switches are involved, the round trip times may even be in the 10-millisecond range).&lt;br /&gt;
These times can easily add up to seconds, for example if you enumerate a remote collection with many elements. &lt;br /&gt;
&lt;br /&gt;
Possible solutions to that problem:&lt;br /&gt;
* install the code which enumerates the object inside the bridge (i.e. define another elementary action or require/import a piece of code which contains this functionality. Then call this code which runs completely inside the bridge.&lt;br /&gt;
* pass the object&#039;s encoding (for example as JSON) and decode it on the expecco side for the enumeration. This one call will then probably run much slower, but further processing (enumeration of elements) is much faster. This is the way to go, if bulk data has been collected inside the bridge, which has to be analyzed inside expecco afterwards.&amp;lt;br&amp;gt;Be aware though, that the object inside expecco is effectively a &amp;quot;copy&amp;quot; of the object; if the object will change inside the bridge, expecco will still present the old state.&lt;br /&gt;
&lt;br /&gt;
== I get Timeouts - how can I change the Defaults ? ==&lt;br /&gt;
There is currently no dialog to change the default settings (except for the CBridge in v23.1). &lt;br /&gt;
&lt;br /&gt;
However, the default values which control the timeouts can be changed via Smalltalk expressions (or corresponding JavaScript expressions).&lt;br /&gt;
&amp;lt;br&amp;gt;To set a common default for all Python, CBridge and NodeJS bridge&amp;lt;br&amp;gt;In Smalltalk syntax:&lt;br /&gt;
 SimpleBridge connectTimeout: &#039;&#039;seconds&#039;&#039;.     &lt;br /&gt;
 SimpleBridge defineAckTimeoutMS: &#039;&#039;millis&#039;&#039;.  &lt;br /&gt;
 SimpleBridge executeAckTimeoutMS: &#039;&#039;millis&#039;&#039;.&lt;br /&gt;
or in JavaScript syntax:&lt;br /&gt;
 SimpleBridge.connectTimeout(&#039;&#039;seconds&#039;&#039;);     &lt;br /&gt;
 SimpleBridge.defineAckTimeoutMS(&#039;&#039;millis&#039;&#039;);  &lt;br /&gt;
 SimpleBridge.executeAckTimeoutMS(&#039;&#039;millis&#039;&#039;);&lt;br /&gt;
&lt;br /&gt;
We are sorry for the inconsistent argument (seconds vs. milliseconds); these result from history and later added features. We will provide a consistent interface in later versions.&lt;br /&gt;
 &lt;br /&gt;
To set the default for individual language bridges:&lt;br /&gt;
 PythonBridge connectTimeout: &#039;&#039;seconds&#039;&#039;.      &lt;br /&gt;
 PythonBridge defineAckTimeoutMS: &#039;&#039;millis&#039;&#039;.  &lt;br /&gt;
 PythonBridge executeAckTimeoutMS: &#039;&#039;millis&#039;&#039;. &lt;br /&gt;
 &lt;br /&gt;
 CBridge connectTimeout: &#039;&#039;seconds&#039;&#039;.      &lt;br /&gt;
 CBridge defineAckTimeoutMS: &#039;&#039;millis&#039;&#039;.  &lt;br /&gt;
 CBridge executeAckTimeoutMS: &#039;&#039;millis&#039;&#039;. &lt;br /&gt;
 &lt;br /&gt;
 NodeJSBridge connectTimeout: &#039;&#039;seconds&#039;&#039;.      &lt;br /&gt;
 NodeJSBridge defineAckTimeoutMS: &#039;&#039;millis&#039;&#039;.  &lt;br /&gt;
 NodeJSBridge executeAckTimeoutMS: &#039;&#039;millis&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
You can retrieve the default values via corresponding getter messages:&lt;br /&gt;
 &amp;lt;someBridgeClass&amp;gt; connectTimeout =&amp;gt; that bridge type&#039;s connectTimeout&lt;br /&gt;
 &amp;lt;someBridgeClass&amp;gt; defineAckTimeoutMS =&amp;gt; that bridge type&#039;s defineAckTimeout in milliseconds&lt;br /&gt;
 &amp;lt;someBridgeClass&amp;gt; executeAckTimeoutMS =&amp;gt; that bridge type&#039;s executeAckTimeout in milliseconds&lt;br /&gt;
&lt;br /&gt;
&amp;quot;connectTimeout&amp;quot; is relevant, when connecting to a remote machine;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;quot;defineAckTimeoutMS&amp;quot; is the time expecco will wait for a confirmation after an action&#039;s code has been transferred to the bridge;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;quot;executeAckTimeoutMS&amp;quot; is the time expecco waits for an acknowledge after sending an action-step&#039;s data (pin values). See note below. &lt;br /&gt;
&lt;br /&gt;
To be effective, these default values must be set before a bridge connection is setup.&lt;br /&gt;
You can either place these expressions into the &amp;quot;startup.rc&amp;quot; file (in the expecco installation folder), or into a test suite as an elementary (Smalltalk or JavaScript) action which is executed as post-load action or as a prepare action of a testplan.&lt;br /&gt;
&lt;br /&gt;
The above control the default values for new bridge connections. You can also change an existing connection&#039;s parameter by sending the same messages to a concrete bridge.&lt;br /&gt;
&lt;br /&gt;
Expecco 21.2: &lt;br /&gt;
:You may have to adjust the executeAckTimeout, if huge amounts of data are transfered.&lt;br /&gt;
:(we encountered this limit eg. when transferring a vector of 1 mio floats to the bridge)&lt;br /&gt;
Expecco 22.1:&lt;br /&gt;
:Expecco adjusts this automatically to match the amount of data.&lt;br /&gt;
&lt;br /&gt;
== I don&#039;t see the difference between DLL actions and bridged C-actions; when to use which? ==&lt;br /&gt;
&lt;br /&gt;
DLL action blocks - when executed - will ensure that the specified DLL (shared object) is loaded into the running expecco program and directly called from within expecco. In other words: it executes in the same address space as expecco.&lt;br /&gt;
&lt;br /&gt;
On the other hand, C-Bridge actions are executed by a separate program (the &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;) and expecco communicates with that program via a socket connection.&lt;br /&gt;
&lt;br /&gt;
Both mechanisms have advantages and disadvantages:&lt;br /&gt;
&lt;br /&gt;
DLL actions:&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; very fast call (no remote procedure call)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; very fast data transmission (especially, big vectors can be passed quickly)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; can be used if only the binary is present and no compiler toolchain is needed/wanted&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; the called code may crash the expecco executable if it misbehaves or is provided with wrong arguments&amp;lt;BR&amp;gt;(notice: although expecco tries to catch any illegal memory references, it may still happen, that a called DLL-function misbehaves and overwrites/manipulates memory which it is not supposed to. For example, if it overwrites any data of expecco, there may be a later crash in expecco.&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; the called code may block expecco or slow down the user interface&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; the called code may exit/terminate the current thread or the program and thus terminate expecco (i.e. make sure, it does not call &amp;quot;&amp;lt;code&amp;gt;exit()&amp;lt;/code&amp;gt;&amp;quot;). Although expecco sets the &amp;quot;atexit&amp;quot; handler, shared libraries may still find other means to terminate.&lt;br /&gt;
&lt;br /&gt;
Bridged Calls:&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; code runs completely isolated from expecco - the bridge may crash due to bad data or a bad library call, but expecco will remain alive. You&#039;ll get a fail-status of the corresponding action in expecco.&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; additional data conversions or checks are easily added (in case the called function&#039;s interface requires complicated object structures, such as complex structures/unions, pointers to pointers, pointers to return values etc.)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; better support for debugging (writing log-entries, adding printfs or running the cBridge under a debugger)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; the expecco UI will not be blocked by a runaway bridged action&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; the bridged action can be executed on the local or on a remote machine&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; the bridged action can be executed on a different operating system or CPU architecture (eg. expecco runs on a Linux machine, the bridge on Windows or even in a RasperryPI)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; multiple bridged C actions can execute in parallel on multiple machines or in multiple processes on one machine&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; the round trip times are quite long compared to direct DLL calls (in the order of milliseconds, in contrast to direct DLL calls, which run in the order of nanoseconds)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; multiple round trips if many fields of returned object references have to be accessed&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; a C-compiler toolchain is needed (on the local or the target system, where the bridged C code is to be executed)&lt;br /&gt;
  &lt;br /&gt;
Despite the above mentioned performance differences, we highly recommend to use bridged C actions instead of direct DLL calls, iff the performance/round trip times are acceptable. The added safety due to the process boundaries makes it much easier to develop and maintain those.&lt;br /&gt;
&lt;br /&gt;
== How can I debug the code in the bridge ==&lt;br /&gt;
Expecco does include some limited debugging support for bridged code, but the level of support is different between individual bridges:&lt;br /&gt;
* Python, Node, Smalltalk, Groovy&amp;lt;br&amp;gt;Support breakpoints and single stepping. Walkback and some access to remote objects.&lt;br /&gt;
* C&amp;lt;br&amp;gt;Walkback and some access to remote objects. &lt;br /&gt;
&lt;br /&gt;
To debug bridged C-code, we recommend executing the bridge inside a debugger; for example, under Unix, you would compile your framework with the &amp;quot;-g&amp;quot; (debug) option, then open a shell terminal window and start the bridge under a debugger (say &amp;quot;lldb&amp;quot; or &amp;quot;gdb&amp;quot;) with:&lt;br /&gt;
 lldb cBridge&lt;br /&gt;
 &amp;gt;&amp;gt; r --server --port 8899&lt;br /&gt;
then change the expecco settings for the CBridge to &amp;quot;&#039;&#039;Connect to an already running bridge&#039;&#039;&amp;quot; and also define the port  there. Expecco will then connect to your debugged bridge instead of starting one itself.&lt;br /&gt;
A similar setup is to be used when debugging with Eclipse or Visual Studio on Windows. &lt;br /&gt;
&lt;br /&gt;
To debug Python code, you can check the &amp;quot;&#039;&#039;Disable Debugger&#039;&#039;&amp;quot; flag in the Python settings, and attach a Python debugger (eg. VSCode) to the running Python process.&lt;br /&gt;
&lt;br /&gt;
== Security Considerations ==&lt;br /&gt;
Make sure that remote bridges are only reachable from inside your local network and only from trusted hosts. You should configure your firewall as appropriate, or run tests in an isolated network. You may also configure switches and routers to limit access, or use a secondary interface (alternative lan or wifi).&lt;br /&gt;
&lt;br /&gt;
Another recommened option is to setup an ssh tunnel for the particular port(s) in question.&lt;br /&gt;
&lt;br /&gt;
All bridges support command line arguments to only accept connections from a particular host; this option should be used if a remote bridge is started via a remote shell command.&lt;br /&gt;
&lt;br /&gt;
=== SSL/TLS Encrypted Connections ===&lt;br /&gt;
All bridges support SSL/TLS encrypted connections. This provides both confidentiality and (with mutual TLS) authentication.&lt;br /&gt;
&lt;br /&gt;
To enable SSL for a bridge connection, configure the following in the expecco bridge settings (under &amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;Python&#039;&#039;&amp;quot;):&lt;br /&gt;
* &#039;&#039;&#039;Use SSL&#039;&#039;&#039;: enable TLS encryption&lt;br /&gt;
* &#039;&#039;&#039;Certificate File&#039;&#039;&#039;: PEM file containing the server certificate and private key (used by the bridge process)&lt;br /&gt;
* &#039;&#039;&#039;Client Certificate&#039;&#039;&#039;: the client certificate for mutual TLS authentication. The PEM content is stored directly in the expecco preferences. Use the &#039;&#039;&#039;&amp;quot;...&amp;quot;&#039;&#039;&#039; button to load from a file, &#039;&#039;&#039;&amp;quot;?&amp;quot;&#039;&#039;&#039; to inspect the certificate chain, and &#039;&#039;&#039;&amp;quot;X&amp;quot;&#039;&#039;&#039; to clear it. On load, the certificate chain is validated (expiry, CA flag, key identifier match).&lt;br /&gt;
&lt;br /&gt;
When the client certificate PEM includes the CA certificate in the chain, the CA is automatically trusted for server verification.&lt;br /&gt;
&lt;br /&gt;
Certificate generation is built into the cBridge and Python bridge executables using Ed25519 keys:&lt;br /&gt;
 cBridge --generateCA ca.pem --cn &amp;quot;My CA&amp;quot;&lt;br /&gt;
 cBridge --generateCert server.pem --ca ca.pem --cn myhost&lt;br /&gt;
 cBridge --generateCert client.pem --ca ca.pem --cn expecco&lt;br /&gt;
&lt;br /&gt;
Generated PEM files include the full certificate chain.&lt;br /&gt;
&lt;br /&gt;
For mutual TLS (mTLS), start the remote bridge with the &amp;lt;code&amp;gt;--clientAuth&amp;lt;/code&amp;gt; flag:&lt;br /&gt;
 cBridge --port 8855 --ssl --cert server.pem --clientAuth&lt;br /&gt;
&lt;br /&gt;
See [[Cbridge_setup#SSL/TLS_Connection_to_a_Remote_CBridge|CBridge Setup: SSL/TLS Connection]] for detailed instructions.&lt;br /&gt;
&lt;br /&gt;
== File Transfer and Remote Directory Operations ==&lt;br /&gt;
All bridges (C, Python, NodeJS, Ruby, Dart, Smalltalk) support transferring files between expecco and the remote bridge:&lt;br /&gt;
* &amp;lt;code&amp;gt;putFile: localPath to: remotePath&amp;lt;/code&amp;gt; — upload a file to the bridge&lt;br /&gt;
* &amp;lt;code&amp;gt;getFile: remotePath to: localPath&amp;lt;/code&amp;gt; — download a file from the bridge&lt;br /&gt;
* &amp;lt;code&amp;gt;makeDirectory: remotePath&amp;lt;/code&amp;gt; — create a directory (including parent directories) on the bridge&lt;br /&gt;
&lt;br /&gt;
These operations use the existing bridge connection and work over SSL if enabled. They are useful for deploying test data, collecting results, or transferring compiled binaries.&lt;br /&gt;
&lt;br /&gt;
== [OS X Only] CrashReporter Dialog Boxes ==&lt;br /&gt;
You may get annoying dialog boxes telling about &amp;quot;&#039;&#039;Unexpected Termination&#039;&#039;&amp;quot; whenever a bridge is hard terminated via the &amp;quot;&#039;&#039;Terminate&#039;&#039;&amp;quot; button in the debugger.&lt;br /&gt;
&lt;br /&gt;
This is a &amp;quot;&#039;&#039;feature&#039;&#039;&amp;quot; of the OS X system, not controlled by expecco.&lt;br /&gt;
&lt;br /&gt;
You can disable this by executing the following command in a console terminal:&lt;br /&gt;
 defaults write com.apple.CrashReporter DialogType none&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Back to [[FAQ/en | FAQ]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Cbridge_setup&amp;diff=31330</id>
		<title>Cbridge setup</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Cbridge_setup&amp;diff=31330"/>
		<updated>2026-05-27T13:48:21Z</updated>

		<summary type="html">&lt;p&gt;Sv: update SSL settings: client cert stored in prefs, load/inspect/clear buttons, chain validation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= CBridge Setup =&lt;br /&gt;
&lt;br /&gt;
In order to compile and execute C/C++ actions, a C compiler toolchain has to be installed on the machine, where the Bridge executable (Windows: &amp;lt;code&amp;gt;cBridge.exe&amp;lt;/code&amp;gt; / Unix: &amp;lt;code&amp;gt;cBridge&amp;lt;/code&amp;gt;) runs.&lt;br /&gt;
&amp;lt;br&amp;gt;Typically this is the local machine on which expecco runs, but it may be any other machine in your reachable network.&lt;br /&gt;
&amp;lt;br&amp;gt;The CPU architecture and or operating system may be (and often is) different from the CPU which executes expecco, therefore the C-code is compiled on the target machine (i.e. not cross-compiled).&lt;br /&gt;
&lt;br /&gt;
== C-Compiler Toolchain Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Windows + Borland Compiler (32bit) ===&lt;br /&gt;
The compiler is a bit outdated, but still free and useful.&amp;lt;br&amp;gt;However, it only supports 32bit programs. Thus, it cannot be used if you need access to 64bit dlls.&amp;lt;br&amp;gt;(on the other hand, if you need to interact with a 32bit dll to communicate with some hardware device, you&#039;ll must use a 32bit cBridge).&lt;br /&gt;
&lt;br /&gt;
We recommend to install the toolchain at its default installation path (typically &amp;quot;&amp;lt;code&amp;gt;C:\borland&amp;lt;/code&amp;gt;&amp;quot;). Then, the provided cc-compile script for borland can be used unchanged.&lt;br /&gt;
&lt;br /&gt;
=== Windows + MINGW Compiler (32 or 64bit) ===&lt;br /&gt;
The mingw toolchain is based on the gcc compiler. This is recommended and preferred over borland.&lt;br /&gt;
&lt;br /&gt;
=== Windows + Microsoft Visual C / Visual Studio === &lt;br /&gt;
There is no need to install the full Visual Studio IDE. Only the C-compiler, linker and libraries are required. &lt;br /&gt;
Thus, you can untoggle all additional optional packages during the installation.&lt;br /&gt;
&lt;br /&gt;
Check for the &amp;quot;&amp;lt;code&amp;gt;cl&amp;lt;/code&amp;gt;&amp;quot; command (being in your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;) or find and remember the path to it (typically somewhere under &amp;quot;&amp;lt;code&amp;gt;c:\Program Files\Microsoft\...&amp;lt;/code&amp;gt;&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Unix / Linux ===&lt;br /&gt;
Install whatever packages are required; check if the &amp;quot;&amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;&amp;quot; command is found along your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; (before starting expecco),&lt;br /&gt;
or alternatively, remember the path to the &amp;quot;&amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;&amp;quot; command.&lt;br /&gt;
 &lt;br /&gt;
Regardless of which compiler you use, &lt;br /&gt;
please remember the installation path. You will need it later unless the compiler is found along your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Quick Check if it Already Works ==&lt;br /&gt;
We provide a number of setups for common installations.&lt;br /&gt;
In many situations, this will already be correct for your setup.&lt;br /&gt;
&lt;br /&gt;
To check, create a new elementary-bridgedC action, and run it.&lt;br /&gt;
(the initial tree which is created with examples also contains a simple C action).&lt;br /&gt;
&lt;br /&gt;
If you get a green OK result, you&#039;re already done.&lt;br /&gt;
If not, your setup needs to be adjusted.&lt;br /&gt;
&lt;br /&gt;
Read the chapter at the end of this page on step-by-step setup verification.&lt;br /&gt;
&lt;br /&gt;
== The Compile Script ==&lt;br /&gt;
For compilation, the cBridge program calls a batch/shell script, which is responsible for compilation of the C-code to a DLL/shared object file.&lt;br /&gt;
This script may need to be edited.&lt;br /&gt;
We recommend, that you navigate to the folder where the cBridge executable is located, take one of the existing scripts as template, make a copy under a new name, and then edit this copy to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Take one of the existing scripts which uses the same compiler (borland/mingw/visualC).&lt;br /&gt;
&lt;br /&gt;
In the editor, check the &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; setting, and edit it as appropriate, finally save it.&lt;br /&gt;
&lt;br /&gt;
Back in expecco, go to the cBridge settings under the external-script settings,&lt;br /&gt;
and change the CC-script setting there (&amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Shutdown any already running bridge (&amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Debugging&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Shutdown all Bridge Connections&#039;&#039;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Go back to the C-action and repeat the quick check.&lt;br /&gt;
&lt;br /&gt;
Eventually, you should see a green OK result.&lt;br /&gt;
&lt;br /&gt;
== Special Additional Libraries ==&lt;br /&gt;
&lt;br /&gt;
It may be required that your C-code needs additional shared libraries to be linked against your code.&lt;br /&gt;
The easiest is to add those libraries and corresponding command line options to the CC-script&#039;s compiler call command.&lt;br /&gt;
The details depend on the compiler used.&lt;br /&gt;
You may need to do a &amp;quot;&amp;lt;code&amp;gt;cc --help&amp;lt;/code&amp;gt;&amp;quot;, &amp;quot;&amp;lt;code&amp;gt;cl/help&amp;lt;/code&amp;gt;&amp;quot; or similar, to figure out which command line options are required.&lt;br /&gt;
&lt;br /&gt;
= Remote C-Bridges =&lt;br /&gt;
You can run any number of cBridges on any machine within your network (and also multiple cBridges on your local or a remote machine), and execute C-actions there. For this, the following setup steps are to be performed:&lt;br /&gt;
&lt;br /&gt;
* copy the cbridge executable and its support files to a new folder on the target machine; the files are found below the expecco installation folder&amp;lt;br&amp;gt;(eg. &amp;quot;&amp;lt;code&amp;gt;c:\Program Files\exept\expecco&amp;lt;/code&amp;gt;&amp;quot;) in &amp;quot;&amp;lt;code&amp;gt;packages\bridgeFramework\cBridge\cLibrary&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
:The easiest way is to copy the whole folder. Make sure that the copied folder matches the target machine&#039;s architecture (i.e. if the target is a Linux machine, you must obviously copy the files from a Linux installation, and same so for Windows machines).&lt;br /&gt;
&lt;br /&gt;
* on the remote machine, make sure that the cBridge is running; either by executing a remote command from inside your expecco suite, or by adding the cBridge startup to a cron tab (Unix/Linux) or the service list or autoexec.bat (Windows).&lt;br /&gt;
:As a test, start it manually with: &amp;quot;&amp;lt;code&amp;gt;cBridge --port 8856&amp;lt;/code&amp;gt;&amp;quot;, and connect to it from expecco (change the settings (&amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;) to connect to an already running cBridge at that port).&lt;br /&gt;
&lt;br /&gt;
* if in doubt, start the cBridge with a &amp;quot;&amp;lt;code&amp;gt;--help&amp;lt;/code&amp;gt;&amp;quot; option.&lt;br /&gt;
&lt;br /&gt;
=== CBridge Command Line Arguments ===&lt;br /&gt;
&lt;br /&gt;
* --help&amp;lt;br&amp;gt;Prints a list of the supported comamnd line arguments&lt;br /&gt;
* --localOnly&amp;lt;br&amp;gt;only accept connections from the local machine&lt;br /&gt;
* --host &#039;&#039;hostname&#039;&#039;&amp;lt;br&amp;gt;connect to that host instead of waiting for incoming connections&lt;br /&gt;
* --port&amp;lt;br&amp;gt;the TCP port on which the bridge is waiting for incoming requests or connecting to (with --host argument)&lt;br /&gt;
* -- keepAlive&amp;lt;br&amp;gt;after the first connection, keep on waiting for more incoming connections. Without this, the bridge will only handle a single session and exit after that.&lt;br /&gt;
* --log&amp;lt;br&amp;gt;log actions on stderr&lt;br /&gt;
* --log2&amp;lt;br&amp;gt;even more detailed log&lt;br /&gt;
* --ccScript &#039;&#039;scriptFile&#039;&#039;&amp;lt;br&amp;gt;Use the given script file (a shell or batch script) to compile&lt;br /&gt;
* --includes &#039;&#039;files&#039;&#039;&amp;lt;br&amp;gt;Additional include folders passed to the compile script&lt;br /&gt;
* --dlls &#039;&#039;files&#039;&#039;&amp;lt;br&amp;gt;Additional dlls (shared libraries) to be linked against actions&lt;br /&gt;
* --testCompile&amp;lt;br&amp;gt;For a standalone selftest if compilation  works. Compiles a dummy almost empty source file&lt;br /&gt;
&lt;br /&gt;
More arguments may be recognized, depending on the cBridge version. Use &amp;quot;--help&amp;quot; to see more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SSL/TLS Connection to a Remote CBridge ===&lt;br /&gt;
&lt;br /&gt;
The cBridge supports encrypted TLS connections with optional mutual certificate authentication (mTLS).&lt;br /&gt;
&lt;br /&gt;
==== Generating Certificates ====&lt;br /&gt;
The cBridge can generate Ed25519 CA and server/client certificates directly:&lt;br /&gt;
&lt;br /&gt;
 cBridge --generateCA ~/.ssl/ca.pem --cn &amp;quot;My CA&amp;quot; --days 3650&lt;br /&gt;
 cBridge --generateCert ~/.ssl/server.pem --ca ~/.ssl/ca.pem --cn myhost&lt;br /&gt;
 cBridge --generateCert ~/.ssl/client.pem --ca ~/.ssl/ca.pem --cn expecco&lt;br /&gt;
&lt;br /&gt;
Each generated PEM file contains the certificate, private key, and the CA certificate chain. The &amp;lt;code&amp;gt;--cn&amp;lt;/code&amp;gt; option sets the common name (default: localhost), &amp;lt;code&amp;gt;--days&amp;lt;/code&amp;gt; sets the validity period (default: 3650).&lt;br /&gt;
&lt;br /&gt;
The same certificate generation is also available via the Python bridge script (&amp;lt;code&amp;gt;--generateCA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;--generateCert&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Starting the CBridge with SSL ====&lt;br /&gt;
&lt;br /&gt;
 cBridge --port 8855 --ssl --cert ~/.ssl/server.pem&lt;br /&gt;
&lt;br /&gt;
A combined PEM file (certificate + key in one file) is used by default. Separate files are also supported:&lt;br /&gt;
&lt;br /&gt;
 cBridge --port 8855 --ssl --cert server.crt --key server.key&lt;br /&gt;
&lt;br /&gt;
==== Mutual TLS (Client Certificate Authentication) ====&lt;br /&gt;
&lt;br /&gt;
To require clients to present a certificate signed by the same CA:&lt;br /&gt;
&lt;br /&gt;
 cBridge --port 8855 --ssl --cert ~/.ssl/server.pem --ca ~/.ssl/ca.pem --clientAuth&lt;br /&gt;
&lt;br /&gt;
Clients must then be configured with a client certificate (see the expecco CBridge settings dialog).&lt;br /&gt;
&lt;br /&gt;
==== Expecco Settings for SSL ====&lt;br /&gt;
&lt;br /&gt;
In expecco, configure the CBridge connection under &amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;:&lt;br /&gt;
* &#039;&#039;&#039;Use SSL&#039;&#039;&#039;: enable encrypted connection&lt;br /&gt;
* &#039;&#039;&#039;Certificate File&#039;&#039;&#039;: the server certificate PEM (used by the bridge process)&lt;br /&gt;
* &#039;&#039;&#039;Client Certificate&#039;&#039;&#039;: load a client certificate PEM for mutual TLS authentication. The certificate is stored in the expecco preferences (not as a file path). Use the buttons:&lt;br /&gt;
** &#039;&#039;&#039;&amp;quot;...&amp;quot;&#039;&#039;&#039; to load a certificate from a PEM file&lt;br /&gt;
** &#039;&#039;&#039;&amp;quot;?&amp;quot;&#039;&#039;&#039; to inspect the certificate chain (shows subject, issuer, validity, algorithm, CA flag, private key presence, and chain verification via AuthorityKeyIdentifier)&lt;br /&gt;
** &#039;&#039;&#039;&amp;quot;X&amp;quot;&#039;&#039;&#039; to remove the stored certificate&lt;br /&gt;
: On load, the certificate chain is validated: the leaf certificate must not be expired, the CA must have the BasicConstraints CA flag set, and the AuthorityKeyIdentifier must match the CA&#039;s SubjectKeyIdentifier (or the issuer DN must match).&lt;br /&gt;
&lt;br /&gt;
When the client certificate PEM includes the CA certificate in the chain, the CA is automatically trusted for server certificate verification — no need to enable &amp;quot;Allow Self-Signed Certificates&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The same SSL and client certificate settings are also available in the Python bridge settings dialog (&amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Python&#039;&#039;&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==== File Transfer and Directory Operations ====&lt;br /&gt;
All bridges (C, Python, NodeJS, Ruby, Dart, Smalltalk) support file transfer to and from the remote bridge:&lt;br /&gt;
* &amp;lt;code&amp;gt;putFile: localPath to: remotePath&amp;lt;/code&amp;gt; — upload a file&lt;br /&gt;
* &amp;lt;code&amp;gt;getFile: remotePath to: localPath&amp;lt;/code&amp;gt; — download a file&lt;br /&gt;
* &amp;lt;code&amp;gt;makeDirectory: remotePath&amp;lt;/code&amp;gt; — create a directory (including parent directories)&lt;br /&gt;
&lt;br /&gt;
These operations work over the bridge&#039;s existing connection (including SSL if enabled).&lt;br /&gt;
&lt;br /&gt;
= Step-by-Step Problem Finding/Fixing =&lt;br /&gt;
&lt;br /&gt;
=== Ensure that the cBridge executable is present and can be executed ===&lt;br /&gt;
* goto the expecco installation folder (typically somewhere under &amp;quot;&amp;lt;code&amp;gt;c:\Program Files\exept\expecco\&amp;lt;/code&amp;gt;&amp;quot;), and look for files named &amp;quot;&amp;lt;code&amp;gt;cBridge.exe&amp;lt;/code&amp;gt;&amp;quot; or &amp;quot;&amp;lt;code&amp;gt;cBridge32.exe&amp;lt;/code&amp;gt;&amp;quot; or &amp;quot;&amp;lt;code&amp;gt;cBridge&amp;lt;/code&amp;gt;&amp;quot; (under Unix/Linux). It is typically found in a sub folder named &amp;quot;&amp;lt;code&amp;gt;packages/expecco/bridgeFramework/cBridge&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
* open a shell- or cmd window (and navigate there).&lt;br /&gt;
* type &amp;quot;&amp;lt;code&amp;gt;cBridge --help&amp;lt;/code&amp;gt;&amp;quot; (with powershell: &amp;quot;&amp;lt;code&amp;gt;.\cBridge --help&amp;lt;/code&amp;gt;&amp;quot;)&lt;br /&gt;
:: if that fails (does not output some useful help text), make sure that it can be executed.&amp;lt;br&amp;gt;Maybe your security policy is too strict ot the installation is incomplete.&lt;br /&gt;
* start the bridge with &amp;quot;&amp;lt;code&amp;gt;cBridge --log&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
:: it should output some messages, the last being &amp;quot;&amp;lt;code&amp;gt;... listening on port xxxx&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
* if the cBridge is to run on a machine different from the one expecco is running on, make sure that the firewall allows for connections between the two machines (at the desired port nr)&lt;br /&gt;
* if required, use another port (i.e. run it with &amp;quot;&amp;lt;code&amp;gt;cBridge --log --port &amp;lt;Nr&amp;gt;&amp;lt;/code&amp;gt;&amp;quot;)&lt;br /&gt;
:: now we have a cBridge running, ready to accept connections from expecco at the given port nr.&lt;br /&gt;
* keep the shell/cmd window open; we will need it later (and also to see any diagnostic output)&lt;br /&gt;
&lt;br /&gt;
=== Ensure that the cBridge is reachable from expecco ===&lt;br /&gt;
for this, we first try to connect to that already running bridge (instead of letting expecco start it itself)&lt;br /&gt;
* in expecco, open the settings and navigate to the cBridge settings: &lt;br /&gt;
:: &amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;&lt;br /&gt;
* check the &amp;quot;&#039;&#039;Connect to already Running Bridge&#039;&#039;&amp;quot; box.&lt;br /&gt;
:: enter CBridge Host and Port as &amp;quot;&amp;lt;code&amp;gt;localhost&amp;lt;/code&amp;gt;&amp;quot; and the above port number (default is 8855)&lt;br /&gt;
* close the settings dialog (without saving the settings for now, when asked)&lt;br /&gt;
* open a new project.&amp;lt;br&amp;gt;The default project will contain a sample &amp;quot;Hello World&amp;quot; C-action, which can be used for the smoke test.&lt;br /&gt;
* in the expecco tree, find the &amp;quot;&amp;lt;code&amp;gt;Simple Elementary Action (C)&amp;lt;/code&amp;gt;&amp;quot; (under the &amp;quot;Activities&amp;quot; group).&lt;br /&gt;
* run it&lt;br /&gt;
:: if the error states that no connection could be setup, check your security policy, firewall etc. and repeat&lt;br /&gt;
:: if the error states that the compilation failed, the bridge connection works, but the compilation script setup is wrong. Proceed.&lt;br /&gt;
&lt;br /&gt;
=== Ensure that the compilation works ===&lt;br /&gt;
the cBridge needs a C-compiler for its operation; to verify,&lt;br /&gt;
* stop the manually started C-bridge (in the shell/cmd window) by pressing CTRL-C there&lt;br /&gt;
* depending on the C-compiler you intend to use, one of the &amp;quot;compile_xxx.bat&amp;quot; or &amp;quot;compile_xxx.sh&amp;quot; script files will be used. The task to be performed by these scripts is to compile a given C-file.&lt;br /&gt;
* check which compiler you installed and intent to use (MSVC, Mingw, gcc, clang or bcc), and look for a corresponding script file.&lt;br /&gt;
* check if the compiler is correctly installed (especially MSVC and MINGW sometimes make trouble here).&lt;br /&gt;
:: one particular problem with MSVC is when deinstalling and reinstalling another version. There seem to be leftover files and/or registry entries which may break the installation (this happend to one of our customers, and also to us when upgrading)&lt;br /&gt;
* Enter the compile-command (&amp;quot;cl&amp;quot;, &amp;quot;cc&amp;quot;, &amp;quot;gcc&amp;quot; or &amp;quot;clang&amp;quot;) on a command line (cmd or shell)&lt;br /&gt;
:: check that you get an answer&lt;br /&gt;
:: try to compile a one-liner (eg. &amp;quot;&amp;lt;code&amp;gt;main() { printf(&amp;quot;hello\n&amp;quot;); }&amp;lt;/code&amp;gt;&amp;quot; and run it to see if it links correctly. Run it!&lt;br /&gt;
* check if the bridge&#039;s cc script file works by running &amp;quot;&amp;lt;code&amp;gt;cBridge --log --ccScript &amp;lt;scriptfilename&amp;gt; --testCompile&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
:: if required, make a copy of one of the script files (the one you think is best to start with) and fix any path inside. If required, add echo statements to show what is going on. Keep the original script and create a new one.&lt;br /&gt;
:: under Windows: in the script, the PATH to the compiler is typically searched and set; for example by calling the vcvarsall.bat script which is provided by microsoft. If required, change the line which calls this (you can also add this batch-call to your autoexec.bat and remove the line from the script). In any case: at the end, the &amp;quot;cl&amp;quot; command should be found along your path. &lt;br /&gt;
* the --testCompile will generate a piece of code which outputs &amp;quot;here is foo&amp;quot;; i.e. on your system (we use mingw), the command is &amp;quot;&amp;lt;code&amp;gt;cBridge --log --ccScript compile_windows_mingw.bat&amp;quot; --testCompile&amp;lt;/code&amp;gt;&amp;quot;, and the output is &amp;quot;here is foo&amp;quot; - &amp;quot;void pointer size: 8&amp;quot;, followed by the usual &amp;quot;listening on port&amp;quot; message.&lt;br /&gt;
&lt;br /&gt;
=== Change the Settings in expecco ===&lt;br /&gt;
* open the cBridge settings again&lt;br /&gt;
* uncheck the &amp;quot;&#039;&#039;Connect to already running bridge&#039;&#039;&amp;quot; box&lt;br /&gt;
* enter the name of the compile script into the &amp;quot;CC-script&amp;quot; field (possibly the name of the new script)&lt;br /&gt;
* shutdown any already running bridge&lt;br /&gt;
* try executing the expecco-C action again&lt;br /&gt;
* if you get a green outcome, save your expecco settings.&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=FAQ_on_Bridges/en&amp;diff=31329</id>
		<title>FAQ on Bridges/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=FAQ_on_Bridges/en&amp;diff=31329"/>
		<updated>2026-05-27T07:02:32Z</updated>

		<summary type="html">&lt;p&gt;Sv: add SSL/TLS section and file transfer/makeDirectory documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Why Bridged Actions and not Script Actions? ==&lt;br /&gt;
&lt;br /&gt;
Script actions will start the language interpreter anew for every such action. Thus, no internal state can be held and transferred from one such action to another. Especially, it is not possible to open a communication channel on one script action, pass it back to expecco, and use it with different followup script actions (i.e. actions like &amp;quot;&#039;&#039;open-port&#039;&#039;&amp;quot; / &amp;quot;&#039;&#039;send message&#039;&#039;&amp;quot; / &amp;quot;&#039;&#039;receive message&#039;&#039;&amp;quot; / &amp;quot;&#039;&#039;close port&#039;&#039;&amp;quot; etc.).&lt;br /&gt;
&lt;br /&gt;
For this, it is required to call individual API-entries inside the bridge code, and return handles from the bridge to expecco, which can later be passed to other API entries.&amp;lt;br&amp;gt;This of course only makes sense, if the interpreter stays active during the lifetime of the connection.&lt;br /&gt;
&lt;br /&gt;
On the other hand, script actions are easier to use if a self contained program or app is to be started, or which performs a single processing action or which stays alive for longer, serving a socket or database. In any such case, there is no need to exchange individual data objects (such as handles) with expecco. It may however interact with expecco through other communication channels, such as sockets, RPC calls, SOAP interface or stdin/stdout text ports.&lt;br /&gt;
&lt;br /&gt;
Thus, you will typically use script actions to start server applications and bridged actions to call individual functions of a framework.&lt;br /&gt;
&lt;br /&gt;
== Which Languages are Supported ==&lt;br /&gt;
&lt;br /&gt;
By the time this chapter was written, bridged code execution is supported for:&lt;br /&gt;
* Java (using either Groovy or Jython as &amp;quot;&#039;&#039;glue&#039;&#039;&amp;quot; or directly using proxy objects in expecco)&lt;br /&gt;
* Python (both Python 2.7 and Python 3.x)&lt;br /&gt;
* .NET (using IronPython as &amp;quot;&#039;&#039;glue&#039;&#039;&amp;quot; or directly via proxy objects)&lt;br /&gt;
* NodeJS (i.e. JavaScript)&lt;br /&gt;
* C/C++ (Root/Cling is being developed currently)&lt;br /&gt;
* Smalltalk (Smalltalk/X and VisualWorks are currently supported; VisualAge may come in the near future, if there is sufficient customer interest)&lt;br /&gt;
* Ruby&lt;br /&gt;
Plans are to support Dart and Scheme in the future.&lt;br /&gt;
&lt;br /&gt;
== How are Bridges Started ==&lt;br /&gt;
&lt;br /&gt;
Bridges on the local machine are usually automatically started by expecco itself, when the first bridged action is executed (and stays alive, until expecco is terminated, or bridge connections are closed via the menu or via an expecco action block). Some language interpreters may take a few seconds to come up, so the first such action will execute relatively slow compared to later actions. You can add a dummy (empty) bridged action to your startup actions (project&#039;s postLoad action) to prepare the bridged interpreter and have it startup in parallel. &lt;br /&gt;
&lt;br /&gt;
Bridges on other machines, or additional bridges on the local machine must be started by executing a bridge start action block or simply by executing a shell/batch action to call the corresponding language interpreter (python, node, etc.). On Windows, a powershell action can be used for remote execution (given that your Windows security settings allow that). On Unix, a shell action executin a remote shell / ssh command could be used.&lt;br /&gt;
&lt;br /&gt;
== Who Initiates the Connection ==&lt;br /&gt;
&lt;br /&gt;
Normally, expecco opens a port and tells the bridge (via command line argument) to which port to connect. Depending on the setup of your network and firewall, it may sometimes be required to reverse this, and let the bridge open a port and expecco connect to it. &lt;br /&gt;
&lt;br /&gt;
For this, some bridges support command line arguments (&amp;quot;--server&amp;quot; and &amp;quot;--port&amp;quot;) to specify this behavior. Details are found in the individual bridge documentation pages.&lt;br /&gt;
&lt;br /&gt;
== Ports Used for Bridge Communication ==&lt;br /&gt;
&lt;br /&gt;
Unless ports are configured in the expecco settings,&lt;br /&gt;
the following default ports are used (bridges typically listen on or connect to this port, unless an explicit &amp;quot;--port&amp;quot; argument is given):&lt;br /&gt;
&lt;br /&gt;
 Python      8677 , 8678 (debug) &lt;br /&gt;
   Python2     8679 , 8680 (debug)&lt;br /&gt;
   Python3     8681 , 8682 (debug)&lt;br /&gt;
 Jython      8777 , 8778 (debug)&lt;br /&gt;
 IronPython  8787 , 8788 (debug)&lt;br /&gt;
 Node        8577 , 8578 (debug)&lt;br /&gt;
 Electron    8588 , 8589 (debug)&lt;br /&gt;
 [[Embedded_Systems_C_Bridge_API |CBridge]]     8855 (also for Root/Cling)&lt;br /&gt;
 Smalltalk   8877&lt;br /&gt;
 VASmalltalk 8879&lt;br /&gt;
 VWSmalltalk 8881&lt;br /&gt;
 Dart        8885&lt;br /&gt;
 Ruby        8977&lt;br /&gt;
 Scheme      8599&lt;br /&gt;
&lt;br /&gt;
Note: for some bridges, two connections are used, the debug port being used by the debugger to inspect data and to support breakpoints, single stepping etc. &lt;br /&gt;
&amp;lt;br&amp;gt;Note: expecco will try successive alternative ports, if the default port happens to be already in use.&lt;br /&gt;
&lt;br /&gt;
Debugging is not supported on all bridges (eg. the C-bridge does not).&lt;br /&gt;
&lt;br /&gt;
== What are Proxy Objects ==&lt;br /&gt;
These are placeholder objects inside expecco, which will look like regular Smalltalk (or JavaScript) objects inside expecco, but will forward any call via the socket ower to the bridge partner. &lt;br /&gt;
&lt;br /&gt;
Thus, they can be used transparently inside Smalltalk and JavaScript elementary actions, performing whatever is implemented in the remote side.&lt;br /&gt;
&lt;br /&gt;
For example, given a Java bridge connection named &amp;quot;bridge&amp;quot;, you can instantiate a remote object there from within your local JavaScript code with:&lt;br /&gt;
 var remoteArrayListClass, myList;&lt;br /&gt;
 remoteArrayListClass = bridge.resolveType(&amp;quot;java.util.ArrayList&amp;quot;);&lt;br /&gt;
 myList = new remoteArrayListClass(10);&lt;br /&gt;
 myList.add(&amp;quot;hello&amp;quot;);&lt;br /&gt;
 ...&lt;br /&gt;
in a Smalltalk action, the code is similar:&lt;br /&gt;
 |remoteArrayList myList|&lt;br /&gt;
 remoteArrayListClass := bridge resolveType:&#039;java.util.ArrayList&#039;.&lt;br /&gt;
 myList := remoteArrayListClass new:10.&lt;br /&gt;
 myList add:&#039;hello&#039;.&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
This works without any remote code injection and is typically a bit faster than via Groovy. However, the language syntax is the local expecco syntax, which might be inconvenient to hard-core Java programmers (and the same is true for proxies to other language&#039;s bridge objects).&lt;br /&gt;
&lt;br /&gt;
Remote proxy objects are also returned by the &amp;quot;&amp;lt;code&amp;gt;makeRef&amp;lt;/code&amp;gt;&amp;quot; API as described in the API documentation, and when remote objects are inspected in the expecco data inspector window.&lt;br /&gt;
&lt;br /&gt;
== What can I do if a Bridge is Dead(locked) ==&lt;br /&gt;
&lt;br /&gt;
The code executed in the bridge may run into an endless loop (for example, if your elementary code contains a bug), or when it calls a function which does not return.&lt;br /&gt;
As a consequence, the corresponding elementary action will not finish.&lt;br /&gt;
&lt;br /&gt;
Try to stop or abort the execution via the corresponding toolbar function (in the expecco editor page).&lt;br /&gt;
Even though the corresponding expecco action has now finished (i.e. expecco will no longer wait for the action&#039;s return value and status), a problem might be reported when expecco is about to call another action inside that bridge: the bridge will still be in its loop, and the bridge will therefore not respond and expecco will report that the bridge is no longer answering.&lt;br /&gt;
&lt;br /&gt;
You now have to kill the bridge, and start a new one, with the undesired consequence of loosing any state inside the previous bridge incarnation. Especially, any connections in the previous bridge are now lost.&lt;br /&gt;
&lt;br /&gt;
To kill the bridge, perform one of the following:&lt;br /&gt;
* click the &amp;quot;&#039;&#039;Terminate Bridge&#039;&#039;&amp;quot; toolbar button in the &amp;quot;Test/Demo&amp;quot; tab.&amp;lt;br&amp;gt;This will terminate that language&#039;s bridges&lt;br /&gt;
* select &amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Debugging&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Close all Bridge Connections&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;This will terminate all running bridges (maybe this is not what you want)&lt;br /&gt;
* select &amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Debugging&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;OS Process Monitor&#039;&#039;&amp;quot; to get a list of all OS processes which have been started by expecco, select the one to terminate and choose &amp;quot;&#039;&#039;Terminate&#039;&#039;&amp;quot; from the context menu.&lt;br /&gt;
* select and terminate the bridge process in the Task Manager (Windows) or find its processID with the &amp;quot;ps&amp;quot; command and terminate it via the &amp;quot;kill&amp;quot; command (Unix)&lt;br /&gt;
&lt;br /&gt;
If the bridge was not originally started by expecco (eg. if it is a remote bridge, which was started automatically or by a remote command), you have to log into that remote machine and terminate the bridge there.&lt;br /&gt;
&lt;br /&gt;
In a production system, you may add time limits to actions which might suffer from locked bridges, and add a bridge-terminate action (found in the Standard Library) to be triggered via the exception pin of the time-controlled action.&lt;br /&gt;
&lt;br /&gt;
== I got an Object (Reference) from a Bridge but Access to its Fields is Slow ==&lt;br /&gt;
Be reminded ([[#What_are_Proxy_Objects |see above]]), that when an object (reference) is returned from a bridge to expecco, a proxy (placeholder object) is created inside expecco, which is what appears at the output pin.&lt;br /&gt;
This proxy object will intercept any calls to it and forward them to the real object inside the bridge, then wait for a response (the return value) and encode that again as a proxy.&lt;br /&gt;
&lt;br /&gt;
Thus, every such operation involves a round trip which is typically in the order of milliseconds (if the bridge is on a remote machine, and additional routers/switches are involved, the round trip times may even be in the 10-millisecond range).&lt;br /&gt;
These times can easily add up to seconds, for example if you enumerate a remote collection with many elements. &lt;br /&gt;
&lt;br /&gt;
Possible solutions to that problem:&lt;br /&gt;
* install the code which enumerates the object inside the bridge (i.e. define another elementary action or require/import a piece of code which contains this functionality. Then call this code which runs completely inside the bridge.&lt;br /&gt;
* pass the object&#039;s encoding (for example as JSON) and decode it on the expecco side for the enumeration. This one call will then probably run much slower, but further processing (enumeration of elements) is much faster. This is the way to go, if bulk data has been collected inside the bridge, which has to be analyzed inside expecco afterwards.&amp;lt;br&amp;gt;Be aware though, that the object inside expecco is effectively a &amp;quot;copy&amp;quot; of the object; if the object will change inside the bridge, expecco will still present the old state.&lt;br /&gt;
&lt;br /&gt;
== I get Timeouts - how can I change the Defaults ? ==&lt;br /&gt;
There is currently no dialog to change the default settings (except for the CBridge in v23.1). &lt;br /&gt;
&lt;br /&gt;
However, the default values which control the timeouts can be changed via Smalltalk expressions (or corresponding JavaScript expressions).&lt;br /&gt;
&amp;lt;br&amp;gt;To set a common default for all Python, CBridge and NodeJS bridge&amp;lt;br&amp;gt;In Smalltalk syntax:&lt;br /&gt;
 SimpleBridge connectTimeout: &#039;&#039;seconds&#039;&#039;.     &lt;br /&gt;
 SimpleBridge defineAckTimeoutMS: &#039;&#039;millis&#039;&#039;.  &lt;br /&gt;
 SimpleBridge executeAckTimeoutMS: &#039;&#039;millis&#039;&#039;.&lt;br /&gt;
or in JavaScript syntax:&lt;br /&gt;
 SimpleBridge.connectTimeout(&#039;&#039;seconds&#039;&#039;);     &lt;br /&gt;
 SimpleBridge.defineAckTimeoutMS(&#039;&#039;millis&#039;&#039;);  &lt;br /&gt;
 SimpleBridge.executeAckTimeoutMS(&#039;&#039;millis&#039;&#039;);&lt;br /&gt;
&lt;br /&gt;
We are sorry for the inconsistent argument (seconds vs. milliseconds); these result from history and later added features. We will provide a consistent interface in later versions.&lt;br /&gt;
 &lt;br /&gt;
To set the default for individual language bridges:&lt;br /&gt;
 PythonBridge connectTimeout: &#039;&#039;seconds&#039;&#039;.      &lt;br /&gt;
 PythonBridge defineAckTimeoutMS: &#039;&#039;millis&#039;&#039;.  &lt;br /&gt;
 PythonBridge executeAckTimeoutMS: &#039;&#039;millis&#039;&#039;. &lt;br /&gt;
 &lt;br /&gt;
 CBridge connectTimeout: &#039;&#039;seconds&#039;&#039;.      &lt;br /&gt;
 CBridge defineAckTimeoutMS: &#039;&#039;millis&#039;&#039;.  &lt;br /&gt;
 CBridge executeAckTimeoutMS: &#039;&#039;millis&#039;&#039;. &lt;br /&gt;
 &lt;br /&gt;
 NodeJSBridge connectTimeout: &#039;&#039;seconds&#039;&#039;.      &lt;br /&gt;
 NodeJSBridge defineAckTimeoutMS: &#039;&#039;millis&#039;&#039;.  &lt;br /&gt;
 NodeJSBridge executeAckTimeoutMS: &#039;&#039;millis&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
You can retrieve the default values via corresponding getter messages:&lt;br /&gt;
 &amp;lt;someBridgeClass&amp;gt; connectTimeout =&amp;gt; that bridge type&#039;s connectTimeout&lt;br /&gt;
 &amp;lt;someBridgeClass&amp;gt; defineAckTimeoutMS =&amp;gt; that bridge type&#039;s defineAckTimeout in milliseconds&lt;br /&gt;
 &amp;lt;someBridgeClass&amp;gt; executeAckTimeoutMS =&amp;gt; that bridge type&#039;s executeAckTimeout in milliseconds&lt;br /&gt;
&lt;br /&gt;
&amp;quot;connectTimeout&amp;quot; is relevant, when connecting to a remote machine;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;quot;defineAckTimeoutMS&amp;quot; is the time expecco will wait for a confirmation after an action&#039;s code has been transferred to the bridge;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;quot;executeAckTimeoutMS&amp;quot; is the time expecco waits for an acknowledge after sending an action-step&#039;s data (pin values). See note below. &lt;br /&gt;
&lt;br /&gt;
To be effective, these default values must be set before a bridge connection is setup.&lt;br /&gt;
You can either place these expressions into the &amp;quot;startup.rc&amp;quot; file (in the expecco installation folder), or into a test suite as an elementary (Smalltalk or JavaScript) action which is executed as post-load action or as a prepare action of a testplan.&lt;br /&gt;
&lt;br /&gt;
The above control the default values for new bridge connections. You can also change an existing connection&#039;s parameter by sending the same messages to a concrete bridge.&lt;br /&gt;
&lt;br /&gt;
Expecco 21.2: &lt;br /&gt;
:You may have to adjust the executeAckTimeout, if huge amounts of data are transfered.&lt;br /&gt;
:(we encountered this limit eg. when transferring a vector of 1 mio floats to the bridge)&lt;br /&gt;
Expecco 22.1:&lt;br /&gt;
:Expecco adjusts this automatically to match the amount of data.&lt;br /&gt;
&lt;br /&gt;
== I don&#039;t see the difference between DLL actions and bridged C-actions; when to use which? ==&lt;br /&gt;
&lt;br /&gt;
DLL action blocks - when executed - will ensure that the specified DLL (shared object) is loaded into the running expecco program and directly called from within expecco. In other words: it executes in the same address space as expecco.&lt;br /&gt;
&lt;br /&gt;
On the other hand, C-Bridge actions are executed by a separate program (the &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;) and expecco communicates with that program via a socket connection.&lt;br /&gt;
&lt;br /&gt;
Both mechanisms have advantages and disadvantages:&lt;br /&gt;
&lt;br /&gt;
DLL actions:&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; very fast call (no remote procedure call)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; very fast data transmission (especially, big vectors can be passed quickly)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; can be used if only the binary is present and no compiler toolchain is needed/wanted&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; the called code may crash the expecco executable if it misbehaves or is provided with wrong arguments&amp;lt;BR&amp;gt;(notice: although expecco tries to catch any illegal memory references, it may still happen, that a called DLL-function misbehaves and overwrites/manipulates memory which it is not supposed to. For example, if it overwrites any data of expecco, there may be a later crash in expecco.&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; the called code may block expecco or slow down the user interface&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; the called code may exit/terminate the current thread or the program and thus terminate expecco (i.e. make sure, it does not call &amp;quot;&amp;lt;code&amp;gt;exit()&amp;lt;/code&amp;gt;&amp;quot;). Although expecco sets the &amp;quot;atexit&amp;quot; handler, shared libraries may still find other means to terminate.&lt;br /&gt;
&lt;br /&gt;
Bridged Calls:&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; code runs completely isolated from expecco - the bridge may crash due to bad data or a bad library call, but expecco will remain alive. You&#039;ll get a fail-status of the corresponding action in expecco.&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; additional data conversions or checks are easily added (in case the called function&#039;s interface requires complicated object structures, such as complex structures/unions, pointers to pointers, pointers to return values etc.)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; better support for debugging (writing log-entries, adding printfs or running the cBridge under a debugger)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; the expecco UI will not be blocked by a runaway bridged action&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; the bridged action can be executed on the local or on a remote machine&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; the bridged action can be executed on a different operating system or CPU architecture (eg. expecco runs on a Linux machine, the bridge on Windows or even in a RasperryPI)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(+)&#039;&#039;&#039; multiple bridged C actions can execute in parallel on multiple machines or in multiple processes on one machine&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; the round trip times are quite long compared to direct DLL calls (in the order of milliseconds, in contrast to direct DLL calls, which run in the order of nanoseconds)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; multiple round trips if many fields of returned object references have to be accessed&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;(-)&#039;&#039;&#039; a C-compiler toolchain is needed (on the local or the target system, where the bridged C code is to be executed)&lt;br /&gt;
  &lt;br /&gt;
Despite the above mentioned performance differences, we highly recommend to use bridged C actions instead of direct DLL calls, iff the performance/round trip times are acceptable. The added safety due to the process boundaries makes it much easier to develop and maintain those.&lt;br /&gt;
&lt;br /&gt;
== How can I debug the code in the bridge ==&lt;br /&gt;
Expecco does include some limited debugging support for bridged code, but the level of support is different between individual bridges:&lt;br /&gt;
* Python, Node, Smalltalk, Groovy&amp;lt;br&amp;gt;Support breakpoints and single stepping. Walkback and some access to remote objects.&lt;br /&gt;
* C&amp;lt;br&amp;gt;Walkback and some access to remote objects. &lt;br /&gt;
&lt;br /&gt;
To debug bridged C-code, we recommend executing the bridge inside a debugger; for example, under Unix, you would compile your framework with the &amp;quot;-g&amp;quot; (debug) option, then open a shell terminal window and start the bridge under a debugger (say &amp;quot;lldb&amp;quot; or &amp;quot;gdb&amp;quot;) with:&lt;br /&gt;
 lldb cBridge&lt;br /&gt;
 &amp;gt;&amp;gt; r --server --port 8899&lt;br /&gt;
then change the expecco settings for the CBridge to &amp;quot;&#039;&#039;Connect to an already running bridge&#039;&#039;&amp;quot; and also define the port  there. Expecco will then connect to your debugged bridge instead of starting one itself.&lt;br /&gt;
A similar setup is to be used when debugging with Eclipse or Visual Studio on Windows. &lt;br /&gt;
&lt;br /&gt;
To debug Python code, you can check the &amp;quot;&#039;&#039;Disable Debugger&#039;&#039;&amp;quot; flag in the Python settings, and attach a Python debugger (eg. VSCode) to the running Python process.&lt;br /&gt;
&lt;br /&gt;
== Security Considerations ==&lt;br /&gt;
Make sure that remote bridges are only reachable from inside your local network and only from trusted hosts. You should configure your firewall as appropriate, or run tests in an isolated network. You may also configure switches and routers to limit access, or use a secondary interface (alternative lan or wifi).&lt;br /&gt;
&lt;br /&gt;
Another recommened option is to setup an ssh tunnel for the particular port(s) in question.&lt;br /&gt;
&lt;br /&gt;
All bridges support command line arguments to only accept connections from a particular host; this option should be used if a remote bridge is started via a remote shell command.&lt;br /&gt;
&lt;br /&gt;
=== SSL/TLS Encrypted Connections ===&lt;br /&gt;
All bridges support SSL/TLS encrypted connections. This provides both confidentiality and (with mutual TLS) authentication.&lt;br /&gt;
&lt;br /&gt;
To enable SSL for a bridge connection, configure the following in the expecco bridge settings:&lt;br /&gt;
* &#039;&#039;&#039;Use SSL&#039;&#039;&#039;: enable TLS encryption&lt;br /&gt;
* &#039;&#039;&#039;Certificate File&#039;&#039;&#039;: PEM file containing the server certificate and private key (used by the bridge process)&lt;br /&gt;
* &#039;&#039;&#039;Client Certificate File&#039;&#039;&#039;: PEM file containing the client certificate and private key (presented to servers that require client authentication)&lt;br /&gt;
&lt;br /&gt;
Certificate generation is built into the cBridge and Python bridge executables using Ed25519 keys:&lt;br /&gt;
 cBridge --generateCA ca.pem --cn &amp;quot;My CA&amp;quot;&lt;br /&gt;
 cBridge --generateCert server.pem --ca ca.pem --cn myhost&lt;br /&gt;
 cBridge --generateCert client.pem --ca ca.pem --cn expecco&lt;br /&gt;
&lt;br /&gt;
Generated PEM files include the full certificate chain, so the CA is automatically trusted without additional configuration.&lt;br /&gt;
&lt;br /&gt;
For mutual TLS (mTLS), start the remote bridge with the &amp;lt;code&amp;gt;--clientAuth&amp;lt;/code&amp;gt; flag:&lt;br /&gt;
 cBridge --port 8855 --ssl --cert server.pem --clientAuth&lt;br /&gt;
&lt;br /&gt;
See [[Cbridge_setup#SSL/TLS_Connection_to_a_Remote_CBridge|CBridge Setup: SSL/TLS Connection]] for detailed instructions.&lt;br /&gt;
&lt;br /&gt;
== File Transfer and Remote Directory Operations ==&lt;br /&gt;
All bridges (C, Python, NodeJS, Ruby, Dart, Smalltalk) support transferring files between expecco and the remote bridge:&lt;br /&gt;
* &amp;lt;code&amp;gt;putFile: localPath to: remotePath&amp;lt;/code&amp;gt; — upload a file to the bridge&lt;br /&gt;
* &amp;lt;code&amp;gt;getFile: remotePath to: localPath&amp;lt;/code&amp;gt; — download a file from the bridge&lt;br /&gt;
* &amp;lt;code&amp;gt;makeDirectory: remotePath&amp;lt;/code&amp;gt; — create a directory (including parent directories) on the bridge&lt;br /&gt;
&lt;br /&gt;
These operations use the existing bridge connection and work over SSL if enabled. They are useful for deploying test data, collecting results, or transferring compiled binaries.&lt;br /&gt;
&lt;br /&gt;
== [OS X Only] CrashReporter Dialog Boxes ==&lt;br /&gt;
You may get annoying dialog boxes telling about &amp;quot;&#039;&#039;Unexpected Termination&#039;&#039;&amp;quot; whenever a bridge is hard terminated via the &amp;quot;&#039;&#039;Terminate&#039;&#039;&amp;quot; button in the debugger.&lt;br /&gt;
&lt;br /&gt;
This is a &amp;quot;&#039;&#039;feature&#039;&#039;&amp;quot; of the OS X system, not controlled by expecco.&lt;br /&gt;
&lt;br /&gt;
You can disable this by executing the following command in a console terminal:&lt;br /&gt;
 defaults write com.apple.CrashReporter DialogType none&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Back to [[FAQ/en | FAQ]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Cbridge_setup&amp;diff=31328</id>
		<title>Cbridge setup</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Cbridge_setup&amp;diff=31328"/>
		<updated>2026-05-27T07:01:30Z</updated>

		<summary type="html">&lt;p&gt;Sv: document SSL/TLS, certificate generation, mTLS, file transfer, makeDirectory&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= CBridge Setup =&lt;br /&gt;
&lt;br /&gt;
In order to compile and execute C/C++ actions, a C compiler toolchain has to be installed on the machine, where the Bridge executable (Windows: &amp;lt;code&amp;gt;cBridge.exe&amp;lt;/code&amp;gt; / Unix: &amp;lt;code&amp;gt;cBridge&amp;lt;/code&amp;gt;) runs.&lt;br /&gt;
&amp;lt;br&amp;gt;Typically this is the local machine on which expecco runs, but it may be any other machine in your reachable network.&lt;br /&gt;
&amp;lt;br&amp;gt;The CPU architecture and or operating system may be (and often is) different from the CPU which executes expecco, therefore the C-code is compiled on the target machine (i.e. not cross-compiled).&lt;br /&gt;
&lt;br /&gt;
== C-Compiler Toolchain Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Windows + Borland Compiler (32bit) ===&lt;br /&gt;
The compiler is a bit outdated, but still free and useful.&amp;lt;br&amp;gt;However, it only supports 32bit programs. Thus, it cannot be used if you need access to 64bit dlls.&amp;lt;br&amp;gt;(on the other hand, if you need to interact with a 32bit dll to communicate with some hardware device, you&#039;ll must use a 32bit cBridge).&lt;br /&gt;
&lt;br /&gt;
We recommend to install the toolchain at its default installation path (typically &amp;quot;&amp;lt;code&amp;gt;C:\borland&amp;lt;/code&amp;gt;&amp;quot;). Then, the provided cc-compile script for borland can be used unchanged.&lt;br /&gt;
&lt;br /&gt;
=== Windows + MINGW Compiler (32 or 64bit) ===&lt;br /&gt;
The mingw toolchain is based on the gcc compiler. This is recommended and preferred over borland.&lt;br /&gt;
&lt;br /&gt;
=== Windows + Microsoft Visual C / Visual Studio === &lt;br /&gt;
There is no need to install the full Visual Studio IDE. Only the C-compiler, linker and libraries are required. &lt;br /&gt;
Thus, you can untoggle all additional optional packages during the installation.&lt;br /&gt;
&lt;br /&gt;
Check for the &amp;quot;&amp;lt;code&amp;gt;cl&amp;lt;/code&amp;gt;&amp;quot; command (being in your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;) or find and remember the path to it (typically somewhere under &amp;quot;&amp;lt;code&amp;gt;c:\Program Files\Microsoft\...&amp;lt;/code&amp;gt;&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Unix / Linux ===&lt;br /&gt;
Install whatever packages are required; check if the &amp;quot;&amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;&amp;quot; command is found along your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; (before starting expecco),&lt;br /&gt;
or alternatively, remember the path to the &amp;quot;&amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;&amp;quot; command.&lt;br /&gt;
 &lt;br /&gt;
Regardless of which compiler you use, &lt;br /&gt;
please remember the installation path. You will need it later unless the compiler is found along your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Quick Check if it Already Works ==&lt;br /&gt;
We provide a number of setups for common installations.&lt;br /&gt;
In many situations, this will already be correct for your setup.&lt;br /&gt;
&lt;br /&gt;
To check, create a new elementary-bridgedC action, and run it.&lt;br /&gt;
(the initial tree which is created with examples also contains a simple C action).&lt;br /&gt;
&lt;br /&gt;
If you get a green OK result, you&#039;re already done.&lt;br /&gt;
If not, your setup needs to be adjusted.&lt;br /&gt;
&lt;br /&gt;
Read the chapter at the end of this page on step-by-step setup verification.&lt;br /&gt;
&lt;br /&gt;
== The Compile Script ==&lt;br /&gt;
For compilation, the cBridge program calls a batch/shell script, which is responsible for compilation of the C-code to a DLL/shared object file.&lt;br /&gt;
This script may need to be edited.&lt;br /&gt;
We recommend, that you navigate to the folder where the cBridge executable is located, take one of the existing scripts as template, make a copy under a new name, and then edit this copy to suit your needs.&lt;br /&gt;
&lt;br /&gt;
Take one of the existing scripts which uses the same compiler (borland/mingw/visualC).&lt;br /&gt;
&lt;br /&gt;
In the editor, check the &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; setting, and edit it as appropriate, finally save it.&lt;br /&gt;
&lt;br /&gt;
Back in expecco, go to the cBridge settings under the external-script settings,&lt;br /&gt;
and change the CC-script setting there (&amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Shutdown any already running bridge (&amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Debugging&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Shutdown all Bridge Connections&#039;&#039;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Go back to the C-action and repeat the quick check.&lt;br /&gt;
&lt;br /&gt;
Eventually, you should see a green OK result.&lt;br /&gt;
&lt;br /&gt;
== Special Additional Libraries ==&lt;br /&gt;
&lt;br /&gt;
It may be required that your C-code needs additional shared libraries to be linked against your code.&lt;br /&gt;
The easiest is to add those libraries and corresponding command line options to the CC-script&#039;s compiler call command.&lt;br /&gt;
The details depend on the compiler used.&lt;br /&gt;
You may need to do a &amp;quot;&amp;lt;code&amp;gt;cc --help&amp;lt;/code&amp;gt;&amp;quot;, &amp;quot;&amp;lt;code&amp;gt;cl/help&amp;lt;/code&amp;gt;&amp;quot; or similar, to figure out which command line options are required.&lt;br /&gt;
&lt;br /&gt;
= Remote C-Bridges =&lt;br /&gt;
You can run any number of cBridges on any machine within your network (and also multiple cBridges on your local or a remote machine), and execute C-actions there. For this, the following setup steps are to be performed:&lt;br /&gt;
&lt;br /&gt;
* copy the cbridge executable and its support files to a new folder on the target machine; the files are found below the expecco installation folder&amp;lt;br&amp;gt;(eg. &amp;quot;&amp;lt;code&amp;gt;c:\Program Files\exept\expecco&amp;lt;/code&amp;gt;&amp;quot;) in &amp;quot;&amp;lt;code&amp;gt;packages\bridgeFramework\cBridge\cLibrary&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
:The easiest way is to copy the whole folder. Make sure that the copied folder matches the target machine&#039;s architecture (i.e. if the target is a Linux machine, you must obviously copy the files from a Linux installation, and same so for Windows machines).&lt;br /&gt;
&lt;br /&gt;
* on the remote machine, make sure that the cBridge is running; either by executing a remote command from inside your expecco suite, or by adding the cBridge startup to a cron tab (Unix/Linux) or the service list or autoexec.bat (Windows).&lt;br /&gt;
:As a test, start it manually with: &amp;quot;&amp;lt;code&amp;gt;cBridge --port 8856&amp;lt;/code&amp;gt;&amp;quot;, and connect to it from expecco (change the settings (&amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; - &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;) to connect to an already running cBridge at that port).&lt;br /&gt;
&lt;br /&gt;
* if in doubt, start the cBridge with a &amp;quot;&amp;lt;code&amp;gt;--help&amp;lt;/code&amp;gt;&amp;quot; option.&lt;br /&gt;
&lt;br /&gt;
=== CBridge Command Line Arguments ===&lt;br /&gt;
&lt;br /&gt;
* --help&amp;lt;br&amp;gt;Prints a list of the supported comamnd line arguments&lt;br /&gt;
* --localOnly&amp;lt;br&amp;gt;only accept connections from the local machine&lt;br /&gt;
* --host &#039;&#039;hostname&#039;&#039;&amp;lt;br&amp;gt;connect to that host instead of waiting for incoming connections&lt;br /&gt;
* --port&amp;lt;br&amp;gt;the TCP port on which the bridge is waiting for incoming requests or connecting to (with --host argument)&lt;br /&gt;
* -- keepAlive&amp;lt;br&amp;gt;after the first connection, keep on waiting for more incoming connections. Without this, the bridge will only handle a single session and exit after that.&lt;br /&gt;
* --log&amp;lt;br&amp;gt;log actions on stderr&lt;br /&gt;
* --log2&amp;lt;br&amp;gt;even more detailed log&lt;br /&gt;
* --ccScript &#039;&#039;scriptFile&#039;&#039;&amp;lt;br&amp;gt;Use the given script file (a shell or batch script) to compile&lt;br /&gt;
* --includes &#039;&#039;files&#039;&#039;&amp;lt;br&amp;gt;Additional include folders passed to the compile script&lt;br /&gt;
* --dlls &#039;&#039;files&#039;&#039;&amp;lt;br&amp;gt;Additional dlls (shared libraries) to be linked against actions&lt;br /&gt;
* --testCompile&amp;lt;br&amp;gt;For a standalone selftest if compilation  works. Compiles a dummy almost empty source file&lt;br /&gt;
&lt;br /&gt;
More arguments may be recognized, depending on the cBridge version. Use &amp;quot;--help&amp;quot; to see more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SSL/TLS Connection to a Remote CBridge ===&lt;br /&gt;
&lt;br /&gt;
The cBridge supports encrypted TLS connections with optional mutual certificate authentication (mTLS).&lt;br /&gt;
&lt;br /&gt;
==== Generating Certificates ====&lt;br /&gt;
The cBridge can generate Ed25519 CA and server/client certificates directly:&lt;br /&gt;
&lt;br /&gt;
 cBridge --generateCA ~/.ssl/ca.pem --cn &amp;quot;My CA&amp;quot; --days 3650&lt;br /&gt;
 cBridge --generateCert ~/.ssl/server.pem --ca ~/.ssl/ca.pem --cn myhost&lt;br /&gt;
 cBridge --generateCert ~/.ssl/client.pem --ca ~/.ssl/ca.pem --cn expecco&lt;br /&gt;
&lt;br /&gt;
Each generated PEM file contains the certificate, private key, and the CA certificate chain. The &amp;lt;code&amp;gt;--cn&amp;lt;/code&amp;gt; option sets the common name (default: localhost), &amp;lt;code&amp;gt;--days&amp;lt;/code&amp;gt; sets the validity period (default: 3650).&lt;br /&gt;
&lt;br /&gt;
The same certificate generation is also available via the Python bridge script (&amp;lt;code&amp;gt;--generateCA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;--generateCert&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Starting the CBridge with SSL ====&lt;br /&gt;
&lt;br /&gt;
 cBridge --port 8855 --ssl --cert ~/.ssl/server.pem&lt;br /&gt;
&lt;br /&gt;
A combined PEM file (certificate + key in one file) is used by default. Separate files are also supported:&lt;br /&gt;
&lt;br /&gt;
 cBridge --port 8855 --ssl --cert server.crt --key server.key&lt;br /&gt;
&lt;br /&gt;
==== Mutual TLS (Client Certificate Authentication) ====&lt;br /&gt;
&lt;br /&gt;
To require clients to present a certificate signed by the same CA:&lt;br /&gt;
&lt;br /&gt;
 cBridge --port 8855 --ssl --cert ~/.ssl/server.pem --ca ~/.ssl/ca.pem --clientAuth&lt;br /&gt;
&lt;br /&gt;
Clients must then be configured with a client certificate (see the expecco CBridge settings dialog).&lt;br /&gt;
&lt;br /&gt;
==== Expecco Settings for SSL ====&lt;br /&gt;
&lt;br /&gt;
In expecco, configure the CBridge connection under &amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;:&lt;br /&gt;
* &#039;&#039;&#039;Use SSL&#039;&#039;&#039;: enable encrypted connection&lt;br /&gt;
* &#039;&#039;&#039;Certificate File&#039;&#039;&#039;: the server certificate PEM (for the bridge process)&lt;br /&gt;
* &#039;&#039;&#039;Client Certificate File&#039;&#039;&#039;: the client certificate PEM (presented to the server for mTLS)&lt;br /&gt;
&lt;br /&gt;
When the client certificate PEM includes the CA certificate in the chain, the CA is automatically trusted for server certificate verification.&lt;br /&gt;
&lt;br /&gt;
==== File Transfer and Directory Operations ====&lt;br /&gt;
All bridges (C, Python, NodeJS, Ruby, Dart, Smalltalk) support file transfer to and from the remote bridge:&lt;br /&gt;
* &amp;lt;code&amp;gt;putFile: localPath to: remotePath&amp;lt;/code&amp;gt; — upload a file&lt;br /&gt;
* &amp;lt;code&amp;gt;getFile: remotePath to: localPath&amp;lt;/code&amp;gt; — download a file&lt;br /&gt;
* &amp;lt;code&amp;gt;makeDirectory: remotePath&amp;lt;/code&amp;gt; — create a directory (including parent directories)&lt;br /&gt;
&lt;br /&gt;
These operations work over the bridge&#039;s existing connection (including SSL if enabled).&lt;br /&gt;
&lt;br /&gt;
= Step-by-Step Problem Finding/Fixing =&lt;br /&gt;
&lt;br /&gt;
=== Ensure that the cBridge executable is present and can be executed ===&lt;br /&gt;
* goto the expecco installation folder (typically somewhere under &amp;quot;&amp;lt;code&amp;gt;c:\Program Files\exept\expecco\&amp;lt;/code&amp;gt;&amp;quot;), and look for files named &amp;quot;&amp;lt;code&amp;gt;cBridge.exe&amp;lt;/code&amp;gt;&amp;quot; or &amp;quot;&amp;lt;code&amp;gt;cBridge32.exe&amp;lt;/code&amp;gt;&amp;quot; or &amp;quot;&amp;lt;code&amp;gt;cBridge&amp;lt;/code&amp;gt;&amp;quot; (under Unix/Linux). It is typically found in a sub folder named &amp;quot;&amp;lt;code&amp;gt;packages/expecco/bridgeFramework/cBridge&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
* open a shell- or cmd window (and navigate there).&lt;br /&gt;
* type &amp;quot;&amp;lt;code&amp;gt;cBridge --help&amp;lt;/code&amp;gt;&amp;quot; (with powershell: &amp;quot;&amp;lt;code&amp;gt;.\cBridge --help&amp;lt;/code&amp;gt;&amp;quot;)&lt;br /&gt;
:: if that fails (does not output some useful help text), make sure that it can be executed.&amp;lt;br&amp;gt;Maybe your security policy is too strict ot the installation is incomplete.&lt;br /&gt;
* start the bridge with &amp;quot;&amp;lt;code&amp;gt;cBridge --log&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
:: it should output some messages, the last being &amp;quot;&amp;lt;code&amp;gt;... listening on port xxxx&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
* if the cBridge is to run on a machine different from the one expecco is running on, make sure that the firewall allows for connections between the two machines (at the desired port nr)&lt;br /&gt;
* if required, use another port (i.e. run it with &amp;quot;&amp;lt;code&amp;gt;cBridge --log --port &amp;lt;Nr&amp;gt;&amp;lt;/code&amp;gt;&amp;quot;)&lt;br /&gt;
:: now we have a cBridge running, ready to accept connections from expecco at the given port nr.&lt;br /&gt;
* keep the shell/cmd window open; we will need it later (and also to see any diagnostic output)&lt;br /&gt;
&lt;br /&gt;
=== Ensure that the cBridge is reachable from expecco ===&lt;br /&gt;
for this, we first try to connect to that already running bridge (instead of letting expecco start it itself)&lt;br /&gt;
* in expecco, open the settings and navigate to the cBridge settings: &lt;br /&gt;
:: &amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Settings&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;Execution&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;External Script Interpreters&#039;&#039;&amp;quot; &amp;amp;rarr; &amp;quot;&#039;&#039;CBridge&#039;&#039;&amp;quot;&lt;br /&gt;
* check the &amp;quot;&#039;&#039;Connect to already Running Bridge&#039;&#039;&amp;quot; box.&lt;br /&gt;
:: enter CBridge Host and Port as &amp;quot;&amp;lt;code&amp;gt;localhost&amp;lt;/code&amp;gt;&amp;quot; and the above port number (default is 8855)&lt;br /&gt;
* close the settings dialog (without saving the settings for now, when asked)&lt;br /&gt;
* open a new project.&amp;lt;br&amp;gt;The default project will contain a sample &amp;quot;Hello World&amp;quot; C-action, which can be used for the smoke test.&lt;br /&gt;
* in the expecco tree, find the &amp;quot;&amp;lt;code&amp;gt;Simple Elementary Action (C)&amp;lt;/code&amp;gt;&amp;quot; (under the &amp;quot;Activities&amp;quot; group).&lt;br /&gt;
* run it&lt;br /&gt;
:: if the error states that no connection could be setup, check your security policy, firewall etc. and repeat&lt;br /&gt;
:: if the error states that the compilation failed, the bridge connection works, but the compilation script setup is wrong. Proceed.&lt;br /&gt;
&lt;br /&gt;
=== Ensure that the compilation works ===&lt;br /&gt;
the cBridge needs a C-compiler for its operation; to verify,&lt;br /&gt;
* stop the manually started C-bridge (in the shell/cmd window) by pressing CTRL-C there&lt;br /&gt;
* depending on the C-compiler you intend to use, one of the &amp;quot;compile_xxx.bat&amp;quot; or &amp;quot;compile_xxx.sh&amp;quot; script files will be used. The task to be performed by these scripts is to compile a given C-file.&lt;br /&gt;
* check which compiler you installed and intent to use (MSVC, Mingw, gcc, clang or bcc), and look for a corresponding script file.&lt;br /&gt;
* check if the compiler is correctly installed (especially MSVC and MINGW sometimes make trouble here).&lt;br /&gt;
:: one particular problem with MSVC is when deinstalling and reinstalling another version. There seem to be leftover files and/or registry entries which may break the installation (this happend to one of our customers, and also to us when upgrading)&lt;br /&gt;
* Enter the compile-command (&amp;quot;cl&amp;quot;, &amp;quot;cc&amp;quot;, &amp;quot;gcc&amp;quot; or &amp;quot;clang&amp;quot;) on a command line (cmd or shell)&lt;br /&gt;
:: check that you get an answer&lt;br /&gt;
:: try to compile a one-liner (eg. &amp;quot;&amp;lt;code&amp;gt;main() { printf(&amp;quot;hello\n&amp;quot;); }&amp;lt;/code&amp;gt;&amp;quot; and run it to see if it links correctly. Run it!&lt;br /&gt;
* check if the bridge&#039;s cc script file works by running &amp;quot;&amp;lt;code&amp;gt;cBridge --log --ccScript &amp;lt;scriptfilename&amp;gt; --testCompile&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
:: if required, make a copy of one of the script files (the one you think is best to start with) and fix any path inside. If required, add echo statements to show what is going on. Keep the original script and create a new one.&lt;br /&gt;
:: under Windows: in the script, the PATH to the compiler is typically searched and set; for example by calling the vcvarsall.bat script which is provided by microsoft. If required, change the line which calls this (you can also add this batch-call to your autoexec.bat and remove the line from the script). In any case: at the end, the &amp;quot;cl&amp;quot; command should be found along your path. &lt;br /&gt;
* the --testCompile will generate a piece of code which outputs &amp;quot;here is foo&amp;quot;; i.e. on your system (we use mingw), the command is &amp;quot;&amp;lt;code&amp;gt;cBridge --log --ccScript compile_windows_mingw.bat&amp;quot; --testCompile&amp;lt;/code&amp;gt;&amp;quot;, and the output is &amp;quot;here is foo&amp;quot; - &amp;quot;void pointer size: 8&amp;quot;, followed by the usual &amp;quot;listening on port&amp;quot; message.&lt;br /&gt;
&lt;br /&gt;
=== Change the Settings in expecco ===&lt;br /&gt;
* open the cBridge settings again&lt;br /&gt;
* uncheck the &amp;quot;&#039;&#039;Connect to already running bridge&#039;&#039;&amp;quot; box&lt;br /&gt;
* enter the name of the compile script into the &amp;quot;CC-script&amp;quot; field (possibly the name of the new script)&lt;br /&gt;
* shutdown any already running bridge&lt;br /&gt;
* try executing the expecco-C action again&lt;br /&gt;
* if you get a green outcome, save your expecco settings.&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31321</id>
		<title>Remote Access/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31321"/>
		<updated>2026-05-26T18:52:47Z</updated>

		<summary type="html">&lt;p&gt;Sv: Fold the Automatic-Refresh bullets onto single physical lines&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access/en|label=English}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Remote access&#039;&#039;&#039; is the ability to drive a remote computer or&lt;br /&gt;
network from this expecco image — opening shells, running commands,&lt;br /&gt;
moving files, or driving a test target.  Three protocol families are&lt;br /&gt;
supported, listed in current-recommended order:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH and SFTP&#039;&#039;&#039; (recommended) — encrypted shell + secure file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on OpenSSL or libssh.  Use this for anything that touches credentials or sensitive payloads.&lt;br /&gt;
* &#039;&#039;&#039;Local Command Shell&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (legacy) — plain-text terminal session.  No encryption, passwords on the wire in clear.  Use only when the target hardware has no other option.&lt;br /&gt;
&lt;br /&gt;
= SSH and SFTP =&lt;br /&gt;
&lt;br /&gt;
The SSH stack covers the full SSH-2 protocol (RFC 4251–4254,&lt;br /&gt;
RFC 5656, RFC 8709, RFC 8731) plus OpenSSH&#039;s chacha20-poly1305&lt;br /&gt;
transport cipher and the SFTP v3 file-transfer subsystem&lt;br /&gt;
(draft-ietf-secsh-filexfer-02).  Two layers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatic SSH access (remote&lt;br /&gt;
&amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY shell, agent forwarding, ProxyJump bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — a &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt; subclass that lets the rest of ST/X treat a remote SFTP path the same way it treats a local file.&lt;br /&gt;
&lt;br /&gt;
The rest of this section is organised user-task-first: what the user&lt;br /&gt;
sees and does, the expecco-library hooks below that, then the&lt;br /&gt;
implementation detail at the end for the curious.&lt;br /&gt;
&lt;br /&gt;
== From the FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Open the location dropdown and paste an &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt; URL.&lt;br /&gt;
The browser tab populates as if it were a local path.  Tree&lt;br /&gt;
expansion, column sort (name / size / mtime), preview, and&lt;br /&gt;
double-click-to-open-in-editor all behave normally.  The first&lt;br /&gt;
click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent&lt;br /&gt;
clicks reuse the pooled connection.&lt;br /&gt;
&lt;br /&gt;
URL syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User defaults to the local login name, port to 22, path to&lt;br /&gt;
&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Refresh&#039;&#039;&#039; button in the toolbar (the round-arrow icon&lt;br /&gt;
between &#039;&#039;Forward&#039;&#039; and &#039;&#039;DirectoryUp&#039;&#039;) re-reads both the&lt;br /&gt;
directory tree and the contents pane on demand.  Works uniformly&lt;br /&gt;
for local and SFTP paths; for SFTP it also flushes the per-file&lt;br /&gt;
STAT cache, so changes made directly on the remote side become&lt;br /&gt;
visible immediately rather than waiting for the 5-second cache TTL&lt;br /&gt;
to expire.&lt;br /&gt;
&lt;br /&gt;
The small arrow next to the Refresh icon opens a dropdown with a&lt;br /&gt;
single checkbox, &#039;&#039;&#039;Automatic Refresh&#039;&#039;&#039;, controlling the&lt;br /&gt;
background polling task that walks every expanded tree item to&lt;br /&gt;
detect external changes.  The default depends on the current root:&lt;br /&gt;
&lt;br /&gt;
* Local filesystem &amp;amp;rarr; &#039;&#039;&#039;on&#039;&#039;&#039; (10-second cycle, matches the long-standing behaviour).&lt;br /&gt;
* SFTP &amp;amp;rarr; &#039;&#039;&#039;off&#039;&#039;&#039;.  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.&lt;br /&gt;
&lt;br /&gt;
When you navigate between local and SFTP roots the toggle flips&lt;br /&gt;
automatically &amp;amp;mdash; but only if you haven&#039;t overridden it for&lt;br /&gt;
the previous root.  An explicit user choice is preserved across&lt;br /&gt;
navigations.&lt;br /&gt;
&lt;br /&gt;
The Tools menu offers four browser actions, three of them gated on&lt;br /&gt;
the SSH library being loaded:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — opens the same key-generation dialog described under [[#Generating an SSH key pair]] below.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — opens an interactive VT100 terminal to a remote host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — points this browser tab at a remote filesystem via SFTP.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — 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 &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; 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 &#039;&#039;X TiB&#039;&#039; rather than &#039;&#039;10240 GiB&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== From expecco actions ==&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) exposes the following test&lt;br /&gt;
actions to the expecco action palette:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — opens an SSH session via the platform&#039;s &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; binary (PuTTY&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; on Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — same but with explicit public-key authentication.&lt;br /&gt;
&lt;br /&gt;
To run these you need a configured keypair (private key on this&lt;br /&gt;
machine, public key in the remote host&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;).  Generate one via the dialog&lt;br /&gt;
below or via &amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The plugin also adds a settings page at &#039;&#039;&#039;Extras → Settings →&lt;br /&gt;
Plugins → Remote Access — SSH Keys&#039;&#039;&#039; carrying a single&lt;br /&gt;
&#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; button that opens the same dialog.&lt;br /&gt;
&lt;br /&gt;
== Generating an SSH key pair ==&lt;br /&gt;
&lt;br /&gt;
=== The dialog (FileBrowserV2 / settings page) ===&lt;br /&gt;
&lt;br /&gt;
The dialog asks for all parameters in one form:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — embedded in the generated key (defaults to&lt;br /&gt;
&amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — writes &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (or wherever) plus a matching &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt; companion.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — writes the file and also hands the key to the running ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — key lives in agent memory only; gone on agent restart.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — full path; disabled in agent-only mode.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — empty leaves the on-disk file unencrypted (agent-only mode ignores the passphrase, since the OpenSSH agent wire protocol carries only the decrypted key).&lt;br /&gt;
&lt;br /&gt;
On &#039;&#039;&#039;Generate&#039;&#039;&#039;, the public-key line (the same&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt; string ssh-keygen&lt;br /&gt;
emits) is copied to the system clipboard for pasting into the&lt;br /&gt;
remote host&#039;s &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From a workspace ===&lt;br /&gt;
&lt;br /&gt;
For headless deployments, sandboxed builds, or scripts,&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; exposes a pure-Smalltalk key generator&lt;br /&gt;
that produces output bit-compatible with&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Save passphrase-encrypted to disk&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ AND load into the running agent&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Print the public-key line to paste into authorized_keys&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keys generated this way are interoperable with OpenSSH&#039;s own&lt;br /&gt;
tooling (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; re-derives the public&lt;br /&gt;
key, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; changes the passphrase,&lt;br /&gt;
etc.).&lt;br /&gt;
&lt;br /&gt;
=== Using the shell tools instead ===&lt;br /&gt;
&lt;br /&gt;
The traditional path also works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparing ssh-agent ==&lt;br /&gt;
&lt;br /&gt;
The agent path is strongly preferred over reading raw keyfiles: it&lt;br /&gt;
keeps encrypted private keys unlocked once per session, and handles&lt;br /&gt;
identities (hardware-token-backed keys, KeePassXC entries) that&lt;br /&gt;
ST/X should never see directly.&lt;br /&gt;
&lt;br /&gt;
ST/X picks the agent path automatically when&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is set in the process environment&lt;br /&gt;
&#039;&#039;&#039;at the time stx is launched&#039;&#039;&#039;.  Setting it later from a&lt;br /&gt;
workspace does not help.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Most desktop distributions launch an agent automatically as part of&lt;br /&gt;
the session (gnome-keyring on GNOME, ssh-agent.service on systemd,&lt;br /&gt;
KWallet on KDE).  Verify in a terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar&lt;br /&gt;
ssh-add -l             # lists loaded identities&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If no agent runs at all, add this snippet to your shell rc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc or ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X must be launched from a shell that has seen this rc — a&lt;br /&gt;
desktop launcher started from the file manager does NOT inherit&lt;br /&gt;
the variable.  Wrap the stx start command in a small script under&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt; that sources the rc first.&lt;br /&gt;
&lt;br /&gt;
The Remote Access settings page (&#039;&#039;&#039;Extras → Settings → Plugins&lt;br /&gt;
→ Remote Access — SSH Keys&#039;&#039;&#039;) shows whether the running image&lt;br /&gt;
sees an agent.&lt;br /&gt;
&lt;br /&gt;
==== Permanent setup via systemd ====&lt;br /&gt;
&lt;br /&gt;
For a truly cross-session agent (survives desktop logouts, comes&lt;br /&gt;
up automatically at next login), enable the per-user systemd&lt;br /&gt;
unit shipped with most distros&#039; &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
package:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then point &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the user-service socket&lt;br /&gt;
in your shell rc (this replaces the &amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;&lt;br /&gt;
snippet above):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
To skip the manual &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; step, let OpenSSH load&lt;br /&gt;
keys into the agent automatically the first time they are needed.&lt;br /&gt;
Add to &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first SSH connection then prompts for the key passphrase&lt;br /&gt;
once and hands the unlocked key to the agent; subsequent&lt;br /&gt;
connections use the cached identity without prompting.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ ships native OpenSSH including an agent service.&lt;br /&gt;
One-time setup:&lt;br /&gt;
&lt;br /&gt;
# Open &#039;&#039;&#039;Services&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) as Administrator.&lt;br /&gt;
# Find &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039;, set Startup Type to &#039;&#039;&#039;Automatic&#039;&#039;&#039;, click &#039;&#039;&#039;Start&#039;&#039;&#039;.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Windows OpenSSH agent listens on a named pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), not a Unix socket.  ST/X&lt;br /&gt;
supports both transports, but Windows ssh-add does &#039;&#039;&#039;not&#039;&#039;&#039; set&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; for you.  Add it manually:&lt;br /&gt;
&lt;br /&gt;
# Press {{Key|Win}} → type &amp;quot;environment&amp;quot; → &amp;quot;Edit the system environment variables&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Environment Variables&#039;&#039;&#039; → under &#039;&#039;&#039;User variables&#039;&#039;&#039;, &#039;&#039;&#039;New&#039;&#039;&#039;.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Value: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Log out and back in (or restart stx) so the new env propagates.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell quick-setup ====&lt;br /&gt;
&lt;br /&gt;
The same setup from an &#039;&#039;&#039;elevated&#039;&#039;&#039; PowerShell prompt, for&lt;br /&gt;
scripts or unattended provisioning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Start the agent now AND on every reboot (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# Persist SSH_AUTH_SOCK for the user (survives reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Load a key (prompts for the passphrase if the file is encrypted).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a one-shot agent start without making it persistent (e.g.&lt;br /&gt;
single-session test), drop the &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt; line and&lt;br /&gt;
just run &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt;.  The env-var line&lt;br /&gt;
can also be omitted if &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is only needed&lt;br /&gt;
in the current shell — use &amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
instead for that session-local form.&lt;br /&gt;
&lt;br /&gt;
On stripped-down Windows installs the ssh-agent service may not&lt;br /&gt;
be present.  Add it once via &#039;&#039;&#039;Settings → Apps → Optional&lt;br /&gt;
features → OpenSSH Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative agents:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — uses its own protocol; NOT supported by ST/X&#039;s SSH::Agent.  Migrate the keys to OpenSSH.&lt;br /&gt;
* &#039;&#039;&#039;Git for Windows ssh-agent&#039;&#039;&#039; — works; point&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the socket it publishes.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — a ST/X inside WSL sees WSL&#039;s Linux agent normally; a ST/X on the Windows side does not.  Bridging needs a helper like &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify in the Remote Access settings page&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
the agent indicator there reports whether the running image sees&lt;br /&gt;
the agent.&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
Windows OpenSSH does &#039;&#039;&#039;not&#039;&#039;&#039; persist agent-loaded keys across&lt;br /&gt;
agent restarts.  To avoid running &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; manually&lt;br /&gt;
after each reboot, add the same lazy-load configuration to&lt;br /&gt;
&amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH then loads the key into the agent on first use (prompts&lt;br /&gt;
for the passphrase once) and reuses it for the rest of the&lt;br /&gt;
session.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All tunables are class-side on &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Default !! What it controls&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 min) || How long a pooled&lt;br /&gt;
connection sits idle before the next access proactively closes +&lt;br /&gt;
reopens it.  Just under typical sshd&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; so we recycle&lt;br /&gt;
before the server TCP-RESETs us.  Pass &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; to restore&lt;br /&gt;
the default.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Max age (s) of a&lt;br /&gt;
cached STAT before &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; refetches.  Parent&lt;br /&gt;
listDir always re-stamps fresh attrs onto children, so navigating&lt;br /&gt;
an open directory does not pay the TTL.  Set to &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to&lt;br /&gt;
disable caching.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (action) || Tears down every&lt;br /&gt;
pooled connection.  Useful after a known-bad network event, before&lt;br /&gt;
a deliberate identity swap, or as part of a clean image shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
Open &amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; from the Launcher&#039;s &amp;quot;Status&amp;quot;&lt;br /&gt;
sub-menu.  Per-host SFTP mutex appears as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;; the pool-wide mutex as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Right-click a row:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — dumps the last-owner&#039;s walkback plus each waiter&#039;s, formatted as plain text.  Use when a process is wedged in &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; inside&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; and you need to see which SFTP&lt;br /&gt;
request it is on.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — the whole table, for an email-this-to-someone diagnosis.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS over the wait-for graph, reports cycles.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
The SSH stack logs interesting events:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; on auto-reconnect after a dead connection.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when a pool entry is idle-evicted.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when an SSH key file cannot be parsed (e.g. legacy PEM, encrypted-without-agent) — the file is skipped, others tried.&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SFTP v3 only.&#039;&#039;&#039;  No SETSTAT (no remote chmod / chown / utime), no SSH_FXP_READLINK exposed (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; always&lt;br /&gt;
&amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; returns the regular&lt;br /&gt;
stat info).  Several SFTPv5+ niceties are nevertheless picked up&lt;br /&gt;
via OpenSSH SSH_FXP_EXTENDED requests — see&lt;br /&gt;
[[#OpenSSH SFTP extensions]] below.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation.&#039;&#039;&#039;  Two concurrent operations on the same host queue through the host mutex.  See [[#Future work]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; fallback has a TOCTOU window.&#039;&#039;&#039;  On servers that advertise &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; (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.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; is heuristic.&#039;&#039;&#039;  Always returns &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (the accurate answer would cost three round-trips per directory icon, which made the original tree expansion unbearably slow).&lt;br /&gt;
&lt;br /&gt;
== Implementation details ==&lt;br /&gt;
&lt;br /&gt;
For readers wanting the architecture.  Five classes, top-down:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename subclass; the public&lt;br /&gt;
API.  Maps &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt; URLs to remote files; exposes&lt;br /&gt;
&amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3 protocol&lt;br /&gt;
(request/response codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Driven by SftpFilename.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH channel multiplexer&lt;br /&gt;
(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST).  One logical&lt;br /&gt;
session per Channel instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-level SSH client: opens the&lt;br /&gt;
transport, runs KEX, host-key check, userauth, then dispenses&lt;br /&gt;
Channels.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Wire layer.  Banner + KEXINIT&lt;br /&gt;
exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq,&lt;br /&gt;
heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH SFTP extensions ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal.&lt;br /&gt;
OpenSSH ships an open-ended extension mechanism: the server lists&lt;br /&gt;
extension names it understands in its &amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;&lt;br /&gt;
reply, and the client invokes them via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt; packets carrying the extension&lt;br /&gt;
name as the first string.  Each extension is feature-detected via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;; callers fall&lt;br /&gt;
back when the server doesn&#039;t advertise it.&lt;br /&gt;
&lt;br /&gt;
The stack uses four of the OpenSSH extensions today:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomic rename-with-overwrite.  Picked up automatically by&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt;; the delete-then-rename&lt;br /&gt;
fallback only fires on servers that lack it.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — create a POSIX hard link. Exposed as &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-shape filesystem stats.  Exposed as&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;; the result is&lt;br /&gt;
shape-compatible with &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;&lt;br /&gt;
so callers can treat local and remote uniformly.  Drives the&lt;br /&gt;
&#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; menu entry described at the&lt;br /&gt;
top of this page.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — flush server-side write buffer to disk on an open handle.  Available on the low-level&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt;; not yet plumbed&lt;br /&gt;
into a Filename-level &amp;quot;durable write&amp;quot; API.&lt;br /&gt;
&lt;br /&gt;
The remaining OpenSSH extensions&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
are recognised in the advertised-extensions list but not wrapped at&lt;br /&gt;
Filename level — there&#039;s no Filename-side caller for them yet.&lt;br /&gt;
&lt;br /&gt;
=== Connection pooling ===&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt; instance pointing at the same&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; triple shares one&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; plus one &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Pool is class-side, guarded by a single&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy bring-up&#039;&#039;&#039; — TCP + KEX + userauth + SFTP INIT happens on the first SFTP operation, not on &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation&#039;&#039;&#039; — SFTP requests on a given host are serialised through a &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; named&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; (visible in&lt;br /&gt;
SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle eviction&#039;&#039;&#039; — unused for longer than&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt;, the entry is proactively&lt;br /&gt;
closed + reopened on the next access.&lt;br /&gt;
* &#039;&#039;&#039;Auto-reconnect&#039;&#039;&#039; — a transport-level failure (broken pipe, EOF, MNU on nil socket) evicts the dead pool entry, opens a fresh client, retries the request &#039;&#039;&#039;once&#039;&#039;&#039;.  Application-level SFTP STATUS errors propagate immediately.&lt;br /&gt;
&lt;br /&gt;
== Future work ==&lt;br /&gt;
&lt;br /&gt;
Tracked but not yet implemented:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-channel parallelism per host&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Accurate &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (first batch only) + CLOSE — three RTTs per probe; needs SftpClient to pipeline requests before this pays off.&lt;br /&gt;
* &#039;&#039;&#039;SFTP v5/v6 negotiation&#039;&#039;&#039; for extended attrs and FTP-style canonicalisation.  (Atomic-overwrite rename is already handled via the OpenSSH &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; extension; see [[#OpenSSH SFTP extensions]].)&lt;br /&gt;
&lt;br /&gt;
= Command Shell =&lt;br /&gt;
&lt;br /&gt;
Local command shell on this expecco machine.  Typical applications:&lt;br /&gt;
local command-line, running a local helper tool, bridging a&lt;br /&gt;
remote workflow to a local utility.&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No credentials, no network — runs as the expecco process&#039;s own&lt;br /&gt;
user.  Output streams to expecco&#039;s log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warning]] &#039;&#039;&#039;Telnet is a legacy protocol&lt;br /&gt;
with no encryption.&#039;&#039;&#039; Passwords are transmitted in plain text on&lt;br /&gt;
the wire; anyone on the network path can read them.  Use Telnet&lt;br /&gt;
ONLY when the target device has no other option (typically: old&lt;br /&gt;
industrial controllers, lab instruments, embedded measurement&lt;br /&gt;
equipment without an SSH stack).  For everything else use&lt;br /&gt;
[[#SSH and SFTP]].&lt;br /&gt;
&lt;br /&gt;
The expecco plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (internal demo)&lt;br /&gt;
&lt;br /&gt;
The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream&lt;br /&gt;
over TCP, with in-band control sequences for terminal options.&lt;br /&gt;
A connection is established to a target host:port; after optional&lt;br /&gt;
in-band login, both sides can send data.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent forwarding, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of this stack.&lt;br /&gt;
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack for its HTTPS transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Network]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31320</id>
		<title>Remote Access</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31320"/>
		<updated>2026-05-26T18:52:45Z</updated>

		<summary type="html">&lt;p&gt;Sv: Fold the Automatic-Refresh bullets onto single physical lines&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access|label=Deutsch}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere Dateiübertragung über einen SSH-2-Tunnel.  Reine Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen Maschine.  Für die Anbindung lokaler Werkzeuge und für die lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff (entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung, ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
&amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Schaltfläche &#039;&#039;&#039;Refresh&#039;&#039;&#039; in der Symbolleiste&lt;br /&gt;
(Pfeil-Kreis-Symbol zwischen &#039;&#039;Forward&#039;&#039; und &#039;&#039;DirectoryUp&#039;&#039;) liest&lt;br /&gt;
Baum und Inhalts-Panel auf Anforderung neu ein.  Funktioniert&lt;br /&gt;
einheitlich für lokale und SFTP-Pfade; bei SFTP wird zusätzlich der&lt;br /&gt;
per-Datei-STAT-Cache geleert, sodass Änderungen, die direkt auf&lt;br /&gt;
der Gegenseite gemacht wurden, sofort sichtbar werden — ohne auf&lt;br /&gt;
den Ablauf der 5-Sekunden-Cache-TTL zu warten.&lt;br /&gt;
&lt;br /&gt;
Der kleine Pfeil neben dem Refresh-Symbol öffnet ein Aufklappmenü&lt;br /&gt;
mit einem einzelnen Kontrollkästchen, &#039;&#039;&#039;Automatic Refresh&#039;&#039;&#039;, das&lt;br /&gt;
den Hintergrund-Task an- bzw. abschaltet, der alle expandierten&lt;br /&gt;
Baumeinträge auf externe Änderungen prüft.  Die Vorgabe richtet&lt;br /&gt;
sich nach der aktuellen Wurzel:&lt;br /&gt;
&lt;br /&gt;
* Lokales Dateisystem &amp;amp;rarr; &#039;&#039;&#039;an&#039;&#039;&#039; (10-Sekunden-Zyklus, entspricht dem bisherigen Verhalten).&lt;br /&gt;
* SFTP &amp;amp;rarr; &#039;&#039;&#039;aus&#039;&#039;&#039;.  Jeder Zyklus kostet einen STAT-Roundtrip pro Kind — für eine Handvoll lokaler Verzeichnisse harmlos, über das Netz schmerzhaft.  Bei Bedarf manuell auf Refresh klicken, um Änderungen zu sehen.&lt;br /&gt;
&lt;br /&gt;
Beim Wechsel zwischen lokalen und SFTP-Wurzeln wird der Schalter&lt;br /&gt;
automatisch umgelegt &amp;amp;mdash; aber nur, sofern man ihn für die&lt;br /&gt;
vorherige Wurzel nicht selbst verstellt hat.  Eine explizite&lt;br /&gt;
Benutzerwahl bleibt über Navigationen hinweg erhalten.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet vier Aktionen — die&lt;br /&gt;
drei SSH-spezifischen sind nur bei geladener SSH-Bibliothek&lt;br /&gt;
sichtbar:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den Schlüsselerzeugungs-Dialog, siehe [[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP auf ein entferntes Dateisystem.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — zeigt Größe, freien Platz und Belegung des Dateisystems, das das aktuell angezeigte Verzeichnis enthält. Funktioniert einheitlich für lokale und SFTP-Pfade; bei SFTP setzt der Aufruf voraus, daß der Server die Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; ankündigt (jedes moderne OpenSSH&lt;br /&gt;
tut das).  Größen werden in IEC-Binäreinheiten ausgegeben (MiB,&lt;br /&gt;
GiB, TiB) — gewählt wird die größte Einheit, die einen Wert ≥ 1&lt;br /&gt;
liefert, damit ein TB-großes Volume als &#039;&#039;X TiB&#039;&#039; statt&lt;br /&gt;
&#039;&#039;10240 GiB&#039;&#039; erscheint.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary (PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet (Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf &#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs- variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;, &#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback des letzten Eigners samt aller Waiter als Text in die Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
&amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes chmod / chown / utime), kein SSH_FXP_READLINK exponiert (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  Einige&lt;br /&gt;
SFTPv5+-Annehmlichkeiten werden dennoch über OpenSSH-spezifische&lt;br /&gt;
SSH_FXP_EXTENDED-Aufrufe nutzbar — siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]] weiter unten.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt;-Fallback hat ein TOCTOU-Fenster.&#039;&#039;&#039; Bei Servern, die &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; ankündigen (jedes moderne OpenSSH tut das), ist das Überschreiben atomar. Beim seltenen Server, der das nicht tut, wird auf Delete-dann-Rename ausgewichen und ein anderer Prozess kann sich dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039; Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH-SFTP-Erweiterungen ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (Entwurf draft-ietf-secsh-filexfer-02) ist bewusst&lt;br /&gt;
minimal gehalten.  OpenSSH bringt einen offenen&lt;br /&gt;
Erweiterungsmechanismus mit: der Server listet im&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;-Reply die Erweiterungsnamen auf, die&lt;br /&gt;
er versteht, und der Client ruft sie über&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt;-Pakete mit dem&lt;br /&gt;
Erweiterungsnamen als erstem String auf.  Jede Erweiterung wird&lt;br /&gt;
über &amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;&lt;br /&gt;
feature-detektiert; Aufrufer fallen zurück, wenn der Server sie&lt;br /&gt;
nicht ankündigt.&lt;br /&gt;
&lt;br /&gt;
Der Stack nutzt heute vier OpenSSH-Erweiterungen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomares rename-mit-Überschreiben.  Wird automatisch von&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt; aufgegriffen; die&lt;br /&gt;
Delete-dann-Rename-Fallback-Variante kommt nur bei Servern zum&lt;br /&gt;
Einsatz, die die Erweiterung nicht haben.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — Erzeugt einen POSIX-Hardlink. Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX-&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-typische Dateisystem-Statistik.&lt;br /&gt;
Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
das Ergebnis ist form-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;, sodass Aufrufer&lt;br /&gt;
lokale und entfernte Pfade einheitlich behandeln können.&lt;br /&gt;
Treibt den Menü-Eintrag &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; an,&lt;br /&gt;
der am Anfang dieser Seite beschrieben ist.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — schreibt den serverseitigen Schreibpuffer eines geöffneten Handles auf Platte.  Liegt als&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt; bereit; noch nicht&lt;br /&gt;
in eine &amp;quot;Durable-Write&amp;quot;-API auf Filename-Ebene eingebunden.&lt;br /&gt;
&lt;br /&gt;
Die verbleibenden OpenSSH-Erweiterungen&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
werden in der angekündigten Liste erkannt, aber nicht auf&lt;br /&gt;
Filename-Ebene gekapselt — es gibt dafür noch keinen&lt;br /&gt;
Filename-seitigen Aufrufer.&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene (Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den Pool-Eintrag, öffnet einen frischen Client und wiederholt die Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet eine TCP- plus eine SFTP-Verbindung pro Host, dass N gleichzeitige Anfragen serialisieren.  Pipelining über mehrere SshClients im Pool (bevorzugt) oder ein transport-seitiger Reader-Prozess, der eingehende Pakete in Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel erlauben, weiter aufzulisten, während das Inhalts-Panel eine große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für erweiterte Attribute und FTP-artige Kanonisierung.  (Atomares Überschreibungs-rename ist bereits über die OpenSSH-Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; abgedeckt; siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]].)&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY, Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31319</id>
		<title>Remote Access/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31319"/>
		<updated>2026-05-26T18:50:31Z</updated>

		<summary type="html">&lt;p&gt;Sv: Document the Automatic Refresh dropdown toggle and its root-type default&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access/en|label=English}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Remote access&#039;&#039;&#039; is the ability to drive a remote computer or&lt;br /&gt;
network from this expecco image — opening shells, running commands,&lt;br /&gt;
moving files, or driving a test target.  Three protocol families are&lt;br /&gt;
supported, listed in current-recommended order:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH and SFTP&#039;&#039;&#039; (recommended) — encrypted shell + secure file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on OpenSSL or libssh.  Use this for anything that touches credentials or sensitive payloads.&lt;br /&gt;
* &#039;&#039;&#039;Local Command Shell&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (legacy) — plain-text terminal session.  No encryption, passwords on the wire in clear.  Use only when the target hardware has no other option.&lt;br /&gt;
&lt;br /&gt;
= SSH and SFTP =&lt;br /&gt;
&lt;br /&gt;
The SSH stack covers the full SSH-2 protocol (RFC 4251–4254,&lt;br /&gt;
RFC 5656, RFC 8709, RFC 8731) plus OpenSSH&#039;s chacha20-poly1305&lt;br /&gt;
transport cipher and the SFTP v3 file-transfer subsystem&lt;br /&gt;
(draft-ietf-secsh-filexfer-02).  Two layers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatic SSH access (remote&lt;br /&gt;
&amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY shell, agent forwarding, ProxyJump bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — a &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt; subclass that lets the rest of ST/X treat a remote SFTP path the same way it treats a local file.&lt;br /&gt;
&lt;br /&gt;
The rest of this section is organised user-task-first: what the user&lt;br /&gt;
sees and does, the expecco-library hooks below that, then the&lt;br /&gt;
implementation detail at the end for the curious.&lt;br /&gt;
&lt;br /&gt;
== From the FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Open the location dropdown and paste an &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt; URL.&lt;br /&gt;
The browser tab populates as if it were a local path.  Tree&lt;br /&gt;
expansion, column sort (name / size / mtime), preview, and&lt;br /&gt;
double-click-to-open-in-editor all behave normally.  The first&lt;br /&gt;
click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent&lt;br /&gt;
clicks reuse the pooled connection.&lt;br /&gt;
&lt;br /&gt;
URL syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User defaults to the local login name, port to 22, path to&lt;br /&gt;
&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Refresh&#039;&#039;&#039; button in the toolbar (the round-arrow icon&lt;br /&gt;
between &#039;&#039;Forward&#039;&#039; and &#039;&#039;DirectoryUp&#039;&#039;) re-reads both the&lt;br /&gt;
directory tree and the contents pane on demand.  Works uniformly&lt;br /&gt;
for local and SFTP paths; for SFTP it also flushes the per-file&lt;br /&gt;
STAT cache, so changes made directly on the remote side become&lt;br /&gt;
visible immediately rather than waiting for the 5-second cache TTL&lt;br /&gt;
to expire.&lt;br /&gt;
&lt;br /&gt;
The small arrow next to the Refresh icon opens a dropdown with a&lt;br /&gt;
single checkbox, &#039;&#039;&#039;Automatic Refresh&#039;&#039;&#039;, controlling the&lt;br /&gt;
background polling task that walks every expanded tree item to&lt;br /&gt;
detect external changes.  The default depends on the current root:&lt;br /&gt;
&lt;br /&gt;
* Local filesystem &amp;amp;rarr; &#039;&#039;&#039;on&#039;&#039;&#039; (10-second cycle, matches the&lt;br /&gt;
  long-standing behaviour).&lt;br /&gt;
* SFTP &amp;amp;rarr; &#039;&#039;&#039;off&#039;&#039;&#039;.  Each cycle costs one STAT round-trip&lt;br /&gt;
  per child, which is fine for a handful of local directories but&lt;br /&gt;
  painful over the network.  Click Refresh manually when you need&lt;br /&gt;
  to pick up remote changes.&lt;br /&gt;
&lt;br /&gt;
When you navigate between local and SFTP roots the toggle flips&lt;br /&gt;
automatically &amp;amp;mdash; but only if you haven&#039;t overridden it for&lt;br /&gt;
the previous root.  An explicit user choice is preserved across&lt;br /&gt;
navigations.&lt;br /&gt;
&lt;br /&gt;
The Tools menu offers four browser actions, three of them gated on&lt;br /&gt;
the SSH library being loaded:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — opens the same key-generation dialog described under [[#Generating an SSH key pair]] below.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — opens an interactive VT100 terminal to a remote host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — points this browser tab at a remote filesystem via SFTP.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — 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 &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; 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 &#039;&#039;X TiB&#039;&#039; rather than &#039;&#039;10240 GiB&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== From expecco actions ==&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) exposes the following test&lt;br /&gt;
actions to the expecco action palette:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — opens an SSH session via the platform&#039;s &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; binary (PuTTY&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; on Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — same but with explicit public-key authentication.&lt;br /&gt;
&lt;br /&gt;
To run these you need a configured keypair (private key on this&lt;br /&gt;
machine, public key in the remote host&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;).  Generate one via the dialog&lt;br /&gt;
below or via &amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The plugin also adds a settings page at &#039;&#039;&#039;Extras → Settings →&lt;br /&gt;
Plugins → Remote Access — SSH Keys&#039;&#039;&#039; carrying a single&lt;br /&gt;
&#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; button that opens the same dialog.&lt;br /&gt;
&lt;br /&gt;
== Generating an SSH key pair ==&lt;br /&gt;
&lt;br /&gt;
=== The dialog (FileBrowserV2 / settings page) ===&lt;br /&gt;
&lt;br /&gt;
The dialog asks for all parameters in one form:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — embedded in the generated key (defaults to&lt;br /&gt;
&amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — writes &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (or wherever) plus a matching &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt; companion.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — writes the file and also hands the key to the running ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — key lives in agent memory only; gone on agent restart.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — full path; disabled in agent-only mode.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — empty leaves the on-disk file unencrypted (agent-only mode ignores the passphrase, since the OpenSSH agent wire protocol carries only the decrypted key).&lt;br /&gt;
&lt;br /&gt;
On &#039;&#039;&#039;Generate&#039;&#039;&#039;, the public-key line (the same&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt; string ssh-keygen&lt;br /&gt;
emits) is copied to the system clipboard for pasting into the&lt;br /&gt;
remote host&#039;s &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From a workspace ===&lt;br /&gt;
&lt;br /&gt;
For headless deployments, sandboxed builds, or scripts,&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; exposes a pure-Smalltalk key generator&lt;br /&gt;
that produces output bit-compatible with&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Save passphrase-encrypted to disk&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ AND load into the running agent&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Print the public-key line to paste into authorized_keys&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keys generated this way are interoperable with OpenSSH&#039;s own&lt;br /&gt;
tooling (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; re-derives the public&lt;br /&gt;
key, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; changes the passphrase,&lt;br /&gt;
etc.).&lt;br /&gt;
&lt;br /&gt;
=== Using the shell tools instead ===&lt;br /&gt;
&lt;br /&gt;
The traditional path also works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparing ssh-agent ==&lt;br /&gt;
&lt;br /&gt;
The agent path is strongly preferred over reading raw keyfiles: it&lt;br /&gt;
keeps encrypted private keys unlocked once per session, and handles&lt;br /&gt;
identities (hardware-token-backed keys, KeePassXC entries) that&lt;br /&gt;
ST/X should never see directly.&lt;br /&gt;
&lt;br /&gt;
ST/X picks the agent path automatically when&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is set in the process environment&lt;br /&gt;
&#039;&#039;&#039;at the time stx is launched&#039;&#039;&#039;.  Setting it later from a&lt;br /&gt;
workspace does not help.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Most desktop distributions launch an agent automatically as part of&lt;br /&gt;
the session (gnome-keyring on GNOME, ssh-agent.service on systemd,&lt;br /&gt;
KWallet on KDE).  Verify in a terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar&lt;br /&gt;
ssh-add -l             # lists loaded identities&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If no agent runs at all, add this snippet to your shell rc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc or ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X must be launched from a shell that has seen this rc — a&lt;br /&gt;
desktop launcher started from the file manager does NOT inherit&lt;br /&gt;
the variable.  Wrap the stx start command in a small script under&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt; that sources the rc first.&lt;br /&gt;
&lt;br /&gt;
The Remote Access settings page (&#039;&#039;&#039;Extras → Settings → Plugins&lt;br /&gt;
→ Remote Access — SSH Keys&#039;&#039;&#039;) shows whether the running image&lt;br /&gt;
sees an agent.&lt;br /&gt;
&lt;br /&gt;
==== Permanent setup via systemd ====&lt;br /&gt;
&lt;br /&gt;
For a truly cross-session agent (survives desktop logouts, comes&lt;br /&gt;
up automatically at next login), enable the per-user systemd&lt;br /&gt;
unit shipped with most distros&#039; &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
package:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then point &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the user-service socket&lt;br /&gt;
in your shell rc (this replaces the &amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;&lt;br /&gt;
snippet above):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
To skip the manual &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; step, let OpenSSH load&lt;br /&gt;
keys into the agent automatically the first time they are needed.&lt;br /&gt;
Add to &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first SSH connection then prompts for the key passphrase&lt;br /&gt;
once and hands the unlocked key to the agent; subsequent&lt;br /&gt;
connections use the cached identity without prompting.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ ships native OpenSSH including an agent service.&lt;br /&gt;
One-time setup:&lt;br /&gt;
&lt;br /&gt;
# Open &#039;&#039;&#039;Services&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) as Administrator.&lt;br /&gt;
# Find &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039;, set Startup Type to &#039;&#039;&#039;Automatic&#039;&#039;&#039;, click &#039;&#039;&#039;Start&#039;&#039;&#039;.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Windows OpenSSH agent listens on a named pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), not a Unix socket.  ST/X&lt;br /&gt;
supports both transports, but Windows ssh-add does &#039;&#039;&#039;not&#039;&#039;&#039; set&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; for you.  Add it manually:&lt;br /&gt;
&lt;br /&gt;
# Press {{Key|Win}} → type &amp;quot;environment&amp;quot; → &amp;quot;Edit the system environment variables&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Environment Variables&#039;&#039;&#039; → under &#039;&#039;&#039;User variables&#039;&#039;&#039;, &#039;&#039;&#039;New&#039;&#039;&#039;.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Value: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Log out and back in (or restart stx) so the new env propagates.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell quick-setup ====&lt;br /&gt;
&lt;br /&gt;
The same setup from an &#039;&#039;&#039;elevated&#039;&#039;&#039; PowerShell prompt, for&lt;br /&gt;
scripts or unattended provisioning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Start the agent now AND on every reboot (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# Persist SSH_AUTH_SOCK for the user (survives reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Load a key (prompts for the passphrase if the file is encrypted).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a one-shot agent start without making it persistent (e.g.&lt;br /&gt;
single-session test), drop the &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt; line and&lt;br /&gt;
just run &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt;.  The env-var line&lt;br /&gt;
can also be omitted if &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is only needed&lt;br /&gt;
in the current shell — use &amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
instead for that session-local form.&lt;br /&gt;
&lt;br /&gt;
On stripped-down Windows installs the ssh-agent service may not&lt;br /&gt;
be present.  Add it once via &#039;&#039;&#039;Settings → Apps → Optional&lt;br /&gt;
features → OpenSSH Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative agents:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — uses its own protocol; NOT supported by ST/X&#039;s SSH::Agent.  Migrate the keys to OpenSSH.&lt;br /&gt;
* &#039;&#039;&#039;Git for Windows ssh-agent&#039;&#039;&#039; — works; point&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the socket it publishes.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — a ST/X inside WSL sees WSL&#039;s Linux agent normally; a ST/X on the Windows side does not.  Bridging needs a helper like &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify in the Remote Access settings page&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
the agent indicator there reports whether the running image sees&lt;br /&gt;
the agent.&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
Windows OpenSSH does &#039;&#039;&#039;not&#039;&#039;&#039; persist agent-loaded keys across&lt;br /&gt;
agent restarts.  To avoid running &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; manually&lt;br /&gt;
after each reboot, add the same lazy-load configuration to&lt;br /&gt;
&amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH then loads the key into the agent on first use (prompts&lt;br /&gt;
for the passphrase once) and reuses it for the rest of the&lt;br /&gt;
session.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All tunables are class-side on &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Default !! What it controls&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 min) || How long a pooled&lt;br /&gt;
connection sits idle before the next access proactively closes +&lt;br /&gt;
reopens it.  Just under typical sshd&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; so we recycle&lt;br /&gt;
before the server TCP-RESETs us.  Pass &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; to restore&lt;br /&gt;
the default.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Max age (s) of a&lt;br /&gt;
cached STAT before &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; refetches.  Parent&lt;br /&gt;
listDir always re-stamps fresh attrs onto children, so navigating&lt;br /&gt;
an open directory does not pay the TTL.  Set to &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to&lt;br /&gt;
disable caching.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (action) || Tears down every&lt;br /&gt;
pooled connection.  Useful after a known-bad network event, before&lt;br /&gt;
a deliberate identity swap, or as part of a clean image shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
Open &amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; from the Launcher&#039;s &amp;quot;Status&amp;quot;&lt;br /&gt;
sub-menu.  Per-host SFTP mutex appears as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;; the pool-wide mutex as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Right-click a row:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — dumps the last-owner&#039;s walkback plus each waiter&#039;s, formatted as plain text.  Use when a process is wedged in &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; inside&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; and you need to see which SFTP&lt;br /&gt;
request it is on.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — the whole table, for an email-this-to-someone diagnosis.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS over the wait-for graph, reports cycles.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
The SSH stack logs interesting events:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; on auto-reconnect after a dead connection.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when a pool entry is idle-evicted.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when an SSH key file cannot be parsed (e.g. legacy PEM, encrypted-without-agent) — the file is skipped, others tried.&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SFTP v3 only.&#039;&#039;&#039;  No SETSTAT (no remote chmod / chown / utime), no SSH_FXP_READLINK exposed (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; always&lt;br /&gt;
&amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; returns the regular&lt;br /&gt;
stat info).  Several SFTPv5+ niceties are nevertheless picked up&lt;br /&gt;
via OpenSSH SSH_FXP_EXTENDED requests — see&lt;br /&gt;
[[#OpenSSH SFTP extensions]] below.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation.&#039;&#039;&#039;  Two concurrent operations on the same host queue through the host mutex.  See [[#Future work]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; fallback has a TOCTOU window.&#039;&#039;&#039;  On servers that advertise &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; (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.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; is heuristic.&#039;&#039;&#039;  Always returns &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (the accurate answer would cost three round-trips per directory icon, which made the original tree expansion unbearably slow).&lt;br /&gt;
&lt;br /&gt;
== Implementation details ==&lt;br /&gt;
&lt;br /&gt;
For readers wanting the architecture.  Five classes, top-down:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename subclass; the public&lt;br /&gt;
API.  Maps &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt; URLs to remote files; exposes&lt;br /&gt;
&amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3 protocol&lt;br /&gt;
(request/response codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Driven by SftpFilename.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH channel multiplexer&lt;br /&gt;
(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST).  One logical&lt;br /&gt;
session per Channel instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-level SSH client: opens the&lt;br /&gt;
transport, runs KEX, host-key check, userauth, then dispenses&lt;br /&gt;
Channels.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Wire layer.  Banner + KEXINIT&lt;br /&gt;
exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq,&lt;br /&gt;
heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH SFTP extensions ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal.&lt;br /&gt;
OpenSSH ships an open-ended extension mechanism: the server lists&lt;br /&gt;
extension names it understands in its &amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;&lt;br /&gt;
reply, and the client invokes them via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt; packets carrying the extension&lt;br /&gt;
name as the first string.  Each extension is feature-detected via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;; callers fall&lt;br /&gt;
back when the server doesn&#039;t advertise it.&lt;br /&gt;
&lt;br /&gt;
The stack uses four of the OpenSSH extensions today:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomic rename-with-overwrite.  Picked up automatically by&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt;; the delete-then-rename&lt;br /&gt;
fallback only fires on servers that lack it.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — create a POSIX hard link. Exposed as &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-shape filesystem stats.  Exposed as&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;; the result is&lt;br /&gt;
shape-compatible with &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;&lt;br /&gt;
so callers can treat local and remote uniformly.  Drives the&lt;br /&gt;
&#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; menu entry described at the&lt;br /&gt;
top of this page.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — flush server-side write buffer to disk on an open handle.  Available on the low-level&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt;; not yet plumbed&lt;br /&gt;
into a Filename-level &amp;quot;durable write&amp;quot; API.&lt;br /&gt;
&lt;br /&gt;
The remaining OpenSSH extensions&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
are recognised in the advertised-extensions list but not wrapped at&lt;br /&gt;
Filename level — there&#039;s no Filename-side caller for them yet.&lt;br /&gt;
&lt;br /&gt;
=== Connection pooling ===&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt; instance pointing at the same&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; triple shares one&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; plus one &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Pool is class-side, guarded by a single&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy bring-up&#039;&#039;&#039; — TCP + KEX + userauth + SFTP INIT happens on the first SFTP operation, not on &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation&#039;&#039;&#039; — SFTP requests on a given host are serialised through a &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; named&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; (visible in&lt;br /&gt;
SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle eviction&#039;&#039;&#039; — unused for longer than&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt;, the entry is proactively&lt;br /&gt;
closed + reopened on the next access.&lt;br /&gt;
* &#039;&#039;&#039;Auto-reconnect&#039;&#039;&#039; — a transport-level failure (broken pipe, EOF, MNU on nil socket) evicts the dead pool entry, opens a fresh client, retries the request &#039;&#039;&#039;once&#039;&#039;&#039;.  Application-level SFTP STATUS errors propagate immediately.&lt;br /&gt;
&lt;br /&gt;
== Future work ==&lt;br /&gt;
&lt;br /&gt;
Tracked but not yet implemented:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-channel parallelism per host&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Accurate &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (first batch only) + CLOSE — three RTTs per probe; needs SftpClient to pipeline requests before this pays off.&lt;br /&gt;
* &#039;&#039;&#039;SFTP v5/v6 negotiation&#039;&#039;&#039; for extended attrs and FTP-style canonicalisation.  (Atomic-overwrite rename is already handled via the OpenSSH &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; extension; see [[#OpenSSH SFTP extensions]].)&lt;br /&gt;
&lt;br /&gt;
= Command Shell =&lt;br /&gt;
&lt;br /&gt;
Local command shell on this expecco machine.  Typical applications:&lt;br /&gt;
local command-line, running a local helper tool, bridging a&lt;br /&gt;
remote workflow to a local utility.&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No credentials, no network — runs as the expecco process&#039;s own&lt;br /&gt;
user.  Output streams to expecco&#039;s log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warning]] &#039;&#039;&#039;Telnet is a legacy protocol&lt;br /&gt;
with no encryption.&#039;&#039;&#039; Passwords are transmitted in plain text on&lt;br /&gt;
the wire; anyone on the network path can read them.  Use Telnet&lt;br /&gt;
ONLY when the target device has no other option (typically: old&lt;br /&gt;
industrial controllers, lab instruments, embedded measurement&lt;br /&gt;
equipment without an SSH stack).  For everything else use&lt;br /&gt;
[[#SSH and SFTP]].&lt;br /&gt;
&lt;br /&gt;
The expecco plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (internal demo)&lt;br /&gt;
&lt;br /&gt;
The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream&lt;br /&gt;
over TCP, with in-band control sequences for terminal options.&lt;br /&gt;
A connection is established to a target host:port; after optional&lt;br /&gt;
in-band login, both sides can send data.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent forwarding, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of this stack.&lt;br /&gt;
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack for its HTTPS transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Network]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31318</id>
		<title>Remote Access</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31318"/>
		<updated>2026-05-26T18:50:28Z</updated>

		<summary type="html">&lt;p&gt;Sv: Document the Automatic Refresh dropdown toggle and its root-type default&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access|label=Deutsch}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere Dateiübertragung über einen SSH-2-Tunnel.  Reine Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen Maschine.  Für die Anbindung lokaler Werkzeuge und für die lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff (entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung, ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
&amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Schaltfläche &#039;&#039;&#039;Refresh&#039;&#039;&#039; in der Symbolleiste&lt;br /&gt;
(Pfeil-Kreis-Symbol zwischen &#039;&#039;Forward&#039;&#039; und &#039;&#039;DirectoryUp&#039;&#039;) liest&lt;br /&gt;
Baum und Inhalts-Panel auf Anforderung neu ein.  Funktioniert&lt;br /&gt;
einheitlich für lokale und SFTP-Pfade; bei SFTP wird zusätzlich der&lt;br /&gt;
per-Datei-STAT-Cache geleert, sodass Änderungen, die direkt auf&lt;br /&gt;
der Gegenseite gemacht wurden, sofort sichtbar werden — ohne auf&lt;br /&gt;
den Ablauf der 5-Sekunden-Cache-TTL zu warten.&lt;br /&gt;
&lt;br /&gt;
Der kleine Pfeil neben dem Refresh-Symbol öffnet ein Aufklappmenü&lt;br /&gt;
mit einem einzelnen Kontrollkästchen, &#039;&#039;&#039;Automatic Refresh&#039;&#039;&#039;, das&lt;br /&gt;
den Hintergrund-Task an- bzw. abschaltet, der alle expandierten&lt;br /&gt;
Baumeinträge auf externe Änderungen prüft.  Die Vorgabe richtet&lt;br /&gt;
sich nach der aktuellen Wurzel:&lt;br /&gt;
&lt;br /&gt;
* Lokales Dateisystem &amp;amp;rarr; &#039;&#039;&#039;an&#039;&#039;&#039; (10-Sekunden-Zyklus,&lt;br /&gt;
  entspricht dem bisherigen Verhalten).&lt;br /&gt;
* SFTP &amp;amp;rarr; &#039;&#039;&#039;aus&#039;&#039;&#039;.  Jeder Zyklus kostet einen&lt;br /&gt;
  STAT-Roundtrip pro Kind — für eine Handvoll lokaler&lt;br /&gt;
  Verzeichnisse harmlos, über das Netz schmerzhaft.  Bei Bedarf&lt;br /&gt;
  manuell auf Refresh klicken, um Änderungen zu sehen.&lt;br /&gt;
&lt;br /&gt;
Beim Wechsel zwischen lokalen und SFTP-Wurzeln wird der Schalter&lt;br /&gt;
automatisch umgelegt &amp;amp;mdash; aber nur, sofern man ihn für die&lt;br /&gt;
vorherige Wurzel nicht selbst verstellt hat.  Eine explizite&lt;br /&gt;
Benutzerwahl bleibt über Navigationen hinweg erhalten.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet vier Aktionen — die&lt;br /&gt;
drei SSH-spezifischen sind nur bei geladener SSH-Bibliothek&lt;br /&gt;
sichtbar:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den Schlüsselerzeugungs-Dialog, siehe [[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP auf ein entferntes Dateisystem.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — zeigt Größe, freien Platz und Belegung des Dateisystems, das das aktuell angezeigte Verzeichnis enthält. Funktioniert einheitlich für lokale und SFTP-Pfade; bei SFTP setzt der Aufruf voraus, daß der Server die Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; ankündigt (jedes moderne OpenSSH&lt;br /&gt;
tut das).  Größen werden in IEC-Binäreinheiten ausgegeben (MiB,&lt;br /&gt;
GiB, TiB) — gewählt wird die größte Einheit, die einen Wert ≥ 1&lt;br /&gt;
liefert, damit ein TB-großes Volume als &#039;&#039;X TiB&#039;&#039; statt&lt;br /&gt;
&#039;&#039;10240 GiB&#039;&#039; erscheint.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary (PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet (Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf &#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs- variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;, &#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback des letzten Eigners samt aller Waiter als Text in die Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
&amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes chmod / chown / utime), kein SSH_FXP_READLINK exponiert (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  Einige&lt;br /&gt;
SFTPv5+-Annehmlichkeiten werden dennoch über OpenSSH-spezifische&lt;br /&gt;
SSH_FXP_EXTENDED-Aufrufe nutzbar — siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]] weiter unten.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt;-Fallback hat ein TOCTOU-Fenster.&#039;&#039;&#039; Bei Servern, die &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; ankündigen (jedes moderne OpenSSH tut das), ist das Überschreiben atomar. Beim seltenen Server, der das nicht tut, wird auf Delete-dann-Rename ausgewichen und ein anderer Prozess kann sich dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039; Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH-SFTP-Erweiterungen ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (Entwurf draft-ietf-secsh-filexfer-02) ist bewusst&lt;br /&gt;
minimal gehalten.  OpenSSH bringt einen offenen&lt;br /&gt;
Erweiterungsmechanismus mit: der Server listet im&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;-Reply die Erweiterungsnamen auf, die&lt;br /&gt;
er versteht, und der Client ruft sie über&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt;-Pakete mit dem&lt;br /&gt;
Erweiterungsnamen als erstem String auf.  Jede Erweiterung wird&lt;br /&gt;
über &amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;&lt;br /&gt;
feature-detektiert; Aufrufer fallen zurück, wenn der Server sie&lt;br /&gt;
nicht ankündigt.&lt;br /&gt;
&lt;br /&gt;
Der Stack nutzt heute vier OpenSSH-Erweiterungen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomares rename-mit-Überschreiben.  Wird automatisch von&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt; aufgegriffen; die&lt;br /&gt;
Delete-dann-Rename-Fallback-Variante kommt nur bei Servern zum&lt;br /&gt;
Einsatz, die die Erweiterung nicht haben.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — Erzeugt einen POSIX-Hardlink. Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX-&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-typische Dateisystem-Statistik.&lt;br /&gt;
Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
das Ergebnis ist form-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;, sodass Aufrufer&lt;br /&gt;
lokale und entfernte Pfade einheitlich behandeln können.&lt;br /&gt;
Treibt den Menü-Eintrag &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; an,&lt;br /&gt;
der am Anfang dieser Seite beschrieben ist.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — schreibt den serverseitigen Schreibpuffer eines geöffneten Handles auf Platte.  Liegt als&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt; bereit; noch nicht&lt;br /&gt;
in eine &amp;quot;Durable-Write&amp;quot;-API auf Filename-Ebene eingebunden.&lt;br /&gt;
&lt;br /&gt;
Die verbleibenden OpenSSH-Erweiterungen&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
werden in der angekündigten Liste erkannt, aber nicht auf&lt;br /&gt;
Filename-Ebene gekapselt — es gibt dafür noch keinen&lt;br /&gt;
Filename-seitigen Aufrufer.&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene (Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den Pool-Eintrag, öffnet einen frischen Client und wiederholt die Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet eine TCP- plus eine SFTP-Verbindung pro Host, dass N gleichzeitige Anfragen serialisieren.  Pipelining über mehrere SshClients im Pool (bevorzugt) oder ein transport-seitiger Reader-Prozess, der eingehende Pakete in Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel erlauben, weiter aufzulisten, während das Inhalts-Panel eine große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für erweiterte Attribute und FTP-artige Kanonisierung.  (Atomares Überschreibungs-rename ist bereits über die OpenSSH-Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; abgedeckt; siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]].)&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY, Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31306</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31306"/>
		<updated>2026-05-26T14:42:30Z</updated>

		<summary type="html">&lt;p&gt;Sv: update bridge features: SSL for all bridges incl. Dart+Smalltalk, getFile/putFile/makeDirectory for all bridges, defineFunction/callFunction for NodeJS/Ruby/Smalltalk&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q2 2026) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Feature: &#039;&#039;&#039;NEW&#039;&#039;&#039; [[KI Coding Plugin|&#039;&#039;&#039;AI Coding&#039;&#039;&#039;]] plugin (chat-assistant integration for the activity editor and class browser):&lt;br /&gt;
** supports both &#039;&#039;&#039;Anthropic Claude&#039;&#039;&#039;, &#039;&#039;&#039;OpenAI ChatGPT&#039;&#039;&#039; or &#039;&#039;&#039;Llama&#039;&#039;&#039; as backends, switchable from the settings dialog (Plugins → AI Coding);&amp;lt;br&amp;gt;API keys are stored per provider so you can flip between them without re-entering&lt;br /&gt;
** Toolbar / class-browser menu adapts to the active provider — reads &amp;quot;&#039;&#039;Ask Claude&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;Ask ChatGPT&#039;&#039;&amp;quot;, updates live when the provider is switched&lt;br /&gt;
** Menu actions: Explain code/method, Suggest improvement, Generate test, Generate doc-comment (fills the Documentation tab and pin comments), Find bugs, Custom prompt; &amp;quot;[Apply]&amp;quot; can install proposed code directly into the activity body or compile a proposed helper method into a class&lt;br /&gt;
** Chat window streams responses live (Server-Sent Events) and shows running token count + estimated cost in the title; supports image attachments (screenshots / PNG-JPG files)&lt;br /&gt;
** model, API key, endpoint and max-tokens are configurable via the Claude settings dialog&lt;br /&gt;
* Feature: SSL1.3 support (without external SSL library)&lt;br /&gt;
* Feature: SSL support for C, Python, NodeJS, Ruby, Dart and Smalltalk bridges (combined cert+key PEM files supported)&lt;br /&gt;
* Feature: SSH builtin, plus SSH and SFTP clients (both via FileBrowser)&lt;br /&gt;
* Feature: public/private keypair generation via a FileBrowser menu (for easy SSH setup)&lt;br /&gt;
* Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;br /&gt;
* Feature: Improved/Fixed Number stack:&lt;br /&gt;
** Enhanced multiprecision numbers (eg. &amp;lt;float&amp;gt;q, &amp;lt;float&amp;gt;Q constants in freeze values)&lt;br /&gt;
** Float32 numbers (&amp;lt;float&amp;gt;f)&lt;br /&gt;
** Integer freezeValues in exponential notation (eg. 1e5)&lt;br /&gt;
** Recognize type specific infinities eg. &amp;quot;inf.0&amp;quot;, &amp;quot;inf.0f&amp;quot;, &amp;quot;inf.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** Recognize type specific NaNs eg. &amp;quot;nan.0&amp;quot;, &amp;quot;nan.0f&amp;quot;, &amp;quot;nan.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** fixed/added missing trigonometric functions for multiprecision floats and complex numbers (eg. arcTan)&lt;br /&gt;
** inspector (and activitylog as a consequence) show the type of a float (suffix &#039;f&#039;, &#039;q&#039;, &#039;Q&#039; etc.)&lt;br /&gt;
&lt;br /&gt;
* Feature: Workflow editor — improved orthogonal routing of connections:&lt;br /&gt;
** connections now detour around blocks, freeze values and annotation boxes instead of cutting through them&lt;br /&gt;
** connections from a compound block&#039;s input-pin descriptions are bundled into a bus column next to the source pin&lt;br /&gt;
** end-stub avoidance no longer fires on near-misses (strict overlap check, no clearance margin)&lt;br /&gt;
** routing prefers the source-side bend when the source step has multiple sibling pins&lt;br /&gt;
* Feature: Workflow editor — improved naïve autolayout: added horizontal and vertical expansion passes that spread adjacent blocks apart for clearer connection routing&lt;br /&gt;
* Feature: file transfer (getFile/putFile) and makeDirectory for all bridges (C, Python, NodeJS, Ruby, Dart, Smalltalk)&lt;br /&gt;
* Feature: defineFunction/callFunction support for NodeJS, Ruby and Smalltalk bridges&lt;br /&gt;
* Performance: execution of elementary Smalltalk and JavaScript actions tuned for speed (Jitter improvements)&lt;br /&gt;
* Feature: OLE for 64 bit architectures&lt;br /&gt;
* Feature: optional HTTPS for the AIDYMO and license server — drop a PEM cert+key into &amp;lt;code&amp;gt;--workDir&amp;lt;/code&amp;gt; (combined &amp;lt;code&amp;gt;server.pem&amp;lt;/code&amp;gt;, or split&amp;lt;code&amp;gt;fullchain.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;privkey.pem&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;cert.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;key.pem&amp;lt;/code&amp;gt;) and the service binds TLS automatically; informational hostname is derived from the certificate (SAN-aware, wildcard- and multi-SAN-safe)&lt;br /&gt;
* Feature: more search options in the [[How_to_Program/en#MethodFinder:_Find_Functions_by_Example | MethodFinder]]. &lt;br /&gt;
*Fix: many fixes related to DPI scaling. I.e. when multiple monitors are configured with different scaling (especially different from 100%). Includes scaling of fonts, bitmap and UI components (widgets).&lt;br /&gt;
*Fix: display of very long lines in a text editor/inspector (workaround a Windows 16bit line limit)&lt;br /&gt;
*Fix: due to a bug in enumeration datatypes, the size of &amp;quot;.ets&amp;quot; files grew over time to huge sizes (some information was redundantly and identically written twice). This had no effect on the execution, but made load/save times almost unacceptably long by storing/reloading unneeded data. When loaded and saved again, this will fix those ets files automatically (there is also a patch for older versions)&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31305</id>
		<title>Remote Access/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31305"/>
		<updated>2026-05-26T13:46:08Z</updated>

		<summary type="html">&lt;p&gt;Sv: Document the Refresh toolbar button (and the disabled tree auto-poll)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access/en|label=English}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Remote access&#039;&#039;&#039; is the ability to drive a remote computer or&lt;br /&gt;
network from this expecco image — opening shells, running commands,&lt;br /&gt;
moving files, or driving a test target.  Three protocol families are&lt;br /&gt;
supported, listed in current-recommended order:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH and SFTP&#039;&#039;&#039; (recommended) — encrypted shell + secure file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on OpenSSL or libssh.  Use this for anything that touches credentials or sensitive payloads.&lt;br /&gt;
* &#039;&#039;&#039;Local Command Shell&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (legacy) — plain-text terminal session.  No encryption, passwords on the wire in clear.  Use only when the target hardware has no other option.&lt;br /&gt;
&lt;br /&gt;
= SSH and SFTP =&lt;br /&gt;
&lt;br /&gt;
The SSH stack covers the full SSH-2 protocol (RFC 4251–4254,&lt;br /&gt;
RFC 5656, RFC 8709, RFC 8731) plus OpenSSH&#039;s chacha20-poly1305&lt;br /&gt;
transport cipher and the SFTP v3 file-transfer subsystem&lt;br /&gt;
(draft-ietf-secsh-filexfer-02).  Two layers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatic SSH access (remote&lt;br /&gt;
&amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY shell, agent forwarding, ProxyJump bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — a &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt; subclass that lets the rest of ST/X treat a remote SFTP path the same way it treats a local file.&lt;br /&gt;
&lt;br /&gt;
The rest of this section is organised user-task-first: what the user&lt;br /&gt;
sees and does, the expecco-library hooks below that, then the&lt;br /&gt;
implementation detail at the end for the curious.&lt;br /&gt;
&lt;br /&gt;
== From the FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Open the location dropdown and paste an &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt; URL.&lt;br /&gt;
The browser tab populates as if it were a local path.  Tree&lt;br /&gt;
expansion, column sort (name / size / mtime), preview, and&lt;br /&gt;
double-click-to-open-in-editor all behave normally.  The first&lt;br /&gt;
click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent&lt;br /&gt;
clicks reuse the pooled connection.&lt;br /&gt;
&lt;br /&gt;
URL syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User defaults to the local login name, port to 22, path to&lt;br /&gt;
&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The directory tree does &#039;&#039;&#039;not&#039;&#039;&#039; auto-update: previously a background&lt;br /&gt;
task walked every expanded item every 10 seconds, which over SFTP&lt;br /&gt;
meant one STAT round-trip per child on every cycle.  Click the&lt;br /&gt;
&#039;&#039;&#039;Refresh&#039;&#039;&#039; button in the toolbar (the round-arrow icon between&lt;br /&gt;
&#039;&#039;Forward&#039;&#039; and &#039;&#039;DirectoryUp&#039;&#039;) to re-read the tree and the&lt;br /&gt;
contents pane on demand.  Refresh works uniformly for local and&lt;br /&gt;
SFTP paths; for SFTP it also flushes the per-file STAT cache, so&lt;br /&gt;
changes made directly on the remote side become visible&lt;br /&gt;
immediately rather than waiting for the 5-second cache TTL to&lt;br /&gt;
expire.&lt;br /&gt;
&lt;br /&gt;
The Tools menu offers four browser actions, three of them gated on&lt;br /&gt;
the SSH library being loaded:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — opens the same key-generation dialog described under [[#Generating an SSH key pair]] below.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — opens an interactive VT100 terminal to a remote host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — points this browser tab at a remote filesystem via SFTP.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — 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 &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; 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 &#039;&#039;X TiB&#039;&#039; rather than &#039;&#039;10240 GiB&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== From expecco actions ==&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) exposes the following test&lt;br /&gt;
actions to the expecco action palette:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — opens an SSH session via the platform&#039;s &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; binary (PuTTY&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; on Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — same but with explicit public-key authentication.&lt;br /&gt;
&lt;br /&gt;
To run these you need a configured keypair (private key on this&lt;br /&gt;
machine, public key in the remote host&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;).  Generate one via the dialog&lt;br /&gt;
below or via &amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The plugin also adds a settings page at &#039;&#039;&#039;Extras → Settings →&lt;br /&gt;
Plugins → Remote Access — SSH Keys&#039;&#039;&#039; carrying a single&lt;br /&gt;
&#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; button that opens the same dialog.&lt;br /&gt;
&lt;br /&gt;
== Generating an SSH key pair ==&lt;br /&gt;
&lt;br /&gt;
=== The dialog (FileBrowserV2 / settings page) ===&lt;br /&gt;
&lt;br /&gt;
The dialog asks for all parameters in one form:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — embedded in the generated key (defaults to&lt;br /&gt;
&amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — writes &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (or wherever) plus a matching &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt; companion.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — writes the file and also hands the key to the running ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — key lives in agent memory only; gone on agent restart.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — full path; disabled in agent-only mode.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — empty leaves the on-disk file unencrypted (agent-only mode ignores the passphrase, since the OpenSSH agent wire protocol carries only the decrypted key).&lt;br /&gt;
&lt;br /&gt;
On &#039;&#039;&#039;Generate&#039;&#039;&#039;, the public-key line (the same&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt; string ssh-keygen&lt;br /&gt;
emits) is copied to the system clipboard for pasting into the&lt;br /&gt;
remote host&#039;s &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From a workspace ===&lt;br /&gt;
&lt;br /&gt;
For headless deployments, sandboxed builds, or scripts,&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; exposes a pure-Smalltalk key generator&lt;br /&gt;
that produces output bit-compatible with&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Save passphrase-encrypted to disk&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ AND load into the running agent&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Print the public-key line to paste into authorized_keys&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keys generated this way are interoperable with OpenSSH&#039;s own&lt;br /&gt;
tooling (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; re-derives the public&lt;br /&gt;
key, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; changes the passphrase,&lt;br /&gt;
etc.).&lt;br /&gt;
&lt;br /&gt;
=== Using the shell tools instead ===&lt;br /&gt;
&lt;br /&gt;
The traditional path also works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparing ssh-agent ==&lt;br /&gt;
&lt;br /&gt;
The agent path is strongly preferred over reading raw keyfiles: it&lt;br /&gt;
keeps encrypted private keys unlocked once per session, and handles&lt;br /&gt;
identities (hardware-token-backed keys, KeePassXC entries) that&lt;br /&gt;
ST/X should never see directly.&lt;br /&gt;
&lt;br /&gt;
ST/X picks the agent path automatically when&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is set in the process environment&lt;br /&gt;
&#039;&#039;&#039;at the time stx is launched&#039;&#039;&#039;.  Setting it later from a&lt;br /&gt;
workspace does not help.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Most desktop distributions launch an agent automatically as part of&lt;br /&gt;
the session (gnome-keyring on GNOME, ssh-agent.service on systemd,&lt;br /&gt;
KWallet on KDE).  Verify in a terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar&lt;br /&gt;
ssh-add -l             # lists loaded identities&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If no agent runs at all, add this snippet to your shell rc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc or ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X must be launched from a shell that has seen this rc — a&lt;br /&gt;
desktop launcher started from the file manager does NOT inherit&lt;br /&gt;
the variable.  Wrap the stx start command in a small script under&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt; that sources the rc first.&lt;br /&gt;
&lt;br /&gt;
The Remote Access settings page (&#039;&#039;&#039;Extras → Settings → Plugins&lt;br /&gt;
→ Remote Access — SSH Keys&#039;&#039;&#039;) shows whether the running image&lt;br /&gt;
sees an agent.&lt;br /&gt;
&lt;br /&gt;
==== Permanent setup via systemd ====&lt;br /&gt;
&lt;br /&gt;
For a truly cross-session agent (survives desktop logouts, comes&lt;br /&gt;
up automatically at next login), enable the per-user systemd&lt;br /&gt;
unit shipped with most distros&#039; &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
package:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then point &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the user-service socket&lt;br /&gt;
in your shell rc (this replaces the &amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;&lt;br /&gt;
snippet above):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
To skip the manual &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; step, let OpenSSH load&lt;br /&gt;
keys into the agent automatically the first time they are needed.&lt;br /&gt;
Add to &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first SSH connection then prompts for the key passphrase&lt;br /&gt;
once and hands the unlocked key to the agent; subsequent&lt;br /&gt;
connections use the cached identity without prompting.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ ships native OpenSSH including an agent service.&lt;br /&gt;
One-time setup:&lt;br /&gt;
&lt;br /&gt;
# Open &#039;&#039;&#039;Services&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) as Administrator.&lt;br /&gt;
# Find &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039;, set Startup Type to &#039;&#039;&#039;Automatic&#039;&#039;&#039;, click &#039;&#039;&#039;Start&#039;&#039;&#039;.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Windows OpenSSH agent listens on a named pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), not a Unix socket.  ST/X&lt;br /&gt;
supports both transports, but Windows ssh-add does &#039;&#039;&#039;not&#039;&#039;&#039; set&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; for you.  Add it manually:&lt;br /&gt;
&lt;br /&gt;
# Press {{Key|Win}} → type &amp;quot;environment&amp;quot; → &amp;quot;Edit the system environment variables&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Environment Variables&#039;&#039;&#039; → under &#039;&#039;&#039;User variables&#039;&#039;&#039;, &#039;&#039;&#039;New&#039;&#039;&#039;.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Value: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Log out and back in (or restart stx) so the new env propagates.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell quick-setup ====&lt;br /&gt;
&lt;br /&gt;
The same setup from an &#039;&#039;&#039;elevated&#039;&#039;&#039; PowerShell prompt, for&lt;br /&gt;
scripts or unattended provisioning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Start the agent now AND on every reboot (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# Persist SSH_AUTH_SOCK for the user (survives reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Load a key (prompts for the passphrase if the file is encrypted).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a one-shot agent start without making it persistent (e.g.&lt;br /&gt;
single-session test), drop the &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt; line and&lt;br /&gt;
just run &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt;.  The env-var line&lt;br /&gt;
can also be omitted if &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is only needed&lt;br /&gt;
in the current shell — use &amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
instead for that session-local form.&lt;br /&gt;
&lt;br /&gt;
On stripped-down Windows installs the ssh-agent service may not&lt;br /&gt;
be present.  Add it once via &#039;&#039;&#039;Settings → Apps → Optional&lt;br /&gt;
features → OpenSSH Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative agents:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — uses its own protocol; NOT supported by ST/X&#039;s SSH::Agent.  Migrate the keys to OpenSSH.&lt;br /&gt;
* &#039;&#039;&#039;Git for Windows ssh-agent&#039;&#039;&#039; — works; point&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the socket it publishes.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — a ST/X inside WSL sees WSL&#039;s Linux agent normally; a ST/X on the Windows side does not.  Bridging needs a helper like &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify in the Remote Access settings page&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
the agent indicator there reports whether the running image sees&lt;br /&gt;
the agent.&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
Windows OpenSSH does &#039;&#039;&#039;not&#039;&#039;&#039; persist agent-loaded keys across&lt;br /&gt;
agent restarts.  To avoid running &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; manually&lt;br /&gt;
after each reboot, add the same lazy-load configuration to&lt;br /&gt;
&amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH then loads the key into the agent on first use (prompts&lt;br /&gt;
for the passphrase once) and reuses it for the rest of the&lt;br /&gt;
session.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All tunables are class-side on &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Default !! What it controls&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 min) || How long a pooled&lt;br /&gt;
connection sits idle before the next access proactively closes +&lt;br /&gt;
reopens it.  Just under typical sshd&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; so we recycle&lt;br /&gt;
before the server TCP-RESETs us.  Pass &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; to restore&lt;br /&gt;
the default.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Max age (s) of a&lt;br /&gt;
cached STAT before &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; refetches.  Parent&lt;br /&gt;
listDir always re-stamps fresh attrs onto children, so navigating&lt;br /&gt;
an open directory does not pay the TTL.  Set to &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to&lt;br /&gt;
disable caching.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (action) || Tears down every&lt;br /&gt;
pooled connection.  Useful after a known-bad network event, before&lt;br /&gt;
a deliberate identity swap, or as part of a clean image shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
Open &amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; from the Launcher&#039;s &amp;quot;Status&amp;quot;&lt;br /&gt;
sub-menu.  Per-host SFTP mutex appears as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;; the pool-wide mutex as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Right-click a row:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — dumps the last-owner&#039;s walkback plus each waiter&#039;s, formatted as plain text.  Use when a process is wedged in &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; inside&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; and you need to see which SFTP&lt;br /&gt;
request it is on.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — the whole table, for an email-this-to-someone diagnosis.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS over the wait-for graph, reports cycles.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
The SSH stack logs interesting events:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; on auto-reconnect after a dead connection.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when a pool entry is idle-evicted.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when an SSH key file cannot be parsed (e.g. legacy PEM, encrypted-without-agent) — the file is skipped, others tried.&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SFTP v3 only.&#039;&#039;&#039;  No SETSTAT (no remote chmod / chown / utime), no SSH_FXP_READLINK exposed (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; always&lt;br /&gt;
&amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; returns the regular&lt;br /&gt;
stat info).  Several SFTPv5+ niceties are nevertheless picked up&lt;br /&gt;
via OpenSSH SSH_FXP_EXTENDED requests — see&lt;br /&gt;
[[#OpenSSH SFTP extensions]] below.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation.&#039;&#039;&#039;  Two concurrent operations on the same host queue through the host mutex.  See [[#Future work]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; fallback has a TOCTOU window.&#039;&#039;&#039;  On servers that advertise &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; (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.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; is heuristic.&#039;&#039;&#039;  Always returns &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (the accurate answer would cost three round-trips per directory icon, which made the original tree expansion unbearably slow).&lt;br /&gt;
&lt;br /&gt;
== Implementation details ==&lt;br /&gt;
&lt;br /&gt;
For readers wanting the architecture.  Five classes, top-down:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename subclass; the public&lt;br /&gt;
API.  Maps &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt; URLs to remote files; exposes&lt;br /&gt;
&amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3 protocol&lt;br /&gt;
(request/response codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Driven by SftpFilename.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH channel multiplexer&lt;br /&gt;
(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST).  One logical&lt;br /&gt;
session per Channel instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-level SSH client: opens the&lt;br /&gt;
transport, runs KEX, host-key check, userauth, then dispenses&lt;br /&gt;
Channels.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Wire layer.  Banner + KEXINIT&lt;br /&gt;
exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq,&lt;br /&gt;
heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH SFTP extensions ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal.&lt;br /&gt;
OpenSSH ships an open-ended extension mechanism: the server lists&lt;br /&gt;
extension names it understands in its &amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;&lt;br /&gt;
reply, and the client invokes them via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt; packets carrying the extension&lt;br /&gt;
name as the first string.  Each extension is feature-detected via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;; callers fall&lt;br /&gt;
back when the server doesn&#039;t advertise it.&lt;br /&gt;
&lt;br /&gt;
The stack uses four of the OpenSSH extensions today:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomic rename-with-overwrite.  Picked up automatically by&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt;; the delete-then-rename&lt;br /&gt;
fallback only fires on servers that lack it.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — create a POSIX hard link. Exposed as &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-shape filesystem stats.  Exposed as&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;; the result is&lt;br /&gt;
shape-compatible with &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;&lt;br /&gt;
so callers can treat local and remote uniformly.  Drives the&lt;br /&gt;
&#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; menu entry described at the&lt;br /&gt;
top of this page.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — flush server-side write buffer to disk on an open handle.  Available on the low-level&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt;; not yet plumbed&lt;br /&gt;
into a Filename-level &amp;quot;durable write&amp;quot; API.&lt;br /&gt;
&lt;br /&gt;
The remaining OpenSSH extensions&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
are recognised in the advertised-extensions list but not wrapped at&lt;br /&gt;
Filename level — there&#039;s no Filename-side caller for them yet.&lt;br /&gt;
&lt;br /&gt;
=== Connection pooling ===&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt; instance pointing at the same&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; triple shares one&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; plus one &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Pool is class-side, guarded by a single&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy bring-up&#039;&#039;&#039; — TCP + KEX + userauth + SFTP INIT happens on the first SFTP operation, not on &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation&#039;&#039;&#039; — SFTP requests on a given host are serialised through a &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; named&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; (visible in&lt;br /&gt;
SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle eviction&#039;&#039;&#039; — unused for longer than&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt;, the entry is proactively&lt;br /&gt;
closed + reopened on the next access.&lt;br /&gt;
* &#039;&#039;&#039;Auto-reconnect&#039;&#039;&#039; — a transport-level failure (broken pipe, EOF, MNU on nil socket) evicts the dead pool entry, opens a fresh client, retries the request &#039;&#039;&#039;once&#039;&#039;&#039;.  Application-level SFTP STATUS errors propagate immediately.&lt;br /&gt;
&lt;br /&gt;
== Future work ==&lt;br /&gt;
&lt;br /&gt;
Tracked but not yet implemented:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-channel parallelism per host&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Accurate &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (first batch only) + CLOSE — three RTTs per probe; needs SftpClient to pipeline requests before this pays off.&lt;br /&gt;
* &#039;&#039;&#039;SFTP v5/v6 negotiation&#039;&#039;&#039; for extended attrs and FTP-style canonicalisation.  (Atomic-overwrite rename is already handled via the OpenSSH &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; extension; see [[#OpenSSH SFTP extensions]].)&lt;br /&gt;
&lt;br /&gt;
= Command Shell =&lt;br /&gt;
&lt;br /&gt;
Local command shell on this expecco machine.  Typical applications:&lt;br /&gt;
local command-line, running a local helper tool, bridging a&lt;br /&gt;
remote workflow to a local utility.&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No credentials, no network — runs as the expecco process&#039;s own&lt;br /&gt;
user.  Output streams to expecco&#039;s log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warning]] &#039;&#039;&#039;Telnet is a legacy protocol&lt;br /&gt;
with no encryption.&#039;&#039;&#039; Passwords are transmitted in plain text on&lt;br /&gt;
the wire; anyone on the network path can read them.  Use Telnet&lt;br /&gt;
ONLY when the target device has no other option (typically: old&lt;br /&gt;
industrial controllers, lab instruments, embedded measurement&lt;br /&gt;
equipment without an SSH stack).  For everything else use&lt;br /&gt;
[[#SSH and SFTP]].&lt;br /&gt;
&lt;br /&gt;
The expecco plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (internal demo)&lt;br /&gt;
&lt;br /&gt;
The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream&lt;br /&gt;
over TCP, with in-band control sequences for terminal options.&lt;br /&gt;
A connection is established to a target host:port; after optional&lt;br /&gt;
in-band login, both sides can send data.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent forwarding, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of this stack.&lt;br /&gt;
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack for its HTTPS transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Network]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31304</id>
		<title>Remote Access</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31304"/>
		<updated>2026-05-26T13:46:07Z</updated>

		<summary type="html">&lt;p&gt;Sv: Document the Refresh toolbar button (and the disabled tree auto-poll)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access|label=Deutsch}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere Dateiübertragung über einen SSH-2-Tunnel.  Reine Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen Maschine.  Für die Anbindung lokaler Werkzeuge und für die lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff (entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung, ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
&amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Verzeichnisbaum aktualisiert sich &#039;&#039;&#039;nicht&#039;&#039;&#039; automatisch:&lt;br /&gt;
früher lief im Hintergrund alle 10 Sekunden ein Task über jeden&lt;br /&gt;
expandierten Eintrag, was bei SFTP einen STAT-Roundtrip pro Kind&lt;br /&gt;
und Zyklus bedeutete.  Mit der Schaltfläche &#039;&#039;&#039;Refresh&#039;&#039;&#039; in der&lt;br /&gt;
Symbolleiste (Pfeil-Kreis-Symbol zwischen &#039;&#039;Forward&#039;&#039; und&lt;br /&gt;
&#039;&#039;DirectoryUp&#039;&#039;) werden Baum und Inhalts-Panel auf Anforderung&lt;br /&gt;
neu eingelesen.  Refresh funktioniert einheitlich für lokale und&lt;br /&gt;
SFTP-Pfade; bei SFTP wird zusätzlich der per-Datei-STAT-Cache&lt;br /&gt;
geleert, sodass Änderungen, die direkt auf der Gegenseite&lt;br /&gt;
gemacht wurden, sofort sichtbar werden — ohne auf den Ablauf der&lt;br /&gt;
5-Sekunden-Cache-TTL zu warten.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet vier Aktionen — die&lt;br /&gt;
drei SSH-spezifischen sind nur bei geladener SSH-Bibliothek&lt;br /&gt;
sichtbar:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den Schlüsselerzeugungs-Dialog, siehe [[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP auf ein entferntes Dateisystem.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — zeigt Größe, freien Platz und Belegung des Dateisystems, das das aktuell angezeigte Verzeichnis enthält. Funktioniert einheitlich für lokale und SFTP-Pfade; bei SFTP setzt der Aufruf voraus, daß der Server die Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; ankündigt (jedes moderne OpenSSH&lt;br /&gt;
tut das).  Größen werden in IEC-Binäreinheiten ausgegeben (MiB,&lt;br /&gt;
GiB, TiB) — gewählt wird die größte Einheit, die einen Wert ≥ 1&lt;br /&gt;
liefert, damit ein TB-großes Volume als &#039;&#039;X TiB&#039;&#039; statt&lt;br /&gt;
&#039;&#039;10240 GiB&#039;&#039; erscheint.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary (PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet (Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf &#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs- variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;, &#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback des letzten Eigners samt aller Waiter als Text in die Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
&amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes chmod / chown / utime), kein SSH_FXP_READLINK exponiert (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  Einige&lt;br /&gt;
SFTPv5+-Annehmlichkeiten werden dennoch über OpenSSH-spezifische&lt;br /&gt;
SSH_FXP_EXTENDED-Aufrufe nutzbar — siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]] weiter unten.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt;-Fallback hat ein TOCTOU-Fenster.&#039;&#039;&#039; Bei Servern, die &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; ankündigen (jedes moderne OpenSSH tut das), ist das Überschreiben atomar. Beim seltenen Server, der das nicht tut, wird auf Delete-dann-Rename ausgewichen und ein anderer Prozess kann sich dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039; Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH-SFTP-Erweiterungen ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (Entwurf draft-ietf-secsh-filexfer-02) ist bewusst&lt;br /&gt;
minimal gehalten.  OpenSSH bringt einen offenen&lt;br /&gt;
Erweiterungsmechanismus mit: der Server listet im&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;-Reply die Erweiterungsnamen auf, die&lt;br /&gt;
er versteht, und der Client ruft sie über&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt;-Pakete mit dem&lt;br /&gt;
Erweiterungsnamen als erstem String auf.  Jede Erweiterung wird&lt;br /&gt;
über &amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;&lt;br /&gt;
feature-detektiert; Aufrufer fallen zurück, wenn der Server sie&lt;br /&gt;
nicht ankündigt.&lt;br /&gt;
&lt;br /&gt;
Der Stack nutzt heute vier OpenSSH-Erweiterungen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomares rename-mit-Überschreiben.  Wird automatisch von&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt; aufgegriffen; die&lt;br /&gt;
Delete-dann-Rename-Fallback-Variante kommt nur bei Servern zum&lt;br /&gt;
Einsatz, die die Erweiterung nicht haben.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — Erzeugt einen POSIX-Hardlink. Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX-&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-typische Dateisystem-Statistik.&lt;br /&gt;
Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
das Ergebnis ist form-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;, sodass Aufrufer&lt;br /&gt;
lokale und entfernte Pfade einheitlich behandeln können.&lt;br /&gt;
Treibt den Menü-Eintrag &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; an,&lt;br /&gt;
der am Anfang dieser Seite beschrieben ist.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — schreibt den serverseitigen Schreibpuffer eines geöffneten Handles auf Platte.  Liegt als&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt; bereit; noch nicht&lt;br /&gt;
in eine &amp;quot;Durable-Write&amp;quot;-API auf Filename-Ebene eingebunden.&lt;br /&gt;
&lt;br /&gt;
Die verbleibenden OpenSSH-Erweiterungen&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
werden in der angekündigten Liste erkannt, aber nicht auf&lt;br /&gt;
Filename-Ebene gekapselt — es gibt dafür noch keinen&lt;br /&gt;
Filename-seitigen Aufrufer.&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene (Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den Pool-Eintrag, öffnet einen frischen Client und wiederholt die Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet eine TCP- plus eine SFTP-Verbindung pro Host, dass N gleichzeitige Anfragen serialisieren.  Pipelining über mehrere SshClients im Pool (bevorzugt) oder ein transport-seitiger Reader-Prozess, der eingehende Pakete in Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel erlauben, weiter aufzulisten, während das Inhalts-Panel eine große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für erweiterte Attribute und FTP-artige Kanonisierung.  (Atomares Überschreibungs-rename ist bereits über die OpenSSH-Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; abgedeckt; siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]].)&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY, Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31302</id>
		<title>Remote Access/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31302"/>
		<updated>2026-05-26T10:33:01Z</updated>

		<summary type="html">&lt;p&gt;Sv: Fold bullet continuation lines onto a single physical line so MediaWiki keeps them inside &amp;lt;li&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access/en|label=English}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Remote access&#039;&#039;&#039; is the ability to drive a remote computer or&lt;br /&gt;
network from this expecco image — opening shells, running commands,&lt;br /&gt;
moving files, or driving a test target.  Three protocol families are&lt;br /&gt;
supported, listed in current-recommended order:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH and SFTP&#039;&#039;&#039; (recommended) — encrypted shell + secure file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on OpenSSL or libssh.  Use this for anything that touches credentials or sensitive payloads.&lt;br /&gt;
* &#039;&#039;&#039;Local Command Shell&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (legacy) — plain-text terminal session.  No encryption, passwords on the wire in clear.  Use only when the target hardware has no other option.&lt;br /&gt;
&lt;br /&gt;
= SSH and SFTP =&lt;br /&gt;
&lt;br /&gt;
The SSH stack covers the full SSH-2 protocol (RFC 4251–4254,&lt;br /&gt;
RFC 5656, RFC 8709, RFC 8731) plus OpenSSH&#039;s chacha20-poly1305&lt;br /&gt;
transport cipher and the SFTP v3 file-transfer subsystem&lt;br /&gt;
(draft-ietf-secsh-filexfer-02).  Two layers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatic SSH access (remote&lt;br /&gt;
&amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY shell, agent forwarding, ProxyJump bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — a &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt; subclass that lets the rest of ST/X treat a remote SFTP path the same way it treats a local file.&lt;br /&gt;
&lt;br /&gt;
The rest of this section is organised user-task-first: what the user&lt;br /&gt;
sees and does, the expecco-library hooks below that, then the&lt;br /&gt;
implementation detail at the end for the curious.&lt;br /&gt;
&lt;br /&gt;
== From the FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Open the location dropdown and paste an &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt; URL.&lt;br /&gt;
The browser tab populates as if it were a local path.  Tree&lt;br /&gt;
expansion, column sort (name / size / mtime), preview, and&lt;br /&gt;
double-click-to-open-in-editor all behave normally.  The first&lt;br /&gt;
click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent&lt;br /&gt;
clicks reuse the pooled connection.&lt;br /&gt;
&lt;br /&gt;
URL syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User defaults to the local login name, port to 22, path to&lt;br /&gt;
&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Tools menu offers four browser actions, three of them gated on&lt;br /&gt;
the SSH library being loaded:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — opens the same key-generation dialog described under [[#Generating an SSH key pair]] below.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — opens an interactive VT100 terminal to a remote host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — points this browser tab at a remote filesystem via SFTP.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — 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 &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; 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 &#039;&#039;X TiB&#039;&#039; rather than &#039;&#039;10240 GiB&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== From expecco actions ==&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) exposes the following test&lt;br /&gt;
actions to the expecco action palette:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — opens an SSH session via the platform&#039;s &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; binary (PuTTY&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; on Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — same but with explicit public-key authentication.&lt;br /&gt;
&lt;br /&gt;
To run these you need a configured keypair (private key on this&lt;br /&gt;
machine, public key in the remote host&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;).  Generate one via the dialog&lt;br /&gt;
below or via &amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The plugin also adds a settings page at &#039;&#039;&#039;Extras → Settings →&lt;br /&gt;
Plugins → Remote Access — SSH Keys&#039;&#039;&#039; carrying a single&lt;br /&gt;
&#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; button that opens the same dialog.&lt;br /&gt;
&lt;br /&gt;
== Generating an SSH key pair ==&lt;br /&gt;
&lt;br /&gt;
=== The dialog (FileBrowserV2 / settings page) ===&lt;br /&gt;
&lt;br /&gt;
The dialog asks for all parameters in one form:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — embedded in the generated key (defaults to&lt;br /&gt;
&amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — writes &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (or wherever) plus a matching &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt; companion.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — writes the file and also hands the key to the running ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — key lives in agent memory only; gone on agent restart.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — full path; disabled in agent-only mode.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — empty leaves the on-disk file unencrypted (agent-only mode ignores the passphrase, since the OpenSSH agent wire protocol carries only the decrypted key).&lt;br /&gt;
&lt;br /&gt;
On &#039;&#039;&#039;Generate&#039;&#039;&#039;, the public-key line (the same&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt; string ssh-keygen&lt;br /&gt;
emits) is copied to the system clipboard for pasting into the&lt;br /&gt;
remote host&#039;s &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From a workspace ===&lt;br /&gt;
&lt;br /&gt;
For headless deployments, sandboxed builds, or scripts,&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; exposes a pure-Smalltalk key generator&lt;br /&gt;
that produces output bit-compatible with&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Save passphrase-encrypted to disk&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ AND load into the running agent&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Print the public-key line to paste into authorized_keys&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keys generated this way are interoperable with OpenSSH&#039;s own&lt;br /&gt;
tooling (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; re-derives the public&lt;br /&gt;
key, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; changes the passphrase,&lt;br /&gt;
etc.).&lt;br /&gt;
&lt;br /&gt;
=== Using the shell tools instead ===&lt;br /&gt;
&lt;br /&gt;
The traditional path also works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparing ssh-agent ==&lt;br /&gt;
&lt;br /&gt;
The agent path is strongly preferred over reading raw keyfiles: it&lt;br /&gt;
keeps encrypted private keys unlocked once per session, and handles&lt;br /&gt;
identities (hardware-token-backed keys, KeePassXC entries) that&lt;br /&gt;
ST/X should never see directly.&lt;br /&gt;
&lt;br /&gt;
ST/X picks the agent path automatically when&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is set in the process environment&lt;br /&gt;
&#039;&#039;&#039;at the time stx is launched&#039;&#039;&#039;.  Setting it later from a&lt;br /&gt;
workspace does not help.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Most desktop distributions launch an agent automatically as part of&lt;br /&gt;
the session (gnome-keyring on GNOME, ssh-agent.service on systemd,&lt;br /&gt;
KWallet on KDE).  Verify in a terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar&lt;br /&gt;
ssh-add -l             # lists loaded identities&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If no agent runs at all, add this snippet to your shell rc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc or ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X must be launched from a shell that has seen this rc — a&lt;br /&gt;
desktop launcher started from the file manager does NOT inherit&lt;br /&gt;
the variable.  Wrap the stx start command in a small script under&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt; that sources the rc first.&lt;br /&gt;
&lt;br /&gt;
The Remote Access settings page (&#039;&#039;&#039;Extras → Settings → Plugins&lt;br /&gt;
→ Remote Access — SSH Keys&#039;&#039;&#039;) shows whether the running image&lt;br /&gt;
sees an agent.&lt;br /&gt;
&lt;br /&gt;
==== Permanent setup via systemd ====&lt;br /&gt;
&lt;br /&gt;
For a truly cross-session agent (survives desktop logouts, comes&lt;br /&gt;
up automatically at next login), enable the per-user systemd&lt;br /&gt;
unit shipped with most distros&#039; &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
package:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then point &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the user-service socket&lt;br /&gt;
in your shell rc (this replaces the &amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;&lt;br /&gt;
snippet above):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
To skip the manual &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; step, let OpenSSH load&lt;br /&gt;
keys into the agent automatically the first time they are needed.&lt;br /&gt;
Add to &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first SSH connection then prompts for the key passphrase&lt;br /&gt;
once and hands the unlocked key to the agent; subsequent&lt;br /&gt;
connections use the cached identity without prompting.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ ships native OpenSSH including an agent service.&lt;br /&gt;
One-time setup:&lt;br /&gt;
&lt;br /&gt;
# Open &#039;&#039;&#039;Services&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) as Administrator.&lt;br /&gt;
# Find &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039;, set Startup Type to &#039;&#039;&#039;Automatic&#039;&#039;&#039;, click &#039;&#039;&#039;Start&#039;&#039;&#039;.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Windows OpenSSH agent listens on a named pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), not a Unix socket.  ST/X&lt;br /&gt;
supports both transports, but Windows ssh-add does &#039;&#039;&#039;not&#039;&#039;&#039; set&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; for you.  Add it manually:&lt;br /&gt;
&lt;br /&gt;
# Press {{Key|Win}} → type &amp;quot;environment&amp;quot; → &amp;quot;Edit the system environment variables&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Environment Variables&#039;&#039;&#039; → under &#039;&#039;&#039;User variables&#039;&#039;&#039;, &#039;&#039;&#039;New&#039;&#039;&#039;.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Value: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Log out and back in (or restart stx) so the new env propagates.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell quick-setup ====&lt;br /&gt;
&lt;br /&gt;
The same setup from an &#039;&#039;&#039;elevated&#039;&#039;&#039; PowerShell prompt, for&lt;br /&gt;
scripts or unattended provisioning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Start the agent now AND on every reboot (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# Persist SSH_AUTH_SOCK for the user (survives reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Load a key (prompts for the passphrase if the file is encrypted).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a one-shot agent start without making it persistent (e.g.&lt;br /&gt;
single-session test), drop the &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt; line and&lt;br /&gt;
just run &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt;.  The env-var line&lt;br /&gt;
can also be omitted if &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is only needed&lt;br /&gt;
in the current shell — use &amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
instead for that session-local form.&lt;br /&gt;
&lt;br /&gt;
On stripped-down Windows installs the ssh-agent service may not&lt;br /&gt;
be present.  Add it once via &#039;&#039;&#039;Settings → Apps → Optional&lt;br /&gt;
features → OpenSSH Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative agents:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — uses its own protocol; NOT supported by ST/X&#039;s SSH::Agent.  Migrate the keys to OpenSSH.&lt;br /&gt;
* &#039;&#039;&#039;Git for Windows ssh-agent&#039;&#039;&#039; — works; point&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the socket it publishes.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — a ST/X inside WSL sees WSL&#039;s Linux agent normally; a ST/X on the Windows side does not.  Bridging needs a helper like &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify in the Remote Access settings page&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
the agent indicator there reports whether the running image sees&lt;br /&gt;
the agent.&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
Windows OpenSSH does &#039;&#039;&#039;not&#039;&#039;&#039; persist agent-loaded keys across&lt;br /&gt;
agent restarts.  To avoid running &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; manually&lt;br /&gt;
after each reboot, add the same lazy-load configuration to&lt;br /&gt;
&amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH then loads the key into the agent on first use (prompts&lt;br /&gt;
for the passphrase once) and reuses it for the rest of the&lt;br /&gt;
session.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All tunables are class-side on &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Default !! What it controls&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 min) || How long a pooled&lt;br /&gt;
connection sits idle before the next access proactively closes +&lt;br /&gt;
reopens it.  Just under typical sshd&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; so we recycle&lt;br /&gt;
before the server TCP-RESETs us.  Pass &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; to restore&lt;br /&gt;
the default.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Max age (s) of a&lt;br /&gt;
cached STAT before &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; refetches.  Parent&lt;br /&gt;
listDir always re-stamps fresh attrs onto children, so navigating&lt;br /&gt;
an open directory does not pay the TTL.  Set to &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to&lt;br /&gt;
disable caching.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (action) || Tears down every&lt;br /&gt;
pooled connection.  Useful after a known-bad network event, before&lt;br /&gt;
a deliberate identity swap, or as part of a clean image shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
Open &amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; from the Launcher&#039;s &amp;quot;Status&amp;quot;&lt;br /&gt;
sub-menu.  Per-host SFTP mutex appears as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;; the pool-wide mutex as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Right-click a row:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — dumps the last-owner&#039;s walkback plus each waiter&#039;s, formatted as plain text.  Use when a process is wedged in &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; inside&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; and you need to see which SFTP&lt;br /&gt;
request it is on.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — the whole table, for an email-this-to-someone diagnosis.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS over the wait-for graph, reports cycles.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
The SSH stack logs interesting events:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; on auto-reconnect after a dead connection.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when a pool entry is idle-evicted.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when an SSH key file cannot be parsed (e.g. legacy PEM, encrypted-without-agent) — the file is skipped, others tried.&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SFTP v3 only.&#039;&#039;&#039;  No SETSTAT (no remote chmod / chown / utime), no SSH_FXP_READLINK exposed (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; always&lt;br /&gt;
&amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; returns the regular&lt;br /&gt;
stat info).  Several SFTPv5+ niceties are nevertheless picked up&lt;br /&gt;
via OpenSSH SSH_FXP_EXTENDED requests — see&lt;br /&gt;
[[#OpenSSH SFTP extensions]] below.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation.&#039;&#039;&#039;  Two concurrent operations on the same host queue through the host mutex.  See [[#Future work]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; fallback has a TOCTOU window.&#039;&#039;&#039;  On servers that advertise &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; (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.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; is heuristic.&#039;&#039;&#039;  Always returns &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (the accurate answer would cost three round-trips per directory icon, which made the original tree expansion unbearably slow).&lt;br /&gt;
&lt;br /&gt;
== Implementation details ==&lt;br /&gt;
&lt;br /&gt;
For readers wanting the architecture.  Five classes, top-down:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename subclass; the public&lt;br /&gt;
API.  Maps &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt; URLs to remote files; exposes&lt;br /&gt;
&amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3 protocol&lt;br /&gt;
(request/response codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Driven by SftpFilename.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH channel multiplexer&lt;br /&gt;
(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST).  One logical&lt;br /&gt;
session per Channel instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-level SSH client: opens the&lt;br /&gt;
transport, runs KEX, host-key check, userauth, then dispenses&lt;br /&gt;
Channels.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Wire layer.  Banner + KEXINIT&lt;br /&gt;
exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq,&lt;br /&gt;
heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH SFTP extensions ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal.&lt;br /&gt;
OpenSSH ships an open-ended extension mechanism: the server lists&lt;br /&gt;
extension names it understands in its &amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;&lt;br /&gt;
reply, and the client invokes them via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt; packets carrying the extension&lt;br /&gt;
name as the first string.  Each extension is feature-detected via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;; callers fall&lt;br /&gt;
back when the server doesn&#039;t advertise it.&lt;br /&gt;
&lt;br /&gt;
The stack uses four of the OpenSSH extensions today:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomic rename-with-overwrite.  Picked up automatically by&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt;; the delete-then-rename&lt;br /&gt;
fallback only fires on servers that lack it.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — create a POSIX hard link. Exposed as &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-shape filesystem stats.  Exposed as&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;; the result is&lt;br /&gt;
shape-compatible with &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;&lt;br /&gt;
so callers can treat local and remote uniformly.  Drives the&lt;br /&gt;
&#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; menu entry described at the&lt;br /&gt;
top of this page.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — flush server-side write buffer to disk on an open handle.  Available on the low-level&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt;; not yet plumbed&lt;br /&gt;
into a Filename-level &amp;quot;durable write&amp;quot; API.&lt;br /&gt;
&lt;br /&gt;
The remaining OpenSSH extensions&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
are recognised in the advertised-extensions list but not wrapped at&lt;br /&gt;
Filename level — there&#039;s no Filename-side caller for them yet.&lt;br /&gt;
&lt;br /&gt;
=== Connection pooling ===&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt; instance pointing at the same&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; triple shares one&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; plus one &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Pool is class-side, guarded by a single&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy bring-up&#039;&#039;&#039; — TCP + KEX + userauth + SFTP INIT happens on the first SFTP operation, not on &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation&#039;&#039;&#039; — SFTP requests on a given host are serialised through a &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; named&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; (visible in&lt;br /&gt;
SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle eviction&#039;&#039;&#039; — unused for longer than&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt;, the entry is proactively&lt;br /&gt;
closed + reopened on the next access.&lt;br /&gt;
* &#039;&#039;&#039;Auto-reconnect&#039;&#039;&#039; — a transport-level failure (broken pipe, EOF, MNU on nil socket) evicts the dead pool entry, opens a fresh client, retries the request &#039;&#039;&#039;once&#039;&#039;&#039;.  Application-level SFTP STATUS errors propagate immediately.&lt;br /&gt;
&lt;br /&gt;
== Future work ==&lt;br /&gt;
&lt;br /&gt;
Tracked but not yet implemented:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-channel parallelism per host&#039;&#039;&#039; — 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.&lt;br /&gt;
* &#039;&#039;&#039;Accurate &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (first batch only) + CLOSE — three RTTs per probe; needs SftpClient to pipeline requests before this pays off.&lt;br /&gt;
* &#039;&#039;&#039;SFTP v5/v6 negotiation&#039;&#039;&#039; for extended attrs and FTP-style canonicalisation.  (Atomic-overwrite rename is already handled via the OpenSSH &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; extension; see [[#OpenSSH SFTP extensions]].)&lt;br /&gt;
&lt;br /&gt;
= Command Shell =&lt;br /&gt;
&lt;br /&gt;
Local command shell on this expecco machine.  Typical applications:&lt;br /&gt;
local command-line, running a local helper tool, bridging a&lt;br /&gt;
remote workflow to a local utility.&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No credentials, no network — runs as the expecco process&#039;s own&lt;br /&gt;
user.  Output streams to expecco&#039;s log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warning]] &#039;&#039;&#039;Telnet is a legacy protocol&lt;br /&gt;
with no encryption.&#039;&#039;&#039; Passwords are transmitted in plain text on&lt;br /&gt;
the wire; anyone on the network path can read them.  Use Telnet&lt;br /&gt;
ONLY when the target device has no other option (typically: old&lt;br /&gt;
industrial controllers, lab instruments, embedded measurement&lt;br /&gt;
equipment without an SSH stack).  For everything else use&lt;br /&gt;
[[#SSH and SFTP]].&lt;br /&gt;
&lt;br /&gt;
The expecco plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (internal demo)&lt;br /&gt;
&lt;br /&gt;
The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream&lt;br /&gt;
over TCP, with in-band control sequences for terminal options.&lt;br /&gt;
A connection is established to a target host:port; after optional&lt;br /&gt;
in-band login, both sides can send data.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent forwarding, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of this stack.&lt;br /&gt;
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack for its HTTPS transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Network]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31301</id>
		<title>Remote Access</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31301"/>
		<updated>2026-05-26T10:33:00Z</updated>

		<summary type="html">&lt;p&gt;Sv: Fold bullet continuation lines onto a single physical line so MediaWiki keeps them inside &amp;lt;li&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access|label=Deutsch}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere Dateiübertragung über einen SSH-2-Tunnel.  Reine Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen Maschine.  Für die Anbindung lokaler Werkzeuge und für die lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff (entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung, ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
&amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet vier Aktionen — die&lt;br /&gt;
drei SSH-spezifischen sind nur bei geladener SSH-Bibliothek&lt;br /&gt;
sichtbar:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den Schlüsselerzeugungs-Dialog, siehe [[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP auf ein entferntes Dateisystem.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — zeigt Größe, freien Platz und Belegung des Dateisystems, das das aktuell angezeigte Verzeichnis enthält. Funktioniert einheitlich für lokale und SFTP-Pfade; bei SFTP setzt der Aufruf voraus, daß der Server die Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; ankündigt (jedes moderne OpenSSH&lt;br /&gt;
tut das).  Größen werden in IEC-Binäreinheiten ausgegeben (MiB,&lt;br /&gt;
GiB, TiB) — gewählt wird die größte Einheit, die einen Wert ≥ 1&lt;br /&gt;
liefert, damit ein TB-großes Volume als &#039;&#039;X TiB&#039;&#039; statt&lt;br /&gt;
&#039;&#039;10240 GiB&#039;&#039; erscheint.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary (PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet (Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf &#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs- variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;, &#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback des letzten Eigners samt aller Waiter als Text in die Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
&amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes chmod / chown / utime), kein SSH_FXP_READLINK exponiert (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  Einige&lt;br /&gt;
SFTPv5+-Annehmlichkeiten werden dennoch über OpenSSH-spezifische&lt;br /&gt;
SSH_FXP_EXTENDED-Aufrufe nutzbar — siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]] weiter unten.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt;-Fallback hat ein TOCTOU-Fenster.&#039;&#039;&#039; Bei Servern, die &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; ankündigen (jedes moderne OpenSSH tut das), ist das Überschreiben atomar. Beim seltenen Server, der das nicht tut, wird auf Delete-dann-Rename ausgewichen und ein anderer Prozess kann sich dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039; Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH-SFTP-Erweiterungen ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (Entwurf draft-ietf-secsh-filexfer-02) ist bewusst&lt;br /&gt;
minimal gehalten.  OpenSSH bringt einen offenen&lt;br /&gt;
Erweiterungsmechanismus mit: der Server listet im&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;-Reply die Erweiterungsnamen auf, die&lt;br /&gt;
er versteht, und der Client ruft sie über&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt;-Pakete mit dem&lt;br /&gt;
Erweiterungsnamen als erstem String auf.  Jede Erweiterung wird&lt;br /&gt;
über &amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;&lt;br /&gt;
feature-detektiert; Aufrufer fallen zurück, wenn der Server sie&lt;br /&gt;
nicht ankündigt.&lt;br /&gt;
&lt;br /&gt;
Der Stack nutzt heute vier OpenSSH-Erweiterungen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomares rename-mit-Überschreiben.  Wird automatisch von&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt; aufgegriffen; die&lt;br /&gt;
Delete-dann-Rename-Fallback-Variante kommt nur bei Servern zum&lt;br /&gt;
Einsatz, die die Erweiterung nicht haben.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — Erzeugt einen POSIX-Hardlink. Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX-&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-typische Dateisystem-Statistik.&lt;br /&gt;
Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
das Ergebnis ist form-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;, sodass Aufrufer&lt;br /&gt;
lokale und entfernte Pfade einheitlich behandeln können.&lt;br /&gt;
Treibt den Menü-Eintrag &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; an,&lt;br /&gt;
der am Anfang dieser Seite beschrieben ist.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — schreibt den serverseitigen Schreibpuffer eines geöffneten Handles auf Platte.  Liegt als&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt; bereit; noch nicht&lt;br /&gt;
in eine &amp;quot;Durable-Write&amp;quot;-API auf Filename-Ebene eingebunden.&lt;br /&gt;
&lt;br /&gt;
Die verbleibenden OpenSSH-Erweiterungen&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
werden in der angekündigten Liste erkannt, aber nicht auf&lt;br /&gt;
Filename-Ebene gekapselt — es gibt dafür noch keinen&lt;br /&gt;
Filename-seitigen Aufrufer.&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene (Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den Pool-Eintrag, öffnet einen frischen Client und wiederholt die Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet eine TCP- plus eine SFTP-Verbindung pro Host, dass N gleichzeitige Anfragen serialisieren.  Pipelining über mehrere SshClients im Pool (bevorzugt) oder ein transport-seitiger Reader-Prozess, der eingehende Pakete in Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel erlauben, weiter aufzulisten, während das Inhalts-Panel eine große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR + READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für erweiterte Attribute und FTP-artige Kanonisierung.  (Atomares Überschreibungs-rename ist bereits über die OpenSSH-Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; abgedeckt; siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]].)&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY, Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31300</id>
		<title>Remote Access/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31300"/>
		<updated>2026-05-26T10:29:04Z</updated>

		<summary type="html">&lt;p&gt;Sv: Remove 2-space continuation-line indent (was triggering MediaWiki &amp;lt;pre&amp;gt;)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access/en|label=English}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Remote access&#039;&#039;&#039; is the ability to drive a remote computer or&lt;br /&gt;
network from this expecco image — opening shells, running commands,&lt;br /&gt;
moving files, or driving a test target.  Three protocol families are&lt;br /&gt;
supported, listed in current-recommended order:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH and SFTP&#039;&#039;&#039; (recommended) — encrypted shell + secure&lt;br /&gt;
file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation&lt;br /&gt;
in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on&lt;br /&gt;
OpenSSL or libssh.  Use this for anything that touches credentials&lt;br /&gt;
or sensitive payloads.&lt;br /&gt;
* &#039;&#039;&#039;Local Command Shell&#039;&#039;&#039; — fork + exec on the local machine.&lt;br /&gt;
Used for local-tool integration and for the local end of a remote&lt;br /&gt;
workflow that bridges via another protocol.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (legacy) — plain-text terminal session.  No&lt;br /&gt;
encryption, passwords on the wire in clear.  Use only when the&lt;br /&gt;
target hardware has no other option.&lt;br /&gt;
&lt;br /&gt;
= SSH and SFTP =&lt;br /&gt;
&lt;br /&gt;
The SSH stack covers the full SSH-2 protocol (RFC 4251–4254,&lt;br /&gt;
RFC 5656, RFC 8709, RFC 8731) plus OpenSSH&#039;s chacha20-poly1305&lt;br /&gt;
transport cipher and the SFTP v3 file-transfer subsystem&lt;br /&gt;
(draft-ietf-secsh-filexfer-02).  Two layers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatic SSH access (remote&lt;br /&gt;
&amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY shell, agent forwarding, ProxyJump bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — a &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;&lt;br /&gt;
subclass that lets the rest of ST/X treat a remote SFTP path the&lt;br /&gt;
same way it treats a local file.&lt;br /&gt;
&lt;br /&gt;
The rest of this section is organised user-task-first: what the user&lt;br /&gt;
sees and does, the expecco-library hooks below that, then the&lt;br /&gt;
implementation detail at the end for the curious.&lt;br /&gt;
&lt;br /&gt;
== From the FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Open the location dropdown and paste an &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt; URL.&lt;br /&gt;
The browser tab populates as if it were a local path.  Tree&lt;br /&gt;
expansion, column sort (name / size / mtime), preview, and&lt;br /&gt;
double-click-to-open-in-editor all behave normally.  The first&lt;br /&gt;
click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent&lt;br /&gt;
clicks reuse the pooled connection.&lt;br /&gt;
&lt;br /&gt;
URL syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User defaults to the local login name, port to 22, path to&lt;br /&gt;
&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Tools menu offers four browser actions, three of them gated on&lt;br /&gt;
the SSH library being loaded:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — opens the same key-generation&lt;br /&gt;
dialog described under [[#Generating an SSH key pair]] below.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — opens an interactive VT100 terminal to a&lt;br /&gt;
remote host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — points this browser tab at a remote&lt;br /&gt;
filesystem via SFTP.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — shows size, free space and usage of&lt;br /&gt;
the filesystem holding the currently displayed directory.  Works&lt;br /&gt;
uniformly for local paths and SFTP paths; for SFTP it requires&lt;br /&gt;
the server to advertise the &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt;&lt;br /&gt;
extension (every modern OpenSSH does).  Sizes are reported in&lt;br /&gt;
IEC binary units (MiB, GiB, TiB) — the largest unit yielding a&lt;br /&gt;
value ≥ 1 is chosen, so a TB-scale volume reads as &#039;&#039;X TiB&#039;&#039;&lt;br /&gt;
rather than &#039;&#039;10240 GiB&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== From expecco actions ==&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) exposes the following test&lt;br /&gt;
actions to the expecco action palette:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — opens an SSH session&lt;br /&gt;
via the platform&#039;s &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; binary (PuTTY&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; on Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — same&lt;br /&gt;
but with explicit public-key authentication.&lt;br /&gt;
&lt;br /&gt;
To run these you need a configured keypair (private key on this&lt;br /&gt;
machine, public key in the remote host&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;).  Generate one via the dialog&lt;br /&gt;
below or via &amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The plugin also adds a settings page at &#039;&#039;&#039;Extras → Settings →&lt;br /&gt;
Plugins → Remote Access — SSH Keys&#039;&#039;&#039; carrying a single&lt;br /&gt;
&#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; button that opens the same dialog.&lt;br /&gt;
&lt;br /&gt;
== Generating an SSH key pair ==&lt;br /&gt;
&lt;br /&gt;
=== The dialog (FileBrowserV2 / settings page) ===&lt;br /&gt;
&lt;br /&gt;
The dialog asks for all parameters in one form:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — embedded in the generated key (defaults to&lt;br /&gt;
&amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — writes &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt;&lt;br /&gt;
(or wherever) plus a matching &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt; companion.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — writes the file and&lt;br /&gt;
also hands the key to the running ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — key lives in agent memory only;&lt;br /&gt;
gone on agent restart.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — full path; disabled in agent-only mode.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — empty leaves the on-disk file&lt;br /&gt;
unencrypted (agent-only mode ignores the passphrase, since the&lt;br /&gt;
OpenSSH agent wire protocol carries only the decrypted key).&lt;br /&gt;
&lt;br /&gt;
On &#039;&#039;&#039;Generate&#039;&#039;&#039;, the public-key line (the same&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt; string ssh-keygen&lt;br /&gt;
emits) is copied to the system clipboard for pasting into the&lt;br /&gt;
remote host&#039;s &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From a workspace ===&lt;br /&gt;
&lt;br /&gt;
For headless deployments, sandboxed builds, or scripts,&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; exposes a pure-Smalltalk key generator&lt;br /&gt;
that produces output bit-compatible with&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Save passphrase-encrypted to disk&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ AND load into the running agent&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Print the public-key line to paste into authorized_keys&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keys generated this way are interoperable with OpenSSH&#039;s own&lt;br /&gt;
tooling (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; re-derives the public&lt;br /&gt;
key, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; changes the passphrase,&lt;br /&gt;
etc.).&lt;br /&gt;
&lt;br /&gt;
=== Using the shell tools instead ===&lt;br /&gt;
&lt;br /&gt;
The traditional path also works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparing ssh-agent ==&lt;br /&gt;
&lt;br /&gt;
The agent path is strongly preferred over reading raw keyfiles: it&lt;br /&gt;
keeps encrypted private keys unlocked once per session, and handles&lt;br /&gt;
identities (hardware-token-backed keys, KeePassXC entries) that&lt;br /&gt;
ST/X should never see directly.&lt;br /&gt;
&lt;br /&gt;
ST/X picks the agent path automatically when&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is set in the process environment&lt;br /&gt;
&#039;&#039;&#039;at the time stx is launched&#039;&#039;&#039;.  Setting it later from a&lt;br /&gt;
workspace does not help.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Most desktop distributions launch an agent automatically as part of&lt;br /&gt;
the session (gnome-keyring on GNOME, ssh-agent.service on systemd,&lt;br /&gt;
KWallet on KDE).  Verify in a terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar&lt;br /&gt;
ssh-add -l             # lists loaded identities&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If no agent runs at all, add this snippet to your shell rc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc or ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X must be launched from a shell that has seen this rc — a&lt;br /&gt;
desktop launcher started from the file manager does NOT inherit&lt;br /&gt;
the variable.  Wrap the stx start command in a small script under&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt; that sources the rc first.&lt;br /&gt;
&lt;br /&gt;
The Remote Access settings page (&#039;&#039;&#039;Extras → Settings → Plugins&lt;br /&gt;
→ Remote Access — SSH Keys&#039;&#039;&#039;) shows whether the running image&lt;br /&gt;
sees an agent.&lt;br /&gt;
&lt;br /&gt;
==== Permanent setup via systemd ====&lt;br /&gt;
&lt;br /&gt;
For a truly cross-session agent (survives desktop logouts, comes&lt;br /&gt;
up automatically at next login), enable the per-user systemd&lt;br /&gt;
unit shipped with most distros&#039; &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
package:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then point &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the user-service socket&lt;br /&gt;
in your shell rc (this replaces the &amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;&lt;br /&gt;
snippet above):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
To skip the manual &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; step, let OpenSSH load&lt;br /&gt;
keys into the agent automatically the first time they are needed.&lt;br /&gt;
Add to &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first SSH connection then prompts for the key passphrase&lt;br /&gt;
once and hands the unlocked key to the agent; subsequent&lt;br /&gt;
connections use the cached identity without prompting.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ ships native OpenSSH including an agent service.&lt;br /&gt;
One-time setup:&lt;br /&gt;
&lt;br /&gt;
# Open &#039;&#039;&#039;Services&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) as Administrator.&lt;br /&gt;
# Find &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039;, set Startup Type to&lt;br /&gt;
&#039;&#039;&#039;Automatic&#039;&#039;&#039;, click &#039;&#039;&#039;Start&#039;&#039;&#039;.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Windows OpenSSH agent listens on a named pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), not a Unix socket.  ST/X&lt;br /&gt;
supports both transports, but Windows ssh-add does &#039;&#039;&#039;not&#039;&#039;&#039; set&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; for you.  Add it manually:&lt;br /&gt;
&lt;br /&gt;
# Press {{Key|Win}} → type &amp;quot;environment&amp;quot; → &amp;quot;Edit the system environment variables&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Environment Variables&#039;&#039;&#039; → under &#039;&#039;&#039;User variables&#039;&#039;&#039;, &#039;&#039;&#039;New&#039;&#039;&#039;.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Value: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Log out and back in (or restart stx) so the new env propagates.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell quick-setup ====&lt;br /&gt;
&lt;br /&gt;
The same setup from an &#039;&#039;&#039;elevated&#039;&#039;&#039; PowerShell prompt, for&lt;br /&gt;
scripts or unattended provisioning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Start the agent now AND on every reboot (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# Persist SSH_AUTH_SOCK for the user (survives reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Load a key (prompts for the passphrase if the file is encrypted).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a one-shot agent start without making it persistent (e.g.&lt;br /&gt;
single-session test), drop the &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt; line and&lt;br /&gt;
just run &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt;.  The env-var line&lt;br /&gt;
can also be omitted if &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is only needed&lt;br /&gt;
in the current shell — use &amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
instead for that session-local form.&lt;br /&gt;
&lt;br /&gt;
On stripped-down Windows installs the ssh-agent service may not&lt;br /&gt;
be present.  Add it once via &#039;&#039;&#039;Settings → Apps → Optional&lt;br /&gt;
features → OpenSSH Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative agents:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — uses its own protocol; NOT supported by&lt;br /&gt;
ST/X&#039;s SSH::Agent.  Migrate the keys to OpenSSH.&lt;br /&gt;
* &#039;&#039;&#039;Git for Windows ssh-agent&#039;&#039;&#039; — works; point&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the socket it publishes.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — a ST/X inside WSL sees WSL&#039;s Linux agent normally;&lt;br /&gt;
a ST/X on the Windows side does not.  Bridging needs a helper&lt;br /&gt;
like &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify in the Remote Access settings page&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
the agent indicator there reports whether the running image sees&lt;br /&gt;
the agent.&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
Windows OpenSSH does &#039;&#039;&#039;not&#039;&#039;&#039; persist agent-loaded keys across&lt;br /&gt;
agent restarts.  To avoid running &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; manually&lt;br /&gt;
after each reboot, add the same lazy-load configuration to&lt;br /&gt;
&amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH then loads the key into the agent on first use (prompts&lt;br /&gt;
for the passphrase once) and reuses it for the rest of the&lt;br /&gt;
session.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All tunables are class-side on &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Default !! What it controls&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 min) || How long a pooled&lt;br /&gt;
connection sits idle before the next access proactively closes +&lt;br /&gt;
reopens it.  Just under typical sshd&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; so we recycle&lt;br /&gt;
before the server TCP-RESETs us.  Pass &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; to restore&lt;br /&gt;
the default.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Max age (s) of a&lt;br /&gt;
cached STAT before &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; refetches.  Parent&lt;br /&gt;
listDir always re-stamps fresh attrs onto children, so navigating&lt;br /&gt;
an open directory does not pay the TTL.  Set to &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to&lt;br /&gt;
disable caching.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (action) || Tears down every&lt;br /&gt;
pooled connection.  Useful after a known-bad network event, before&lt;br /&gt;
a deliberate identity swap, or as part of a clean image shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
Open &amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; from the Launcher&#039;s &amp;quot;Status&amp;quot;&lt;br /&gt;
sub-menu.  Per-host SFTP mutex appears as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;; the pool-wide mutex as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Right-click a row:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — dumps the last-owner&#039;s&lt;br /&gt;
walkback plus each waiter&#039;s, formatted as plain text.  Use when&lt;br /&gt;
a process is wedged in &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; inside&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; and you need to see which SFTP&lt;br /&gt;
request it is on.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — the whole table, for an&lt;br /&gt;
email-this-to-someone diagnosis.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS over the wait-for graph, reports&lt;br /&gt;
cycles.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
The SSH stack logs interesting events:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; on auto-reconnect after a dead connection.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when a pool entry is idle-evicted.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when an SSH key file cannot be parsed&lt;br /&gt;
(e.g. legacy PEM, encrypted-without-agent) — the file is skipped,&lt;br /&gt;
others tried.&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SFTP v3 only.&#039;&#039;&#039;  No SETSTAT (no remote chmod / chown / utime),&lt;br /&gt;
no SSH_FXP_READLINK exposed (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; always&lt;br /&gt;
&amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; returns the regular&lt;br /&gt;
stat info).  Several SFTPv5+ niceties are nevertheless picked up&lt;br /&gt;
via OpenSSH SSH_FXP_EXTENDED requests — see&lt;br /&gt;
[[#OpenSSH SFTP extensions]] below.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation.&#039;&#039;&#039;  Two concurrent operations on the&lt;br /&gt;
same host queue through the host mutex.  See [[#Future work]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; fallback has a TOCTOU window.&#039;&#039;&#039;  On&lt;br /&gt;
servers that advertise &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; (every&lt;br /&gt;
modern OpenSSH does), overwrite is atomic; on the rare server&lt;br /&gt;
that does not, the receiver is emulated as delete-then-rename&lt;br /&gt;
and another process can race in between.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; is heuristic.&#039;&#039;&#039;  Always&lt;br /&gt;
returns &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (the accurate answer would cost&lt;br /&gt;
three round-trips per directory icon, which made the original tree&lt;br /&gt;
expansion unbearably slow).&lt;br /&gt;
&lt;br /&gt;
== Implementation details ==&lt;br /&gt;
&lt;br /&gt;
For readers wanting the architecture.  Five classes, top-down:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename subclass; the public&lt;br /&gt;
API.  Maps &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt; URLs to remote files; exposes&lt;br /&gt;
&amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3 protocol&lt;br /&gt;
(request/response codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Driven by SftpFilename.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH channel multiplexer&lt;br /&gt;
(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST).  One logical&lt;br /&gt;
session per Channel instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-level SSH client: opens the&lt;br /&gt;
transport, runs KEX, host-key check, userauth, then dispenses&lt;br /&gt;
Channels.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Wire layer.  Banner + KEXINIT&lt;br /&gt;
exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq,&lt;br /&gt;
heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH SFTP extensions ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal.&lt;br /&gt;
OpenSSH ships an open-ended extension mechanism: the server lists&lt;br /&gt;
extension names it understands in its &amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;&lt;br /&gt;
reply, and the client invokes them via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt; packets carrying the extension&lt;br /&gt;
name as the first string.  Each extension is feature-detected via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;; callers fall&lt;br /&gt;
back when the server doesn&#039;t advertise it.&lt;br /&gt;
&lt;br /&gt;
The stack uses four of the OpenSSH extensions today:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomic&lt;br /&gt;
rename-with-overwrite.  Picked up automatically by&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt;; the delete-then-rename&lt;br /&gt;
fallback only fires on servers that lack it.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — create a POSIX hard link.&lt;br /&gt;
Exposed as &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-shape filesystem stats.  Exposed as&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;; the result is&lt;br /&gt;
shape-compatible with &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;&lt;br /&gt;
so callers can treat local and remote uniformly.  Drives the&lt;br /&gt;
&#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; menu entry described at the&lt;br /&gt;
top of this page.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — flush server-side write buffer&lt;br /&gt;
to disk on an open handle.  Available on the low-level&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt;; not yet plumbed&lt;br /&gt;
into a Filename-level &amp;quot;durable write&amp;quot; API.&lt;br /&gt;
&lt;br /&gt;
The remaining OpenSSH extensions&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
are recognised in the advertised-extensions list but not wrapped at&lt;br /&gt;
Filename level — there&#039;s no Filename-side caller for them yet.&lt;br /&gt;
&lt;br /&gt;
=== Connection pooling ===&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt; instance pointing at the same&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; triple shares one&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; plus one &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Pool is class-side, guarded by a single&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy bring-up&#039;&#039;&#039; — TCP + KEX + userauth + SFTP INIT happens&lt;br /&gt;
on the first SFTP operation, not on &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation&#039;&#039;&#039; — SFTP requests on a given host&lt;br /&gt;
are serialised through a &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; named&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; (visible in&lt;br /&gt;
SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle eviction&#039;&#039;&#039; — unused for longer than&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt;, the entry is proactively&lt;br /&gt;
closed + reopened on the next access.&lt;br /&gt;
* &#039;&#039;&#039;Auto-reconnect&#039;&#039;&#039; — a transport-level failure (broken pipe,&lt;br /&gt;
EOF, MNU on nil socket) evicts the dead pool entry, opens a&lt;br /&gt;
fresh client, retries the request &#039;&#039;&#039;once&#039;&#039;&#039;.  Application-level&lt;br /&gt;
SFTP STATUS errors propagate immediately.&lt;br /&gt;
&lt;br /&gt;
== Future work ==&lt;br /&gt;
&lt;br /&gt;
Tracked but not yet implemented:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-channel parallelism per host&#039;&#039;&#039; — today one TCP +&lt;br /&gt;
one SFTP channel per host means N concurrent requests&lt;br /&gt;
serialise.  Pipelining over multiple SshClients in the pool&lt;br /&gt;
(preferred), or a transport-level reader process demultiplexing&lt;br /&gt;
to per-channel inboxes, would let the tree pane keep listing&lt;br /&gt;
while the content pane reads a large file.&lt;br /&gt;
* &#039;&#039;&#039;Accurate &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR&lt;br /&gt;
+ READ_DIR (first batch only) + CLOSE — three RTTs per probe;&lt;br /&gt;
needs SftpClient to pipeline requests before this pays off.&lt;br /&gt;
* &#039;&#039;&#039;SFTP v5/v6 negotiation&#039;&#039;&#039; for extended attrs and FTP-style&lt;br /&gt;
canonicalisation.  (Atomic-overwrite rename is already handled&lt;br /&gt;
via the OpenSSH &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; extension;&lt;br /&gt;
see [[#OpenSSH SFTP extensions]].)&lt;br /&gt;
&lt;br /&gt;
= Command Shell =&lt;br /&gt;
&lt;br /&gt;
Local command shell on this expecco machine.  Typical applications:&lt;br /&gt;
local command-line, running a local helper tool, bridging a&lt;br /&gt;
remote workflow to a local utility.&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No credentials, no network — runs as the expecco process&#039;s own&lt;br /&gt;
user.  Output streams to expecco&#039;s log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warning]] &#039;&#039;&#039;Telnet is a legacy protocol&lt;br /&gt;
with no encryption.&#039;&#039;&#039; Passwords are transmitted in plain text on&lt;br /&gt;
the wire; anyone on the network path can read them.  Use Telnet&lt;br /&gt;
ONLY when the target device has no other option (typically: old&lt;br /&gt;
industrial controllers, lab instruments, embedded measurement&lt;br /&gt;
equipment without an SSH stack).  For everything else use&lt;br /&gt;
[[#SSH and SFTP]].&lt;br /&gt;
&lt;br /&gt;
The expecco plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (internal demo)&lt;br /&gt;
&lt;br /&gt;
The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream&lt;br /&gt;
over TCP, with in-band control sequences for terminal options.&lt;br /&gt;
A connection is established to a target host:port; after optional&lt;br /&gt;
in-band login, both sides can send data.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent&lt;br /&gt;
forwarding, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of&lt;br /&gt;
this stack.&lt;br /&gt;
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack&lt;br /&gt;
for its HTTPS transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Network]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31299</id>
		<title>Remote Access</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31299"/>
		<updated>2026-05-26T10:29:02Z</updated>

		<summary type="html">&lt;p&gt;Sv: Remove 2-space continuation-line indent (was triggering MediaWiki &amp;lt;pre&amp;gt;)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access|label=Deutsch}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere&lt;br /&gt;
Dateiübertragung über einen SSH-2-Tunnel.  Reine&lt;br /&gt;
Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;;&lt;br /&gt;
keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles&lt;br /&gt;
mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen&lt;br /&gt;
Maschine.  Für die Anbindung lokaler Werkzeuge und für die&lt;br /&gt;
lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine&lt;br /&gt;
Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur&lt;br /&gt;
einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff&lt;br /&gt;
(entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung,&lt;br /&gt;
ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
&amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet vier Aktionen — die&lt;br /&gt;
drei SSH-spezifischen sind nur bei geladener SSH-Bibliothek&lt;br /&gt;
sichtbar:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den&lt;br /&gt;
Schlüsselerzeugungs-Dialog, siehe&lt;br /&gt;
[[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal&lt;br /&gt;
zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP&lt;br /&gt;
auf ein entferntes Dateisystem.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — zeigt Größe, freien Platz und Belegung&lt;br /&gt;
des Dateisystems, das das aktuell angezeigte Verzeichnis enthält.&lt;br /&gt;
Funktioniert einheitlich für lokale und SFTP-Pfade; bei SFTP&lt;br /&gt;
setzt der Aufruf voraus, daß der Server die Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; ankündigt (jedes moderne OpenSSH&lt;br /&gt;
tut das).  Größen werden in IEC-Binäreinheiten ausgegeben (MiB,&lt;br /&gt;
GiB, TiB) — gewählt wird die größte Einheit, die einen Wert ≥ 1&lt;br /&gt;
liefert, damit ein TB-großes Volume als &#039;&#039;X TiB&#039;&#039; statt&lt;br /&gt;
&#039;&#039;10240 GiB&#039;&#039; erscheint.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine&lt;br /&gt;
SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary&lt;br /&gt;
(PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; —&lt;br /&gt;
dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet&lt;br /&gt;
(Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei&lt;br /&gt;
UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im&lt;br /&gt;
Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im&lt;br /&gt;
Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei&lt;br /&gt;
unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da&lt;br /&gt;
das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten&lt;br /&gt;
Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf&lt;br /&gt;
&#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs-&lt;br /&gt;
variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;,&lt;br /&gt;
&#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue&lt;br /&gt;
Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen&lt;br /&gt;
Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine&lt;br /&gt;
Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist&lt;br /&gt;
möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback&lt;br /&gt;
des letzten Eigners samt aller Waiter als Text in die&lt;br /&gt;
Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
&amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
&amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine&lt;br /&gt;
E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet&lt;br /&gt;
Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter&lt;br /&gt;
Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht&lt;br /&gt;
geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes&lt;br /&gt;
chmod / chown / utime), kein SSH_FXP_READLINK exponiert&lt;br /&gt;
(&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  Einige&lt;br /&gt;
SFTPv5+-Annehmlichkeiten werden dennoch über OpenSSH-spezifische&lt;br /&gt;
SSH_FXP_EXTENDED-Aufrufe nutzbar — siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]] weiter unten.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen&lt;br /&gt;
am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt;-Fallback hat ein TOCTOU-Fenster.&#039;&#039;&#039;&lt;br /&gt;
Bei Servern, die &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; ankündigen&lt;br /&gt;
(jedes moderne OpenSSH tut das), ist das Überschreiben atomar.&lt;br /&gt;
Beim seltenen Server, der das nicht tut, wird auf&lt;br /&gt;
Delete-dann-Rename ausgewichen und ein anderer Prozess kann sich&lt;br /&gt;
dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039;&lt;br /&gt;
Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort&lt;br /&gt;
würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das&lt;br /&gt;
ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH-SFTP-Erweiterungen ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (Entwurf draft-ietf-secsh-filexfer-02) ist bewusst&lt;br /&gt;
minimal gehalten.  OpenSSH bringt einen offenen&lt;br /&gt;
Erweiterungsmechanismus mit: der Server listet im&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;-Reply die Erweiterungsnamen auf, die&lt;br /&gt;
er versteht, und der Client ruft sie über&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt;-Pakete mit dem&lt;br /&gt;
Erweiterungsnamen als erstem String auf.  Jede Erweiterung wird&lt;br /&gt;
über &amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;&lt;br /&gt;
feature-detektiert; Aufrufer fallen zurück, wenn der Server sie&lt;br /&gt;
nicht ankündigt.&lt;br /&gt;
&lt;br /&gt;
Der Stack nutzt heute vier OpenSSH-Erweiterungen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomares&lt;br /&gt;
rename-mit-Überschreiben.  Wird automatisch von&lt;br /&gt;
&amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt; aufgegriffen; die&lt;br /&gt;
Delete-dann-Rename-Fallback-Variante kommt nur bei Servern zum&lt;br /&gt;
Einsatz, die die Erweiterung nicht haben.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — Erzeugt einen POSIX-Hardlink.&lt;br /&gt;
Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX-&lt;br /&gt;
&amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-typische Dateisystem-Statistik.&lt;br /&gt;
Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
das Ergebnis ist form-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;, sodass Aufrufer&lt;br /&gt;
lokale und entfernte Pfade einheitlich behandeln können.&lt;br /&gt;
Treibt den Menü-Eintrag &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; an,&lt;br /&gt;
der am Anfang dieser Seite beschrieben ist.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — schreibt den&lt;br /&gt;
serverseitigen Schreibpuffer eines geöffneten Handles auf&lt;br /&gt;
Platte.  Liegt als&lt;br /&gt;
&amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt; bereit; noch nicht&lt;br /&gt;
in eine &amp;quot;Durable-Write&amp;quot;-API auf Filename-Ebene eingebunden.&lt;br /&gt;
&lt;br /&gt;
Die verbleibenden OpenSSH-Erweiterungen&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
werden in der angekündigten Liste erkannt, aber nicht auf&lt;br /&gt;
Filename-Ebene gekapselt — es gibt dafür noch keinen&lt;br /&gt;
Filename-seitigen Aufrufer.&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen&lt;br /&gt;
erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen&lt;br /&gt;
bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt;&lt;br /&gt;
mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
&amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene&lt;br /&gt;
(Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den&lt;br /&gt;
Pool-Eintrag, öffnet einen frischen Client und wiederholt die&lt;br /&gt;
Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus&lt;br /&gt;
SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet&lt;br /&gt;
eine TCP- plus eine SFTP-Verbindung pro Host, dass N&lt;br /&gt;
gleichzeitige Anfragen serialisieren.  Pipelining über mehrere&lt;br /&gt;
SshClients im Pool (bevorzugt) oder ein transport-seitiger&lt;br /&gt;
Reader-Prozess, der eingehende Pakete in&lt;br /&gt;
Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel&lt;br /&gt;
erlauben, weiter aufzulisten, während das Inhalts-Panel eine&lt;br /&gt;
große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR&lt;br /&gt;
+ READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro&lt;br /&gt;
Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen&lt;br /&gt;
kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für erweiterte Attribute und&lt;br /&gt;
FTP-artige Kanonisierung.  (Atomares Überschreibungs-rename&lt;br /&gt;
ist bereits über die OpenSSH-Erweiterung&lt;br /&gt;
&amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; abgedeckt; siehe&lt;br /&gt;
[[#OpenSSH-SFTP-Erweiterungen]].)&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY,&lt;br /&gt;
Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack&lt;br /&gt;
als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Vorlage:Languages&amp;diff=31298</id>
		<title>Vorlage:Languages</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Vorlage:Languages&amp;diff=31298"/>
		<updated>2026-05-26T10:22:02Z</updated>

		<summary type="html">&lt;p&gt;Sv: Add /en to the enumerated language subpages (for non-English master pages)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{nmbox&lt;br /&gt;
 | header = &#039;&#039;&#039;[[Project:Language policy|{{Languages/Title|{{SUBPAGENAME}}}}]]&#039;&#039;&#039;&lt;br /&gt;
 | text = &lt;br /&gt;
&#039;&#039;&#039;[[{{{1|:{{NAMESPACE}}:{{BASEPAGENAME}}}}}|{{{label|English}}}]]&#039;&#039;&#039; {{Languages/Lang|af|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ar|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ast|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|az|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bcc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bg|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bn|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bs|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ca|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|cs|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|da|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|de|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|diq|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|el|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|en|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|eo|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|es|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fa|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|he|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hy|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|id|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|io|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|it|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ja|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ka|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|kk|{{{1|}}}|&lt;br /&gt;
}}&amp;lt;span class=&amp;quot;autonym&amp;quot;&amp;gt;{{Languages/Lang|km|{{{1|}}}|&lt;br /&gt;
}}&amp;lt;/span&amp;gt;{{Languages/Lang|ko|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ksh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|kw|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|la|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|min|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|mk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ml|{{{1|}}}|&lt;br /&gt;
}}&amp;lt;span class=&amp;quot;autonym&amp;quot;&amp;gt;{{Languages/Lang|mr|{{{1|}}}|&lt;br /&gt;
}}&amp;lt;/span&amp;gt;{{Languages/Lang|ms|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|nl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|no|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|oc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|or|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt-br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ro|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ru|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|si|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sq|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sv|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ta|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|th|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|tr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|uk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|vi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|yi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|yue|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hans|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hant|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-tw|{{{1|}}}}}|&lt;br /&gt;
}}&amp;lt;includeonly&amp;gt;{{#if:{{Languages/Lang|af|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ar|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ast|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|az|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bcc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bg|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bn|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bs|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ca|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|cs|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|da|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|de|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|diq|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|el|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|en|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|eo|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|es|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fa|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|he|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hy|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|id|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|io|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|it|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ja|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ka|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|kk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|km|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ko|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ksh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|kw|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|la|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|min|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|mk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ml|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|mr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ms|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|nl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|no|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|oc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|or|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt-br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ro|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ru|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|si|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sq|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sv|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ta|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|th|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|tr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|uk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|vi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|yi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|yue|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hans|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hant|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-tw|{{{1|}}}}}&lt;br /&gt;
||[[Category:Languages pages without translations]]}}&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{{documentation}}&lt;br /&gt;
[[Category:Exclude in print]]&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31297</id>
		<title>Remote Access/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31297"/>
		<updated>2026-05-26T10:20:13Z</updated>

		<summary type="html">&lt;p&gt;Sv: Use label= for the bold language link (after Template:Languages patch)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access/en|label=English}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Remote access&#039;&#039;&#039; is the ability to drive a remote computer or&lt;br /&gt;
network from this expecco image — opening shells, running commands,&lt;br /&gt;
moving files, or driving a test target.  Three protocol families are&lt;br /&gt;
supported, listed in current-recommended order:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH and SFTP&#039;&#039;&#039; (recommended) — encrypted shell + secure&lt;br /&gt;
  file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation&lt;br /&gt;
  in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on&lt;br /&gt;
  OpenSSL or libssh.  Use this for anything that touches credentials&lt;br /&gt;
  or sensitive payloads.&lt;br /&gt;
* &#039;&#039;&#039;Local Command Shell&#039;&#039;&#039; — fork + exec on the local machine.&lt;br /&gt;
  Used for local-tool integration and for the local end of a remote&lt;br /&gt;
  workflow that bridges via another protocol.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (legacy) — plain-text terminal session.  No&lt;br /&gt;
  encryption, passwords on the wire in clear.  Use only when the&lt;br /&gt;
  target hardware has no other option.&lt;br /&gt;
&lt;br /&gt;
= SSH and SFTP =&lt;br /&gt;
&lt;br /&gt;
The SSH stack covers the full SSH-2 protocol (RFC 4251–4254,&lt;br /&gt;
RFC 5656, RFC 8709, RFC 8731) plus OpenSSH&#039;s chacha20-poly1305&lt;br /&gt;
transport cipher and the SFTP v3 file-transfer subsystem&lt;br /&gt;
(draft-ietf-secsh-filexfer-02).  Two layers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatic SSH access (remote&lt;br /&gt;
  &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY shell, agent forwarding, ProxyJump bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — a &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;&lt;br /&gt;
  subclass that lets the rest of ST/X treat a remote SFTP path the&lt;br /&gt;
  same way it treats a local file.&lt;br /&gt;
&lt;br /&gt;
The rest of this section is organised user-task-first: what the user&lt;br /&gt;
sees and does, the expecco-library hooks below that, then the&lt;br /&gt;
implementation detail at the end for the curious.&lt;br /&gt;
&lt;br /&gt;
== From the FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Open the location dropdown and paste an &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt; URL.&lt;br /&gt;
The browser tab populates as if it were a local path.  Tree&lt;br /&gt;
expansion, column sort (name / size / mtime), preview, and&lt;br /&gt;
double-click-to-open-in-editor all behave normally.  The first&lt;br /&gt;
click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent&lt;br /&gt;
clicks reuse the pooled connection.&lt;br /&gt;
&lt;br /&gt;
URL syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User defaults to the local login name, port to 22, path to&lt;br /&gt;
&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Tools menu offers four browser actions, three of them gated on&lt;br /&gt;
the SSH library being loaded:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — opens the same key-generation&lt;br /&gt;
  dialog described under [[#Generating an SSH key pair]] below.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — opens an interactive VT100 terminal to a&lt;br /&gt;
  remote host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — points this browser tab at a remote&lt;br /&gt;
  filesystem via SFTP.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — shows size, free space and usage of&lt;br /&gt;
  the filesystem holding the currently displayed directory.  Works&lt;br /&gt;
  uniformly for local paths and SFTP paths; for SFTP it requires&lt;br /&gt;
  the server to advertise the &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt;&lt;br /&gt;
  extension (every modern OpenSSH does).  Sizes are reported in&lt;br /&gt;
  IEC binary units (MiB, GiB, TiB) — the largest unit yielding a&lt;br /&gt;
  value ≥ 1 is chosen, so a TB-scale volume reads as &#039;&#039;X TiB&#039;&#039;&lt;br /&gt;
  rather than &#039;&#039;10240 GiB&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== From expecco actions ==&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) exposes the following test&lt;br /&gt;
actions to the expecco action palette:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — opens an SSH session&lt;br /&gt;
  via the platform&#039;s &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; binary (PuTTY&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; on Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — same&lt;br /&gt;
  but with explicit public-key authentication.&lt;br /&gt;
&lt;br /&gt;
To run these you need a configured keypair (private key on this&lt;br /&gt;
machine, public key in the remote host&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;).  Generate one via the dialog&lt;br /&gt;
below or via &amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The plugin also adds a settings page at &#039;&#039;&#039;Extras → Settings →&lt;br /&gt;
Plugins → Remote Access — SSH Keys&#039;&#039;&#039; carrying a single&lt;br /&gt;
&#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; button that opens the same dialog.&lt;br /&gt;
&lt;br /&gt;
== Generating an SSH key pair ==&lt;br /&gt;
&lt;br /&gt;
=== The dialog (FileBrowserV2 / settings page) ===&lt;br /&gt;
&lt;br /&gt;
The dialog asks for all parameters in one form:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — embedded in the generated key (defaults to&lt;br /&gt;
  &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — writes &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt;&lt;br /&gt;
   (or wherever) plus a matching &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt; companion.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — writes the file and&lt;br /&gt;
   also hands the key to the running ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — key lives in agent memory only;&lt;br /&gt;
   gone on agent restart.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — full path; disabled in agent-only mode.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — empty leaves the on-disk file&lt;br /&gt;
  unencrypted (agent-only mode ignores the passphrase, since the&lt;br /&gt;
  OpenSSH agent wire protocol carries only the decrypted key).&lt;br /&gt;
&lt;br /&gt;
On &#039;&#039;&#039;Generate&#039;&#039;&#039;, the public-key line (the same&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt; string ssh-keygen&lt;br /&gt;
emits) is copied to the system clipboard for pasting into the&lt;br /&gt;
remote host&#039;s &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From a workspace ===&lt;br /&gt;
&lt;br /&gt;
For headless deployments, sandboxed builds, or scripts,&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; exposes a pure-Smalltalk key generator&lt;br /&gt;
that produces output bit-compatible with&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Save passphrase-encrypted to disk&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ AND load into the running agent&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Print the public-key line to paste into authorized_keys&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keys generated this way are interoperable with OpenSSH&#039;s own&lt;br /&gt;
tooling (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; re-derives the public&lt;br /&gt;
key, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; changes the passphrase,&lt;br /&gt;
etc.).&lt;br /&gt;
&lt;br /&gt;
=== Using the shell tools instead ===&lt;br /&gt;
&lt;br /&gt;
The traditional path also works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparing ssh-agent ==&lt;br /&gt;
&lt;br /&gt;
The agent path is strongly preferred over reading raw keyfiles: it&lt;br /&gt;
keeps encrypted private keys unlocked once per session, and handles&lt;br /&gt;
identities (hardware-token-backed keys, KeePassXC entries) that&lt;br /&gt;
ST/X should never see directly.&lt;br /&gt;
&lt;br /&gt;
ST/X picks the agent path automatically when&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is set in the process environment&lt;br /&gt;
&#039;&#039;&#039;at the time stx is launched&#039;&#039;&#039;.  Setting it later from a&lt;br /&gt;
workspace does not help.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Most desktop distributions launch an agent automatically as part of&lt;br /&gt;
the session (gnome-keyring on GNOME, ssh-agent.service on systemd,&lt;br /&gt;
KWallet on KDE).  Verify in a terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar&lt;br /&gt;
ssh-add -l             # lists loaded identities&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If no agent runs at all, add this snippet to your shell rc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc or ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X must be launched from a shell that has seen this rc — a&lt;br /&gt;
desktop launcher started from the file manager does NOT inherit&lt;br /&gt;
the variable.  Wrap the stx start command in a small script under&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt; that sources the rc first.&lt;br /&gt;
&lt;br /&gt;
The Remote Access settings page (&#039;&#039;&#039;Extras → Settings → Plugins&lt;br /&gt;
→ Remote Access — SSH Keys&#039;&#039;&#039;) shows whether the running image&lt;br /&gt;
sees an agent.&lt;br /&gt;
&lt;br /&gt;
==== Permanent setup via systemd ====&lt;br /&gt;
&lt;br /&gt;
For a truly cross-session agent (survives desktop logouts, comes&lt;br /&gt;
up automatically at next login), enable the per-user systemd&lt;br /&gt;
unit shipped with most distros&#039; &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
package:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then point &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the user-service socket&lt;br /&gt;
in your shell rc (this replaces the &amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;&lt;br /&gt;
snippet above):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
To skip the manual &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; step, let OpenSSH load&lt;br /&gt;
keys into the agent automatically the first time they are needed.&lt;br /&gt;
Add to &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first SSH connection then prompts for the key passphrase&lt;br /&gt;
once and hands the unlocked key to the agent; subsequent&lt;br /&gt;
connections use the cached identity without prompting.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ ships native OpenSSH including an agent service.&lt;br /&gt;
One-time setup:&lt;br /&gt;
&lt;br /&gt;
# Open &#039;&#039;&#039;Services&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) as Administrator.&lt;br /&gt;
# Find &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039;, set Startup Type to&lt;br /&gt;
  &#039;&#039;&#039;Automatic&#039;&#039;&#039;, click &#039;&#039;&#039;Start&#039;&#039;&#039;.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Windows OpenSSH agent listens on a named pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), not a Unix socket.  ST/X&lt;br /&gt;
supports both transports, but Windows ssh-add does &#039;&#039;&#039;not&#039;&#039;&#039; set&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; for you.  Add it manually:&lt;br /&gt;
&lt;br /&gt;
# Press {{Key|Win}} → type &amp;quot;environment&amp;quot; → &amp;quot;Edit the system environment variables&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Environment Variables&#039;&#039;&#039; → under &#039;&#039;&#039;User variables&#039;&#039;&#039;, &#039;&#039;&#039;New&#039;&#039;&#039;.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Value: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Log out and back in (or restart stx) so the new env propagates.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell quick-setup ====&lt;br /&gt;
&lt;br /&gt;
The same setup from an &#039;&#039;&#039;elevated&#039;&#039;&#039; PowerShell prompt, for&lt;br /&gt;
scripts or unattended provisioning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Start the agent now AND on every reboot (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# Persist SSH_AUTH_SOCK for the user (survives reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Load a key (prompts for the passphrase if the file is encrypted).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a one-shot agent start without making it persistent (e.g.&lt;br /&gt;
single-session test), drop the &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt; line and&lt;br /&gt;
just run &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt;.  The env-var line&lt;br /&gt;
can also be omitted if &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is only needed&lt;br /&gt;
in the current shell — use &amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
instead for that session-local form.&lt;br /&gt;
&lt;br /&gt;
On stripped-down Windows installs the ssh-agent service may not&lt;br /&gt;
be present.  Add it once via &#039;&#039;&#039;Settings → Apps → Optional&lt;br /&gt;
features → OpenSSH Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative agents:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — uses its own protocol; NOT supported by&lt;br /&gt;
  ST/X&#039;s SSH::Agent.  Migrate the keys to OpenSSH.&lt;br /&gt;
* &#039;&#039;&#039;Git for Windows ssh-agent&#039;&#039;&#039; — works; point&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the socket it publishes.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — a ST/X inside WSL sees WSL&#039;s Linux agent normally;&lt;br /&gt;
  a ST/X on the Windows side does not.  Bridging needs a helper&lt;br /&gt;
  like &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify in the Remote Access settings page&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
the agent indicator there reports whether the running image sees&lt;br /&gt;
the agent.&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
Windows OpenSSH does &#039;&#039;&#039;not&#039;&#039;&#039; persist agent-loaded keys across&lt;br /&gt;
agent restarts.  To avoid running &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; manually&lt;br /&gt;
after each reboot, add the same lazy-load configuration to&lt;br /&gt;
&amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH then loads the key into the agent on first use (prompts&lt;br /&gt;
for the passphrase once) and reuses it for the rest of the&lt;br /&gt;
session.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All tunables are class-side on &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Default !! What it controls&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 min) || How long a pooled&lt;br /&gt;
connection sits idle before the next access proactively closes +&lt;br /&gt;
reopens it.  Just under typical sshd&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; so we recycle&lt;br /&gt;
before the server TCP-RESETs us.  Pass &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; to restore&lt;br /&gt;
the default.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Max age (s) of a&lt;br /&gt;
cached STAT before &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; refetches.  Parent&lt;br /&gt;
listDir always re-stamps fresh attrs onto children, so navigating&lt;br /&gt;
an open directory does not pay the TTL.  Set to &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to&lt;br /&gt;
disable caching.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (action) || Tears down every&lt;br /&gt;
pooled connection.  Useful after a known-bad network event, before&lt;br /&gt;
a deliberate identity swap, or as part of a clean image shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
Open &amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; from the Launcher&#039;s &amp;quot;Status&amp;quot;&lt;br /&gt;
sub-menu.  Per-host SFTP mutex appears as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;; the pool-wide mutex as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Right-click a row:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — dumps the last-owner&#039;s&lt;br /&gt;
  walkback plus each waiter&#039;s, formatted as plain text.  Use when&lt;br /&gt;
  a process is wedged in &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; inside&lt;br /&gt;
  &amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; and you need to see which SFTP&lt;br /&gt;
  request it is on.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — the whole table, for an&lt;br /&gt;
  email-this-to-someone diagnosis.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS over the wait-for graph, reports&lt;br /&gt;
  cycles.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
The SSH stack logs interesting events:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; on auto-reconnect after a dead connection.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when a pool entry is idle-evicted.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when an SSH key file cannot be parsed&lt;br /&gt;
  (e.g. legacy PEM, encrypted-without-agent) — the file is skipped,&lt;br /&gt;
  others tried.&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SFTP v3 only.&#039;&#039;&#039;  No SETSTAT (no remote chmod / chown / utime),&lt;br /&gt;
  no SSH_FXP_READLINK exposed (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; always&lt;br /&gt;
  &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; returns the regular&lt;br /&gt;
  stat info).  Several SFTPv5+ niceties are nevertheless picked up&lt;br /&gt;
  via OpenSSH SSH_FXP_EXTENDED requests — see&lt;br /&gt;
  [[#OpenSSH SFTP extensions]] below.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation.&#039;&#039;&#039;  Two concurrent operations on the&lt;br /&gt;
  same host queue through the host mutex.  See [[#Future work]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; fallback has a TOCTOU window.&#039;&#039;&#039;  On&lt;br /&gt;
  servers that advertise &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; (every&lt;br /&gt;
  modern OpenSSH does), overwrite is atomic; on the rare server&lt;br /&gt;
  that does not, the receiver is emulated as delete-then-rename&lt;br /&gt;
  and another process can race in between.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; is heuristic.&#039;&#039;&#039;  Always&lt;br /&gt;
  returns &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (the accurate answer would cost&lt;br /&gt;
  three round-trips per directory icon, which made the original tree&lt;br /&gt;
  expansion unbearably slow).&lt;br /&gt;
&lt;br /&gt;
== Implementation details ==&lt;br /&gt;
&lt;br /&gt;
For readers wanting the architecture.  Five classes, top-down:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename subclass; the public&lt;br /&gt;
API.  Maps &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt; URLs to remote files; exposes&lt;br /&gt;
&amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3 protocol&lt;br /&gt;
(request/response codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Driven by SftpFilename.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH channel multiplexer&lt;br /&gt;
(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST).  One logical&lt;br /&gt;
session per Channel instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-level SSH client: opens the&lt;br /&gt;
transport, runs KEX, host-key check, userauth, then dispenses&lt;br /&gt;
Channels.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Wire layer.  Banner + KEXINIT&lt;br /&gt;
exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq,&lt;br /&gt;
heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH SFTP extensions ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal.&lt;br /&gt;
OpenSSH ships an open-ended extension mechanism: the server lists&lt;br /&gt;
extension names it understands in its &amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;&lt;br /&gt;
reply, and the client invokes them via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt; packets carrying the extension&lt;br /&gt;
name as the first string.  Each extension is feature-detected via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;; callers fall&lt;br /&gt;
back when the server doesn&#039;t advertise it.&lt;br /&gt;
&lt;br /&gt;
The stack uses four of the OpenSSH extensions today:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomic&lt;br /&gt;
  rename-with-overwrite.  Picked up automatically by&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt;; the delete-then-rename&lt;br /&gt;
  fallback only fires on servers that lack it.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — create a POSIX hard link.&lt;br /&gt;
  Exposed as &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX&lt;br /&gt;
  &amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-shape filesystem stats.  Exposed as&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;; the result is&lt;br /&gt;
  shape-compatible with &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;&lt;br /&gt;
  so callers can treat local and remote uniformly.  Drives the&lt;br /&gt;
  &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; menu entry described at the&lt;br /&gt;
  top of this page.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — flush server-side write buffer&lt;br /&gt;
  to disk on an open handle.  Available on the low-level&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt;; not yet plumbed&lt;br /&gt;
  into a Filename-level &amp;quot;durable write&amp;quot; API.&lt;br /&gt;
&lt;br /&gt;
The remaining OpenSSH extensions&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
are recognised in the advertised-extensions list but not wrapped at&lt;br /&gt;
Filename level — there&#039;s no Filename-side caller for them yet.&lt;br /&gt;
&lt;br /&gt;
=== Connection pooling ===&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt; instance pointing at the same&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; triple shares one&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; plus one &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Pool is class-side, guarded by a single&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy bring-up&#039;&#039;&#039; — TCP + KEX + userauth + SFTP INIT happens&lt;br /&gt;
  on the first SFTP operation, not on &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation&#039;&#039;&#039; — SFTP requests on a given host&lt;br /&gt;
  are serialised through a &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; named&lt;br /&gt;
  &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; (visible in&lt;br /&gt;
  SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle eviction&#039;&#039;&#039; — unused for longer than&lt;br /&gt;
  &amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt;, the entry is proactively&lt;br /&gt;
  closed + reopened on the next access.&lt;br /&gt;
* &#039;&#039;&#039;Auto-reconnect&#039;&#039;&#039; — a transport-level failure (broken pipe,&lt;br /&gt;
  EOF, MNU on nil socket) evicts the dead pool entry, opens a&lt;br /&gt;
  fresh client, retries the request &#039;&#039;&#039;once&#039;&#039;&#039;.  Application-level&lt;br /&gt;
  SFTP STATUS errors propagate immediately.&lt;br /&gt;
&lt;br /&gt;
== Future work ==&lt;br /&gt;
&lt;br /&gt;
Tracked but not yet implemented:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-channel parallelism per host&#039;&#039;&#039; — today one TCP +&lt;br /&gt;
  one SFTP channel per host means N concurrent requests&lt;br /&gt;
  serialise.  Pipelining over multiple SshClients in the pool&lt;br /&gt;
  (preferred), or a transport-level reader process demultiplexing&lt;br /&gt;
  to per-channel inboxes, would let the tree pane keep listing&lt;br /&gt;
  while the content pane reads a large file.&lt;br /&gt;
* &#039;&#039;&#039;Accurate &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR&lt;br /&gt;
  + READ_DIR (first batch only) + CLOSE — three RTTs per probe;&lt;br /&gt;
  needs SftpClient to pipeline requests before this pays off.&lt;br /&gt;
* &#039;&#039;&#039;SFTP v5/v6 negotiation&#039;&#039;&#039; for extended attrs and FTP-style&lt;br /&gt;
  canonicalisation.  (Atomic-overwrite rename is already handled&lt;br /&gt;
  via the OpenSSH &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; extension;&lt;br /&gt;
  see [[#OpenSSH SFTP extensions]].)&lt;br /&gt;
&lt;br /&gt;
= Command Shell =&lt;br /&gt;
&lt;br /&gt;
Local command shell on this expecco machine.  Typical applications:&lt;br /&gt;
local command-line, running a local helper tool, bridging a&lt;br /&gt;
remote workflow to a local utility.&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No credentials, no network — runs as the expecco process&#039;s own&lt;br /&gt;
user.  Output streams to expecco&#039;s log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warning]] &#039;&#039;&#039;Telnet is a legacy protocol&lt;br /&gt;
with no encryption.&#039;&#039;&#039; Passwords are transmitted in plain text on&lt;br /&gt;
the wire; anyone on the network path can read them.  Use Telnet&lt;br /&gt;
ONLY when the target device has no other option (typically: old&lt;br /&gt;
industrial controllers, lab instruments, embedded measurement&lt;br /&gt;
equipment without an SSH stack).  For everything else use&lt;br /&gt;
[[#SSH and SFTP]].&lt;br /&gt;
&lt;br /&gt;
The expecco plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (internal demo)&lt;br /&gt;
&lt;br /&gt;
The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream&lt;br /&gt;
over TCP, with in-band control sequences for terminal options.&lt;br /&gt;
A connection is established to a target host:port; after optional&lt;br /&gt;
in-band login, both sides can send data.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent&lt;br /&gt;
  forwarding, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of&lt;br /&gt;
  this stack.&lt;br /&gt;
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack&lt;br /&gt;
  for its HTTPS transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Network]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31296</id>
		<title>Remote Access</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31296"/>
		<updated>2026-05-26T10:20:11Z</updated>

		<summary type="html">&lt;p&gt;Sv: Use label= for the bold language link (after Template:Languages patch)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access|label=Deutsch}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere&lt;br /&gt;
  Dateiübertragung über einen SSH-2-Tunnel.  Reine&lt;br /&gt;
  Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;;&lt;br /&gt;
  keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles&lt;br /&gt;
  mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen&lt;br /&gt;
  Maschine.  Für die Anbindung lokaler Werkzeuge und für die&lt;br /&gt;
  lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine&lt;br /&gt;
  Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur&lt;br /&gt;
  einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff&lt;br /&gt;
  (entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung,&lt;br /&gt;
  ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
  &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
  erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
  Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet vier Aktionen — die&lt;br /&gt;
drei SSH-spezifischen sind nur bei geladener SSH-Bibliothek&lt;br /&gt;
sichtbar:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den&lt;br /&gt;
  Schlüsselerzeugungs-Dialog, siehe&lt;br /&gt;
  [[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal&lt;br /&gt;
  zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP&lt;br /&gt;
  auf ein entferntes Dateisystem.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — zeigt Größe, freien Platz und Belegung&lt;br /&gt;
  des Dateisystems, das das aktuell angezeigte Verzeichnis enthält.&lt;br /&gt;
  Funktioniert einheitlich für lokale und SFTP-Pfade; bei SFTP&lt;br /&gt;
  setzt der Aufruf voraus, daß der Server die Erweiterung&lt;br /&gt;
  &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; ankündigt (jedes moderne OpenSSH&lt;br /&gt;
  tut das).  Größen werden in IEC-Binäreinheiten ausgegeben (MiB,&lt;br /&gt;
  GiB, TiB) — gewählt wird die größte Einheit, die einen Wert ≥ 1&lt;br /&gt;
  liefert, damit ein TB-großes Volume als &#039;&#039;X TiB&#039;&#039; statt&lt;br /&gt;
  &#039;&#039;10240 GiB&#039;&#039; erscheint.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine&lt;br /&gt;
  SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary&lt;br /&gt;
  (PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; —&lt;br /&gt;
  dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet&lt;br /&gt;
  (Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
   &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
   zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei&lt;br /&gt;
   UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im&lt;br /&gt;
   Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im&lt;br /&gt;
  Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei&lt;br /&gt;
  unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da&lt;br /&gt;
  das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten&lt;br /&gt;
  Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf&lt;br /&gt;
  &#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs-&lt;br /&gt;
  variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;,&lt;br /&gt;
  &#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue&lt;br /&gt;
  Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
  OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
  zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen&lt;br /&gt;
  Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine&lt;br /&gt;
  Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist&lt;br /&gt;
  möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback&lt;br /&gt;
  des letzten Eigners samt aller Waiter als Text in die&lt;br /&gt;
  Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
  &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
  &amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine&lt;br /&gt;
  E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet&lt;br /&gt;
  Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter&lt;br /&gt;
  Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht&lt;br /&gt;
  geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes&lt;br /&gt;
  chmod / chown / utime), kein SSH_FXP_READLINK exponiert&lt;br /&gt;
  (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
  &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  Einige&lt;br /&gt;
  SFTPv5+-Annehmlichkeiten werden dennoch über OpenSSH-spezifische&lt;br /&gt;
  SSH_FXP_EXTENDED-Aufrufe nutzbar — siehe&lt;br /&gt;
  [[#OpenSSH-SFTP-Erweiterungen]] weiter unten.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen&lt;br /&gt;
  am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt;-Fallback hat ein TOCTOU-Fenster.&#039;&#039;&#039;&lt;br /&gt;
  Bei Servern, die &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; ankündigen&lt;br /&gt;
  (jedes moderne OpenSSH tut das), ist das Überschreiben atomar.&lt;br /&gt;
  Beim seltenen Server, der das nicht tut, wird auf&lt;br /&gt;
  Delete-dann-Rename ausgewichen und ein anderer Prozess kann sich&lt;br /&gt;
  dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039;&lt;br /&gt;
  Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort&lt;br /&gt;
  würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das&lt;br /&gt;
  ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH-SFTP-Erweiterungen ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (Entwurf draft-ietf-secsh-filexfer-02) ist bewusst&lt;br /&gt;
minimal gehalten.  OpenSSH bringt einen offenen&lt;br /&gt;
Erweiterungsmechanismus mit: der Server listet im&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;-Reply die Erweiterungsnamen auf, die&lt;br /&gt;
er versteht, und der Client ruft sie über&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt;-Pakete mit dem&lt;br /&gt;
Erweiterungsnamen als erstem String auf.  Jede Erweiterung wird&lt;br /&gt;
über &amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;&lt;br /&gt;
feature-detektiert; Aufrufer fallen zurück, wenn der Server sie&lt;br /&gt;
nicht ankündigt.&lt;br /&gt;
&lt;br /&gt;
Der Stack nutzt heute vier OpenSSH-Erweiterungen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomares&lt;br /&gt;
  rename-mit-Überschreiben.  Wird automatisch von&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt; aufgegriffen; die&lt;br /&gt;
  Delete-dann-Rename-Fallback-Variante kommt nur bei Servern zum&lt;br /&gt;
  Einsatz, die die Erweiterung nicht haben.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — Erzeugt einen POSIX-Hardlink.&lt;br /&gt;
  Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX-&lt;br /&gt;
  &amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-typische Dateisystem-Statistik.&lt;br /&gt;
  Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
  das Ergebnis ist form-kompatibel zu&lt;br /&gt;
  &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;, sodass Aufrufer&lt;br /&gt;
  lokale und entfernte Pfade einheitlich behandeln können.&lt;br /&gt;
  Treibt den Menü-Eintrag &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; an,&lt;br /&gt;
  der am Anfang dieser Seite beschrieben ist.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — schreibt den&lt;br /&gt;
  serverseitigen Schreibpuffer eines geöffneten Handles auf&lt;br /&gt;
  Platte.  Liegt als&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt; bereit; noch nicht&lt;br /&gt;
  in eine &amp;quot;Durable-Write&amp;quot;-API auf Filename-Ebene eingebunden.&lt;br /&gt;
&lt;br /&gt;
Die verbleibenden OpenSSH-Erweiterungen&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
werden in der angekündigten Liste erkannt, aber nicht auf&lt;br /&gt;
Filename-Ebene gekapselt — es gibt dafür noch keinen&lt;br /&gt;
Filename-seitigen Aufrufer.&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen&lt;br /&gt;
  erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen&lt;br /&gt;
  bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt;&lt;br /&gt;
  mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
  serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
  &amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
  nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene&lt;br /&gt;
  (Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den&lt;br /&gt;
  Pool-Eintrag, öffnet einen frischen Client und wiederholt die&lt;br /&gt;
  Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus&lt;br /&gt;
  SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet&lt;br /&gt;
  eine TCP- plus eine SFTP-Verbindung pro Host, dass N&lt;br /&gt;
  gleichzeitige Anfragen serialisieren.  Pipelining über mehrere&lt;br /&gt;
  SshClients im Pool (bevorzugt) oder ein transport-seitiger&lt;br /&gt;
  Reader-Prozess, der eingehende Pakete in&lt;br /&gt;
  Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel&lt;br /&gt;
  erlauben, weiter aufzulisten, während das Inhalts-Panel eine&lt;br /&gt;
  große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR&lt;br /&gt;
  + READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro&lt;br /&gt;
  Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen&lt;br /&gt;
  kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für erweiterte Attribute und&lt;br /&gt;
  FTP-artige Kanonisierung.  (Atomares Überschreibungs-rename&lt;br /&gt;
  ist bereits über die OpenSSH-Erweiterung&lt;br /&gt;
  &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; abgedeckt; siehe&lt;br /&gt;
  [[#OpenSSH-SFTP-Erweiterungen]].)&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY,&lt;br /&gt;
  Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack&lt;br /&gt;
  als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Vorlage:Languages&amp;diff=31295</id>
		<title>Vorlage:Languages</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Vorlage:Languages&amp;diff=31295"/>
		<updated>2026-05-26T10:19:27Z</updated>

		<summary type="html">&lt;p&gt;Sv: Make bold-link label parameterizable (default English) — see Talk for rationale&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{nmbox&lt;br /&gt;
 | header = &#039;&#039;&#039;[[Project:Language policy|{{Languages/Title|{{SUBPAGENAME}}}}]]&#039;&#039;&#039;&lt;br /&gt;
 | text = &lt;br /&gt;
&#039;&#039;&#039;[[{{{1|:{{NAMESPACE}}:{{BASEPAGENAME}}}}}|{{{label|English}}}]]&#039;&#039;&#039; {{Languages/Lang|af|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ar|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ast|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|az|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bcc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bg|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bn|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bs|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ca|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|cs|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|da|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|de|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|diq|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|el|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|eo|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|es|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fa|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|he|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hy|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|id|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|io|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|it|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ja|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ka|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|kk|{{{1|}}}|&lt;br /&gt;
}}&amp;lt;span class=&amp;quot;autonym&amp;quot;&amp;gt;{{Languages/Lang|km|{{{1|}}}|&lt;br /&gt;
}}&amp;lt;/span&amp;gt;{{Languages/Lang|ko|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ksh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|kw|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|la|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|min|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|mk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ml|{{{1|}}}|&lt;br /&gt;
}}&amp;lt;span class=&amp;quot;autonym&amp;quot;&amp;gt;{{Languages/Lang|mr|{{{1|}}}|&lt;br /&gt;
}}&amp;lt;/span&amp;gt;{{Languages/Lang|ms|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|nl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|no|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|oc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|or|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt-br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ro|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ru|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|si|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sq|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sv|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ta|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|th|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|tr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|uk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|vi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|yi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|yue|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hans|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hant|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-tw|{{{1|}}}}}|&lt;br /&gt;
}}&amp;lt;includeonly&amp;gt;{{#if:{{Languages/Lang|af|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ar|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ast|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|az|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bcc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bg|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bn|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bs|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ca|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|cs|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|da|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|de|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|diq|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|el|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|eo|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|es|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fa|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|he|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hy|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|id|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|io|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|it|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ja|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ka|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|kk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|km|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ko|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ksh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|kw|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|la|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|min|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|mk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ml|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|mr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ms|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|nl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|no|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|oc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|or|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt-br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ro|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ru|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|si|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sq|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sv|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ta|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|th|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|tr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|uk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|vi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|yi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|yue|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hans|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hant|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-tw|{{{1|}}}}}&lt;br /&gt;
||[[Category:Languages pages without translations]]}}&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{{documentation}}&lt;br /&gt;
[[Category:Exclude in print]]&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31294</id>
		<title>Remote Access/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31294"/>
		<updated>2026-05-26T09:53:07Z</updated>

		<summary type="html">&lt;p&gt;Sv: Add Filesystem Info feature; document OpenSSH SFTP extensions (posix-rename, hardlink, statvfs, fsync); correct stale limitations/future-work entries.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access|Remote Access/en}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Remote access&#039;&#039;&#039; is the ability to drive a remote computer or&lt;br /&gt;
network from this expecco image — opening shells, running commands,&lt;br /&gt;
moving files, or driving a test target.  Three protocol families are&lt;br /&gt;
supported, listed in current-recommended order:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH and SFTP&#039;&#039;&#039; (recommended) — encrypted shell + secure&lt;br /&gt;
  file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation&lt;br /&gt;
  in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on&lt;br /&gt;
  OpenSSL or libssh.  Use this for anything that touches credentials&lt;br /&gt;
  or sensitive payloads.&lt;br /&gt;
* &#039;&#039;&#039;Local Command Shell&#039;&#039;&#039; — fork + exec on the local machine.&lt;br /&gt;
  Used for local-tool integration and for the local end of a remote&lt;br /&gt;
  workflow that bridges via another protocol.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (legacy) — plain-text terminal session.  No&lt;br /&gt;
  encryption, passwords on the wire in clear.  Use only when the&lt;br /&gt;
  target hardware has no other option.&lt;br /&gt;
&lt;br /&gt;
= SSH and SFTP =&lt;br /&gt;
&lt;br /&gt;
The SSH stack covers the full SSH-2 protocol (RFC 4251–4254,&lt;br /&gt;
RFC 5656, RFC 8709, RFC 8731) plus OpenSSH&#039;s chacha20-poly1305&lt;br /&gt;
transport cipher and the SFTP v3 file-transfer subsystem&lt;br /&gt;
(draft-ietf-secsh-filexfer-02).  Two layers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatic SSH access (remote&lt;br /&gt;
  &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY shell, agent forwarding, ProxyJump bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — a &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;&lt;br /&gt;
  subclass that lets the rest of ST/X treat a remote SFTP path the&lt;br /&gt;
  same way it treats a local file.&lt;br /&gt;
&lt;br /&gt;
The rest of this section is organised user-task-first: what the user&lt;br /&gt;
sees and does, the expecco-library hooks below that, then the&lt;br /&gt;
implementation detail at the end for the curious.&lt;br /&gt;
&lt;br /&gt;
== From the FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Open the location dropdown and paste an &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt; URL.&lt;br /&gt;
The browser tab populates as if it were a local path.  Tree&lt;br /&gt;
expansion, column sort (name / size / mtime), preview, and&lt;br /&gt;
double-click-to-open-in-editor all behave normally.  The first&lt;br /&gt;
click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent&lt;br /&gt;
clicks reuse the pooled connection.&lt;br /&gt;
&lt;br /&gt;
URL syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User defaults to the local login name, port to 22, path to&lt;br /&gt;
&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Tools menu offers four browser actions, three of them gated on&lt;br /&gt;
the SSH library being loaded:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — opens the same key-generation&lt;br /&gt;
  dialog described under [[#Generating an SSH key pair]] below.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — opens an interactive VT100 terminal to a&lt;br /&gt;
  remote host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — points this browser tab at a remote&lt;br /&gt;
  filesystem via SFTP.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — shows size, free space and usage of&lt;br /&gt;
  the filesystem holding the currently displayed directory.  Works&lt;br /&gt;
  uniformly for local paths and SFTP paths; for SFTP it requires&lt;br /&gt;
  the server to advertise the &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt;&lt;br /&gt;
  extension (every modern OpenSSH does).  Sizes are reported in&lt;br /&gt;
  IEC binary units (MiB, GiB, TiB) — the largest unit yielding a&lt;br /&gt;
  value ≥ 1 is chosen, so a TB-scale volume reads as &#039;&#039;X TiB&#039;&#039;&lt;br /&gt;
  rather than &#039;&#039;10240 GiB&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== From expecco actions ==&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) exposes the following test&lt;br /&gt;
actions to the expecco action palette:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — opens an SSH session&lt;br /&gt;
  via the platform&#039;s &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; binary (PuTTY&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; on Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — same&lt;br /&gt;
  but with explicit public-key authentication.&lt;br /&gt;
&lt;br /&gt;
To run these you need a configured keypair (private key on this&lt;br /&gt;
machine, public key in the remote host&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;).  Generate one via the dialog&lt;br /&gt;
below or via &amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The plugin also adds a settings page at &#039;&#039;&#039;Extras → Settings →&lt;br /&gt;
Plugins → Remote Access — SSH Keys&#039;&#039;&#039; carrying a single&lt;br /&gt;
&#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; button that opens the same dialog.&lt;br /&gt;
&lt;br /&gt;
== Generating an SSH key pair ==&lt;br /&gt;
&lt;br /&gt;
=== The dialog (FileBrowserV2 / settings page) ===&lt;br /&gt;
&lt;br /&gt;
The dialog asks for all parameters in one form:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — embedded in the generated key (defaults to&lt;br /&gt;
  &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — writes &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt;&lt;br /&gt;
   (or wherever) plus a matching &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt; companion.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — writes the file and&lt;br /&gt;
   also hands the key to the running ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — key lives in agent memory only;&lt;br /&gt;
   gone on agent restart.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — full path; disabled in agent-only mode.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — empty leaves the on-disk file&lt;br /&gt;
  unencrypted (agent-only mode ignores the passphrase, since the&lt;br /&gt;
  OpenSSH agent wire protocol carries only the decrypted key).&lt;br /&gt;
&lt;br /&gt;
On &#039;&#039;&#039;Generate&#039;&#039;&#039;, the public-key line (the same&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt; string ssh-keygen&lt;br /&gt;
emits) is copied to the system clipboard for pasting into the&lt;br /&gt;
remote host&#039;s &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From a workspace ===&lt;br /&gt;
&lt;br /&gt;
For headless deployments, sandboxed builds, or scripts,&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; exposes a pure-Smalltalk key generator&lt;br /&gt;
that produces output bit-compatible with&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Save passphrase-encrypted to disk&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ AND load into the running agent&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Print the public-key line to paste into authorized_keys&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keys generated this way are interoperable with OpenSSH&#039;s own&lt;br /&gt;
tooling (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; re-derives the public&lt;br /&gt;
key, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; changes the passphrase,&lt;br /&gt;
etc.).&lt;br /&gt;
&lt;br /&gt;
=== Using the shell tools instead ===&lt;br /&gt;
&lt;br /&gt;
The traditional path also works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparing ssh-agent ==&lt;br /&gt;
&lt;br /&gt;
The agent path is strongly preferred over reading raw keyfiles: it&lt;br /&gt;
keeps encrypted private keys unlocked once per session, and handles&lt;br /&gt;
identities (hardware-token-backed keys, KeePassXC entries) that&lt;br /&gt;
ST/X should never see directly.&lt;br /&gt;
&lt;br /&gt;
ST/X picks the agent path automatically when&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is set in the process environment&lt;br /&gt;
&#039;&#039;&#039;at the time stx is launched&#039;&#039;&#039;.  Setting it later from a&lt;br /&gt;
workspace does not help.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Most desktop distributions launch an agent automatically as part of&lt;br /&gt;
the session (gnome-keyring on GNOME, ssh-agent.service on systemd,&lt;br /&gt;
KWallet on KDE).  Verify in a terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar&lt;br /&gt;
ssh-add -l             # lists loaded identities&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If no agent runs at all, add this snippet to your shell rc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc or ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X must be launched from a shell that has seen this rc — a&lt;br /&gt;
desktop launcher started from the file manager does NOT inherit&lt;br /&gt;
the variable.  Wrap the stx start command in a small script under&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt; that sources the rc first.&lt;br /&gt;
&lt;br /&gt;
The Remote Access settings page (&#039;&#039;&#039;Extras → Settings → Plugins&lt;br /&gt;
→ Remote Access — SSH Keys&#039;&#039;&#039;) shows whether the running image&lt;br /&gt;
sees an agent.&lt;br /&gt;
&lt;br /&gt;
==== Permanent setup via systemd ====&lt;br /&gt;
&lt;br /&gt;
For a truly cross-session agent (survives desktop logouts, comes&lt;br /&gt;
up automatically at next login), enable the per-user systemd&lt;br /&gt;
unit shipped with most distros&#039; &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
package:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then point &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the user-service socket&lt;br /&gt;
in your shell rc (this replaces the &amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;&lt;br /&gt;
snippet above):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
To skip the manual &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; step, let OpenSSH load&lt;br /&gt;
keys into the agent automatically the first time they are needed.&lt;br /&gt;
Add to &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first SSH connection then prompts for the key passphrase&lt;br /&gt;
once and hands the unlocked key to the agent; subsequent&lt;br /&gt;
connections use the cached identity without prompting.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ ships native OpenSSH including an agent service.&lt;br /&gt;
One-time setup:&lt;br /&gt;
&lt;br /&gt;
# Open &#039;&#039;&#039;Services&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) as Administrator.&lt;br /&gt;
# Find &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039;, set Startup Type to&lt;br /&gt;
  &#039;&#039;&#039;Automatic&#039;&#039;&#039;, click &#039;&#039;&#039;Start&#039;&#039;&#039;.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Windows OpenSSH agent listens on a named pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), not a Unix socket.  ST/X&lt;br /&gt;
supports both transports, but Windows ssh-add does &#039;&#039;&#039;not&#039;&#039;&#039; set&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; for you.  Add it manually:&lt;br /&gt;
&lt;br /&gt;
# Press {{Key|Win}} → type &amp;quot;environment&amp;quot; → &amp;quot;Edit the system environment variables&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Environment Variables&#039;&#039;&#039; → under &#039;&#039;&#039;User variables&#039;&#039;&#039;, &#039;&#039;&#039;New&#039;&#039;&#039;.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Value: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Log out and back in (or restart stx) so the new env propagates.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell quick-setup ====&lt;br /&gt;
&lt;br /&gt;
The same setup from an &#039;&#039;&#039;elevated&#039;&#039;&#039; PowerShell prompt, for&lt;br /&gt;
scripts or unattended provisioning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Start the agent now AND on every reboot (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# Persist SSH_AUTH_SOCK for the user (survives reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Load a key (prompts for the passphrase if the file is encrypted).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a one-shot agent start without making it persistent (e.g.&lt;br /&gt;
single-session test), drop the &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt; line and&lt;br /&gt;
just run &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt;.  The env-var line&lt;br /&gt;
can also be omitted if &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is only needed&lt;br /&gt;
in the current shell — use &amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
instead for that session-local form.&lt;br /&gt;
&lt;br /&gt;
On stripped-down Windows installs the ssh-agent service may not&lt;br /&gt;
be present.  Add it once via &#039;&#039;&#039;Settings → Apps → Optional&lt;br /&gt;
features → OpenSSH Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative agents:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — uses its own protocol; NOT supported by&lt;br /&gt;
  ST/X&#039;s SSH::Agent.  Migrate the keys to OpenSSH.&lt;br /&gt;
* &#039;&#039;&#039;Git for Windows ssh-agent&#039;&#039;&#039; — works; point&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the socket it publishes.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — a ST/X inside WSL sees WSL&#039;s Linux agent normally;&lt;br /&gt;
  a ST/X on the Windows side does not.  Bridging needs a helper&lt;br /&gt;
  like &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify in the Remote Access settings page&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
the agent indicator there reports whether the running image sees&lt;br /&gt;
the agent.&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
Windows OpenSSH does &#039;&#039;&#039;not&#039;&#039;&#039; persist agent-loaded keys across&lt;br /&gt;
agent restarts.  To avoid running &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; manually&lt;br /&gt;
after each reboot, add the same lazy-load configuration to&lt;br /&gt;
&amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH then loads the key into the agent on first use (prompts&lt;br /&gt;
for the passphrase once) and reuses it for the rest of the&lt;br /&gt;
session.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All tunables are class-side on &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Default !! What it controls&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 min) || How long a pooled&lt;br /&gt;
connection sits idle before the next access proactively closes +&lt;br /&gt;
reopens it.  Just under typical sshd&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; so we recycle&lt;br /&gt;
before the server TCP-RESETs us.  Pass &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; to restore&lt;br /&gt;
the default.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Max age (s) of a&lt;br /&gt;
cached STAT before &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; refetches.  Parent&lt;br /&gt;
listDir always re-stamps fresh attrs onto children, so navigating&lt;br /&gt;
an open directory does not pay the TTL.  Set to &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to&lt;br /&gt;
disable caching.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (action) || Tears down every&lt;br /&gt;
pooled connection.  Useful after a known-bad network event, before&lt;br /&gt;
a deliberate identity swap, or as part of a clean image shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
Open &amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; from the Launcher&#039;s &amp;quot;Status&amp;quot;&lt;br /&gt;
sub-menu.  Per-host SFTP mutex appears as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;; the pool-wide mutex as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Right-click a row:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — dumps the last-owner&#039;s&lt;br /&gt;
  walkback plus each waiter&#039;s, formatted as plain text.  Use when&lt;br /&gt;
  a process is wedged in &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; inside&lt;br /&gt;
  &amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; and you need to see which SFTP&lt;br /&gt;
  request it is on.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — the whole table, for an&lt;br /&gt;
  email-this-to-someone diagnosis.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS over the wait-for graph, reports&lt;br /&gt;
  cycles.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
The SSH stack logs interesting events:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; on auto-reconnect after a dead connection.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when a pool entry is idle-evicted.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when an SSH key file cannot be parsed&lt;br /&gt;
  (e.g. legacy PEM, encrypted-without-agent) — the file is skipped,&lt;br /&gt;
  others tried.&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SFTP v3 only.&#039;&#039;&#039;  No SETSTAT (no remote chmod / chown / utime),&lt;br /&gt;
  no SSH_FXP_READLINK exposed (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; always&lt;br /&gt;
  &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; returns the regular&lt;br /&gt;
  stat info).  Several SFTPv5+ niceties are nevertheless picked up&lt;br /&gt;
  via OpenSSH SSH_FXP_EXTENDED requests — see&lt;br /&gt;
  [[#OpenSSH SFTP extensions]] below.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation.&#039;&#039;&#039;  Two concurrent operations on the&lt;br /&gt;
  same host queue through the host mutex.  See [[#Future work]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; fallback has a TOCTOU window.&#039;&#039;&#039;  On&lt;br /&gt;
  servers that advertise &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; (every&lt;br /&gt;
  modern OpenSSH does), overwrite is atomic; on the rare server&lt;br /&gt;
  that does not, the receiver is emulated as delete-then-rename&lt;br /&gt;
  and another process can race in between.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; is heuristic.&#039;&#039;&#039;  Always&lt;br /&gt;
  returns &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (the accurate answer would cost&lt;br /&gt;
  three round-trips per directory icon, which made the original tree&lt;br /&gt;
  expansion unbearably slow).&lt;br /&gt;
&lt;br /&gt;
== Implementation details ==&lt;br /&gt;
&lt;br /&gt;
For readers wanting the architecture.  Five classes, top-down:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename subclass; the public&lt;br /&gt;
API.  Maps &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt; URLs to remote files; exposes&lt;br /&gt;
&amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3 protocol&lt;br /&gt;
(request/response codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Driven by SftpFilename.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH channel multiplexer&lt;br /&gt;
(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST).  One logical&lt;br /&gt;
session per Channel instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-level SSH client: opens the&lt;br /&gt;
transport, runs KEX, host-key check, userauth, then dispenses&lt;br /&gt;
Channels.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Wire layer.  Banner + KEXINIT&lt;br /&gt;
exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq,&lt;br /&gt;
heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH SFTP extensions ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (RFC draft-ietf-secsh-filexfer-02) is intentionally minimal.&lt;br /&gt;
OpenSSH ships an open-ended extension mechanism: the server lists&lt;br /&gt;
extension names it understands in its &amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;&lt;br /&gt;
reply, and the client invokes them via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt; packets carrying the extension&lt;br /&gt;
name as the first string.  Each extension is feature-detected via&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;; callers fall&lt;br /&gt;
back when the server doesn&#039;t advertise it.&lt;br /&gt;
&lt;br /&gt;
The stack uses four of the OpenSSH extensions today:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomic&lt;br /&gt;
  rename-with-overwrite.  Picked up automatically by&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt;; the delete-then-rename&lt;br /&gt;
  fallback only fires on servers that lack it.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — create a POSIX hard link.&lt;br /&gt;
  Exposed as &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX&lt;br /&gt;
  &amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-shape filesystem stats.  Exposed as&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;; the result is&lt;br /&gt;
  shape-compatible with &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;&lt;br /&gt;
  so callers can treat local and remote uniformly.  Drives the&lt;br /&gt;
  &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; menu entry described at the&lt;br /&gt;
  top of this page.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — flush server-side write buffer&lt;br /&gt;
  to disk on an open handle.  Available on the low-level&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt;; not yet plumbed&lt;br /&gt;
  into a Filename-level &amp;quot;durable write&amp;quot; API.&lt;br /&gt;
&lt;br /&gt;
The remaining OpenSSH extensions&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
are recognised in the advertised-extensions list but not wrapped at&lt;br /&gt;
Filename level — there&#039;s no Filename-side caller for them yet.&lt;br /&gt;
&lt;br /&gt;
=== Connection pooling ===&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt; instance pointing at the same&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; triple shares one&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; plus one &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Pool is class-side, guarded by a single&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy bring-up&#039;&#039;&#039; — TCP + KEX + userauth + SFTP INIT happens&lt;br /&gt;
  on the first SFTP operation, not on &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation&#039;&#039;&#039; — SFTP requests on a given host&lt;br /&gt;
  are serialised through a &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; named&lt;br /&gt;
  &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; (visible in&lt;br /&gt;
  SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle eviction&#039;&#039;&#039; — unused for longer than&lt;br /&gt;
  &amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt;, the entry is proactively&lt;br /&gt;
  closed + reopened on the next access.&lt;br /&gt;
* &#039;&#039;&#039;Auto-reconnect&#039;&#039;&#039; — a transport-level failure (broken pipe,&lt;br /&gt;
  EOF, MNU on nil socket) evicts the dead pool entry, opens a&lt;br /&gt;
  fresh client, retries the request &#039;&#039;&#039;once&#039;&#039;&#039;.  Application-level&lt;br /&gt;
  SFTP STATUS errors propagate immediately.&lt;br /&gt;
&lt;br /&gt;
== Future work ==&lt;br /&gt;
&lt;br /&gt;
Tracked but not yet implemented:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-channel parallelism per host&#039;&#039;&#039; — today one TCP +&lt;br /&gt;
  one SFTP channel per host means N concurrent requests&lt;br /&gt;
  serialise.  Pipelining over multiple SshClients in the pool&lt;br /&gt;
  (preferred), or a transport-level reader process demultiplexing&lt;br /&gt;
  to per-channel inboxes, would let the tree pane keep listing&lt;br /&gt;
  while the content pane reads a large file.&lt;br /&gt;
* &#039;&#039;&#039;Accurate &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR&lt;br /&gt;
  + READ_DIR (first batch only) + CLOSE — three RTTs per probe;&lt;br /&gt;
  needs SftpClient to pipeline requests before this pays off.&lt;br /&gt;
* &#039;&#039;&#039;SFTP v5/v6 negotiation&#039;&#039;&#039; for extended attrs and FTP-style&lt;br /&gt;
  canonicalisation.  (Atomic-overwrite rename is already handled&lt;br /&gt;
  via the OpenSSH &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; extension;&lt;br /&gt;
  see [[#OpenSSH SFTP extensions]].)&lt;br /&gt;
&lt;br /&gt;
= Command Shell =&lt;br /&gt;
&lt;br /&gt;
Local command shell on this expecco machine.  Typical applications:&lt;br /&gt;
local command-line, running a local helper tool, bridging a&lt;br /&gt;
remote workflow to a local utility.&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No credentials, no network — runs as the expecco process&#039;s own&lt;br /&gt;
user.  Output streams to expecco&#039;s log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warning]] &#039;&#039;&#039;Telnet is a legacy protocol&lt;br /&gt;
with no encryption.&#039;&#039;&#039; Passwords are transmitted in plain text on&lt;br /&gt;
the wire; anyone on the network path can read them.  Use Telnet&lt;br /&gt;
ONLY when the target device has no other option (typically: old&lt;br /&gt;
industrial controllers, lab instruments, embedded measurement&lt;br /&gt;
equipment without an SSH stack).  For everything else use&lt;br /&gt;
[[#SSH and SFTP]].&lt;br /&gt;
&lt;br /&gt;
The expecco plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (internal demo)&lt;br /&gt;
&lt;br /&gt;
The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream&lt;br /&gt;
over TCP, with in-band control sequences for terminal options.&lt;br /&gt;
A connection is established to a target host:port; after optional&lt;br /&gt;
in-band login, both sides can send data.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent&lt;br /&gt;
  forwarding, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of&lt;br /&gt;
  this stack.&lt;br /&gt;
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack&lt;br /&gt;
  for its HTTPS transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Network]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31293</id>
		<title>Remote Access</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31293"/>
		<updated>2026-05-26T09:53:04Z</updated>

		<summary type="html">&lt;p&gt;Sv: Add Filesystem Info feature; document OpenSSH SFTP extensions (posix-rename, hardlink, statvfs, fsync); correct stale limitations/future-work entries.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Remote Access|Remote Access/en}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere&lt;br /&gt;
  Dateiübertragung über einen SSH-2-Tunnel.  Reine&lt;br /&gt;
  Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;;&lt;br /&gt;
  keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles&lt;br /&gt;
  mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen&lt;br /&gt;
  Maschine.  Für die Anbindung lokaler Werkzeuge und für die&lt;br /&gt;
  lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine&lt;br /&gt;
  Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur&lt;br /&gt;
  einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff&lt;br /&gt;
  (entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung,&lt;br /&gt;
  ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
  &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
  erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
  Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet vier Aktionen — die&lt;br /&gt;
drei SSH-spezifischen sind nur bei geladener SSH-Bibliothek&lt;br /&gt;
sichtbar:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den&lt;br /&gt;
  Schlüsselerzeugungs-Dialog, siehe&lt;br /&gt;
  [[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal&lt;br /&gt;
  zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP&lt;br /&gt;
  auf ein entferntes Dateisystem.&lt;br /&gt;
* &#039;&#039;&#039;Filesystem Info...&#039;&#039;&#039; — zeigt Größe, freien Platz und Belegung&lt;br /&gt;
  des Dateisystems, das das aktuell angezeigte Verzeichnis enthält.&lt;br /&gt;
  Funktioniert einheitlich für lokale und SFTP-Pfade; bei SFTP&lt;br /&gt;
  setzt der Aufruf voraus, daß der Server die Erweiterung&lt;br /&gt;
  &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; ankündigt (jedes moderne OpenSSH&lt;br /&gt;
  tut das).  Größen werden in IEC-Binäreinheiten ausgegeben (MiB,&lt;br /&gt;
  GiB, TiB) — gewählt wird die größte Einheit, die einen Wert ≥ 1&lt;br /&gt;
  liefert, damit ein TB-großes Volume als &#039;&#039;X TiB&#039;&#039; statt&lt;br /&gt;
  &#039;&#039;10240 GiB&#039;&#039; erscheint.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine&lt;br /&gt;
  SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary&lt;br /&gt;
  (PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; —&lt;br /&gt;
  dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet&lt;br /&gt;
  (Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
   &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
   zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei&lt;br /&gt;
   UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im&lt;br /&gt;
   Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im&lt;br /&gt;
  Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei&lt;br /&gt;
  unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da&lt;br /&gt;
  das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten&lt;br /&gt;
  Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf&lt;br /&gt;
  &#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs-&lt;br /&gt;
  variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;,&lt;br /&gt;
  &#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue&lt;br /&gt;
  Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
  OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
  zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen&lt;br /&gt;
  Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine&lt;br /&gt;
  Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist&lt;br /&gt;
  möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback&lt;br /&gt;
  des letzten Eigners samt aller Waiter als Text in die&lt;br /&gt;
  Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
  &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
  &amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine&lt;br /&gt;
  E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet&lt;br /&gt;
  Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter&lt;br /&gt;
  Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht&lt;br /&gt;
  geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes&lt;br /&gt;
  chmod / chown / utime), kein SSH_FXP_READLINK exponiert&lt;br /&gt;
  (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
  &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  Einige&lt;br /&gt;
  SFTPv5+-Annehmlichkeiten werden dennoch über OpenSSH-spezifische&lt;br /&gt;
  SSH_FXP_EXTENDED-Aufrufe nutzbar — siehe&lt;br /&gt;
  [[#OpenSSH-SFTP-Erweiterungen]] weiter unten.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen&lt;br /&gt;
  am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt;-Fallback hat ein TOCTOU-Fenster.&#039;&#039;&#039;&lt;br /&gt;
  Bei Servern, die &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; ankündigen&lt;br /&gt;
  (jedes moderne OpenSSH tut das), ist das Überschreiben atomar.&lt;br /&gt;
  Beim seltenen Server, der das nicht tut, wird auf&lt;br /&gt;
  Delete-dann-Rename ausgewichen und ein anderer Prozess kann sich&lt;br /&gt;
  dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039;&lt;br /&gt;
  Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort&lt;br /&gt;
  würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das&lt;br /&gt;
  ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== OpenSSH-SFTP-Erweiterungen ===&lt;br /&gt;
&lt;br /&gt;
SFTP v3 (Entwurf draft-ietf-secsh-filexfer-02) ist bewusst&lt;br /&gt;
minimal gehalten.  OpenSSH bringt einen offenen&lt;br /&gt;
Erweiterungsmechanismus mit: der Server listet im&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_VERSION&amp;lt;/code&amp;gt;-Reply die Erweiterungsnamen auf, die&lt;br /&gt;
er versteht, und der Client ruft sie über&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_FXP_EXTENDED(200)&amp;lt;/code&amp;gt;-Pakete mit dem&lt;br /&gt;
Erweiterungsnamen als erstem String auf.  Jede Erweiterung wird&lt;br /&gt;
über &amp;lt;code&amp;gt;SSH::SftpClient&amp;amp;gt;&amp;amp;gt;supportsExtension:&amp;lt;/code&amp;gt;&lt;br /&gt;
feature-detektiert; Aufrufer fallen zurück, wenn der Server sie&lt;br /&gt;
nicht ankündigt.&lt;br /&gt;
&lt;br /&gt;
Der Stack nutzt heute vier OpenSSH-Erweiterungen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; — atomares&lt;br /&gt;
  rename-mit-Überschreiben.  Wird automatisch von&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;renameTo:&amp;lt;/code&amp;gt; aufgegriffen; die&lt;br /&gt;
  Delete-dann-Rename-Fallback-Variante kommt nur bei Servern zum&lt;br /&gt;
  Einsatz, die die Erweiterung nicht haben.&lt;br /&gt;
* &amp;lt;code&amp;gt;hardlink@openssh.com&amp;lt;/code&amp;gt; — Erzeugt einen POSIX-Hardlink.&lt;br /&gt;
  Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;createHardLinkAs:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;statvfs@openssh.com&amp;lt;/code&amp;gt; — POSIX-&lt;br /&gt;
  &amp;lt;code&amp;gt;statvfs(3)&amp;lt;/code&amp;gt;-typische Dateisystem-Statistik.&lt;br /&gt;
  Verfügbar als &amp;lt;code&amp;gt;SftpFilename&amp;amp;gt;&amp;amp;gt;fileSystemInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
  das Ergebnis ist form-kompatibel zu&lt;br /&gt;
  &amp;lt;code&amp;gt;OperatingSystem getDiskInfoOf:&amp;lt;/code&amp;gt;, sodass Aufrufer&lt;br /&gt;
  lokale und entfernte Pfade einheitlich behandeln können.&lt;br /&gt;
  Treibt den Menü-Eintrag &#039;&#039;&#039;Tools &amp;amp;rarr; Filesystem Info...&#039;&#039;&#039; an,&lt;br /&gt;
  der am Anfang dieser Seite beschrieben ist.&lt;br /&gt;
* &amp;lt;code&amp;gt;fsync@openssh.com&amp;lt;/code&amp;gt; — schreibt den&lt;br /&gt;
  serverseitigen Schreibpuffer eines geöffneten Handles auf&lt;br /&gt;
  Platte.  Liegt als&lt;br /&gt;
  &amp;lt;code&amp;gt;SftpClient&amp;amp;gt;&amp;amp;gt;fsyncHandle:&amp;lt;/code&amp;gt; bereit; noch nicht&lt;br /&gt;
  in eine &amp;quot;Durable-Write&amp;quot;-API auf Filename-Ebene eingebunden.&lt;br /&gt;
&lt;br /&gt;
Die verbleibenden OpenSSH-Erweiterungen&lt;br /&gt;
(&amp;lt;code&amp;gt;lsetstat@openssh.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fstatvfs@openssh.com&amp;lt;/code&amp;gt;)&lt;br /&gt;
werden in der angekündigten Liste erkannt, aber nicht auf&lt;br /&gt;
Filename-Ebene gekapselt — es gibt dafür noch keinen&lt;br /&gt;
Filename-seitigen Aufrufer.&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen&lt;br /&gt;
  erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen&lt;br /&gt;
  bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt;&lt;br /&gt;
  mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
  serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
  &amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
  nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene&lt;br /&gt;
  (Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den&lt;br /&gt;
  Pool-Eintrag, öffnet einen frischen Client und wiederholt die&lt;br /&gt;
  Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus&lt;br /&gt;
  SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet&lt;br /&gt;
  eine TCP- plus eine SFTP-Verbindung pro Host, dass N&lt;br /&gt;
  gleichzeitige Anfragen serialisieren.  Pipelining über mehrere&lt;br /&gt;
  SshClients im Pool (bevorzugt) oder ein transport-seitiger&lt;br /&gt;
  Reader-Prozess, der eingehende Pakete in&lt;br /&gt;
  Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel&lt;br /&gt;
  erlauben, weiter aufzulisten, während das Inhalts-Panel eine&lt;br /&gt;
  große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR&lt;br /&gt;
  + READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro&lt;br /&gt;
  Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen&lt;br /&gt;
  kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für erweiterte Attribute und&lt;br /&gt;
  FTP-artige Kanonisierung.  (Atomares Überschreibungs-rename&lt;br /&gt;
  ist bereits über die OpenSSH-Erweiterung&lt;br /&gt;
  &amp;lt;code&amp;gt;posix-rename@openssh.com&amp;lt;/code&amp;gt; abgedeckt; siehe&lt;br /&gt;
  [[#OpenSSH-SFTP-Erweiterungen]].)&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY,&lt;br /&gt;
  Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack&lt;br /&gt;
  als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31292</id>
		<title>Remote Access/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/en&amp;diff=31292"/>
		<updated>2026-05-26T08:49:01Z</updated>

		<summary type="html">&lt;p&gt;Sv: Die Seite wurde neu angelegt: „ &amp;#039;&amp;#039;&amp;#039;Remote access&amp;#039;&amp;#039;&amp;#039; 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:  * &amp;#039;&amp;#039;&amp;#039;SSH and SFTP&amp;#039;&amp;#039;&amp;#039; (recommended) — encrypted shell + secure   file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation   in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on   OpenSS…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&#039;&#039;&#039;Remote access&#039;&#039;&#039; is the ability to drive a remote computer or&lt;br /&gt;
network from this expecco image — opening shells, running commands,&lt;br /&gt;
moving files, or driving a test target.  Three protocol families are&lt;br /&gt;
supported, listed in current-recommended order:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH and SFTP&#039;&#039;&#039; (recommended) — encrypted shell + secure&lt;br /&gt;
  file transfer over an SSH-2 tunnel.  Pure-Smalltalk implementation&lt;br /&gt;
  in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;; no external dependency on&lt;br /&gt;
  OpenSSL or libssh.  Use this for anything that touches credentials&lt;br /&gt;
  or sensitive payloads.&lt;br /&gt;
* &#039;&#039;&#039;Local Command Shell&#039;&#039;&#039; — fork + exec on the local machine.&lt;br /&gt;
  Used for local-tool integration and for the local end of a remote&lt;br /&gt;
  workflow that bridges via another protocol.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (legacy) — plain-text terminal session.  No&lt;br /&gt;
  encryption, passwords on the wire in clear.  Use only when the&lt;br /&gt;
  target hardware has no other option.&lt;br /&gt;
&lt;br /&gt;
= SSH and SFTP =&lt;br /&gt;
&lt;br /&gt;
The SSH stack covers the full SSH-2 protocol (RFC 4251–4254,&lt;br /&gt;
RFC 5656, RFC 8709, RFC 8731) plus OpenSSH&#039;s chacha20-poly1305&lt;br /&gt;
transport cipher and the SFTP v3 file-transfer subsystem&lt;br /&gt;
(draft-ietf-secsh-filexfer-02).  Two layers:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatic SSH access (remote&lt;br /&gt;
  &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY shell, agent forwarding, ProxyJump bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — a &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;&lt;br /&gt;
  subclass that lets the rest of ST/X treat a remote SFTP path the&lt;br /&gt;
  same way it treats a local file.&lt;br /&gt;
&lt;br /&gt;
The rest of this section is organised user-task-first: what the user&lt;br /&gt;
sees and does, the expecco-library hooks below that, then the&lt;br /&gt;
implementation detail at the end for the curious.&lt;br /&gt;
&lt;br /&gt;
== From the FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Open the location dropdown and paste an &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt; URL.&lt;br /&gt;
The browser tab populates as if it were a local path.  Tree&lt;br /&gt;
expansion, column sort (name / size / mtime), preview, and&lt;br /&gt;
double-click-to-open-in-editor all behave normally.  The first&lt;br /&gt;
click on a host takes ~200–500 ms (TCP + KEX + auth); subsequent&lt;br /&gt;
clicks reuse the pooled connection.&lt;br /&gt;
&lt;br /&gt;
URL syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
User defaults to the local login name, port to 22, path to&lt;br /&gt;
&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Tools menu offers three SSH-related actions, all gated on the&lt;br /&gt;
SSH library being loaded:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — opens the same key-generation&lt;br /&gt;
  dialog described under [[#Generating an SSH key pair]] below.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — opens an interactive VT100 terminal to a&lt;br /&gt;
  remote host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — points this browser tab at a remote&lt;br /&gt;
  filesystem via SFTP.&lt;br /&gt;
&lt;br /&gt;
== From expecco actions ==&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) exposes the following test&lt;br /&gt;
actions to the expecco action palette:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — opens an SSH session&lt;br /&gt;
  via the platform&#039;s &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; binary (PuTTY&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; on Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; — same&lt;br /&gt;
  but with explicit public-key authentication.&lt;br /&gt;
&lt;br /&gt;
To run these you need a configured keypair (private key on this&lt;br /&gt;
machine, public key in the remote host&#039;s&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;).  Generate one via the dialog&lt;br /&gt;
below or via &amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The plugin also adds a settings page at &#039;&#039;&#039;Extras → Settings →&lt;br /&gt;
Plugins → Remote Access — SSH Keys&#039;&#039;&#039; carrying a single&lt;br /&gt;
&#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; button that opens the same dialog.&lt;br /&gt;
&lt;br /&gt;
== Generating an SSH key pair ==&lt;br /&gt;
&lt;br /&gt;
=== The dialog (FileBrowserV2 / settings page) ===&lt;br /&gt;
&lt;br /&gt;
The dialog asks for all parameters in one form:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — embedded in the generated key (defaults to&lt;br /&gt;
  &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — writes &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt;&lt;br /&gt;
   (or wherever) plus a matching &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt; companion.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — writes the file and&lt;br /&gt;
   also hands the key to the running ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — key lives in agent memory only;&lt;br /&gt;
   gone on agent restart.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — full path; disabled in agent-only mode.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — empty leaves the on-disk file&lt;br /&gt;
  unencrypted (agent-only mode ignores the passphrase, since the&lt;br /&gt;
  OpenSSH agent wire protocol carries only the decrypted key).&lt;br /&gt;
&lt;br /&gt;
On &#039;&#039;&#039;Generate&#039;&#039;&#039;, the public-key line (the same&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt; string ssh-keygen&lt;br /&gt;
emits) is copied to the system clipboard for pasting into the&lt;br /&gt;
remote host&#039;s &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== From a workspace ===&lt;br /&gt;
&lt;br /&gt;
For headless deployments, sandboxed builds, or scripts,&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; exposes a pure-Smalltalk key generator&lt;br /&gt;
that produces output bit-compatible with&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Save passphrase-encrypted to disk&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ AND load into the running agent&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Print the public-key line to paste into authorized_keys&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keys generated this way are interoperable with OpenSSH&#039;s own&lt;br /&gt;
tooling (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; re-derives the public&lt;br /&gt;
key, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; changes the passphrase,&lt;br /&gt;
etc.).&lt;br /&gt;
&lt;br /&gt;
=== Using the shell tools instead ===&lt;br /&gt;
&lt;br /&gt;
The traditional path also works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparing ssh-agent ==&lt;br /&gt;
&lt;br /&gt;
The agent path is strongly preferred over reading raw keyfiles: it&lt;br /&gt;
keeps encrypted private keys unlocked once per session, and handles&lt;br /&gt;
identities (hardware-token-backed keys, KeePassXC entries) that&lt;br /&gt;
ST/X should never see directly.&lt;br /&gt;
&lt;br /&gt;
ST/X picks the agent path automatically when&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is set in the process environment&lt;br /&gt;
&#039;&#039;&#039;at the time stx is launched&#039;&#039;&#039;.  Setting it later from a&lt;br /&gt;
workspace does not help.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Most desktop distributions launch an agent automatically as part of&lt;br /&gt;
the session (gnome-keyring on GNOME, ssh-agent.service on systemd,&lt;br /&gt;
KWallet on KDE).  Verify in a terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh or similar&lt;br /&gt;
ssh-add -l             # lists loaded identities&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # load yours if not loaded&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If no agent runs at all, add this snippet to your shell rc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc or ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X must be launched from a shell that has seen this rc — a&lt;br /&gt;
desktop launcher started from the file manager does NOT inherit&lt;br /&gt;
the variable.  Wrap the stx start command in a small script under&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt; that sources the rc first.&lt;br /&gt;
&lt;br /&gt;
The Remote Access settings page (&#039;&#039;&#039;Extras → Settings → Plugins&lt;br /&gt;
→ Remote Access — SSH Keys&#039;&#039;&#039;) shows whether the running image&lt;br /&gt;
sees an agent.&lt;br /&gt;
&lt;br /&gt;
==== Permanent setup via systemd ====&lt;br /&gt;
&lt;br /&gt;
For a truly cross-session agent (survives desktop logouts, comes&lt;br /&gt;
up automatically at next login), enable the per-user systemd&lt;br /&gt;
unit shipped with most distros&#039; &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
package:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then point &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the user-service socket&lt;br /&gt;
in your shell rc (this replaces the &amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;&lt;br /&gt;
snippet above):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
To skip the manual &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; step, let OpenSSH load&lt;br /&gt;
keys into the agent automatically the first time they are needed.&lt;br /&gt;
Add to &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first SSH connection then prompts for the key passphrase&lt;br /&gt;
once and hands the unlocked key to the agent; subsequent&lt;br /&gt;
connections use the cached identity without prompting.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ ships native OpenSSH including an agent service.&lt;br /&gt;
One-time setup:&lt;br /&gt;
&lt;br /&gt;
# Open &#039;&#039;&#039;Services&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) as Administrator.&lt;br /&gt;
# Find &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039;, set Startup Type to&lt;br /&gt;
  &#039;&#039;&#039;Automatic&#039;&#039;&#039;, click &#039;&#039;&#039;Start&#039;&#039;&#039;.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The Windows OpenSSH agent listens on a named pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), not a Unix socket.  ST/X&lt;br /&gt;
supports both transports, but Windows ssh-add does &#039;&#039;&#039;not&#039;&#039;&#039; set&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; for you.  Add it manually:&lt;br /&gt;
&lt;br /&gt;
# Press {{Key|Win}} → type &amp;quot;environment&amp;quot; → &amp;quot;Edit the system environment variables&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Environment Variables&#039;&#039;&#039; → under &#039;&#039;&#039;User variables&#039;&#039;&#039;, &#039;&#039;&#039;New&#039;&#039;&#039;.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Value: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Log out and back in (or restart stx) so the new env propagates.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell quick-setup ====&lt;br /&gt;
&lt;br /&gt;
The same setup from an &#039;&#039;&#039;elevated&#039;&#039;&#039; PowerShell prompt, for&lt;br /&gt;
scripts or unattended provisioning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Start the agent now AND on every reboot (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# Persist SSH_AUTH_SOCK for the user (survives reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Load a key (prompts for the passphrase if the file is encrypted).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a one-shot agent start without making it persistent (e.g.&lt;br /&gt;
single-session test), drop the &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt; line and&lt;br /&gt;
just run &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt;.  The env-var line&lt;br /&gt;
can also be omitted if &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; is only needed&lt;br /&gt;
in the current shell — use &amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
instead for that session-local form.&lt;br /&gt;
&lt;br /&gt;
On stripped-down Windows installs the ssh-agent service may not&lt;br /&gt;
be present.  Add it once via &#039;&#039;&#039;Settings → Apps → Optional&lt;br /&gt;
features → OpenSSH Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative agents:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — uses its own protocol; NOT supported by&lt;br /&gt;
  ST/X&#039;s SSH::Agent.  Migrate the keys to OpenSSH.&lt;br /&gt;
* &#039;&#039;&#039;Git for Windows ssh-agent&#039;&#039;&#039; — works; point&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; at the socket it publishes.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — a ST/X inside WSL sees WSL&#039;s Linux agent normally;&lt;br /&gt;
  a ST/X on the Windows side does not.  Bridging needs a helper&lt;br /&gt;
  like &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify in the Remote Access settings page&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
the agent indicator there reports whether the running image sees&lt;br /&gt;
the agent.&lt;br /&gt;
&lt;br /&gt;
==== Auto-loading keys on first use ====&lt;br /&gt;
&lt;br /&gt;
Windows OpenSSH does &#039;&#039;&#039;not&#039;&#039;&#039; persist agent-loaded keys across&lt;br /&gt;
agent restarts.  To avoid running &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; manually&lt;br /&gt;
after each reboot, add the same lazy-load configuration to&lt;br /&gt;
&amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH then loads the key into the agent on first use (prompts&lt;br /&gt;
for the passphrase once) and reuses it for the rest of the&lt;br /&gt;
session.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
All tunables are class-side on &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Default !! What it controls&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 min) || How long a pooled&lt;br /&gt;
connection sits idle before the next access proactively closes +&lt;br /&gt;
reopens it.  Just under typical sshd&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; so we recycle&lt;br /&gt;
before the server TCP-RESETs us.  Pass &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; to restore&lt;br /&gt;
the default.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Max age (s) of a&lt;br /&gt;
cached STAT before &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; refetches.  Parent&lt;br /&gt;
listDir always re-stamps fresh attrs onto children, so navigating&lt;br /&gt;
an open directory does not pay the TTL.  Set to &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to&lt;br /&gt;
disable caching.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (action) || Tears down every&lt;br /&gt;
pooled connection.  Useful after a known-bad network event, before&lt;br /&gt;
a deliberate identity swap, or as part of a clean image shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnostics ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
Open &amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; from the Launcher&#039;s &amp;quot;Status&amp;quot;&lt;br /&gt;
sub-menu.  Per-host SFTP mutex appears as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;; the pool-wide mutex as&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Right-click a row:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — dumps the last-owner&#039;s&lt;br /&gt;
  walkback plus each waiter&#039;s, formatted as plain text.  Use when&lt;br /&gt;
  a process is wedged in &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; inside&lt;br /&gt;
  &amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; and you need to see which SFTP&lt;br /&gt;
  request it is on.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — the whole table, for an&lt;br /&gt;
  email-this-to-someone diagnosis.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS over the wait-for graph, reports&lt;br /&gt;
  cycles.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
The SSH stack logs interesting events:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; on auto-reconnect after a dead connection.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when a pool entry is idle-evicted.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; when an SSH key file cannot be parsed&lt;br /&gt;
  (e.g. legacy PEM, encrypted-without-agent) — the file is skipped,&lt;br /&gt;
  others tried.&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SFTP v3 only.&#039;&#039;&#039;  No SETSTAT (no remote chmod / chown / utime),&lt;br /&gt;
  no SSH_FXP_READLINK exposed (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; always&lt;br /&gt;
  &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; returns the regular&lt;br /&gt;
  stat info).  SFTPv5+ features (atomic-overwrite rename via&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_FXF_OVERWRITE&amp;lt;/code&amp;gt;) not supported.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation.&#039;&#039;&#039;  Two concurrent operations on the&lt;br /&gt;
  same host queue through the host mutex.  See [[#Future work]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; has a TOCTOU window.&#039;&#039;&#039;  POSIX-style&lt;br /&gt;
  overwrite is emulated as delete-then-rename; another process can&lt;br /&gt;
  race in between.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; is heuristic.&#039;&#039;&#039;  Always&lt;br /&gt;
  returns &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (the accurate answer would cost&lt;br /&gt;
  three round-trips per directory icon, which made the original tree&lt;br /&gt;
  expansion unbearably slow).&lt;br /&gt;
&lt;br /&gt;
== Implementation details ==&lt;br /&gt;
&lt;br /&gt;
For readers wanting the architecture.  Five classes, top-down:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Class !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename subclass; the public&lt;br /&gt;
API.  Maps &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt; URLs to remote files; exposes&lt;br /&gt;
&amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; etc.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3 protocol&lt;br /&gt;
(request/response codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Driven by SftpFilename.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH channel multiplexer&lt;br /&gt;
(CHANNEL_OPEN, DATA, EOF, CLOSE, WINDOW_ADJUST).  One logical&lt;br /&gt;
session per Channel instance.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-level SSH client: opens the&lt;br /&gt;
transport, runs KEX, host-key check, userauth, then dispenses&lt;br /&gt;
Channels.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Wire layer.  Banner + KEXINIT&lt;br /&gt;
exchange, ChaCha20-Poly1305 packet framing, sendSeq / recvSeq,&lt;br /&gt;
heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Connection pooling ===&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt; instance pointing at the same&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; triple shares one&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; plus one &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Pool is class-side, guarded by a single&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy bring-up&#039;&#039;&#039; — TCP + KEX + userauth + SFTP INIT happens&lt;br /&gt;
  on the first SFTP operation, not on &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Per-host serialisation&#039;&#039;&#039; — SFTP requests on a given host&lt;br /&gt;
  are serialised through a &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt; named&lt;br /&gt;
  &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt; (visible in&lt;br /&gt;
  SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle eviction&#039;&#039;&#039; — unused for longer than&lt;br /&gt;
  &amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt;, the entry is proactively&lt;br /&gt;
  closed + reopened on the next access.&lt;br /&gt;
* &#039;&#039;&#039;Auto-reconnect&#039;&#039;&#039; — a transport-level failure (broken pipe,&lt;br /&gt;
  EOF, MNU on nil socket) evicts the dead pool entry, opens a&lt;br /&gt;
  fresh client, retries the request &#039;&#039;&#039;once&#039;&#039;&#039;.  Application-level&lt;br /&gt;
  SFTP STATUS errors propagate immediately.&lt;br /&gt;
&lt;br /&gt;
== Future work ==&lt;br /&gt;
&lt;br /&gt;
Tracked but not yet implemented:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-channel parallelism per host&#039;&#039;&#039; — today one TCP +&lt;br /&gt;
  one SFTP channel per host means N concurrent requests&lt;br /&gt;
  serialise.  Pipelining over multiple SshClients in the pool&lt;br /&gt;
  (preferred), or a transport-level reader process demultiplexing&lt;br /&gt;
  to per-channel inboxes, would let the tree pane keep listing&lt;br /&gt;
  while the content pane reads a large file.&lt;br /&gt;
* &#039;&#039;&#039;Accurate &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR&lt;br /&gt;
  + READ_DIR (first batch only) + CLOSE — three RTTs per probe;&lt;br /&gt;
  needs SftpClient to pipeline requests before this pays off.&lt;br /&gt;
* &#039;&#039;&#039;SFTP v5/v6 negotiation&#039;&#039;&#039; for atomic-overwrite rename,&lt;br /&gt;
  extended attrs, FTP-style canonicalisation.&lt;br /&gt;
&lt;br /&gt;
= Command Shell =&lt;br /&gt;
&lt;br /&gt;
Local command shell on this expecco machine.  Typical applications:&lt;br /&gt;
local command-line, running a local helper tool, bridging a&lt;br /&gt;
remote workflow to a local utility.&lt;br /&gt;
&lt;br /&gt;
The Expecco RemoteAccess plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No credentials, no network — runs as the expecco process&#039;s own&lt;br /&gt;
user.  Output streams to expecco&#039;s log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warning]] &#039;&#039;&#039;Telnet is a legacy protocol&lt;br /&gt;
with no encryption.&#039;&#039;&#039; Passwords are transmitted in plain text on&lt;br /&gt;
the wire; anyone on the network path can read them.  Use Telnet&lt;br /&gt;
ONLY when the target device has no other option (typically: old&lt;br /&gt;
industrial controllers, lab instruments, embedded measurement&lt;br /&gt;
equipment without an SSH stack).  For everything else use&lt;br /&gt;
[[#SSH and SFTP]].&lt;br /&gt;
&lt;br /&gt;
The expecco plugin exposes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (internal demo)&lt;br /&gt;
&lt;br /&gt;
The Telnet protocol (RFC 854) is a bidirectional 8-bit byte stream&lt;br /&gt;
over TCP, with in-band control sequences for terminal options.&lt;br /&gt;
A connection is established to a target host:port; after optional&lt;br /&gt;
in-band login, both sides can send data.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client/en|SSH::Client]] — the SSH layer (exec, TTY, agent&lt;br /&gt;
  forwarding, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2/en|FileBrowserV2]] — the main UI client of&lt;br /&gt;
  this stack.&lt;br /&gt;
* [[ClaudeCode plugin/en|Claude Code]] — uses the same SSH stack&lt;br /&gt;
  for its HTTPS transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Network]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access/&amp;diff=31291</id>
		<title>Remote Access/</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access/&amp;diff=31291"/>
		<updated>2026-05-26T08:46:04Z</updated>

		<summary type="html">&lt;p&gt;Sv: Die Seite wurde neu angelegt: „&amp;#039;&amp;#039;&amp;#039;Fernzugriff&amp;#039;&amp;#039;&amp;#039; bezeichnet die Möglichkeit, einen entfernten Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien sind unterstützt, in absteigender Empfehlungsreihenfolge:  * &amp;#039;&amp;#039;&amp;#039;SSH und SFTP&amp;#039;&amp;#039;&amp;#039; (empfohlen) — verschlüsselte Shell und sichere   Dateiübertragung über einen SSH-2-Tunnel.  Reine   Smal…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere&lt;br /&gt;
  Dateiübertragung über einen SSH-2-Tunnel.  Reine&lt;br /&gt;
  Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;;&lt;br /&gt;
  keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles&lt;br /&gt;
  mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen&lt;br /&gt;
  Maschine.  Für die Anbindung lokaler Werkzeuge und für die&lt;br /&gt;
  lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine&lt;br /&gt;
  Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur&lt;br /&gt;
  einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff&lt;br /&gt;
  (entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung,&lt;br /&gt;
  ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
  &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
  erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
  Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet drei SSH-Aktionen,&lt;br /&gt;
sichtbar nur bei geladener SSH-Bibliothek:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den&lt;br /&gt;
  Schlüsselerzeugungs-Dialog, siehe&lt;br /&gt;
  [[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal&lt;br /&gt;
  zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP&lt;br /&gt;
  auf ein entferntes Dateisystem.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine&lt;br /&gt;
  SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary&lt;br /&gt;
  (PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; —&lt;br /&gt;
  dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet&lt;br /&gt;
  (Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
   &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
   zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei&lt;br /&gt;
   UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im&lt;br /&gt;
   Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im&lt;br /&gt;
  Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei&lt;br /&gt;
  unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da&lt;br /&gt;
  das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten&lt;br /&gt;
  Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf&lt;br /&gt;
  &#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs-&lt;br /&gt;
  variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;,&lt;br /&gt;
  &#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue&lt;br /&gt;
  Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
  OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
  zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen&lt;br /&gt;
  Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine&lt;br /&gt;
  Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist&lt;br /&gt;
  möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback&lt;br /&gt;
  des letzten Eigners samt aller Waiter als Text in die&lt;br /&gt;
  Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
  &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
  &amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine&lt;br /&gt;
  E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet&lt;br /&gt;
  Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter&lt;br /&gt;
  Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht&lt;br /&gt;
  geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes&lt;br /&gt;
  chmod / chown / utime), kein SSH_FXP_READLINK exponiert&lt;br /&gt;
  (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
  &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  SFTPv5+-Features&lt;br /&gt;
  (atomares Überschreibungs-rename via&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_FXF_OVERWRITE&amp;lt;/code&amp;gt;) werden nicht unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen&lt;br /&gt;
  am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; hat ein TOCTOU-Fenster.&#039;&#039;&#039;&lt;br /&gt;
  POSIX-typisches Überschreiben wird als Delete-dann-Rename&lt;br /&gt;
  emuliert; ein anderer Prozess kann sich dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039;&lt;br /&gt;
  Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort&lt;br /&gt;
  würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das&lt;br /&gt;
  ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen&lt;br /&gt;
  erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen&lt;br /&gt;
  bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt;&lt;br /&gt;
  mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
  serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
  &amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
  nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene&lt;br /&gt;
  (Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den&lt;br /&gt;
  Pool-Eintrag, öffnet einen frischen Client und wiederholt die&lt;br /&gt;
  Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus&lt;br /&gt;
  SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet&lt;br /&gt;
  eine TCP- plus eine SFTP-Verbindung pro Host, dass N&lt;br /&gt;
  gleichzeitige Anfragen serialisieren.  Pipelining über mehrere&lt;br /&gt;
  SshClients im Pool (bevorzugt) oder ein transport-seitiger&lt;br /&gt;
  Reader-Prozess, der eingehende Pakete in&lt;br /&gt;
  Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel&lt;br /&gt;
  erlauben, weiter aufzulisten, während das Inhalts-Panel eine&lt;br /&gt;
  große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR&lt;br /&gt;
  + READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro&lt;br /&gt;
  Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen&lt;br /&gt;
  kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für atomares Überschreibungs-&lt;br /&gt;
  rename, erweiterte Attribute und FTP-artige Kanonisierung.&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY,&lt;br /&gt;
  Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack&lt;br /&gt;
  als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31290</id>
		<title>Remote Access</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Remote_Access&amp;diff=31290"/>
		<updated>2026-05-26T08:44:55Z</updated>

		<summary type="html">&lt;p&gt;Sv: Native SSH/SFTP&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Fernzugriff&#039;&#039;&#039; bezeichnet die Möglichkeit, einen entfernten&lt;br /&gt;
Rechner oder ein entferntes Netzwerk aus diesem expecco-Image heraus&lt;br /&gt;
zu bedienen — Shells zu öffnen, Befehle abzusetzen, Dateien zu&lt;br /&gt;
verschieben oder ein Testgerät anzusteuern.  Drei Protokoll-Familien&lt;br /&gt;
sind unterstützt, in absteigender Empfehlungsreihenfolge:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SSH und SFTP&#039;&#039;&#039; (empfohlen) — verschlüsselte Shell und sichere&lt;br /&gt;
  Dateiübertragung über einen SSH-2-Tunnel.  Reine&lt;br /&gt;
  Smalltalk-Implementierung in &amp;lt;code&amp;gt;exept:libcrypt/ssh&amp;lt;/code&amp;gt;;&lt;br /&gt;
  keine externe Abhängigkeit von OpenSSL oder libssh.  Für alles&lt;br /&gt;
  mit Zugangsdaten oder sensiblen Nutzdaten.&lt;br /&gt;
* &#039;&#039;&#039;Lokale Kommando-Shell&#039;&#039;&#039; — fork + exec auf der lokalen&lt;br /&gt;
  Maschine.  Für die Anbindung lokaler Werkzeuge und für die&lt;br /&gt;
  lokale Seite eines hybriden Workflows.&lt;br /&gt;
* &#039;&#039;&#039;Telnet&#039;&#039;&#039; (veraltet) — Klartext-Terminalsitzung.  Keine&lt;br /&gt;
  Verschlüsselung, Passwörter im Klartext auf der Leitung.  Nur&lt;br /&gt;
  einsetzen, wenn die Gegenstelle keine Alternative bietet.&lt;br /&gt;
&lt;br /&gt;
= SSH und SFTP =&lt;br /&gt;
&lt;br /&gt;
Der SSH-Stack deckt das vollständige SSH-2-Protokoll ab&lt;br /&gt;
(RFC 4251–4254, RFC 5656, RFC 8709, RFC 8731) inklusive der&lt;br /&gt;
chacha20-poly1305-Transportchiffrierung von OpenSSH sowie das&lt;br /&gt;
SFTP-v3-Subsystem (draft-ietf-secsh-filexfer-02).  Zwei Schichten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt;&#039;&#039;&#039; — programmatischer SSH-Zugriff&lt;br /&gt;
  (entferntes &amp;lt;code&amp;gt;exec&amp;lt;/code&amp;gt;, TTY-Shell, Agent-Weiterleitung,&lt;br /&gt;
  ProxyJump-Bastion).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt;&#039;&#039;&#039; — eine&lt;br /&gt;
  &amp;lt;code&amp;gt;Filename&amp;lt;/code&amp;gt;-Unterklasse, die es dem restlichen ST/X&lt;br /&gt;
  erlaubt, einen entfernten SFTP-Pfad zu behandeln wie eine lokale&lt;br /&gt;
  Datei.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Abschnitte sind nutzeraufgaben-zuerst aufgebaut:&lt;br /&gt;
zuerst das, was der Anwender sieht und tut, darunter die&lt;br /&gt;
expecco-Bibliotheks-Anbindung, ganz unten Implementierungsdetails&lt;br /&gt;
für Interessierte.&lt;br /&gt;
&lt;br /&gt;
== Aus dem FileBrowserV2 ==&lt;br /&gt;
&lt;br /&gt;
Im Adress-Dropdown eine &amp;lt;code&amp;gt;sftp://&amp;lt;/code&amp;gt;-URL einfügen.  Der&lt;br /&gt;
Browser-Tab füllt sich wie bei einem lokalen Pfad.&lt;br /&gt;
Baum-Ausklappen, Spaltensortierung (Name / Größe / mtime),&lt;br /&gt;
Vorschau und Doppelklick zum Öffnen im Editor verhalten sich&lt;br /&gt;
normal.  Der erste Klick auf einen Host dauert ~200–500 ms&lt;br /&gt;
(TCP + KEX + Auth); folgende Klicks nutzen die gepoolte&lt;br /&gt;
Verbindung weiter.&lt;br /&gt;
&lt;br /&gt;
URL-Syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sftp://[user@]host[:port]/remote/path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fehlt &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, wird der lokale Login-Name verwendet, Port&lt;br /&gt;
ist standardmäßig 22, Pfad standardmäßig &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Menü &#039;&#039;&#039;Tools&#039;&#039;&#039; im FileBrowserV2 bietet drei SSH-Aktionen,&lt;br /&gt;
sichtbar nur bei geladener SSH-Bibliothek:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039; — öffnet den&lt;br /&gt;
  Schlüsselerzeugungs-Dialog, siehe&lt;br /&gt;
  [[#Einen SSH-Schlüssel erzeugen]] unten.&lt;br /&gt;
* &#039;&#039;&#039;SSH Connect...&#039;&#039;&#039; — öffnet ein interaktives VT100-Terminal&lt;br /&gt;
  zu einem entfernten Host.&lt;br /&gt;
* &#039;&#039;&#039;SFTP Connect...&#039;&#039;&#039; — navigiert diesen Browser-Tab über SFTP&lt;br /&gt;
  auf ein entferntes Dateisystem.&lt;br /&gt;
&lt;br /&gt;
== Aus expecco-Aktionen ==&lt;br /&gt;
&lt;br /&gt;
Das Expecco-RemoteAccess-Plugin&lt;br /&gt;
([[Expecco::RemoteAccessImportPlugin]]) stellt folgende Testaktionen&lt;br /&gt;
in der expecco-Aktionspalette bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection&#039;&#039;&#039; — öffnet eine&lt;br /&gt;
  SSH-Sitzung über das plattformeigene &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;-Binary&lt;br /&gt;
  (PuTTYs &amp;lt;code&amp;gt;plink&amp;lt;/code&amp;gt; unter Windows).&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open SSH Remote Connection and PublicKey&#039;&#039;&#039; —&lt;br /&gt;
  dasselbe, jedoch mit expliziter Public-Key-Authentifizierung.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung: ein eingerichtetes Schlüsselpaar (privater&lt;br /&gt;
Schlüssel auf dieser Maschine, öffentlicher Teil in der&lt;br /&gt;
&amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des Zielhosts).  Schlüssel&lt;br /&gt;
erzeugen entweder über den Dialog unten oder über&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das Plugin fügt zusätzlich eine Settings-Seite hinzu:&lt;br /&gt;
&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039; mit&lt;br /&gt;
einer einzelnen Schaltfläche &#039;&#039;&#039;Generate SSH Key Pair...&#039;&#039;&#039;, die&lt;br /&gt;
denselben Dialog öffnet.&lt;br /&gt;
&lt;br /&gt;
== Einen SSH-Schlüssel erzeugen ==&lt;br /&gt;
&lt;br /&gt;
=== Der Dialog (FileBrowserV2 / Settings-Seite) ===&lt;br /&gt;
&lt;br /&gt;
Der Dialog fragt alle Parameter in einem Formular ab:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Comment&#039;&#039;&#039; — wird in den erzeugten Schlüssel eingebettet&lt;br /&gt;
  (Voreinstellung &amp;lt;code&amp;gt;stx@&amp;amp;lt;hostname&amp;amp;gt;&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;Save to disk file only&#039;&#039; — schreibt&lt;br /&gt;
   &amp;lt;code&amp;gt;~/.ssh/id_ed25519_stx&amp;lt;/code&amp;gt; (oder wohin man will) samt&lt;br /&gt;
   zugehöriger &amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;-Datei daneben.&lt;br /&gt;
** &#039;&#039;Save to disk AND load into ssh-agent&#039;&#039; — schreibt die Datei&lt;br /&gt;
   UND übergibt den Schlüssel dem laufenden ssh-agent.&lt;br /&gt;
** &#039;&#039;Load into ssh-agent only&#039;&#039; — der Schlüssel lebt nur im&lt;br /&gt;
   Speicher des Agents; nach Agent-Neustart ist er verloren.&lt;br /&gt;
* &#039;&#039;&#039;Private key file&#039;&#039;&#039; — vollständiger Pfad; ausgegraut im&lt;br /&gt;
  Agent-only-Modus.&lt;br /&gt;
* &#039;&#039;&#039;Passphrase / Confirm&#039;&#039;&#039; — leer lässt die On-Disk-Datei&lt;br /&gt;
  unverschlüsselt (Agent-only-Modus ignoriert die Passphrase, da&lt;br /&gt;
  das OpenSSH-Agent-Wire-Protokoll nur den entschlüsselten&lt;br /&gt;
  Schlüssel transportiert).&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Generate&#039;&#039;&#039; wird die Public-Key-Zeile (dieselbe&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-ed25519 AAAA... comment&amp;lt;/code&amp;gt;-Zeichenfolge, die&lt;br /&gt;
ssh-keygen ausgibt) in die System-Zwischenablage kopiert — zum&lt;br /&gt;
direkten Einfügen in die &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; des&lt;br /&gt;
Zielhosts.&lt;br /&gt;
&lt;br /&gt;
=== Aus einem Workspace ===&lt;br /&gt;
&lt;br /&gt;
Für Headless-Deployments, Sandbox-Builds oder Skripte stellt&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; einen reinen Smalltalk-Schlüsselgenerator&lt;br /&gt;
bereit, dessen Ausgabe bit-kompatibel zu&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
| seed comment priv |&lt;br /&gt;
seed    := SSH::Client generateEd25519Seed.&lt;br /&gt;
comment := &#039;stx@&#039;, OperatingSystem getHostName.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Passphrase-verschlüsselt auf Platte speichern&amp;quot;&lt;br /&gt;
priv := (Filename homeDirectory / &#039;.ssh&#039; / &#039;id_ed25519_stx&#039;) pathName.&lt;br /&gt;
SSH::Client&lt;br /&gt;
    saveOpenSshEd25519Seed:seed&lt;br /&gt;
    toFile:priv&lt;br /&gt;
    comment:comment&lt;br /&gt;
    passphrase:&#039;choose-something-long&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ UND in den laufenden Agent laden&amp;quot;&lt;br /&gt;
SSH::Client addEd25519SeedToAgent:seed comment:comment.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;/ Public-Key-Zeile zum Einfügen in authorized_keys ausgeben&amp;quot;&lt;br /&gt;
Transcript showCR:&lt;br /&gt;
    (SSH::Client authorizedKeysLineForEd25519Seed:seed comment:comment).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die so erzeugten Schlüssel sind mit den OpenSSH-Werkzeugen voll&lt;br /&gt;
interoperabel (&amp;lt;code&amp;gt;ssh-keygen -y -f ...&amp;lt;/code&amp;gt; rekonstruiert den&lt;br /&gt;
öffentlichen Schlüssel, &amp;lt;code&amp;gt;ssh-keygen -p -f ...&amp;lt;/code&amp;gt; ändert&lt;br /&gt;
die Passphrase usw.).&lt;br /&gt;
&lt;br /&gt;
=== Mit den Shell-Werkzeugen ===&lt;br /&gt;
&lt;br /&gt;
Der klassische Weg funktioniert weiterhin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh-keygen -t ed25519 -C &amp;quot;stx@your.host&amp;quot;&lt;br /&gt;
ssh-copy-id user@remotehost&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ssh-agent vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
Der Weg über den Agent ist dem direkten Lesen von Schlüsseldateien&lt;br /&gt;
deutlich vorzuziehen: er hält verschlüsselte private Schlüssel&lt;br /&gt;
einmal pro Sitzung entsperrt und kann Identitäten verwalten&lt;br /&gt;
(hardware-tokengestützte Schlüssel, KeePassXC-Einträge), die ST/X&lt;br /&gt;
nie direkt sehen soll.&lt;br /&gt;
&lt;br /&gt;
ST/X erkennt den Agent-Pfad automatisch, sobald&lt;br /&gt;
&amp;lt;code&amp;gt;$SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;zum Zeitpunkt des Starts von stx&#039;&#039;&#039;&lt;br /&gt;
in der Prozessumgebung gesetzt ist.  Eine spätere Zuweisung aus&lt;br /&gt;
einem Workspace nützt nichts.&lt;br /&gt;
&lt;br /&gt;
=== Linux / macOS ===&lt;br /&gt;
&lt;br /&gt;
Die meisten Desktop-Distributionen starten einen Agent automatisch&lt;br /&gt;
beim Login (gnome-keyring unter GNOME, ssh-agent.service unter&lt;br /&gt;
systemd, KWallet unter KDE).  Prüfen im Terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo $SSH_AUTH_SOCK    # /run/user/1000/keyring/ssh oder ähnlich&lt;br /&gt;
ssh-add -l             # listet geladene Identitäten&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519   # eigene laden, falls nicht da&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Läuft gar kein Agent, dieses Snippet in die Shell-rc-Datei&lt;br /&gt;
aufnehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ~/.bashrc oder ~/.zshrc&lt;br /&gt;
if [ -z &amp;quot;$SSH_AUTH_SOCK&amp;quot; ]; then&lt;br /&gt;
    eval &amp;quot;$(ssh-agent -s)&amp;quot; &amp;gt; /dev/null&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ST/X muss aus einer Shell gestartet werden, die diese rc bereits&lt;br /&gt;
gelesen hat — ein Desktop-Launcher aus dem Dateimanager erbt die&lt;br /&gt;
Variable nicht.  Empfehlung: ein kleines Wrapper-Skript unter&lt;br /&gt;
&amp;lt;code&amp;gt;~/.local/bin/&amp;lt;/code&amp;gt;, das die rc sourcet und dann stx&lt;br /&gt;
startet.&lt;br /&gt;
&lt;br /&gt;
Die Settings-Seite (&#039;&#039;&#039;Extras → Settings → Plugins → Remote&lt;br /&gt;
Access — SSH Keys&#039;&#039;&#039;) zeigt an, ob das laufende Image einen&lt;br /&gt;
Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Permanente Einrichtung via systemd ====&lt;br /&gt;
&lt;br /&gt;
Für einen wirklich sitzungsübergreifenden Agent (überlebt Desktop-&lt;br /&gt;
Abmeldung, kommt beim nächsten Login wieder hoch) die bei den&lt;br /&gt;
meisten Distros mit dem Paket &amp;lt;code&amp;gt;openssh-clients&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgelieferte Per-User-systemd-Unit aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl --user enable --now ssh-agent.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; in der Shell-rc auf den&lt;br /&gt;
User-Service-Socket zeigen lassen (ersetzt das&lt;br /&gt;
&amp;lt;code&amp;gt;eval $(ssh-agent -s)&amp;lt;/code&amp;gt;-Snippet oben):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export SSH_AUTH_SOCK=&amp;quot;${XDG_RUNTIME_DIR}/ssh-agent.socket&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Um den manuellen &amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt;-Schritt zu sparen, kann&lt;br /&gt;
OpenSSH Schlüssel beim ersten Bedarf selbst in den Agent laden.&lt;br /&gt;
In &amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt; eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die erste SSH-Verbindung fragt dann einmal nach der Passphrase und&lt;br /&gt;
übergibt den entsperrten Schlüssel an den Agent; weitere&lt;br /&gt;
Verbindungen nutzen die gespeicherte Identität ohne Prompt.&lt;br /&gt;
&lt;br /&gt;
=== Windows ===&lt;br /&gt;
&lt;br /&gt;
Windows 10+ bringt das native OpenSSH inklusive Agent-Dienst mit.&lt;br /&gt;
Einmalige Einrichtung:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Dienste&#039;&#039;&#039; (&amp;lt;code&amp;gt;services.msc&amp;lt;/code&amp;gt;) als Administrator öffnen.&lt;br /&gt;
# &#039;&#039;&#039;OpenSSH Authentication Agent&#039;&#039;&#039; suchen, Starttyp auf&lt;br /&gt;
  &#039;&#039;&#039;Automatisch&#039;&#039;&#039; setzen, &#039;&#039;&#039;Starten&#039;&#039;&#039; anklicken.&lt;br /&gt;
# In PowerShell: &amp;lt;code&amp;gt;ssh-add $HOME\.ssh\id_ed25519&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Prüfen: &amp;lt;code&amp;gt;ssh-add -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Windows-OpenSSH-Agent lauscht auf einer Named Pipe&lt;br /&gt;
(&amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;), nicht auf einem&lt;br /&gt;
Unix-Socket.  ST/X unterstützt beide Transporte, jedoch setzt das&lt;br /&gt;
Windows-ssh-add &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; selbst.&lt;br /&gt;
Daher einmalig systemweit setzen:&lt;br /&gt;
&lt;br /&gt;
# {{Key|Win}} drücken → &amp;quot;Umgebungsvariablen&amp;quot; → „Systemumgebungs-&lt;br /&gt;
  variablen bearbeiten&amp;quot;.&lt;br /&gt;
# &#039;&#039;&#039;Umgebungsvariablen&#039;&#039;&#039; → unter &#039;&#039;&#039;Benutzervariablen&#039;&#039;&#039;,&lt;br /&gt;
  &#039;&#039;&#039;Neu&#039;&#039;&#039; klicken.&lt;br /&gt;
# Name: &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt;&lt;br /&gt;
# Wert: &amp;lt;code&amp;gt;\\.\pipe\openssh-ssh-agent&amp;lt;/code&amp;gt;&lt;br /&gt;
# Ab- und wieder anmelden (oder stx neu starten), damit die neue&lt;br /&gt;
  Umgebung übernommen wird.&lt;br /&gt;
&lt;br /&gt;
==== PowerShell-Schnelleinrichtung ====&lt;br /&gt;
&lt;br /&gt;
Derselbe Aufbau aus einer &#039;&#039;&#039;Administrator-PowerShell&#039;&#039;&#039; heraus,&lt;br /&gt;
z.B. für Skripte oder unbeaufsichtigte Bereitstellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Agent jetzt und bei jedem Neustart starten (permanent).&lt;br /&gt;
Set-Service -Name ssh-agent -StartupType Automatic&lt;br /&gt;
Start-Service ssh-agent&lt;br /&gt;
&lt;br /&gt;
# SSH_AUTH_SOCK dauerhaft für den Benutzer setzen (übersteht Reboots).&lt;br /&gt;
[Environment]::SetEnvironmentVariable(&lt;br /&gt;
    &#039;SSH_AUTH_SOCK&#039;,&lt;br /&gt;
    &#039;\\.\pipe\openssh-ssh-agent&#039;,&lt;br /&gt;
    &#039;User&#039;)&lt;br /&gt;
&lt;br /&gt;
# Schlüssel laden (fragt nach Passphrase, falls die Datei verschlüsselt ist).&lt;br /&gt;
ssh-add $HOME\.ssh\id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für einen einmaligen Agent-Start ohne dauerhafte Aktivierung&lt;br /&gt;
(z.B. Einzelsitzung) die &amp;lt;code&amp;gt;Set-Service&amp;lt;/code&amp;gt;-Zeile weglassen&lt;br /&gt;
und nur &amp;lt;code&amp;gt;Start-Service ssh-agent&amp;lt;/code&amp;gt; ausführen.  Die&lt;br /&gt;
env-var-Zeile lässt sich ebenfalls weglassen, wenn&lt;br /&gt;
&amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; nur in der aktuellen Shell gebraucht&lt;br /&gt;
wird — dann statt der &amp;lt;code&amp;gt;[Environment]&amp;lt;/code&amp;gt;-Variante&lt;br /&gt;
&amp;lt;code&amp;gt;$env:SSH_AUTH_SOCK = &#039;...&#039;&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Auf stark abgespeckten Windows-Installationen ist der&lt;br /&gt;
ssh-agent-Dienst eventuell nicht vorhanden.  Einmalig nachrüsten&lt;br /&gt;
über &#039;&#039;&#039;Einstellungen → Apps → Optionale Features → OpenSSH-&lt;br /&gt;
Client&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Alternative Agenten:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;PuTTY pageant&#039;&#039;&#039; — eigenes Protokoll; von ST/X&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH::Agent&amp;lt;/code&amp;gt; &#039;&#039;&#039;nicht&#039;&#039;&#039; unterstützt.  Schlüssel zu&lt;br /&gt;
  OpenSSH migrieren.&lt;br /&gt;
* &#039;&#039;&#039;Git für Windows ssh-agent&#039;&#039;&#039; — funktioniert;&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_AUTH_SOCK&amp;lt;/code&amp;gt; auf den dort veröffentlichten Socket&lt;br /&gt;
  zeigen lassen.&lt;br /&gt;
* &#039;&#039;&#039;WSL 2&#039;&#039;&#039; — ein ST/X innerhalb der WSL sieht den WSL-eigenen&lt;br /&gt;
  Agent normal; ein ST/X auf der Windows-Seite nicht.  Eine&lt;br /&gt;
  Brücke per &amp;lt;code&amp;gt;npiperelay&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt; ist&lt;br /&gt;
  möglich.&lt;br /&gt;
&lt;br /&gt;
Prüfung über die Settings-Seite&lt;br /&gt;
(&#039;&#039;&#039;Extras → Settings → Plugins → Remote Access — SSH Keys&#039;&#039;&#039;) —&lt;br /&gt;
die Anzeige dort meldet, ob das laufende Image den Agent sieht.&lt;br /&gt;
&lt;br /&gt;
==== Schlüssel automatisch beim ersten Einsatz laden ====&lt;br /&gt;
&lt;br /&gt;
Windows-OpenSSH speichert agent-geladene Schlüssel &#039;&#039;&#039;nicht&#039;&#039;&#039;&lt;br /&gt;
über Agent-Neustarts hinweg.  Um nicht nach jedem Reboot manuell&lt;br /&gt;
&amp;lt;code&amp;gt;ssh-add&amp;lt;/code&amp;gt; aufrufen zu müssen, dieselbe Lazy-Load-&lt;br /&gt;
Konfiguration in &amp;lt;code&amp;gt;%USERPROFILE%\.ssh\config&amp;lt;/code&amp;gt;&lt;br /&gt;
eintragen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Host *&lt;br /&gt;
    AddKeysToAgent yes&lt;br /&gt;
    IdentityFile ~/.ssh/id_ed25519&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenSSH lädt den Schlüssel dann beim ersten Einsatz in den Agent&lt;br /&gt;
(fragt einmal nach der Passphrase) und nutzt ihn für die übrige&lt;br /&gt;
Sitzung weiter.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Alle Stellschrauben sind klassenseitig auf&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; erreichbar:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Accessor !! Voreinstellung !! Steuert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#idleEvictionSeconds:&amp;lt;/code&amp;gt; || 240 (4 Min) || Wie lange&lt;br /&gt;
eine gepoolte Verbindung im Leerlauf liegen darf, bevor sie beim&lt;br /&gt;
nächsten Zugriff proaktiv geschlossen und neu geöffnet wird.&lt;br /&gt;
Liegt knapp unter dem typischen&lt;br /&gt;
&amp;lt;code&amp;gt;ClientAliveInterval × ClientAliveCountMax&amp;lt;/code&amp;gt; des sshd,&lt;br /&gt;
damit wir uns recyceln, bevor der Server uns mit TCP-RESET&lt;br /&gt;
trennt.  &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; setzt auf Voreinstellung zurück.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#attrsCacheTtlSeconds:&amp;lt;/code&amp;gt; || 5 || Maximales Alter (s)&lt;br /&gt;
eines gecachten STAT, bevor &amp;lt;code&amp;gt;#ensureAttrs&amp;lt;/code&amp;gt; neu am&lt;br /&gt;
Server fragt.  Eltern-listDir stempelt ohnehin frische Attribute&lt;br /&gt;
auf alle Kinder, daher zahlt das Navigieren im offenen&lt;br /&gt;
Verzeichnis das TTL nicht.  &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; schaltet den Cache&lt;br /&gt;
ab.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;#closeAllConnections&amp;lt;/code&amp;gt; || (Aktion) || Reißt jede&lt;br /&gt;
gepoolte Verbindung ab.  Nützlich nach einem bekannt schlechten&lt;br /&gt;
Netzereignis, vor einem bewussten Identitätswechsel oder zum&lt;br /&gt;
sauberen Image-Shutdown.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Diagnose ==&lt;br /&gt;
&lt;br /&gt;
=== SemaphoreMonitor ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SemaphoreMonitor&amp;lt;/code&amp;gt; über das Untermenü „Status&amp;quot; des&lt;br /&gt;
Launchers öffnen.  Der pro-Host-SFTP-Mutex erscheint als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;, der pool-weite Mutex als&lt;br /&gt;
&amp;lt;code&amp;gt;SFTP/pool&amp;lt;/code&amp;gt;.  Per Rechtsklick:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Copy Waiters Stack to Clipboard&#039;&#039;&#039; — schreibt den Walkback&lt;br /&gt;
  des letzten Eigners samt aller Waiter als Text in die&lt;br /&gt;
  Zwischenablage.  Unverzichtbar, wenn ein Prozess in&lt;br /&gt;
  &amp;lt;code&amp;gt;readWait&amp;lt;/code&amp;gt; innerhalb von&lt;br /&gt;
  &amp;lt;code&amp;gt;withSftpClientDo:&amp;lt;/code&amp;gt; klemmt.&lt;br /&gt;
* &#039;&#039;&#039;Copy List to Clipboard&#039;&#039;&#039; — die ganze Tabelle, ideal für eine&lt;br /&gt;
  E-Mail-Diagnose.&lt;br /&gt;
* &#039;&#039;&#039;Detect Deadlocks&#039;&#039;&#039; — DFS über den Wait-for-Graph, meldet&lt;br /&gt;
  Zyklen.&lt;br /&gt;
&lt;br /&gt;
=== Logger ===&lt;br /&gt;
&lt;br /&gt;
Interessante Ereignisse werden über &amp;lt;code&amp;gt;Logger&amp;lt;/code&amp;gt; geloggt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei automatischem Reconnect nach toter&lt;br /&gt;
  Verbindung.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; bei Idle-Verdrängung eines Pool-Eintrags.&lt;br /&gt;
* &amp;lt;code&amp;gt;warning:&amp;lt;/code&amp;gt; wenn eine SSH-Schlüsseldatei nicht&lt;br /&gt;
  geparst werden konnte — die Datei wird übersprungen.&lt;br /&gt;
&lt;br /&gt;
== Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Nur SFTP v3.&#039;&#039;&#039;  Kein SETSTAT (kein entferntes&lt;br /&gt;
  chmod / chown / utime), kein SSH_FXP_READLINK exponiert&lt;br /&gt;
  (&amp;lt;code&amp;gt;#isSymbolicLink&amp;lt;/code&amp;gt; liefert immer &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;,&lt;br /&gt;
  &amp;lt;code&amp;gt;#linkInfo&amp;lt;/code&amp;gt; die normale stat-Info).  SFTPv5+-Features&lt;br /&gt;
  (atomares Überschreibungs-rename via&lt;br /&gt;
  &amp;lt;code&amp;gt;SSH_FXF_OVERWRITE&amp;lt;/code&amp;gt;) werden nicht unterstützt.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host.&#039;&#039;&#039;  Zwei gleichzeitige Operationen&lt;br /&gt;
  am selben Host stehen am Host-Mutex an.  Siehe [[#Ausblick]].&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#renameTo:&amp;lt;/code&amp;gt; hat ein TOCTOU-Fenster.&#039;&#039;&#039;&lt;br /&gt;
  POSIX-typisches Überschreiben wird als Delete-dann-Rename&lt;br /&gt;
  emuliert; ein anderer Prozess kann sich dazwischenschieben.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt; ist eine Heuristik.&#039;&#039;&#039;&lt;br /&gt;
  Liefert immer &amp;lt;code&amp;gt;#isDirectory&amp;lt;/code&amp;gt; (die genaue Antwort&lt;br /&gt;
  würde drei Roundtrips pro Verzeichnis-Symbol kosten, was das&lt;br /&gt;
  ursprüngliche Baum-Ausklappen unerträglich gebremst hatte).&lt;br /&gt;
&lt;br /&gt;
== Implementierungsdetails ==&lt;br /&gt;
&lt;br /&gt;
Für Leser, die die Architektur verstehen wollen.  Fünf Klassen,&lt;br /&gt;
von oben nach unten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Klasse !! Aufgabe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpFilename&amp;lt;/code&amp;gt; || Filename-Unterklasse, die&lt;br /&gt;
öffentliche API.  Bildet &amp;lt;code&amp;gt;sftp://...&amp;lt;/code&amp;gt;-URLs auf&lt;br /&gt;
entfernte Dateien ab und stellt &amp;lt;code&amp;gt;directoryContents&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;readingFileDo:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renameTo:&amp;lt;/code&amp;gt; usw. bereit.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt; || SFTP-v3-Protokoll&lt;br /&gt;
(Request/Response-Codec, listDir, stat, open, read, write, mkdir).&lt;br /&gt;
Wird von SftpFilename angesteuert.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Channel&amp;lt;/code&amp;gt; || SSH-Kanal-Multiplexer (CHANNEL_OPEN,&lt;br /&gt;
DATA, EOF, CLOSE, WINDOW_ADJUST).  Eine logische Sitzung pro&lt;br /&gt;
Channel-Instanz.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; || High-Level-SSH-Client: öffnet den&lt;br /&gt;
Transport, führt KEX, Hostschlüssel-Prüfung und userauth durch und&lt;br /&gt;
verteilt anschließend Kanäle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SSH::Transport&amp;lt;/code&amp;gt; || Drahtschicht.  Banner- und&lt;br /&gt;
KEXINIT-Austausch, ChaCha20-Poly1305-Paket-Framing, sendSeq /&lt;br /&gt;
recvSeq, Heartbeat, SSH_MSG_DISCONNECT.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Verbindungs-Pooling ===&lt;br /&gt;
&lt;br /&gt;
Alle &amp;lt;code&amp;gt;SftpFilename&amp;lt;/code&amp;gt;-Instanzen, die auf dasselbe Tripel&lt;br /&gt;
&amp;lt;code&amp;gt;user@host:port&amp;lt;/code&amp;gt; zeigen, teilen sich einen&lt;br /&gt;
&amp;lt;code&amp;gt;SSH::Client&amp;lt;/code&amp;gt; samt einem &amp;lt;code&amp;gt;SSH::SftpClient&amp;lt;/code&amp;gt;.&lt;br /&gt;
Der Pool ist klassenseitig und wird von einem einzigen&lt;br /&gt;
&amp;lt;code&amp;gt;ConnectionPoolMutex&amp;lt;/code&amp;gt; bewacht:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lazy-Aufbau&#039;&#039;&#039; — TCP + KEX + userauth + SFTP-INIT laufen&lt;br /&gt;
  erst beim ersten SFTP-Aufruf, nicht in &amp;lt;code&amp;gt;forUrl:&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;&#039;Serialisierung pro Host&#039;&#039;&#039; — SFTP-Anfragen an einen&lt;br /&gt;
  bestimmten Host werden durch einen &amp;lt;code&amp;gt;RecursionLock&amp;lt;/code&amp;gt;&lt;br /&gt;
  mit dem Namen &amp;lt;code&amp;gt;SFTP/&amp;amp;lt;user@host:port&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
  serialisiert (sichtbar im SemaphoreMonitor).&lt;br /&gt;
* &#039;&#039;&#039;Idle-Verdrängung&#039;&#039;&#039; — ein Pool-Eintrag, der länger als&lt;br /&gt;
  &amp;lt;code&amp;gt;idleEvictionSeconds&amp;lt;/code&amp;gt; ungenutzt liegt, wird beim&lt;br /&gt;
  nächsten Zugriff proaktiv geschlossen und neu geöffnet.&lt;br /&gt;
* &#039;&#039;&#039;Automatischer Reconnect&#039;&#039;&#039; — ein Fehler auf Transportebene&lt;br /&gt;
  (Broken Pipe, EOF, MNU auf nil-Socket) verdrängt den&lt;br /&gt;
  Pool-Eintrag, öffnet einen frischen Client und wiederholt die&lt;br /&gt;
  Anfrage &#039;&#039;&#039;einmal&#039;&#039;&#039;.  Anwendungsfehler aus&lt;br /&gt;
  SFTP-STATUS-Antworten werden sofort durchgereicht.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Geplant, aber noch nicht umgesetzt:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-Channel-Parallelität pro Host&#039;&#039;&#039; — aktuell bedeutet&lt;br /&gt;
  eine TCP- plus eine SFTP-Verbindung pro Host, dass N&lt;br /&gt;
  gleichzeitige Anfragen serialisieren.  Pipelining über mehrere&lt;br /&gt;
  SshClients im Pool (bevorzugt) oder ein transport-seitiger&lt;br /&gt;
  Reader-Prozess, der eingehende Pakete in&lt;br /&gt;
  Pro-Kanal-Postfächer demultiplext, würde es dem Baum-Panel&lt;br /&gt;
  erlauben, weiter aufzulisten, während das Inhalts-Panel eine&lt;br /&gt;
  große Datei liest.&lt;br /&gt;
* &#039;&#039;&#039;Genaues &amp;lt;code&amp;gt;#isNonEmptyDirectory&amp;lt;/code&amp;gt;&#039;&#039;&#039; via OPEN_DIR&lt;br /&gt;
  + READ_DIR (nur erstes Batch) + CLOSE — drei Roundtrips pro&lt;br /&gt;
  Sondierung; lohnt erst, wenn der SftpClient Anfragen pipelinen&lt;br /&gt;
  kann.&lt;br /&gt;
* &#039;&#039;&#039;SFTP-v5/v6-Aushandlung&#039;&#039;&#039; für atomares Überschreibungs-&lt;br /&gt;
  rename, erweiterte Attribute und FTP-artige Kanonisierung.&lt;br /&gt;
&lt;br /&gt;
= Kommando-Shell =&lt;br /&gt;
&lt;br /&gt;
Lokale Kommando-Shell auf dieser expecco-Maschine.  Typische&lt;br /&gt;
Anwendungen: lokale Kommandozeile, lokales Hilfsprogramm,&lt;br /&gt;
Brücke zwischen entferntem Workflow und lokalem Tool.&lt;br /&gt;
&lt;br /&gt;
Das RemoteAccess-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Open&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;CmdShell - Close&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Keine Zugangsdaten, kein Netzwerk — läuft als der Benutzer des&lt;br /&gt;
expecco-Prozesses.  Ausgaben gehen in das expecco-Log.&lt;br /&gt;
&lt;br /&gt;
= Telnet =&lt;br /&gt;
&lt;br /&gt;
[[File:Warning.svg|24px|Warnung]] &#039;&#039;&#039;Telnet ist ein veraltetes&lt;br /&gt;
Protokoll ohne Verschlüsselung.&#039;&#039;&#039; Passwörter werden im Klartext&lt;br /&gt;
über die Leitung übertragen; jeder im Netzpfad kann sie lesen.&lt;br /&gt;
Telnet NUR einsetzen, wenn die Gegenstelle keine Alternative&lt;br /&gt;
bietet (typisch: alte Industriesteuerungen, Laborgeräte,&lt;br /&gt;
eingebettete Messgeräte ohne SSH-Stack).  Für alles andere&lt;br /&gt;
[[#SSH und SFTP]] verwenden.&lt;br /&gt;
&lt;br /&gt;
Das expecco-Plugin stellt bereit:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Open Remote Connection With Login&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Telnet - Execute Remote Command&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Example - Remote Device Control via Telnet&#039;&#039;&#039; (interne Demo)&lt;br /&gt;
&lt;br /&gt;
Das Telnet-Protokoll (RFC 854) ist ein bidirektionaler&lt;br /&gt;
8-Bit-Byte-Strom über TCP, mit In-Band-Steuersequenzen für&lt;br /&gt;
Terminal-Optionen.  Verbindungsaufbau zum Ziel-Host:Port; nach&lt;br /&gt;
optionalem In-Band-Login können beide Seiten Daten senden.&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
&lt;br /&gt;
* [[SSH Client|SSH::Client]] — die SSH-Schicht (exec, TTY,&lt;br /&gt;
  Agent-Weiterleitung, ProxyJump).&lt;br /&gt;
* [[FileBrowserV2]] — die Haupt-UI dieses Stacks.&lt;br /&gt;
* [[ClaudeCode plugin|Claude Code]] — nutzt denselben SSH-Stack&lt;br /&gt;
  als HTTPS-Transport.&lt;br /&gt;
* [[Wikipedia:Secure Shell|RFC 4251 (SSH-2 Architecture)]]&lt;br /&gt;
* [[Wikipedia:Telnet|RFC 854 (Telnet Protocol)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;br /&gt;
[[Category:Netz]]&lt;br /&gt;
[[Category:SSH]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Hauptseite&amp;diff=31289</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Hauptseite&amp;diff=31289"/>
		<updated>2026-05-26T07:27:38Z</updated>

		<summary type="html">&lt;p&gt;Sv: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Hauptseite/en|English Version]]&lt;br /&gt;
&lt;br /&gt;
Klicken Sie links auf &#039;&#039;&amp;quot;Letzte Änderungen&amp;quot;&#039;&#039; (&amp;quot;Recent changes&amp;quot;) um die neuesten Einträge in der Dokumentation zu sehen.&amp;lt;br&amp;gt;Sie sehen dann meist, woran unsere Entwickler gerade vornehmlich arbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt;expecco&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; padding:0; white-space:nowrap&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Für Einsteiger&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;!-- Link mit Fragezeichen funktioniert nicht, deshalb externer Link --&amp;gt;&lt;br /&gt;
			&amp;lt;!-- &amp;lt;li&amp;gt;[[Was ist expecco?|Allgemeine Informationen]]&amp;lt;/li&amp;gt; --&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[https://doc.expecco.de/w2.x/index.php?title=Was_ist_expecco%3F Allgemeine Informationen]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Expecco Usecases|Einsatzbereiche]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Features|expecco Feature-List]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Tutorials]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[usageHints/en|Hinweise zur Bedienung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Glossary|Begriffserklärung]] (Glossar)&amp;lt;/li&amp;gt;&lt;br /&gt;
 			&amp;lt;!-- &amp;lt;li&amp;gt;[[Testorganisation|Testorganisation]]&amp;lt;/li&amp;gt; --&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[FAQ]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage5.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Installation&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Installation]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Installing additional Frameworks/en| Installation zusätzlicher Tools (Node, Python, ...)]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Personal Settings|Persönliche&amp;amp;nbsp;Einstellungen]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Configuration &amp;amp; Setup|Konfiguration]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Anbindung expecco ALM]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[SAP_Testing|Anbindung expecco an SAP]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Spezifische Anpassung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco UI]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_Remote_Control_App|expecco Mobile Remote App]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Probleme &amp;amp; Fehler]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage4.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Werkzeuge&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[API von Elementaraktionen]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Debugger]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Editoren]]&amp;lt;/li&amp;gt;		&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Standard Libraries/Bibliotheken]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Weitere Werkzeuge]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Weitere Funktionen]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage6.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Reportgenerierung&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Report Generation|Reportgenerierung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Errors during Execution|Fehler während der Ausführung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Secret Strings|Geheime Zeichenketten und Passwörter]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	 &amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Erweiterung_plugin.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Erweiterungen (Plugins)&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;li&amp;gt;[[KI Coding Plugin|NEU: KI Unterstützung beim Programmieren]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[GUI Testing]]&lt;br /&gt;
				&amp;lt;ul&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Java GUI Plugins|Java Swing/FX/SWT UI Testing]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Mobile Testing Plugin|Mobile Testing auf Android und iOS]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[WindowsAutomation Reference 2.0|UI Testing mit der Windows Automation Library]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Selenium_WebDriver_Plugin|Web Testing mit Selenium WebDriver]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                                        &amp;lt;li&amp;gt;[[VNC_Plugin_Reference|VNC UI Testing]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[GUI Testing|Weitere GUI Testing Plugins]]&amp;lt;/li&amp;gt;&lt;br /&gt;
				&amp;lt;/ul&amp;gt;&lt;br /&gt;
			&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Schnittstellen zum SUT]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[ManualTest]]&amp;lt;/li&amp;gt;&lt;br /&gt;
            &amp;lt;li&amp;gt;[[Remote Access|&amp;lt;b&amp;gt;NEU: native SSH/SFTP&amp;lt;/b&amp;gt; Remote Access via SSH/telnet]]&lt;br /&gt;
			&amp;lt;li&amp;gt;Produktivit&amp;amp;auml;t&lt;br /&gt;
				&amp;lt;ul&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[GIT Plugin]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                                        &amp;lt;li&amp;gt;[[HG Plugin (Mercurial)]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[CVS Plugin]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Anbindung expecco ALM | expeccoALM Schnittstelle]]&amp;lt;/li&amp;gt;&lt;br /&gt;
				&amp;lt;/ul&amp;gt;&lt;br /&gt;
			&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Weitere Plugins]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe;  margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage3.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Elemente der Testsuite&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Tree Elements|Tree Elemente]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Folder Element|Ordner]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Testplan Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Aktionen/Aktionsblöcke]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Datatype Element|Datentyp Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Inventory Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Skill Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Resource Element|Ressource Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Attachment Element|Anhänge]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[ReportTemplate Element|Report Templates]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color:#ecf0fe;  margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage3.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;API Referenz (Elementarbausteine)&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[API von Elementaraktionen|Übersicht]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco API#JavaScript_and_Smalltalk_Elementary_Blocks|Smalltalk &amp;amp; JavaScript]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco API#Groovy_Elementary_Blocks|Groovy]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Node.js_.28Bridged.29_Elementary_Blocks|NodeJS]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Bridged_Python_Elementary_Blocks|Python]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Bridged_Ruby_Elementary_Blocks|Ruby]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Bridged_C_Elementary_Blocks|C]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco API#VisualBasic_Elementary_Blocks|VisualBasic ]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Expecco Scripting API|Shell und Script Languages]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Useful API Functions|Useful API Functions:]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;ul&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Number API Functions|Number API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[String API Functions|String API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Collection API Functions|Collection API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Filename API Functions|Filename API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Stream API Functions|Stream API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Date_and_Time_API_Functions|Date &amp;amp; Time API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;/ul&amp;gt; &lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Diagramm.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Diagramm - Elemente&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Step|Schritt]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Pins (Ein - Ausgänge)]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Code Ausführung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Connection|Verbindung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Pin|Pin Beschreibung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Annotation|Notiz]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Probe|Messfühler]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Refresh.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Sonstiges&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 26.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 25.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 24.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 23.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 22.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 21.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 20.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 19.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 18.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 2.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 1.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Future releases expecco|Zukünftige Releases]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Smalltalk]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Beispiele_icon.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Beispiele&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
                &amp;lt;span style=&amp;quot;padding:0 1em;font-size:0.9em&amp;quot;&amp;gt;Sie haben Fragen, wie ein Projekt aussieht?&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
                &lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;amp;nbsp;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Beispiele | Hier finden Sie weitere Anwendungsbeispiele.]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Expecco Code Snippets/en | Codeschnipsel]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Beispiele_icon.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Unterstützung weiterer Qualitätsmanagement Tools&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
                &lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Jira_Plugin_Reference|Jira]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[PolarionPlugin_Reference|Polarion]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Aqua_Plugin_Reference|Aqua]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Starting_expecco_via_Command_Line#Integration_with_Jenkins|Jenkins Integration]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2 style=&amp;quot;clear:left&amp;quot;&amp;gt;expecco ALM&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; padding:0; white-space:nowrap&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Sonstiges-Info.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Allgemein&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Was ist expecco ALM?]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Was ist der expecco Lizenzserver?]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Anbindung expecco ALM]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Rollen und Rechte]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Filter]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[FAQ expeccoALM/en | FAQ]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage5.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Installation/Einrichten&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Installation|Installation]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Installation Patches|expecco ALM Installation Patches]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Einrichten Vorgehensweise|expecco ALM Einrichten]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM expecco Patch Service |Patchservice für expecco]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Refresh.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Plugins&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[AIDYMO Jira|Atlassian Jira]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[CustomerSpecific|Kundenspezifische Portale]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Refresh.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Sonstiges&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM App]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Installation Lizenzserver]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Release Notes|Release Notes]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Known Issues|Known Issues]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=KI_Coding_Plugin&amp;diff=31259</id>
		<title>KI Coding Plugin</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=KI_Coding_Plugin&amp;diff=31259"/>
		<updated>2026-05-20T10:21:09Z</updated>

		<summary type="html">&lt;p&gt;Sv: Ändrung im GUI&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= KI Coding Plugin =&lt;br /&gt;
&lt;br /&gt;
Das KI Coding Plugin bindet einen Large Language Model (LLM) basierten&lt;br /&gt;
KI-Assistenten in den Activity-Editor (Aktivitäten-Code), den&lt;br /&gt;
Compound-Netzwerk-Editor, den Dokumentations-Editor und in den&lt;br /&gt;
ST/X Class Browser ein.  Das Plugin unterstützt zwei Anbieter, die im&lt;br /&gt;
Einstellungsdialog umschaltbar sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Anthropic Claude&#039;&#039;&#039; (claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5)&lt;br /&gt;
* &#039;&#039;&#039;OpenAI ChatGPT&#039;&#039;&#039; (gpt-4o, gpt-4o-mini, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, o1, o3)&lt;br /&gt;
&lt;br /&gt;
Je nach gewähltem Anbieter erscheint die Toolbar-Schaltfläche als&lt;br /&gt;
&#039;&#039;&#039;&amp;quot;Ask Claude&amp;quot;&#039;&#039;&#039; bzw. &#039;&#039;&#039;&amp;quot;Ask ChatGPT&amp;quot;&#039;&#039;&#039;; das Einstellungs-Tab&lt;br /&gt;
heißt &#039;&#039;&#039;&amp;quot;AI Coding&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Aktivitäten-Editor ==&lt;br /&gt;
&lt;br /&gt;
Im Aktivitäten-Code-Editor erscheint in der Toolbar eine Schaltfläche&lt;br /&gt;
&amp;quot;Ask Claude&amp;quot; / &amp;quot;Ask ChatGPT&amp;quot; mit folgenden Aktionen:&lt;br /&gt;
&lt;br /&gt;
* Open KI Window — öffnet das eigenständige Chat-Fenster&lt;br /&gt;
* Explain code — erklärt den Code der aktuellen Aktivität&lt;br /&gt;
* Suggest improvement — schlägt Verbesserungen vor&lt;br /&gt;
* Find bugs — sucht nach Fehlern, Race Conditions, nil-Handling-Problemen&lt;br /&gt;
* Generate doc-comment — generiert eine Aktivitäts-Dokumentation inklusive Pin-Kommentaren und füllt den Documentation-Tab&lt;br /&gt;
* Custom prompt... — freier Prompt; der Aktivitäts-Code wird als Kontext mitgesendet&lt;br /&gt;
* Set model ▸ — Untermenü mit den Modellen des aktiven Anbieters (das aktuell aktive ist mit &#039;&#039;(active)&#039;&#039; markiert)&lt;br /&gt;
* Set provider ▸ — nur sichtbar, wenn API-Schlüssel für mehr als einen Anbieter konfiguriert sind; Untermenü zum Umschalten zwischen Claude und ChatGPT&lt;br /&gt;
&lt;br /&gt;
Code-Vorschläge können mit &#039;&#039;&#039;[Apply]&#039;&#039;&#039; (direkt am jeweiligen&lt;br /&gt;
Code-Block im Chat oder über die obere Apply-Schaltfläche) in den&lt;br /&gt;
Aktivitäts-Body übernommen werden.  Vom KI gelieferte Smalltalk/X&lt;br /&gt;
Hilfsmethoden (Form: &amp;lt;code&amp;gt;Klasse &amp;gt;&amp;gt; selector&amp;lt;/code&amp;gt;) werden nach&lt;br /&gt;
Rückfrage in die genannte Klasse compiliert.&lt;br /&gt;
&lt;br /&gt;
== Compound (Netzwerk) Editor ==&lt;br /&gt;
&lt;br /&gt;
Auf der Toolbar von Compound-Worksheets erscheint dieselbe&lt;br /&gt;
Schaltfläche, beschränkt auf die für Netze sinnvollen Aktionen&lt;br /&gt;
(Open KI Window, Generate doc-comment) — ebenfalls mit den&lt;br /&gt;
Untermenüs &#039;&#039;&#039;Set model&#039;&#039;&#039; und ggf. &#039;&#039;&#039;Set provider&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Dokumentations-Editor ==&lt;br /&gt;
&lt;br /&gt;
Der Dokumentations-Tab der Block-Description (Editor für Test-Cases,&lt;br /&gt;
Test-Plans und Aktivitäten) trägt dieselbe Toolbar-Schaltfläche&lt;br /&gt;
&amp;quot;Ask Claude&amp;quot; / &amp;quot;Ask ChatGPT&amp;quot; mit den Einträgen&lt;br /&gt;
&#039;&#039;&#039;Open KI Window&#039;&#039;&#039;, &#039;&#039;&#039;Generate doc-comment&#039;&#039;&#039; sowie den&lt;br /&gt;
Untermenüs &#039;&#039;&#039;Set model&#039;&#039;&#039; und ggf. &#039;&#039;&#039;Set provider&#039;&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Generate doc-comment&#039;&#039;&#039; analysiert den aktuellen Block (Name,&lt;br /&gt;
vorhandene Beschreibung, Eingangs-/Ausgangs-Pins, Sub-Steps eines&lt;br /&gt;
Compound-Netzes, referenzierte Environment-Variablen) und schlägt&lt;br /&gt;
eine vollständige Block- und Pin-Dokumentation vor.&lt;br /&gt;
&lt;br /&gt;
Ist der Block schreibgeschützt (importierte Library, RTL-Lizenz),&lt;br /&gt;
fragt &#039;&#039;&#039;[Apply]&#039;&#039;&#039; nach, ob der Block freigeschaltet und die&lt;br /&gt;
Dokumentation eingetragen werden soll.&lt;br /&gt;
&lt;br /&gt;
== Class Browser (ST/X) ==&lt;br /&gt;
&lt;br /&gt;
Im Class Browser stehen die Aktionen unter dem &#039;&#039;&#039;AI&#039;&#039;&#039;-Untermenü&lt;br /&gt;
sowie im Selektor-Kontextmenü zur Verfügung.  Die Aktionen&lt;br /&gt;
operieren auf der aktuell ausgewählten Methode (Klasse + Selektor +&lt;br /&gt;
Quelltext werden als Kontext mitgesendet).  &#039;&#039;&#039;[Apply]&#039;&#039;&#039; kann das&lt;br /&gt;
Resultat direkt in die Methode der aktiven Klasse einbauen.&lt;br /&gt;
Auch hier sind &#039;&#039;&#039;Set model&#039;&#039;&#039; und ggf. &#039;&#039;&#039;Set provider&#039;&#039;&#039; als&lt;br /&gt;
Untermenüs verfügbar.&lt;br /&gt;
&lt;br /&gt;
== Chat-Fenster ==&lt;br /&gt;
&lt;br /&gt;
Das eigenständige Chat-Fenster trägt den Titel&lt;br /&gt;
&#039;&#039;&#039;AI Coding [&amp;amp;lt;Produkt&amp;amp;gt; / &amp;amp;lt;Modell&amp;amp;gt;]&#039;&#039;&#039; (z.B.&lt;br /&gt;
&amp;quot;AI Coding [Claude / claude-opus-4-7]&amp;quot;) und zeigt nach jedem Turn&lt;br /&gt;
den Tokenverbrauch und die kumulierten Kosten — sofern Preise für&lt;br /&gt;
das gewählte Modell hinterlegt sind.  Anbieter- und Modellwechsel&lt;br /&gt;
im Einstellungsdialog werden live übernommen.&lt;br /&gt;
&lt;br /&gt;
Das Transkript ist als vertikales Panel von &amp;quot;Karten&amp;quot; aufgebaut:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Prosa-Karten&#039;&#039;&#039; enthalten den fließenden Text der Antwort; Zeilen werden auf die Fenster-Breite umgebrochen.&lt;br /&gt;
* &#039;&#039;&#039;Code-Karten&#039;&#039;&#039; (eingerahmt) entstehen aus markdown-Code-Blöcken (&amp;lt;code&amp;gt;```...```&amp;lt;/code&amp;gt;) und sind als &#039;&#039;&#039;Workspace&#039;&#039;&#039; editierbar — das Standard-Smalltalk-Popup-Menü bietet doIt / printIt / inspectIt, ein &amp;lt;code&amp;gt;TestCase&amp;lt;/code&amp;gt;-&amp;lt;code&amp;gt;simulatedSelf&amp;lt;/code&amp;gt; ist gesetzt, so dass &amp;lt;code&amp;gt;self assert:... equals:...&amp;lt;/code&amp;gt; direkt ausgeführt werden kann.  Lange Blöcke scrollen innerhalb der Karte.  Rechts oben sitzen die Schaltflächen &#039;&#039;&#039;[Apply]&#039;&#039;&#039; (sofern für den Kontext sinnvoll) und &#039;&#039;&#039;[⎘ Copy]&#039;&#039;&#039;; links wird der Sprachtag (smalltalk, python, …) angezeigt.&lt;br /&gt;
* &#039;&#039;&#039;Doku-Karten&#039;&#039;&#039; (volle Breite, Prosa) entstehen aus dem speziellen &amp;lt;code&amp;gt;```doc&amp;lt;/code&amp;gt;-Block, den der Dokumentations-Generator anfordert; Inhalt ist die Vorschlags-Dokumentation für Block- und Pin-Beschreibungen.&lt;br /&gt;
&lt;br /&gt;
Scroll-Lock: solange der Anwender bereits am unteren Ende des&lt;br /&gt;
Transkripts steht, folgt die Anzeige neu eintreffenden Karten&lt;br /&gt;
automatisch; nach manuellem Hochscrollen bleibt die Position stehen.&lt;br /&gt;
&lt;br /&gt;
Bilder können als Anhang versendet werden (Screenshot oder&lt;br /&gt;
PNG/JPG-Datei).  Anhänge funktionieren mit beiden Anbietern; bei&lt;br /&gt;
OpenAI nur mit vision-fähigen Modellen (gpt-4o-Familie).&lt;br /&gt;
&lt;br /&gt;
== Einstellungen (AI Coding) ==&lt;br /&gt;
&lt;br /&gt;
Im Einstellungsdialog unter &#039;&#039;&#039;Plugins → AI Coding&#039;&#039;&#039; (bzw. unter&lt;br /&gt;
&#039;&#039;&#039;Tools → AI Coding&#039;&#039;&#039; im Smalltalk-Launcher) werden konfiguriert:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Provider&#039;&#039;&#039; — Anthropic oder OpenAI.  Beim Wechsel werden API-URL und Default-Modell entsprechend angepasst; der gespeicherte API-Schlüssel des jeweiligen Anbieters wird geladen.&lt;br /&gt;
* &#039;&#039;&#039;API Key&#039;&#039;&#039; — Schlüssel des aktuell gewählten Anbieters.  Die Schlüssel werden pro Anbieter getrennt gespeichert (&amp;lt;code&amp;gt;#claudeApiKey_anthropic&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;#claudeApiKey_openai&amp;lt;/code&amp;gt;), so dass zwischen den Anbietern ohne erneute Eingabe gewechselt werden kann.  Das Setzen des Schlüssels erfolgt ausschließlich über diesen Dialog.&lt;br /&gt;
* &#039;&#039;&#039;Model&#039;&#039;&#039; — ein Modell aus der Liste des aktiven Anbieters oder ein selbst eingegebener Modellname.  Kann auch direkt über das &#039;&#039;&#039;Set model&#039;&#039;&#039;-Untermenü in den Editor-Toolbars umgeschaltet werden.&lt;br /&gt;
* &#039;&#039;&#039;Max output tokens&#039;&#039;&#039; — maximale Antwortlänge.&lt;br /&gt;
* &#039;&#039;&#039;API URL&#039;&#039;&#039; — nur zu ändern für eigene Proxies / Gateways.  Standard: &amp;lt;code&amp;gt;https://api.anthropic.com/v1/messages&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;https://api.openai.com/v1/chat/completions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== API-Schlüssel beschaffen ==&lt;br /&gt;
&lt;br /&gt;
* Anthropic: [https://console.anthropic.com console.anthropic.com], Schlüsselformat &amp;lt;code&amp;gt;sk-ant-...&amp;lt;/code&amp;gt;.&lt;br /&gt;
* OpenAI: [https://platform.openai.com/api-keys platform.openai.com/api-keys], Schlüsselformat &amp;lt;code&amp;gt;sk-...&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;sk-proj-...&amp;lt;/code&amp;gt;.  Voraussetzung ist ein aufgeladenes Konto (Mindestbetrag derzeit USD 5).&lt;br /&gt;
&lt;br /&gt;
== Datenschutz / Datenfluss ==&lt;br /&gt;
&lt;br /&gt;
Bei aktivem Anbieter &#039;&#039;&#039;Anthropic&#039;&#039;&#039; gehen die Anfragen direkt an&lt;br /&gt;
&amp;lt;code&amp;gt;api.anthropic.com&amp;lt;/code&amp;gt;, bei &#039;&#039;&#039;OpenAI&#039;&#039;&#039; direkt an&lt;br /&gt;
&amp;lt;code&amp;gt;api.openai.com&amp;lt;/code&amp;gt;.  Es gibt keinen eXept-seitigen Proxy oder&lt;br /&gt;
Zwischenspeicher.  Mit dem Aktivitäts-Quelltext bzw. den Methoden-&lt;br /&gt;
Quelltexten werden auch Pin-Beschreibungen, Sub-Step-Namen und&lt;br /&gt;
referenzierte Environment-Variablen aus dem Block-Description-Modell&lt;br /&gt;
als Kontext versendet.&lt;br /&gt;
&lt;br /&gt;
Bei Benutzung von Anthropic API-Tokens - wie hier der Fall - werden laut Anthropic die Daten nicht zum Training des KI-Modells genutzt ([https://privacy.claude.com/de/articles/7996868-werden-meine-daten-fur-das-modelltraining-verwendet Anthropic Erkärung dazu]).&lt;br /&gt;
&lt;br /&gt;
== Tipps ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mehrere Konversationen&#039;&#039;&#039;: der Chat ist ein Singleton — eine neue Anfrage über ein Browser-/Editor-Menü startet jedesmal eine &#039;&#039;&#039;neue&#039;&#039;&#039; Konversation. Folge-Fragen (Klärung, Vertiefung) gehen über das Eingabefeld in derselben Konversation.&lt;br /&gt;
* &#039;&#039;&#039;Vorsicht:&#039;&#039;&#039; Die Größe der übertragenen Daten (auch die aus dem Chatfenster) geht in die Kostenberechnung ein. Deshalb immer eine neue Konversation starten, wenn es um ein neues Thema geht.&lt;br /&gt;
* &#039;&#039;&#039;Modell wechseln&#039;&#039;&#039;: Für schnelle Routine-Antworten Sonnet, für schwierige Refactorings Opus. Über das &#039;&#039;&#039;Set model&#039;&#039;&#039;-Untermenü direkt aus der Toolbar oder über die Settings.&lt;br /&gt;
* &#039;&#039;&#039;Anbieter wechseln&#039;&#039;&#039;: Sind Schlüssel für beide Anbieter konfiguriert, schaltet das &#039;&#039;&#039;Set provider&#039;&#039;&#039;-Untermenü ohne Umweg über den Settings-Dialog um.&lt;br /&gt;
* &#039;&#039;&#039;Code direkt ausführen&#039;&#039;&#039;: In Code-Karten kann markierter Smalltalk-Code per doIt / printIt / inspectIt direkt evaluiert werden — praktisch z.B. um vom Modell vorgeschlagene SUnit-Asserts gleich auszuprobieren, ohne den Code erst in einen Workspace zu kopieren.&lt;br /&gt;
* &#039;&#039;&#039;Custom prompt&#039;&#039;&#039; eignet sich gut für „warum macht Methode X es so und nicht so?&amp;quot; oder „schreib mir einen ParameterizedTest dazu mit folgenden Daten: …&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Plugin]]&lt;br /&gt;
[[Kategorie:Erweiterung]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31249</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31249"/>
		<updated>2026-05-18T14:00:53Z</updated>

		<summary type="html">&lt;p&gt;Sv: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q2 2026) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Feature: &#039;&#039;&#039;NEW&#039;&#039;&#039; [[KI Coding Plugin|&#039;&#039;&#039;AI Coding&#039;&#039;&#039;]] plugin (chat-assistant integration for the activity editor and class browser):&lt;br /&gt;
** supports both &#039;&#039;&#039;Anthropic Claude&#039;&#039;&#039; and &#039;&#039;&#039;OpenAI ChatGPT&#039;&#039;&#039; as backends, switchable from the settings dialog (Plugins → AI Coding); API keys are stored per provider so you can flip between them without re-entering&lt;br /&gt;
** Toolbar / class-browser menu adapts to the active provider — reads &amp;quot;Ask Claude&amp;quot; or &amp;quot;Ask ChatGPT&amp;quot;, updates live when the provider is switched&lt;br /&gt;
** menu actions: Explain code/method, Suggest improvement, Generate test, Generate doc-comment (fills the Documentation tab and pin comments), Find bugs, Custom prompt; &amp;quot;[Apply]&amp;quot; can install proposed code directly into the activity body or compile a proposed helper method into a class&lt;br /&gt;
** chat window streams responses live (Server-Sent Events) and shows running token count + estimated cost in the title; supports image attachments (screenshots / PNG-JPG files)&lt;br /&gt;
** model, API key, endpoint and max-tokens are configurable via the Claude settings dialog&lt;br /&gt;
&lt;br /&gt;
* Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;br /&gt;
* Feature: Improved/Fixed Number stack:&lt;br /&gt;
** Enhanced multiprecision numbers (eg. &amp;lt;float&amp;gt;q, &amp;lt;float&amp;gt;Q constants in freeze values)&lt;br /&gt;
** Float32 numbers (&amp;lt;float&amp;gt;f)&lt;br /&gt;
** Integer freezeValues in exponential notation (eg. 1e5)&lt;br /&gt;
** Recognize type specific infinities eg. &amp;quot;inf.0&amp;quot;, &amp;quot;inf.0f&amp;quot;, &amp;quot;inf.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** Recognize type specific NaNs eg. &amp;quot;nan.0&amp;quot;, &amp;quot;nan.0f&amp;quot;, &amp;quot;nan.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** fixed/added missing trigonometric functions for multiprecision floats and complex numbers (eg. arcTan)&lt;br /&gt;
** inspector (and activitylog as a consequence) show the type of a float (suffix &#039;f&#039;, &#039;q&#039;, &#039;Q&#039; etc.)&lt;br /&gt;
&lt;br /&gt;
* Feature: Workflow editor — improved orthogonal routing of connections:&lt;br /&gt;
** connections now detour around blocks, freeze values and annotation boxes instead of cutting through them&lt;br /&gt;
** connections from a compound block&#039;s input-pin descriptions are bundled into a bus column next to the source pin&lt;br /&gt;
** end-stub avoidance no longer fires on near-misses (strict overlap check, no clearance margin)&lt;br /&gt;
** routing prefers the source-side bend when the source step has multiple sibling pins&lt;br /&gt;
* Feature: Workflow editor — improved naïve autolayout: added horizontal and vertical expansion passes that spread adjacent blocks apart for clearer connection routing&lt;br /&gt;
* Feature: function to upload/download files from/to remote CBridges (for testData and binaries)&lt;br /&gt;
* Performance: execution of elementary Smalltalk and JavaScript actions tuned for speed (Jitter improvements)&lt;br /&gt;
* Feature: OLE for 64 bit architectures&lt;br /&gt;
* Feature: optional HTTPS for the AIDYMO and license server — drop a PEM cert+key into &amp;lt;code&amp;gt;--workDir&amp;lt;/code&amp;gt; (combined &amp;lt;code&amp;gt;server.pem&amp;lt;/code&amp;gt;, or split&amp;lt;code&amp;gt;fullchain.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;privkey.pem&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;cert.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;key.pem&amp;lt;/code&amp;gt;) and the service binds TLS automatically; informational hostname is derived from the certificate (SAN-aware, wildcard- and multi-SAN-safe)&lt;br /&gt;
&lt;br /&gt;
*Fix: many fixes related to DPI scaling. I.e. when multiple monitors are configured with different scaling (especially different from 100%). Includes scaling of fonts, bitmap and UI components (widgets).&lt;br /&gt;
*Fix: display of very long lines in a text editor/inspector (workaround Windows 16bit line limit)&lt;br /&gt;
*Fix: due to a bug in enumeration datatypes, the size of &amp;quot;.ets&amp;quot; files grew over time to huge sizes. This had no effect on the execution, but made load/save times almost unacceptably long by storing/reloading unneeded data.&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31196</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31196"/>
		<updated>2026-05-13T15:10:26Z</updated>

		<summary type="html">&lt;p&gt;Sv: /* Release 26.1 (Q2 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q2 2026) ==&lt;br /&gt;
&lt;br /&gt;
*Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: new &#039;&#039;&#039;AI Coding&#039;&#039;&#039; plugin (chat-assistant integration for the activity editor and class browser):&lt;br /&gt;
** supports both &#039;&#039;&#039;Anthropic Claude&#039;&#039;&#039; and &#039;&#039;&#039;OpenAI ChatGPT&#039;&#039;&#039; as backends, switchable from the settings dialog (Plugins → AI Coding); API keys are stored per provider so you can flip between them without re-entering&lt;br /&gt;
** Toolbar / class-browser menu adapts to the active provider — reads &amp;quot;Ask Claude&amp;quot; or &amp;quot;Ask ChatGPT&amp;quot;, updates live when the provider is switched&lt;br /&gt;
** menu actions: Explain code/method, Suggest improvement, Generate test, Generate doc-comment (fills the Documentation tab and pin comments), Find bugs, Custom prompt; &amp;quot;[Apply]&amp;quot; can install proposed code directly into the activity body or compile a proposed helper method into a class&lt;br /&gt;
** chat window streams responses live (Server-Sent Events) and shows running token count + estimated cost in the title; supports image attachments (screenshots / PNG-JPG files)&lt;br /&gt;
** model, API key, endpoint and max-tokens are configurable via the Claude settings dialog&lt;br /&gt;
&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;br /&gt;
* Feature: Improved/Fixed Number stack:&lt;br /&gt;
** Enhanced multiprecision numbers (eg. &amp;lt;float&amp;gt;q, &amp;lt;float&amp;gt;Q constants in freeze values)&lt;br /&gt;
** Float32 numbers (&amp;lt;float&amp;gt;f)&lt;br /&gt;
** Integer freezeValues in exponential notation (eg. 1e5)&lt;br /&gt;
** Recognize type specific infinities eg. &amp;quot;inf.0&amp;quot;, &amp;quot;inf.0f&amp;quot;, &amp;quot;inf.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** Recognize type specific NaNs eg. &amp;quot;nan.0&amp;quot;, &amp;quot;nan.0f&amp;quot;, &amp;quot;nan.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** fixed/added missing trigonometric functions for multiprecision floats and complex numbers (eg. arcTan)&lt;br /&gt;
** inspector (and activitylog as a consequence) show the type of a float (suffix &#039;f&#039;, &#039;q&#039;, &#039;Q&#039; etc.)&lt;br /&gt;
&lt;br /&gt;
* Feature: Workflow editor — improved orthogonal routing of connections:&lt;br /&gt;
** connections now detour around blocks, freeze values and annotation boxes instead of cutting through them&lt;br /&gt;
** connections from a compound block&#039;s input-pin descriptions are bundled into a bus column next to the source pin&lt;br /&gt;
** end-stub avoidance no longer fires on near-misses (strict overlap check, no clearance margin)&lt;br /&gt;
** routing prefers the source-side bend when the source step has multiple sibling pins&lt;br /&gt;
* Feature: Workflow editor — improved naïve autolayout: added horizontal and vertical expansion passes that spread adjacent blocks apart for clearer connection routing&lt;br /&gt;
* Feature: function to upload/download files from/to remote CBridges (for testData and binaries)&lt;br /&gt;
* Performance: execution of elementary Smalltalk and JavaScript actions tuned for speed (Jitter improvements)&lt;br /&gt;
* Feature: OLE for 64 bit architectures&lt;br /&gt;
* Feature: optional HTTPS for the AIDYMO and license server — drop a PEM cert+key into &amp;lt;code&amp;gt;--workDir&amp;lt;/code&amp;gt; (combined &amp;lt;code&amp;gt;server.pem&amp;lt;/code&amp;gt;, or split&amp;lt;code&amp;gt;fullchain.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;privkey.pem&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;cert.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;key.pem&amp;lt;/code&amp;gt;) and the service binds TLS automatically; informational hostname is derived from the certificate (SAN-aware, wildcard- and multi-SAN-safe)&lt;br /&gt;
&lt;br /&gt;
*Fix: many fixes related to DPI scaling. I.e. when multiple monitors are configured with different scaling (especially different from 100%). Includes scaling of fonts, bitmap and UI components (widgets).&lt;br /&gt;
*Fix: display of very long lines in a text editor/inspector (workaround Windows 16bit line limit)&lt;br /&gt;
*Fix: due to a bug in enumeration datatypes, the size of &amp;quot;.ets&amp;quot; files grew over time to huge sizes. This had no effect on the execution, but made load/save times almost unacceptably long by storing/reloading unneeded data.&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=KI_Coding_Plugin&amp;diff=31195</id>
		<title>KI Coding Plugin</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=KI_Coding_Plugin&amp;diff=31195"/>
		<updated>2026-05-13T15:06:10Z</updated>

		<summary type="html">&lt;p&gt;Sv: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= KI Coding Plugin =&lt;br /&gt;
&lt;br /&gt;
Das KI Coding Plugin bindet einen Large Language Model (LLM) basierten&lt;br /&gt;
KI-Assistenten in den Activity-Editor (Aktivitäten-Code) und in den&lt;br /&gt;
ST/X Class Browser ein.  Das Plugin unterstützt zwei Anbieter, die im&lt;br /&gt;
Einstellungsdialog umschaltbar sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Anthropic Claude&#039;&#039;&#039; (claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5)&lt;br /&gt;
* &#039;&#039;&#039;OpenAI ChatGPT&#039;&#039;&#039; (gpt-4o, gpt-4o-mini, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, o1, o3)&lt;br /&gt;
&lt;br /&gt;
Je nach gewähltem Anbieter erscheint die Toolbar-Schaltfläche als&lt;br /&gt;
&#039;&#039;&#039;&amp;quot;Ask Claude&amp;quot;&#039;&#039;&#039; bzw. &#039;&#039;&#039;&amp;quot;Ask ChatGPT&amp;quot;&#039;&#039;&#039;; das Einstellungs-Tab&lt;br /&gt;
heißt &#039;&#039;&#039;&amp;quot;AI Coding&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Aktivitäten-Editor ==&lt;br /&gt;
&lt;br /&gt;
Im Aktivitäten-Code-Editor erscheint in der Toolbar eine Schaltfläche&lt;br /&gt;
&amp;quot;Ask Claude&amp;quot; / &amp;quot;Ask ChatGPT&amp;quot; mit folgenden Aktionen:&lt;br /&gt;
&lt;br /&gt;
* Open KI Window — öffnet das eigenständige Chat-Fenster&lt;br /&gt;
* Explain code — erklärt den Code der aktuellen Aktivität&lt;br /&gt;
* Suggest improvement — schlägt Verbesserungen vor&lt;br /&gt;
* Find bugs — sucht nach Fehlern, Race Conditions, nil-Handling-Problemen&lt;br /&gt;
* Generate doc-comment — generiert eine Aktivitäts-Dokumentation inklusive Pin-Kommentaren und füllt den Documentation-Tab&lt;br /&gt;
* Custom prompt... — freier Prompt; der Aktivitäts-Code wird als Kontext mitgesendet&lt;br /&gt;
* Set API key... — Schlüssel des aktiven Anbieters setzen&lt;br /&gt;
&lt;br /&gt;
Code-Vorschläge können mit &#039;&#039;&#039;[Apply]&#039;&#039;&#039; im Chat direkt in den&lt;br /&gt;
Aktivitäts-Body übernommen werden.  Vom KI gelieferte Smalltalk/X&lt;br /&gt;
Hilfsmethoden (Form: &amp;lt;code&amp;gt;Klasse &amp;gt;&amp;gt; selector&amp;lt;/code&amp;gt;) werden nach&lt;br /&gt;
Rückfrage in die genannte Klasse compiliert.&lt;br /&gt;
&lt;br /&gt;
== Compound (Netzwerk) Editor ==&lt;br /&gt;
&lt;br /&gt;
Auf der Toolbar von Compound-Worksheets erscheint dieselbe&lt;br /&gt;
Schaltfläche, beschränkt auf die für Netze sinnvollen Aktionen&lt;br /&gt;
(Open KI Window, Generate doc-comment, Set API key).&lt;br /&gt;
&lt;br /&gt;
== Class Browser (ST/X) ==&lt;br /&gt;
&lt;br /&gt;
Im Class Browser stehen die Aktionen unter dem &#039;&#039;&#039;AI&#039;&#039;&#039;-Untermenü&lt;br /&gt;
sowie im Selektor-Kontextmenü zur Verfügung.  Die Aktionen&lt;br /&gt;
operieren auf der aktuell ausgewählten Methode (Klasse + Selektor +&lt;br /&gt;
Quelltext werden als Kontext mitgesendet).  &#039;&#039;&#039;[Apply]&#039;&#039;&#039; kann das&lt;br /&gt;
Resultat direkt in die Methode der aktiven Klasse einbauen.&lt;br /&gt;
&lt;br /&gt;
== Chat-Fenster ==&lt;br /&gt;
&lt;br /&gt;
Das eigenständige Chat-Fenster trägt den Titel&lt;br /&gt;
&#039;&#039;&#039;AI Coding [&amp;amp;lt;Produkt&amp;amp;gt; / &amp;amp;lt;Modell&amp;amp;gt;]&#039;&#039;&#039; (z.B.&lt;br /&gt;
&amp;quot;AI Coding [Claude / claude-opus-4-7]&amp;quot;) und zeigt nach jedem Turn&lt;br /&gt;
den Tokenverbrauch und die kumulierten Kosten — sofern Preise für&lt;br /&gt;
das gewählte Modell hinterlegt sind.  Anbieter- und Modellwechsel&lt;br /&gt;
im Einstellungsdialog werden live übernommen.&lt;br /&gt;
&lt;br /&gt;
Bilder können als Anhang versendet werden (Screenshot oder&lt;br /&gt;
PNG/JPG-Datei).  Anhänge funktionieren mit beiden Anbietern; bei&lt;br /&gt;
OpenAI nur mit vision-fähigen Modellen (gpt-4o-Familie).&lt;br /&gt;
&lt;br /&gt;
== Einstellungen (AI Coding) ==&lt;br /&gt;
&lt;br /&gt;
Im Einstellungsdialog unter &#039;&#039;&#039;Plugins → AI Coding&#039;&#039;&#039; (bzw. unter&lt;br /&gt;
&#039;&#039;&#039;Tools → AI Coding&#039;&#039;&#039; im Smalltalk-Launcher) werden konfiguriert:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Provider&#039;&#039;&#039; — Anthropic oder OpenAI.  Beim Wechsel werden API-URL und Default-Modell entsprechend angepasst; der gespeicherte API-Schlüssel des jeweiligen Anbieters wird geladen.&lt;br /&gt;
* &#039;&#039;&#039;API Key&#039;&#039;&#039; — Schlüssel des aktuell gewählten Anbieters.  Die Schlüssel werden pro Anbieter getrennt gespeichert (&amp;lt;code&amp;gt;#claudeApiKey_anthropic&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;#claudeApiKey_openai&amp;lt;/code&amp;gt;), so dass zwischen den Anbietern ohne erneute Eingabe gewechselt werden kann.&lt;br /&gt;
* &#039;&#039;&#039;Model&#039;&#039;&#039; — ein Modell aus der Liste des aktiven Anbieters oder ein selbst eingegebener Modellname.&lt;br /&gt;
* &#039;&#039;&#039;Max output tokens&#039;&#039;&#039; — maximale Antwortlänge.&lt;br /&gt;
* &#039;&#039;&#039;API URL&#039;&#039;&#039; — nur zu ändern für eigene Proxies / Gateways.  Standard: &amp;lt;code&amp;gt;https://api.anthropic.com/v1/messages&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;https://api.openai.com/v1/chat/completions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== API-Schlüssel beschaffen ==&lt;br /&gt;
&lt;br /&gt;
* Anthropic: [https://console.anthropic.com console.anthropic.com], Schlüsselformat &amp;lt;code&amp;gt;sk-ant-...&amp;lt;/code&amp;gt;.&lt;br /&gt;
* OpenAI: [https://platform.openai.com/api-keys platform.openai.com/api-keys], Schlüsselformat &amp;lt;code&amp;gt;sk-...&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;sk-proj-...&amp;lt;/code&amp;gt;.  Voraussetzung ist ein aufgeladenes Konto (Mindestbetrag derzeit USD 5).&lt;br /&gt;
&lt;br /&gt;
== Datenschutz / Datenfluss ==&lt;br /&gt;
&lt;br /&gt;
Bei aktivem Anbieter &#039;&#039;&#039;Anthropic&#039;&#039;&#039; gehen die Anfragen direkt an&lt;br /&gt;
&amp;lt;code&amp;gt;api.anthropic.com&amp;lt;/code&amp;gt;, bei &#039;&#039;&#039;OpenAI&#039;&#039;&#039; direkt an&lt;br /&gt;
&amp;lt;code&amp;gt;api.openai.com&amp;lt;/code&amp;gt;.  Es gibt keinen eXept-seitigen Proxy oder&lt;br /&gt;
Zwischenspeicher.  Mit dem Aktivitäts-Quelltext bzw. den Methoden-&lt;br /&gt;
Quelltexten werden auch Pin-Beschreibungen und Sub-Step-Namen aus&lt;br /&gt;
dem Block-Description-Modell als Kontext versendet.&lt;br /&gt;
&lt;br /&gt;
Bei Benutzung von Anthropic API-Tokens - wie hier der Fall - werden laut Anthropic die Daten nicht zum Training des KI-Modells genutzt ([https://privacy.claude.com/de/articles/7996868-werden-meine-daten-fur-das-modelltraining-verwendet Anthropic Erkärung dazu]).&lt;br /&gt;
&lt;br /&gt;
== Tipps ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mehrere Konversationen&#039;&#039;&#039;: der Chat ist ein Singleton — eine neue Anfrage über ein Browser-/Editor-Menü startet jedesmal eine &#039;&#039;&#039;neue&#039;&#039;&#039; Konversation. Folge-Fragen (Klärung, Vertiefung) gehen über das Eingabefeld in derselben Konversation.&lt;br /&gt;
* &#039;&#039;&#039;Vorsicht:&#039;&#039;&#039; Die Größe der übertragenen Daten (auch die aus dem Chatfenster) geht in die Kostenberechnung ein. Deshalb immer eine neue Konversation starten, wenn es um ein neues Thema geht. &lt;br /&gt;
* &#039;&#039;&#039;Modell wechseln&#039;&#039;&#039;: Für schnelle Routine-Antworten Sonnet, für schwierige Refactorings Opus. Über &#039;&#039;&#039;Set model…&#039;&#039;&#039; im Menü oder in den Settings.&lt;br /&gt;
* &#039;&#039;&#039;Custom prompt&#039;&#039;&#039; eignet sich gut für „warum macht Methode X es so und nicht so?&amp;quot; oder „schreib mir einen ParameterizedTest dazu mit folgenden Daten: …&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Plugin]]&lt;br /&gt;
[[Kategorie:Erweiterung]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31194</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31194"/>
		<updated>2026-05-13T11:54:00Z</updated>

		<summary type="html">&lt;p&gt;Sv: /* Release 26.1 (Q2 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q2 2026) ==&lt;br /&gt;
&lt;br /&gt;
*Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: new ClaudeCodeView — interactive Claude (Anthropic) chat window integrated into the IDE: [[KI Coding Plugin]]&lt;br /&gt;
** multi-turn conversations with streaming replies (SSE), shown live in the transcript&lt;br /&gt;
** code blocks in assistant replies are extracted and openable in a Workspace via [Apply]; callers can plug in a custom apply handler (e.g. to recompile a method)&lt;br /&gt;
** model, API key, endpoint and max-tokens are configurable via the Claude settings dialog&lt;br /&gt;
&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;br /&gt;
* Feature: Improved/Fixed Number stack:&lt;br /&gt;
** Enhanced multiprecision numbers (eg. &amp;lt;float&amp;gt;q, &amp;lt;float&amp;gt;Q constants in freeze values)&lt;br /&gt;
** Float32 numbers (&amp;lt;float&amp;gt;f)&lt;br /&gt;
** Integer freezeValues in exponential notation (eg. 1e5)&lt;br /&gt;
** Recognize type specific infinities eg. &amp;quot;inf.0&amp;quot;, &amp;quot;inf.0f&amp;quot;, &amp;quot;inf.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** Recognize type specific NaNs eg. &amp;quot;nan.0&amp;quot;, &amp;quot;nan.0f&amp;quot;, &amp;quot;nan.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** fixed/added missing trigonometric functions for multiprecision floats and complex numbers (eg. arcTan)&lt;br /&gt;
** inspector (and activitylog as a consequence) show the type of a float (suffix &#039;f&#039;, &#039;q&#039;, &#039;Q&#039; etc.)&lt;br /&gt;
&lt;br /&gt;
* Feature: Workflow editor — improved orthogonal routing of connections:&lt;br /&gt;
** connections now detour around blocks, freeze values and annotation boxes instead of cutting through them&lt;br /&gt;
** connections from a compound block&#039;s input-pin descriptions are bundled into a bus column next to the source pin&lt;br /&gt;
** end-stub avoidance no longer fires on near-misses (strict overlap check, no clearance margin)&lt;br /&gt;
** routing prefers the source-side bend when the source step has multiple sibling pins&lt;br /&gt;
* Feature: Workflow editor — improved naïve autolayout: added horizontal and vertical expansion passes that spread adjacent blocks apart for clearer connection routing&lt;br /&gt;
* Feature: function to upload/download files from/to remote CBridges (for testData and binaries)&lt;br /&gt;
* Performance: execution of elementary Smalltalk and JavaScript actions tuned for speed (Jitter improvements)&lt;br /&gt;
* Feature: OLE for 64 bit architectures&lt;br /&gt;
* Feature: optional HTTPS for the AIDYMO and license server — drop a PEM cert+key into &amp;lt;code&amp;gt;--workDir&amp;lt;/code&amp;gt; (combined &amp;lt;code&amp;gt;server.pem&amp;lt;/code&amp;gt;, or split&amp;lt;code&amp;gt;fullchain.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;privkey.pem&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;cert.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;key.pem&amp;lt;/code&amp;gt;) and the service binds TLS automatically; informational hostname is derived from the certificate (SAN-aware, wildcard- and multi-SAN-safe)&lt;br /&gt;
&lt;br /&gt;
*Fix: many fixes related to DPI scaling. I.e. when multiple monitors are configured with different scaling (especially different from 100%). Includes scaling of fonts, bitmap and UI components (widgets).&lt;br /&gt;
*Fix: display of very long lines in a text editor/inspector (workaround Windows 16bit line limit)&lt;br /&gt;
*Fix: due to a bug in enumeration datatypes, the size of &amp;quot;.ets&amp;quot; files grew over time to huge sizes. This had no effect on the execution, but made load/save times almost unacceptably long by storing/reloading unneeded data.&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31193</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31193"/>
		<updated>2026-05-13T11:53:07Z</updated>

		<summary type="html">&lt;p&gt;Sv: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q2 2026) ==&lt;br /&gt;
&lt;br /&gt;
*Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: new ClaudeCodeView — interactive Claude (Anthropic) chat window integrated into the IDE [[KI Coding Plugin]]:&lt;br /&gt;
** multi-turn conversations with streaming replies (SSE), shown live in the transcript&lt;br /&gt;
** code blocks in assistant replies are extracted and openable in a Workspace via [Apply]; callers can plug in a custom apply handler (e.g. to recompile a method)&lt;br /&gt;
** model, API key, endpoint and max-tokens are configurable via the Claude settings dialog&lt;br /&gt;
&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;br /&gt;
* Feature: Improved/Fixed Number stack:&lt;br /&gt;
** Enhanced multiprecision numbers (eg. &amp;lt;float&amp;gt;q, &amp;lt;float&amp;gt;Q constants in freeze values)&lt;br /&gt;
** Float32 numbers (&amp;lt;float&amp;gt;f)&lt;br /&gt;
** Integer freezeValues in exponential notation (eg. 1e5)&lt;br /&gt;
** Recognize type specific infinities eg. &amp;quot;inf.0&amp;quot;, &amp;quot;inf.0f&amp;quot;, &amp;quot;inf.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** Recognize type specific NaNs eg. &amp;quot;nan.0&amp;quot;, &amp;quot;nan.0f&amp;quot;, &amp;quot;nan.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** fixed/added missing trigonometric functions for multiprecision floats and complex numbers (eg. arcTan)&lt;br /&gt;
** inspector (and activitylog as a consequence) show the type of a float (suffix &#039;f&#039;, &#039;q&#039;, &#039;Q&#039; etc.)&lt;br /&gt;
&lt;br /&gt;
* Feature: Workflow editor — improved orthogonal routing of connections:&lt;br /&gt;
** connections now detour around blocks, freeze values and annotation boxes instead of cutting through them&lt;br /&gt;
** connections from a compound block&#039;s input-pin descriptions are bundled into a bus column next to the source pin&lt;br /&gt;
** end-stub avoidance no longer fires on near-misses (strict overlap check, no clearance margin)&lt;br /&gt;
** routing prefers the source-side bend when the source step has multiple sibling pins&lt;br /&gt;
* Feature: Workflow editor — improved naïve autolayout: added horizontal and vertical expansion passes that spread adjacent blocks apart for clearer connection routing&lt;br /&gt;
* Feature: function to upload/download files from/to remote CBridges (for testData and binaries)&lt;br /&gt;
* Performance: execution of elementary Smalltalk and JavaScript actions tuned for speed (Jitter improvements)&lt;br /&gt;
* Feature: OLE for 64 bit architectures&lt;br /&gt;
* Feature: optional HTTPS for the AIDYMO and license server — drop a PEM cert+key into &amp;lt;code&amp;gt;--workDir&amp;lt;/code&amp;gt; (combined &amp;lt;code&amp;gt;server.pem&amp;lt;/code&amp;gt;, or split&amp;lt;code&amp;gt;fullchain.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;privkey.pem&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;cert.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;key.pem&amp;lt;/code&amp;gt;) and the service binds TLS automatically; informational hostname is derived from the certificate (SAN-aware, wildcard- and multi-SAN-safe)&lt;br /&gt;
&lt;br /&gt;
*Fix: many fixes related to DPI scaling. I.e. when multiple monitors are configured with different scaling (especially different from 100%). Includes scaling of fonts, bitmap and UI components (widgets).&lt;br /&gt;
*Fix: display of very long lines in a text editor/inspector (workaround Windows 16bit line limit)&lt;br /&gt;
*Fix: due to a bug in enumeration datatypes, the size of &amp;quot;.ets&amp;quot; files grew over time to huge sizes. This had no effect on the execution, but made load/save times almost unacceptably long by storing/reloading unneeded data.&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31192</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31192"/>
		<updated>2026-05-13T11:46:31Z</updated>

		<summary type="html">&lt;p&gt;Sv: /* Release 26.1 (Q2 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q2 2026) ==&lt;br /&gt;
*Fix: many fixes related to DPI scaling. I.e. when multiple monitors are configured with different scaling (especially different from 100%). Includes scaling of fonts, bitmap and UI components (widgets).&lt;br /&gt;
*Fix: display of very long lines in a text editor/inspector (workaround Windows 16bit line limit)&lt;br /&gt;
*Fix: due to a bug in enumeration datatypes, the size of &amp;quot;.ets&amp;quot; files grew over time to huge sizes. This had no effect on the execution, but made load/save times almost unacceptably long by storing/reloading unneeded data.&lt;br /&gt;
*Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;br /&gt;
* Feature: Improved/Fixed Number stack:&lt;br /&gt;
** Enhanced multiprecision numbers (eg. &amp;lt;float&amp;gt;q, &amp;lt;float&amp;gt;Q constants in freeze values)&lt;br /&gt;
** Float32 numbers (&amp;lt;float&amp;gt;f)&lt;br /&gt;
** Integer freezeValues in exponential notation (eg. 1e5)&lt;br /&gt;
** Recognize type specific infinities eg. &amp;quot;inf.0&amp;quot;, &amp;quot;inf.0f&amp;quot;, &amp;quot;inf.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** Recognize type specific NaNs eg. &amp;quot;nan.0&amp;quot;, &amp;quot;nan.0f&amp;quot;, &amp;quot;nan.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** fixed/added missing trigonometric functions for multiprecision floats and complex numbers (eg. arcTan)&lt;br /&gt;
** inspector (and activitylog as a consequence) show the type of a float (suffix &#039;f&#039;, &#039;q&#039;, &#039;Q&#039; etc.)&lt;br /&gt;
* Feature: Workflow editor — improved orthogonal routing of connections:&lt;br /&gt;
** connections now detour around blocks, freeze values and annotation boxes instead of cutting through them&lt;br /&gt;
** connections from a compound block&#039;s input-pin descriptions are bundled into a bus column next to the source pin&lt;br /&gt;
** end-stub avoidance no longer fires on near-misses (strict overlap check, no clearance margin)&lt;br /&gt;
** routing prefers the source-side bend when the source step has multiple sibling pins&lt;br /&gt;
* Feature: Workflow editor — improved naïve autolayout: added horizontal and vertical expansion passes that spread adjacent blocks apart for clearer connection routing&lt;br /&gt;
* Feature: function to upload/download files from/to remote CBridges (for testData and binaries)&lt;br /&gt;
* Performance: execution of elementary Smalltalk and JavaScript actions tuned for speed (Jitter improvements)&lt;br /&gt;
* Feature: OLE for 64 bit architectures&lt;br /&gt;
* Feature: optional HTTPS for the AIDYMO and license server — drop a PEM cert+key into &amp;lt;code&amp;gt;--workDir&amp;lt;/code&amp;gt; (combined &amp;lt;code&amp;gt;server.pem&amp;lt;/code&amp;gt;, or split&amp;lt;code&amp;gt;fullchain.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;privkey.pem&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;cert.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;key.pem&amp;lt;/code&amp;gt;) and the service binds TLS automatically; informational hostname is derived from the certificate (SAN-aware, wildcard- and multi-SAN-safe)&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31191</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=31191"/>
		<updated>2026-05-12T08:05:30Z</updated>

		<summary type="html">&lt;p&gt;Sv: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q2 2026) ==&lt;br /&gt;
*Fix: many fixes related to DPI scaling. I.e. when multiple monitors are configured with different scaling (especially different from 100%). Includes scaling of fonts, bitmap and UI components (widgets).&lt;br /&gt;
*Fix: display of very long lines in a text editor/inspector (workaround Windows 16bit line limit)&lt;br /&gt;
*Fix: due to a bug in enumeration datatypes, the size of &amp;quot;.ets&amp;quot; files grew over time to huge sizes. This had no effect on the execution, but made load/save times almost unacceptably long by storing/reloading unneeded data.&lt;br /&gt;
*Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;br /&gt;
* Feature: Improved/Fixed Number stack:&lt;br /&gt;
** Enhanced multiprecision numbers (eg. &amp;lt;float&amp;gt;q, &amp;lt;float&amp;gt;Q constants in freeze values)&lt;br /&gt;
** Float32 numbers (&amp;lt;float&amp;gt;f)&lt;br /&gt;
** Integer freezeValues in exponential notation (eg. 1e5)&lt;br /&gt;
** Recognize type specific infinities eg. &amp;quot;inf.0&amp;quot;, &amp;quot;inf.0f&amp;quot;, &amp;quot;inf.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** Recognize type specific NaNs eg. &amp;quot;nan.0&amp;quot;, &amp;quot;nan.0f&amp;quot;, &amp;quot;nan.0q&amp;quot; etc. (useful when parsing/receiving values from the outside world)&lt;br /&gt;
** fixed/added missing trigonometric functions for multiprecision floats and complex numbers (eg. arcTan)&lt;br /&gt;
** inspector (and activitylog as a consequence) show the type of a float (suffix &#039;f&#039;, &#039;q&#039;, &#039;Q&#039; etc.)&lt;br /&gt;
&lt;br /&gt;
* Feature: function to upload/download files from/to remote CBridges (for testData and binaries)&lt;br /&gt;
* Performance: execution of elementary Smalltalk and JavaScript actions tuned for speed (Jitter improvements)&lt;br /&gt;
* Feature: OLE for 64 bit architectures&lt;br /&gt;
* Feature: optional HTTPS for the AIDYMO and license server — drop a PEM cert+key into &amp;lt;code&amp;gt;--workDir&amp;lt;/code&amp;gt; (combined &amp;lt;code&amp;gt;server.pem&amp;lt;/code&amp;gt;, or split&amp;lt;code&amp;gt;fullchain.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;privkey.pem&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;cert.pem&amp;lt;/code&amp;gt;+&amp;lt;code&amp;gt;key.pem&amp;lt;/code&amp;gt;) and the service binds TLS automatically; informational hostname is derived from the certificate (SAN-aware, wildcard- and multi-SAN-safe)&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=KI_Coding_Plugin&amp;diff=31163</id>
		<title>KI Coding Plugin</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=KI_Coding_Plugin&amp;diff=31163"/>
		<updated>2026-05-07T07:47:51Z</updated>

		<summary type="html">&lt;p&gt;Sv: /* Voraussetzungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= KI-Plugin (Claude Code) =&lt;br /&gt;
&lt;br /&gt;
Das KI-Plugin bindet das Anthropic-Claude-Sprachmodell direkt in den expecco-Activity-Editor und in den ST/X-Klassenbrowser ein. Damit lassen sich Activities und Methoden erklären, verbessern, kommentieren oder auf Bugs untersuchen — Antworten erscheinen in einem eigenen Chat-Fenster und können per Knopfdruck wieder ins ursprüngliche Editor-Fenster zurückgespielt werden.&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
&lt;br /&gt;
* expecco mit installiertem Plugin &amp;lt;code&amp;gt;exept:expecco/plugin/claudeCodePlugin&amp;lt;/code&amp;gt; (im Lieferumfang ab Version 26.1).&lt;br /&gt;
* Ein &#039;&#039;&#039;Anthropic-API-Key&#039;&#039;&#039; (beginnt mit &amp;lt;code&amp;gt;sk-ant-...&amp;lt;/code&amp;gt;). Den bekommst Du unter [https://console.anthropic.com/ console.anthropic.com] nach Anmeldung. Beachte: Anfragen werden nach Anthropic-Tarif abgerechnet (typisch wenige Cent pro Anfrage, siehe Kostenanzeige im Chat-Fenster).&lt;br /&gt;
* Verfügbares &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;-Binary auf dem System (wird vom Plugin für die HTTPS-Verbindung verwendet). Unter Windows wird die mit expecco ausgelieferte Version genutzt; auf Linux/macOS in der Regel das System-&amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Einrichtung ==&lt;br /&gt;
&lt;br /&gt;
=== API-Key setzen ===&lt;br /&gt;
&lt;br /&gt;
# Menü &#039;&#039;&#039;Extras → Einstellungen…&#039;&#039;&#039; öffnen.&lt;br /&gt;
# In der Baumansicht den Tab &#039;&#039;&#039;Claude Code&#039;&#039;&#039; auswählen.&lt;br /&gt;
# In das Feld &#039;&#039;&#039;API key&#039;&#039;&#039; den Anthropic-Key eintragen (&amp;lt;code&amp;gt;sk-ant-…&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Optional anpassen:&lt;br /&gt;
#* &#039;&#039;&#039;Model&#039;&#039;&#039; — z.B. &amp;lt;code&amp;gt;claude-sonnet-4-6&amp;lt;/code&amp;gt; (Standard, ausgewogen) oder &amp;lt;code&amp;gt;claude-opus-4-7&amp;lt;/code&amp;gt; (stärker, teurer)&lt;br /&gt;
#* &#039;&#039;&#039;Max tokens&#039;&#039;&#039; — Obergrenze der Antwortlänge (Standard: 4096)&lt;br /&gt;
#* &#039;&#039;&#039;API URL&#039;&#039;&#039; — nur ändern, wenn ein anderer Endpoint genutzt werden soll&lt;br /&gt;
# Mit &#039;&#039;&#039;OK&#039;&#039;&#039; schließen — der Key wird sowohl in den expecco-Preferences als auch in den User-Preferences abgelegt und überlebt damit Image-Wechsel.&lt;br /&gt;
&lt;br /&gt;
Alternativ ohne Settings-Dialog: im Chat-Menü oder über die Kontextmenüs (s.u.) gibt es einen Punkt &#039;&#039;&#039;Set API key…&#039;&#039;&#039;, der dasselbe interaktiv erledigt.&lt;br /&gt;
&lt;br /&gt;
=== Test ===&lt;br /&gt;
&lt;br /&gt;
Nach dem Setzen des Keys irgendwo eine Activity öffnen, im Code-Editor in das Toolbar-Icon &#039;&#039;&#039;Ask Claude&#039;&#039;&#039; klicken und &#039;&#039;&#039;Explain code&#039;&#039;&#039; wählen. Funktioniert die Verbindung, erscheint nach kurzer Zeit eine Antwort im Chat-Fenster.&lt;br /&gt;
&lt;br /&gt;
== Verwendung in expecco ==&lt;br /&gt;
&lt;br /&gt;
=== Toolbar im Activity-Code-Editor ===&lt;br /&gt;
&lt;br /&gt;
In jedem expecco-Activity-Editor (Smalltalk, JavaScript, Python, Ruby, …) erscheint in der Toolbar ein Eintrag &#039;&#039;&#039;Ask Claude&#039;&#039;&#039; mit Submenü:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Explain code&#039;&#039;&#039; — erläutert, was die Activity tut, inkl. Inputs/Outputs und Nebenwirkungen.&lt;br /&gt;
* &#039;&#039;&#039;Suggest improvement&#039;&#039;&#039; — schlägt konkret verbesserten Code vor (idiomatischer, klarer, robuster).&lt;br /&gt;
* &#039;&#039;&#039;Find bugs&#039;&#039;&#039; — sucht gezielt nach Fehlern, Race-Conditions, Nil-Problemen, vertauschten Branches.&lt;br /&gt;
* &#039;&#039;&#039;Generate doc-comment&#039;&#039;&#039; — generiert einen knappen Dokumentationstext.&lt;br /&gt;
* &#039;&#039;&#039;Custom prompt…&#039;&#039;&#039; — freie Eingabe; die aktuelle Activity wird als Kontext mitgeschickt.&lt;br /&gt;
* &#039;&#039;&#039;Set API key…&#039;&#039;&#039; — interaktive Key-Eingabe.&lt;br /&gt;
&lt;br /&gt;
=== Mitgeschickter Kontext ===&lt;br /&gt;
&lt;br /&gt;
Mit jeder Anfrage an Claude wird automatisch übergeben:&lt;br /&gt;
&lt;br /&gt;
* Activity-Name und (sofern hinterlegt) Beschreibung.&lt;br /&gt;
* Sprache der Activity (z.B. &amp;lt;code&amp;gt;#Smalltalk&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#PythonScript&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Liste aller Input- und Output-Pins mit Datentyp und ggf. Pin-Kommentar.&lt;br /&gt;
* Der vollständige Activity-Body.&lt;br /&gt;
&lt;br /&gt;
Dadurch kennt Claude die Schnittstelle der Activity und schlägt z.B. keine Typchecks für bereits typisierte Pins vor.&lt;br /&gt;
&lt;br /&gt;
=== Antworten und Apply ===&lt;br /&gt;
&lt;br /&gt;
Antworten erscheinen im &#039;&#039;&#039;Claude Code&#039;&#039;&#039;-Chat-Fenster (Singleton — wird beim ersten Aufruf geöffnet, danach jeweils wieder hochgeholt). Drei Buttons unter dem Verlauf:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;New conversation&#039;&#039;&#039; — startet eine neue Konversation (alter Verlauf wird verworfen).&lt;br /&gt;
* &#039;&#039;&#039;Apply&#039;&#039;&#039; — übernimmt einen Code-Block aus der Antwort:&lt;br /&gt;
** Bei Activity-Body-Vorschlag wird die aktuell offene Activity ersetzt (mit Compile-Schutz: schlägt das Compilieren fehl, bleibt der alte Code).&lt;br /&gt;
** Bei Method-Vorschlägen (z.B. Helper-Klassen-Methoden) wird automatisch ein NewSystemBrowser auf der Zielklasse geöffnet, der vorgeschlagene Code als geänderter Buffer geladen — Du kannst ihn vor dem Akzeptieren noch reviewen.&lt;br /&gt;
* &#039;&#039;&#039;Send (Ctrl-⏎)&#039;&#039;&#039; — schickt den Inhalt des Eingabefeldes als Folge-Frage (multi-turn). Während eine Antwort gestreamt wird, wird der Button zu &#039;&#039;&#039;Stop&#039;&#039;&#039; und kann den laufenden Stream abbrechen.&lt;br /&gt;
&lt;br /&gt;
==== Anhänge ====&lt;br /&gt;
&lt;br /&gt;
Über die Buttons &#039;&#039;&#039;+ Image&#039;&#039;&#039; und &#039;&#039;&#039;+ Screenshot&#039;&#039;&#039; kann man der nächsten Anfrage Bilder mitschicken (z.B. ein UI-Screenshot mit der Frage „warum sieht das so aus?&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;+ Image&#039;&#039;&#039; — File-Chooser für PNG/JPEG/GIF/WebP.&lt;br /&gt;
* &#039;&#039;&#039;+ Screenshot&#039;&#039;&#039; — interaktiv einen Bildschirmbereich auswählen.&lt;br /&gt;
&lt;br /&gt;
Die Anhänge werden mit der nächsten gesendeten Nachricht mitgeschickt und danach geleert. Ein Indikator im Send-Button zeigt die Anzahl pending Anhänge (z.B. „Send (1 img)&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==== Eingabefeld ====&lt;br /&gt;
&lt;br /&gt;
Das Eingabefeld ist mehrzeilig:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Enter&#039;&#039;&#039; — fügt einen Zeilenumbruch ein.&lt;br /&gt;
* &#039;&#039;&#039;Ctrl-Enter&#039;&#039;&#039; — sendet die Anfrage.&lt;br /&gt;
&lt;br /&gt;
== Verwendung im Klassenbrowser (NewSystemBrowser) ==&lt;br /&gt;
&lt;br /&gt;
Im ST/X-Klassenbrowser fügt das Plugin einen Eintrag &#039;&#039;&#039;Ask Claude&#039;&#039;&#039; im Methoden-Kontextmenü ein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Explain method&#039;&#039;&#039; — erläutert die selektierte Methode.&lt;br /&gt;
* &#039;&#039;&#039;Suggest improvement&#039;&#039;&#039; — schlägt verbesserten Code für die Methode vor.&lt;br /&gt;
* &#039;&#039;&#039;Generate test&#039;&#039;&#039; — generiert eine TestCase-Methode.&lt;br /&gt;
* &#039;&#039;&#039;Generate doc-comment&#039;&#039;&#039; — fügt einen Doc-Kommentar in die Methode ein (Method-Body bleibt erhalten).&lt;br /&gt;
* &#039;&#039;&#039;Find bugs&#039;&#039;&#039; — Bug-Audit für die selektierte Methode.&lt;br /&gt;
* &#039;&#039;&#039;Custom prompt…&#039;&#039;&#039; — freie Eingabe mit Methode als Kontext.&lt;br /&gt;
* &#039;&#039;&#039;Set API key…&#039;&#039;&#039; / &#039;&#039;&#039;Set model…&#039;&#039;&#039; — Setup direkt aus dem Browser heraus.&lt;br /&gt;
&lt;br /&gt;
=== Smart Context ===&lt;br /&gt;
&lt;br /&gt;
Beim Senden einer Methoden-Anfrage werden zusätzlich übergeben:&lt;br /&gt;
&lt;br /&gt;
* Klassenname, Selector und Klassen-Kategorie.&lt;br /&gt;
* Die &#039;&#039;&#039;Calls&#039;&#039;&#039;-Liste — alle Selectoren, die diese Methode aufruft, mit jeweils der implementierenden Klasse. So kennt Claude die Abhängigkeiten der Methode, ohne dass der Prompt mit fremden Quelltexten überladen wird.&lt;br /&gt;
&lt;br /&gt;
=== Apply ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate doc-comment&#039;&#039;&#039; und &#039;&#039;&#039;Suggest improvement&#039;&#039;&#039; für die &#039;&#039;&#039;aktuell selektierte Methode&#039;&#039;&#039; werden &#039;&#039;in-place&#039;&#039; im offenen Browser-Fenster geladen — kein neues Fenster, der Code-Editor zeigt direkt den modifizierten Vorschlag, den Du mit Ctrl-S akzeptieren kannst.&lt;br /&gt;
* Schlägt Claude eine &#039;&#039;&#039;andere Methode&#039;&#039;&#039; (anderer Selektor) oder eine &#039;&#039;&#039;andere Klasse&#039;&#039;&#039; vor (z.B. eine neue Helper-Methode), wird ein frischer Browser auf der Zielklasse geöffnet — der Vorschlag erscheint dort als modifizierter Buffer.&lt;br /&gt;
* Snippets, die kein Method-Definition-Format haben (z.B. Erklärungen mit Code-Beispielen), öffnen ein Workspace-Fenster zum Begutachten.&lt;br /&gt;
&lt;br /&gt;
=== Existing-Method-Routing ===&lt;br /&gt;
&lt;br /&gt;
Schlägt Claude eine Methode mit einem Selector vor, der auf der Zielklasse &#039;&#039;&#039;bereits existiert&#039;&#039;&#039;, navigiert der Browser direkt zu dieser Methode — Du siehst den Diff zwischen gespeicherter und vorgeschlagener Source und kannst gezielt akzeptieren oder verwerfen.&lt;br /&gt;
&lt;br /&gt;
== Streaming, Kosten und Verlauf ==&lt;br /&gt;
&lt;br /&gt;
* Antworten kommen &#039;&#039;&#039;live&#039;&#039;&#039; angeflossen (Token für Token), nicht erst nach Komplett-Empfang. Code-Blöcke (&amp;lt;code&amp;gt;```&amp;lt;/code&amp;gt;) werden farblich abgesetzt.&lt;br /&gt;
* &#039;&#039;&#039;Scroll-Lock&#039;&#039;&#039;: Wenn Du während des Streams hochscrollst, springt das Fenster nicht mehr zum Ende — Du kannst in Ruhe lesen, was vorher kam.&lt;br /&gt;
* Nach jeder Antwort erscheint in grau eine kleine Footer-Zeile mit Token-Verbrauch und Kostenabschätzung, z.B. &amp;lt;code&amp;gt;[in: 1234 / out: 567 tok — $0.0123]&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Im &#039;&#039;&#039;Fenstertitel&#039;&#039;&#039; steht die kumulative Summe für die laufende Sitzung (alle Anfragen seit dem Öffnen des Chat-Fensters): &amp;lt;code&amp;gt;Claude Code — 12345 in / 6789 out tok — $0.1450&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Bei Anthropic-Überlastung (HTTP 429/529) wird automatisch bis zu vier Mal mit linearem Backoff (3, 6, 9 s) wiederholt.&lt;br /&gt;
* Bei Fehler bleibt Deine zuletzt eingetippte Frage im Eingabefeld, sodass Du sie mit &#039;&#039;&#039;Send&#039;&#039;&#039; erneut absenden kannst.&lt;br /&gt;
&lt;br /&gt;
== Datenschutz ==&lt;br /&gt;
&lt;br /&gt;
Alle Anfragen gehen über HTTPS direkt an &amp;lt;code&amp;gt;api.anthropic.com&amp;lt;/code&amp;gt;. Es findet &#039;&#039;&#039;keine&#039;&#039;&#039; Zwischenspeicherung bei eXept statt; eXept hat keinen Einblick in Anfragen oder Antworten. Beachte aber, dass Anthropic nach deren Datenschutzbestimmungen Daten verarbeitet — schicke &#039;&#039;&#039;keinen produktiven, geheimen oder personenbezogenen Code/Daten&#039;&#039;&#039;, wenn das nicht zulässig ist.&amp;lt;p&amp;gt;&lt;br /&gt;
Bei Benutzung von API-Tokens - wie hier der Fall - werden laut Anthropic die Daten nicht zum Training des KI-Modells genutzt ([https://privacy.claude.com/de/articles/7996868-werden-meine-daten-fur-das-modelltraining-verwendet Anthropic Erkärung dazu]).&lt;br /&gt;
&lt;br /&gt;
== Tipps ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mehrere Konversationen&#039;&#039;&#039;: der Chat ist ein Singleton — eine neue Anfrage über ein Browser-/Editor-Menü startet jedesmal eine &#039;&#039;&#039;neue&#039;&#039;&#039; Konversation. Folge-Fragen (Klärung, Vertiefung) gehen über das Eingabefeld in derselben Konversation.&lt;br /&gt;
* &#039;&#039;&#039;Modell wechseln&#039;&#039;&#039;: Für schnelle Routine-Antworten Sonnet, für schwierige Refactorings Opus. Über &#039;&#039;&#039;Set model…&#039;&#039;&#039; im Menü oder in den Settings.&lt;br /&gt;
* &#039;&#039;&#039;Custom prompt&#039;&#039;&#039; eignet sich gut für „warum macht Methode X es so und nicht so?&amp;quot; oder „schreib mir einen ParameterizedTest dazu mit folgenden Daten: …&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* Native TLS funktioniert noch nicht zuverlässig; das Plugin ruft daher das System-&amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; per Subprozess auf.&lt;br /&gt;
* Sehr lange Activity-Bodies (&amp;gt; ein paar tausend Zeilen) können das Token-Limit der Anfrage sprengen — Claude bekommt dann nur einen Ausschnitt zu sehen. In diesem Fall: relevante Stelle in einer eigenen Helper-Method extrahieren und gezielt fragen.&lt;br /&gt;
* Inline-Markdown (&amp;lt;code&amp;gt;**fett**&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;`inline`&amp;lt;/code&amp;gt;, Headers) wird im Transcript noch nicht hervorgehoben — nur fenced Code-Blocks.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Plugin]]&lt;br /&gt;
[[Kategorie:Erweiterung]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=KI_Coding_Plugin&amp;diff=31162</id>
		<title>KI Coding Plugin</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=KI_Coding_Plugin&amp;diff=31162"/>
		<updated>2026-05-07T07:47:06Z</updated>

		<summary type="html">&lt;p&gt;Sv: Die Seite wurde neu angelegt: „= KI-Plugin (Claude Code) =  Das KI-Plugin bindet das Anthropic-Claude-Sprachmodell direkt in den expecco-Activity-Editor und in den ST/X-Klassenbrowser ein. Damit lassen sich Activities und Methoden erklären, verbessern, kommentieren oder auf Bugs untersuchen — Antworten erscheinen in einem eigenen Chat-Fenster und können per Knopfdruck wieder ins ursprüngliche Editor-Fenster zurückgespielt werden.  == Voraussetzungen ==  * expecco mit installierte…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= KI-Plugin (Claude Code) =&lt;br /&gt;
&lt;br /&gt;
Das KI-Plugin bindet das Anthropic-Claude-Sprachmodell direkt in den expecco-Activity-Editor und in den ST/X-Klassenbrowser ein. Damit lassen sich Activities und Methoden erklären, verbessern, kommentieren oder auf Bugs untersuchen — Antworten erscheinen in einem eigenen Chat-Fenster und können per Knopfdruck wieder ins ursprüngliche Editor-Fenster zurückgespielt werden.&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
&lt;br /&gt;
* expecco mit installiertem Plugin &amp;lt;code&amp;gt;exept:expecco/plugin/claudeCodePlugin&amp;lt;/code&amp;gt; (im Lieferumfang ab Version XYZ).&lt;br /&gt;
* Ein &#039;&#039;&#039;Anthropic-API-Key&#039;&#039;&#039; (beginnt mit &amp;lt;code&amp;gt;sk-ant-...&amp;lt;/code&amp;gt;). Den bekommst Du unter [https://console.anthropic.com/ console.anthropic.com] nach Anmeldung. Beachte: Anfragen werden nach Anthropic-Tarif abgerechnet (typisch wenige Cent pro Anfrage, siehe Kostenanzeige im Chat-Fenster).&lt;br /&gt;
* Verfügbares &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;-Binary auf dem System (wird vom Plugin für die HTTPS-Verbindung verwendet). Unter Windows wird die mit expecco ausgelieferte Version genutzt; auf Linux/macOS in der Regel das System-&amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Einrichtung ==&lt;br /&gt;
&lt;br /&gt;
=== API-Key setzen ===&lt;br /&gt;
&lt;br /&gt;
# Menü &#039;&#039;&#039;Extras → Einstellungen…&#039;&#039;&#039; öffnen.&lt;br /&gt;
# In der Baumansicht den Tab &#039;&#039;&#039;Claude Code&#039;&#039;&#039; auswählen.&lt;br /&gt;
# In das Feld &#039;&#039;&#039;API key&#039;&#039;&#039; den Anthropic-Key eintragen (&amp;lt;code&amp;gt;sk-ant-…&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Optional anpassen:&lt;br /&gt;
#* &#039;&#039;&#039;Model&#039;&#039;&#039; — z.B. &amp;lt;code&amp;gt;claude-sonnet-4-6&amp;lt;/code&amp;gt; (Standard, ausgewogen) oder &amp;lt;code&amp;gt;claude-opus-4-7&amp;lt;/code&amp;gt; (stärker, teurer)&lt;br /&gt;
#* &#039;&#039;&#039;Max tokens&#039;&#039;&#039; — Obergrenze der Antwortlänge (Standard: 4096)&lt;br /&gt;
#* &#039;&#039;&#039;API URL&#039;&#039;&#039; — nur ändern, wenn ein anderer Endpoint genutzt werden soll&lt;br /&gt;
# Mit &#039;&#039;&#039;OK&#039;&#039;&#039; schließen — der Key wird sowohl in den expecco-Preferences als auch in den User-Preferences abgelegt und überlebt damit Image-Wechsel.&lt;br /&gt;
&lt;br /&gt;
Alternativ ohne Settings-Dialog: im Chat-Menü oder über die Kontextmenüs (s.u.) gibt es einen Punkt &#039;&#039;&#039;Set API key…&#039;&#039;&#039;, der dasselbe interaktiv erledigt.&lt;br /&gt;
&lt;br /&gt;
=== Test ===&lt;br /&gt;
&lt;br /&gt;
Nach dem Setzen des Keys irgendwo eine Activity öffnen, im Code-Editor in das Toolbar-Icon &#039;&#039;&#039;Ask Claude&#039;&#039;&#039; klicken und &#039;&#039;&#039;Explain code&#039;&#039;&#039; wählen. Funktioniert die Verbindung, erscheint nach kurzer Zeit eine Antwort im Chat-Fenster.&lt;br /&gt;
&lt;br /&gt;
== Verwendung in expecco ==&lt;br /&gt;
&lt;br /&gt;
=== Toolbar im Activity-Code-Editor ===&lt;br /&gt;
&lt;br /&gt;
In jedem expecco-Activity-Editor (Smalltalk, JavaScript, Python, Ruby, …) erscheint in der Toolbar ein Eintrag &#039;&#039;&#039;Ask Claude&#039;&#039;&#039; mit Submenü:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Explain code&#039;&#039;&#039; — erläutert, was die Activity tut, inkl. Inputs/Outputs und Nebenwirkungen.&lt;br /&gt;
* &#039;&#039;&#039;Suggest improvement&#039;&#039;&#039; — schlägt konkret verbesserten Code vor (idiomatischer, klarer, robuster).&lt;br /&gt;
* &#039;&#039;&#039;Find bugs&#039;&#039;&#039; — sucht gezielt nach Fehlern, Race-Conditions, Nil-Problemen, vertauschten Branches.&lt;br /&gt;
* &#039;&#039;&#039;Generate doc-comment&#039;&#039;&#039; — generiert einen knappen Dokumentationstext.&lt;br /&gt;
* &#039;&#039;&#039;Custom prompt…&#039;&#039;&#039; — freie Eingabe; die aktuelle Activity wird als Kontext mitgeschickt.&lt;br /&gt;
* &#039;&#039;&#039;Set API key…&#039;&#039;&#039; — interaktive Key-Eingabe.&lt;br /&gt;
&lt;br /&gt;
=== Mitgeschickter Kontext ===&lt;br /&gt;
&lt;br /&gt;
Mit jeder Anfrage an Claude wird automatisch übergeben:&lt;br /&gt;
&lt;br /&gt;
* Activity-Name und (sofern hinterlegt) Beschreibung.&lt;br /&gt;
* Sprache der Activity (z.B. &amp;lt;code&amp;gt;#Smalltalk&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#PythonScript&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Liste aller Input- und Output-Pins mit Datentyp und ggf. Pin-Kommentar.&lt;br /&gt;
* Der vollständige Activity-Body.&lt;br /&gt;
&lt;br /&gt;
Dadurch kennt Claude die Schnittstelle der Activity und schlägt z.B. keine Typchecks für bereits typisierte Pins vor.&lt;br /&gt;
&lt;br /&gt;
=== Antworten und Apply ===&lt;br /&gt;
&lt;br /&gt;
Antworten erscheinen im &#039;&#039;&#039;Claude Code&#039;&#039;&#039;-Chat-Fenster (Singleton — wird beim ersten Aufruf geöffnet, danach jeweils wieder hochgeholt). Drei Buttons unter dem Verlauf:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;New conversation&#039;&#039;&#039; — startet eine neue Konversation (alter Verlauf wird verworfen).&lt;br /&gt;
* &#039;&#039;&#039;Apply&#039;&#039;&#039; — übernimmt einen Code-Block aus der Antwort:&lt;br /&gt;
** Bei Activity-Body-Vorschlag wird die aktuell offene Activity ersetzt (mit Compile-Schutz: schlägt das Compilieren fehl, bleibt der alte Code).&lt;br /&gt;
** Bei Method-Vorschlägen (z.B. Helper-Klassen-Methoden) wird automatisch ein NewSystemBrowser auf der Zielklasse geöffnet, der vorgeschlagene Code als geänderter Buffer geladen — Du kannst ihn vor dem Akzeptieren noch reviewen.&lt;br /&gt;
* &#039;&#039;&#039;Send (Ctrl-⏎)&#039;&#039;&#039; — schickt den Inhalt des Eingabefeldes als Folge-Frage (multi-turn). Während eine Antwort gestreamt wird, wird der Button zu &#039;&#039;&#039;Stop&#039;&#039;&#039; und kann den laufenden Stream abbrechen.&lt;br /&gt;
&lt;br /&gt;
==== Anhänge ====&lt;br /&gt;
&lt;br /&gt;
Über die Buttons &#039;&#039;&#039;+ Image&#039;&#039;&#039; und &#039;&#039;&#039;+ Screenshot&#039;&#039;&#039; kann man der nächsten Anfrage Bilder mitschicken (z.B. ein UI-Screenshot mit der Frage „warum sieht das so aus?&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;+ Image&#039;&#039;&#039; — File-Chooser für PNG/JPEG/GIF/WebP.&lt;br /&gt;
* &#039;&#039;&#039;+ Screenshot&#039;&#039;&#039; — interaktiv einen Bildschirmbereich auswählen.&lt;br /&gt;
&lt;br /&gt;
Die Anhänge werden mit der nächsten gesendeten Nachricht mitgeschickt und danach geleert. Ein Indikator im Send-Button zeigt die Anzahl pending Anhänge (z.B. „Send (1 img)&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==== Eingabefeld ====&lt;br /&gt;
&lt;br /&gt;
Das Eingabefeld ist mehrzeilig:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Enter&#039;&#039;&#039; — fügt einen Zeilenumbruch ein.&lt;br /&gt;
* &#039;&#039;&#039;Ctrl-Enter&#039;&#039;&#039; — sendet die Anfrage.&lt;br /&gt;
&lt;br /&gt;
== Verwendung im Klassenbrowser (NewSystemBrowser) ==&lt;br /&gt;
&lt;br /&gt;
Im ST/X-Klassenbrowser fügt das Plugin einen Eintrag &#039;&#039;&#039;Ask Claude&#039;&#039;&#039; im Methoden-Kontextmenü ein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Explain method&#039;&#039;&#039; — erläutert die selektierte Methode.&lt;br /&gt;
* &#039;&#039;&#039;Suggest improvement&#039;&#039;&#039; — schlägt verbesserten Code für die Methode vor.&lt;br /&gt;
* &#039;&#039;&#039;Generate test&#039;&#039;&#039; — generiert eine TestCase-Methode.&lt;br /&gt;
* &#039;&#039;&#039;Generate doc-comment&#039;&#039;&#039; — fügt einen Doc-Kommentar in die Methode ein (Method-Body bleibt erhalten).&lt;br /&gt;
* &#039;&#039;&#039;Find bugs&#039;&#039;&#039; — Bug-Audit für die selektierte Methode.&lt;br /&gt;
* &#039;&#039;&#039;Custom prompt…&#039;&#039;&#039; — freie Eingabe mit Methode als Kontext.&lt;br /&gt;
* &#039;&#039;&#039;Set API key…&#039;&#039;&#039; / &#039;&#039;&#039;Set model…&#039;&#039;&#039; — Setup direkt aus dem Browser heraus.&lt;br /&gt;
&lt;br /&gt;
=== Smart Context ===&lt;br /&gt;
&lt;br /&gt;
Beim Senden einer Methoden-Anfrage werden zusätzlich übergeben:&lt;br /&gt;
&lt;br /&gt;
* Klassenname, Selector und Klassen-Kategorie.&lt;br /&gt;
* Die &#039;&#039;&#039;Calls&#039;&#039;&#039;-Liste — alle Selectoren, die diese Methode aufruft, mit jeweils der implementierenden Klasse. So kennt Claude die Abhängigkeiten der Methode, ohne dass der Prompt mit fremden Quelltexten überladen wird.&lt;br /&gt;
&lt;br /&gt;
=== Apply ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Generate doc-comment&#039;&#039;&#039; und &#039;&#039;&#039;Suggest improvement&#039;&#039;&#039; für die &#039;&#039;&#039;aktuell selektierte Methode&#039;&#039;&#039; werden &#039;&#039;in-place&#039;&#039; im offenen Browser-Fenster geladen — kein neues Fenster, der Code-Editor zeigt direkt den modifizierten Vorschlag, den Du mit Ctrl-S akzeptieren kannst.&lt;br /&gt;
* Schlägt Claude eine &#039;&#039;&#039;andere Methode&#039;&#039;&#039; (anderer Selektor) oder eine &#039;&#039;&#039;andere Klasse&#039;&#039;&#039; vor (z.B. eine neue Helper-Methode), wird ein frischer Browser auf der Zielklasse geöffnet — der Vorschlag erscheint dort als modifizierter Buffer.&lt;br /&gt;
* Snippets, die kein Method-Definition-Format haben (z.B. Erklärungen mit Code-Beispielen), öffnen ein Workspace-Fenster zum Begutachten.&lt;br /&gt;
&lt;br /&gt;
=== Existing-Method-Routing ===&lt;br /&gt;
&lt;br /&gt;
Schlägt Claude eine Methode mit einem Selector vor, der auf der Zielklasse &#039;&#039;&#039;bereits existiert&#039;&#039;&#039;, navigiert der Browser direkt zu dieser Methode — Du siehst den Diff zwischen gespeicherter und vorgeschlagener Source und kannst gezielt akzeptieren oder verwerfen.&lt;br /&gt;
&lt;br /&gt;
== Streaming, Kosten und Verlauf ==&lt;br /&gt;
&lt;br /&gt;
* Antworten kommen &#039;&#039;&#039;live&#039;&#039;&#039; angeflossen (Token für Token), nicht erst nach Komplett-Empfang. Code-Blöcke (&amp;lt;code&amp;gt;```&amp;lt;/code&amp;gt;) werden farblich abgesetzt.&lt;br /&gt;
* &#039;&#039;&#039;Scroll-Lock&#039;&#039;&#039;: Wenn Du während des Streams hochscrollst, springt das Fenster nicht mehr zum Ende — Du kannst in Ruhe lesen, was vorher kam.&lt;br /&gt;
* Nach jeder Antwort erscheint in grau eine kleine Footer-Zeile mit Token-Verbrauch und Kostenabschätzung, z.B. &amp;lt;code&amp;gt;[in: 1234 / out: 567 tok — $0.0123]&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Im &#039;&#039;&#039;Fenstertitel&#039;&#039;&#039; steht die kumulative Summe für die laufende Sitzung (alle Anfragen seit dem Öffnen des Chat-Fensters): &amp;lt;code&amp;gt;Claude Code — 12345 in / 6789 out tok — $0.1450&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Bei Anthropic-Überlastung (HTTP 429/529) wird automatisch bis zu vier Mal mit linearem Backoff (3, 6, 9 s) wiederholt.&lt;br /&gt;
* Bei Fehler bleibt Deine zuletzt eingetippte Frage im Eingabefeld, sodass Du sie mit &#039;&#039;&#039;Send&#039;&#039;&#039; erneut absenden kannst.&lt;br /&gt;
&lt;br /&gt;
== Datenschutz ==&lt;br /&gt;
&lt;br /&gt;
Alle Anfragen gehen über HTTPS direkt an &amp;lt;code&amp;gt;api.anthropic.com&amp;lt;/code&amp;gt;. Es findet &#039;&#039;&#039;keine&#039;&#039;&#039; Zwischenspeicherung bei eXept statt; eXept hat keinen Einblick in Anfragen oder Antworten. Beachte aber, dass Anthropic nach deren Datenschutzbestimmungen Daten verarbeitet — schicke &#039;&#039;&#039;keinen produktiven, geheimen oder personenbezogenen Code/Daten&#039;&#039;&#039;, wenn das nicht zulässig ist.&amp;lt;p&amp;gt;&lt;br /&gt;
Bei Benutzung von API-Tokens - wie hier der Fall - werden laut Anthropic die Daten nicht zum Training des KI-Modells genutzt ([https://privacy.claude.com/de/articles/7996868-werden-meine-daten-fur-das-modelltraining-verwendet Anthropic Erkärung dazu]).&lt;br /&gt;
&lt;br /&gt;
== Tipps ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mehrere Konversationen&#039;&#039;&#039;: der Chat ist ein Singleton — eine neue Anfrage über ein Browser-/Editor-Menü startet jedesmal eine &#039;&#039;&#039;neue&#039;&#039;&#039; Konversation. Folge-Fragen (Klärung, Vertiefung) gehen über das Eingabefeld in derselben Konversation.&lt;br /&gt;
* &#039;&#039;&#039;Modell wechseln&#039;&#039;&#039;: Für schnelle Routine-Antworten Sonnet, für schwierige Refactorings Opus. Über &#039;&#039;&#039;Set model…&#039;&#039;&#039; im Menü oder in den Settings.&lt;br /&gt;
* &#039;&#039;&#039;Custom prompt&#039;&#039;&#039; eignet sich gut für „warum macht Methode X es so und nicht so?&amp;quot; oder „schreib mir einen ParameterizedTest dazu mit folgenden Daten: …&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Einschränkungen ==&lt;br /&gt;
&lt;br /&gt;
* Native TLS funktioniert noch nicht zuverlässig; das Plugin ruft daher das System-&amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; per Subprozess auf.&lt;br /&gt;
* Sehr lange Activity-Bodies (&amp;gt; ein paar tausend Zeilen) können das Token-Limit der Anfrage sprengen — Claude bekommt dann nur einen Ausschnitt zu sehen. In diesem Fall: relevante Stelle in einer eigenen Helper-Method extrahieren und gezielt fragen.&lt;br /&gt;
* Inline-Markdown (&amp;lt;code&amp;gt;**fett**&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;`inline`&amp;lt;/code&amp;gt;, Headers) wird im Transcript noch nicht hervorgehoben — nur fenced Code-Blocks.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Plugin]]&lt;br /&gt;
[[Kategorie:Erweiterung]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Hauptseite&amp;diff=31161</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Hauptseite&amp;diff=31161"/>
		<updated>2026-05-07T07:25:40Z</updated>

		<summary type="html">&lt;p&gt;Sv: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Hauptseite/en|English Version]]&lt;br /&gt;
&lt;br /&gt;
Klicken Sie links auf &#039;&#039;&amp;quot;Letzte Änderungen&amp;quot;&#039;&#039; (&amp;quot;Recent changes&amp;quot;) um die neuesten Einträge in der Dokumentation zu sehen.&amp;lt;br&amp;gt;Sie sehen dann meist, woran unsere Entwickler gerade vornehmlich arbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt;expecco&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; padding:0; white-space:nowrap&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Für Einsteiger&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;!-- Link mit Fragezeichen funktioniert nicht, deshalb externer Link --&amp;gt;&lt;br /&gt;
			&amp;lt;!-- &amp;lt;li&amp;gt;[[Was ist expecco?|Allgemeine Informationen]]&amp;lt;/li&amp;gt; --&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[https://doc.expecco.de/w2.x/index.php?title=Was_ist_expecco%3F Allgemeine Informationen]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Expecco Usecases|Einsatzbereiche]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Features|expecco Feature-List]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Tutorials]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[usageHints/en|Hinweise zur Bedienung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Glossary|Begriffserklärung]] (Glossar)&amp;lt;/li&amp;gt;&lt;br /&gt;
 			&amp;lt;!-- &amp;lt;li&amp;gt;[[Testorganisation|Testorganisation]]&amp;lt;/li&amp;gt; --&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[FAQ]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage5.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Installation&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Installation]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Installing additional Frameworks/en| Installation zusätzlicher Tools (Node, Python, ...)]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Personal Settings|Persönliche&amp;amp;nbsp;Einstellungen]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Configuration &amp;amp; Setup|Konfiguration]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Anbindung expecco ALM]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[SAP_Testing|Anbindung expecco an SAP]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Spezifische Anpassung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco UI]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_Remote_Control_App|expecco Mobile Remote App]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Probleme &amp;amp; Fehler]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage4.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Werkzeuge&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[API von Elementaraktionen]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Debugger]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Editoren]]&amp;lt;/li&amp;gt;		&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Standard Libraries/Bibliotheken]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Weitere Werkzeuge]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Weitere Funktionen]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage6.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Reportgenerierung&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Report Generation|Reportgenerierung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Errors during Execution|Fehler während der Ausführung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Secret Strings|Geheime Zeichenketten und Passwörter]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	 &amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Erweiterung_plugin.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Erweiterungen (Plugins)&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;li&amp;gt;[[KI Coding Plugin|NEU: KI Unterstützung beim Programmieren]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[GUI Testing]]&lt;br /&gt;
				&amp;lt;ul&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Java GUI Plugins|Java Swing/FX/SWT UI Testing]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Mobile Testing Plugin|Mobile Testing auf Android und iOS]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[WindowsAutomation Reference 2.0|UI Testing mit der Windows Automation Library]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Selenium_WebDriver_Plugin|Web Testing mit Selenium WebDriver]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                                        &amp;lt;li&amp;gt;[[VNC_Plugin_Reference|VNC UI Testing]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[GUI Testing|Weitere GUI Testing Plugins]]&amp;lt;/li&amp;gt;&lt;br /&gt;
				&amp;lt;/ul&amp;gt;&lt;br /&gt;
			&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Schnittstellen zum SUT]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[ManualTest]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;Produktivit&amp;amp;auml;t&lt;br /&gt;
				&amp;lt;ul&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[GIT Plugin]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                                        &amp;lt;li&amp;gt;[[HG Plugin (Mercurial)]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[CVS Plugin]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Anbindung expecco ALM | expeccoALM Schnittstelle]]&amp;lt;/li&amp;gt;&lt;br /&gt;
				&amp;lt;/ul&amp;gt;&lt;br /&gt;
			&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Weitere Plugins]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe;  margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage3.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Elemente der Testsuite&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Tree Elements|Tree Elemente]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Folder Element|Ordner]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Testplan Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Aktionen/Aktionsblöcke]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Datatype Element|Datentyp Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Inventory Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Skill Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Resource Element|Ressource Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Attachment Element|Anhänge]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[ReportTemplate Element|Report Templates]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color:#ecf0fe;  margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage3.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;API Referenz (Elementarbausteine)&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[API von Elementaraktionen|Übersicht]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco API#JavaScript_and_Smalltalk_Elementary_Blocks|Smalltalk &amp;amp; JavaScript]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco API#Groovy_Elementary_Blocks|Groovy]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Node.js_.28Bridged.29_Elementary_Blocks|NodeJS]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Bridged_Python_Elementary_Blocks|Python]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Bridged_Ruby_Elementary_Blocks|Ruby]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Bridged_C_Elementary_Blocks|C]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco API#VisualBasic_Elementary_Blocks|VisualBasic ]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Expecco Scripting API|Shell und Script Languages]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Useful API Functions|Useful API Functions:]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;ul&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Number API Functions|Number API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[String API Functions|String API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Collection API Functions|Collection API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Filename API Functions|Filename API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Stream API Functions|Stream API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Date_and_Time_API_Functions|Date &amp;amp; Time API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;/ul&amp;gt; &lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Diagramm.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Diagramm - Elemente&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Step|Schritt]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Pins (Ein - Ausgänge)]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Code Ausführung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Connection|Verbindung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Pin|Pin Beschreibung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Annotation|Notiz]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Probe|Messfühler]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Refresh.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Sonstiges&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 26.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 25.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 24.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 23.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 22.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 21.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 20.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 19.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 18.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 2.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 1.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Future releases expecco|Zukünftige Releases]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Smalltalk]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Beispiele_icon.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Beispiele&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
                &amp;lt;span style=&amp;quot;padding:0 1em;font-size:0.9em&amp;quot;&amp;gt;Sie haben Fragen, wie ein Projekt aussieht?&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
                &lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;amp;nbsp;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Beispiele | Hier finden Sie weitere Anwendungsbeispiele.]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Expecco Code Snippets/en | Codeschnipsel]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Beispiele_icon.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Unterstützung weiterer Qualitätsmanagement Tools&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
                &lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Jira_Plugin_Reference|Jira]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[PolarionPlugin_Reference|Polarion]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Aqua_Plugin_Reference|Aqua]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Starting_expecco_via_Command_Line#Integration_with_Jenkins|Jenkins Integration]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2 style=&amp;quot;clear:left&amp;quot;&amp;gt;expecco ALM&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; padding:0; white-space:nowrap&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Sonstiges-Info.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Allgemein&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Was ist expecco ALM?]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Was ist der expecco Lizenzserver?]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Anbindung expecco ALM]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Rollen und Rechte]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Filter]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[FAQ expeccoALM/en | FAQ]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage5.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Installation/Einrichten&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Installation|Installation]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Installation Patches|expecco ALM Installation Patches]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Einrichten Vorgehensweise|expecco ALM Einrichten]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM expecco Patch Service |Patchservice für expecco]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Refresh.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Plugins&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[AIDYMO Jira|Atlassian Jira]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[CustomerSpecific|Kundenspezifische Portale]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Refresh.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Sonstiges&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM App]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Installation Lizenzserver]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Release Notes|Release Notes]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Known Issues|Known Issues]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Plot/Graph_Action_Blocks&amp;diff=30941</id>
		<title>Plot/Graph Action Blocks</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Plot/Graph_Action_Blocks&amp;diff=30941"/>
		<updated>2026-03-03T13:37:42Z</updated>

		<summary type="html">&lt;p&gt;Sv: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
With expecco18.1, a new block type named &amp;quot;&#039;&#039;Plot/Graph Actions&#039;&#039;&amp;quot; was first introduced.&lt;br /&gt;
These behave similar to other script action blocks, in that they are defined by a script&lt;br /&gt;
(in this case: gnuplot scripting language) and are placed into the test sequence flow&lt;br /&gt;
just like any other block.&lt;br /&gt;
&lt;br /&gt;
However, when executed, these actions generate a graphic document (jpeg, png or postscript),&lt;br /&gt;
which is then either added to the activity log, or added as attachment. It will therefore be &lt;br /&gt;
available in the generated &amp;quot;&amp;lt;code&amp;gt;elf&amp;lt;/code&amp;gt;&amp;quot; execution log file, or shown in the generated &amp;quot;&amp;lt;code&amp;gt;pdf&amp;lt;/code&amp;gt;&amp;quot; report file.&lt;br /&gt;
Of course, you can do anything you like with the generated graphic file, by feeding the name&lt;br /&gt;
of the generated file to another action for further processing.&lt;br /&gt;
&lt;br /&gt;
Notice: if you prefer other graphic packages or other programming languages, you may of course use R language scripts or Python scripts (with matplotlib and/or seaboarn) to generate graphics in a similar way.&lt;br /&gt;
&lt;br /&gt;
== Creating Plot/Graph Action Blocks ==&lt;br /&gt;
&lt;br /&gt;
Plot/graph action blocks are created via the &lt;br /&gt;
&amp;quot;&#039;&#039;Actions&#039;&#039; - &#039;&#039;More&#039;&#039; - &#039;&#039;Plot/Graph&#039;&#039;&amp;quot; menu item.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Plot_Graph_Actions_in_Menu.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Select the &amp;quot;&#039;&#039;Plot/Graph&#039;&#039;&amp;quot; item, to create a new gnuplot action.&lt;br /&gt;
An initial set of input pins and an initial (demo-) script is generated for you.&lt;br /&gt;
&lt;br /&gt;
For details on the scripting language, please refer to the public  &lt;br /&gt;
[http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf gnuplot documentation].&lt;br /&gt;
&lt;br /&gt;
Any input pin named &amp;quot;&amp;lt;code&amp;gt;&amp;lt;pinName&amp;gt;&amp;lt;/code&amp;gt;&amp;quot; is available in the script as &amp;quot;&amp;lt;code&amp;gt;%(pinName)&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
Thus, the script can be parametrized in arbitrary ways.&lt;br /&gt;
A number of pins are already created for you; these are:&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;outputFile&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;both an input and an output. If the input is specified, gnuplot will generate the plot into that file. Otherwise, a new temporary file will be created and the output written into that. In any case will the actual outputFileName be available on the corresponding output pin for further processing.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;outputFormat&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;specifies the kind of file to generate. For inclusion into the report, an image format should be chosen (i.e. one of &amp;quot;jpeg&amp;quot;, &amp;quot;png&amp;quot; or &amp;quot;gif&amp;quot;). For archival or to generate high quality documents, you can also choose &amp;quot;postscript&amp;quot; or &amp;quot;svg&amp;quot;. However, those can (currently) not be automatically converted for the pdf report.&amp;lt;br&amp;gt;On Unix systems, you can also specify &amp;quot;x11&amp;quot; as output format. In this case, a window is opened, displaying the generated graph. Be aware that expecco is (currently) not informed about the window being closed, so you may have to stop the test-run via the expecco-&amp;quot;Stop&amp;quot; button. &lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;title&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;A title string to be drawn above the graphic (actually, the position of the title is specified by the &amp;quot;set key&amp;quot; gnuplot expression, inside the script)&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;data&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;If connected, and a collection of values is present, these values will be written into a temporary data file, and the filename is available in the gnuplot script as &amp;quot;&amp;lt;code&amp;gt;%(dataFile)&amp;lt;/code&amp;gt;&amp;quot;. This will be the raw data, to be processed by the script.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;dataSize&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;There is no pin for that, but the value is available to the script. It provides the number of data points (or rows) in the data file.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;attachToLog&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;if true (the default), the generated graph will be attached to the final pdf report. This pin&#039;s value is not forwarded to the gnuplot script.&lt;br /&gt;
&lt;br /&gt;
You can add additional input pins and refer to the corresponding pin value in the script with the &amp;quot;%(...)&amp;quot; notation.&lt;br /&gt;
&lt;br /&gt;
== A Simple Graph Script ==&lt;br /&gt;
&lt;br /&gt;
A typical gnuplot script looks like:&lt;br /&gt;
&lt;br /&gt;
 # The code below is sent to gnuplot and the generated output&lt;br /&gt;
 # will be stored in a file named %%(outputFile) &lt;br /&gt;
 # (unless explicitly specified, a temporary file is created)&lt;br /&gt;
 #&lt;br /&gt;
 set output &amp;quot;%(outputFile)&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
 # &lt;br /&gt;
 set term %(outputFormat)&lt;br /&gt;
 &lt;br /&gt;
 # All input pin values can be used here as %%(nameOfPin).&lt;br /&gt;
 # so the next line controls the title via the &amp;quot;title&amp;quot; pin&lt;br /&gt;
 #&lt;br /&gt;
 set title &amp;quot;%(title)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # You can add more pins.&lt;br /&gt;
 # For example, add pins named &amp;quot;min&amp;quot; and &amp;quot;max&amp;quot; with type Number,&lt;br /&gt;
 # and replace &amp;quot;-10:10&amp;quot; below by &amp;quot;%%(min):%%(max) to parametrize the y-axis range.&lt;br /&gt;
 set key left box&lt;br /&gt;
 plot [1:%(dataSize)] [-10:10] &amp;quot;%(dataFile)&amp;quot; title &amp;quot;My Data&amp;quot; with impulses&lt;br /&gt;
&lt;br /&gt;
to plot a simple impulse graph of the data values, as presented at the input pin,&lt;br /&gt;
assuming that the pins get the following values:&lt;br /&gt;
* &amp;quot;outputFile&amp;quot; - leave unconnected or remove the pin (to let the action choose a temp file)&lt;br /&gt;
* &amp;quot;outputFormat&amp;quot; - freeze as &amp;quot;&amp;lt;code&amp;gt;png&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
* &amp;quot;title&amp;quot; - any title - such as &amp;quot;Measurement #1&amp;quot;&lt;br /&gt;
* &amp;quot;data&amp;quot; - a vector of numbers&lt;br /&gt;
&lt;br /&gt;
Of course, this is actually the simplest possible script to render such data.&lt;br /&gt;
Gnuplot provides a wide area of possible plots, both 2D and 3D, with or without colors, etc.&lt;br /&gt;
A few examples are provided in the &amp;quot;&amp;lt;code&amp;gt;Plot_Graph Library.ets&amp;lt;/code&amp;gt;&amp;quot;, but much more is found on the gnuplot&lt;br /&gt;
website [http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf gnu plot documentation].&lt;br /&gt;
&lt;br /&gt;
== Short List of Useful Script Commands ==&lt;br /&gt;
&lt;br /&gt;
For a full list, please take a look at the [http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf gnu plot documentation]. The following list is only presenting the most common plot commands:&lt;br /&gt;
* &amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with impulses&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;You can omit the &amp;quot;[yMin:yMax]&amp;quot; parameter, for a reasonable box to be automatically chosen by gnuplot.&amp;lt;br&amp;gt;Replace &amp;quot;nameOfDataFile&amp;quot; by &amp;quot;%(dataFile)&amp;quot; if the data comes via an input pin and is not already in a file.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;Without the &amp;quot;impulses&amp;quot; parameter, a regular graph is drawn (straight lines between points)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with: lines&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;The same as above (straight lines between points)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with: dots&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;Draws dots&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with candlesticks&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;the data file should contain multiple colums per line. Select the colums with a &amp;quot;using...&amp;quot; parameter.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set key left box&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines the position (left) and type (boxed) of the graph-title (the string comes from the &amp;lt;code&amp;gt;title &amp;quot;graph title&amp;quot;&amp;lt;/code&amp;gt; argument in the plot command)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set title &amp;quot;someTitle&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines a headline title&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set term %(outputFormat)&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines the type of image to be generated. For &amp;quot;%(outputFormat)&amp;quot;, the input pin&#039;s format is generated. Otherwise, you can also hardcode any of &amp;quot;&amp;lt;code&amp;gt;png&amp;lt;/code&amp;gt;&amp;quot;, &amp;quot;&amp;lt;code&amp;gt;jpg&amp;lt;/code&amp;gt;&amp;quot;, etc. here&lt;br /&gt;
&lt;br /&gt;
A few examples are found in the demo library: &amp;quot;Plot_Graph_Library.ets&amp;quot;, which is found in the plugin&#039;s folder and in the online documentation [http://www.gnuplot.info/docs_5.0/gnuplot.pdf &amp;quot;http://www.gnuplot.info/docs_5.0/gnuplot.pdf&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
== Accessing Data from Attachments ==&lt;br /&gt;
&lt;br /&gt;
You can directly access expecco attachment files by their name inside the script. I.e. if you have an attachment with file named &amp;quot;&amp;lt;code&amp;gt;foo.dat&amp;lt;/code&amp;gt;&amp;quot;, use &amp;lt;code&amp;gt;... data &amp;quot;foo.dat&amp;quot; ...&amp;lt;/code&amp;gt; inside the script. Notice that the attchment&#039;s filename is to be used - not its name in the tree.&lt;br /&gt;
&lt;br /&gt;
== Plotting Multiple Graphs into a Single Picture ==&lt;br /&gt;
&lt;br /&gt;
To do this, generate multiple datasets (as ascii files, with one textline per row). Add &amp;lt;code&amp;gt;data&amp;amp;lt;i&amp;amp;gt;&amp;lt;/code&amp;gt; input pins and feed them the filenames. Add multiple &amp;lt;code&amp;gt;plot&amp;lt;/code&amp;gt; commands, each with its own &amp;quot;&amp;lt;code&amp;gt;%(data&amp;amp;lt;i&amp;amp;gt;&amp;lt;/code&amp;gt;)&amp;quot; argument, and possibly with its own graph-title.&lt;br /&gt;
&lt;br /&gt;
== Changing the Size of the Generated Image ==&lt;br /&gt;
&lt;br /&gt;
By default, the generated image&#039;s size is 640 x 400 pixels.&lt;br /&gt;
For a fix size, change the script&#039;s &amp;quot;&amp;lt;code&amp;gt;set term&amp;lt;/code&amp;gt;&amp;quot; statement to:&lt;br /&gt;
&lt;br /&gt;
 set term %(outputFormat) size &amp;lt;width&amp;gt;,&amp;lt;height&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &amp;amp;lt;width&amp;amp;gt; and &amp;amp;lt;height&amp;amp;gt; define the size in pixels.&lt;br /&gt;
&lt;br /&gt;
Alternatively, add two pins named &amp;quot;&#039;&#039;width&#039;&#039;&amp;quot; and &amp;quot;&#039;&#039;height&#039;&#039;&amp;quot;, and&lt;br /&gt;
change the statement to:&lt;br /&gt;
&lt;br /&gt;
 set term %(outputFormat) size %(width),%(height)&lt;br /&gt;
&lt;br /&gt;
and provide the desired size (as integers) at the input pins.&lt;br /&gt;
&lt;br /&gt;
== Caveats ==&lt;br /&gt;
&lt;br /&gt;
Do not place &amp;lt;code&amp;gt;pause&amp;lt;/code&amp;gt; statements into the script; there is no input stream given to gnuplot, so it will immediately read an EOF (end of file) and proceed.&lt;br /&gt;
&lt;br /&gt;
== Complete Example 1: Gathering Response Times of MQTT Protocol Messages ==&lt;br /&gt;
&lt;br /&gt;
This example demonstrates, how a test suite can gather response times of a remote system,&lt;br /&gt;
when sending MQTT messages to it.&lt;br /&gt;
&lt;br /&gt;
==== Step 1: Define the Test Sequence ====&lt;br /&gt;
&lt;br /&gt;
this is a very simple publish-response setup, in that a topic is published to an MQTT broker,&lt;br /&gt;
and the time measured, until the notification arrives (in order to run this on your machine,&lt;br /&gt;
the expecco MQTT plugin needs to be installed):&lt;br /&gt;
&lt;br /&gt;
Outside (Schema) view of the measuring action:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Schema_Measuring_MQTT.png|400px]]&lt;br /&gt;
&lt;br /&gt;
which is implemented as:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Net_Measuring_MQTT.png|800px]]&lt;br /&gt;
&lt;br /&gt;
The yellow area does the MQTT setup. First, an MQTT client connection is created,&lt;br /&gt;
and the client handle forwarded to the Subscribe action, which registers expecco&lt;br /&gt;
to react to the &amp;quot;expeccoTesting/test&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
The green area does the actual publish operation,&lt;br /&gt;
publishing the &amp;quot;Hello from Expecco&amp;quot; string on this topic.&lt;br /&gt;
&lt;br /&gt;
In parallel, the blue area waits for the next incoming event,&lt;br /&gt;
(and generates a printed detail info on the received packet for the&lt;br /&gt;
activity log).&lt;br /&gt;
&lt;br /&gt;
The red area contains a stopWatch action block, which gets started&lt;br /&gt;
with when the publish is done, and stops when the event arrives.&lt;br /&gt;
&lt;br /&gt;
The measurement action is prametrized with the broker&#039;s host and port,&lt;br /&gt;
and generates the measured timeDuration as output.&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Test Run ====&lt;br /&gt;
&lt;br /&gt;
Assuming that you have an MQTT client running on the local host,&lt;br /&gt;
the test sequence can be immediately tried via the Test/Demo page:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Test_Measuring_MQTT.png|300px]]&lt;br /&gt;
&lt;br /&gt;
A good strategy is to package the MQTT broker into a docker file.&lt;br /&gt;
Here is the MQTT client&#039;s output of the above run:&lt;br /&gt;
&lt;br /&gt;
 $ sh rundocker.sh &lt;br /&gt;
 1522084243: mosquitto version 1.4.8 (build date Thu, 01 Mar 2018 09:34:49 -0500) starting&lt;br /&gt;
 1522084243: Using default config.&lt;br /&gt;
 1522084243: Opening ipv4 listen socket on port 1883.&lt;br /&gt;
 1522084243: Opening ipv6 listen socket on port 1883.&lt;br /&gt;
 1522084272: New connection from 172.17.0.1 on port 1883.&lt;br /&gt;
 1522084272: New client connected from 172.17.0.1 as expeccoMQTT (c1, k0).&lt;br /&gt;
 1522084272: Client expeccoMQTT disconnected.&lt;br /&gt;
&lt;br /&gt;
and here the activitylog in expecco:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log1_Measuring_MQTT.png|600px]]&lt;br /&gt;
&lt;br /&gt;
with the event info detail:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log2_Measuring_MQTT.png|600px]]&lt;br /&gt;
&lt;br /&gt;
==== Step 3: Running that Sequence Multiple Times ====&lt;br /&gt;
&lt;br /&gt;
Now that we have verified, that the communication with the MQTT broker works correctly,&lt;br /&gt;
we create another action bock, which runs the above action multiple times and collects&lt;br /&gt;
all the execution times:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT.png|700px]]&lt;br /&gt;
&lt;br /&gt;
[ explanation: first, a new empty colletion is created, then, the timing values of&lt;br /&gt;
the multi-triggered measure-step are all added to this collection.&lt;br /&gt;
When done (no step active), the collection is forwarded (now filled) to the output pin]&lt;br /&gt;
&lt;br /&gt;
Here is the activity log after 10 runs:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log1_Multiple_Measuring_MQTT.png|700px]]&lt;br /&gt;
&lt;br /&gt;
==== Step 4: Adding the Graph ====&lt;br /&gt;
&lt;br /&gt;
The last step is the easiest: select the &amp;quot;New Plot/Graph&amp;quot; action block from the menu,&lt;br /&gt;
name it &amp;quot;Time Measurement Graph&amp;quot;&lt;br /&gt;
and change its autogenerated gnuplot script to:&lt;br /&gt;
&lt;br /&gt;
 set output &amp;quot;%(outputFile)&amp;quot;&lt;br /&gt;
 set term %(outputFormat)&lt;br /&gt;
 set title &amp;quot;%(title)&amp;quot;&lt;br /&gt;
 set key left box&lt;br /&gt;
 plot [1:%(dataSize)] [0:100] &amp;quot;%(dataFile)&amp;quot; title &amp;quot;%(lineTitle)&amp;quot; with impulses&lt;br /&gt;
&lt;br /&gt;
then place a step of it into your network and connect the &amp;quot;collectedTimes&amp;quot; output of the above measurement step with the &amp;quot;data&amp;quot; input&lt;br /&gt;
of the &amp;quot;Measurement-Graph&amp;quot; step:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_plus_Graph_MQTT.png|500px]]&lt;br /&gt;
&lt;br /&gt;
Running it, and generating the report, produces the following pdf-report (the logging of subactivities was disabled for this report):&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT_Report_img.png|700px]]&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT_Report.pdf]]&lt;br /&gt;
&lt;br /&gt;
== Example 1 Simplified: Using Probes to Gather the Data ==&lt;br /&gt;
&lt;br /&gt;
The above data collection may look a bit complicated, due to the need for start/stop synchronization.&lt;br /&gt;
A much nicer and easier to use solution is in using a probe to gather the data.&lt;br /&gt;
For this, we need a probe which does no range checks, but instead only collects the incoming values:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_Probe.png|700px]]&lt;br /&gt;
&lt;br /&gt;
Here, a probe was added (via the &amp;quot;timeDelta&amp;quot; pin&#039;s &amp;quot;Add Probe&amp;quot; menu item),&lt;br /&gt;
and specified to &amp;quot;collect incoming data without check&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_ProbeMenu.png|500px]]&lt;br /&gt;
&lt;br /&gt;
Now that we have a probe at the roundtrip time,&lt;br /&gt;
the outer measurement looks much simpler:&lt;br /&gt;
&lt;br /&gt;
* reset the probe (to clear any previously gathered data)&lt;br /&gt;
* prime the probe enable data collecting)&lt;br /&gt;
* run the roundtrip-test n-times&lt;br /&gt;
* unprime the probe (disable further data collecting)&lt;br /&gt;
* fetch the probe&#039;s values and pass them to the gnuplot action&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_Probe2.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
[http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf Gnuplot Manual]&lt;br /&gt;
&amp;lt;br&amp;gt;[https://alvinalexander.com/technology/gnuplot-charts-graphs-examples Collection of Examples]&lt;br /&gt;
&lt;br /&gt;
&amp;quot;[[R Action Blocks]]&amp;quot; which provide similar graph/plot functions in addition to sophisticated mathematical and statistics libraries.&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;quot;[[Octave Script Action Tutorial|Octave/Matlab Actions]]&amp;quot; another framework for mathematical computations and/or to generate graphs/plots.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- {{Languages}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category: Languages ]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Plot/Graph_Action_Blocks&amp;diff=30940</id>
		<title>Plot/Graph Action Blocks</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Plot/Graph_Action_Blocks&amp;diff=30940"/>
		<updated>2026-03-03T13:36:32Z</updated>

		<summary type="html">&lt;p&gt;Sv: Renamed %(data) to %(dataFile)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
With expecco18.1, a new block type named &amp;quot;&#039;&#039;Plot/Graph Actions&#039;&#039;&amp;quot; is introduced.&lt;br /&gt;
These behave similar to other script action blocks, in that they are defined by a script&lt;br /&gt;
(in this case: gnuplot scripting language) and are placed into the test sequence flow&lt;br /&gt;
just like any other block.&lt;br /&gt;
&lt;br /&gt;
However, when executed, these actions generate a graphic document (jpeg, png or postscript),&lt;br /&gt;
which is then either added to the activity log, or added as attachment. It will therefore be &lt;br /&gt;
available in the generated &amp;quot;&amp;lt;code&amp;gt;elf&amp;lt;/code&amp;gt;&amp;quot; execution log file, or shown in the generated &amp;quot;&amp;lt;code&amp;gt;pdf&amp;lt;/code&amp;gt;&amp;quot; report file.&lt;br /&gt;
Of course, you can do anything you like with the generated graphic file, by feeding the name&lt;br /&gt;
of the generated file to another action for further processing.&lt;br /&gt;
&lt;br /&gt;
Notice: if you prefer other graphic packages or other programming languages, you may of course use R language scripts or Python scripts (with matplotlib and/or seaboarn) to generate graphics in a similar way.&lt;br /&gt;
&lt;br /&gt;
== Creating Plot/Graph Action Blocks ==&lt;br /&gt;
&lt;br /&gt;
Plot/graph action blocks are created via the &lt;br /&gt;
&amp;quot;&#039;&#039;Actions&#039;&#039; - &#039;&#039;More&#039;&#039; - &#039;&#039;Plot/Graph&#039;&#039;&amp;quot; menu item.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Plot_Graph_Actions_in_Menu.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Select the &amp;quot;&#039;&#039;Plot/Graph&#039;&#039;&amp;quot; item, to create a new gnuplot action.&lt;br /&gt;
An initial set of input pins and an initial (demo-) script is generated for you.&lt;br /&gt;
&lt;br /&gt;
For details on the scripting language, please refer to the public  &lt;br /&gt;
[http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf gnuplot documentation].&lt;br /&gt;
&lt;br /&gt;
Any input pin named &amp;quot;&amp;lt;code&amp;gt;&amp;lt;pinName&amp;gt;&amp;lt;/code&amp;gt;&amp;quot; is available in the script as &amp;quot;&amp;lt;code&amp;gt;%(pinName)&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
Thus, the script can be parametrized in arbitrary ways.&lt;br /&gt;
A number of pins are already created for you; these are:&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;outputFile&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;both an input and an output. If the input is specified, gnuplot will generate the plot into that file. Otherwise, a new temporary file will be created and the output written into that. In any case will the actual outputFileName be available on the corresponding output pin for further processing.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;outputFormat&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;specifies the kind of file to generate. For inclusion into the report, an image format should be chosen (i.e. one of &amp;quot;jpeg&amp;quot;, &amp;quot;png&amp;quot; or &amp;quot;gif&amp;quot;). For archival or to generate high quality documents, you can also choose &amp;quot;postscript&amp;quot; or &amp;quot;svg&amp;quot;. However, those can (currently) not be automatically converted for the pdf report.&amp;lt;br&amp;gt;On Unix systems, you can also specify &amp;quot;x11&amp;quot; as output format. In this case, a window is opened, displaying the generated graph. Be aware that expecco is (currently) not informed about the window being closed, so you may have to stop the test-run via the expecco-&amp;quot;Stop&amp;quot; button. &lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;title&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;A title string to be drawn above the graphic (actually, the position of the title is specified by the &amp;quot;set key&amp;quot; gnuplot expression, inside the script)&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;data&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;If connected, and a collection of values is present, these values will be written into a temporary data file, and the filename is available in the gnuplot script as &amp;quot;&amp;lt;code&amp;gt;%(dataFile)&amp;lt;/code&amp;gt;&amp;quot;. This will be the raw data, to be processed by the script.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;dataSize&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;There is no pin for that, but the value is available to the script. It provides the number of data points (or rows) in the data file.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;attachToLog&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;if true (the default), the generated graph will be attached to the final pdf report. This pin&#039;s value is not forwarded to the gnuplot script.&lt;br /&gt;
&lt;br /&gt;
You can add additional input pins and refer to the corresponding pin value in the script with the &amp;quot;%(...)&amp;quot; notation.&lt;br /&gt;
&lt;br /&gt;
== A Simple Graph Script ==&lt;br /&gt;
&lt;br /&gt;
A typical gnuplot script looks like:&lt;br /&gt;
&lt;br /&gt;
 # The code below is sent to gnuplot and the generated output&lt;br /&gt;
 # will be stored in a file named %%(outputFile) &lt;br /&gt;
 # (unless explicitly specified, a temporary file is created)&lt;br /&gt;
 #&lt;br /&gt;
 set output &amp;quot;%(outputFile)&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
 # &lt;br /&gt;
 set term %(outputFormat)&lt;br /&gt;
 &lt;br /&gt;
 # All input pin values can be used here as %%(nameOfPin).&lt;br /&gt;
 # so the next line controls the title via the &amp;quot;title&amp;quot; pin&lt;br /&gt;
 #&lt;br /&gt;
 set title &amp;quot;%(title)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # You can add more pins.&lt;br /&gt;
 # For example, add pins named &amp;quot;min&amp;quot; and &amp;quot;max&amp;quot; with type Number,&lt;br /&gt;
 # and replace &amp;quot;-10:10&amp;quot; below by &amp;quot;%%(min):%%(max) to parametrize the y-axis range.&lt;br /&gt;
 set key left box&lt;br /&gt;
 plot [1:%(dataSize)] [-10:10] &amp;quot;%(dataFile)&amp;quot; title &amp;quot;My Data&amp;quot; with impulses&lt;br /&gt;
&lt;br /&gt;
to plot a simple impulse graph of the data values, as presented at the input pin,&lt;br /&gt;
assuming that the pins get the following values:&lt;br /&gt;
* &amp;quot;outputFile&amp;quot; - leave unconnected or remove the pin (to let the action choose a temp file)&lt;br /&gt;
* &amp;quot;outputFormat&amp;quot; - freeze as &amp;quot;&amp;lt;code&amp;gt;png&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
* &amp;quot;title&amp;quot; - any title - such as &amp;quot;Measurement #1&amp;quot;&lt;br /&gt;
* &amp;quot;data&amp;quot; - a vector of numbers&lt;br /&gt;
&lt;br /&gt;
Of course, this is actually the simplest possible script to render such data.&lt;br /&gt;
Gnuplot provides a wide area of possible plots, both 2D and 3D, with or without colors, etc.&lt;br /&gt;
A few examples are provided in the &amp;quot;&amp;lt;code&amp;gt;Plot_Graph Library.ets&amp;lt;/code&amp;gt;&amp;quot;, but much more is found on the gnuplot&lt;br /&gt;
website [http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf gnu plot documentation].&lt;br /&gt;
&lt;br /&gt;
== Short List of Useful Script Commands ==&lt;br /&gt;
&lt;br /&gt;
For a full list, please take a look at the [http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf gnu plot documentation]. The following list is only presenting the most common plot commands:&lt;br /&gt;
* &amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with impulses&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;You can omit the &amp;quot;[yMin:yMax]&amp;quot; parameter, for a reasonable box to be automatically chosen by gnuplot.&amp;lt;br&amp;gt;Replace &amp;quot;nameOfDataFile&amp;quot; by &amp;quot;%(dataFile)&amp;quot; if the data comes via an input pin and is not already in a file.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;Without the &amp;quot;impulses&amp;quot; parameter, a regular graph is drawn (straight lines between points)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with: lines&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;The same as above (straight lines between points)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with: dots&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;Draws dots&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with candlesticks&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;the data file should contain multiple colums per line. Select the colums with a &amp;quot;using...&amp;quot; parameter.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set key left box&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines the position (left) and type (boxed) of the graph-title (the string comes from the &amp;lt;code&amp;gt;title &amp;quot;graph title&amp;quot;&amp;lt;/code&amp;gt; argument in the plot command)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set title &amp;quot;someTitle&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines a headline title&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set term %(outputFormat)&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines the type of image to be generated. For &amp;quot;%(outputFormat)&amp;quot;, the input pin&#039;s format is generated. Otherwise, you can also hardcode any of &amp;quot;&amp;lt;code&amp;gt;png&amp;lt;/code&amp;gt;&amp;quot;, &amp;quot;&amp;lt;code&amp;gt;jpg&amp;lt;/code&amp;gt;&amp;quot;, etc. here&lt;br /&gt;
&lt;br /&gt;
A few examples are found in the demo library: &amp;quot;Plot_Graph_Library.ets&amp;quot;, which is found in the plugin&#039;s folder and in the online documentation [http://www.gnuplot.info/docs_5.0/gnuplot.pdf &amp;quot;http://www.gnuplot.info/docs_5.0/gnuplot.pdf&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
== Accessing Data from Attachments ==&lt;br /&gt;
&lt;br /&gt;
You can directly access expecco attachment files by their name inside the script. I.e. if you have an attachment with file named &amp;quot;&amp;lt;code&amp;gt;foo.dat&amp;lt;/code&amp;gt;&amp;quot;, use &amp;lt;code&amp;gt;... data &amp;quot;foo.dat&amp;quot; ...&amp;lt;/code&amp;gt; inside the script. Notice that the attchment&#039;s filename is to be used - not its name in the tree.&lt;br /&gt;
&lt;br /&gt;
== Plotting Multiple Graphs into a Single Picture ==&lt;br /&gt;
&lt;br /&gt;
To do this, generate multiple datasets (as ascii files, with one textline per row). Add &amp;lt;code&amp;gt;data&amp;amp;lt;i&amp;amp;gt;&amp;lt;/code&amp;gt; input pins and feed them the filenames. Add multiple &amp;lt;code&amp;gt;plot&amp;lt;/code&amp;gt; commands, each with its own &amp;quot;&amp;lt;code&amp;gt;%(data&amp;amp;lt;i&amp;amp;gt;&amp;lt;/code&amp;gt;)&amp;quot; argument, and possibly with its own graph-title.&lt;br /&gt;
&lt;br /&gt;
== Changing the Size of the Generated Image ==&lt;br /&gt;
&lt;br /&gt;
By default, the generated image&#039;s size is 640 x 400 pixels.&lt;br /&gt;
For a fix size, change the script&#039;s &amp;quot;&amp;lt;code&amp;gt;set term&amp;lt;/code&amp;gt;&amp;quot; statement to:&lt;br /&gt;
&lt;br /&gt;
 set term %(outputFormat) size &amp;lt;width&amp;gt;,&amp;lt;height&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &amp;amp;lt;width&amp;amp;gt; and &amp;amp;lt;height&amp;amp;gt; define the size in pixels.&lt;br /&gt;
&lt;br /&gt;
Alternatively, add two pins named &amp;quot;&#039;&#039;width&#039;&#039;&amp;quot; and &amp;quot;&#039;&#039;height&#039;&#039;&amp;quot;, and&lt;br /&gt;
change the statement to:&lt;br /&gt;
&lt;br /&gt;
 set term %(outputFormat) size %(width),%(height)&lt;br /&gt;
&lt;br /&gt;
and provide the desired size (as integers) at the input pins.&lt;br /&gt;
&lt;br /&gt;
== Caveats ==&lt;br /&gt;
&lt;br /&gt;
Do not place &amp;lt;code&amp;gt;pause&amp;lt;/code&amp;gt; statements into the script; there is no input stream given to gnuplot, so it will immediately read an EOF (end of file) and proceed.&lt;br /&gt;
&lt;br /&gt;
== Complete Example 1: Gathering Response Times of MQTT Protocol Messages ==&lt;br /&gt;
&lt;br /&gt;
This example demonstrates, how a test suite can gather response times of a remote system,&lt;br /&gt;
when sending MQTT messages to it.&lt;br /&gt;
&lt;br /&gt;
==== Step 1: Define the Test Sequence ====&lt;br /&gt;
&lt;br /&gt;
this is a very simple publish-response setup, in that a topic is published to an MQTT broker,&lt;br /&gt;
and the time measured, until the notification arrives (in order to run this on your machine,&lt;br /&gt;
the expecco MQTT plugin needs to be installed):&lt;br /&gt;
&lt;br /&gt;
Outside (Schema) view of the measuring action:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Schema_Measuring_MQTT.png|400px]]&lt;br /&gt;
&lt;br /&gt;
which is implemented as:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Net_Measuring_MQTT.png|800px]]&lt;br /&gt;
&lt;br /&gt;
The yellow area does the MQTT setup. First, an MQTT client connection is created,&lt;br /&gt;
and the client handle forwarded to the Subscribe action, which registers expecco&lt;br /&gt;
to react to the &amp;quot;expeccoTesting/test&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
The green area does the actual publish operation,&lt;br /&gt;
publishing the &amp;quot;Hello from Expecco&amp;quot; string on this topic.&lt;br /&gt;
&lt;br /&gt;
In parallel, the blue area waits for the next incoming event,&lt;br /&gt;
(and generates a printed detail info on the received packet for the&lt;br /&gt;
activity log).&lt;br /&gt;
&lt;br /&gt;
The red area contains a stopWatch action block, which gets started&lt;br /&gt;
with when the publish is done, and stops when the event arrives.&lt;br /&gt;
&lt;br /&gt;
The measurement action is prametrized with the broker&#039;s host and port,&lt;br /&gt;
and generates the measured timeDuration as output.&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Test Run ====&lt;br /&gt;
&lt;br /&gt;
Assuming that you have an MQTT client running on the local host,&lt;br /&gt;
the test sequence can be immediately tried via the Test/Demo page:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Test_Measuring_MQTT.png|300px]]&lt;br /&gt;
&lt;br /&gt;
A good strategy is to package the MQTT broker into a docker file.&lt;br /&gt;
Here is the MQTT client&#039;s output of the above run:&lt;br /&gt;
&lt;br /&gt;
 $ sh rundocker.sh &lt;br /&gt;
 1522084243: mosquitto version 1.4.8 (build date Thu, 01 Mar 2018 09:34:49 -0500) starting&lt;br /&gt;
 1522084243: Using default config.&lt;br /&gt;
 1522084243: Opening ipv4 listen socket on port 1883.&lt;br /&gt;
 1522084243: Opening ipv6 listen socket on port 1883.&lt;br /&gt;
 1522084272: New connection from 172.17.0.1 on port 1883.&lt;br /&gt;
 1522084272: New client connected from 172.17.0.1 as expeccoMQTT (c1, k0).&lt;br /&gt;
 1522084272: Client expeccoMQTT disconnected.&lt;br /&gt;
&lt;br /&gt;
and here the activitylog in expecco:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log1_Measuring_MQTT.png|600px]]&lt;br /&gt;
&lt;br /&gt;
with the event info detail:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log2_Measuring_MQTT.png|600px]]&lt;br /&gt;
&lt;br /&gt;
==== Step 3: Running that Sequence Multiple Times ====&lt;br /&gt;
&lt;br /&gt;
Now that we have verified, that the communication with the MQTT broker works correctly,&lt;br /&gt;
we create another action bock, which runs the above action multiple times and collects&lt;br /&gt;
all the execution times:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT.png|700px]]&lt;br /&gt;
&lt;br /&gt;
[ explanation: first, a new empty colletion is created, then, the timing values of&lt;br /&gt;
the multi-triggered measure-step are all added to this collection.&lt;br /&gt;
When done (no step active), the collection is forwarded (now filled) to the output pin]&lt;br /&gt;
&lt;br /&gt;
Here is the activity log after 10 runs:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log1_Multiple_Measuring_MQTT.png|700px]]&lt;br /&gt;
&lt;br /&gt;
==== Step 4: Adding the Graph ====&lt;br /&gt;
&lt;br /&gt;
The last step is the easiest: select the &amp;quot;New Plot/Graph&amp;quot; action block from the menu,&lt;br /&gt;
name it &amp;quot;Time Measurement Graph&amp;quot;&lt;br /&gt;
and change its autogenerated gnuplot script to:&lt;br /&gt;
&lt;br /&gt;
 set output &amp;quot;%(outputFile)&amp;quot;&lt;br /&gt;
 set term %(outputFormat)&lt;br /&gt;
 set title &amp;quot;%(title)&amp;quot;&lt;br /&gt;
 set key left box&lt;br /&gt;
 plot [1:%(dataSize)] [0:100] &amp;quot;%(dataFile)&amp;quot; title &amp;quot;%(lineTitle)&amp;quot; with impulses&lt;br /&gt;
&lt;br /&gt;
then place a step of it into your network and connect the &amp;quot;collectedTimes&amp;quot; output of the above measurement step with the &amp;quot;data&amp;quot; input&lt;br /&gt;
of the &amp;quot;Measurement-Graph&amp;quot; step:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_plus_Graph_MQTT.png|500px]]&lt;br /&gt;
&lt;br /&gt;
Running it, and generating the report, produces the following pdf-report (the logging of subactivities was disabled for this report):&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT_Report_img.png|700px]]&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT_Report.pdf]]&lt;br /&gt;
&lt;br /&gt;
== Example 1 Simplified: Using Probes to Gather the Data ==&lt;br /&gt;
&lt;br /&gt;
The above data collection may look a bit complicated, due to the need for start/stop synchronization.&lt;br /&gt;
A much nicer and easier to use solution is in using a probe to gather the data.&lt;br /&gt;
For this, we need a probe which does no range checks, but instead only collects the incoming values:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_Probe.png|700px]]&lt;br /&gt;
&lt;br /&gt;
Here, a probe was added (via the &amp;quot;timeDelta&amp;quot; pin&#039;s &amp;quot;Add Probe&amp;quot; menu item),&lt;br /&gt;
and specified to &amp;quot;collect incoming data without check&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_ProbeMenu.png|500px]]&lt;br /&gt;
&lt;br /&gt;
Now that we have a probe at the roundtrip time,&lt;br /&gt;
the outer measurement looks much simpler:&lt;br /&gt;
&lt;br /&gt;
* reset the probe (to clear any previously gathered data)&lt;br /&gt;
* prime the probe enable data collecting)&lt;br /&gt;
* run the roundtrip-test n-times&lt;br /&gt;
* unprime the probe (disable further data collecting)&lt;br /&gt;
* fetch the probe&#039;s values and pass them to the gnuplot action&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_Probe2.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
[http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf Gnuplot Manual]&lt;br /&gt;
&amp;lt;br&amp;gt;[https://alvinalexander.com/technology/gnuplot-charts-graphs-examples Collection of Examples]&lt;br /&gt;
&lt;br /&gt;
&amp;quot;[[R Action Blocks]]&amp;quot; which provide similar graph/plot functions in addition to sophisticated mathematical and statistics libraries.&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;quot;[[Octave Script Action Tutorial|Octave/Matlab Actions]]&amp;quot; another framework for mathematical computations and/or to generate graphs/plots.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- {{Languages}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category: Languages ]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Plot/Graph_Action_Blocks&amp;diff=30939</id>
		<title>Plot/Graph Action Blocks</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Plot/Graph_Action_Blocks&amp;diff=30939"/>
		<updated>2026-03-03T11:14:53Z</updated>

		<summary type="html">&lt;p&gt;Sv: /* Creating Plot/Graph Action Blocks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
With expecco18.1, a new block type named &amp;quot;&#039;&#039;Plot/Graph Actions&#039;&#039;&amp;quot; is introduced.&lt;br /&gt;
These behave similar to other script action blocks, in that they are defined by a script&lt;br /&gt;
(in this case: gnuplot scripting language) and are placed into the test sequence flow&lt;br /&gt;
just like any other block.&lt;br /&gt;
&lt;br /&gt;
However, when executed, these actions generate a graphic document (jpeg, png or postscript),&lt;br /&gt;
which is then either added to the activity log, or added as attachment. It will therefore be &lt;br /&gt;
available in the generated &amp;quot;&amp;lt;code&amp;gt;elf&amp;lt;/code&amp;gt;&amp;quot; execution log file, or shown in the generated &amp;quot;&amp;lt;code&amp;gt;pdf&amp;lt;/code&amp;gt;&amp;quot; report file.&lt;br /&gt;
Of course, you can do anything you like with the generated graphic file, by feeding the name&lt;br /&gt;
of the generated file to another action for further processing.&lt;br /&gt;
&lt;br /&gt;
Notice: if you prefer other graphic packages or other programming languages, you may of course use R language scripts or Python scripts (with matplotlib and/or seaboarn) to generate graphics in a similar way.&lt;br /&gt;
&lt;br /&gt;
== Creating Plot/Graph Action Blocks ==&lt;br /&gt;
&lt;br /&gt;
Plot/graph action blocks are created via the &lt;br /&gt;
&amp;quot;&#039;&#039;Actions&#039;&#039; - &#039;&#039;More&#039;&#039; - &#039;&#039;Plot/Graph&#039;&#039;&amp;quot; menu item.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Plot_Graph_Actions_in_Menu.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Select the &amp;quot;&#039;&#039;Plot/Graph&#039;&#039;&amp;quot; item, to create a new gnuplot action.&lt;br /&gt;
An initial set of input pins and an initial (demo-) script is generated for you.&lt;br /&gt;
&lt;br /&gt;
For details on the scripting language, please refer to the public gnuplot documentation &lt;br /&gt;
[http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf &amp;quot;http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
Any input pin named &amp;quot;&amp;lt;code&amp;gt;&amp;lt;pinName&amp;gt;&amp;lt;/code&amp;gt;&amp;quot; is available in the script as &amp;quot;&amp;lt;code&amp;gt;%(pinName)&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
Thus, the script can be parametrized in arbitrary ways.&lt;br /&gt;
A number of pins are already created for you; these are:&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;outputFile&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;both an input and an output. If the input is specified, gnuplot will generate the plot into that file. Otherwise, a new temporary file will be created and the output written into that. In any case will the actual outputFileName be available on the corresponding output pin for further processing.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;outputFormat&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;specifies the kind of file to generate. For inclusion into the report, an image format should be chosen (i.e. one of &amp;quot;jpeg&amp;quot;, &amp;quot;png&amp;quot; or &amp;quot;gif&amp;quot;). For archival or to generate high quality documents, you can also choose &amp;quot;postscript&amp;quot; or &amp;quot;svg&amp;quot;. However, those can (currently) not be automatically converted for the pdf report.&amp;lt;br&amp;gt;On Unix systems, you can also specify &amp;quot;x11&amp;quot; as output format. In this case, a window is opened, displaying the generated graph. Be aware that expecco is (currently) not informed about the window being closed, so you may have to stop the test-run via the expecco-&amp;quot;Stop&amp;quot; button. &lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;title&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;A title string to be drawn above the graphic (actually, the position of the title is specified by the &amp;quot;set key&amp;quot; gnuplot expression, inside the script)&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;data&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;If connected, and a collection of values is present, these values will be written into a temporary data file, and the filename is available in the gnuplot script as &amp;quot;&amp;lt;code&amp;gt;%(data)&amp;lt;/code&amp;gt;&amp;quot;. Theis will be the raw data, to be processed by the script.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;dataSize&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;There is no pin for that, but the value is available to the script. It provides the number of data points (or rows) in the data file.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;attachToLog&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;if true (the default), the generated graph will be attached to the final pdf report. This pin&#039;s value is not forwarded to the gnuplot script.&lt;br /&gt;
&lt;br /&gt;
You can add additional input pins and refer to the corresponding pin value in the script with the &amp;quot;%(...)&amp;quot; notation.&lt;br /&gt;
&lt;br /&gt;
== A Simple Graph Script ==&lt;br /&gt;
&lt;br /&gt;
A typical gnuplot script looks like:&lt;br /&gt;
&lt;br /&gt;
 # The code below is sent to gnuplot and the generated output&lt;br /&gt;
 # will be stored in a file named %%(outputFile) &lt;br /&gt;
 # (unless explicitly specified, a temporary file is created)&lt;br /&gt;
 #&lt;br /&gt;
 set output &amp;quot;%(outputFile)&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
 # &lt;br /&gt;
 set term %(outputFormat)&lt;br /&gt;
 &lt;br /&gt;
 # All input pin values can be used here as %%(nameOfPin).&lt;br /&gt;
 # so the next line controls the title via the &amp;quot;title&amp;quot; pin&lt;br /&gt;
 #&lt;br /&gt;
 set title &amp;quot;%(title)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # You can add more pins.&lt;br /&gt;
 # For example, add pins named &amp;quot;min&amp;quot; and &amp;quot;max&amp;quot; with type Number,&lt;br /&gt;
 # and replace &amp;quot;-10:10&amp;quot; below by &amp;quot;%%(min):%%(max) to parametrize the y-axis range.&lt;br /&gt;
 set key left box&lt;br /&gt;
 plot [1:%(dataSize)] [-10:10] &amp;quot;%(data)&amp;quot; title &amp;quot;My Data&amp;quot; with impulses&lt;br /&gt;
&lt;br /&gt;
to plot a simple impulse graph of the data values, as presented at the input pin,&lt;br /&gt;
assuming that the pins get the following values:&lt;br /&gt;
* &amp;quot;outputFile&amp;quot; - leave unconnected or remove the pin (to let the action choose a temp file)&lt;br /&gt;
* &amp;quot;outputFormat&amp;quot; - freeze as &amp;quot;&amp;lt;code&amp;gt;png&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
* &amp;quot;title&amp;quot; - any title - such as &amp;quot;Measurement #1&amp;quot;&lt;br /&gt;
* &amp;quot;data&amp;quot; - a vector of numbers&lt;br /&gt;
&lt;br /&gt;
Of course, this is actually the simplest possible script to render such data.&lt;br /&gt;
Gnuplot provides a wide area of possible plots, both 2D and 3D, with or without colors, etc.&lt;br /&gt;
A few examples are provided in the &amp;quot;&amp;lt;code&amp;gt;Plot_Graph Library.ets&amp;lt;/code&amp;gt;&amp;quot;, but much more is found on the gnuplot&lt;br /&gt;
website and gnuplot documentation [http://www.gnuplot.info/docs_5.0/gnuplot.pdf &amp;quot;http://www.gnuplot.info/docs_5.0/gnuplot.pdf&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
== Short List of Useful Script Commands ==&lt;br /&gt;
&lt;br /&gt;
For a full list, please take a look at the gnu plot documentation [http://www.gnuplot.info/docs_5.0/gnuplot.pdf &amp;quot;http://www.gnuplot.info/docs_5.0/gnuplot.pdf&amp;quot;]. The following list is only presenting the most common plot commands:&lt;br /&gt;
* &amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with impulses&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;You can omit the &amp;quot;[yMin:yMax]&amp;quot; parameter, for a reasonable box to be automatically chosen by gnuplot.&amp;lt;br&amp;gt;Replace &amp;quot;nameOfDataFile&amp;quot; by &amp;quot;%(data)&amp;quot; if the data comes via an input pin and is not already in a file.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;Without the &amp;quot;impulses&amp;quot; parameter, a regular graph is drawn (straight lines between points)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with: lines&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;The same as above (straight lines between points)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with: dots&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;Draws dots&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with candlesticks&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;the data file should contain multiple colums per line. Select the colums with a &amp;quot;using...&amp;quot; parameter.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set key left box&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines the position (left) and type (boxed) of the graph-title (the string comes from the &amp;lt;code&amp;gt;title &amp;quot;graph title&amp;quot;&amp;lt;/code&amp;gt; argument in the plot command)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set title &amp;quot;someTitle&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines a headline title&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set term %(outputFormat)&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines the type of image to be generated. For &amp;quot;%(outputFormat)&amp;quot;, the input pin&#039;s format is generated. Otherwise, you can also hardcode any of &amp;quot;&amp;lt;code&amp;gt;png&amp;lt;/code&amp;gt;&amp;quot;, &amp;quot;&amp;lt;code&amp;gt;jpg&amp;lt;/code&amp;gt;&amp;quot;, etc. here&lt;br /&gt;
&lt;br /&gt;
A few examples are found in the demo library: &amp;quot;Plot_Graph_Library.ets&amp;quot;, which is found in the plugin&#039;s folder and in the online documentation [http://www.gnuplot.info/docs_5.0/gnuplot.pdf &amp;quot;http://www.gnuplot.info/docs_5.0/gnuplot.pdf&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
== Accessing Data from Attachments ==&lt;br /&gt;
&lt;br /&gt;
You can directly access expecco attachment files by their name inside the script. I.e. if you have an attachment with file named &amp;quot;&amp;lt;code&amp;gt;foo.dat&amp;lt;/code&amp;gt;&amp;quot;, use &amp;lt;code&amp;gt;... data &amp;quot;foo.dat&amp;quot; ...&amp;lt;/code&amp;gt; inside the script. Notice that the attchment&#039;s filename is to be used - not its name in the tree.&lt;br /&gt;
&lt;br /&gt;
== Plotting Multiple Graphs into a Single Picture ==&lt;br /&gt;
&lt;br /&gt;
To do this, generate multiple datasets (as ascii files, with one textline per row). Add &amp;lt;code&amp;gt;data&amp;amp;lt;i&amp;amp;gt;&amp;lt;/code&amp;gt; input pins and feed them the filenames. Add multiple &amp;lt;code&amp;gt;plot&amp;lt;/code&amp;gt; commands, each with its own &amp;quot;&amp;lt;code&amp;gt;%(data&amp;amp;lt;i&amp;amp;gt;&amp;lt;/code&amp;gt;)&amp;quot; argument, and possibly with its own graph-title.&lt;br /&gt;
&lt;br /&gt;
== Changing the Size of the Generated Image ==&lt;br /&gt;
&lt;br /&gt;
By default, the generated image&#039;s size is 640 x 400 pixels.&lt;br /&gt;
For a fix size, change the script&#039;s &amp;quot;&amp;lt;code&amp;gt;set term&amp;lt;/code&amp;gt;&amp;quot; statement to:&lt;br /&gt;
&lt;br /&gt;
 set term %(outputFormat) size &amp;lt;width&amp;gt;,&amp;lt;height&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &amp;amp;lt;width&amp;amp;gt; and &amp;amp;lt;height&amp;amp;gt; define the size in pixels.&lt;br /&gt;
&lt;br /&gt;
Alternatively, add two pins named &amp;quot;&#039;&#039;width&#039;&#039;&amp;quot; and &amp;quot;&#039;&#039;height&#039;&#039;&amp;quot;, and&lt;br /&gt;
change the statement to:&lt;br /&gt;
&lt;br /&gt;
 set term %(outputFormat) size %(width),%(height)&lt;br /&gt;
&lt;br /&gt;
and provide the desired size (as integers) at the input pins.&lt;br /&gt;
&lt;br /&gt;
== Caveats ==&lt;br /&gt;
&lt;br /&gt;
Do not place &amp;lt;code&amp;gt;pause&amp;lt;/code&amp;gt; statements into the script; there is no input stream given to gnuplot, so it will immediately read an EOF (end of file) and proceed.&lt;br /&gt;
&lt;br /&gt;
== Complete Example 1: Gathering Response Times of MQTT Protocol Messages ==&lt;br /&gt;
&lt;br /&gt;
This example demonstrates, how a test suite can gather response times of a remote system,&lt;br /&gt;
when sending MQTT messages to it.&lt;br /&gt;
&lt;br /&gt;
==== Step 1: Define the Test Sequence ====&lt;br /&gt;
&lt;br /&gt;
this is a very simple publish-response setup, in that a topic is published to an MQTT broker,&lt;br /&gt;
and the time measured, until the notification arrives (in order to run this on your machine,&lt;br /&gt;
the expecco MQTT plugin needs to be installed):&lt;br /&gt;
&lt;br /&gt;
Outside (Schema) view of the measuring action:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Schema_Measuring_MQTT.png|400px]]&lt;br /&gt;
&lt;br /&gt;
which is implemented as:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Net_Measuring_MQTT.png|800px]]&lt;br /&gt;
&lt;br /&gt;
The yellow area does the MQTT setup. First, an MQTT client connection is created,&lt;br /&gt;
and the client handle forwarded to the Subscribe action, which registers expecco&lt;br /&gt;
to react to the &amp;quot;expeccoTesting/test&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
The green area does the actual publish operation,&lt;br /&gt;
publishing the &amp;quot;Hello from Expecco&amp;quot; string on this topic.&lt;br /&gt;
&lt;br /&gt;
In parallel, the blue area waits for the next incoming event,&lt;br /&gt;
(and generates a printed detail info on the received packet for the&lt;br /&gt;
activity log).&lt;br /&gt;
&lt;br /&gt;
The red area contains a stopWatch action block, which gets started&lt;br /&gt;
with when the publish is done, and stops when the event arrives.&lt;br /&gt;
&lt;br /&gt;
The measurement action is prametrized with the broker&#039;s host and port,&lt;br /&gt;
and generates the measured timeDuration as output.&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Test Run ====&lt;br /&gt;
&lt;br /&gt;
Assuming that you have an MQTT client running on the local host,&lt;br /&gt;
the test sequence can be immediately tried via the Test/Demo page:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Test_Measuring_MQTT.png|300px]]&lt;br /&gt;
&lt;br /&gt;
A good strategy is to package the MQTT broker into a docker file.&lt;br /&gt;
Here is the MQTT client&#039;s output of the above run:&lt;br /&gt;
&lt;br /&gt;
 $ sh rundocker.sh &lt;br /&gt;
 1522084243: mosquitto version 1.4.8 (build date Thu, 01 Mar 2018 09:34:49 -0500) starting&lt;br /&gt;
 1522084243: Using default config.&lt;br /&gt;
 1522084243: Opening ipv4 listen socket on port 1883.&lt;br /&gt;
 1522084243: Opening ipv6 listen socket on port 1883.&lt;br /&gt;
 1522084272: New connection from 172.17.0.1 on port 1883.&lt;br /&gt;
 1522084272: New client connected from 172.17.0.1 as expeccoMQTT (c1, k0).&lt;br /&gt;
 1522084272: Client expeccoMQTT disconnected.&lt;br /&gt;
&lt;br /&gt;
and here the activitylog in expecco:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log1_Measuring_MQTT.png|600px]]&lt;br /&gt;
&lt;br /&gt;
with the event info detail:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log2_Measuring_MQTT.png|600px]]&lt;br /&gt;
&lt;br /&gt;
==== Step 3: Running that Sequence Multiple Times ====&lt;br /&gt;
&lt;br /&gt;
Now that we have verified, that the communication with the MQTT broker works correctly,&lt;br /&gt;
we create another action bock, which runs the above action multiple times and collects&lt;br /&gt;
all the execution times:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT.png|700px]]&lt;br /&gt;
&lt;br /&gt;
[ explanation: first, a new empty colletion is created, then, the timing values of&lt;br /&gt;
the multi-triggered measure-step are all added to this collection.&lt;br /&gt;
When done (no step active), the collection is forwarded (now filled) to the output pin]&lt;br /&gt;
&lt;br /&gt;
Here is the activity log after 10 runs:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log1_Multiple_Measuring_MQTT.png|700px]]&lt;br /&gt;
&lt;br /&gt;
==== Step 4: Adding the Graph ====&lt;br /&gt;
&lt;br /&gt;
The last step is the easiest: select the &amp;quot;New Plot/Graph&amp;quot; action block from the menu,&lt;br /&gt;
name it &amp;quot;Time Measurement Graph&amp;quot;&lt;br /&gt;
and change its autogenerated gnuplot script to:&lt;br /&gt;
&lt;br /&gt;
 set output &amp;quot;%(outputFile)&amp;quot;&lt;br /&gt;
 set term %(outputFormat)&lt;br /&gt;
 set title &amp;quot;%(title)&amp;quot;&lt;br /&gt;
 set key left box&lt;br /&gt;
 plot [1:%(dataSize)] [0:100] &amp;quot;%(data)&amp;quot; title &amp;quot;%(lineTitle)&amp;quot; with impulses&lt;br /&gt;
&lt;br /&gt;
then place a step of it into your network and connect the &amp;quot;collectedTimes&amp;quot; output of the above measurement step with the &amp;quot;data&amp;quot; input&lt;br /&gt;
of the &amp;quot;Measurement-Graph&amp;quot; step:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_plus_Graph_MQTT.png|500px]]&lt;br /&gt;
&lt;br /&gt;
Running it, and generating the report, produces the following pdf-report (the logging of subactivities was disabled for this report):&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT_Report_img.png|700px]]&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT_Report.pdf]]&lt;br /&gt;
&lt;br /&gt;
== Example 1 Simplified: Using Probes to Gather the Data ==&lt;br /&gt;
&lt;br /&gt;
The above data collection may look a bit complicated, due to the need for start/stop synchronization.&lt;br /&gt;
A much nicer and easier to use solution is in using a probe to gather the data.&lt;br /&gt;
For this, we need a probe which does no range checks, but instead only collects the incoming values:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_Probe.png|700px]]&lt;br /&gt;
&lt;br /&gt;
Here, a probe was added (via the &amp;quot;timeDelta&amp;quot; pin&#039;s &amp;quot;Add Probe&amp;quot; menu item),&lt;br /&gt;
and specified to &amp;quot;collect incoming data without check&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_ProbeMenu.png|500px]]&lt;br /&gt;
&lt;br /&gt;
Now that we have a probe at the roundtrip time,&lt;br /&gt;
the outer measurement looks much simpler:&lt;br /&gt;
&lt;br /&gt;
* reset the probe (to clear any previously gathered data)&lt;br /&gt;
* prime the probe enable data collecting)&lt;br /&gt;
* run the roundtrip-test n-times&lt;br /&gt;
* unprime the probe (disable further data collecting)&lt;br /&gt;
* fetch the probe&#039;s values and pass them to the gnuplot action&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_Probe2.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
[http://www.gnuplot.info/docs_5.0/gnuplot.pdf Gnuplot Manual]&lt;br /&gt;
&amp;lt;br&amp;gt;[https://alvinalexander.com/technology/gnuplot-charts-graphs-examples Collection of Examples]&lt;br /&gt;
&lt;br /&gt;
&amp;quot;[[R Action Blocks]]&amp;quot; which provide similar graph/plot functions in addition to sophisticated mathematical and statistics libraries.&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;quot;[[Octave Script Action Tutorial|Octave/Matlab Actions]]&amp;quot; another framework for mathematical computations and/or to generate graphs/plots.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- {{Languages}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category: Languages ]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Plot/Graph_Action_Blocks&amp;diff=30938</id>
		<title>Plot/Graph Action Blocks</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Plot/Graph_Action_Blocks&amp;diff=30938"/>
		<updated>2026-03-03T11:13:55Z</updated>

		<summary type="html">&lt;p&gt;Sv: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
With expecco18.1, a new block type named &amp;quot;&#039;&#039;Plot/Graph Actions&#039;&#039;&amp;quot; is introduced.&lt;br /&gt;
These behave similar to other script action blocks, in that they are defined by a script&lt;br /&gt;
(in this case: gnuplot scripting language) and are placed into the test sequence flow&lt;br /&gt;
just like any other block.&lt;br /&gt;
&lt;br /&gt;
However, when executed, these actions generate a graphic document (jpeg, png or postscript),&lt;br /&gt;
which is then either added to the activity log, or added as attachment. It will therefore be &lt;br /&gt;
available in the generated &amp;quot;&amp;lt;code&amp;gt;elf&amp;lt;/code&amp;gt;&amp;quot; execution log file, or shown in the generated &amp;quot;&amp;lt;code&amp;gt;pdf&amp;lt;/code&amp;gt;&amp;quot; report file.&lt;br /&gt;
Of course, you can do anything you like with the generated graphic file, by feeding the name&lt;br /&gt;
of the generated file to another action for further processing.&lt;br /&gt;
&lt;br /&gt;
Notice: if you prefer other graphic packages or other programming languages, you may of course use R language scripts or Python scripts (with matplotlib and/or seaboarn) to generate graphics in a similar way.&lt;br /&gt;
&lt;br /&gt;
== Creating Plot/Graph Action Blocks ==&lt;br /&gt;
&lt;br /&gt;
Plot/graph action blocks are created via the &lt;br /&gt;
&amp;quot;&#039;&#039;Actions&#039;&#039; - &#039;&#039;More&#039;&#039; - &#039;&#039;Plot/Graph&#039;&#039;&amp;quot; menu item.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Plot_Graph_Actions_in_Menu.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Select the &amp;quot;&#039;&#039;Plot/Graph&#039;&#039;&amp;quot; item, to create a new gnuplot action.&lt;br /&gt;
An initial set of input pins and an initial (demo-) script is generated for you.&lt;br /&gt;
&lt;br /&gt;
For details on the scripting language, please refer to the public gnuplot documentation &lt;br /&gt;
[http://www.gnuplot.info/docs_5.0/gnuplot.pdf &amp;quot;http://www.gnuplot.info/docs_6.0/Gnuplot_6.pdf&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
Any input pin named &amp;quot;&amp;lt;code&amp;gt;&amp;lt;pinName&amp;gt;&amp;lt;/code&amp;gt;&amp;quot; is available in the script as &amp;quot;&amp;lt;code&amp;gt;%(pinName)&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
Thus, the script can be parametrized in arbitrary ways.&lt;br /&gt;
A number of pins are already created for you; these are:&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;outputFile&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;both an input and an output. If the input is specified, gnuplot will generate the plot into that file. Otherwise, a new temporary file will be created and the output written into that. In any case will the actual outputFileName be available on the corresponding output pin for further processing.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;outputFormat&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;specifies the kind of file to generate. For inclusion into the report, an image format should be chosen (i.e. one of &amp;quot;jpeg&amp;quot;, &amp;quot;png&amp;quot; or &amp;quot;gif&amp;quot;). For archival or to generate high quality documents, you can also choose &amp;quot;postscript&amp;quot; or &amp;quot;svg&amp;quot;. However, those can (currently) not be automatically converted for the pdf report.&amp;lt;br&amp;gt;On Unix systems, you can also specify &amp;quot;x11&amp;quot; as output format. In this case, a window is opened, displaying the generated graph. Be aware that expecco is (currently) not informed about the window being closed, so you may have to stop the test-run via the expecco-&amp;quot;Stop&amp;quot; button. &lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;title&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;A title string to be drawn above the graphic (actually, the position of the title is specified by the &amp;quot;set key&amp;quot; gnuplot expression, inside the script)&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;data&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;If connected, and a collection of values is present, these values will be written into a temporary data file, and the filename is available in the gnuplot script as &amp;quot;&amp;lt;code&amp;gt;%(data)&amp;lt;/code&amp;gt;&amp;quot;. Theis will be the raw data, to be processed by the script.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;dataSize&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;There is no pin for that, but the value is available to the script. It provides the number of data points (or rows) in the data file.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;&#039;&#039;&#039;attachToLog&#039;&#039;&#039;&amp;quot;&amp;lt;br&amp;gt;if true (the default), the generated graph will be attached to the final pdf report. This pin&#039;s value is not forwarded to the gnuplot script.&lt;br /&gt;
&lt;br /&gt;
You can add additional input pins and refer to the corresponding pin value in the script with the &amp;quot;%(...)&amp;quot; notation.&lt;br /&gt;
&lt;br /&gt;
== A Simple Graph Script ==&lt;br /&gt;
&lt;br /&gt;
A typical gnuplot script looks like:&lt;br /&gt;
&lt;br /&gt;
 # The code below is sent to gnuplot and the generated output&lt;br /&gt;
 # will be stored in a file named %%(outputFile) &lt;br /&gt;
 # (unless explicitly specified, a temporary file is created)&lt;br /&gt;
 #&lt;br /&gt;
 set output &amp;quot;%(outputFile)&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
 # &lt;br /&gt;
 set term %(outputFormat)&lt;br /&gt;
 &lt;br /&gt;
 # All input pin values can be used here as %%(nameOfPin).&lt;br /&gt;
 # so the next line controls the title via the &amp;quot;title&amp;quot; pin&lt;br /&gt;
 #&lt;br /&gt;
 set title &amp;quot;%(title)&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 # You can add more pins.&lt;br /&gt;
 # For example, add pins named &amp;quot;min&amp;quot; and &amp;quot;max&amp;quot; with type Number,&lt;br /&gt;
 # and replace &amp;quot;-10:10&amp;quot; below by &amp;quot;%%(min):%%(max) to parametrize the y-axis range.&lt;br /&gt;
 set key left box&lt;br /&gt;
 plot [1:%(dataSize)] [-10:10] &amp;quot;%(data)&amp;quot; title &amp;quot;My Data&amp;quot; with impulses&lt;br /&gt;
&lt;br /&gt;
to plot a simple impulse graph of the data values, as presented at the input pin,&lt;br /&gt;
assuming that the pins get the following values:&lt;br /&gt;
* &amp;quot;outputFile&amp;quot; - leave unconnected or remove the pin (to let the action choose a temp file)&lt;br /&gt;
* &amp;quot;outputFormat&amp;quot; - freeze as &amp;quot;&amp;lt;code&amp;gt;png&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
* &amp;quot;title&amp;quot; - any title - such as &amp;quot;Measurement #1&amp;quot;&lt;br /&gt;
* &amp;quot;data&amp;quot; - a vector of numbers&lt;br /&gt;
&lt;br /&gt;
Of course, this is actually the simplest possible script to render such data.&lt;br /&gt;
Gnuplot provides a wide area of possible plots, both 2D and 3D, with or without colors, etc.&lt;br /&gt;
A few examples are provided in the &amp;quot;&amp;lt;code&amp;gt;Plot_Graph Library.ets&amp;lt;/code&amp;gt;&amp;quot;, but much more is found on the gnuplot&lt;br /&gt;
website and gnuplot documentation [http://www.gnuplot.info/docs_5.0/gnuplot.pdf &amp;quot;http://www.gnuplot.info/docs_5.0/gnuplot.pdf&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
== Short List of Useful Script Commands ==&lt;br /&gt;
&lt;br /&gt;
For a full list, please take a look at the gnu plot documentation [http://www.gnuplot.info/docs_5.0/gnuplot.pdf &amp;quot;http://www.gnuplot.info/docs_5.0/gnuplot.pdf&amp;quot;]. The following list is only presenting the most common plot commands:&lt;br /&gt;
* &amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with impulses&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;You can omit the &amp;quot;[yMin:yMax]&amp;quot; parameter, for a reasonable box to be automatically chosen by gnuplot.&amp;lt;br&amp;gt;Replace &amp;quot;nameOfDataFile&amp;quot; by &amp;quot;%(data)&amp;quot; if the data comes via an input pin and is not already in a file.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;Without the &amp;quot;impulses&amp;quot; parameter, a regular graph is drawn (straight lines between points)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with: lines&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;The same as above (straight lines between points)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with: dots&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;Draws dots&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;plot [xMin:xMax] [yMin:yMax] &amp;quot;nameOfDataFile&amp;quot; title &amp;quot;graph title&amp;quot; with candlesticks&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;the data file should contain multiple colums per line. Select the colums with a &amp;quot;using...&amp;quot; parameter.&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set key left box&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines the position (left) and type (boxed) of the graph-title (the string comes from the &amp;lt;code&amp;gt;title &amp;quot;graph title&amp;quot;&amp;lt;/code&amp;gt; argument in the plot command)&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set title &amp;quot;someTitle&amp;quot;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines a headline title&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;set term %(outputFormat)&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;defines the type of image to be generated. For &amp;quot;%(outputFormat)&amp;quot;, the input pin&#039;s format is generated. Otherwise, you can also hardcode any of &amp;quot;&amp;lt;code&amp;gt;png&amp;lt;/code&amp;gt;&amp;quot;, &amp;quot;&amp;lt;code&amp;gt;jpg&amp;lt;/code&amp;gt;&amp;quot;, etc. here&lt;br /&gt;
&lt;br /&gt;
A few examples are found in the demo library: &amp;quot;Plot_Graph_Library.ets&amp;quot;, which is found in the plugin&#039;s folder and in the online documentation [http://www.gnuplot.info/docs_5.0/gnuplot.pdf &amp;quot;http://www.gnuplot.info/docs_5.0/gnuplot.pdf&amp;quot;].&lt;br /&gt;
&lt;br /&gt;
== Accessing Data from Attachments ==&lt;br /&gt;
&lt;br /&gt;
You can directly access expecco attachment files by their name inside the script. I.e. if you have an attachment with file named &amp;quot;&amp;lt;code&amp;gt;foo.dat&amp;lt;/code&amp;gt;&amp;quot;, use &amp;lt;code&amp;gt;... data &amp;quot;foo.dat&amp;quot; ...&amp;lt;/code&amp;gt; inside the script. Notice that the attchment&#039;s filename is to be used - not its name in the tree.&lt;br /&gt;
&lt;br /&gt;
== Plotting Multiple Graphs into a Single Picture ==&lt;br /&gt;
&lt;br /&gt;
To do this, generate multiple datasets (as ascii files, with one textline per row). Add &amp;lt;code&amp;gt;data&amp;amp;lt;i&amp;amp;gt;&amp;lt;/code&amp;gt; input pins and feed them the filenames. Add multiple &amp;lt;code&amp;gt;plot&amp;lt;/code&amp;gt; commands, each with its own &amp;quot;&amp;lt;code&amp;gt;%(data&amp;amp;lt;i&amp;amp;gt;&amp;lt;/code&amp;gt;)&amp;quot; argument, and possibly with its own graph-title.&lt;br /&gt;
&lt;br /&gt;
== Changing the Size of the Generated Image ==&lt;br /&gt;
&lt;br /&gt;
By default, the generated image&#039;s size is 640 x 400 pixels.&lt;br /&gt;
For a fix size, change the script&#039;s &amp;quot;&amp;lt;code&amp;gt;set term&amp;lt;/code&amp;gt;&amp;quot; statement to:&lt;br /&gt;
&lt;br /&gt;
 set term %(outputFormat) size &amp;lt;width&amp;gt;,&amp;lt;height&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where &amp;amp;lt;width&amp;amp;gt; and &amp;amp;lt;height&amp;amp;gt; define the size in pixels.&lt;br /&gt;
&lt;br /&gt;
Alternatively, add two pins named &amp;quot;&#039;&#039;width&#039;&#039;&amp;quot; and &amp;quot;&#039;&#039;height&#039;&#039;&amp;quot;, and&lt;br /&gt;
change the statement to:&lt;br /&gt;
&lt;br /&gt;
 set term %(outputFormat) size %(width),%(height)&lt;br /&gt;
&lt;br /&gt;
and provide the desired size (as integers) at the input pins.&lt;br /&gt;
&lt;br /&gt;
== Caveats ==&lt;br /&gt;
&lt;br /&gt;
Do not place &amp;lt;code&amp;gt;pause&amp;lt;/code&amp;gt; statements into the script; there is no input stream given to gnuplot, so it will immediately read an EOF (end of file) and proceed.&lt;br /&gt;
&lt;br /&gt;
== Complete Example 1: Gathering Response Times of MQTT Protocol Messages ==&lt;br /&gt;
&lt;br /&gt;
This example demonstrates, how a test suite can gather response times of a remote system,&lt;br /&gt;
when sending MQTT messages to it.&lt;br /&gt;
&lt;br /&gt;
==== Step 1: Define the Test Sequence ====&lt;br /&gt;
&lt;br /&gt;
this is a very simple publish-response setup, in that a topic is published to an MQTT broker,&lt;br /&gt;
and the time measured, until the notification arrives (in order to run this on your machine,&lt;br /&gt;
the expecco MQTT plugin needs to be installed):&lt;br /&gt;
&lt;br /&gt;
Outside (Schema) view of the measuring action:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Schema_Measuring_MQTT.png|400px]]&lt;br /&gt;
&lt;br /&gt;
which is implemented as:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Net_Measuring_MQTT.png|800px]]&lt;br /&gt;
&lt;br /&gt;
The yellow area does the MQTT setup. First, an MQTT client connection is created,&lt;br /&gt;
and the client handle forwarded to the Subscribe action, which registers expecco&lt;br /&gt;
to react to the &amp;quot;expeccoTesting/test&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
The green area does the actual publish operation,&lt;br /&gt;
publishing the &amp;quot;Hello from Expecco&amp;quot; string on this topic.&lt;br /&gt;
&lt;br /&gt;
In parallel, the blue area waits for the next incoming event,&lt;br /&gt;
(and generates a printed detail info on the received packet for the&lt;br /&gt;
activity log).&lt;br /&gt;
&lt;br /&gt;
The red area contains a stopWatch action block, which gets started&lt;br /&gt;
with when the publish is done, and stops when the event arrives.&lt;br /&gt;
&lt;br /&gt;
The measurement action is prametrized with the broker&#039;s host and port,&lt;br /&gt;
and generates the measured timeDuration as output.&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Test Run ====&lt;br /&gt;
&lt;br /&gt;
Assuming that you have an MQTT client running on the local host,&lt;br /&gt;
the test sequence can be immediately tried via the Test/Demo page:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Test_Measuring_MQTT.png|300px]]&lt;br /&gt;
&lt;br /&gt;
A good strategy is to package the MQTT broker into a docker file.&lt;br /&gt;
Here is the MQTT client&#039;s output of the above run:&lt;br /&gt;
&lt;br /&gt;
 $ sh rundocker.sh &lt;br /&gt;
 1522084243: mosquitto version 1.4.8 (build date Thu, 01 Mar 2018 09:34:49 -0500) starting&lt;br /&gt;
 1522084243: Using default config.&lt;br /&gt;
 1522084243: Opening ipv4 listen socket on port 1883.&lt;br /&gt;
 1522084243: Opening ipv6 listen socket on port 1883.&lt;br /&gt;
 1522084272: New connection from 172.17.0.1 on port 1883.&lt;br /&gt;
 1522084272: New client connected from 172.17.0.1 as expeccoMQTT (c1, k0).&lt;br /&gt;
 1522084272: Client expeccoMQTT disconnected.&lt;br /&gt;
&lt;br /&gt;
and here the activitylog in expecco:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log1_Measuring_MQTT.png|600px]]&lt;br /&gt;
&lt;br /&gt;
with the event info detail:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log2_Measuring_MQTT.png|600px]]&lt;br /&gt;
&lt;br /&gt;
==== Step 3: Running that Sequence Multiple Times ====&lt;br /&gt;
&lt;br /&gt;
Now that we have verified, that the communication with the MQTT broker works correctly,&lt;br /&gt;
we create another action bock, which runs the above action multiple times and collects&lt;br /&gt;
all the execution times:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT.png|700px]]&lt;br /&gt;
&lt;br /&gt;
[ explanation: first, a new empty colletion is created, then, the timing values of&lt;br /&gt;
the multi-triggered measure-step are all added to this collection.&lt;br /&gt;
When done (no step active), the collection is forwarded (now filled) to the output pin]&lt;br /&gt;
&lt;br /&gt;
Here is the activity log after 10 runs:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Log1_Multiple_Measuring_MQTT.png|700px]]&lt;br /&gt;
&lt;br /&gt;
==== Step 4: Adding the Graph ====&lt;br /&gt;
&lt;br /&gt;
The last step is the easiest: select the &amp;quot;New Plot/Graph&amp;quot; action block from the menu,&lt;br /&gt;
name it &amp;quot;Time Measurement Graph&amp;quot;&lt;br /&gt;
and change its autogenerated gnuplot script to:&lt;br /&gt;
&lt;br /&gt;
 set output &amp;quot;%(outputFile)&amp;quot;&lt;br /&gt;
 set term %(outputFormat)&lt;br /&gt;
 set title &amp;quot;%(title)&amp;quot;&lt;br /&gt;
 set key left box&lt;br /&gt;
 plot [1:%(dataSize)] [0:100] &amp;quot;%(data)&amp;quot; title &amp;quot;%(lineTitle)&amp;quot; with impulses&lt;br /&gt;
&lt;br /&gt;
then place a step of it into your network and connect the &amp;quot;collectedTimes&amp;quot; output of the above measurement step with the &amp;quot;data&amp;quot; input&lt;br /&gt;
of the &amp;quot;Measurement-Graph&amp;quot; step:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_plus_Graph_MQTT.png|500px]]&lt;br /&gt;
&lt;br /&gt;
Running it, and generating the report, produces the following pdf-report (the logging of subactivities was disabled for this report):&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT_Report_img.png|700px]]&lt;br /&gt;
&lt;br /&gt;
[[Datei:Multiple_Measuring_MQTT_Report.pdf]]&lt;br /&gt;
&lt;br /&gt;
== Example 1 Simplified: Using Probes to Gather the Data ==&lt;br /&gt;
&lt;br /&gt;
The above data collection may look a bit complicated, due to the need for start/stop synchronization.&lt;br /&gt;
A much nicer and easier to use solution is in using a probe to gather the data.&lt;br /&gt;
For this, we need a probe which does no range checks, but instead only collects the incoming values:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_Probe.png|700px]]&lt;br /&gt;
&lt;br /&gt;
Here, a probe was added (via the &amp;quot;timeDelta&amp;quot; pin&#039;s &amp;quot;Add Probe&amp;quot; menu item),&lt;br /&gt;
and specified to &amp;quot;collect incoming data without check&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_ProbeMenu.png|500px]]&lt;br /&gt;
&lt;br /&gt;
Now that we have a probe at the roundtrip time,&lt;br /&gt;
the outer measurement looks much simpler:&lt;br /&gt;
&lt;br /&gt;
* reset the probe (to clear any previously gathered data)&lt;br /&gt;
* prime the probe enable data collecting)&lt;br /&gt;
* run the roundtrip-test n-times&lt;br /&gt;
* unprime the probe (disable further data collecting)&lt;br /&gt;
* fetch the probe&#039;s values and pass them to the gnuplot action&lt;br /&gt;
&lt;br /&gt;
[[Datei:Measuring_MQTT_with_Probe2.png|500px]]&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
[http://www.gnuplot.info/docs_5.0/gnuplot.pdf Gnuplot Manual]&lt;br /&gt;
&amp;lt;br&amp;gt;[https://alvinalexander.com/technology/gnuplot-charts-graphs-examples Collection of Examples]&lt;br /&gt;
&lt;br /&gt;
&amp;quot;[[R Action Blocks]]&amp;quot; which provide similar graph/plot functions in addition to sophisticated mathematical and statistics libraries.&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;quot;[[Octave Script Action Tutorial|Octave/Matlab Actions]]&amp;quot; another framework for mathematical computations and/or to generate graphs/plots.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- {{Languages}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category: Languages ]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=30914</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=30914"/>
		<updated>2026-02-23T13:31:04Z</updated>

		<summary type="html">&lt;p&gt;Sv: /* Release 26.1 (Q2 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q2 2026) ==&lt;br /&gt;
*Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=30913</id>
		<title>Release Notes 26.x</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Release_Notes_26.x&amp;diff=30913"/>
		<updated>2026-02-23T13:30:43Z</updated>

		<summary type="html">&lt;p&gt;Sv: Release dates added&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;See also: [[Release Notes 25.x]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Release 26.2 (Q4 2026) ==&lt;br /&gt;
&lt;br /&gt;
== Release 26.1 (Q&amp;quot; 2026) ==&lt;br /&gt;
*Feature: Qt-Plugin supports Qt6.8 ([[QT_Testing/en#ExpeccoTestService_Library%3A_Delivery_in_Expecco_Versions|Delivered versions for QT and build environment]])&lt;br /&gt;
* Feature: improved search text box behavior in text editors (type RETURN, CMD-f or CMD-b while box is open) and back to original position button added.&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Hauptseite&amp;diff=30912</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Hauptseite&amp;diff=30912"/>
		<updated>2026-02-23T13:29:46Z</updated>

		<summary type="html">&lt;p&gt;Sv: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Hauptseite/en|English Version]]&lt;br /&gt;
&lt;br /&gt;
Klicken Sie links auf &#039;&#039;&amp;quot;Letzte Änderungen&amp;quot;&#039;&#039; (&amp;quot;Recent changes&amp;quot;) um die neuesten Einträge in der Dokumentation zu sehen.&amp;lt;br&amp;gt;Sie sehen dann meist, woran unsere Entwickler gerade vornehmlich arbeiten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt;expecco&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; padding:0; white-space:nowrap&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Für Einsteiger&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;!-- Link mit Fragezeichen funktioniert nicht, deshalb externer Link --&amp;gt;&lt;br /&gt;
			&amp;lt;!-- &amp;lt;li&amp;gt;[[Was ist expecco?|Allgemeine Informationen]]&amp;lt;/li&amp;gt; --&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[https://doc.expecco.de/w2.x/index.php?title=Was_ist_expecco%3F Allgemeine Informationen]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Expecco Usecases|Einsatzbereiche]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Features|expecco Feature-List]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Tutorials]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[usageHints/en|Hinweise zur Bedienung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Glossary|Begriffserklärung]] (Glossar)&amp;lt;/li&amp;gt;&lt;br /&gt;
 			&amp;lt;!-- &amp;lt;li&amp;gt;[[Testorganisation|Testorganisation]]&amp;lt;/li&amp;gt; --&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[FAQ]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage5.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Installation&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Installation]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Installing additional Frameworks/en| Installation zusätzlicher Tools (Node, Python, ...)]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Personal Settings|Persönliche&amp;amp;nbsp;Einstellungen]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Configuration &amp;amp; Setup|Konfiguration]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Anbindung expecco ALM]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[SAP_Testing|Anbindung expecco an SAP]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Spezifische Anpassung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco UI]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_Remote_Control_App|expecco Mobile Remote App]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Probleme &amp;amp; Fehler]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage4.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Werkzeuge&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[API von Elementaraktionen]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Debugger]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Editoren]]&amp;lt;/li&amp;gt;		&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Standard Libraries/Bibliotheken]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Weitere Werkzeuge]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Weitere Funktionen]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage6.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Reportgenerierung&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Report Generation|Reportgenerierung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Errors during Execution|Fehler während der Ausführung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Secret Strings|Geheime Zeichenketten und Passwörter]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	 &amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Erweiterung_plugin.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Erweiterungen (Plugins)&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[GUI Testing]]&lt;br /&gt;
				&amp;lt;ul&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Java GUI Plugins|Java Swing/FX/SWT UI Testing]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Mobile Testing Plugin|Mobile Testing auf Android und iOS]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[WindowsAutomation Reference 2.0|UI Testing mit der Windows Automation Library]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Selenium_WebDriver_Plugin|Web Testing mit Selenium WebDriver]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                                        &amp;lt;li&amp;gt;[[VNC_Plugin_Reference|VNC UI Testing]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[GUI Testing|Weitere GUI Testing Plugins]]&amp;lt;/li&amp;gt;&lt;br /&gt;
				&amp;lt;/ul&amp;gt;&lt;br /&gt;
			&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Schnittstellen zum SUT]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[ManualTest]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;Produktivit&amp;amp;auml;t&lt;br /&gt;
				&amp;lt;ul&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[GIT Plugin]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                                        &amp;lt;li&amp;gt;[[HG Plugin (Mercurial)]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[CVS Plugin]]&amp;lt;/li&amp;gt;&lt;br /&gt;
					&amp;lt;li&amp;gt;[[Anbindung expecco ALM | expeccoALM Schnittstelle]]&amp;lt;/li&amp;gt;&lt;br /&gt;
				&amp;lt;/ul&amp;gt;&lt;br /&gt;
			&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Weitere Plugins]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe;  margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage3.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Elemente der Testsuite&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Tree Elements|Tree Elemente]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Folder Element|Ordner]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Testplan Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Aktionen/Aktionsblöcke]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Datatype Element|Datentyp Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Inventory Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Skill Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Resource Element|Ressource Element]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Attachment Element|Anhänge]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[ReportTemplate Element|Report Templates]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color:#ecf0fe;  margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage3.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;API Referenz (Elementarbausteine)&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[API von Elementaraktionen|Übersicht]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco API#JavaScript_and_Smalltalk_Elementary_Blocks|Smalltalk &amp;amp; JavaScript]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco API#Groovy_Elementary_Blocks|Groovy]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Node.js_.28Bridged.29_Elementary_Blocks|NodeJS]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Bridged_Python_Elementary_Blocks|Python]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Bridged_Ruby_Elementary_Blocks|Ruby]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco_API#Bridged_C_Elementary_Blocks|C]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Expecco API#VisualBasic_Elementary_Blocks|VisualBasic ]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Expecco Scripting API|Shell und Script Languages]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Useful API Functions|Useful API Functions:]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;ul&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Number API Functions|Number API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[String API Functions|String API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Collection API Functions|Collection API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Filename API Functions|Filename API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Stream API Functions|Stream API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                          &amp;lt;li&amp;gt;[[Date_and_Time_API_Functions|Date &amp;amp; Time API Functions]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;/ul&amp;gt; &lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Diagramm.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Diagramm - Elemente&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Step|Schritt]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Pins (Ein - Ausgänge)]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Code Ausführung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Connection|Verbindung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Pin|Pin Beschreibung]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Annotation|Notiz]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[DiagramElements-Probe|Messfühler]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Refresh.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Sonstiges&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 26.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 25.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 24.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 23.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 22.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 21.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 20.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 19.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 18.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 2.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Release Notes 1.x]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Future releases expecco|Zukünftige Releases]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Smalltalk]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Beispiele_icon.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Beispiele&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
                &amp;lt;span style=&amp;quot;padding:0 1em;font-size:0.9em&amp;quot;&amp;gt;Sie haben Fragen, wie ein Projekt aussieht?&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
                &lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;amp;nbsp;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Beispiele | Hier finden Sie weitere Anwendungsbeispiele.]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Expecco Code Snippets/en | Codeschnipsel]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Beispiele_icon.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Unterstützung weiterer Qualitätsmanagement Tools&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
                &lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Jira_Plugin_Reference|Jira]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[PolarionPlugin_Reference|Polarion]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Aqua_Plugin_Reference|Aqua]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Starting_expecco_via_Command_Line#Integration_with_Jenkins|Jenkins Integration]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2 style=&amp;quot;clear:left&amp;quot;&amp;gt;expecco ALM&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; flex-wrap:wrap; padding:0; white-space:nowrap&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Sonstiges-Info.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Allgemein&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Was ist expecco ALM?]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Was ist der expecco Lizenzserver?]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Anbindung expecco ALM]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Rollen und Rechte]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[Filter]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[FAQ expeccoALM/en | FAQ]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Icon_Landingpage5.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Installation/Einrichten&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Installation|Installation]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Installation Patches|expecco ALM Installation Patches]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Einrichten Vorgehensweise|expecco ALM Einrichten]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM expecco Patch Service |Patchservice für expecco]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Refresh.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Plugins&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[AIDYMO Jira|Atlassian Jira]]&amp;lt;/li&amp;gt;&lt;br /&gt;
                        &amp;lt;li&amp;gt;[[CustomerSpecific|Kundenspezifische Portale]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div style=&amp;quot;background-color:#ecf0fe; margin:12px 8px 0 0; flex:1 0 auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		 &amp;lt;span style=&amp;quot;padding-left:0.6em&amp;quot;&amp;gt;[[Datei:Refresh.png|30px]]&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;padding:0 1em&amp;quot;&amp;gt;Sonstiges&amp;lt;/span&amp;gt;&amp;lt;hr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		  &amp;lt;ul style=&amp;quot;font-size:0.8em; padding:0.7em 1em 1em 2.4em&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM App]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[Installation Lizenzserver]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Release Notes|Release Notes]]&amp;lt;/li&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;[[expecco ALM Known Issues|Known Issues]]&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
	&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Settings_ExternalScriptInterpreterSettings/en&amp;diff=30911</id>
		<title>Settings ExternalScriptInterpreterSettings/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Settings_ExternalScriptInterpreterSettings/en&amp;diff=30911"/>
		<updated>2026-02-23T12:53:21Z</updated>

		<summary type="html">&lt;p&gt;Sv: Änderung übernommen aus altem Wiki von cg, 19.11.25&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== External Script Interpreter Settings ==&lt;br /&gt;
&lt;br /&gt;
=== General Script Execution ===&lt;br /&gt;
===== Additional Path =====&lt;br /&gt;
Defines additional paths which are added to the $PATH environment variable (%PATH% on Windows).&lt;br /&gt;
This is passed to any external script interpreter.&amp;lt;br&amp;gt;&lt;br /&gt;
[[Datei:bulb.png|20px]]Notice, that paths are separated by &amp;quot;:&amp;quot; (colon) under Unix/Linux but &amp;quot;;&amp;quot; (semi) under Windows.&lt;br /&gt;
Additional paths are useful if a script calls for other programs, and you do not want to hardcode any paths in the script, in order to allow for the suite to be portable to other machines.&lt;br /&gt;
&lt;br /&gt;
===== Environment Variables =====&lt;br /&gt;
Additional environment variables passed to external script interpreters.&lt;br /&gt;
To change, enter name and value and press &amp;quot;&#039;&#039;Add&#039;&#039;&amp;quot;, or select a variable from the list and press &amp;quot;&#039;&#039;Remove&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Subitems under External Script Interpreter Settings ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_Shell_and_BatchSettings/en |Shell &amp;amp; Batch Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_GroovyClassPathSettings/en|Groovy Class Path Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_NodeSettings/en|Node Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_PythonSettings/en|Python Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_Jython_and_IronPythonSettings/en|Jython &amp;amp; IronPython Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_CBridgeSettings/en|CBridge Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_ClingSettings/en|Root/Cling Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_RubySettings/en|Ruby Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_DartSettings/en|Dart Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_JuliaSettings/en|Julia Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_SmalltalkSettings/en|Smalltalk Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_SchemeSettings/en|Scheme Settings]]&lt;br /&gt;
* [[ Settings_ExternalScriptInterpreters_OtherSettings/en|Other Settings]]&lt;br /&gt;
&lt;br /&gt;
&amp;amp;nbsp;&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
See also [[ Installing_additional_Frameworks/en |&amp;quot;Installing additional frameworks&amp;quot;]].&lt;br /&gt;
&lt;br /&gt;
[[ Settings_ExternalScriptInterpreterSettings/en|Back to &amp;quot;External Script Interpreter Settings&amp;quot;]]&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
[[ Settings/en|Back to the main page &amp;quot;Settings&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Settings]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Settings_SSDPSettings/en&amp;diff=30910</id>
		<title>Settings SSDPSettings/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Settings_SSDPSettings/en&amp;diff=30910"/>
		<updated>2026-02-23T12:50:54Z</updated>

		<summary type="html">&lt;p&gt;Sv: Änderung übernommen aus altem Wiki von cg, 19.11.25&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Enable SSDP Announcements ==&lt;br /&gt;
&lt;br /&gt;
If enabled, expecco will announce its availability via SSDP multicast messages (also known as UPnP).&lt;br /&gt;
These announcements are especially useful for:&lt;br /&gt;
* the remote control app which runs on a mobile device&lt;br /&gt;
* other expeccos to detect possible execution partners&lt;br /&gt;
* expeccoALM to detect possible execution slaves&lt;br /&gt;
* administrators looking for machines which might have floating licenses allocated&lt;br /&gt;
&lt;br /&gt;
[[Datei:bulb.png|20px]]Notice, that SSDP messages are UDP messages which are sent in regular time intervals, &lt;br /&gt;
and received within your local network (the site), depending on your router/switch and firewall setup.&lt;br /&gt;
&lt;br /&gt;
To reduce traffic in the local network, some (usually bigger) organizations might disable, block or forbid SSDP announcements. &lt;br /&gt;
Or their spread may be limited to a subset of your machines (i.e. rooms/departments).&lt;br /&gt;
&amp;lt;br&amp;gt;Also, WLAN access points may have to be configured to forward these messages within the segment.&lt;br /&gt;
 &lt;br /&gt;
If in doubt, ask your administrator.&lt;br /&gt;
&lt;br /&gt;
For your convenience, expecco includes a monitor window to show incoming SSDP messages (&amp;quot;&#039;&#039;Extras&#039;&#039;&amp;quot; &amp;amp;#8594; &amp;quot;&#039;&#039;Debugging&#039;&#039;&amp;quot; &amp;amp;#8594; &amp;quot;&#039;&#039;Network Diagnose&#039;&#039;&amp;quot; &amp;amp;#8594; &amp;quot;&#039;&#039;SSDP Monitor&#039;&#039;&amp;quot;).&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Folder_Element/en&amp;diff=30909</id>
		<title>Folder Element/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Folder_Element/en&amp;diff=30909"/>
		<updated>2026-02-23T12:48:51Z</updated>

		<summary type="html">&lt;p&gt;Sv: Änderung übernommen aus altem Wiki von cg, 19.11.25&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
Folder elements are used to group related items. Notice that any action block item can have sub items. Besides being used to specify user-defined action menus (see [[Testsuite Editor-Metadata Editor/en | Testsuite Editor]]), folders have no special semantic relevance.&lt;br /&gt;
&lt;br /&gt;
[[Datei:bulb.png|20px]]Notice that in contrast to other test tools, folders are NOT used to group testcases into test sequences or sub-activities into a composite activities. They are only used for organization - not to specify any execution order or execution behavior.&lt;br /&gt;
&lt;br /&gt;
Folder elements only provide a single editor tab, into which documentation related to the folder can be entered. If this documentation is left empty, the documentation editor presents a short summary of the containing element&#039;s documentation texts (read-only).&lt;br /&gt;
&lt;br /&gt;
== Useful Tricks ==&lt;br /&gt;
&lt;br /&gt;
=== Public vs. Private Actions ===&lt;br /&gt;
&lt;br /&gt;
In imported library suites, put stuff which is supposed to be used by importing projects into a separate folder named &amp;quot;API&amp;quot; or &amp;quot;Exported&amp;quot;. This makes it easier for other team members to find reusable stuff. You may also want to change the folder&#039;s icon color (contextMenu &amp;amp;#8594; &amp;quot;&#039;&#039;Misc&#039;&#039;&amp;quot; &amp;amp;#8594; &amp;quot;&#039;&#039;IconColor&#039;&#039;&amp;quot;) to emphasize particular folders (such as &amp;quot;red&amp;quot; for obsolete or experimental actions, &amp;quot;green&amp;quot; for released/public actions etc.)&lt;br /&gt;
&lt;br /&gt;
=== Demos and Examples ===&lt;br /&gt;
&lt;br /&gt;
Add a folder named &amp;quot;Playground&amp;quot;, &amp;quot;Sandbox&amp;quot; or &amp;quot;Examples&amp;quot; or similar and place compound action blocks which demonstrate key features or low level access functionality. Other team members may later easily find those and play with the examples. It is also a good idea to place simple connection, communication, [[Glossary/en#SUT_.28System_Under_Test.29|SUT]] is running tests, setup and shutdown blocks there (unless those are commonly used, then put them into API/Exported).&lt;br /&gt;
&lt;br /&gt;
=== Define Useful Actions as Menu Functions ===&lt;br /&gt;
&lt;br /&gt;
Add a folder named &amp;quot;menu Actions&amp;quot; or similar, place and useful actions for test setup, equipment startup and shutdown or calibration into it. When this folder is defined as the project&#039;s operation menu folder (in the test suite&#039;s &amp;quot;Misc&amp;quot; editor page), you can quickly execute those functions via the main menu. For more information, see [[User Defined Menu Items]].&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
[[Tree Elements]]&lt;br /&gt;
&amp;lt;br&amp;gt;[[ Navigation Tree ]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Tree Elements]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
	<entry>
		<id>https://doc.expecco.de/index.php?title=Settings_WindowsAutomationSettings/en&amp;diff=30908</id>
		<title>Settings WindowsAutomationSettings/en</title>
		<link rel="alternate" type="text/html" href="https://doc.expecco.de/index.php?title=Settings_WindowsAutomationSettings/en&amp;diff=30908"/>
		<updated>2026-02-23T12:47:06Z</updated>

		<summary type="html">&lt;p&gt;Sv: Änderung übernommen aus altem Wiki von cg, 25.11.25&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
[[Datei:bulb.png|20px]]Notice:&amp;lt;br&amp;gt;The Windows Automation Plugin (WA V1) has been replaced by a new version,&lt;br /&gt;
and is only maintained to provide backward compatibility for customers using it.&lt;br /&gt;
Please migrate to use the WA2 Plugin, which is described in&lt;br /&gt;
&amp;quot;[[WindowsAutomation_Reference_2.0/en| WindowsAutomation Reference 2.0]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Windows-Automation Plugin Settings ==&lt;br /&gt;
 &lt;br /&gt;
The Windows-Automation plugin extends the GUI-browser&#039;s capabilities to support recording, replay and testing of GUI applications via the Windows-Automation technology.&lt;br /&gt;
&lt;br /&gt;
This settings dialog is only shown/enabled if the plugin is loaded and licensed.&lt;br /&gt;
----&lt;br /&gt;
Back to [[Online Documentation]]&lt;br /&gt;
&lt;br /&gt;
[[Category: Settings]]&lt;/div&gt;</summary>
		<author><name>Sv</name></author>
	</entry>
</feed>