Clipboard history for Linux and macOS — everything you've copied, one shortcut away,
with end-to-end-encrypted sync between your machines.
Install · Features · Shortcuts · Device sync · Troubleshooting · Security
Press a global shortcut and a strip of tiles slides up from the bottom of the screen showing everything you've recently copied — text, images, and files. Click a tile, or press Enter, to load it back onto the clipboard.
It stays out of your way: a paperclip in the menubar or system tray, never in the dock. And because the same history engine runs on both platforms, an end-to-end-encrypted LAN sync keeps your clipboard with you as you move between machines — including between Linux and macOS.
Linux — Wayland / COSMIC. Custom tabs, type badges, and the shortcut bar along the bottom.
macOS — the same panel, showing clips that synced over from the Linux machine above.
The same panel and history engine run on both platforms:
- Bottom panel of tiles for recent text, images, and files, summoned by a global shortcut and dismissed by clicking away or pressing Esc.
- Previews — images show inline; files and documents show a type tile, with thumbnails for images, videos, PDFs and docs.
- Pin items so they survive pruning and sort first; search by typing; keyboard navigation and quick-select.
- Tabs — Recent, ★ Pinned, and your own colored, named custom tabs; pin a clip and choose which tab it lands in.
- Type filter — show only one kind at a time (text / image / video / audio / PDF / spreadsheet / archive / other), with per-type colored badges.
- Plain vs. rich paste — strip formatting, or keep it when the source had it. Right-click a tile to copy as plain text.
- History retention — keep for 1 day / 1 week / 1 month / 1 year / forever, with automatic pruning, plus a manual Clear history.
- Live updates — an open panel refreshes as new clips arrive, including ones pushed from a synced peer.
- Follows the OS light/dark theme automatically.
- Encrypted LAN sync (opt-in) — share the clipboard across paired devices: text, images, and any file, with previews and a size cap you control.
- Local-only storage — SQLite + files under your data dir; nothing leaves your machine unless you pair devices for sync.
- Diagnosable —
clippy typesshows what the clipboard is actually offering and how Clippy would file it;CLIPPY_DEBUG=1traces every capture and restore. See When a clip won't paste.
Each platform integrates natively:
- Linux: a system-tray paperclip, automatic COSMIC shortcut binding, and a full-screen click-away overlay.
- macOS: a menubar paperclip, QuickLook thumbnails, and the icon of the app each clip came from on every tile (a Wayland security boundary makes that last one macOS-only — see Limitations).
Built for Wayland — developed on Pop!_OS 24.04 + COSMIC, and also works on Sway, Hyprland, and other wlroots compositors.
Ubuntu / Pop!_OS / Debian — APT repository (recommended) — add the repo once,
then install and get updates with apt like any system package:
curl -fsSL https://davidboulay.github.io/Clippy/clippy.gpg | sudo tee /usr/share/keyrings/clippy.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/clippy.gpg] https://davidboulay.github.io/Clippy ./" | sudo tee /etc/apt/sources.list.d/clippy.list
sudo apt update && sudo apt install clippyNew versions then arrive with sudo apt upgrade. The repo is GPG-signed and
served over GitHub Pages.
Or grab the .deb directly from the
Releases page:
gh release download --repo davidboulay/clippy --pattern '*.deb'
sudo apt install ./clippy_*.debapt pulls in the dependencies. Then launch Clippy from your app list, open
Settings, and bind a shortcut (see below).
Other distributions
- Arch / Manjaro:
cd packaging/arch && makepkg -si - AppImage (experimental, any distro):
make appimage, then rundist/Clippy-*.AppImage - From source:
git clone … && cd clippy && ./scripts/install.sh— installs deps, a~/.local/bin/clippylauncher + icon, enables autostart, and starts the daemon. Build a.debinstead withmake deb(seepackaging/README.md). - Flatpak / COSMIC Store: ❌ not viable — COSMIC withholds the privileged
layer-shellanddata-controlWayland protocols from Flatpak-sandboxed apps, which Clippy requires. Details inFLATHUB.md.
Dependencies (handled by the .deb): wl-clipboard, python3-gi,
gir1.2-gtk-3.0, gir1.2-gtklayershell-0.1, libgtk-layer-shell0,
gir1.2-ayatanaappindicator3-0.1, libayatana-appindicator3-1, pipewire-bin,
plus python3-nacl + python3-zeroconf + python3-spake2 for sync (poppler-utils for PDF
thumbnails — pulled in via Recommends; ffmpeg optional for video thumbnails;
xclip optional — a fallback for reaching XWayland apps when the GTK 4 owner
can't start).
Open the tray icon → Settings (or the ⚙ in the panel), click the shortcut
button, and press your combo (e.g. Super+V). Clippy writes
the COSMIC binding for you, keeping a backup of your existing shortcuts. To do it
from the terminal: clippy setup-shortcut.
Tray not showing? Ensure COSMIC's Status Area / applet is on your panel. Either way, the panel's ⚙ opens Settings and the shortcut still works.
A native menubar app; the panel opens on ⌘+⇧+V
(no Accessibility permission needed — the hotkey uses Carbon RegisterEventHotKey).
Grab Clippy-<ver>.dmg from the
Releases page and
drag Clippy to Applications. Or build it yourself on a Mac:
git clone https://github.com/davidboulay/clippy.git && cd clippy
./packaging/macos/build-app.sh --dmg # → dist/Clippy.app and dist/Clippy-<ver>.dmgThe build is ad-hoc signed (no Apple Developer ID), so the first launch needs
right-click → Open once (or xattr -dr com.apple.quarantine /Applications/Clippy.app).
See packaging/macos/README.md.
Updating: open Settings → Check for updates. When a newer release exists
the button becomes Download <version> — clicking it downloads the new
.dmg, swaps the app in place, and relaunches automatically (no manual
re-download or drag). The Linux .deb updates the same way via Settings →
Check for updates.
| Action | Linux | macOS |
|---|---|---|
| Open / close the panel | your COSMIC shortcut | ⌘+⇧+V |
| Search history | type | type |
| Move between tiles | ←/→/↑/↓ | ←/→ |
| Copy selected & close | Enter | Enter |
| Copy the Nth tile | Ctrl+1…9 | ⌘+1…9 |
| Pin / unpin selected | Ctrl+P | ⌘+P |
| Delete selected | Delete | ⌘+⌫ |
| Close | Esc / click away | Esc / click away |
| Copy as plain text | right-click tile | right-click tile |
Selecting a tile sets the clipboard — then paste with Ctrl/⌘+V yourself (Clippy never injects keystrokes).
Most clipboard sync is either locked to one ecosystem or routed through a cloud:
- Apple Universal Clipboard only works between Apple devices signed in to the same Apple ID, over Continuity.
- Cloud clipboard managers copy your clipboard — passwords, tokens, snippets — onto someone else's servers.
Clippy's sync is local-first and end-to-end encrypted: devices talk directly to each other on your own network, with no cloud and no account. It's built for the setups Universal Clipboard can't cover:
- A Linux machine and a Mac (or any mix) — copy on one, paste on the other.
- Two Apple devices on different Apple IDs — e.g. a work Mac and a personal Mac — where Continuity won't bridge them.
- Anyone who simply doesn't want clipboard contents leaving their LAN.
If all your devices are Apple and share one Apple ID, you may not need this — Continuity already does it. Clippy's sync is for everyone else.
- What syncs: text, images, and any file (video, PDF, …). Files arrive as the real file (right name + type); images and videos show a preview tile.
- Discovery: automatic via mDNS (zeroconf); falls back to a manual IP if your network blocks multicast.
- Security: each device has a long-term X25519 identity key (stored
0600). Pairing runs SPAKE2 (a password-authenticated key exchange) keyed by a 6-digit code — neither side transmits the code or anything an eavesdropper could crack, and a wrong code cannot complete pairing, so a man-in-the-middle can't slip in a key. Every payload is encrypted + authenticated with NaCl. Only paired devices are accepted, and traffic never leaves the LAN. Trust is keyed to the stable identity key, so it survives a device's id changing. - Device status: a green/grey bulb per paired device shows real reachability (an active check, with a "checked Ns ago" note), on both platforms.
- Size cap: default 512 MiB, raise up to 2 GiB (enforced on both ends). Transfers over ~5 MiB show a progress bar on the sender.
- Turn on sync: Linux — Settings → Sync, then restart Clippy; macOS — Settings → Device sync.
- On one device: Show pairing code (Linux CLI:
clippy pair). - On the other: Enter code (Linux CLI:
clippy pair <code>, orclippy pair <code> <ip>if multicast is blocked).
That's it — the macOS Device sync pane and clippy peers list paired devices,
and either side can unpair later (which clears the pairing on both).
Updating from a pre-1.5.2 build? The pairing protocol changed for the SPAKE2 security fix, so existing pairings are disabled and shown as "re-pair required" — re-pair once (both devices on 1.5.2) to resume syncing.
Same portable core (history, clipboard I/O, sync) on both platforms; the panel and OS integration use each platform's native mechanisms. macOS lets an app float a window over everything and grab a global hotkey; Wayland — by design — does not, so the Linux side uses the native Wayland protocols.
| Need | Linux (Wayland) | macOS |
|---|---|---|
| Panel pinned to the screen edge | wlr-layer-shell (gtk-layer-shell) |
borderless NSPanel at pop-up-menu window level |
| Watch the clipboard | wl-paste --watch (ext-data-control) |
NSPasteboard polling |
| Global hotkey | a COSMIC custom shortcut running clippy toggle |
Carbon RegisterEventHotKey (⌘⇧V) |
| Menubar / tray presence | StatusNotifierItem (Ayatana AppIndicator) | NSStatusItem |
| UI toolkit | GTK 3 (PyGObject) | AppKit (PyObjC) |
| Theme | reads COSMIC's is_dark |
NSAppearance light/dark |
| Storage | SQLite + files under ~/.local/share/clippy |
same (shared core) |
On Linux, one binary plays several roles:
clippy daemon # tray + overlay panel + IPC server + clipboard watcher
clippy toggle # tiny client → tells the daemon to open/close (your shortcut runs this)
clippy settings # open the settings window
clippy _store # internal: wl-paste runs this on every clipboard change
The shortcut → toggle → Unix socket → daemon path is what lets a global key
open the panel without any forbidden hotkey grab. On macOS the menubar app hosts
the panel directly.
Everything stays local under ~/.local/share/clippy (Linux) / the app's data
dir (macOS):
-
history.db— SQLite history (text + rich html inline) -
images/,files/,received/,thumbs/— copied images, file payloads, files received from peers, and cached previews -
identity.key(0600) +peers.json— sync identity key and trusted devices -
copy.wav— synthesized copy sound -
tabs.json,device-id,sync.log— custom tabs, this device's stable id, and a log of sync deliveries -
debug.log— only written when diagnostics are switched on (see below)
Preferences live in settings.json (Linux: ~/.config/clippy/), edited via the
Settings window. Fixed limits/geometry are in clippy/config.py. On Linux,
./scripts/uninstall.sh --purge removes everything, and a backup of your COSMIC
shortcuts is kept the first time Clippy edits them.
Clipboard problems on Wayland are timing- and compositor-dependent, so guessing is expensive. Two things answer most questions:
clippy types # what the clipboard is offering right now, and how Clippy
# would file it — plus who owns the X11 selectionCLIPPY_DEBUG=1 clippy daemon # trace every capture, publish and release to
# ~/.local/share/clippy/debug.log(debug_log in settings.json does the same thing permanently.) A bug report
with a few lines of that trace is worth far more than a description of the
symptom.
Two things are worth knowing before reading the output:
- An empty X11 result is usually normal. The compositor exports the
selection to X11 only while an X11 window has keyboard focus, and gates reads
the same way — so an unfocused probe (including
xclipfrom a terminal) sees nothing even when the clipboard is perfectly healthy. It means "not exported yet", not "broken". - Some compositor bugs aren't Clippy's to fix. cosmic-comp corrupts clips of
256 KiB or more when proxying them from X11 to Wayland; Clippy works around it
by writing the Wayland selection itself. See
docs/cosmic-comp-clipboard-bug.mdfor the analysis and a standalone reproducer.
- No auto-paste. Selecting a tile sets the clipboard; you press Ctrl/⌘+V yourself.
- Plain/rich. A restored rich clip is offered as
text/htmland as plain text simultaneously, so apps that only ask for plain targets paste correctly without you having to pick "Copy as plain text". Setalways_plain_textin Settings if you'd rather formatting were never restored. - Linux needs a compositor with
wlr-layer-shellandext-/wlr-data-control(COSMIC, Sway, Hyprland); a plain GNOME Wayland session lacks layer-shell. The panel appears on the active output. - Source-app icons are macOS-only. macOS records the frontmost app at copy time and shows its icon on each tile. Wayland deliberately denies apps any way to query the active/foreground window or its app id (a security boundary), and there is no COSMIC portal for it — so this can't be supported in a Wayland session. The Linux tiles show the clip's type badge instead.
- macOS builds are ad-hoc signed (no Developer ID), so Gatekeeper shows "unidentified developer" on first launch.
clippy/
cli.py subcommand dispatch (Linux)
daemon.py AppController: tray + panel + settings + IPC + retention
panel.py Linux overlay panel, tiles, context menu, plain/rich paste
settings_window.py Linux settings UI + shortcut capture
tray.py AppIndicator tray icon
mac_app.py macOS menubar app + global hotkey
mac_panel.py macOS clipboard-history panel (tiles, tabs, filter, QuickLook)
mac_settings.py macOS settings window
mac_source.py macOS per-clip source-app map (Wayland has no equivalent)
tabs.py shared custom tabs store (tabs.json) · mac_tabs.py alias
clip_types.py shared clip type classifier (label + key + icon)
capture.py read clipboard → storage (+ sound, retention)
clipboard.py backend dispatch (text/image/file read + write)
backends/ per-OS clipboard: wayland.py (wl-*) + mac.py (NSPasteboard)
x11clip.py persistent X11/XWayland selection owner (multi-flavor)
richtext.py html → plain text, for clips carrying only markup
debuglog.py opt-in capture/publish/release trace (CLIPPY_DEBUG=1)
notify.py fire-and-forget desktop notifications
updates.py GitHub release check + in-app update · mac_update.py
sync.py encrypted LAN sync engine (mDNS, pairing, streamed media)
progress.py sender transfer-progress window (large media)
storage.py SQLite history (+ html column, files, time retention)
settings.py JSON preferences
theme.py COSMIC light/dark → generated GTK CSS
sound.py synthesize + play the copy sound
setup.py autostart + COSMIC shortcut editing
ipc.py Unix-socket control channel
config.py paths & limits
packaging/ deb · arch · appimage · macos (py2app) builders
scripts/ install/uninstall + the test suite (scripts/README.md)
docs/ screenshots · cosmic-comp-clipboard-bug.md
MIT

