Cpynet a pastebin you talk to with curl, that forgets everything you send it
A zero-dependency, single-file Go pastebin built for terminals - burn-after-read by default, two independent encryption layers, and a curl one-liner instead of a login form. I keep ending up in situations where I need to move a small piece of text - a log snippet, a password, a container's stdout - from one machine to another, and the clipboard just isn't there. SSH session on a remote box. A locked-down corporate laptop that won't let me touch the OS clipboard at all. A container with no shared volume and no browser. Slack is right there, but pasting a database password into a channel that's archived forever is a special kind of bad idea. So I built CPYNET - a paste-sharing tool with exactly one interface that matters: curl . echo "hello world" | curl --data-binary @- https://cpynet.com/ # https://cpynet.com/482913 curl https://cpynet.com/482913 # hello world That's the whole thing. No account, no API key, no clicking around. Two curl calls and you've moved text between two machines that have nothing in common except a network path. Burn-after-read, actually The paste above is gone the instant that second curl runs. Not "gone in 24 hours" - gone the moment it's read, whether that's one second later or one minute later. Read it twice (even from the same machine) and the second request gets a plain 404 . It also auto-expires on a timer (2 minutes by default) even if nobody ever reads it, so an unread secret doesn't just sit there. None of this lives on disk. It's a Go map behind a mutex, in memory, for the lifetime of one process. Restart the server and every paste that hasn't been read yet is just... gone. That's not a limitation I'm working around - it's the actual point. A "burn after read" tool that persists to disk somewhere you're not thinking about isn't really burning anything. The shell functions, if you don't want to remember the curl flags curl -s https://cpynet.com/install.sh -o install.sh && bash -n install.sh && . install.sh That wires up two functions, cpy and pst : $ journalctl -u myservice -n 200 | cpy 482913 curl https://cpynet.com/482913 # on the other machine $ pst 482913 Aug 08 14:02:11 myservice[1823]: connection refused: db.internal:5432 ... pst also drops the text on your clipboard if it can find a way to (pbcopy /wl-copy /xclip /xsel /clip.exe ) - pure convenience, never required. Both functions are plain shell, both talk to the exact same HTTP endpoints the web UI uses. There's no separate "API" to keep in sync with the "real" product - the curl pipe is the product. Two encryption layers, for two different threat models Password protection (?p= / cpy -password=xxx ) is AES-256-GCM with a key derived via PBKDF2 (210k iterations - OWASP's current minimum). The server decrypts the text for a moment to verify the password, then re-encrypts it at rest. This is "I trust the server, I just don't want it sitting there in plaintext" - protects against a disk dump or a backup leak, not against the server operator. True end-to-end encryption (the shield icon, or cpy -e ) is a different thing entirely: the key is generated client-side and never sent to the server at all. On the web it's WebCrypto AES-256-GCM with the key living in the link's #key= fragment - browsers never send fragments to a server, by spec, so it physically can't leak that way. On the CLI it's openssl enc with a random passphrase printed on its own line, -aes-256-cbc -pbkdf2 deliberately never appended to the URL. Two independent implementations, neither required to interoperate with the other, both landing on the same guarantee: the server only ever sees ciphertext. What actually makes this "zero dependency" go.mod has one line: go 1.22 . No router package, no ORM, no crypto library beyond crypto/* in the standard library, no frontend build step - the HTML/CSS/JS are Go string constants rendered with html/template , and static assets like screenshots are //go:embed ded straight into the binary. The whole server is one main.go file. That's not a purity flex. It means: - You can read the entire thing in an afternoon. No dependency tree to audit, no transitive CVE to track down at 2am. - docker build produces one static binary ondistroless . No shell in the final image, no package manager, nothing to exploit even if something got in. - It doesn't rot. There's no npm audit running against a pile of frontend packages six months from now. A few other things it does - QR code for the link - scan it with a phone camera, it opens and decrypts right in the browser. Generated by a small dependency-free QR encoder that runs entirely client-side; nothing gets sent to a third party to render a code. - Live "it was just read" notification via Server-Sent Events - the tab that created the paste knows the instant it's gone, no polling. - Small file uploads - a text-file-only allowlist (plus a magic-byte check on the decoded bytes, so a renamed .exe claiming to be a.txt gets rejected too), downloaded back with the original filename and a sniffed content type. - A netcat fallback ( nc host port < file ) for environments too minimal to havecurl at all - a scratch container, say. - Per-IP rate limiting on both writes and reads - the read limit is what actually keeps a 6-digit code space safe against brute-forcing. - The web UI itself is optional. It's there because sometimes you don't want a terminal - but the terminal path was never the fallback. Why not just use pastebin / termbin / privnote? Short version, longer one's on the site: pastebin and termbin don't burn after read by default (your paste sits there until you delete it, or forever); privnote does burn after read but has no CLI story at all - it's a web form, full stop. CPYNET is built specifically for the "I'm already in a terminal and don't want to leave it" case, with burn-after-read as the default rather than an opt-in. Try it curl -s https://cpynet.com/install.sh -o install.sh && bash -n install.sh && . install.sh echo "it works" | cpy Feedback, issues, and "here's a threat model you didn't think about" are all welcome. Top comments (0)
Comments
No comments yet. Start the discussion.