Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

72 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🦾 AgentSSH

Crates.io Rust License Platform Downloads

📖 中文文档

AI-native SSH toolkit. Not a human terminal. Not an OpenSSH wrapper.

AgentSSH speaks SSH directly through russh — a pure-Rust async SSH implementation. One binary: client + daemon + proxy. No C library to install. No shell wrapping. All output in structured JSON. Built for agents, also works for humans.

🐙 GitHub · 📦 crates.io · 🔧 Quick Start · 🏗️ Architecture · 📋 Command Reference


🤖 AI Agent Skill

Drop SKILL.md into your agent's skill directory to give it SSH superpowers.

Your agent gains: exec, file upload/download, session management, port forwarding, SOCKS5 proxy — all via structured JSON. See SKILL.md for the full capability definition.


🆚 Why AgentSSH

ssh paramiko libssh2 AgentSSH
Output Raw terminal Mixed string App parses ✅ Structured JSON
Connections One shot Manual Manual ✅ Daemon-pooled + reuse
File transfer scp / sftp Separate impl Separate impl ✅ SFTP → exec built-in
Port forward -L / -D flags Manual coding Manual coding ✅ Daemon-managed
PTY model Screen-scraped Blocking reads Polling loops ✅ Async drain task
Auth config ~/.ssh/config Inline params Inline params ✅ JSON profiles
C dependency Yes Yes Yes None — pure Rust
Agent-first --json everywhere

The last two rows are the whole point. No C library to fight with. No screen scraping. Programs call AgentSSH like they'd call an API.


🚀 Quick start

# Install from crates.io (recommended)
cargo install agentssh

# Or build from source
cargo build --release
# → target/release/agentssh

🔐 Save a profile

agentssh profile add prod \
  --host example.com \
  --username root \
  --private-key ~/.ssh/id_ed25519

agentssh profile list
# tencent   root@82.157.147.224:22
# prod      root@example.com:22

⚡ Run & go (one-shot)

agentssh --output json exec --profile prod --retry 3 -- uptime
# {"ok":true,"status":"completed","exit_code":0,"stdout":"21:03:01 up 42 days\n","stderr":""}

Commands run through /bin/sh -c — pipes, redirects, and shell chains work natively:

# Pipe and filter
agentssh --output json exec --profile prod -- grep ERROR /var/log/syslog \| tail -5

# Redirect output to remote file
agentssh --output json exec --profile prod -- cat \> /etc/config \</dev/null

# Chain commands
agentssh --output json exec --profile prod -- ls /etc \&\& systemctl status nginx

Tip: Escape |, >, && with backslashes so your local shell doesn't eat them.

⏱️ Auto-suspend (long-running commands)

exec automatically suspends long-running commands after 30 seconds (configurable):

# Short command → returns immediately
agentssh exec -p prod -- uptime
# → {"status":"completed", "stdout":"...", "exit_code":0}

# Long command → auto-suspends after 30s, returns session ID
agentssh exec -p prod -- cargo build
# → {"status":"suspended", "session_id":"s7", "commands":{...}}

# Check suspended command
agentssh session status --session-id s7
agentssh session read --session-id s7 --follow

# Start server without blocking
agentssh exec -p prod -- python3 app.py
# → {"status":"suspended", "session_id":"s8", ...}
# Server continues running on remote host

# Never suspend (wait forever)
agentssh exec -p prod --suspend-timeout 0 -- short-command

🔄 Long-lived sessions

agentssh connect --profile prod --reconnect
# → session_id: s1 (auto-reconnect on disconnect)

# Clean command execution (no PTY echo — structured JSON)
agentssh session exec --session-id s1 -- uname -a
# → {"exit_code":0, "stdout":"Linux ...\n", "stderr":""}

# Interactive PTY mode
agentssh session send --session-id s1 --input $'ls -la\n'
agentssh session send --session-id s1 \
  --input $'sudo systemctl restart nginx\n' \
  --expect "[sudo] password" \
  --respond $'mypassword\n'

