mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: add pure ssh2 remote operations
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
# pi-ssh
|
||||
|
||||
`pi-ssh` keeps Pi and its local tools on the local machine while exposing explicit remote tools over a persistent Node `ssh2` connection:
|
||||
|
||||
- `ssh_read`
|
||||
- `ssh_write`
|
||||
- `ssh_edit`
|
||||
- `ssh_find`
|
||||
- `ssh_grep`
|
||||
- `ssh_bash`
|
||||
|
||||
The extension does not override Pi's local `read`, `write`, `edit`, `find`, `grep`, or `bash` tools.
|
||||
|
||||
## Architecture
|
||||
|
||||
Runtime connections are pure `ssh2`; the extension does not spawn OpenSSH and does not require `sshpass`, `ControlMaster`, or passwordless login. Remote file operations use SFTP and remote shell commands use an SSH exec channel.
|
||||
|
||||
Hosts must be explicitly imported before use. OpenSSH remains only an import source: the configuration helper runs `ssh -G <alias>` once to resolve the selected alias, then stores the resulting endpoint and authentication data in the pi-ssh vault. Later changes to `~/.ssh/config` require re-importing the host.
|
||||
|
||||
ProxyJump and ProxyCommand are intentionally rejected in the first ssh2 release.
|
||||
|
||||
## Configure hosts
|
||||
|
||||
From the installed bundle or this repository:
|
||||
|
||||
```sh
|
||||
./ssh_config.sh import
|
||||
./ssh_config.sh import packaging-server
|
||||
```
|
||||
|
||||
The helper:
|
||||
|
||||
1. lets you select concrete aliases from `~/.ssh/config`;
|
||||
2. uses `ssh -G` to resolve HostName, User, Port, and IdentityFile;
|
||||
3. asks whether the selected host uses a private key or password;
|
||||
4. asks for a private-key passphrase when needed;
|
||||
5. obtains and displays the server's SHA256 host-key fingerprint;
|
||||
6. connects with `ssh2` to verify authentication;
|
||||
7. optionally assigns a display label and group;
|
||||
8. writes the encrypted vault.
|
||||
|
||||
Other commands:
|
||||
|
||||
```sh
|
||||
./ssh_config.sh list
|
||||
./ssh_config.sh update packaging-server
|
||||
./ssh_config.sh remove packaging-server
|
||||
./ssh_config.sh rotate-key
|
||||
```
|
||||
|
||||
Secret prompts require an interactive terminal. Passwords and passphrases are never passed as command-line arguments.
|
||||
|
||||
## Vault
|
||||
|
||||
The default paths are:
|
||||
|
||||
```text
|
||||
${XDG_CONFIG_HOME:-$HOME/.config}/my-pi/pi-ssh/
|
||||
├── hosts.enc
|
||||
└── vault.key
|
||||
```
|
||||
|
||||
On POSIX systems the directory is mode `700` and both files are mode `600`. `hosts.enc` is encrypted and authenticated with AES-256-GCM; `vault.key` contains the adjacent random 256-bit key. Writes use a fresh IV and an atomic temporary-file rename. Plaintext configuration is never written to a temporary file.
|
||||
|
||||
This is deliberately a **casual-disclosure boundary**, not protection against compromise of the local account: anyone who can read both files can decrypt the vault. Encryption prevents the host configuration and passwords from being exposed by accidentally viewing or copying `hosts.enc` alone.
|
||||
|
||||
The encrypted payload contains host endpoints, pinned host-key fingerprints, groups, and either:
|
||||
|
||||
- a private-key path plus optional passphrase; or
|
||||
- the server password.
|
||||
|
||||
Private-key contents are not copied into the vault.
|
||||
|
||||
## Usage
|
||||
|
||||
Connect interactively:
|
||||
|
||||
```text
|
||||
/ssh
|
||||
/ssh packaging-server
|
||||
/ssh packaging-server:/absolute/remote/path
|
||||
/ssh status
|
||||
/ssh off
|
||||
```
|
||||
|
||||
Or at startup:
|
||||
|
||||
```sh
|
||||
pi --ssh packaging-server
|
||||
pi --ssh packaging-server:/absolute/remote/path
|
||||
```
|
||||
|
||||
Only imported host IDs are accepted. Arbitrary `user@host` targets are rejected.
|
||||
|
||||
The active host ID and remote cwd are stored in the Pi session for resume. Credentials are never stored in Pi session entries.
|
||||
|
||||
## Runtime behavior
|
||||
|
||||
- One persistent `ssh2` client is used for the active host.
|
||||
- Each `ssh_bash` call opens an exec channel and runs under `bash -lc` in the selected remote cwd.
|
||||
- SFTP provides remote reads and writes.
|
||||
- Writes use a temporary remote file and prefer OpenSSH's atomic rename SFTP extension when the server supports it.
|
||||
- A pinned SHA256 host-key mismatch fails closed.
|
||||
- Connection loss fails closed; the extension does not silently replay a command.
|
||||
- Remote `AGENTS.md` and `CLAUDE.md` files are never discovered or injected.
|
||||
- `ssh_find` and `ssh_grep` perform capability detection inside each approved call and return at most 200 bounded result lines.
|
||||
- RTK treats only `ssh_bash` as a Bash output-compaction alias; it does not rewrite remote commands or process remote search/read results.
|
||||
|
||||
### Adaptive remote search
|
||||
|
||||
`ssh_find` performs fixed-substring path matching with this backend order:
|
||||
|
||||
```text
|
||||
fd → fdfind → git ls-files → find
|
||||
```
|
||||
|
||||
`ssh_grep` defaults to literal, case-insensitive content matching with this backend order:
|
||||
|
||||
```text
|
||||
ripgrep → git grep → find + grep
|
||||
```
|
||||
|
||||
No backend is installed or uploaded. The tools use what the server already provides, report the chosen backend and truncation state, and cap `limit` at 200. A genuine no-match result succeeds with zero rows; invalid regexes, missing roots, and backend failures remain errors even though output passes through bounded `head`/`cut` stages. Narrow `path` and `pattern` when truncated rather than increasing the limit. Direct `find`, `fd`, `grep`, and `rg` commands remain forbidden through `ssh_bash`; use the structured search tools instead. Regex mode (`literal: false`) follows the selected backend's regex dialect.
|
||||
|
||||
The remote host must provide `bash`. SFTP support is required for file tools.
|
||||
|
||||
## Permission-system integration
|
||||
|
||||
All remote operations enter the bundle's existing permission chain:
|
||||
|
||||
- `ssh_read`, `ssh_write`, `ssh_edit`, `ssh_find`, and `ssh_grep` start as `ask`;
|
||||
- `ssh_bash` uses the full deterministic Bash policy and `decisionFloor: "ask"`;
|
||||
- deterministic hard denies remain denies;
|
||||
- asks enter the configured `auto-review` authorizer;
|
||||
- reviewer failures defer to the normal terminal prompt.
|
||||
|
||||
Permission evidence includes the configured host ID, resolved endpoint, port, remote cwd, and a bounded operation summary. It never includes passwords, passphrases, private-key contents, or the vault key. Remote paths are not normalized as local filesystem paths.
|
||||
|
||||
## Security notes
|
||||
|
||||
- Import only servers you control or trust.
|
||||
- Verify host-key fingerprints through an independent channel before accepting them.
|
||||
- Treat both vault files as secrets even though `hosts.enc` is encrypted.
|
||||
- The extension's threat model does not protect credentials from malicious code already running as the same local user.
|
||||
- Password keyboard-interactive mode reuses the configured password for the server's prompts; use it only with a trusted pinned host.
|
||||
- Remote content reaches the model only through an explicit `ssh_*` call or explicit user `!` command.
|
||||
|
||||
## Development
|
||||
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
Important files:
|
||||
|
||||
- `index.ts` — Pi extension and tool registration
|
||||
- `src/ssh2-transport.ts` — persistent ssh2, exec, and SFTP transport
|
||||
- `src/config.ts` — validated configuration types
|
||||
- `src/vault.ts` — AES-GCM vault
|
||||
- `src/import.ts` — `ssh -G` import helpers
|
||||
- `scripts/ssh-config.mjs` — interactive configuration CLI
|
||||
- `permission-integration.ts` — permission-system bridge
|
||||
|
||||
## Upstream and license
|
||||
|
||||
This maintained fork originates from `pansapiens/pi-ssh`; see [UPSTREAM.md](UPSTREAM.md). The pure ssh2 design also references the transport architecture in `@99percentpeople/pi-ssh-remote` without adopting its local-tool override model. Licensed under MIT; see [LICENSE](LICENSE).
|
||||
Reference in New Issue
Block a user