Skip to content
Back to skills

Host Ssh

ASecurity

Run PowerShell on Windows hosts or a POSIX shell on macOS/Linux hosts over SSH, via the machine registry in the guide Environment (GA_SSH_MACHINES). Use when the user needs host OS tasks — drives, dotnet, Playwright, processes, SSH into their laptop or desktop — not file I/O on paths already visible in the workspace file list. Per-machine "os" field or auto-detection (uname -s) picks the command style; hosts resolve IPv4-first; paramiko fallback used when sshpass is unavailable.

  • 61 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 29, 2026
ai-agentspythonrustgoshellbashdockerapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 29, 2026

npx -y skills add Elumenotion/GuideAnts --skill host-ssh --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Host Ssh?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Host Ssh
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/elumenotion-host-ssh/badge)](https://www.skillsdirectory.com/skills/elumenotion-host-ssh)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: host-ssh
description: >-
  Run PowerShell on Windows hosts or a POSIX shell on macOS/Linux hosts over
  SSH, via the machine registry in the guide Environment (GA_SSH_MACHINES).
  Use when the user needs host OS tasks — drives, dotnet, Playwright,
  processes, SSH into their laptop or desktop — not file I/O on paths
  already visible in the workspace file list. Per-machine "os" field or
  auto-detection (uname -s) picks the command style; hosts resolve
  IPv4-first; paramiko fallback used when sshpass is unavailable.
---

# Host SSH (Windows PowerShell + macOS/Linux POSIX shell)

## Sandbox paths — authoritative, do not discover

Your **system context includes a file list** (`[@files]` / context options). That list,
plus the rules below, fully defines what exists and where. **Do not run `pwd`, `find`,
`ls /`, `cd /home`, or any other command to discover paths.** Exploration wastes turns
and breaks working-directory assumptions.

**SANDBOX CWD is already the notebook output directory** (private notebooks: the
`Output/` folder). Every `run_bash` / `run_python` call **starts there**.

| Rule | Do | Never |
|------|-----|--------|
| Skill scripts | `python3 Skills/host-ssh/scripts/host_ssh.py …` | `Output/Skills/…`, `/app/…`, `/home/…` |
| Deliverables | bare filenames in CWD | prefix `Output/` in commands |
| Change directory | stay in CWD | `cd /home`, `cd Output`, `cd` before skill commands |
| Inputs in the file list | use the **exact path shown** (`../repo/…`, `Skills/…`, bare names) | guess, probe, or re-derive |
| Host repo edits | edit via **mounted paths in the file list** (bash heredoc) | `run_python` one-liners that embed `\` or `\\` |

If `Skills/host-ssh/scripts/host_ssh.py` appears in the file list, run it **as written**
from CWD. If a path is in the list, the file exists — **trust the list**.

Host setup (OpenSSH, guide Environment variables) is in the operator README bundled with
this skill or `samples/skills/host-ssh skills/README.md` on the repo. Missing
`GA_HOST_SSH_*` env vars → stop and tell the user to set them in the guide editor;
do not scan or guess credentials.

## Machines — the guide Environment is the source of truth

`GA_SSH_MACHINES` (JSON array in the guide Environment) declares which machines this
guide can reach. **Run `host_ssh.py machines` to list them** — never guess a host or IP.
When no `--machine` is given, the record with `"default": true` is the target. When
`GA_SSH_MACHINES` is absent, the legacy single machine from `GA_HOST_SSH_HOST` is the
default (named `host`).

```json
[
  {"name": "office", "host": "host.docker.internal", "default": true, "os": "windows",
   "share": {"unc": "\\\\FILESERVER\\content", "user": "DOMAIN\\GuideAnts", "drive": "R"}},
  {"name": "laptop", "host": "192.168.1.60", "os": "mac"},
  {"name": "gpu", "host": "192.168.1.50", "os": "linux"}
]
```

A `share` block (optional, per machine) makes `host_ssh.py` re-map the drive on every
run, because **mapped drive letters in the desktop session do not carry into SSH
sessions** (Windows isolates sessions). Use the mapped drive (default **`R:`**) in
commands; the skill re-maps it from `share.unc` each time. `share.password`, when
present, is a secret; it falls back to `GA_HOST_SSH_SHARE_PASSWORD`, then
`GA_HOST_SSH_PASSWORD`.

## Windows vs macOS/Linux targets (per-machine `os`)

Each machine record accepts an optional `"os"` field: `"windows"`, `"mac"`, or
`"linux"` (aliases: `win`, `macos`, `darwin`, `posix`, `unix`).

| Target `os` | Remote command style |
|---|---|
| `windows` | `powershell.exe -NoProfile -NonInteractive -EncodedCommand <b64>` (UTF-16LE base64, + share bootstrap) |
| `mac` / `linux` | plain POSIX shell text via the login shell (zsh on macOS) — no encoding, no share bootstrap (SMB drive mapping is Windows-only) |

When `"os"` is absent it is **auto-detected once** (`uname -s`; empty output means
Windows) and cached in `/tmp/ga_host_ssh_os_cache.json` for 10 minutes — delete that
file to force re-detection. Hosts resolve **IPv4-first** (AF_INET lookup / `ssh -4`)
because `host.docker.internal` can resolve to an unroutable IPv6 address from the
sandbox.

macOS targets need **Remote Login** enabled (System Settings → General → Sharing →
Remote Login), plus **"Allow full disk access for remote users"** so SSH can read
Desktop/Documents/Downloads; otherwise macOS TCC blocks those reads (`probe` reports
`documents: DENIED`). Apple's guide: [Turn on Remote Login]
(https://support.apple.com/guide/mac-help/turn-on-remote-login-mchlp1066/mac)

When sshpass cannot be installed (no root in the sandbox), use the paramiko
variant — identical CLI, pure-Python SSH from `pylibs/`:

```bash
PYTHONPATH=pylibs:Skills/host-ssh/scripts python3 Skills/host-ssh/scripts/host_ssh_pylibs.py probe --all
```

## Capability profile (verified 2026-09-01) — read before choosing commands

The SSH account (`GA_HOST_SSH_USER`) is a **standard (non-admin) user**. On both
current machines the following are **denied for it — by design, not a
regression. Do not use these, and do not burn round-trips discovering this**:

| Probe | Result |
|-------|--------|
| `Get-CimInstance Win32_*` (WMI — e.g. `LastBootUpTime` boot time) | **Access denied** |
| `Get-Volume` | **Access denied** |
| `HKLM` registry read | **Access denied** |
| `systeminfo` | **Access denied** (errors to stderr) |
| Filesystem / `Get-PSDrive -PSProvider FileSystem` | ok |
| `dotnet`, `docker` | ok on both |
| `nvidia-smi` | ok on OfficeDesktop, **absent on Max** |

(Windows targets. macOS/Linux have no WMI; `probe` reports their fs/tool state
directly.)

Consequences:

- **Boot time is NOT readable** through this account. The canonical
  post-restart verification is the **`probe`** command below — reachability +
  identity + clock — not `LastBootUpTime` / `systeminfo`.
- Prefer `Get-PSDrive -PSProvider FileSystem` over `Get-Volume` for drives.
- If a command fails, check this table first; re-run `probe` to refresh the
  profile before concluding anything changed.

One-command verification (read-only, non-admin-safe) — proves SSH auth,
machine, account, and clock, and reports tool availability:

```bash
python3 Skills/host-ssh/scripts/host_ssh.py probe --all
```

`probe` never fails on the denied capabilities above. If a line shows
`remote shell OK (command error: …)` the SSH transport itself worked — the
problem is in the PowerShell text (e.g. a bash idiom like `hostname` instead
of `$env:COMPUTERNAME`).

## When to use SSH vs the workspace

| Need | Use |
|------|-----|
| Read/write files **already in the file list** (e.g. `../repo/src/…`) | **Bash in sandbox** — heredoc writes, `sed`, etc. Same bytes as the host when path is a host mount. |
| `dotnet`, `npm test`, Playwright, host processes, drives, services | **This skill** — `host_ssh.py` over SSH |
| Host HTTP | `http://host.docker.internal:…` directly |
| Host files **not** in the file list | Host folder mount (GuideAnts UI) or ask the user |

**Default:** file edits on a mounted repo = sandbox heredoc. SSH = run commands on the
machine OS, not to patch source files.

## Run a command on a machine (PowerShell or POSIX shell)

Windows targets get PowerShell via **`-EncodedCommand`** (pipes and quotes survive
Windows OpenSSH). **Always use `run -` + bash heredoc** for anything non-trivial — never
inline Python, never nested quote gymnastics, never hand-rolled `ssh`/`sshpass`. On
mac/linux targets the script text is sent verbatim to the login shell (zsh on macOS) —
same heredoc discipline, POSIX syntax.

```bash
python3 Skills/host-ssh/scripts/host_ssh.py run - <<'PS'
Set-Location C:\repos\repo\src\server
dotnet build RepoApi\RepoApi.csproj -v q --nologo
PS
```

On mac/linux targets, write POSIX shell instead (same heredoc pattern):

```bash
python3 Skills/host-ssh/scripts/host_ssh.py run --machine laptop - <<'SH'
ls -lah ~/Documents
SH
```

Target a specific machine (names from `machines`):

```bash
python3 Skills/host-ssh/scripts/host_ssh.py run --machine gpu - <<'PS'
Get-PSDrive -PSProvider FileSystem |
  Select-Object Name, Root, Used, Free |
  Format-Table -AutoSize
PS
```

List drives (non-admin users often cannot use `Get-Volume`):

```bash
python3 Skills/host-ssh/scripts/host_ssh.py run - <<'PS'
Get-PSDrive -PSProvider FileSystem |
  Select-Object Name, Root, Used, Free |
  Format-Table -AutoSize
PS
```

Save remote stdout to a file in sandbox CWD:

```bash
python3 Skills/host-ssh/scripts/host_ssh.py run - -o drives.json <<'PS'
Get-PSDrive -PSProvider FileSystem | ConvertTo-Json -Depth 3
PS
```

Ignore `#< CLIXML` in stderr — harmless PowerShell remoting progress, not failure.

## Edit files on a mounted host repo (bash heredoc, not Python)

When the file list shows a mount (e.g. `../repo/src/…`), **patch with bash
heredoc** so backslashes and quotes are not mangled by the tool layer:

```bash
cat > ../repo/src/example.txt <<'EOF'
line one
line two
EOF
```

For a partial edit, use `sed` or write a small `.sh` / `.py` **via heredoc** and run it
once — do **not** pass Windows paths containing `\` through `run_python` string
arguments (the platform collapses `\\` → `\` and corrupts the file).

## Required Environment (guide → sandbox)

| Variable | Purpose |
|----------|---------|
| `GA_HOST_SSH_USER` | Local account on each machine (e.g. `GuideAnts`); on a Mac: the account short name |
| `GA_HOST_SSH_PASSWORD` | Password (**secret** in guide editor) |
| `GA_SSH_MACHINES` | JSON machine registry (see above) |

Legacy single machine (used when `GA_SSH_MACHINES` is absent): `GA_HOST_SSH_HOST`
(usually `host.docker.internal`) and optional `GA_HOST_SSH_PORT` (default `22`).

Legacy share variables (default machine only):

| Variable | Purpose |
|----------|---------|
| `GA_HOST_SSH_SHARE_UNC` | UNC behind `R:` (e.g. `\\FILESERVER\content`) |
| `GA_HOST_SSH_SHARE_USER` | Share account (e.g. `DOMAIN\GuideAnts`) |
| `GA_HOST_SSH_SHARE_PASSWORD` | Share password (**secret**); defaults to `GA_HOST_SSH_PASSWORD` |
| `GA_HOST_SSH_SHARE_DRIVE` | Drive letter (default **`R`**) |

Quick check (optional, one line — **not** a gate before work):

```bash
test -n "$GA_HOST_SSH_USER" && test -n "$GA_HOST_SSH_PASSWORD" && echo env ok || echo env missing
```

If SSH fails, report the error verbatim and point the operator at README troubleshooting.

## Rules

- **Trust the file list.** No path discovery commands.
- **Know the machines.** `host_ssh.py machines` — do not invent hosts or IPs.
- **Never `cd`** in the same script as `Skills/…` paths.
- **Heredoc first** for SSH commands and for file writes on mounts.
- **Never** print or log `GA_HOST_SSH_PASSWORD` (or any share password).
- **Probe, do not guess capabilities.** `host_ssh.py probe --all` reports what the
  account can actually do (the denied set is by design; see the capability table).
- **Declare or detect `os`.** `run`/`probe` pick PowerShell vs POSIX shell from the
  per-machine `os` field; auto-detection caches in `/tmp` — delete the cache file
  after a machine's OS changes.
- **Never** run `preflight.py` unless the user explicitly asks for diagnostics — it is
  not part of the normal workflow.

## Reporting

State which machine ran, what ran, whether it succeeded, and a short summary. If
blocked on missing env, unknown machine, or SSH auth, say so clearly and stop.

Files in this skill

  • SKILL.md11.2 KB
  • scripts/host_ssh.py7.4 KB
  • scripts/host_ssh_pylibs.py4.6 KB
  • scripts/ssh_common.py14.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.