pbgopy: a clipboard you can paste from another device
Copy and paste between devices
At a glance
- What is it?
- pbgopy is a Go command line tool that behaves like pbcopy and pbpaste across a set of devices, using a small server as the shared clipboard. It adds client-side encryption, a history of entries and a per-entry TTL, and it is at its best on a trusted network or over a tunnel.
- Who is it for?
- pbgopy fits a person or a small team moving text and images between a laptop and a server over ssh, who want history and encryption without setting up a sync service, and it is the wrong tool for a hostile network, since the server is plain HTTP and the only built-in protection is HTTP basic authentication or an encryption flag on the client.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 179 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A clipboard with a server in the middle
pbgopy does the job pbcopy and pbpaste do on a single machine, across several machines instead. The idea is simple enough to describe in one line: one device runs a server that holds the clipboard, the others copy into it and paste out of it, and the data moves over HTTP.
The audience is whoever moves data between machines constantly and finds the usual answer unsatisfying. Copying a URL from a terminal session on a server to a laptop, or moving an image off a headless box, normally means ssh with a terminal multiplexer, or a file transfer, or a messaging app. pbgopy gives you the shell pipeline instead, and it keeps the payload out of the clipboard manager of the OS in between.
It is written in Go, it is a single binary, and the licence is MIT. The repository holds the usual Go layout: a main.go, a commands directory, a crypto directory, a cache directory and a datasize directory, with a Dockerfile, a Makefile and a .goreleaser.yaml for the release plumbing.
The release history is worth noting for a different reason. Versions 0.2.1 and 0.3.0 both came out in December 2020, and then nothing until v0.4.0 on 2026-04-29, which is where the history feature and the encryption options live. The last push to the repository was on 2026-04-29, the same day as that release, so the recent work is a single large step rather than a steady stream.
The server holds the clipboard, the client moves the bytes
There are two roles, and the split is what makes the tool easy to reason about.
The server is started with pbgopy serve and listens on port 9090 by default. The documentation is explicit that you must allow access to this port for each device you want to share data with, which is the operational reality of the design: this is a listening service on a port, not a peer-to-peer exchange.
The clients are the copy and paste commands. They find the server through the PBGOPY_SERVER environment variable, and they read from standard input and write to standard output, which is what makes it compose with a shell pipeline:
export PBGOPY_SERVER=http://host.xz:9090
pbgopy copy <foo.pngand on the other machine:
export PBGOPY_SERVER=http://host.xz:9090
pbgopy paste >foo.pngThe environment variable is a plain URL, so pointing a client at a different server is a matter of exporting a different value, which is also how you use several independent clipboards.
There is one shortcut for the local case: pbgopy copy -c puts the data currently on the operating system clipboard into the server, so you can copy in the way you are used to and paste everywhere else. The tool reads the local clipboard through a Go clipboard library, and the same flag exists on the paste side, which is not something the documentation spells out but which follows from the flag list.
The copy command also carries a --max-size option with a default of 500mb and a --timeout option with a default of 5s, so a stalled server fails the command rather than hanging the pipeline.
History turns a one-slot clipboard into a list
This is the substantial change in the 0.4 release, and it changes the shape of the tool.
A clipboard by definition holds one thing. pbgopy can now hold a list. The server is started with a larger history limit:
pbgopy serve --history-limit 20Each successful copy creates a new history entry, and paste still returns the latest one by default, so the old behaviour is preserved. What is new is that you can look at what is in the clipboard without dumping its contents, paste a specific entry, and delete history.
$ pbgopy history
ID AGE TYPE SIZE LATEST PREVIEW
fcd0bc9afd9586c4544c377024d4e3b1 4s text/plain; charset=utf-8 6B * "world"
f9db4d7b8befb5d8b86b39e58b695126 9m image/jpeg 1.6MB JPEG image 3144x4192
$ pbgopy paste --id f9db4d7b8befb5d8b86b39e58b695126 >foo.jpgThe listing is the interesting part, because it shows a content type and a size per entry and a preview for text. That means the clipboard is not a byte buffer, it is a set of typed items, and the type of what you copied is visible before you paste it.
Two defaults are worth knowing. The default history limit is 1, which is the old latest-only behaviour, so history is something you switch on rather than something you get. And the limit can be 0 for unlimited in-memory history, which is the mode to be careful with, since everything is held in the server's memory and disappears when it restarts.
Encryption lives on the client commands
The tool has built-in encryption and decryption with several kinds of key, and the design detail that matters is where the flags sit: they belong to copy and paste, not to serve. The data is encrypted on the way in and decrypted on the way out, so the server holds what it was given rather than the plaintext.
With a symmetric key, the -p flag derives the key from a password, and the documentation provides it so you can encrypt and decrypt without previous setup. The caveat is stated plainly: this cannot prevent a dictionary attack. For more safety, a 32-byte symmetric key generated by other means is recommended, passed with the -k flag or with the PBGOPY_SYMMETRIC_KEY_FILE environment variable, both of which point at a key file on disk.
pbgopy copy -k /path/to/pbgopy.key <plaintext.txtWith a public and private key pair, pbgopy uses a hybrid cryptosystem, and this is the mode for devices that have already exchanged public keys:
pbgopy copy --public-key-file /path/to/public.key <plaintext.txtpbgopy paste --private-key-file /path/to/private.key <plaintext.txtThe keys must be RSA in PEM or DER format, which rules out other formats and is the kind of detail that costs an afternoon if you assumed otherwise.
There is also a GPG path, using the -u or --gpg-user-id flag, with the executable located through --gpg-path, which defaults to gpg. That route suits anyone who already manages a keyring, and it hands the key management to GPG rather than to pbgopy.
TTL and basic auth are the server's two controls
The server has a small set of options of its own, and they are the ones that decide how much you trust the network in between.
The --ttl flag sets a time to live for the cache. It applies to each history entry individually, and expired entries are neither listed nor pasteable. The default is 24h, and giving 0s disables expiry entirely:
pbgopy serve --ttl 10mThe design intent is stated as not wanting more data cached on the server than necessary, which is the right instinct for a service that holds whatever you last copied. Note that the TTL governs the history entries, and it is worth being clear in your head about which of your two data paths it covers, since the latest-entry behaviour and the history list are related but distinct.
Authentication is HTTP basic authentication, enabled with the -a flag on the server and repeated on the client side for copy and paste:
pbgopy serve -a user:passpbgopy copy -a user:pass <foo.pngBasic authentication over plain HTTP means the credential travels in a trivially readable form, so it protects against a stray device on the network more than it protects against anyone who can watch the traffic. It is a reason to run this on a trusted network, over a tunnel, or to use the encryption flags and treat the transport as untrusted.
Six ways to install, and a Docker image for scripts
Installation is the best documented part of the project, and there is a route for most systems.
Binary releases are published on the releases page. On macOS, Homebrew installs it as a tap:
brew install nakabonne/pbgopy/pbgopyRHEL and CentOS get an rpm installed with rpm -ivh pointing at the release asset, while Debian and Ubuntu get a .deb fetched with wget and installed with apt install. Arch Linux has an AUR package installed with yay pbgopy, and Nix users run nix-shell -p nixpkgs.pbgopy.
For Go developers the binary is installed with the go tool:
go install github.com/nakabonne/pbgopy@latestAnd there is a container image, which is the route to take in a script or a CI job:
docker run --rm nakabonne/pbgopy pbgopy helpThe Docker image is built from alpine and copies the cross-compiled binary for the target platform into /usr/bin, with the default command set to pbgopy. The .goreleaser.yaml in the repository is what produces the per-platform assets, and the Makefile holds a single test target that runs the Go tests with the race detector and atomic coverage.
The module itself requires Go 1.21, and its dependencies are small and stable: cobra for the command line, the clipboard library, testify for tests, and the x/crypto package. The source layout mirrors the feature set, with separate directories for the commands, the crypto implementations, the cache and the data size handling.
The limits are in the trust model, not the features
What pbgopy is not is a general sync service, and the gaps are all in the same direction.
There is no transport security. The documentation uses plain HTTP URLs throughout, and the only built-in protection is the basic authentication flag and the client-side encryption options. The server is a listening port on a host you have to open to the network, and a copy of your password or a private key pasted into it is stored in that host's memory until it is overwritten or expires.
The history is in memory and unbounded if you ask for it to be, with a 500mb ceiling per entry by default. That combination is fine for a personal clipboard and wrong for a shared server where other people can read the history.
The alternative to all of this is to keep a sync directory, whether a synced folder, a shared network drive or a private git repository, and pipe clipboard data into it. The difference in approach is that the sync approach needs both devices online and a sync client configured, and it gives you versioning for free, while pbgopy gives you a single command with no client state, typed entries, client-side encryption and a history you can query by identifier.
If the data is sensitive and the network is not yours, the sensible order is to use the symmetric key file or the RSA key pair, put the server behind a tunnel, and set a short TTL so nothing sits in memory for a day.
Editorial conclusion
pbgopy fits a person or a small team moving text and images between a laptop and a server over ssh, who want history and encryption without setting up a sync service, and it is the wrong tool for a hostile network, since the server is plain HTTP and the only built-in protection is HTTP basic authentication or an encryption flag on the client. Verify first that port 9090 is reachable from every device you intend to paste from, that your payload fits the 500mb default maximum size, and that your client has a way to store the symmetric key file, since the -p password option is documented as unable to prevent a dictionary attack.
Frequently asked questions
How do I set up pbgopy between two devices?
Start the server with pbgopy serve, which listens on port 9090 by default, and allow access to that port from the other device. On each client, set PBGOPY_SERVER to the server address, for example export PBGOPY_SERVER=http://host.xz:9090, then use pbgopy copy and pbgopy paste.
How do I keep a history of previous copies in pbgopy?
Start the server with a larger limit, for example pbgopy serve --history-limit 20. Each successful copy creates a new entry, paste still returns the latest by default, and you can list entries with pbgopy history or paste a specific one with pbgopy paste --id. The default limit is 1, and 0 means unlimited in-memory history.
Does pbgopy encrypt the data it copies?
It can, using the client side flags. A password can be given with -p, a symmetric key file with -k or PBGOPY_SYMMETRIC_KEY_FILE, an RSA key pair with --public-key-file and --private-key-file, or a GPG user id with -u. The documentation warns that the password option cannot prevent a dictionary attack and recommends a 32-byte key from another method.
How long does pbgopy keep clipboard data on the server?
Each history entry has a time to live set with the --ttl flag, the default is 24h, and expired entries are neither listed nor pasteable. Giving 0s disables expiry. The TTL is set on the server, for example pbgopy serve --ttl 10m.
How do I install pbgopy on Linux or with Go?
Debian and Ubuntu use the .deb release asset with wget and apt install, RHEL and CentOS use the rpm asset, Arch has an AUR package, and Go users run go install github.com/nakabonne/pbgopy@latest. A Docker image is also published, used as docker run --rm nakabonne/pbgopy pbgopy help.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nakabonne-pbgopy)Was this useful?