Remote machine access
Manage and access remote machines via SSH or Tailscale. Use when the user references a remote machine, node, server, or host — e.g., "run this on the remote box", "check GPU usage on the node", "deploy to the cluster", "access the remote node", "SSH into the machine". Triggers on any request involving remote execution, remote commands, remote file operations, or cluster/node management.From its SKILL.md
npx -y skills add newtonapple/remote-machine-accessAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.7 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it
Remote Machine Access
Manage remote machines defined in .remote-nodes/<host>.yaml: discover them, connect via
SSH or Tailscale, run commands, transfer files, and add new nodes.
Rules
- MUST NOT store passwords, private keys, or secrets in configs. Authentication comes
only from SSH keys, ssh-agent,
~/.ssh/config, or Tailscale SSH. - MUST run non-interactive commands with
ssh -T(no PTY) — otherwise SIGHUP can kill backgrounded processes when~/.ssh/configsetsRequestTTY=yes. - MUST clean up any temporary tunnel you create (see Tunnels).
- MUST name each config file
<host>.yaml, matching itshost:field. - Node configs MUST hold only connection info. MUST NOT add component-specific metadata (model lists, Docker images, recipes, GPU specs, project paths) — a parent config merges into every sub-component's context, and such data duplicates what the component owns and goes stale.
Building the SSH Target
Every ssh/scp/rsync/tailscale ssh command uses the same target:
- If
user:is set in the config, useuser@hostname. - If
user:is omitted, use barehostnameand let~/.ssh/configsupply the user (verify withssh -G <host>).
Examples below use a bare host (gpu-box); substitute user@gpu-box when a user is set.
hostname resolution order (try until one connects):
- DNS / mDNS (e.g.
gpu-box.local) ~/.ssh/configHost alias- Tailscale MagicDNS (if Tailscale is up)
tailscale_ipfallbacklan_ipfallback
Fall back to tailscale ssh when direct ssh fails or the node is only reachable on the
tailnet.
Config Format
A config is a flat YAML file at .remote-nodes/<host>.yaml defining one machine.
| Field | Required | Description |
|---|---|---|
host | Yes | Identifier; must match the filename |
hostname | Yes | Resolvable SSH target — DNS name, IP, or Tailscale MagicDNS |
user | No | SSH username (omit if set in ~/.ssh/config) |
port | No | SSH port (default 22) |
identity_file | No | SSH key path (else ssh-agent / default keys) |
description | No | Human-readable note |
lan_ip | No | LAN IP for direct access / fallback |
tailscale_ip | No | Tailscale IP fallback |
Default to the minimal form; add fields only when ~/.ssh/config doesn't supply them:
# minimal
host: gpu-box
hostname: gpu-box
# with explicit fields
host: gpu-box
user: alice
hostname: gpu-box
port: 22
identity_file: ~/.ssh/id_ed25519
description: GPU workstation
lan_ip: 192.168.1.50
tailscale_ip: 100.64.0.1
Parser note: keep configs flat (no nested mappings) and put comments on their own lines, not inline after a value — some tooling reads these with a simple line parser, not a full YAML library.
Discovery & Resolution
To resolve a host the user names ("run this on gpu-box"):
- Look in
./.remote-nodes/first, then the parent dir's.remote-nodes/(one level up only). Merge both; the closer dir wins for a duplicatehost. - Read the matching
<host>.yaml, build the target, trysshthentailscale ssh.
Sharing one config across deeply-nested components: because discovery scans only one level up, a component more than one level below a shared config can't see it. Symlink rather than copy, to keep a single source of truth:
# from the component's .remote-nodes/ dir; adjust ../ depth to reach the shared config
ln -s ../../../.remote-nodes/<host>.yaml <host>.yaml
Adding a Node
- Pick the directory: the one the user names; else the current dir's
.remote-nodes/; else a named subproject's.remote-nodes/. Create it if missing. - Write
<dir>/.remote-nodes/<host>.yaml(filename =host) using the minimal form. - Validate, then verify reachability:
python3 -c "import yaml; c=yaml.safe_load(open('.remote-nodes/gpu-box.yaml')); \
assert c.get('host') and c.get('hostname'), 'missing host/hostname'; print('config OK')"
ssh -T gpu-box "hostname" # or: tailscale ssh gpu-box "hostname"
Running Commands
ssh -T gpu-box "nvidia-smi" # non-interactive (always -T)
ssh -T gpu-box -i ~/.ssh/id_ed25519 "docker ps"
ssh gpu-box # interactive session
tailscale ssh gpu-box "uptime" # via Tailscale
# multi-line
ssh -T gpu-box <<'EOF'
cd /path/to/project && git pull
EOF
Tunnels
Use -N -f for background tunnels. Always kill temporary tunnels when done.
ssh -L 8000:localhost:8000 gpu-box -N -f # local: localhost:8000 → remote:8000
ssh -R 3000:localhost:3000 gpu-box -N -f # remote: remote:3000 → local:3000
ssh -D 1080 gpu-box -N -f # dynamic SOCKS proxy
pkill -f 'ssh -[LRD]' # kill all tunnels (or kill <pid>)
lsof -i :8000 # verify gone
File Transfers
scp ./file gpu-box:/remote/path/ # push (scp gpu-box:/path ./ to pull)
scp -r ./dir gpu-box:/remote/dir/ # recursive
rsync -avz ./dir/ gpu-box:/remote/dir/ # sync (add --dry-run to preview)
rsync -avz --delete ./dir/ gpu-box:/remote/dir/ # mirror (deletes extra dest files)
rsync -avz --exclude='.git' ./ gpu-box:/remote/proj/
rsync -avz gpu-box:/remote/dir/ ./local/ # pull
What ships with it: 6 files
11.2 KB alongside SKILL.md, 1 of them executable
scripts/
- check-node.pyruns6.0 KB
- CHANGELOG.md1.2 KB
- .gitignore28 B
- LICENSE1.0 KB
- README.md3.0 KB
- VERSION6 B