scitex_ssh

SciTeX SSH - SSH primitives (exec/copy/attach) and gated reverse tunnels.

exception scitex_ssh.PolicyError[source]

Bases: RuntimeError

Raised when an action is denied by the allowlist.

class scitex_ssh.ProbeResult(reachable, capabilities=<factory>, returncode=0, stdout='', stderr='')[source]

Bases: object

reachable: bool
capabilities: dict
returncode: int = 0
stdout: str = ''
stderr: str = ''
property ok: bool

Reachable and every requested capability present (vacuously True if none requested).

has(name)[source]
Return type:

bool

__init__(reachable, capabilities=<factory>, returncode=0, stdout='', stderr='')
class scitex_ssh.SSHResult(returncode, stdout, stderr)[source]

Bases: object

returncode: int
stdout: str
stderr: str
property success: bool
__init__(returncode, stdout, stderr)
scitex_ssh.attach(host, command=None, *, ssh_opts=())[source]

Interactive ssh -t. Replaces current process via os.execvp; returns rc only on failure-to-launch.

Return type:

int

scitex_ssh.copy_from(host, src, dest, *, recursive=False, ssh_opts=(), runner=None)[source]

scp host:src to local dest. Same safe-by-default ssh_opts as copy_to.

Return type:

SSHResult

scitex_ssh.copy_to(host, src, dest, *, recursive=False, ssh_opts=(), runner=None)[source]

scp local src to host:dest. ssh_opts forwarded via -o (same safe-by-default ControlMaster/ClearAllForwardings behavior as exec_remote).

Return type:

SSHResult

scitex_ssh.exec_remote(host, command, *, ssh_opts=(), check=False, timeout=None, runner=None)[source]

Run a command on host via ssh.

ssh_opts is a list of raw ssh flags (e.g. [‘-A’, ‘-o’, ‘StrictHostKeyChecking=no’]) passed through verbatim. Users opt into agent forwarding by passing ‘-A’ themselves. Defaults to -o ControlMaster=no -o ControlPath=none -o ClearAllForwardings=yes (see _with_safe_transport_defaults) unless ssh_opts already sets one of those keys — a one-shot call shouldn’t inherit multiplexing or ambient config forwards.

Parameters:

runner (callable, optional) – Subprocess invoker matching subprocess.run’s signature. Defaults to subprocess.run. Pass a hand-rolled fake from tests to observe and stub the call without mocks.

Return type:

SSHResult

scitex_ssh.probe_remote(host, *, requires=(), ssh_opts=(), timeout=None, runner=None)[source]

Reachability + capability preflight over a single ssh round-trip.

Confirms host answers, then checks each name in requires via command -v <name> on the remote (e.g. “apptainer”, “rsync”). Policy-free like sync_dir: the caller decides which capabilities matter (what to require, what to do if missing); this only reports what is there.

Capability names are matched to remote output by ORDER, not by echoing the name back through the remote shell — avoids any quoting hazard for unusual capability strings and tolerates login-banner/MOTD noise on stdout (the reachability marker is located by scanning, not by assuming it is line 0).

Parameters:
  • host (str) – Remote ssh host (an ~/.ssh/config alias like spartan works).

  • requires (sequence of str) – Executable names to probe for via command -v on the remote. Empty by default — a bare reachability check.

  • ssh_opts (sequence of str) – Raw ssh flags forwarded verbatim, same convention as exec_remote (including the same safe-by-default ControlMaster/ClearAllForwardings behavior — see _with_safe_transport_defaults).

  • timeout (float, optional) – Passed through to the underlying ssh subprocess call.

  • runner (callable, optional) – subprocess.run-shaped invoker; defaults to subprocess.run. Pass a hand-rolled fake from tests to observe argv without mocks.

Returns:

reachable is False if ssh itself failed (down, auth, timeout) — in that case capabilities is always empty rather than reporting every requirement as absent, since an unreachable host tells you nothing about what is installed there.

