Secret is a short-lived sharing system for private text and files. The product model is intentionally small: clients encrypt data locally, the API stores only ciphertext and lifecycle metadata, and the final access URL keeps the decryption secret in the browser-only hash fragment.

portal or cli
  -> secret-cipher: local encryption and access-url encoding
  -> edge api: ciphertext, read tokens, tracking, expiration
  -> recipient: decrypts locally with /s/{readId}#{accessSecret}

This means the web portal and terminal client share the same security boundary. The terminal is just another client: it prepares encrypted payloads with secret-cipher, sends ciphertext to the API, and prints read links plus a tracking link.

Use Secret from a terminal

If you only want to try Secret, run npx secret without installing it. The same command can create text secrets, create file secrets, and tune read limits or expiration time for a quick test.

# Create a text secret without installing.
npx secret

# Create 3 read links that expire in 15 minutes.
npx secret --links 3 --expiration 900

# Create a file secret for a quick test.
npx secret -f ./incident-notes.txt

# Create a file secret with 5 read links and a 1 hour TTL.
npx secret -f ./archive.zip --links 5 --expiration 3600

The main command prompts for the secret value, encrypts it locally, creates one or more read links, and saves enough local tracking metadata for later status checks.

For regular use, install the CLI globally with npm i secret -G, then run secret directly from any terminal.

# Install for repeated use.
npm i secret -G

# Create a text secret after installing.
secret

# Create a file secret after installing.
secret -f ./incident-notes.txt

# Create 3 read links that expire in 15 minutes.
secret --links 3 --expiration 900

Files use the same model with chunk encryption and encrypted file metadata. The API receives encrypted bytes and never sees the original filename, content, or decryption key in plaintext.

Reveal and track

A recipient can reveal a text or file secret from the full URL. Text is printed to the terminal. Files first show encrypted manifest details after local decryption, then ask before downloading and decrypting the object.

secret reveal 'https://secret.witt.im/s/read_123#access_secret'

# The CLI also accepts the compact readId.secret form.
secret reveal 'read_123.access_secret'

The sender receives a separate tracking link and a track id. Use it to inspect whether a secret is still ready, consumed, expired, or destroyed.

secret track track_123
secret status

Self-host origins

Self-hosted users can point the CLI at their own API and portal origins. The API origin is where ciphertext is created, read, tracked, and cleaned up. The portal origin is used when the CLI prints browser-readable access and tracking URLs.

secret config \
  --api https://secret-api.example.com \
  --portal https://secret.example.com

# Local development hosts are normalized to http.
secret config --api localhost:3001 --portal localhost:3000

Origins are normalized before saving. A host without a protocol defaults to https, except localhost-style hosts, which default to http. The config is stored in the user home directory under .secret-cli/config.

Build your own CLI

If you need to publish your own Secret-compatible command line tool, start with packages/secret-cipher/README.md. The package is the stable boundary for local encryption, decryption, and access URL encoding.

  1. Install secret-cipher and use sealText, openText, encodeTextAccessUrl, and the file helpers as your cryptographic core.
  2. Implement API calls that store ciphertext, request read ids, complete file uploads, load tracking state, and fetch encrypted payloads.
  3. Add an origin configuration command equivalent to secret config --api --portal so self-hosted users can target their own deployment.
  4. Rename the package and binary for your distribution, remove private package metadata, then build and publish through your usual npm release process.
import { encodeTextAccessUrl, openText, sealText } from 'secret-cipher'

const sealed = await sealText('private text')
const url = encodeTextAccessUrl({
  origin: 'https://secret.example.com',
  readId: 'read_123',
  secret: sealed.secret,
})

          const text = await openText(sealed)