Your clipboard, everywhere. A Rust desktop app, terminal UI, and CLI for sharing plain-text clipboards between trusted macOS and Linux computers. Browse saved copies, manage devices, and pause syncing from either companion.
Experimental. Plain-text sync is working in a macOS ↔ Arch Linux setup, with authenticated LAN connections and automatic reconnection after service restarts verified. Broader desktop compatibility, sleep/wake, and login/logout recovery are still being validated.
The running macOS app, showing sample clipboard history in an isolated test setup.
ClipSync uses direct connections, local discovery, Ed25519 device identities, and mutually authenticated TLS 1.3. No cloud service is involved. Devices can connect directly over the LAN without Tailscale or an SSH session.
Start with the desktop and TUI guide, or use the CLI setup instructions below.
The repaired transport uses protocol 2.0.0: update both devices together. It intentionally refuses the old plaintext protocol.
- Plain text up to 5 MiB; automatic polling every 250 ms.
- macOS NSPasteboard, Linux X11, and Wayland compositors supporting data-control through arboard. Arboard can fall back to X11 where available.
- Explicit peer authorization, automatic discovery, bounded reconnect backoff, stale/duplicate event rejection, and remote-update echo suppression.
- The latest local event is retained in memory for newly connected sessions. Starting a daemon does not automatically broadcast pre-existing clipboard contents; use
syncto request that. - AES-256-GCM encrypted history contents. Database metadata is not encrypted. The current retention limit is 20 entries.
- Separate
status,peers, andsynccommands communicate with the daemon through a protected per-user Unix socket.syncreports queued peers, not confirmed remote application.
Images, RTF synchronization, global hotkeys, and primary-selection configuration are not supported by the repaired sync engine. Sensitive-content filtering is a heuristic and can suppress ordinary text; it is not a guarantee that every secret will be detected.
The experimental Rust desktop app and clipsync tui share Overview, Devices,
History, and Settings. History shows actual saved copies, their source and time;
select one to read it and copy it again. The daemon remains the sole clipboard
owner, so closing either interface leaves syncing running.
Overview in the same isolated setup, with the service running and no device connected.
The desktop uses native macOS window controls with a blue/teal interface. Both clients support setup inspection, user-service controls, pause/resume and device authorization. Adding a device uses a temporary LAN enrollment window and full fingerprint comparison on both computers; it needs no Tailscale or SSH session. Discovery alone never authorizes a device.
To build an installable macOS app or Linux desktop package from this checkout,
use Rust 1.95 or newer and just:
just companion-packageThis packages the desktop app with a matching CLI and TUI. Follow the
platform installation steps
to install it and complete setup. On macOS, open ClipSync.app; on Linux,
launch ClipSync from the app menu. Run clipsync tui for the terminal interface.
For a development build without packaging:
cargo +1.95.0 build --locked --workspace --features tui
./target/debug/clipsync-desktop
./target/debug/clipsync tuiExisting configurations are inspected before changes. If multiple configurations conflict, choose one explicitly. See companion setup, packaging and recovery. See verification and release gates for the isolated checks. These are development builds; public macOS signing/notarization and physical login/logout and sleep/wake acceptance remain release gates.
For a CLI-only installation, run the following from the repository checkout on
each device with a current stable Rust toolchain. These commands do not install
the desktop app; add --features tui to the Cargo command to include the TUI.
cargo install --path . --locked
export PATH="$HOME/.cargo/bin:$PATH"
type -a clipsync
clipsync versionCargo installs to ~/.cargo/bin by default. cargo build --release and just release only build; they do not replace an installed executable or restart a service. Older copies in ~/.local/bin or /usr/local/bin can shadow a new installation. Check the executable path and build date on both devices; the version number alone may be unchanged.
On Linux, install the native build dependencies (pkg-config, OpenSSL and X11/XCB development packages). See the disposable test image for a working Debian setup.
Create a dedicated unencrypted Ed25519 identity on each device:
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519_clipsyncClipSync cannot unlock passphrase-protected identities. Keep the dedicated private key owner-readable only. Configure its path; do not replace an existing SSH key.
Example config.toml (use a separate file on each device). Use a dedicated ClipSync authorization file containing only the devices that should receive clipboard contents; do not point it at your general SSH authorized_keys. When updating an existing setup, preserve its history database and key paths.
The commands below use an explicit --config path so the daemon and CLI read the same configuration:
listen_addr = "0.0.0.0:8484"
[auth]
ssh_key = "~/.ssh/id_ed25519_clipsync"
authorized_keys = "~/.config/clipsync/authorized_devices"
[clipboard]
history_db = "~/.local/share/clipsync/history.db"
# Optional: only set for a NEW isolated store. Existing stores retain their old key location.
# history_key = "/absolute/path/to/isolated/history.key"Exchange only the .pub files through a trusted channel. Verify their fingerprints out of band (ssh-keygen -lf peer.pub), then authorize the other device on both sides:
clipsync --config /path/to/config.toml auth add /path/to/peer.pub --name desktop
clipsync --config /path/to/config.toml auth list
clipsync --config /path/to/config.toml start --foregroundThe daemon does not prompt for trust on standard input. Discovery never grants authorization. A non-default port is advertised automatically.
On each device, status and peers should show one authenticated peer for a two-device setup. Then copy ordinary text on the Mac and paste it on Linux, and repeat in reverse. A connected peer confirms the transport; the copy/paste check confirms that the desktop clipboard is working too.
In another terminal, using the same configuration:
clipsync --config /path/to/config.toml status
clipsync --config /path/to/config.toml peers
clipsync --config /path/to/config.toml sync
clipsync --config /path/to/config.toml history
clipsync --config /path/to/config.toml copy "example text"
clipsync --config /path/to/config.toml auth remove 'SHA256:fingerprint-from-auth-list'On Linux, copy requires a running daemon with the same configuration. The daemon
keeps ownership after the CLI exits; success confirms the local clipboard write.
Sensitive-content filtering still controls whether that text enters history or
is forwarded to another device. If the daemon is unavailable, copy fails
without replacing the clipboard.
Authorization is reloaded for new handshakes and checked during active traffic/heartbeats. Removing a key closes its session. Local auth list now uses standard OpenSSH fingerprints; pre-repair trust-cache fingerprints are not used to authorize the daemon.
A macOS LaunchAgent and a Linux systemd user service have been verified together on a real LAN. Both must run as the logged-in desktop user, point at the updated executable, and use start --foreground. The old --daemon argument is unsupported. Automatic reconnection was verified after restarting each service.
After foreground pairing works, stop those foreground instances before installing a background service. The user installer installs to ~/.local/bin and uses ${XDG_CONFIG_HOME:-~/.config}/clipsync/config.toml. Put the validated configuration there first, preserving any existing history paths. From the checkout, use the binary just installed by Cargo:
CLIPSYNC_BINARY_SOURCE="$HOME/.cargo/bin/clipsync" ./scripts/install_user.shOn macOS the installer writes and starts ~/Library/LaunchAgents/com.clipsync.plist. On Linux it enables ~/.config/systemd/user/clipsync.service for the graphical session; start it from your logged-in desktop:
systemctl --user start clipsync.service
systemctl --user status clipsync.serviceUse clipsync --config /path/to/config.toml status and peers with the service's actual config path. Config resolution prefers ${XDG_CONFIG_HOME:-~/.config}/clipsync/config.toml and also inspects the legacy macOS ~/Library/Application Support/clipsync/config.toml. Conflicting files require an explicit choice; identical duplicates select the canonical XDG path. An explicit --config keeps service and client commands on the intended configuration.
Back up its configuration, history database and history key before migrating. Installing a new binary does not update an existing unit or LaunchAgent automatically.
- macOS: unload an already loaded
com.clipsyncLaunchAgent before using the installer. Wait for it to exit; the installer can then load the replacement with the correct executable, configuration and foreground arguments. - Linux: inspect
systemctl cat clipsync.serviceandsystemctl --user cat clipsync.service. If an obsolete system/root service exists, retire it withsudo systemctl disable --now clipsync.service. Stop the old user service before reinstalling it, then start the replacement withsystemctl --user start clipsync.service. A missing executable inExecStartcan cause a continuous restart loop. - Both: ensure the intended install directory precedes old copies in
PATH, open a new terminal, and checktype -a clipsyncandclipsync versionagain. Service executable paths must also be correct; shellPATHalone does not update them.
Check both daemons with status, confirm that each dedicated authorization file contains the other device's public key, and verify that the services use the same config files as the CLI. Local discovery uses mDNS (UDP 5353); the configured listener uses TCP 8484 by default. Guest Wi-Fi isolation or a firewall can allow discovery while blocking the connection. ClipSync does not modify firewall rules.
Refreshing the Mac service restored discovery during one installed-device check; its cause remains unconfirmed. If discovery stalls, restart the services and inspect peers again. This is a troubleshooting step, not evidence that sleep/wake recovery is fully validated.
Never delete or replace an existing history key to fix an error. An unreadable, malformed, missing, or insecurely permissioned key causes initialization to fail without replacing the key or database. Fix its permissions or restore the original key from backup. Back up the key together with the database. See security and compatibility.
just check # formatting and Clippy
just test # unit/integration tests; private named pasteboards on macOS
just test-desktop # Docker: Linux suite plus two real X11 and two headless Wayland daemonsThe desktop harness refuses to run outside its disposable container. It generates temporary identities and history stores and does not mount personal configuration or clipboard sockets.
See repair validation for evidence and limits. Dual licensed under MIT or Apache-2.0.