Return type:

ProbeResult

scitex_ssh.sync_dir(host, local, remote, *, direction='push', exclude=(), delete=False, extra_opts=(), ssh_opts=(), runner=None)[source]

rsync a directory one-way between local and host over ssh.

A thin, policy-free wrapper over rsync -a for syncing a directory tree between the local machine and a remote host. Unlike copy_to / copy_from (single-shot scp, no delta/exclude), this does an incremental transfer with per-file excludes — the right tool for mirroring a per-user dir (e.g. ~/.scitex/scholar/library) between a WSL host and an HPC login node.

The primitive is deliberately generic: it never decides what to exclude or whether to delete. Callers pass exclude globs and any extra_opts (--checksum, --mkpath, --dry-run, …); any post-sync step (e.g. rebuilding a derived index) is the caller’s job via exec_remote.

Parameters:
  • host (str) – Remote ssh host (an ~/.ssh/config alias like spartan works).

  • local (str) – Local directory path. Trailing-slash semantics are rsync’s and are passed through verbatim: src/ copies contents, src copies the dir itself. The caller controls this.

  • remote (str) – Remote directory path on host.

  • direction ({"push", "pull"}) – push sends localhost:remote (default); pull pulls host:remotelocal.

  • exclude (sequence of str) – Glob patterns passed as --exclude=<pat> (e.g. index.db, *.db-wal). Never ship a live sqlite/WAL file — exclude it and rebuild or snapshot it caller-side.

  • delete (bool) – Add --delete (mirror deletions). Off by default: an additive merge library should not have receiver-side files deleted.

  • extra_opts (sequence of str) – Raw rsync flags appended verbatim (escape hatch for --checksum, --mkpath, --dry-run, --info=progress2).

  • ssh_opts (sequence of str) – ssh flags for the transport; wired via -e 'ssh <opts>' (e.g. ['-o', 'BatchMode=yes'] for non-interactive cron). Always includes the same safe-by-default ControlMaster/ClearAllForwardings behavior as exec_remote (see _with_safe_transport_defaults) — a plain rsync transport shouldn’t inherit multiplexing or ambient config forwards either.

  • runner (callable, optional) – subprocess.run-shaped invoker; defaults to subprocess.run. Pass a hand-rolled fake from tests to observe argv without mocks.

Return type:

SSHResult

scitex_ssh.setup(port, bastion_server=None, secret_key_path=None, *, host=None, runner=None, scripts_dir=None)[source]

Set up a persistent SSH reverse tunnel.

Parameters:
  • port (int) – The remote port to forward (e.g. 2222).

  • bastion_server (str, optional) – The bastion/relay server hostname or IP. Falls back to SCITEX_SSH_BASTION_SERVER env var.

  • secret_key_path (str, optional) – Path to the SSH private key for authentication. Falls back to SCITEX_SSH_SECRET_KEY_PATH env var.

Returns:

Result with ‘success’, ‘stdout’, ‘stderr’ keys.

Return type:

dict

Raises:

ValueError – If bastion_server or secret_key_path is not provided and the corresponding environment variable is not set.

scitex_ssh.remove(port, *, host=None, runner=None, scripts_dir=None)[source]

Remove a persistent SSH reverse tunnel.

Parameters:
  • port (int) – The remote port of the tunnel to remove.

  • host (str, optional) – Local host label for allowlist gating. Defaults to local hostname.

Returns:

Result with ‘success’, ‘stdout’, ‘stderr’ keys.

Return type:

dict

scitex_ssh.status(port=None, *, runner=None)[source]

Check status of SSH reverse tunnels.

Parameters:

port (int, optional) – Specific port to check. If None, shows all tunnels.

Returns:

Result with ‘success’, ‘stdout’, ‘stderr’ keys.

Return type:

dict

scitex_ssh.get_version()[source]

Get scitex-ssh version.

Return type:

str