sshrew
by duriantaco
README.md
# sshrew
<p align="center">
<img src="sshrew/assets/sshrew.png" alt="sshrew" width="220">
</p>
Let Claude reach any machine you'd normally SSH into. Claude can now read
files, tail logs, run allowlisted commands **without inbound SSH, port
forwarding, or config files.** Works on home servers, VPSes, Raspberry Pis,
Jetsons, whatever. If it runs Linux and can dial out, sshrew can bridge it.
## Quick start
On the machine you want Claude to see:
```bash
curl -fsSL https://raw.githubusercontent.com/duriantaco/sshrew/main/install.sh | bash
```
It prints:
```
==============================================================
Your bridge is live. In Claude, go to:
Settings -> Connectors -> Add custom connector
and paste this URL:
https://random-words.trycloudflare.com/kJx.../mcp
==============================================================
```
Paste that URL into claude.ai, enable the connector in a chat, done. By
default Claude can only read `~/.sshrew-share`; pass `--root DIR` when you
want to expose a specific project or log directory.
That one command handles everything: installs `uv` (which brings its own
Python 3.12, so old distros work), fetches sshrew, generates and saves a
secret token, downloads the right `cloudflared` binary for your CPU, opens
the tunnel, and starts the server.
## How it works
SSH is you reaching *in*. sshrew is the box reaching *out*:
```
claude.ai --(custom MCP connector)--> https://xxx.trycloudflare.com/<token>/mcp
|
cloudflared (outbound only)
|
sshrew on 127.0.0.1:8787
```
You need access to the machine exactly once — to run the installer. After
that (especially as a systemd service) you never reach in again.
The secret token lives in the URL path, so possession of the URL is the
credential. It's stored in `~/.config/sshrew/token`, reused across restarts,
and rotated with `--rotate-token`.
Every tool call is also recorded locally in `~/.config/sshrew/audit.jsonl`.
The log stores metadata like tool name, path, argv, exit code, bytes returned,
duration, and a token fingerprint. It does not store file contents, command
output, or the secret token itself. Ask Claude to call `recent_activity()` to
review what happened in the current session.
## Multiple machines
Run sshrew on each box. Each gets its own token and its own connector entry
("home-server", "lab-pi", "gpu-box"). Enable several in one chat and ask
Claude to compare across them.
## Options
Default is **read-only**, scoped to `~/.sshrew-share`.
```bash
sshrew --profile readonly # default: shared folder only
sshrew --profile dev # shared folder plus ~/projects and ~/src if present
sshrew --profile server # logs plus safe read-only diagnostics
sshrew --profile edge # server profile plus edge-device diagnostics
sshrew --profile full-trust # home directory plus unrestricted shell; be careful
sshrew --root ~/projects --root /var/log # narrow what Claude can read
sshrew --allow-shell --allow journalctl --allow systemctl
sshrew --allow-shell # full shell access; use only on trusted links
sshrew --no-tunnel # bring your own tunnel
sshrew --rotate-token # invalidate the old URL
```
Profiles are conservative. `dev` does not enable shell. `server`, `edge`,
`workstation`, and `ops` enable only a small read-oriented command allowlist.
`full-trust` is the only profile that grants unrestricted shell access.
Flags pass through the installer too:
```bash
curl -fsSL .../install.sh | bash -s -- --profile server
```
## Installing permanently
```bash
uv tool install --python 3.12 git+https://github.com/duriantaco/sshrew
sshrew
```
Run at boot with systemd. This installs the service for the current Linux user:
```bash
sed "s/^User=me/User=$USER/" sshrew.service | sudo tee /etc/systemd/system/sshrew.service >/dev/null
sudo systemctl daemon-reload && sudo systemctl enable --now sshrew
```
That user controls what sshrew can read and run. Keep it non-root.
Heads-up: **quick-tunnel URLs change every restart**, so a reboot means
re-pasting the connector URL. For a stable URL, point a named Cloudflare
tunnel or Tailscale Funnel at `127.0.0.1:8787` and run `sshrew --no-tunnel`.
The sample service includes systemd sandboxing and only allows writes to
`~/.config/sshrew` and `~/.sshrew-share`.
## Do I need nginx?
Not for the default setup. sshrew listens on `127.0.0.1`, and `cloudflared`
is the only public-facing network layer. nginx does not fix token leakage,
path scoping, or command execution risk.
Use nginx, Caddy, or a named Cloudflare Tunnel with Cloudflare Access if you
are directly exposing a stable domain and need TLS termination, request
limits, access logs, mTLS, SSO, or extra rate limiting.
## Roadmap
sshrew should be more than a tunnel. The goal is an agent-aware host capsule
for anything you can run: laptops, workstations, home servers, VPSes, lab
machines, Raspberry Pis, Jetsons, NAS boxes, CI runners, and edge devices.
Every machine gets explicit, inspectable rules for what an AI agent can see
or do.
- Capability tokens: mint separate URLs for separate roots, tools, and TTLs.
- Local approval queue: require terminal approval for risky operations like
service restarts, package installs, or writes.
- Audit controls: retention, export, tamper-evident hashes, and per-token
activity views.
- Profiles: per-profile command arguments, root templates, and generated
systemd services.
- Host facts: richer GPU, network, process, and service inventory.
- Device packs: optional tools for specific environments like NVIDIA Jetson,
Raspberry Pi, Docker hosts, systemd servers, macOS laptops, Linux laptops,
NAS boxes, and CI runners.
- Safer ops tools: expose `tail_logs`, `restart_service`, `docker_status`, and
`journal_errors` as structured tools instead of relying on raw shell.
- Stable URL wizard: generate named Cloudflare Tunnel, Tailscale Funnel, or
local-only configs from one command.
- Cloudflare Access mode: support service-token headers and JWT validation for
stable public deployments.
- Live dashboard: show current URL, roots, enabled tools, recent calls, and a
one-click token revoke.
- Event channels: push alerts when logs spike, thermals go high, disk fills, or
a watched service fails.
## Security notes
- The URL is the password. Long random token, never post it, rotate if in doubt.
- `--root` is a hard jail. Paths are fully resolved, including symlinks, and refused if they escape.
- Shell is off unless you pass `--allow-shell`. With `--allow CMD`, sshrew executes the parsed command directly instead of through a shell, so shell operators like `&&` and `|` are not executed. Without `--allow`, `--allow-shell` grants full shell access.
- sshrew runs with the permissions of its user. DO NOT run it as root.
- Reads are size-capped, negative offsets and sizes are refused, and binary files are refused.
- Search skips hidden directories and symlinks that point outside the allowed roots.
## Tools exposed
| tool | what it does |
|---|---|
| `list_dir(path)` | directory listing inside allowed roots |
| `read_file(path, offset, max_bytes)` | chunked text reads |
| `file_info(path)` | size / mtime / permissions |
| `search_files(root, name_glob, contains)` | recursive find + grep-lite |
| `run_command(command)` | shell, gated by `--allow-shell` / `--allow` |
| `host_facts()` | structured OS / CPU / memory / disk / service / device facts |
| `system_status()` | uptime, memory, disk (+ tegrastats if it's a Jetson) |
| `recent_activity(limit)` | recent audit log entries for accountability |
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing