rmtx
[!WARNING]
rmtx is still experimental. Expect rough edges, breaking changes, and incomplete docs.
rmtx runs commands on a nearby host machine while keeping a persistent context on that host.
Quick overview
rmtx host: run the host service.
rmtx init: discover host, create local config, pair client.
rmtx exec -- <command> ...: run a command remotely in the current context.
rmtx pair: pair a client with a host.
rmtx ping: verify host connectivity/auth.
rmtx stats: report host CPU/RAM/core/per-core usage.
rmtx context ...: list/delete/prune host contexts.
rmtx context artifacts ...: list/prune/delete host-side context artifacts.
rmtx cache prune: delete unreferenced host cache data.
Install
go install github.com/manuel-huez/rmtx/cmd/rmtx@latest
rmtx version
Build from source
go build ./cmd/rmtx
Core client and host commands build on these platforms:
- Linux:
386, amd64, arm, arm64, loong64, mips, mips64,
mips64le, mipsle, ppc64, ppc64le, riscv64, s390x
- macOS:
amd64, arm64
- Windows:
386, amd64, arm64
- FreeBSD:
386, amd64, arm, arm64
- OpenBSD:
386, amd64, arm, arm64, riscv64
- NetBSD:
amd64, arm, arm64
- DragonFly BSD:
amd64
Support means rmtx host, non-TTY remote exec, sync, pairing, ping, stats,
context, and cache commands compile for that OS/architecture. Feature-specific
support is narrower:
- Interactive
--tty: Linux and Windows.
- OCI runtime: Linux hosts natively, Windows hosts through WSL2.
- NVIDIA GPU runtime support: Linux hosts and Windows hosts through WSL2.
rmtx wsl config: Windows only.
Unsupported Go targets include Android, iOS, JS/WASM, wasip1/WASM, Plan 9,
Solaris, illumos, AIX, NetBSD/386, and OpenBSD/ppc64.
Minimal setup
Start host:
./rmtx host --listen :33221
In your project on the client, initialize once:
./rmtx init
./rmtx exec -- go test ./...
rmtx init discovers available hosts, asks you to trust selected host fingerprint, writes .rmtx.json, then requests a one-time pairing code from that host and prompts you to enter it. After init, rmtx pair re-pairs an existing config. If LAN discovery is blocked, run rmtx host pair-code on host to get fingerprint, then use rmtx init --host 192.168.1.42:33221 --fingerprint sha256:.... For non-interactive/manual pairing, rmtx host pair-code and rmtx pair --code ... still work.
Generated .rmtx.json looks like:
{
"version": 1,
"context": { "name": "my-project" },
"tls": { "host_fingerprint": "sha256:..." },
"mounts": [{ "path": "." }],
"ignore": [".git/**", "node_modules/**"],
"ignore_gitignore": true
}
Config file
rmtx looks upward from the current directory for:
A config file is required for remote execution.
Use top-level ignore patterns to skip files for every mount. Use per-mount exclude
patterns for mount-specific ignores:
{
"mounts": [{ "path": ".", "exclude": ["tmp/**"] }],
"ignore": [".git/**", "node_modules/**", "dist/**"],
"ignore_gitignore": true
}
Use sync_back to limit which host-side changes are copied back after
each run. Paths are relative to the project root; a directory path includes its
descendants, and glob patterns like generated/** are supported:
{
"context": { "name": "my-project" },
"sync_back": ["coverage/", "generated/report.json"]
}
When omitted, all mounted paths are eligible for sync-back. Only paths whose
metadata or content changed are sent back to the client. After each run, the
host workspace is cleaned and rehydrated from synced blobs on the next run;
use runtime volumes for state that should persist only on the host.
Remote commands receive these rmtx environment variables:
RMTX=1: command is running under rmtx.
RMTX_RUNNER=host: command is running on the rmtx host.
RMTX_WORKSPACE: host workspace path visible to the command.
RMTX_CONTEXT_ID: rmtx context id.
RMTX_CPU_COUNT: host logical CPU count.
RMTX_MEMORY_AVAILABLE_BYTES: host available memory in bytes when the
command starts.
Isolated runtimes
By default, rmtx still runs commands directly on the host context workspace.
Add runtime to run commands inside an OCI image pulled by rmtx itself
without Docker or Podman:
{
"runtime": {
"type": "oci",
"image": "docker.io/library/ubuntu:24.04",
"pull_policy": "if_missing",
"workdir": "/workspace",
"network": "host",
"user": "root",
"wsl_distro": "Ubuntu-24.04",
"gpu": "none",
"setup": {
"image_commands": [
"apt-get update",
"apt-get install -y build-essential nodejs npm",
"npm install -g pnpm"
],
"context_commands": [
"pnpm install --frozen-lockfile"
],
"context_inputs": [
"package.json",
"pnpm-lock.yaml"
]
},
"volumes": [
{ "name": "pnpm-store", "target": "/pnpm/store" },
{ "name": "npm-cache", "target": "/root/.npm" }
]
}
}
Runtime defaults:
pull_policy: if_missing; use always to refresh registry metadata and
never to require the image to already exist in the local cache.
workdir: /workspace.
network: host; set none to isolate networking.
user: root only in v1.
gpu: none; set nvidia to require NVIDIA devices.
wsl_distro (Windows hosts only): required. Distro name passed to WSL.
Runtime storage has three roles:
- Image/rootfs: base OS and global tools. Use
setup.image_commands for system
packages and global CLIs. These commands run inside the isolated rootfs and
never install anything onto the host OS.
- Workspace: synced project copy, mounted at
runtime.workdir. Source files
and outputs that should come back to the client belong here and are governed
by sync_back.
- Volumes: persistent host-side context artifacts mounted inside the runtime.
Volumes never enter the sync manifest, never upload/download, and never
sync back. Use them for dependency caches, package stores, build caches,
local DB files, model caches, and other state that should survive runs but
stay on the host.
setup.context_commands run after workspace sync and before the requested
command. When setup.context_inputs is set, rmtx hashes those workspace files
and reruns context setup only when they change. If context_inputs is omitted,
context setup runs every command.
Synced file blobs, OCI image blobs, manifests, and refs are stored once in host
global caches, while contexts keep references to the data they use.
Context artifact commands show the project-owned view and list total bytes:
rmtx context artifacts list --current
rmtx context artifacts prune --current
rmtx context artifacts delete --current --volume pnpm-store
rmtx cache prune
rmtx context delete --current removes that context workspace, volumes,
prepared runtime references, and global cache data that has no remaining context
references. Prepared runtime metadata tracks the current runtime only, so older
rootfs variants stop pinning OCI data when runtime config changes. rmtx cache prune can also remove global cache data with no remaining context references,
old update installs, and stale Windows WSL staged rootfs copies. Runs prune
unreferenced synced file blobs after tracked manifests change.
Linux hosts use rootless user, mount, PID, IPC, and UTS namespaces for OCI
execution. network=none adds a network namespace. Windows hosts delegate OCI
execution to WSL2 through wsl.exe. Set runtime.wsl_distro in the project
config; rmtx uses that distro and auto-installs it if missing.
The private imported rmtx WSL distro and WSL ext4 workspace storage are still
deferred, so Windows OCI runs use paths bridged from Windows into WSL and may be
slower than native WSL ext4 storage.
gpu=nvidia requires NVIDIA/WSL GPU devices and fails clearly when unavailable.
Linux binds /dev/nvidia*, NVIDIA driver libraries discovered through
ldconfig, common CUDA driver paths, and nvidia-smi when present. Windows
WSL binds /dev/dxg and /usr/lib/wsl/lib. This is enough for common CUDA
images, but it is not full nvidia-container-runtime parity.
To ignore everything under a specific directory, use dir/**. For example,
"ignore": ["data/cache/**"] skips every file and subdirectory under
data/cache. A trailing slash also works, so "ignore": ["data/cache/"]
means the same directory tree.
When ignore_gitignore is true, patterns from the project root .gitignore
are added to every mount. Negated .gitignore patterns (!path) are ignored
because rmtx ignore rules only exclude files.
Common commands
rmtx exec --host 192.168.1.42:33221 -- go run ./cmd/api
rmtx exec --tty -- bash
rmtx ping
rmtx stats
rmtx context list
rmtx context delete --current
rmtx context prune --older-than 168h
rmtx context artifacts list --current
rmtx cache prune
Notes
- Contexts keep manifests and shared blobs on the host; workspaces are scratch
copies cleaned after each run.
- OCI prepared rootfs data is a per-context runtime cache. Runtime config changes
replace older prepared rootfs refs; context delete removes the cache.
- Discovery uses UDP broadcast on port
33222; hosts also send outbound announcements so clients can discover Windows hosts even when inbound UDP is blocked.
- If direct TCP to the host is blocked,
rmtx can fall back to a reverse LAN connection where the host dials back to the client.
- Client state is stored in
~/.rmtx/state.json.
- Interactive TTY mode is supported on Linux hosts/clients.
Test
go test ./...