mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
122 lines
5.7 KiB
Markdown
122 lines
5.7 KiB
Markdown
# pi-ssh extension specification
|
||
|
||
## Goal
|
||
|
||
Pi and its default tools remain local. Explicit collision-free tools perform selected operations on one configured remote server:
|
||
|
||
- `ssh_connect`
|
||
- `ssh_read`
|
||
- `ssh_write`
|
||
- `ssh_edit`
|
||
- `ssh_find`
|
||
- `ssh_grep`
|
||
- `ssh_bash`
|
||
|
||
The extension is for operating servers from local projects, not for replacing Pi's local workspace. It never auto-loads remote project instructions.
|
||
|
||
## Connection model
|
||
|
||
Runtime communication uses Node `ssh2` only. OpenSSH is not spawned during Pi sessions. A user must explicitly import a concrete OpenSSH alias before connecting:
|
||
|
||
```text
|
||
ssh_config.sh import <alias>
|
||
→ ssh -G <alias>
|
||
→ user chooses private-key or password auth
|
||
→ ssh2 obtains host key, user confirms fingerprint
|
||
→ ssh2 verifies authentication
|
||
→ encrypted vault is updated
|
||
```
|
||
|
||
The resulting host ID is the only runtime selector. When a direct user request names that imported host as part of a concrete remote task, the agent calls:
|
||
|
||
```text
|
||
ssh_connect({ hostId: "<host-id>", remotePath?: "/absolute/or/~/path" })
|
||
```
|
||
|
||
`ssh_connect` is the only runtime connection surface. It is a model tool governed by `pi-permission-system`; there is no `/ssh` command, `--ssh` flag, session-resume reconnect, or user `!` remote-shell override.
|
||
|
||
Arbitrary `user@host`, port overrides, ProxyJump, and ProxyCommand are not supported in the first pure-ssh2 version. Unsupported imported configuration is rejected rather than ignored.
|
||
|
||
## Vault
|
||
|
||
Default paths:
|
||
|
||
```text
|
||
${XDG_CONFIG_HOME:-$HOME/.config}/my-pi/pi-ssh/hosts.enc
|
||
${XDG_CONFIG_HOME:-$HOME/.config}/my-pi/pi-ssh/vault.key
|
||
```
|
||
|
||
The directory is mode `700` and files are mode `600` on POSIX. A random adjacent 256-bit key encrypts and authenticates the complete validated configuration with AES-256-GCM. Each write uses a fresh IV and atomic replacement; plaintext is never written to a temporary file.
|
||
|
||
The adjacent-key design prevents casual disclosure of `hosts.enc` alone but does not protect against code able to read both files as the same local user.
|
||
|
||
The encrypted payload includes endpoint, authentication data, host groups, default cwd, and a pinned SHA256 host-key fingerprint. Private keys remain in their original paths; only their path and optional passphrase are stored.
|
||
|
||
## Authentication
|
||
|
||
Supported methods:
|
||
|
||
- password;
|
||
- single-password keyboard-interactive;
|
||
- OpenSSH private key;
|
||
- encrypted private key with passphrase.
|
||
|
||
Passwords, passphrases, private-key contents, vault key, and decrypted configuration must never enter command arguments, logs, permission evidence, system prompts, or Pi session entries.
|
||
|
||
## Host-key verification
|
||
|
||
Import displays the observed host-key algorithm and SHA256 fingerprint for explicit confirmation. Runtime `hostVerifier` must compare the server key with the pinned fingerprint and fail closed on any mismatch. Key changes require explicit host update.
|
||
|
||
## Transport
|
||
|
||
One persistent `ssh2.Client` belongs to the active host. Connection loss fails closed and no operation is automatically replayed.
|
||
|
||
### Shell
|
||
|
||
`ssh_bash` opens exec channels. Commands run through `bash -lc` after changing to the selected remote cwd. Output streams through the normal Pi Bash operations callback. Abort or timeout closes the channel without reconnecting or replaying.
|
||
|
||
### Files
|
||
|
||
SFTP implements file operations:
|
||
|
||
- read and access checks;
|
||
- recursive directory creation;
|
||
- remote write through a temporary file;
|
||
- atomic OpenSSH rename extension when available;
|
||
- safe direct-write fallback when SFTP v3 cannot replace an existing target.
|
||
|
||
Remote paths map from Pi's local factory cwd into the selected remote cwd, but permission-system access extractors prevent those paths from entering local path normalization.
|
||
|
||
### Search
|
||
|
||
Search tools execute one capability-adaptive, bounded shell pipeline inside the already approved tool call. They never install or upload binaries.
|
||
|
||
- `ssh_find`: `fd` → `fdfind` → `git ls-files` → `find`; fixed filename/path substring semantics.
|
||
- `ssh_grep`: `rg` → `git grep` → `find -exec grep`; literal case-insensitive semantics by default.
|
||
- each backend emits at most `limit + 1` lines so truncation is explicit; public `limit` is 1–200; each returned line is capped.
|
||
- relative paths resolve against remote cwd and `~/` against remote home.
|
||
- user strings are single-quoted as shell arguments and NUL/newline input is rejected.
|
||
- capability detection occurs only within the reviewed search call.
|
||
- search output is normalized by `pi-ssh` and excluded from RTK compaction.
|
||
|
||
Direct search commands remain denied through `ssh_bash`; structured search tools are the authoritative remote search surface.
|
||
|
||
## Permission boundary
|
||
|
||
There is one permission gate: `pi-permission-system`.
|
||
|
||
- `ssh_connect` defaults to `ask`; its preview resolves the imported host ID to the non-secret endpoint, port, and requested/default cwd before connection.
|
||
- `ssh_read`, `ssh_write`, `ssh_edit`, `ssh_find`, and `ssh_grep` default to `ask`.
|
||
- `ssh_bash` is a Bash-semantic `shellTools` alias with `decisionFloor: "ask"`.
|
||
- Bash hard denies remain denies.
|
||
- All asks enter the configured authorizer chain.
|
||
- Permission service absence or bridge registration failure must not install a permissive fallback.
|
||
|
||
Evidence includes configured host ID, endpoint, port, remote cwd, and a bounded operation summary, never credentials.
|
||
|
||
## Session lifecycle
|
||
|
||
Connections are created only by an approved `ssh_connect` call after session start. They are not persisted or automatically resumed. Connecting another imported host disposes the previous client, and session shutdown disposes the active client.
|
||
|
||
The system prompt states that default tools are local and `ssh_*` tools are remote after a connection becomes active. It does not include remote file content.
|