Room key derived, never transmitted
HKDF-SHA256 mixes SHA256 of the room code with a device-ID salt into a 32-byte key on each client, so the relay forwards traffic it cannot decrypt.
Small, sharp, no nonsense.
Syncs clipboard text and files up to 50 MiB between your own machines through a stateless relay that only ever forwards XChaCha20-Poly1305 blobs, keyed by a shared room code the relay never sees.
./cliprelay --runUtility · Rust, eguiready, 0 errors · offlineHKDF-SHA256 mixes SHA256 of the room code with a device-ID salt into a 32-byte key on each client, so the relay forwards traffic it cannot decrypt.
Each message uses a nonce of SHA256(sender_device_id)[0..16] plus a monotonic little-endian counter, and receivers reject duplicate or stale counters to block replays.
The relay keeps only in-memory room membership and forwards opaque 300 KiB WebSocket frames, chunking files up to 50 MiB and persisting nothing, so offline clients simply miss messages.
Copying a link on one machine and pasting it on another shouldn’t need email or a cloud account. ClipRelay syncs clipboard text and small files between your own devices through a relay, but the relay only ever sees encrypted blobs — devices share a room key derived from a room code, and without it the contents stay unreadable. Pick a room code, share it with your other machines, and your clipboard follows you.
| Component | Purpose |
|---|---|
cliprelay-core | Shared framing, cryptography, room logic, and limits |
cliprelay-relay | WebSocket relay server |
cliprelay-client | Windows tray client |
Room key derivation:
Encryption:
The relay does not know the room key and does not store message history. Offline clients miss messages.
| Resource | Limit |
|---|---|
| Clipboard text | 256 KiB |
| File transfer | 50 MiB |
| Relay frame | 300 KiB |
| Devices per room | 10 |
Files larger than the frame limit are chunked.
Tray status:
| Colour | Meaning |
|---|---|
| Red | Disconnected or cannot reach relay |
| Amber | Connected but room key is not ready, often only one device in the room |
| Green | Connected and ready to send/receive |
--background.Ctrl+Alt+C.%LOCALAPPDATA%\ClipRelay\config.json.Downloads\ClipRelay.cargo run -p cliprelay-relay -- --bind-address 0.0.0.0:8080
Endpoints:
/ws for WebSocket clients/healthz for health checkscargo run -p cliprelay-client -- --server-url wss://relay.example.com/ws --room-code my-secret-room --client-name Laptop
If launched without --room-code, the client shows a room choice dialog.
Download or build the relay binary, then use the included installer:
sudo ./deploy/install-relay-systemd.sh \
--binary ./cliprelay-relay \
--bind-address 127.0.0.1:8080
The installer creates a dedicated cliprelay user, writes /etc/cliprelay/relay.env, installs the binary under /opt/cliprelay/bin, installs a hardened systemd unit, enables it, and starts the service.
Verify:
sudo systemctl status cliprelay-relay
curl http://127.0.0.1:8080/healthz
Typical Caddy TLS proxy:
relay.example.com {
handle /healthz {
respond "ok" 200
}
handle /ws* {
reverse_proxy 127.0.0.1:8080
}
}
Clients then connect to wss://relay.example.com/ws.
git clone https://github.com/Swatto86/ClipRelay.git
cd cliprelay
cargo check
cargo test -p cliprelay-core
cargo test -p cliprelay-relay --test e2e_relay
cargo test -p cliprelay-client
Build the relay:
cargo build --release -p cliprelay-relay
Check relay URL, TLS certificate, firewall, and whether /ws is proxied correctly.
Usually only one device is present or room key negotiation has not completed. Start a second client with the same room code.
Confirm file size is below 50 MiB and both clients remain online during the transfer.
If Auto apply is off, accept the popup manually. Also check OS clipboard permissions and whether another clipboard manager overwrote the value.
https://github.com/Swatto86/ClipRelayhttps://github.com/Swatto86/ClipRelay/releases