Skip to main content

rrun

PyPI Python License publish

中文文档

Run local scripts on remote machines over SSH — via stdin pipes, never via command-line arguments — so quoting, escaping, and CJK/UTF-8 encoding survive the bash → ssh → cmd/powershell journey intact.

  • python / powershell / bash, inferred from the file extension or the remote OS
  • Remote stdout/stderr stream back verbatim; the exit code passes through, so pipes and CI just work
  • ssh ControlMaster multiplexing: ~0.5s on first contact, ~0.02s afterwards
  • One-command provisioning of a unified remote Python 3.12 venv (rrun setup) — the remote never touches the internet
  • Zero third-party Python dependencies

Install

pipx install rrun-cli    # provides the `rrun` command (plus a `remote-machine` alias)
# or: pip install rrun-cli

The PyPI distribution is named rrun-cli (rrun sits on PyPI's prohibited-name list as it is confusable with run); the installed command is plain rrun.

Control machine: Linux or macOS (Windows works via WSL), Python ≥ 3.10, plus ssh and sshpass:

sudo apt install sshpass                        # Debian/Ubuntu
brew install hudochenkov/sshpass/sshpass        # macOS (sshpass is not in homebrew-core)

Remote machines: an OpenSSH server with password auth enabled. Windows remotes execute via PowerShell, Mac/Linux remotes via bash.

Quickstart

Create ~/.rrun/machines.json (full format in Configuration):

{
  "machines": [
    { "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows", "user": "admin", "password": "secret" }
  ]
}

Then:

rrun machines                            # verify the inventory is picked up (redacted)
printf 'Write-Output "hello 中文"\n' > demo.ps1
rrun exec my-win-box demo.ps1            # powershell, inferred from .ps1

That's the whole loop: write a script locally → it runs remotely → you get its output and exit code.

Why

Running commands on remote Windows machines from a POSIX shell is a minefield: sshd lands you in cmd with a GBK codepage, quotes and $ get eaten by one of the three shells along the way, and any non-ASCII argument gets mojibake'd. rrun's rules:

  • Script content (UTF-8, CJK welcome) always travels through stdin — never the command line.
  • PowerShell payloads are base64-wrapped into a single-line pure-ASCII wrapper (powershell -Command - executes stdin line-by-line; multi-line input fails silently), then decoded and invoked as a ScriptBlock remotely.
  • Command-line arguments are restricted to ASCII and passed through safely (sys.argv / $@ / $args). Put anything fancier in the script itself or in a JSON file.

The full set of hard-won conventions and internals: docs/remote-exec-conventions.md (中文).

Subcommands

Command Purpose
rrun exec <host> <script|-c ...> Execute a local script / inline content remotely
rrun machines [--json] List merged machines (redacted, with source)
rrun config Diagnose the machines.json source chain
rrun setup <host|--all> [--force] Provision the unified remote Python 3.12 venv (idempotent)
rrun pip <host> -- list Run pip inside the remote unified venv
rrun doctor <host|--all> Health-check ssh + auth + remote python (--all opens real connections to every machine)
rrun close [<host>|--all] Close ssh ControlMaster multiplexed connections

Useful exec flags: --lang bash|powershell|python, --workdir/--env K=V (not for Windows+python), --timeout, --python <path> (skip detection), --no-mux, -q.

Exit codes

Code Meaning
0–254 The remote script's own exit code, passed through unchanged
255 ssh transport failure (unreachable / auth failure / connection dropped)
124 local --timeout expired; the local ssh client was killed

Configuration: machines.json

Credentials live in local machines.json files — see machines.template.json:

{
  "defaults": { "windows": { "os": "Windows" } },
  "machines": [
    { "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows",
      "user": "admin", "password": "secret" },
    { "name": "cloud-vm", "ip": "1.2.3.4", "port": 2222, "os": "Linux",
      "user": "root", "identity_file": "~/.ssh/id_ed25519" }
  ]
}

Per-machine fields: name/ip/user are required; password (plaintext, via sshpass) or leave it empty for key-based auth (identity_file optional — the default ssh key chain / agent / ~/.ssh/config applies, with BatchMode=yes so a missing key fails fast instead of prompting); port (default 22); os (Windows / Mac / Linux); optional hostname (also resolvable), description, python (explicit remote interpreter, skips auto-detection).

Sources are merged by machine name, highest priority first (all optional, failures skipped silently):

  1. $RRUN_CONFIG (os.pathsep-separated, multiple files allowed)
  2. $REMOTE_MACHINE_CONFIG (legacy name, still honored)
  3. ./machines.json (current working directory)
  4. ~/.rrun/machines.json
  5. ~/.rrun/machines.d/*.json (sorted by filename — point each inventory at its own file/symlink)
  6. Legacy: ~/.remote-machine/machines.json (POSIX) / C:\tools\remote-machine\machines.json (Windows)

Per-file defaults sections (keyed by os, lowercased) are applied before merging. Inspect the chain with rrun config; list machines (redacted) with rrun machines.

Remote Python: unified 3.12 environment

For python scripts, rrun requires a 3.12.x interpreter on the remote, probed in order:

  1. Unified venv: C:\tools\remote-machine\venv\Scripts\python.exe / ~/.remote-machine/venv/bin/python
  2. Standalone base: ...\python312\python.exe / ~/.remote-machine/python312/bin/python3
  3. Existing installs: C:\Python\Python312\python.exe / python3.12 on PATH

If none match, run rrun setup <host>. It is idempotent and non-destructive: if the machine lacks a venv-capable Python 3.12 — none installed, or the interpreter cannot create venvs (Debian/Ubuntu split ensurepip into the separate python3.12-venv package) — a python-build-standalone tarball (Windows/macOS/Linux) is downloaded to the local cache (~/.cache/rrun/) and streamed over ssh stdin — no registry, no PATH changes, no admin rights, and the remote never touches GitHub. A half-created venv left by an interrupted earlier setup (python runs but pip is missing) is detected and recreated automatically. rrun doctor <host> reports all of these states before you provision. The venv gets its own pip.ini/pip.conf (global pip config untouched) plus the standard packages from the bundled remote-requirements.txt (override with ~/.rrun/remote-requirements.txt).

Security

  • machines.json stores plaintext passwords. Keep it local, chmod 600, never commit it — or leave password empty and use key-based auth instead.
  • Key-based auth runs ssh with BatchMode=yes (no interactive prompts; a missing/unauthorized key fails fast instead of eating the script from stdin).
  • The audit log never records passwords, and --env values are logged as keys only.

Auditing & state

  • Every execution appends a JSON line to ~/.rrun/log/remote-exec.jsonl (host, lang, script sha1, exit code, duration).
  • Inline -c payloads are archived under ~/.rrun/drops/ for replay.
  • The state root defaults to ~/.rrun/ and is overridable via $RRUN_HOME.

Upgrading / uninstalling

pipx upgrade rrun-cli
pipx uninstall rrun-cli

Contributing

Issues and PRs are welcome. Development setup: clone → python3.12 -m venv .venv && .venv/bin/pip install -e ".[dev]" → hack → pytest + ruff check . → smoke-test with rrun machines. CI runs unit tests, ruff, and an end-to-end suite against a real sshd container. Releases are cut by pushing a vX.Y.Z tag; CI builds and publishes to PyPI via trusted publishing. See CHANGELOG.md.

License

MIT

Metadata

Release files for rrun-cli 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rrun-cli 0.2.2
File Size Uploaded
rrun_cli-0.2.2.tar.gz 35.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rrun-cli 0.2.2
File Interpreter ABI Platform
rrun_cli-0.2.2-py3-none-any.whl Python 3 none any Details

Total release size: 65.5 kB

Release files / rrun_cli-0.2.2.tar.gz

Download URL rrun_cli-0.2.2.tar.gz
Size 35.1 kB
Tags Source
SHA-256 checksum
How to use checksums
c3f83dece744712806f6f5a90aff3a9c4541e73807911e54b9d1a4844605ce3a
BLAKE2b-256 checksum
How to use checksums
70bd8c585ade371df309b476084dc86fce39791d1803f3664aa63c49b6255097
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / rrun_cli-0.2.2-py3-none-any.whl

Download URL rrun_cli-0.2.2-py3-none-any.whl
Size 30.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c6ee5121575598de42fb27cb340efc607f9471f85580c2b533436bd6615f43e7
BLAKE2b-256 checksum
How to use checksums
2da641375afbac4c14d14ceab331f41a88e7e763f9b15ca51c9f6e9cf225bb72
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

This release

0.2.2 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page