{"slug": "corv-an-ssh-client-for-ai-agents-and-humans", "title": "Corv: An SSH client for AI agents (and humans)", "summary": "Corv, a new SSH client designed for AI agents, launched to enable secure, structured, and reusable remote connections. The tool allows agents to execute commands without exposing credentials, receive JSON output, and manage long-running tasks, while also supporting human use via an interactive terminal UI.", "body_md": "**The SSH client for AI agents and humans.** Connect by name. Reuse authenticated SSH connections. Keep secrets local.\n\nAI agents don't use SSH the way humans do. Plain SSH exposes credentials to the caller, returns terminal text that agents must parse, re-authenticates every command, and relies on tmux, nohup, or custom scripts for long-running tasks.\n\nCorv is built for agent-driven infrastructure. It lets agents connect by name, execute commands without exposing passwords or private keys, receive structured JSON output, reuse a warm authenticated connection, and detach and resume long-running jobs. Humans use the exact same connections through an interactive terminal UI.\n\n**Linux / macOS**\n\n```\ncurl -fsSL https://raw.githubusercontent.com/khalid-src/corv-client/main/install.sh | sh\n```\n\n**Windows** (PowerShell)\n\n```\nirm https://raw.githubusercontent.com/khalid-src/corv-client/main/install.ps1 | iex\n```\n\n**With Go**\n\n```\ngo install github.com/khalid-src/corv-client/cmd/corv@latest\n```\n\n**Update or remove**\n\n```\ncorv update      # download and install the latest release (checksum-verified)\ncorv uninstall   # remove corv; add --purge to also delete saved connections\n```\n\n`corv update`\n\nonly runs when you run it - Corv never updates itself in the\nbackground.\n\nManage connections interactively:\n\n```\ncorv\n```\n\nThe terminal interface supports keyboard and mouse navigation to add, edit,\nimport from `~/.ssh/config`\n\n, and connect. Connections can also be managed from\nthe command line:\n\n```\ncorv add srv-01 ubuntu@10.0.0.4                      # prompts for a password if needed\ncorv add srv-01 ubuntu@10.0.0.4 --key ~/.ssh/id_ed25519\ncorv add db-01 ubuntu@10.0.0.9 --jump ubuntu@bastion # reach through a bastion\ncorv import                                          # import hosts from ~/.ssh/config\ncorv list\ncorv rm srv-01\n```\n\nHosts behind one or more bastions are reached with `--jump`\n\n(OpenSSH `-J`\n\nsyntax: `user@host1,user@host2`\n\n). Jump hosts authenticate with `ssh-agent`\n\nor your keys; the target uses the connection's own credentials.\n\n`corv add`\n\nreads any password without echo and stores it in the local\nencrypted vault; it is never passed as an argument.\n\nConnect interactively:\n\n```\ncorv srv-01\n```\n\nRun a command non-interactively (the agent path):\n\n```\ncorv srv-01 -- systemctl restart api\ncorv srv-01 --json -- df -h              # structured output for tools\ncorv srv-01 -- ./deploy.sh\necho 'cd /app && run \"$X\" | grep foo' | corv srv-01 --json --stdin\ncorv srv-01 --json --stdin < script.sh\n```\n\nA typical agent instruction such as \"restart the API service on srv-01\"\nrequires only `corv srv-01 -- systemctl restart api`\n\n.\n\nCommand handling after `--`\n\nis designed to do what you mean:\n\n- a\n**single** argument is treated as a remote shell command line and run as-is, like`ssh host \"cmd\"`\n\n- e.g.`corv srv -- \"cd /app && make\"`\n\n; **multiple** arguments are passed as a preserved argument vector, each shell-quoted so the remote cannot re-split them - e.g.`corv srv -- sh -lc \"cd /app && make\"`\n\n.\n\nThis avoids the raw-`ssh`\n\npitfall where quoted arguments lose their\nboundaries, which matters for agents that build argument vectors.\n\nFor complex shell text, nested quotes, or multi-line scripts, use an stdin\nmode. `--stdin-base64`\n\nis the cross-shell-safe option because only ASCII\nbase64 passes through the local pipe.\n\nPowerShell can mangle quoting and pipe encoding, so encode a non-trivial remote\ncommand as UTF-8 base64 and send it with `--stdin-base64`\n\n:\n\n``` php\n$b64=[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(@'\nset -eu\ncd /srv/app && ./deploy.sh\n'@)); $b64 | corv srv-01 --json --stdin-base64\n```\n\nOnly ASCII base64 crosses the pipe, so the command arrives unchanged - quoting\nand any non-ASCII text survive intact. A simple exact argument vector can still\nuse `-- <command>`\n\n.\n\nLong commands are detached on the server automatically. If a command is still\nrunning after the broker's wait window, Corv returns the new bounded output,\nthe run id, and exit code `75`\n\n. Re-run the exact same command to watch the next\ndelta; it attaches to the existing remote job and does not start a second copy.\nWhen the job finishes, Corv saves the log locally (up to 20 MiB; a larger log is\ntruncated, with a marker and a `truncated`\n\nflag in `corv output --json`\n\n) and\nremoves the remote temporary files:\n\n```\ncorv srv-01 -- ./long-install.sh\ncorv srv-01 -- ./long-install.sh       # same command: continue watching\ncorv output <run-id>                   # finalize if done, then show the saved log\n```\n\n`CORV_WAIT`\n\ncontrols how long the broker waits before returning a `running`\n\nresponse. It accepts bare seconds (`CORV_WAIT=30`\n\n) or a Go duration\n(`CORV_WAIT=500ms`\n\n, `CORV_WAIT=2m`\n\n) and is read from the environment of each\n`corv`\n\ninvocation, so it takes effect immediately without restarting the broker.\n`corv output <run-id>`\n\nchecks an unfinished detached run and finalizes it\nautomatically once the remote exit status exists. Remote temp files are cleaned\non finalization; abandoned jobs are cleaned by the broker's startup sweep after\n24 hours.\n\nRemote command execution requires a POSIX shell and standard Unix command-line tools. Windows OpenSSH servers are not supported as remote execution targets.\n\nCorv is built for AI coding agents (Claude, Codex, and others). Ready-to-use\nagent instructions live in [ integrations/](/khalid-src/corv-client/blob/main/integrations):\n\n**Claude / Claude Code**- copy`integrations/claude/corv-ssh`\n\ninto`~/.claude/skills/`\n\n(or a project's`.claude/skills/`\n\n).**Codex**- copy`integrations/codex/AGENTS.md`\n\ninto your project.\n\nThe agent then runs commands by name and reads structured output:\n\n```\ncorv <name> --json -- <command>\n```\n\nFor non-trivial scripts or heavily quoted commands, agents should send the remote command as UTF-8 base64:\n\n``` php\n$b64=[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(@'\nset -eu\ncd /srv/app && ./deploy.sh\n'@)); $b64 | corv <name> --json --stdin-base64\n```\n\nAgents should not run `corv list --full`\n\nor `corv doctor --full`\n\nunless the\nuser explicitly asks; those modes show local connection details. Never put\npasswords, private keys, API tokens, kubeadm tokens, bearer tokens, or other\nsecrets on the command line. Corv command history records command lines.\n\nSee [ integrations/README.md](/khalid-src/corv-client/blob/main/integrations/README.md) for details.\n\nA local broker process holds one authenticated SSH connection per machine.\nEach `corv <name> -- <cmd>`\n\nruns as a new channel over the held connection, so\nthe connection and authentication cost is paid once rather than on every\ncommand. This behaviour is identical on Linux, macOS, and Windows.\n\nThe broker starts automatically on first use, exits after 15 minutes idle, and installs nothing on the server. It manages SSH connections only; it does not allocate a local pseudo-terminal, parse shell output, or maintain remote shell state. A held connection can be dropped explicitly:\n\n```\ncorv disconnect srv-01\n```\n\nScope and limitations:\n\n- This is connection reuse, not remote session persistence. Each command runs\nin its own shell with its own exit code; shell state such as the working\ndirectory does not carry between commands (combine them in a single command\nwhen required, e.g.\n`cd /app && make`\n\n). - Because the broker is local, a held connection does not survive the local machine sleeping or a network interruption. Persistence across those events requires remote support, which Corv does not depend on by design.\n\nInteractive sessions open a dedicated connection and proxy the remote pseudo-terminal to the local terminal, so no local pseudo-terminal is required on any platform.\n\nCorv normalises command output for programmatic consumers:\n\n- progress bars and other carriage-return redraws are collapsed to their final state rather than reproduced as thousands of lines;\n- ANSI control sequences, including colour, are removed;\n- a command's stdout and stderr are captured together, interleaved in the order\nthey were written, and returned in\n`stdout`\n\n; with`--json`\n\n, the`stderr`\n\nfield carries Corv-level errors (e.g. transport failures), not the remote command's own stderr; - a finished command returns its output in full up to a generous byte budget;\nonly genuinely large output is trimmed to a leading and trailing section with\na\n`... N line(s) hidden ...`\n\nmarker (the saved log, up to 20 MiB, stays available via`corv output <run-id>`\n\n), and a still-running command returns a short peek; - transport failures are classified (\n`auth_failed`\n\n,`unknown_host`\n\n,`unreachable`\n\n,`host_key`\n\n,`timeout`\n\n,`disconnected`\n\n), and the remote exit code is propagated as the process exit code.\n\nCorv runs entirely on the client and implements SSH through the maintained Go\nSSH library (`golang.org/x/crypto/ssh`\n\n).\n\n- Nothing is installed on the destination server.\n- Connection profiles are encrypted at rest with the OS-protected vault key,\nso the connection file does not expose hosts, users, or paths in plaintext\n(\n`corv list`\n\nis the way to view them). - Passwords and key passphrases are stored in a local encrypted vault. The\nvault key is protected by DPAPI on Windows; on macOS and Linux Corv reads a\nprovisioned Keychain / Secret Service key when present (it never auto-creates\none) and otherwise uses a\n`0600`\n\nkey file, the default. Stored secrets are read only by the local broker and sent over the authenticated SSH connection; they do not appear on a command line, in logs, or in command output. Key-based authentication or`ssh-agent`\n\nis recommended. - Audit logs and saved run logs are local and can contain the command text or remote output an operator asked Corv to run. Never put passwords, private keys, API tokens, kubeadm tokens, bearer tokens, or other secrets on the command line; command history records command lines.\n- Authentication is attempted in order: configured identity file,\n`ssh-agent`\n\n(via`SSH_AUTH_SOCK`\n\non Unix, the`openssh-ssh-agent`\n\nnamed pipe on Windows), default`~/.ssh`\n\nkeys, then password. - Bastion (\n`ProxyJump`\n\n) hops are host-key verified individually and can authenticate with`ssh-agent`\n\n, default keys, a per-hop identity file, or a password/passphrase sourced from a saved profile. - Host keys are verified against\n`~/.ssh/known_hosts`\n\n. An unknown host is rejected unless approved interactively; the broker never accepts unknown hosts automatically. A changed host key is always rejected. - The broker uses OS-permissioned local IPC (Unix socket or Windows named pipe) plus an endpoint token for defense in depth.\n\nCorv is composed of small, independent modules so that the SSH backend or the output processor can be replaced without affecting the rest:\n\n| Package | Responsibility |\n|---|---|\n`internal/profile` |\nconnection definitions, storage, `~/.ssh/config` import |\n`internal/vault` |\nlocal encrypted secret store (DPAPI / key file) |\n`internal/sshconn` |\nSSH transport (x/crypto/ssh): dial, auth, exec, shell |\n`internal/broker` |\nresident process holding one connection per host |\n`internal/output` |\noutput processor (collapse, strip, bound) |\n`internal/audit` |\nlocal command history |\n`internal/tui` |\ninteractive terminal UI (Bubble Tea) |\n`internal/cli` |\ncommand-line routing |\n`internal/paths` |\non-disk file locations |\n\n```\nmake vet         # go vet ./...\nmake build       # build ./bin/corv\nmake build-all   # cross-compile linux/macOS/windows\n```\n\nCorv targets Go 1.25+ on Linux, macOS, and Windows.\n\nSee [LICENSE](/khalid-src/corv-client/blob/main/LICENSE).\n\nBuilt with the assistance of AI coding tools: Claude Opus 4.8 and GPT-5.5.", "url": "https://wpnews.pro/news/corv-an-ssh-client-for-ai-agents-and-humans", "canonical_source": "https://github.com/khalid-src/corv-client", "published_at": "2026-06-27 14:37:59+00:00", "updated_at": "2026-06-27 15:05:17.298823+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-infrastructure"], "entities": ["Corv", "OpenSSH"], "alternates": {"html": "https://wpnews.pro/news/corv-an-ssh-client-for-ai-agents-and-humans", "markdown": "https://wpnews.pro/news/corv-an-ssh-client-for-ai-agents-and-humans.md", "text": "https://wpnews.pro/news/corv-an-ssh-client-for-ai-agents-and-humans.txt", "jsonld": "https://wpnews.pro/news/corv-an-ssh-client-for-ai-agents-and-humans.jsonld"}}