Share secrets from the terminal
Quick start
- Send — the secret is read from stdin and encrypted before anything leaves your machine:
You get a share code likecat id_ed25519 | npx shareasecret@0.1.5 send --ttl 2hXKQ2-M7PT-tiger-ocean-cable-ruby-drumand a link. Speak the code over a call or send the link over any channel. (Examples pin the version — see why; check npm for the latest.) - Receive — run it without the code and it prompts with input hidden, keeping
the code out of your shell history;
--outputwrites the file with0600permissions from the first byte:npx shareasecret@0.1.5 receive --output id_ed25519 - Sent the wrong thing? Burn it before anyone reads it —
npx shareasecret@0.1.5 revokeprompts for the code the same way.
Sending
- Pipe the secret in — a file (
cat .env | npx shareasecret@0.1.5 send) or a command's output (op read "op://vault/item/field" | npx shareasecret@0.1.5 send). Text or binary, up to 10 KB. Running it bare and pasting works too, but the pasted secret stays visible in your terminal scrollback — the CLI warns about this; prefer piping. --ttlsets the expiry:90s,30m,2h,1d— minimum 60 seconds, maximum 7 days, default 1 day. The drop is destroyed at first read regardless.- stderr never duplicates the code — when stdout is captured
(
CODE=$(cat key.pem | npx shareasecret@0.1.5 send), a pipe, CI), the code is written to stdout only, and stderr carries just a non-sensitive status line. That makes stdout the single place to control: capture or redirect it explicitly (uncaptured stdout still lands in CI logs), disable shell tracing (set -xprints assignments), and mask the captured value in your CI system. The full pretty output (code, link, revoke command) appears only when stdout is a terminal. --jsonemits{"code", "link", "expiresAt", "ttlSeconds"}on a single line for scripts.
Receiving
- Accepts the dashed code, the words separated by spaces, or the full
https://shareasecret.io/r#…link. Case and lookalike characters (O/0,l/1) are forgiven. - Keep the code out of shell history — a code typed as an
argument lands in history and is briefly visible to other local processes.
Omit it:
receiveprompts for it with input hidden, and scripts can pipe it to stdin instead of argv. - Writing key files: use
--output— it creates the file with0600permissions from the first byte and refuses to overwrite. A shell redirect (> id_ed25519) creates the file with your umask — often world-readable — andchmod 600afterwards leaves a window. If you must redirect, do it under a tight umask:(umask 077; npx shareasecret@0.1.5 receive > id_ed25519). On Windows,--output's mode is advisory — the file inherits the folder's ACL. - Reading claims the secret: the server copy is destroyed in the same transaction. A second attempt gets "already read" — if you didn't read it twice, treat that as a signal someone else did.
Scripting
Exit codes are stable, so automation can branch on what happened:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Network or unexpected error |
| 2 | Usage error (bad flag, malformed code, empty stdin) |
| 3 | Wrong code words — the error reports attempts left before the drop burns |
| 4 | No drop at that code (expired, revoked, or never existed) |
| 5 | Already read or burned |
| 6 | Secret exceeds 10 KB |
A typical handoff: generate a credential, send it, and post the code — never
the credential — to the channel where your teammate is waiting. The
$(…) capture keeps the code out of the job log provided shell
tracing (set -x) is off; the final echo then discloses it only to
the delivery channel, deliberately:
TOKEN=$(create-scoped-token --expires 7d)
CODE=$(printf '%s' "$TOKEN" | npx shareasecret@0.1.5 send --ttl 2h)
echo "Your token: shareasecret.io/r#$CODE (single read, dead in 2h)"
One caveat that can't be engineered away: whatever channel you post the code to can read the secret once. That's the read-once design — interception is a visible race, not a silent copy — but choose the channel accordingly.
What the CLI does and doesn't do
- Same cryptographic protocol as the web app — the five code
words never leave your machine; the key is derived locally with Argon2id and
the server stores only ciphertext and hashed tags. Sender and receiver can
each use whichever interface they prefer. But a terminal adds its own risk
surface the browser doesn't have: shell history, terminal scrollback, the
process list, file permissions, and CI logs. The flags above exist for
exactly those — hidden code prompt, capture-safe output,
--outputwith0600. - It always uses the encrypted drop. The browser's direct-transfer-only mode (nothing stored, both parties live) is not in the CLI yet — if you need the never-stored guarantee, use the web app's checkbox for now.
- Pin the version. A web page is re-fetched every visit; a
pinned package is not:
npx shareasecret@0.1.5runs bit-identical, inspectable code every time, which is why the examples on this page pin. Unpinnednpx shareasecretruns whatever is newest — convenient, but it trusts the npm supply chain at each invocation. Install permanently withnpm install -g shareasecret.
No terminal on the other end? The code you send works right here in the browser too.
Share a secret →