Skip to Content
deed

deed: the nostr command line

deed is a command line for nostr, built on zig-nostr. It makes keys, builds and signs events, reads and writes NIP-19 codes, encrypts and decrypts with NIP-44, checks signatures, asks relays for events and publishes to them, and keeps what it fetches in a local store that later runs can read without a network. One binary of under 3 MB for macOS and Linux, Intel and ARM, statically linked on Linux.

A deed is two things at once: a signed instrument, and a thing done. So is a nostr event.

Install

curl -fsSL https://raw.githubusercontent.com/zig-nostr/deed/main/scripts/install.sh | bash

The installer works out which build the machine wants, checks the download against the SHA-256 published beside it, and installs into ~/.local/bin, so nothing needs root. If the digest does not match, it installs nothing and says so.

What it does

Verb
keymake a key, or derive the public one from it
eventbuild an event and sign it
decodeturn a NIP-19 code into the fields it carries
encodebuild a NIP-19 code out of its parts
encryptencrypt a message to someone, with NIP-44
decryptdecrypt a NIP-44 payload from someone
verifycheck that events are correctly signed
reqbuild a subscription, and run it
fetchget the events a code names
publishoffer signed events to relays, and print the ones they accepted

Every verb that does not need a socket works without one, so what comes out can be read before any of it leaves the machine. deed help <command> explains any of them.

The verbs compose

Each verb takes its inputs as arguments and, given none, reads them as newline-delimited records on standard input, writing one result per line:

export NOSTR_SECRET_KEY=$(deed key generate) deed event -c "hello" | deed verify cat drafts.jsonl | deed event - | deed publish wss://relay.example > sent.jsonl

publish prints each event a relay accepted, once the relays have answered or the deadline has passed, so what it writes out is what was published. Every relay’s answer goes to standard error with the event’s id on it, and the exit code is 0 only when every event was accepted by at least one relay.

It keeps what it fetches

deed req -k 1 -l 50 --store ~/.deed/db wss://relay.example # once, over the network deed req -k 1 -l 50 --store ~/.deed/db --local # again, dialling nothing

The second command opens no socket, and the events it prints are the same events: what is stored is what was signed, so they still verify. Every event is checked before it is stored or printed, so a relay cannot put a forgery, or an event nobody asked for, into the store or the output.

Judging what it fetches

deed’s output is one verified event per line, which makes it a clean first stage for anything that classifies text. The Jev example  fetches the newest thousand notes from five relays, drops the app data some clients publish as notes, and has Jev , TypeSafe’s classification model, answer three questions about each: what it is about, how much substance it carries, and whether it is spam. The thresholds that decide what to keep are plain constants in the script.

export TYPESAFE_API_KEY=... examples/jev/rank-notes.sh 1000 > ranked.jsonl # most substance first, spam dropped

On one run, 629 of 1,000 notes were text worth judging, 157 of those were dropped as spam, and the whole run cost about two cents. The example’s README has the full numbers and what to watch for.

How fast it is

A one-shot command runs in about 2.3 ms and under 2 MB of memory. deed signs and verifies about 31,000 events a second each, stores 100,000 events from a relay at about 17,000 a second, and answers a lookup from that store in about 3 ms, start to finish. The full numbers, and how to reproduce them, are on the performance page.

What is missing

Relay selection. A code with no relay hints is not looked up yet: deed fetch npub1... asks you to name a relay rather than finding the author’s relay list first.

Windows. deed does not build there yet.

Source, releases and the full reference live at zig-nostr/deed.

Last updated on