diff --git a/README.md b/README.md index 472732a..ed85863 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,6 @@ LAN clipboard sync for macOS. Copy on one Mac, paste on another. Instantly. No cloud. No account. No Apple ID. No flaky Universal Clipboard. -## How it works - ``` ┌──────────────────┐ ┌──────────────────┐ │ MacBook │ UDP :9877 │ iMac │ @@ -21,108 +19,60 @@ No cloud. No account. No Apple ID. No flaky Universal Clipboard. copy ⌘C paste ⌘V ``` -Each Mac runs `clipx`. When you copy something, it sends the clipboard content directly to all paired peers via UDP unicast. When it receives content from a peer, it writes it to your local clipboard. That's it. - -- **UDP unicast** — reliable, fast, no firewall issues with multicast -- **Explicit pairing** — `clipx pair `, no flaky auto-discovery -- **SHA-256 dedup** — prevents infinite ping-pong loops between nodes -- **10 MB max** — large content is automatically chunked into ~1300-byte UDP packets - ## Install -### Homebrew (recommended) - ```bash brew install gomantics/tap/clipx ``` -### Go install +Or with Go: ```bash go install github.com/gomantics/clipx/cmd/clipx@latest ``` -### From source - -```bash -git clone https://github.com/gomantics/clipx.git -cd clipx -make build # binary at ./clipx-bin -``` - -### Download binary - -Pre-built binaries for macOS (Intel & Apple Silicon) are available on the [releases page](https://github.com/gomantics/clipx/releases/latest). +Pre-built binaries for macOS (Intel & Apple Silicon) are on the [releases page](https://github.com/gomantics/clipx/releases/latest). ## Quick start -### 1. Pair your Macs - -On **Mac A** (e.g. 192.168.0.5): +**1. Pair your Macs** ```bash -clipx pair 192.168.0.6 # IP of Mac B -``` - -On **Mac B** (e.g. 192.168.0.6): +# On Mac A (192.168.0.5): +clipx pair 192.168.0.6 -```bash -clipx pair 192.168.0.5 # IP of Mac A +# On Mac B (192.168.0.6): +clipx pair 192.168.0.5 ``` -### 2. Install and run - -On **both Macs**: +**2. Install and run** (on both Macs) ```bash clipx install ``` -This does three things: -- Adds a firewall exception so UDP can get through (may ask for sudo) -- Creates a LaunchAgent that starts clipx at login -- Starts clipx immediately - -**Done.** Copy on one Mac, paste on the other. - -## Disable Apple's Universal Clipboard - -Apple's Universal Clipboard will fight with clipx. Turn it off: - -**System Settings → General → AirDrop & Handoff → Handoff → Off** - -Or from the terminal: - -```bash -defaults write ~/Library/Preferences/ByHost/com.apple.coreservices.useractivityd.plist ActivityAdvertisingAllowed -bool no -defaults write ~/Library/Preferences/ByHost/com.apple.coreservices.useractivityd.plist ActivityReceivingAllowed -bool no -``` - -To re-enable later: +This adds a firewall exception, creates a LaunchAgent, and starts clipx. **Done.** Copy on one, paste on the other. -```bash -defaults delete ~/Library/Preferences/ByHost/com.apple.coreservices.useractivityd.plist ActivityAdvertisingAllowed -defaults delete ~/Library/Preferences/ByHost/com.apple.coreservices.useractivityd.plist ActivityReceivingAllowed -``` +> **Tip:** Disable Apple's Universal Clipboard — it will fight with clipx. +> **System Settings → General → AirDrop & Handoff → Handoff → Off** ## Commands | Command | Description | |---|---| | `clipx` | Start the daemon (foreground) | -| `clipx pair ` | Add a peer to sync with | +| `clipx pair ` | Add a peer | | `clipx unpair ` | Remove a peer | -| `clipx peers` | List peers and their online status | +| `clipx peers` | List peers and their status | | `clipx install` | Install LaunchAgent + firewall exception | -| `clipx uninstall` | Remove the LaunchAgent | -| `clipx status` | Show daemon status and recent logs | +| `clipx uninstall` | Remove LaunchAgent | +| `clipx status` | Show status and recent logs | | `clipx update` | Self-update to latest release | | `clipx version` | Print version | -| `clipx help` | Show help | ## Adding more Macs -Just pair each new Mac with every existing one: +Pair each new Mac with every existing one: ```bash # On the new Mac: @@ -131,95 +81,54 @@ clipx pair 192.168.0.6 clipx install # On each existing Mac: -clipx pair 192.168.0.7 # IP of new Mac -# restart to pick up new peer: +clipx pair 192.168.0.7 clipx uninstall && clipx install ``` ## Configuration -Peers are stored in `~/.config/clipx/config.json`: +Peers live in `~/.config/clipx/config.json`: ```json { - "peers": [ - "192.168.0.5", - "192.168.0.6" - ] + "peers": ["192.168.0.5", "192.168.0.6"] } ``` -You can edit this file directly if you prefer. - -## Ports - -| Port | Protocol | Purpose | -|---|---|---| -| 9877 | UDP | Clipboard sync + ping/pong health checks | - -One port, UDP only. If you run a firewall, `clipx install` handles it automatically. +## How it works -## Design +- **UDP unicast** on port 9877 — no multicast, no firewall headaches +- **Explicit pairing** — `clipx pair `, no flaky auto-discovery +- **SHA-256 dedup** — prevents clipboard ping-pong loops between nodes +- **Chunked transfer** — content >1300 bytes is split into UDP-safe chunks, up to 10 MB +- **Triple send** — small payloads sent 3x for UDP reliability -### Protocol +### Wire format -All communication is UDP unicast on port 9877. Four message types: +``` +[6B magic "CLIPX2"] [1B type] [8B nodeID] [payload] +``` | Type | Byte | Purpose | |---|---|---| -| Clipboard | `C` | Carries clipboard content (≤1300 bytes) | -| Chunk | `K` | Carries a chunk of large clipboard content | -| Ping | `P` | Health check request | +| Clipboard | `C` | Clipboard content (single packet) | +| Chunk | `K` | One chunk of large content | +| Ping | `P` | Health check | | Pong | `A` | Health check response | -Wire format: `[6B magic "CLIPX2"] [1B type] [8B nodeID] [payload]` - -Clipboard payload: `[64B SHA-256 hex hash] [clipboard data]` - -Chunk payload: `[64B SHA-256 hex hash] [2B chunk index] [2B total chunks] [chunk data]` - -### Loop prevention - -1. Every clipboard write is hashed (SHA-256) -2. When content arrives from a peer, its hash is recorded -3. When the local clipboard watcher detects a change, it checks if the hash matches a recently received peer hash — if so, it skips broadcasting - -### Reliability - -- Small clipboard content (≤1300 bytes) is sent 3 times for UDP reliability -- Large content is automatically chunked and reassembled on the receiver -- Incomplete chunk transfers are cleaned up after 30 seconds -- Persistent UDP connections to peers with automatic reconnection - -### Limits - -- Max clipboard: **10 MB** (content >1300 bytes is automatically chunked) -- Text only (uses `pbcopy`/`pbpaste`) -- macOS only -- Peers must be on the same LAN - ## Troubleshooting -**Clipboard doesn't sync** -- Run `clipx peers` — are peers showing as ● online? -- Run `clipx` in foreground on both machines and watch logs -- Check IPs: `ifconfig en0 | grep inet` +**Clipboard doesn't sync** — Run `clipx peers` to check connectivity. Run `clipx` in foreground on both machines to see logs. -**"address already in use"** -- Another clipx is already running: `pkill clipx` then retry -- Or: `clipx uninstall` then `clipx install` +**"address already in use"** — Another instance is running. `pkill clipx` or `clipx uninstall && clipx install`. -**Firewall blocking** -- Re-run `clipx install` — it adds the firewall exception -- Or manually: `sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp $(which clipx)` +**Firewall blocking** — Re-run `clipx install` or manually: `sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp $(which clipx)` -**Logs** -- LaunchAgent logs: `~/Library/Logs/clipx.log` -- Live tail: `tail -f ~/Library/Logs/clipx.log` +**Logs** — `tail -f ~/Library/Logs/clipx.log` ## Contributing -Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. +Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md). ## License