Files
clawsync/crates/clawsync-transport/README.md
osobhandClaude Sonnet 4.6 6d2eb5a1db feat: QUIC backend tests, diff command, clippy/doc fixes, QUIC push race fix
- Add QuicSyncBackend + quic_scheduler; fix push stream/close race by
  waiting for peer close instead of calling close_and_drain() on the
  client side (SyncComplete stream was racing CONNECTION_CLOSE)
- Add PipeWriteHalf::shutdown_push() for client-sends-last QUIC paths;
  use it in cmd_push so the server can process SyncComplete before the
  connection tears down
- Add SshSyncBackend + ssh_scheduler with subprocess integration tests
- Add clawsync diff command with --porcelain flag and subprocess tests
- Add QUIC subprocess push/pull integration tests
- Fix clippy --tests violations across 5 crates
- Fix broken intra-doc links (reader.rs, lib.rs, scheduler.rs)
- Rewrite crate READMEs; update BENCHMARKS.md with clawsync-fs CDC row

653 tests, 0 failures.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-06 22:40:31 -05:00

97 lines
3.1 KiB
Markdown

# clawsync-transport
ClawSync transport layer: TCP, QUIC, mmap ring-buffer, and SSH/pipe.
## Overview
`clawsync-transport` provides the network and IPC layer for ClawSync. All
backends share the same `SyncMessage` wire protocol (rkyv-serialized,
length-prefixed) and the `SyncPeer` abstraction.
## Backends
### TCP (`tcp`)
Async tokio TCP transport. Each `SyncMessage` is framed as a 4-byte LE length
prefix followed by an rkyv-serialized body. Suitable for LAN and WAN sync.
```rust
// Server
let server = TcpServer::bind("0.0.0.0:9999".parse()?).await?;
let (conn, _addr) = server.accept().await?;
let msg = conn.recv().await?;
// Client
let client = TcpConnection::connect("10.0.0.1:9999".parse()?).await?;
client.send(&SyncMessage::IbltRequest { .. }).await?;
```
### QUIC (`quic`)
Quinn-based QUIC transport with TLS 1.3. One unidirectional stream per
`SyncMessage`; W=16 concurrent streams for pipelined push.
```rust
// Server (self-signed cert for dev)
let server = QuicServer::bind(addr, QuicConfig::self_signed()?).await?;
let conn = server.accept().await?;
// Client (insecure — skip cert verification; for dev/same-LAN)
let conn = quic_connect(addr, "localhost", QuicConfig::insecure()?).await?;
```
After sending the last message, callers should use `conn.wait_for_peer_close()`
rather than dropping immediately, to avoid racing the in-flight stream with
`CONNECTION_CLOSE`.
### mmap (`mmap`)
Zero-copy same-node sync via a shared memory-mapped file ring buffer. For
co-located processes on the same machine.
### SSH / pipe (`peer`, `StreamPeer`)
`StreamPeer::from_child(child)` wraps a spawned process's stdin/stdout as a
`SyncPeer`. Used by `SshSyncBackend` to pipe through SSH without a pre-running
daemon.
```rust
let child = Command::new("ssh")
.args(["user@host", "clawsync", "serve", "/data/agent.claws", "--stdio"])
.stdin(Stdio::piped()).stdout(Stdio::piped())
.spawn()?;
let peer = SyncPeer::Stream(StreamPeer::from_child(child));
```
## Protocol
### IBLT push (default)
```text
Client Server
│── IbltRequest { sketch } ──────▶│ IBLT pre-flight: O(diff) wire cost
│◀─ IbltResponse { missing } ──────│
│── LayerPacket (rev K+1) ───────▶│ W=16 pipelined; Ack back
│── LayerPacket (rev K+2) ───────▶│
│ ... │
│── SyncComplete ─────────────────▶│
```
### Flat-manifest pull
```text
Client Server
│── ManifestRequest ──────────────▶│
│◀─ ManifestResponse ──────────────│
│◀─ LayerPacket (rev N+1) ─────────│
│── Ack { revision: N+1 } ────────▶│
│ ... │
│◀─ SyncComplete ──────────────────│
```
`SyncMessage` has 22 variants (discriminants append-only; never reorder).
## License
MIT — see repository root.