CLI Reference
secrettnl is a zero-dependency terminal tool for pushing and pulling end-to-end encrypted secrets. Every byte is encrypted on your machine before any network call is made.
#Installation
No global install required. Use npx to run directly from the npm registry:
npx secrettnl [command] [options]Or install globally for repeated use:
npm install -g secrettnlcrypto module — no external encryption dependencies.#secrettnl push
Encrypts content locally using AES-256-GCM and uploads the ciphertext to the SecretTunnel server. Returns a one-time share URL with the decryption key embedded in the URL fragment.
Signature
secrettnl push <content> [--file <path>] [--ttl <duration>] [--password <value>] [--webhook <url>]<content> as a positional argument or use --file. If both are given, --file takes priority.Flags
| Flag | Type | Status | Default | Description |
|---|---|---|---|---|
--file, -f | string | optional | — | Path to a file to push. Reads the entire file contents and encrypts them. Supports .env, .json, or any plaintext file. |
--ttl | string | optional | 24h | Time-to-live. Supported formats: 30s, 15m, 1h, 7d, or raw seconds like 3600. |
--password | string | optional | — | Optional password for an extra encryption layer. The recipient must provide the same password when pulling. |
--webhook | string (URL) | optional | — | HTTPS URL called when the secret is viewed. POSTs { token, viewedAt, viewerIp } with a 5-second timeout via Upstash QStash. |
Examples
Push inline text
secrettnl push "DATABASE_URL=postgres://user:pass@localhost/db"Push a .env file
secrettnl push --file .env.productionCustom TTL — expires in 30 minutes
secrettnl push "my secret" --ttl 30mPassword-protected push
secrettnl push "my secret" --password mypass123Full example with all flags
secrettnl push \
--file .env.production \
--ttl 1h \
--password mypass123 \
--webhook https://hooks.example.com/secret-viewedOutput
On success the CLI prints a share URL with the decryption key embedded in the URL hash fragment:
✓ Secret created
Share URL:
https://secrettunnel.vercel.app/s/abc12345#key=aB3dK9YzXq...
⚑ Burn-after-read · Expires in 1h# is never sent to the server. Do not truncate the URL when sharing — the full fragment must be preserved.#secrettnl pull
Fetches and decrypts a one-time secret by its share URL or token. This action is irreversible — the secret is permanently destroyed on the server the moment it is retrieved.
Signature
secrettnl pull <share-url | token> [--key <base64>] [--password <value>] [--output <path | ->]Flags
| Flag | Type | Status | Default | Description |
|---|---|---|---|---|
--key | string (base64) | optional | — | The base64-encoded AES key. Only needed when passing a bare token. When passing the full URL the key is parsed automatically from the #key= fragment. |
--password | string | optional | — | Required only if the secret was pushed with --password. If omitted for a password-protected secret, decryption will fail. |
--output, -o | string | - | optional | — | Path to write the decrypted plaintext. Use - to stream to stdout. If the file already exists, the CLI prompts before overwriting. |
Examples
Pull using the full URL (recommended)
secrettnl pull "https://secrettunnel.vercel.app/s/abc12345#key=aB3dK9Yz..."Pull using a bare token and explicit key
secrettnl pull abc12345 --key "aB3dK9Yz..."Pull and write directly to .env
secrettnl pull "https://secrettunnel.vercel.app/s/abc12345#key=..." --output .envPull a password-protected secret
secrettnl pull "https://secrettunnel.vercel.app/s/abc12345#key=..." --password mypass123Stream to stdout for piping
secrettnl pull "https://secrettunnel.vercel.app/s/abc12345#key=..." --output - > .envOutput
Fetching...
Decrypting locally...
✓ Written to .env
⚑ Token consumed. Secret permanently deleted.#Configuration
The CLI talks to the SecretTunnel web API. You can point it at a self-hosted instance via environment variables.
Environment variables
| Variable | Description |
|---|---|
SECRETTUNNEL_API_URL | Highest priority. Overrides the API base URL completely. |
API_URL | Fallback if SECRETTUNNEL_API_URL is not set. |
NODE_ENV | If production and no URL env vars are set, defaults to https://secrettunnel.vercel.app/api/secrets. |
Resolution priority:
SECRETTUNNEL_API_URL(highest priority)API_URLNODE_ENV=production→https://secrettunnel.vercel.app/api/secrets- Default:
http://localhost:3000/api/secrets
export SECRETTUNNEL_API_URL="https://your-self-hosted-instance.com"#Error handling
The CLI exits with a non-zero code on failure. Common errors and how to resolve them:
Missing key.
Cause — You passed a URL without the #key=… fragment, or forgot --key.
Fix — Use the full share URL including the hash, or pass --key explicitly.
Secret not found.
Cause — The secret was already consumed by a previous pull, or its TTL expired.
Fix — Ask the sender to create a new secret.
Failed to decrypt secret / Incorrect password or corrupted key.
Cause — Wrong password, or the key hash was truncated during copy-paste.
Fix — Double-check the password and ensure the full URL was copied.
Unknown option ...
Cause — A flag was passed to the wrong command (e.g., --ttl on pull).
Fix — Flags are validated per-command. Check the signature above.
#Core behaviors
Burn after read
Secrets are permanently destroyed once successfully retrieved. Running pull a second time will fail.
Client-side encryption
The server stores only ciphertext and IV. It never receives your key. The key travels strictly via the URL hash.
Strict decryption
pull requires both the token and the decryption key. Without the exact key, recovery is mathematically impossible.
File overwrite protection
When --output points to an existing file, the CLI prompts for confirmation before overwriting.