Encrypted Nostr DM pipe (NIP-17 + NIP-44) with a Unix-style CLI and TUI
  • C 85.9%
  • Nix 7.3%
  • Makefile 6.3%
  • Shell 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Kyle 🐆 5a3f58e61e
License/mit (#11)
* Add TUI mode with notcurses

* Switch license from AGPL-3.0 to MIT
2026-03-04 21:29:16 -05:00
.github/workflows Add --keep-key integration and CI (#1) 2026-01-12 22:00:12 -05:00
.gitignore Fix Makefile noscrypt path and cleanup recv debug (#4) 2026-01-22 20:17:31 -05:00
flake.lock Initial commit: whisper - Encrypted DM pipe for Nostr (NIP-17 + NIP-44) 2026-01-12 21:13:40 -05:00
flake.nix Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
LICENSE License/mit (#11) 2026-03-04 21:29:16 -05:00
main.c Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
Makefile Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
README.md License/mit (#11) 2026-03-04 21:29:16 -05:00
recv.c Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
send.c Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
test_util.c Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
tui.c Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
tui.h Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
util.c Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
whisper.h Add TUI mode with notcurses (#10) 2026-01-29 10:54:10 -05:00
whisper.sh Initial commit: whisper - Encrypted DM pipe for Nostr (NIP-17 + NIP-44) 2026-01-12 21:13:40 -05:00

whisper

Encrypted DM pipe for Nostr using NIP-17 + NIP-44. Unix-style CLI for private messaging.

# Send encrypted DM (using keep vault)
echo "secret message" | whisper send --to npub1... --keep-key main --relay wss://relay.damus.io

# Receive encrypted DMs
whisper recv --keep-key main --relay wss://relay.damus.io

# Interactive TUI
whisper tui --keep-key main --relay wss://relay.damus.io --to npub1...

Install

# Run directly
nix run github:privkeyio/whisper -- send --to npub1... --relay wss://...

# Or install
nix profile install github:privkeyio/whisper

# Dev shell
nix develop github:privkeyio/whisper

Manual Build

1. Build Dependencies

# Build libnostr-c with relay and NIP-17 support
cd ../libnostr-c/build
cmake .. -DNOSTR_FEATURE_NIP17=ON -DNOSTR_FEATURE_RELAY=ON
make

2. Build whisper

make

3. Set Up Your Key

Option A: Use keep (recommended)

# Install keep: https://github.com/privkeyio/keep
keep init
keep generate --name main

Option B: Manual key file

mkdir -p ~/.config/whisper && chmod 700 ~/.config/whisper
echo "nsec1..." > ~/.config/whisper/nsec && chmod 600 ~/.config/whisper/nsec

4. Send a Message

# Using keep vault (recommended)
echo "hey!" | whisper send --to npub1... --keep-key main --relay wss://relay.damus.io

# Using key file
echo "hey!" | whisper send --to npub1... --nsec-file ~/.config/whisper/nsec --relay wss://relay.damus.io

5. Receive Messages

# Stream incoming DMs
whisper recv --keep-key main --relay wss://relay.damus.io

# Get last 10 messages as JSON
whisper recv --keep-key main --relay wss://relay.damus.io --limit 10 --json

Usage

whisper - Encrypted Nostr DMs (NIP-17)

Usage:
  whisper send --to <npub> --relay <url> [key options]
  whisper recv --relay <url> [key options]
  whisper tui --relay <url> [--to <npub>] [key options]

Key options (in order of priority):
  --keep-key <name>     Use key from keep vault (recommended)
  --nsec-file <path>    Read key from file
  --nsec <nsec|hex>     Key as argument (visible in history)
  NOSTR_NSEC env var    Fallback if no key option

Send options:
  --to <npub|hex>       Recipient public key
  --relay <url>         Relay URL
  --subject <text>      Optional subject
  --timeout <ms>        Timeout (default: 5000)

Recv options:
  --relay <url>         Relay URL
  --since <timestamp>   Only messages after timestamp
  --limit <n>           Max messages (0 = stream)
  --json                Output JSON format
  --timeout <ms>        Timeout (default: 5000)

TUI options:
  --relay <url>         Relay URL
  --to <npub|hex>       Initial recipient (can change with /to)

TUI commands:
  /to <npub>            Set recipient
  /clear                Clear messages
  /quit                 Exit TUI
  /help                 Show commands

TUI keys:
  Enter                 Send message
  Ctrl+Q                Quit
  PgUp/PgDn             Scroll messages

Security

Key Protection:

  • Use --keep-key with keep vault (recommended)
  • Or --nsec-file to avoid keys in shell history
  • Keys are wiped from memory using secure_wipe() before exit
  • Environment variable NOSTR_NSEC as fallback (visible to child processes)

Protocol:

  • NIP-17: Private Direct Messages (triple-wrapped)
  • NIP-44: Modern encryption (XChaCha20-Poly1305)
  • NIP-59: Gift Wrap (ephemeral sender key)

Messages are encrypted and wrapped so relays cannot read content or see the real sender.

Exit Codes

Code Meaning
0 Success
1 Invalid arguments
2 Key parsing error
3 Relay connection failed
4 Encryption/decryption failed
5 Timeout

Examples

# Send secret to a friend (using keep vault)
echo "meet at the usual spot, 9pm" | whisper send \
  --to npub1friend... \
  --keep-key main \
  --relay wss://relay.damus.io

# Pipe from another command
cat secret.txt | whisper send --to npub1... --keep-key main --relay wss://relay.damus.io

# Monitor inbox continuously
whisper recv --keep-key main --relay wss://relay.damus.io | tee inbox.log

# Export messages as JSON for processing
whisper recv --keep-key main --relay wss://relay.damus.io --limit 100 --json > messages.json

# Without keep (using env var)
export NOSTR_NSEC=nsec1...
echo "hello" | whisper send --to npub1... --relay wss://relay.damus.io

Building from Source

Prerequisites

  • libnostr-c (with NIP-17 and relay features)
  • libwebsockets
  • libcjson
  • libsecp256k1
  • noscrypt
  • OpenSSL
  • notcurses (optional, for TUI mode)

Compile

# Standard build
make

# With custom libnostr-c path
LIBNOSTR_DIR=/path/to/libnostr-c make

License

MIT