oxdock-ssh-plugin 0.17.0-alpha

Ephemeral user-space SSH server and client for OxDock scripts, bridged to stdio pipelines through the Engine facade.
Documentation

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).