oxdock-ssh-plugin
Ephemeral user-space SSH server and client for OxDock scripts, bridged to stdio pipelines through the Engine facade.
Part of the OxDock workspace.
Overview
The SSH module spins up ephemeral loopback SSH servers with in-memory
credentials, dequeues each session with its metadata, and pumps bytes
through explicit DSL pipes. Import it with IMPORT [STD, SSH] in a
runner that registers the host module on the
Engine facade. The function
reference below lists every entry with signatures; the type reference
lists the session and server handles.
Functions
Callable as MODULE::NAME(...) in expressions (or bare NAME(...) with the module imported via IMPORT). Introspectable from scripts with FUNCTIONS() and DESCRIBE(name).
SSH_ACCEPT
Signature: SSH_ACCEPT($server, $in_pipe, $out_pipe) -> MAP
Contexts: AST only
Accept one SSH session into pipes.
Accept the next authenticated session and pump it through explicit
pipes until the channel closes. Must run inside ASYNC. Returns a
MAP with closed (BOOL) and command (STRING, empty for shells).
Thin wrapper over SSH_DEQUEUE + SSH_PUMP_CHANNEL for worker loops
that need no pre-pump inspection; use those directly to route on
session metadata first. Returns a MAP with closed (BOOL) and
command (STRING, empty for shells); the example below asserts both
keys on the awaited result. The server sends first: the client side
never EOFs its input, so the reply cannot race teardown. This
complete program runs end to end under the docs conformance suite.
IMPORT [STD, SSH]
LET $m: MAP = SSH_SERVE("doc-ssh-demo", {username: "u", password: "p"})
LET $in: PIPE
LET $out: PIPE
LET $acc: HANDLE = ASYNC { SSH_ACCEPT($m.server, $in, $out) }
LET $cin: PIPE
LET $cout: PIPE
LET $c: HANDLE = ASYNC { SSH_CONNECT("doc-ssh-demo", "u", "p", $cin, $cout) }
WITH_IO [stdout=$in] ECHO "server-greeting"
LET $info: MAP = INSPECT($cout)
LET $empty: BOOL = $info.buffer_bytes == 0
WHILE $empty {
SLEEP 100ms
$info = INSPECT($cout)
$empty = $info.buffer_bytes == 0
}
ASSERT_CONTAINS $cout "server-greeting"
LET $done: MAP = AWAIT $acc
ASSERT_CONTAINS $done "closed"
ASSERT_CONTAINS $done "command"
CANCEL $c
SSH_CLOSE($m.server)
SSH_CLOSE
Signature: SSH_CLOSE($server) -> BOOL
Contexts: AST only
Shut down an SSH server.
Shut a server down and join its runtime thread (bounded). Idempotent: returns BOOL true when no thread remains.
SSH_CONNECT
Signature: SSH_CONNECT($target: STRING, $username: STRING, $password: STRING, $in_pipe, $out_pipe) -> MAP
Contexts: AST only
Open an SSH client session into pipes. Target shapes: a logical port (CLI-mapped address or loopback default), a service name (CLI-mapped address only), a served address, or a host:port dial.
SSH_DEQUEUE
Signature: SSH_DEQUEUE($server) -> MAP
Contexts: AST only
Dequeue one SSH session with its metadata.
Dequeue one authenticated session and expose its metadata before any
byte pumping starts, so scripts can route on the requested command or
client identity. Must run inside ASYNC. Returns a MAP with
session (SSH_SESSION), command (STRING, empty for shells),
username and addr (STRINGs, empty when unknown).
Routing shape: compare the dequeued command against known commands,
build a fresh pipe pair per session, and pump a synthetic reply with
SSH_PUMP_CHANNEL. The server sends first: the client side never
EOFs its input, so the reply cannot race teardown. This complete
program runs end to end under the docs conformance suite.
IMPORT [STD, SSH]
LET $m: MAP = SSH_SERVE("doc-ssh-demo", {username: "u", password: "p"})
LET $in: PIPE
LET $out: PIPE
LET $w: HANDLE = ASYNC {
LET $sess: MAP = SSH_DEQUEUE($m.server)
ASSERT_CONTAINS $sess "session"
ASSERT_CONTAINS $sess "command"
ASSERT_CONTAINS $sess "username"
ASSERT_CONTAINS $sess "addr"
SSH_PUMP_CHANNEL($sess.session, $in, $out)
}
LET $cin: PIPE
LET $cout: PIPE
LET $c: HANDLE = ASYNC { SSH_CONNECT("doc-ssh-demo", "u", "p", $cin, $cout) }
WITH_IO [stdout=$in] ECHO "server-greeting"
LET $info: MAP = INSPECT($cout)
LET $empty: BOOL = $info.buffer_bytes == 0
WHILE $empty {
SLEEP 100ms
$info = INSPECT($cout)
$empty = $info.buffer_bytes == 0
}
ASSERT_CONTAINS $cout "server-greeting"
AWAIT $w
CANCEL $c
SSH_CLOSE($m.server)
SSH_PTY_RUN
Signature: SSH_PTY_RUN($session, $argv, $rows: INT, $cols: INT, $in_pipe, $out_pipe) -> INT
Contexts: AST only
Run a command under a sized local terminal into pipes.
Run argv under a local pseudo-terminal sized from the dequeued
session and pump it through explicit pipes until the child exits.
rows/cols seed the initial size when positive; non-positive falls
back to the session's requested size (the outer pty request, 24x80
default). Outer window-change requests resize this session's terminal
live; every session owns its size cell, so concurrent guests never
observe each other. Must run inside ASYNC. Returns the INT exit
code. Environment is inherited from the host process and layered with
the script environment like RUN: block-scoped ENV (such as the
session's SSH_USER / SSH_CLIENT / SSH_SERVER / SSH_COMMAND
relay) reaches the child; the working directory comes from the script.
SSH_PUMP
Signature: SSH_PUMP($from_pipe, $to_pipe) -> INT
Contexts: AST only
Copy one pipe into another until EOF.
Copy one pipe into another until EOF, then close the target.
Returns the INT byte count. Either task placement works, as long as
the other end is live (usually an ASYNC task).
SSH_PUMP_CHANNEL
Signature: SSH_PUMP_CHANNEL($session, $in_pipe, $out_pipe) -> MAP
Contexts: AST only
Pump a dequeued SSH session through pipes.
Pump a dequeued session between explicit DSL pipes until the channel
closes. Must run inside ASYNC. The session ends are take-once: a
second pump on the same session bails instead of splitting bytes.
Returns a MAP with closed (BOOL).
SSH_SERVE
Signature: SSH_SERVE($bind: STRING, $options) -> MAP
Contexts: AST only
Serve SSH on a virtual service endpoint.
Value types
Value type: SSH_SERVER
Handle to one ephemeral SSH server instance.
Minted by SSH_SERVE, consumed by SSH_ACCEPT and SSH_CLOSE.
Cloning the value shares the server; dropping the last clone
signals shutdown.
Value type: SSH_SESSION
Handle to one dequeued SSH session instance.
Minted by SSH_DEQUEUE, consumed once by SSH_PUMP_CHANNEL. Cloning
the value shares the session; metadata reads never consume. Display
shows id and peer only: the command string may carry secrets.
License
oxdock-ssh-plugin is distributed under the terms of the Apache License (Version 2.0).