> **⚠️ `--input` needs a real newline.** `"echo hello\n"` sends two literal characters `\` and `n` — the shell won't execute the command. Use `$'echo hello\n'` (ANSI-C quoting) or embed an actual line break so the shell gets a real Enter.
>
> ```bash
> # ✅ ANSI-C quoting — sends a real Enter to the PTY
> agentssh session send --session-id s1 --input $'ls -la\n'
>
> # ❌ This sends literal \ and n — shell just echoes, never runs
> agentssh session send --session-id s1 --input "ls -la\n"
> ```

agentssh session read --session-id s1          # latest output
agentssh session read --session-id s1 --follow # stream live
agentssh session spawn --from s1               # new PTY on same SSH conn

agentssh session ping --session-id s1
agentssh session close --session-id s1

📁 File transfer (multi-protocol)

# Default auto: SFTP → exec (works on Linux, macOS, and Windows!)
agentssh file upload --profile prod --local ./app --remote /opt/app
agentssh file download --profile prod --remote /var/log/syslog --local ./syslog
agentssh file ls --profile prod --remote /var/www

# Force a specific protocol
agentssh file upload --profile prod --method sftp --local ./app --remote /opt/app

🌐 Port forwarding & SOCKS5

# Local port forward: localhost:9999 → remote internal :8080
agentssh proxy create --profile prod \
  --local 127.0.0.1:9999 \
  --remote 127.0.0.1:8080

# SOCKS5 dynamic proxy: route all traffic through remote host
agentssh proxy create --profile prod --socks5 127.0.0.1:1080
curl --socks5 127.0.0.1:1080 http://internal-service/

agentssh proxy list
agentssh proxy ping --proxy-id p1
agentssh proxy close --proxy-id p1
agentssh proxy close --all

🏗️ Architecture

┌──────────────┐     Unix Socket      ┌──────────────────┐
│  CLI client  │ ◄──────────────────► │  Daemon (serve)   │
│  (one-shot)  │    JSON-line IPC     │                   │
└──────────────┘                      │  ┌─────────────┐  │
                                      │  │ sessions    │  │
┌──────────────┐                      │  │ proxies     │  │
│  CLI client  │◄────────────────────►│  │ connections │  │
│  (session)   │                      │  └─────────────┘  │
└──────────────┘                      └───────┬──────────┘
                                              │
                                     SSH (russh)
                                              │
                                    ┌─────────▼─────────┐
                                    │  Remote servers    │
                                    │  Linux · macOS ·   │
                                    │  Windows           │
                                    └───────────────────┘

Daemon — auto-starts on first session/proxy command. Survives CLI invocations. Handles heartbeat, session health, connection pooling.

Connection pooling — sessions can share one TCP/SSH connection. session spawn --from <id> opens a fresh PTY on the same underlying connection.

Proxy tasks — each proxy create spawns an async listener task inside the daemon. Accepted connections get their own handler task with non-blocking bidirectional forwarding.


📋 Command reference

# ⚡ One-shot (auto-suspend after 30s if still running)
agentssh exec                          # Run command → stdout + exit code
agentssh exec --suspend-timeout 0      # Never suspend, wait forever
agentssh shell                         # Interactive PTY (human use)

# 🔄 Sessions
agentssh connect                       # Open PTY → session_id
agentssh session send                  # Send input (± expect/respond pairs)
agentssh session spawn --from s1       # New PTY on s1's SSH connection
agentssh session read                  # Read output from cursor
agentssh session resize                # Change PTY dimensions
agentssh session signal                # Send signal (INT, TERM, KILL…)
agentssh session status                # Get session metadata
agentssh session ping                  # Check if session is alive
agentssh session list                  # List sessions + connection groups
agentssh session close                 # Close session

# 📁 Files
agentssh file upload                   # Upload (SFTP → exec)
agentssh file download                 # Download (SFTP → exec)
agentssh file ls                       # List remote directory

# 🌐 Proxy & tunnels
agentssh proxy create                  # -L forward or -D SOCKS5
agentssh proxy list                    # List active proxies
agentssh proxy ping                    # Check proxy health
agentssh proxy close                   # Close one or --all

# 🔐 Profiles
agentssh profile list | read | add | write | delete

# ⚙️ Daemon
agentssh daemon serve                  # Start daemon (auto-started)
agentssh daemon shutdown               # Stop daemon + cleanup

🎛️ Configuration

Path Purpose
~/.config/agentssh/profiles.json SSH connection profiles
$XDG_RUNTIME_DIR/agentssh-{user}.sock Daemon Unix socket
/tmp/agentssh-daemon.log Daemon log (override: AGENTSSH_LOG)

Profile format

{
  "profiles": {
    "prod": {
      "host": "example.com",
      "port": 22,
      "username": "root",
      "private_key": "~/.ssh/id_ed25519",
      "retry": 3,
      "retry_delay_ms": 250
    }
  }
}

Supported fields: host, port, username, password (prefix $ for env var), private_key (path, $ENV, or inline), passphrase (prefix $ for env var), ready_timeout_ms, retry, retry_delay_ms.


🔧 Building

  • Rust ≥ 1.85 (edition 2024)
  • Unix only (uses Unix domain sockets)
  • No C library required — russh is pure Rust
cargo build --release
# → target/release/agentssh

🔒 Security

Host key verification (TOFU)

AgentSSH uses Trust-On-First-Use (TOFU) with ~/.ssh/known_hosts:

  • First connection: the server's host key is appended to known_hosts automatically.
  • Subsequent connections: the key is verified against the stored entry. A mismatch aborts with an error (host key changed).
  • Non-standard ports use the [host]:port bracketed format in known_hosts.

Exec fallback shell escaping

When the exec fallback method is used for file operations, commands are constructed with shell_words::quote() to safely escape arguments and prevent shell injection.


📄 License

MIT


⭐ Found this useful? Give it a star on GitHub.

About

Standalone Rust SSH/SFTP CLI for AI agents — persistent sessions, PTY streaming, SFTP/SCP fallback, port forwarding, SOCKS5 proxy, and structured JSON output. No daemon dependencies.

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages