sync: update from private repo (edc775f2)
CI / build-and-test (push) Has been cancelled

This commit is contained in:
oss-sync
2026-07-06 01:04:12 +00:00
parent 747377bef9
commit b1292e34b2
322 changed files with 28001 additions and 4686 deletions
+24 -67
View File
@@ -141,23 +141,9 @@ Then in the UI:
3. Optionally set `remote_path_prefix` (default `/`) — restricts upload/download paths
4. Click **Test** → first call returns `host_key_first_observe` with a fingerprint
5. **Verify** in the dialog (compare fingerprint with what you expect from `ssh-keyscan <host>`)
6. Add the connection's UUID to a piece's `allowed_ssh_connections`:
6. Enable the `ssh` tool category for the workspace in **Settings → Tools**. Once enabled, every connection registered to that workspace becomes reachable from any task in it — there is no per-piece opt-in and no piece YAML to edit.
```yaml
# pieces/example.yaml
name: ssh-example
movements:
- name: deploy
allowed_tools: [SshExec, SshUpload]
allowed_ssh_connections: ["abcd1234-..."]
rules:
- condition: done
next: COMPLETE
instruction: |
Use SshExec to ...
```
7. Test the piece via the normal task UI.
7. Test via the normal task UI. The worker resolves the workspace's SSH connections once per job (`connectionRepo.listBySpace(spaceId)`) and exposes that exact set to every movement.
## `config.yaml` Reference
@@ -361,60 +347,31 @@ an allowlist. SHA1-RSA and other weak algorithms are rejected before
the key is recorded (`host_key_alg_not_allowed`). This is hard-coded
in `src/ssh/session.ts` to avoid misconfiguration.
## Per-piece `allowed_ssh_connections`
## Workspace-scoped SSH connections
A piece's movement must explicitly opt in to SSH usage. The
piece-runner enforces three invariants:
SSH reachability is a **workspace** property, not a piece property. The
per-piece `allowed_ssh_connections` field was removed in the tool-consolidation
work; if it still appears on a submitted piece it is tolerated but ignored.
1. If a movement's `allowed_tools` contains any SSH tool name
(`SshExec`/`SshUpload`/`SshDownload`), `allowed_ssh_connections`
**must be declared** on that movement (even if empty)
2. The field must be an array of strings
3. Each entry must be `*` or a lowercase hex+hyphen UUID (≥ 8 chars)
The gate now works like this:
Lint failures abort piece load.
1. The workspace's tool policy must enable the `ssh` category
(**Settings → Tools**). If it does not, the SSH tools are not offered to any
movement.
2. When `ssh` is enabled, the worker resolves the connections registered to the
job's space (`connectionRepo.listBySpace(spaceId)`) once per job and applies
that set uniformly to every movement. This space-scoped list is the **sole**
connection gate.
3. The list is always an explicit set of connection IDs — never a `*` wildcard —
so a job can only ever reach connections registered in its own workspace.
Cross-space isolation is guaranteed.
4. The [access grant check](#access-grants) still applies on top: a user without
a grant for a given connection cannot use it even when the workspace exposes
it.
### Forms
```yaml
# Explicit allowlist (most common)
allowed_ssh_connections: ["abcd1234-...", "ef567890-..."]
# Wildcard (admin-style — use sparingly)
allowed_ssh_connections: ["*"]
# Deny-all (still allows SSH tool in allowed_tools but refuses every UUID)
allowed_ssh_connections: []
```
The `*` form skips the per-piece check but **does not** skip the
[access grant check](#access-grants). A user without a grant for a
given connection still cannot use it even when the piece says `*`.
### Example
```yaml
name: backup-rotation
description: Daily backup rotation on prod servers
movements:
- name: list
allowed_tools: [SshExec]
allowed_ssh_connections: ["abcd1234-...", "ef567890-..."]
instruction: |
List the existing backup files on each server.
rules:
- condition: ready to rotate
next: rotate
- name: rotate
allowed_tools: [SshExec, SshUpload]
allowed_ssh_connections: ["abcd1234-...", "ef567890-..."]
instruction: |
Rotate the oldest backup ...
rules:
- condition: done
next: COMPLETE
```
If SSH is enabled but the SSH subsystem is not initialised (e.g. a test
environment without the bridge), the resolved list is empty and every SSH call
is rejected (fail-closed).
## Access Grants
@@ -870,7 +827,7 @@ curl -X DELETE 'http://localhost:3000/api/ssh/admin/audit?older_than_days=90'
| `access denied (no_grant)` | User lacks grant for connection | Admin creates a grant, or user uses an owned connection |
| `access denied (disabled)` | Admin disabled the connection | Admin re-enables, or use different connection |
| `access denied (abuse_locked)` | Counter triggered | Wait for lock window, or admin force-unlocks |
| `piece "X" does not list connection Y` | `allowed_ssh_connections` missing UUID | Add UUID to the movement's `allowed_ssh_connections` |
| `connection Y is not registered to this workspace` | Connection not registered to the job's workspace | Register it under **Settings → SSH** and enable the `ssh` category in **Settings → Tools** |
| `host_key_first_observe` | First time exercising connection | Verify fingerprint in UI |
| `host_key_not_verified` | Key recorded but never verified | Click Verify in UI |
| `host_key_mismatch` | Server key changed | Investigate (legitimate rotation? MITM?), then Replace via UI |