Skip to content

Commit a2d893a

Browse files
committed
Document session-scoped SSH remote tools
1 parent c5a3130 commit a2d893a

6 files changed

Lines changed: 138 additions & 3 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ It is built for people who want a practical, stateful agent they can run locally
1111
![Demo Animation](docs/demo.gif)
1212

1313
- **Streaming web UI** — real-time chat with Markdown, KaTeX, Mermaid, and Adaptive Cards
14-
- **Persistent agent state** — SQLite-backed messages, media, tasks, token usage, and encrypted keychain
15-
- **Workspace-native workflow** — browse files, preview documents, upload attachments, edit code, and reference files in prompts
14+
- **Persistent agent state** — SQLite-backed messages, media, tasks, token usage, encrypted keychain, and per-chat SSH profiles
15+
- **Workspace-native workflow** — browse files, preview documents, upload attachments, edit code, reference files in prompts, and optionally flip core tools to a remote SSH host per chat
1616
- **Built-in tools** — Ghostty-based terminal, code editor, Office/PDF/CSV/image/video viewers, draw.io, kanban board and mindmap editors, VNC client, and browser automation
1717
- **Agent control features** — steering, queued follow-ups, threading, side prompts, autoresearch experiment loops, and scheduled tasks
1818
- **Optional auth and channels** — passkeys/TOTP for the web UI, plus optional WhatsApp integration
@@ -108,7 +108,7 @@ Key environment variables:
108108
| `PICLAW_KEYCHAIN_KEY` | _(empty)_ | Master key for encrypted secret storage |
109109
| `PICLAW_TRUST_PROXY` | `0` | Enable when behind a reverse proxy or tunnel |
110110

111-
For the full list, auth setup (TOTP/passkeys), reverse proxy configuration, and SSHFS/FUSE support, see [docs/configuration.md](docs/configuration.md).
111+
For the full list, auth setup (TOTP/passkeys), per-chat SSH-backed remote tools, reverse proxy configuration, and SSHFS/FUSE support, see [docs/configuration.md](docs/configuration.md).
112112

113113
## Other install methods
114114

docs/architecture.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ These are compiled into the package and registered via `extensionFactories` on t
118118
| `sqlIntrospect` | `introspect_sql` (read-only SQLite queries) |
119119
| `internalTools` | `list_internal_tools` |
120120
| `sendAdaptiveCard` | `send_adaptive_card` for agent-owned Adaptive Card posting |
121+
| `sshTool` | `ssh` agent-only tool for per-chat SSH profile get/set/clear |
121122
| `uiThemeExtension` | `/theme`, `/tint` web UI theme controls |
122123
| `smartCompaction` | Smart compaction via `session_before_compact` hook (DB-driven file lists, junk-path filtering) |
123124

@@ -131,6 +132,7 @@ In addition to the inline factories, piclaw ships **optional extensions** under
131132
|-----------|------|---------|
132133
| `integrations/azure-openai.ts` | `AOAI_BASE_URL` must be set | Azure OpenAI + Foundry provider with managed-identity or API-key auth |
133134
| `integrations/context-mode.ts` | Always loaded | Tool-output storage, search handles, and `exec_batch` tool |
135+
| per-chat `ssh-core` session extension | Created per session by `AgentPool` | Wraps `read`/`write`/`edit`/`bash` with chat-scoped local-or-remote SSH execution |
134136
| `browser/cdp-browser/` | Always loaded | Cross-platform Chromium CDP browser control tool (`cdp_browser`) |
135137
| `platform/windows/win-ui/` | Always loaded (runtime no-op off Windows) | Windows desktop automation via bun:ffi + IAccessible (`win_*` tools) |
136138
| `viewers/drawio-editor/` | Always loaded | Self-hosted draw.io editor with extension route, save endpoint, and workspace export |
@@ -211,6 +213,7 @@ Page load
211213
- Web and WhatsApp share the same storage and agent pool.
212214
- Core utilities (config/env/chat context) live in `src/core`; shared helpers live in `src/utils`.
213215
- Chat context (chat JID + channel) is tracked in AsyncLocalStorage; tools/extensions read from the scoped context (defaults to `web:default` / `web`) rather than env variables.
216+
- SSH-backed core-tool state is chat-scoped and persisted in SQLite (`chat_ssh_configs`). `AgentPool` injects a per-session `ssh-core` extension and can hot-swap the live SSH backend for an existing warm chat session.
214217
- Workspace tree responses are cached briefly (1s) and rate-limited to prevent bursty UI reloads (HTTP 429 when exceeded).
215218
- The **workspace explorer** is a responsive sidebar (visible on desktop/tablet ≥1024px landscape) that shows a file tree of `/workspace`, supports file previews, drag-and-drop upload, inline file creation, inline rename, drag-and-drop move, and file reference pills for prompts.
216219
- The **code editor** is a standalone pane extension (`extensions/viewers/editor/`) using CodeMirror 6 directly (no Preact wrapper). It opens in the tabbed content area when a file is clicked in the explorer. Supports syntax highlighting for 12 languages, search/replace, line wrapping, dirty tracking, Cmd+S save, vim mode, whitespace toggle, and accent-aware theming. The editor bundle is lazy-loaded on first file open. Backend endpoints: `GET /workspace/file?mode=edit` (full content up to 256 KB) and `PUT /workspace/file` (save).

docs/configuration.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,59 @@ Notes:
118118

119119
Deprecated env names (still supported): `ASSISTANT_NAME`, `ASSISTANT_AVATAR`, `AGENT_TIMEOUT`, `AGENT_TIMEOUT_BACKGROUND`.
120120

121+
## SSH-backed remote core tools
122+
123+
Piclaw can redirect the core file/shell tools (`read`, `write`, `edit`, `bash`) to a remote host over SSH.
124+
125+
There are two ways to enable it:
126+
127+
1. **Startup/default session config** via env vars:
128+
129+
| Variable | Default | Purpose |
130+
|----------|---------|---------|
131+
| `PICLAW_SSH_TARGET` | _(empty)_ | SSH target as `user@host` or `user@host:/remote/path` |
132+
| `PICLAW_SSH_PORT` | `22` | SSH port for startup/default remote sessions |
133+
134+
2. **Per-chat live config** via the agent-only `ssh` tool:
135+
- `ssh { action: "set", ssh_target, private_key_keychain, ... }`
136+
- `ssh { action: "get" }`
137+
- `ssh { action: "clear" }`
138+
139+
The `ssh` tool stores chat-scoped profiles in SQLite and applies them immediately to live sessions when possible. That means the agent can switch a chat from local → remote → local again in the same turn/session without recreating the session runtime.
140+
141+
### Required key material
142+
143+
Live per-chat SSH uses keychain-backed credentials:
144+
145+
- `private_key_keychain` — required; keychain entry containing the OpenSSH private key
146+
- `known_hosts_keychain` — optional; keychain entry containing `known_hosts` content
147+
- `strict_host_key_checking``yes`, `accept-new`, or `no`
148+
149+
Example tool payload:
150+
151+
```json
152+
{
153+
"action": "set",
154+
"ssh_target": "[email protected]:/srv/project",
155+
"ssh_port": 22,
156+
"private_key_keychain": "ssh/prod",
157+
"known_hosts_keychain": "ssh/prod.known_hosts",
158+
"strict_host_key_checking": "accept-new"
159+
}
160+
```
161+
162+
### Transport behavior
163+
164+
The SSH backend keeps the same remote-tool semantics as the packaged SSH extension model:
165+
166+
- multiplexed connection reuse with `ControlMaster=auto`
167+
- `ControlPersist=600`
168+
- persistent remote shell/session reuse across tool calls
169+
- remote cwd/home mapping from the configured target path
170+
- immediate live switching when the chat already has a warm session
171+
172+
If a chat has no stored SSH profile, core tools run locally as usual.
173+
121174
### Assistant name and avatar
122175

123176
Set via environment variables (see above) or in `.piclaw/config.json`:

docs/keychain.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,32 @@ The tracked bash runner also replaces `keychain:<name>` placeholders directly in
118118

119119
`keychain:<name>:username` resolves to the stored username (if present).
120120

121+
## SSH keys and known_hosts entries
122+
123+
The per-chat `ssh` tool expects SSH material to come from the keychain.
124+
125+
Typical entries:
126+
127+
- `ssh/prod` — type `secret`, secret = full OpenSSH private key
128+
- `ssh/prod.pub` — optional public key copy
129+
- `ssh/prod.known_hosts` — optional `known_hosts` text blob
130+
131+
Example:
132+
133+
```bash
134+
PICLAW_KEYCHAIN_KEY="your-master-key" \
135+
piclaw keychain set ssh/prod \
136+
--type secret \
137+
--secret-file ~/.ssh/id_ed25519
138+
139+
PICLAW_KEYCHAIN_KEY="your-master-key" \
140+
piclaw keychain set ssh/prod.known_hosts \
141+
--type secret \
142+
--secret-file ~/.ssh/known_hosts
143+
```
144+
145+
The runtime writes these values to temporary files with restrictive permissions and uses them for the SSH control connection.
146+
121147
## Notes
122148

