docs: update README with SSH transport, status/verify/bwlimit sections

- Transport section: TCP, QUIC, and SSH with comparison table
- SSH subsection: on-demand server via --stdio, CLAWSYNC_SSH_COMMAND override
- New sections: status, verify, watch, serve-all, pull-fs, serve-all
- Remote address format table covering TCP/QUIC/SSH syntax
- Test count updated to ~617 default / ~633 with simd-cdc
- --bwlimit, --dry-run, --verbose, --stdio options documented throughout

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
osobh
2026-04-05 22:47:17 -05:00
co-authored by Claude Sonnet 4.6
parent a24eb86544
commit 50001b64ac
+229 -34
View File
@@ -9,6 +9,9 @@ clawsync push weights.h5 server:9999/weights.h5 # push only new revisions
clawsync pull server:9999/weights.h5 weights.h5 # pull only missing revisions
clawsync hdf5-sync model.h5 server:9997/model.h5 # dataset-granular HDF5 sync
clawsync sync ./data/ server:9998/remote/data/ # CDC delta for any file type
clawsync sync ./data/ user@host:/remote/data/ # same, via SSH (no pre-running server)
clawsync status ./data/ # local change report vs last sync
clawsync verify ./data/ server:9998/remote/data/ # diff against remote, no transfer
```
---
@@ -21,16 +24,21 @@ clawsync sync ./data/ server:9998/remote/data/ # CDC delta for any file t
4. [Installation](#installation)
5. [Quick Start](#quick-start)
6. [Command Reference](#command-reference)
- [Remote address format](#remote-address-format)
- [push / pull / serve](#push--pull--serve)
- [hdf5-sync / serve-hdf5](#hdf5-sync--serve-hdf5)
- [sync / serve-fs](#sync--serve-fs)
- [sync / serve-fs / pull-fs](#sync--serve-fs--pull-fs)
- [watch](#watch)
- [serve-all](#serve-all)
- [status](#status)
- [verify](#verify)
- [list-revisions](#list-revisions)
- [rollback](#rollback)
- [export-revision](#export-revision)
- [snapshot](#snapshot)
- [gc](#gc)
- [branch](#branch)
7. [Transport: TCP and QUIC](#transport-tcp-and-quic)
7. [Transport: TCP, QUIC, and SSH](#transport-tcp-quic-and-ssh)
8. [Sync Protocols](#sync-protocols)
9. [Performance](#performance)
10. [Development](#development)
@@ -243,14 +251,28 @@ clawsync gc weights.h5 --keep-last 100
### Remote address format
All network commands use `host:port/path` as the remote address:
All network commands accept three remote formats:
```
server.local:9999/path/to/file.h5
127.0.0.1:9999/relative/path.h5
| Format | Transport | Example |
|--------|-----------|---------|
| `host:port/path` | TCP (default) | `server.local:9999/models/model.h5` |
| `host:port/path` + `--quic` | QUIC (TLS 1.3) | `server.local:9999/models/model.h5 --quic` |
| `[user@]host:path` | SSH (no server setup needed) | `alice@train-node:/data/models/` |
**SSH remote:** ClawSync spawns the appropriate `serve` command on the remote machine via SSH with `--stdio` and wires its stdin/stdout directly to the protocol. No pre-running server or open port required — only SSH access and `clawsync` on the remote `PATH`.
```bash
# TCP (explicit server needed on remote)
clawsync sync ./data/ server:9998/data/
# SSH (no server setup — clawsync spawns one over SSH)
clawsync sync ./data/ user@host:/remote/data/
clawsync push model.h5 user@host:/models/model.h5
clawsync pull user@host:/models/model.h5 model.h5
clawsync hdf5-sync model.h5 user@host:/models/model.h5
```
The path is always relative to the server's serve root (for `serve-hdf5` and `serve-fs`) or an absolute path for `serve` (single-file mode).
Set `CLAWSYNC_SSH_COMMAND` to override the SSH binary (useful for testing with a custom shim or `mosh`).
---
@@ -260,19 +282,22 @@ Revision-aware sync for HDF5 files with `.onion` sidecars. Transfers only the on
```bash
# Push new revisions to a remote server
clawsync push <LOCAL.h5> <HOST:PORT/REMOTE.h5> [--branch NAME] [--quic]
clawsync push <LOCAL.h5> <REMOTE> [--branch NAME] [--quic]
# Pull revisions from a remote server
clawsync pull <HOST:PORT/REMOTE.h5> <LOCAL.h5> [--branch NAME] [--quic]
clawsync pull <REMOTE> <LOCAL.h5> [--branch NAME] [--quic]
# Serve a local HDF5 file (accepts push and pull)
clawsync serve <LOCAL.h5> [--bind ADDR] [--quic]
clawsync serve <LOCAL.h5> [--bind ADDR] [--quic] [--stdio]
# default bind: 0.0.0.0:9999
```
`<REMOTE>` is either `host:port/path.h5` (TCP/QUIC) or `[user@]host:path.h5` (SSH).
**Options:**
- `--branch NAME` — push/pull only revisions on the named branch
- `--quic` — use QUIC transport with self-signed TLS (for testing; configure certificates for production)
- `--quic` — use QUIC transport with self-signed TLS
- `--stdio` — read/write protocol on stdin/stdout (used automatically by SSH clients)
**What gets transferred:** Only `OnionLayerPacket` entries the remote lacks. Pre-flight uses IBLT (Invertible Bloom Lookup Table) sketch exchange — cost is O(revision count), not O(file size).
@@ -280,7 +305,7 @@ clawsync serve <LOCAL.h5> [--bind ADDR] [--quic]
```bash
# First push — cold copy of all revisions
clawsync push model.h5 server:9999/model.h5
# Pushing model.h5 → server:9999 (TCP) ...
# Pushing model.h5 → server:9999/model.h5 (TCP) ...
# Remote has 0 revision(s) (IBLT). Pushing 150 revision(s).
# Push complete: 150 revision(s), 614400 bytes.
@@ -288,6 +313,10 @@ clawsync push model.h5 server:9999/model.h5
clawsync push model.h5 server:9999/model.h5
# Remote has 150 revision(s) (IBLT). Pushing 3 revision(s).
# Push complete: 3 revision(s), 12288 bytes.
# Push over SSH — no server setup needed on remote
clawsync push model.h5 alice@train-node:/models/model.h5
# Pushing model.h5 → alice@train-node:/models/model.h5 (SSH) ...
```
---
@@ -298,16 +327,19 @@ Dataset-granular HDF5 sync. No `.onion` sidecar required on either side. Compare
```bash
# Sync a local HDF5 file to a remote server (only changed datasets)
clawsync hdf5-sync <LOCAL.h5> <HOST:PORT/REMOTE.h5> [--delete] [--quic]
clawsync hdf5-sync <LOCAL.h5> <REMOTE> [--delete] [--quic]
# Serve a directory of HDF5 files
clawsync serve-hdf5 <DIR> [--bind ADDR] [--allow-delete] [--quic]
clawsync serve-hdf5 <DIR> [--bind ADDR] [--allow-delete] [--quic] [--stdio]
# default bind: 0.0.0.0:9997
```
`<REMOTE>` is `host:port/relative.h5` (TCP/QUIC) or `[user@]host:/abs/path.h5` (SSH).
**Options:**
- `--delete` (client) — request removal of datasets absent from the local file
- `--allow-delete` (server) — permit clients to request dataset deletion
- `--stdio` — (server) read/write protocol on stdin/stdout for SSH sessions
- Deletion requires **both** `--delete` (client) and `--allow-delete` (server)
**What gets transferred:** Only datasets whose BLAKE3 hash differs between local and remote. A warm sync of an unchanged file transfers nothing and completes in 1 RTT.
@@ -333,34 +365,164 @@ clawsync hdf5-sync model.h5 server:9997/models/model.h5
---
### sync / serve-fs
### sync / serve-fs / pull-fs
CDC-based delta sync for any file type — not just HDF5. Handles directory trees. Uses Content-Defined Chunking (FastCDC) so chunk boundaries are insertion-stable: adding 1 byte at the start of a file does not invalidate the rest.
```bash
# Sync a local directory to a remote path
clawsync sync <LOCAL_PATH> <HOST:PORT/REMOTE_PATH> [--delete] [--exclude GLOB]... [--quic]
# Push local directory to remote (client → server)
clawsync sync <LOCAL> <REMOTE> [--delete] [--dry-run] [-v] [--exclude GLOB]... [--bwlimit KBPS] [--quic]
# Pull remote directory to local (server → client)
clawsync pull-fs <REMOTE> <LOCAL> [--delete] [--exclude GLOB]... [--quic]
# Serve a local directory
clawsync serve-fs <DIR> [--bind ADDR] [--allow-delete] [--exclude GLOB]... [--quic]
clawsync serve-fs <DIR> [--bind ADDR] [--allow-delete] [--exclude GLOB]... [--quic] [--stdio]
# default bind: 0.0.0.0:9998
```
**Options:**
- `--delete` — delete files on the server that don't exist locally (requires `--allow-delete` on server)
- `--exclude GLOB` — skip files matching the glob (e.g. `"*.tmp"`, `"**/.git/**"`)
- `--allow-delete`(server) permit client-requested deletions
`<REMOTE>` is `host:port/path` (TCP/QUIC) or `[user@]host:path` (SSH).
**Options for `sync`:**
- `--delete`delete files on the remote that don't exist locally (requires `--allow-delete` on server)
- `--dry-run` — report what would change without transferring anything
- `-v` / `--verbose` — print each file as it completes with byte count
- `--exclude GLOB` — skip files matching the glob (may be repeated)
- `--bwlimit KBPS` — cap outbound bandwidth in KiB/s (e.g. `--bwlimit 1024` = 1 MiB/s)
- `--quic` — use QUIC transport
**Protocol:** 2-RTT for incremental and cold copy; 1-RTT for warm no-op. W=16 pipelining on the second RTT.
**Options for `serve-fs`:**
- `--allow-delete` — permit client-requested deletions
- `--stdio` — read/write protocol on stdin/stdout for SSH sessions
- `--exclude GLOB` — exclude paths from serving (may be repeated)
**Example:**
**State cache:** After each sync, ClawSync writes `.clawsync.state` in the local directory — a tab-separated index of `(path, mtime_ns, size, blake3)`. On the next run, files whose mtime and size are unchanged have their BLAKE3 reused from cache, making warm syncs proportional to the number of *changed* files rather than total bytes.
**Protocol:** 2-RTT for incremental and cold copy; 1-RTT for warm no-op. W=16 pipelining on the second RTT. Delta savings are reported as `Sync complete: N added, M modified, R removed, B bytes.`
**Examples:**
```bash
# TCP server
clawsync serve-fs /training-data --bind 0.0.0.0:9998 --allow-delete &
clawsync sync ./checkpoints/ server:9998/checkpoints/ \
--delete --exclude "*.tmp" --exclude "**/__pycache__/**"
# Sync complete: 0 added, 2 modified, 1 removed, 4096 bytes.
# SSH — no server setup needed
clawsync sync ./checkpoints/ alice@gpu-node:/training/checkpoints/
# Bandwidth-limited sync (512 KiB/s max upload)
clawsync sync ./data/ server:9998/data/ --bwlimit 512
# Dry-run: show what would change without syncing
clawsync sync ./data/ server:9998/data/ --dry-run
# Pull (reverse direction: remote sends to local)
clawsync pull-fs server:9998/remote/data/ ./local/data/
clawsync pull-fs alice@gpu-node:/training/data/ ./local/data/ # SSH pull
```
---
### watch
Watch a local directory for changes and re-sync to a remote automatically.
```bash
clawsync watch <LOCAL> <REMOTE> [--delete] [--exclude GLOB]... [--debounce-ms N] [--quic]
```
Uses OS filesystem events (inotify on Linux, kqueue/FSEvents on macOS) with a configurable debounce window before triggering a sync. Reconnects automatically after network errors. Runs until `Ctrl-C`.
```bash
clawsync watch ./experiments/ server:9998/experiments/ \
--delete --debounce-ms 500
# Watching ./experiments/ → server:9998/experiments/ (TCP), debounce 500ms ...
# Watching for changes. Press Ctrl-C to stop.
# Sync complete: 1 added, 0 modified, 0 removed, 12288 bytes.
```
**Options:**
- `--debounce-ms N` — wait N milliseconds after the last event before syncing (default: 200)
- Other options same as `sync`
---
### serve-all
Universal server that handles onion-revision (`push`/`pull`), HDF5-dataset (`hdf5-sync`), and FS-sync (`sync`/`pull-fs`) clients on a **single port**, dispatching on the first message.
```bash
clawsync serve-all <DIR> [--bind ADDR] [--allow-delete] [--exclude GLOB]... [--quic] [--stdio]
# default bind: 0.0.0.0:9996
```
- Onion clients: files are looked up as `<DIR>/<agent-id>.claws`
- HDF5 and FS clients: `<DIR>` is used as the root directory
Useful for deploying a single server that multiple client types can connect to.
```bash
clawsync serve-all /models --bind 0.0.0.0:9996 --allow-delete
# ClawSync server listening on 0.0.0.0:9996
# Onion revision push
clawsync push weights.h5 server:9996/weights.h5
# HDF5 dataset sync
clawsync hdf5-sync model.h5 server:9996/model.h5
# General file sync
clawsync sync ./data/ server:9996/data/
```
---
### status
Show local changes since the last sync — no server connection required.
```bash
clawsync status <DIR>
```
Compares the current directory tree against the `.clawsync.state` cache written by the last `sync`, `pull-fs`, or `watch` run. Useful for checking what has changed before pushing.
```bash
clawsync status ./checkpoints/
# A new_experiment.py
# M config.yaml
# D old_run/weights.pt
clawsync status ./checkpoints/
# Nothing changed since last sync.
```
Output prefix: `A` = added (new file), `M` = modified (mtime or size changed), `D` = deleted (was in cache, gone from disk).
If `.clawsync.state` is absent (never synced), all files are reported as `A`.
---
### verify
Compare a local directory against a remote without transferring anything.
```bash
clawsync verify <LOCAL> <REMOTE> [--delete] [--exclude GLOB]... [--quic]
```
Performs the manifest exchange (1 RTT) and reports which files would be added, modified, or removed on the remote if `sync` were run. No files are written on either side. Supports TCP, QUIC, and SSH remotes.
```bash
clawsync verify ./data/ server:9998/data/
# A new_file.bin
# M weights.pt
# (no output for D if --delete not set)
clawsync verify ./data/ alice@host:/remote/data/
# Remote is up to date.
```
---
@@ -480,17 +642,19 @@ clawsync branch merge model.h5 experiment --into main --strategy latest-wins
---
## Transport: TCP and QUIC
## Transport: TCP, QUIC, and SSH
All network commands support both TCP (default) and QUIC via the `--quic` flag.
All network commands support TCP (default), QUIC (`--quic`), and SSH (automatic when the remote address contains `@`).
| | TCP | QUIC |
|---|---|---|
| Default | ✓ | add `--quic` |
| TLS | ✗ | ✓ (TLS 1.3) |
| Multiplexing | single stream | per-message streams |
| Connection setup | fast | slightly slower (TLS handshake) |
| Production use | plaintext LAN | encrypted WAN |
| | TCP | QUIC | SSH |
|---|---|---|---|
| Address format | `host:port/path` | `host:port/path --quic` | `user@host:path` |
| TLS | ✗ | ✓ (TLS 1.3) | ✓ (SSH layer) |
| Requires pre-running server | ✓ | ✓ | ✗ |
| Firewall-friendly | needs open port | needs open port | SSH port (22) only |
| Production use | plaintext LAN | encrypted WAN | encrypted WAN, no daemon |
### TCP / QUIC
QUIC in development mode uses `QuicConfig::self_signed()` (auto-generated certificate). For production, provide a signed certificate via the configuration API.
@@ -499,6 +663,37 @@ clawsync serve model.h5 --bind 0.0.0.0:9999 --quic
clawsync push model.h5 server:9999/model.h5 --quic
```
### SSH
When the remote address starts with `user@host:`, ClawSync spawns the remote binary over SSH rather than connecting to a TCP/QUIC port. No pre-running server is needed — the remote `clawsync` binary is started on-demand with `--stdio` and the protocol runs over stdin/stdout.
```bash
# push/pull over SSH — no clawsync daemon needed on server
clawsync push model.h5 user@server:models/model.h5
clawsync pull user@server:models/model.h5 local.h5
# general file sync over SSH
clawsync sync ./data/ user@server:backup/data/
# HDF5 dataset sync over SSH
clawsync hdf5-sync local.h5 user@server:hdf5/local.h5
```
The SSH command used is `ssh` by default. Override it by setting `CLAWSYNC_SSH_COMMAND` (useful for testing or when using a custom SSH wrapper):
```bash
CLAWSYNC_SSH_COMMAND=/usr/local/bin/my-ssh clawsync push model.h5 user@server:path/model.h5
```
The `--stdio` flag on server commands exposes the same protocol over stdin/stdout, enabling use with any pipe-based transport:
```bash
# serve over a named pipe or custom tunnel
clawsync serve model.h5 --stdio
clawsync serve-fs ./data/ --stdio
clawsync serve-hdf5 ./hdf5/ --stdio
```
---
## Sync Protocols
@@ -579,7 +774,7 @@ cargo build --workspace --release
### Testing
```bash
# All tests (~573 default, ~589 with SIMD CDC)
# All tests (~617 default, ~633 with SIMD CDC)
cargo test --workspace
cargo test --workspace --features simd-cdc