{"id":13526837,"url":"https://github.com/jedisct1/piknik","last_synced_at":"2025-05-14T23:06:17.968Z","repository":{"id":40280755,"uuid":"63753199","full_name":"jedisct1/piknik","owner":"jedisct1","description":"Copy/paste anything over the network.","archived":false,"fork":false,"pushed_at":"2024-12-12T09:26:00.000Z","size":9886,"stargazers_count":2461,"open_issues_count":1,"forks_count":102,"subscribers_count":45,"default_branch":"master","last_synced_at":"2025-04-13T16:11:21.237Z","etag":null,"topics":["clipboard","copy","crypto","paste","security","security-protocol","staging-server","transit"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jedisct1.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-07-20T05:36:18.000Z","updated_at":"2025-04-13T13:49:11.000Z","dependencies_parsed_at":"2023-02-04T06:17:18.775Z","dependency_job_id":"a710e5fe-f9e1-4c64-be4e-35128df57e0e","html_url":"https://github.com/jedisct1/piknik","commit_stats":{"total_commits":230,"total_committers":12,"mean_commits":"19.166666666666668","dds":"0.10434782608695647","last_synced_commit":"b5871a8628622a64c28852715fae1c9ced93355a"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedisct1%2Fpiknik","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedisct1%2Fpiknik/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedisct1%2Fpiknik/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jedisct1%2Fpiknik/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jedisct1","download_url":"https://codeload.github.com/jedisct1/piknik/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254243360,"owners_count":22038046,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clipboard","copy","crypto","paste","security","security-protocol","staging-server","transit"],"created_at":"2024-08-01T06:01:35.727Z","updated_at":"2025-05-14T23:06:12.933Z","avatar_url":"https://github.com/jedisct1.png","language":"Go","funding_links":[],"categories":["Go","security","Tools","Programming Languages","Networking"],"sub_categories":["Go"],"readme":"[![Latest release](https://img.shields.io/github/release/jedisct1/piknik.svg)](https://github.com/jedisct1/piknik/releases/latest)\n[![Build status](https://travis-ci.com/jedisct1/piknik.svg?branch=master)](https://travis-ci.com/jedisct1/piknik?branch=master)\n![CodeQL scan](https://github.com/jedisct1/piknik/workflows/Code%20scanning%20-%20action/badge.svg)\n\n# Piknik\n\nCopy/paste anything over the network!\n\n[[watch a demo on Asciinema](https://asciinema.org/a/80708)] -\n[[download the source code / binaries](https://github.com/jedisct1/piknik/releases/latest)]\n\n![Piknik](https://raw.github.com/jedisct1/piknik/master/piknik.png)\n\nEver needed a copy/paste clipboard that works over the network?\n\nPiknik seamlessly and securely transfers URLs, code snippets, documents, virtually anything between arbitrary hosts.\n\nNo SSH needed, and hosts can sit behind NAT gateways, on different networks.\n\nFill in the clipboard (\"copy\") with whatever comes in to the standard input:\n\n```sh\n$ pkc\nclipboard content\n```\n\nMagically retrieve that content from any other host having Piknik installed with the same configuration:\n\n```sh\n$ pkp\nclipboard content\n```\n\nBoom.\n\nObviously, it can be used to transfer files as well:\n\n```sh\n$ pkc \u003c kitten.gif\n$ pkp \u003e kittencopy.gif\n```\n\n```sh\n$ tar cvf - *.txt | pkc\n$ pkp | tar xvf -\n```\n\nIn order to work around firewalls/NAT gatways, the clipboard content transits over TCP via a staging server.\n\nNothing transits without end-to-end encryption; the server cannot learn much about what the clipboard actually contains.\n\nData can be shared between different operating systems, including MacOS, Linux and Windows.\n\n## Installation\n\n### Option 1: use precompiled binaries\n\nPrecompiled binaries for MacOS, Linux (i386, x86_64, ARM), Win32, Win64, DragonflyBSD, NetBSD and FreeBSD can be downloaded here:\nhttps://github.com/jedisct1/piknik/releases/latest\n\n### Option 2 (on MacOS): use Homebrew\n\n```sh\n$ brew install piknik\n```\n\n### Option 3: compile the source code\n\nThis project is written in Go.\n\nGo \u003e= 1.11 is required, as well as the following incantation:\n\n```sh\n$ go build\n```\n\nThe `piknik` executable file should then be available in current path.\n\n## Setup\n\nPiknik requires a bunch of keys. Generate them all with\n\n```sh\n$ piknik -genkeys\n```\n\nThis generates random keys (highly recommended).\n\nYou will need to copy parts (not all!) of that command's output to a `piknik.toml` configuration file.\n\nA temporary alternative is to derive the keys from a password. The same password will always generate the same set of keys, on all platforms. In order to do so, add the `-password` switch:\n\n```sh\n$ piknik -genkeys -password\n```\n\nThe output of the `-genkeys` command is all you need to build a configuration file.\n\nOnly copy the section for servers on the staging server. Only copy the section for clients on the clients.\n\nIs a host gonna act both as a staging server and as a client? Ponder on it before copying the \"hybrid\" section, but it's there, just in case.\n\nThe default location for the configuration file is `~/.piknik.toml`. With the exception of Windows, where dot-files are not so common. On that platform, the file is simply called `piknik.toml`.\n\nSample configuration file for a staging server:\n\n```toml\nListen = \"0.0.0.0:8075\"         # Edit appropriately\nPsk    = \"bf82bab384697243fbf616d3428477a563e33268f0f2307dd14e7245dd8c995d\"\nSignPk = \"0c41ca9b0a1b5fe4daae789534e72329a93a352a6ad73d6f1d368d8eff37271c\"\n```\n\nSample configuration file for clients:\n\n```toml\nConnect   = \"127.0.0.1:8075\"    # Edit appropriately\nPsk       = \"bf82bab384697243fbf616d3428477a563e33268f0f2307dd14e7245dd8c995d\"\nSignPk    = \"0c41ca9b0a1b5fe4daae789534e72329a93a352a6ad73d6f1d368d8eff37271c\"\nSignSk    = \"cecf1d92052f7ba87da36ac3e4a745b64ade8f9e908e52b4f7cd41235dfe7481\"\nEncryptSk = \"2f530eb85e59c1977fce726df9f87345206f2a3d40bf91f9e0e9eeec2c59a3e4\"\n```\n\nDo not use these, uh? Get your very own keys with the `piknik -genkeys` command.\nEdit the `Connect` and `Listen` properties to reflect the staging server IP and port.\nAnd `chmod 600 ~/.piknik.toml` might not be a bad idea.\n\nDon't like the default config file location? Use the `-config` switch.\n\n## Usage (staging server)\n\nRun the following command on the staging server (or use `runit`, `openrc`, `systemd`, whatever to run it as a background service):\n\n```sh\n$ piknik -server\n```\n\nThe staging server has to be publicly accessible. At the very least, it must be reachable by the clients over TCP with the port you specify in the configuration.\n\nCommands without a valid API key (present in the client configuration file) will be rejected by the server.\n\n## Usage (clients)\n\n```sh\n$ piknik -copy\n```\n\nCopy the standard input to the clipboard.\n\n```sh\n$ piknik -paste\n```\n\nRetrieve the content of the clipboard and spit it to the standard output.\n`-paste` is actually a no-op. This is the default action if `-copy` was not specified.\n\n```sh\n$ piknik -move\n```\n\nRetrieve the content of the clipboard, spit it to the standard output\nand clear the clipboard. Not necessarily in this order.\nOnly one lucky client will have the privilege to see the content.\n\nThat's it.\n\nFeed it anything. Text, binary data, whatever. As long as it fits in memory.\n\n## Suggested shell aliases\n\nWait. Where are the `pkc` and `pkp` commands mentioned earlier?\n\nSample shell aliases:\n\n```sh\n# pko \u003ccontent\u003e : copy \u003ccontent\u003e to the clipboard\npko() {\n    echo \"$*\" | piknik -copy\n}\n\n# pkf \u003cfile\u003e : copy the content of \u003cfile\u003e to the clipboard\npkf() {\n    piknik -copy \u003c $1\n}\n\n# pkc : read the content to copy to the clipboard from STDIN\nalias pkc='piknik -copy'\n\n# pkp : paste the clipboard content\nalias pkp='piknik -paste'\n\n# pkm : move the clipboard content\nalias pkm='piknik -move'\n\n# pkz : delete the clipboard content\nalias pkz='piknik -copy \u003c /dev/null'\n\n# pkfr [\u003cdir\u003e] : send a whole directory to the clipboard, as a tar archive\npkfr() {\n    tar czpvf - ${1:-.} | piknik -copy\n}\n\n# pkpr : extract clipboard content sent using the pkfr command\nalias pkpr='piknik -paste | tar xzpvf -'\n```\n\n## Piknik integration in third-party packages\n\n* The [Piknik package for Atom](https://atom.io/packages/piknik)\nallows copying/pasting text between hosts running the Atom text editor.\n* The [Piknik package for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=jedisct1.piknik)\nallows copying/pasting text between hosts running the Visual Studio Code text editor.\n\n## Use cases\n\nUse it to:\n\n* Securely send passwords, API keys, URLs from one host to another\n* Share a clipboard with your teammates (which can be a lot of fun)\n* Copy data from/to isolated VMs, without the VMWare tools or shared volumes (great for unsupported operating systems and malware sandboxes)\n* Copy files from/to a Windows machine, without Samba or SSH\n* Transfer data between hosts sitting behind firewalls/NAT gateways\n* Easily copy configuration files to multiple hosts\n* Start a slow download at the office, retrieve it later at home\n* Quickly backup a file to the cloud before messing with it\n* ...and more!\n\n## Protocol\n\nCommon definitions:\n\n```text\nk: API key\nek: 256-bit symmetric encryption key\nekid: encryption key id encoded as a 64-bit little endian integer\nm: plaintext\nct: XChaCha20 ek,n (m)\nHk,s: BLAKE2b(domain=\"SK\", key=k, salt=s, size=32)\nLen(x): x encoded as a 64-bit little endian unsigned integer\nn: random 192-bit nonce\nr: random 256-bit client nonce\nr': random 256-bit server nonce\nts: Unix timestamp as a 64-bit little endian integer\nSig: Ed25519\nv: 6\n```\n\nCopy:\n\n```text\n-\u003e v || r || h0\nh0 := Hk,0(v || r)\n\n\u003c- v || r' || h1\nh1 := Hk,1(v || r' || h0)\n\n-\u003e 'S' || h2 || Len(ekid || n || ct) || ts || s || ekid || n || ct\ns := Sig(ekid || n || ct)\nh2 := Hk,2(h1 || 'S' || ts || s)\n\n\u003c- Hk,3(h2)\n```\n\nMove/Paste:\n\n```text\nMove:  opcode := 'M'\nPaste: opcode := 'G'\n\n-\u003e v || r || h0\nh0 := Hk,0(v || r)\n\n\u003c- v || r' || h1\nh1 := Hk,1(v || r' || H0)\n\n-\u003e opcode || h2\nh2 := Hk,2(h1 || opcode)\n\n\u003c- Hk,3(h2 || ts || s) || Len(ekid || n || ct) || ts || s || ekid || n || ct\ns := Sig(ekid || n || ct)\n```\n\n## License\n\n[ISC](https://en.wikipedia.org/wiki/ISC_license).\n\n## Credits\n\nPiknik diagram by [EasyPi](https://easypi.herokuapp.com/copy-paste-anything-over-network/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjedisct1%2Fpiknik","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjedisct1%2Fpiknik","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjedisct1%2Fpiknik/lists"}