Pipe anything. Get a link.
pastes turns any command's output into a shareable paste. It is small and quick
to install, prints only the URL on stdout so it composes with other tools, and signs in over
SSH on machines with no browser.
./deploy.sh 2>&1 | pastesnpm i -g pastespastes loginMIT, on GitHubGet the CLI
Requires Node.js 20 or newer. It is a small download that pulls in nothing else, and there is nothing to configure afterwards.
# Install it globally
npm install -g pastes
# ...or run it without installing
npx pastes --helpHitting a permissions error? On macOS and Linux a global install writes
to a system directory, so it fails unless you prefix it with sudo. You can
avoid that entirely: npx pastes installs nothing, and Node version managers
such as nvm, fnm, or Volta keep global packages inside your home directory. On Windows
the default location is already in your user profile, so no elevation is needed.
Signing in
pastes login shows a short code that you approve in any browser — your
laptop, your phone, whatever is nearest. Because the code is displayed rather than typed
into a browser on the same machine, this works over SSH on a server with no desktop.
$ pastes login
Your code: ABCD-2345
Approve at: https://pastes.io/cli
Waiting for approval... Logged in as [email protected]
Key saved to ~/.config/pastes/config.jsonApproving creates an API key and hands it to the terminal that asked for it. The key appears as Pastes CLI in your profile, where you can revoke it at any time. Codes expire after ten minutes and can only be used once.
Only approve a code you started yourself. Anyone holding a code you approve gets an API key that can create, read, and delete pastes on your account.
Read the code before you trust it
pastes is open source under the MIT licence. That matters more here than it
might elsewhere: the CLI stores an API key on your machine, and you should not have to
take our word for how it handles that. The whole thing is a few hundred lines you can
read in one sitting.
Auditable
See exactly where your key is written, what is sent to pastes.io, and that nothing goes anywhere else.
Verifiable builds
Every release is published by a GitHub Actions workflow with npm provenance, so the package you install is traceable to the commit that built it.
Yours to change
MIT licensed, so fork it, script around it, or vendor it into your own tooling. Issues and pull requests are welcome.
Running without a login
In CI, containers, and cron jobs there is nobody to approve anything. Set PASTES_API_KEY instead and skip the login entirely.
# CI, containers, cron — no login step, no browser
export PASTES_API_KEY=your-api-key
pastes < build.logThe key is read from --api-key first, then PASTES_API_KEY, then
the stored config file. The environment variable always wins over a stored key, so a
production box can override a stale login without touching disk. Create keys in your profile.
Piping command output
Anything on stdin becomes a paste. With no title given, the first meaningful line of the input is used, which is usually the error you care about.
# Anything on stdin becomes a paste
./deploy.sh 2>&1 | pastes
kubectl logs pod-1 | pastes -t "pod-1 crash" -s logs
docker compose logs --tail 500 | pastes -e 1D
git diff | pastes -s diffPasting files
Pass a path and the title comes from the filename, while the syntax is inferred from its
extension — .py becomes Python, .log becomes Logs, and so on.
Both can be overridden.
# Title and syntax are inferred from the filename
pastes server.py
pastes nginx.log --expire 1D
pastes secrets.env --password hunter2
# ...or set them yourself
pastes dump.txt --title "Prod dump" --syntax sqlReading and managing pastes
pastes get frosty-mole-8821 # print content to stdout
pastes get frosty-mole-8821 > out.txt # save it to a file
pastes ls # your most recent pastes
pastes ls "crash" # search them
pastes rm frosty-mole-8821 # delete one you own
pastes whoami # which account is this key on?pastes ls prints tab-separated slugs and titles, so pastes ls | cut -f1 gives you a plain list of slugs to feed into other
commands.
It composes
The paste URL is the only thing written to stdout. Progress messages, warnings, and errors all go to stderr, so you can capture the URL without parsing anything out of it.
# Only the URL goes to stdout, so it composes
url=$(pastes < error.log)
curl -X POST "$SLACK_WEBHOOK" -d "{\"text\":\"Build failed: $url\"}"
# Everything else goes to stderr, so this stays clean
pastes < error.log 2>/dev/null | pbcopyWhen stdout is a terminal the URL is also copied to your clipboard. In a pipeline it is
not, since you are already consuming the output. Use --no-copy to turn that
off entirely.
Commands
| Command | Description |
|---|---|
pastes [file] | Create a paste from a file, or from piped stdin. |
pastes login | Connect this machine to your account. |
pastes logout | Forget the stored API key. |
pastes whoami | Show which account the current key belongs to. |
pastes get <slug> | Print a paste's content to stdout. |
pastes ls [query] | List your pastes, optionally filtered by a search query. |
pastes rm <slug> | Delete a paste you own. |
Options
| Option | Description |
|---|---|
-t, --title <title> | Paste title. Defaults to the filename, or the first line of input. |
-s, --syntax <slug> | Syntax highlighting. Defaults to the syntax inferred from the filename, else txt. |
-e, --expire <code> | 10M, 1H, 1D, 1W, 2W, 1M, 6M, 1Y, N, SD. Defaults to 1M. Free accounts must
use 1M. |
-p, --password <pw> | Protect the paste with a password. |
--no-copy | Do not copy the URL to the clipboard. |
--api-key <key> | Use this key for one command, ignoring the environment and config. |
--api-url <url> | Point at a different host. Useful for local development. |
--json | Print the raw JSON response instead of the URL. |
-q, --quiet | Suppress non-essential stderr output. |
-h, --help | Show the full built-in reference. |
Environment variables
| Variable | Effect |
|---|---|
PASTES_API_KEY | API key to use. Overrides the stored config. |
PASTES_API_URL | API base URL. Defaults to https://pastes.io. |
PASTES_CONFIG_HOME | Directory holding config.json. Defaults to ~/.config/pastes, or %APPDATA%\pastes on Windows. |
PASTES_NO_BROWSER | Never launch a browser during login; only print the link. |
Exit codes
0Success.1Request failed — network error, rejected key, rate limit.2Usage error — bad flag, no input, no API key configured.Quotas are shared with the HTTP API. Free accounts have per-window and daily creation limits plus a daily cap on reads and searches; Pro raises the creation limits and removes the read cap. See limits and quotas or pricing.
Use it as a library
The same package exports a typed client, so a Node script can create pastes without shelling out.
import { PastesClient } from 'pastes';
const client = new PastesClient({
apiUrl: 'https://pastes.io',
apiKey: process.env.PASTES_API_KEY
});
const { success } = await client.createPaste({
title: 'Build log',
content: log,
syntax: 'logs'
});
console.log(success.paste_url);The CLI is open source and MIT licensed — view it on npm or read the source on GitHub. Prefer plain HTTP? See the API documentation.