123149
- Entry names can be hierarchical (`github/foo/bar`).

docs/runtime-flows.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,47 @@ Without isolation, a scheduled task's prompt and response would appear in the ag
219219
- **Model safety**: The model is restored to its pre-task state on the correct branch.
220220
- **No session forking**: Unlike `fork()` which creates a new session file, `navigateTree()` stays in the same file and simply moves the branch pointer.
221221

222+
## Session-scoped SSH remote tools
223+
224+
A chat can optionally switch its core file/shell tools to a remote host over SSH.
225+
226+
- Control surface: agent-only `ssh` tool
227+
- Scope: one chat JID at a time
228+
- Persistence: SQLite `chat_ssh_configs`
229+
- Affected tools: `read`, `write`, `edit`, `bash`
230+
231+
The important runtime property is that SSH mode is **live mutable**. If a warm session already exists, `ssh set` and `ssh clear` can flip the backend immediately for the next tool/model step without rebuilding the whole session.
232+
233+
```mermaid
234+
sequenceDiagram
235+
participant Agent
236+
participant ssh as ssh tool
237+
participant Pool as AgentPool
238+
participant DB as SQLite
239+
participant Core as ssh-core wrappers
240+
participant Host as Local or Remote host
241+
242+
Agent->>ssh: action=set target+keychain
243+
ssh->>Pool: setChatSshConfig(chatJid, config)
244+
Pool->>DB: upsert chat_ssh_configs
245+
Pool->>Core: applyLiveChatSshConfig(chatJid)
246+
Core-->>Host: next read/write/edit/bash uses SSH
247+
248+
Agent->>ssh: action=clear
249+
ssh->>Pool: clearChatSshConfig(chatJid)
250+
Pool->>DB: delete chat_ssh_configs row
251+
Pool->>Core: clearLiveChatSshConfig(chatJid)
252+
Core-->>Host: next core tool call runs locally
253+
```
254+
255+
Transport semantics match the packaged SSH extension model:
256+
257+
- multiplexed connection reuse
258+
- `ControlMaster=auto`
259+
- `ControlPersist=600`
260+
- persistent remote shell state
261+
- configured remote cwd/home mapping
262+
222263
## Session lifecycle (summary)
223264

224265
- Messages for a chat JID share a warm `AgentSession`.

docs/tools-and-skills.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,24 @@ You can extend that baseline with `.piclaw/config.json`:
6464
- `exec_batch` — run multiple shell commands and return concise summaries for each
6565
- `powershell` — Windows-only replacement for the default shell tool; active instead of `bash` on Windows hosts
6666
- `exit_process` — gracefully terminate piclaw so Supervisor restarts it; kept always active because lifecycle control should not depend on same-turn lazy activation
67+
- `ssh` — get, set, or clear the session-scoped SSH profile used by remote-backed core tools (`read`, `write`, `edit`, `bash`)
6768

6869
`messages` `search` accepts `query`, `chat_jid` (or `*`/`all`), `role`, `after`, `before`, `since`, `limit`, `offset`, and `details_max_chars` for controlling detail payloads.
6970
`messages` `get` accepts `row_ids`, optional `chat_jid`, `role`, `context_before`, `context_after`, and `details_max_chars`.
7071
`messages` `add` accepts `content`, optional `chat_jid`, `type` (`user` or `agent`), and `media_ids`.
7172
`messages` `delete` accepts `row_ids` and optional `chat_jid`, `force`, and `dry_run`.
7273

74+
`ssh` accepts:
75+
- `action``get`, `set`, or `clear`
76+
- `chat_jid` — optional override; defaults to the current chat
77+
- `ssh_target``user@host` or `user@host:/remote/path`
78+
- `ssh_port` — optional port (default `22`)
79+
- `private_key_keychain` — keychain entry containing the private key
80+
- `known_hosts_keychain` — optional keychain entry containing `known_hosts`; empty string clears it
81+
- `strict_host_key_checking``yes`, `accept-new`, or `no`
82+
83+
When a live session already exists for the chat, `ssh set` and `ssh clear` apply immediately to subsequent tool/model steps in the same turn.
84+
7385
`search_workspace` accepts:
7486
- `query` — FTS query text
7587
- `scope``notes`, `skills`, or `all` (default)

0 commit comments

Comments
 (0)