Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 89 additions & 16 deletions content/nips.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,19 @@ official specification vectors.

| NIP | Title | Status |
|----|----|----|
| [01](https://github.com/nostr-protocol/nips/blob/master/01.md) | Basic protocol: events, signatures, subscriptions | ✅ done |
| [06](https://github.com/nostr-protocol/nips/blob/master/06.md) | Key derivation from mnemonic seed | ✅ done |
| [09](https://github.com/nostr-protocol/nips/blob/master/09.md) | Event deletion (ingestion) | ✅ done |
| [19](https://github.com/nostr-protocol/nips/blob/master/19.md) | bech32-encoded entities | ✅ done |
| [01](https://github.com/nostr-protocol/nips/blob/master/01.md) | Basic protocol: events, signatures, subscriptions, `kind:0` profiles and `kind:1` notes | ✅ done |
| [06](https://github.com/nostr-protocol/nips/blob/master/06.md) | Key derivation from a BIP-39 mnemonic, path `m/44'/1237'/<account>'/0/0` | ✅ done |
| [09](https://github.com/nostr-protocol/nips/blob/master/09.md) | Event deletion, `kind:5` (ingestion) | ✅ done |
| [19](https://github.com/nostr-protocol/nips/blob/master/19.md) | bech32-encoded entities: `npub`, `nsec`, `note`, `nprofile`, `nevent`, `naddr` | ✅ done |
| [21](https://github.com/nostr-protocol/nips/blob/master/21.md) | `nostr:` URI scheme | ✅ done |
| [42](https://github.com/nostr-protocol/nips/blob/master/42.md) | Client-to-relay authentication | ✅ done |
| [44](https://github.com/nostr-protocol/nips/blob/master/44.md) | Encrypted payloads (v2) | ✅ done |
| [46](https://github.com/nostr-protocol/nips/blob/master/46.md) | Nostr Connect, remote signing | ✅ done |
| [46](https://github.com/nostr-protocol/nips/blob/master/46.md) | Nostr Connect, remote signing over `kind:24133` | ✅ done |
| [49](https://github.com/nostr-protocol/nips/blob/master/49.md) | Private key encryption (`ncryptsec`) | ✅ done |
| [65](https://github.com/nostr-protocol/nips/blob/master/65.md) | Relay list metadata (outbox) | ✅ done |
| [17](https://github.com/nostr-protocol/nips/blob/master/17.md) | Private direct messages | 🚧 planned |
| [59](https://github.com/nostr-protocol/nips/blob/master/59.md) | Gift wrap | 🚧 planned |
| [65](https://github.com/nostr-protocol/nips/blob/master/65.md) | Relay list metadata, `kind:10002`, what people call the outbox model | ✅ done |

Private messages and gift wrap land together, in [Plaza's private messages
milestone](/roadmap).
That is the library surface, and all of it is done. What the roadmap commits to
next is [further down this page](#planned-and-where-it-lands).

## In the apps

Expand All @@ -31,11 +29,86 @@ rather than library surface.

| NIP | Title | Where |
|----|----|----|
| [02](https://github.com/nostr-protocol/nips/blob/master/02.md) | Contact lists | reading and writing your follows |
| [05](https://github.com/nostr-protocol/nips/blob/master/05.md) | DNS identifiers | the verified handle on a profile |
| [10](https://github.com/nostr-protocol/nips/blob/master/10.md) | Reply threading | threads, and replies you post |
| [25](https://github.com/nostr-protocol/nips/blob/master/25.md) | Reactions | likes, in the feed and notifications |
| [02](https://github.com/nostr-protocol/nips/blob/master/02.md) | Contact lists, `kind:3` | reading and writing your follows |
| [05](https://github.com/nostr-protocol/nips/blob/master/05.md) | DNS identifiers, `/.well-known/nostr.json` | the `name@domain` handle on a profile, checked against the domain |
| [10](https://github.com/nostr-protocol/nips/blob/master/10.md) | Reply threading, marked `e` tags | threads, and replies you post |
| [18](https://github.com/nostr-protocol/nips/blob/master/18.md) | Reposts, `kind:6` and `kind:16` | counted on a note, and a row in notifications |
| [25](https://github.com/nostr-protocol/nips/blob/master/25.md) | Reactions, `kind:7` | likes, in the feed and notifications |
| [27](https://github.com/nostr-protocol/nips/blob/master/27.md) | `nostr:` mentions in content | names instead of raw base32 |
| [89](https://github.com/nostr-protocol/nips/blob/master/89.md) | Application handlers | the `client` tag on notes you post |
| [92](https://github.com/nostr-protocol/nips/blob/master/92.md) | Media attachments | `imeta` dimensions, so an image reserves its box before it loads |
| [57](https://github.com/nostr-protocol/nips/blob/master/57.md) | Zaps | receipts are read and shown. Sending is a milestone away |
| [92](https://github.com/nostr-protocol/nips/blob/master/92.md) | Media attachments, `imeta` | dimensions read off the tag, so an image reserves its box before it loads |
| [57](https://github.com/nostr-protocol/nips/blob/master/57.md) | Zaps, `kind:9735` receipts | receipts are read and shown. Sending is a milestone away |

## Planned, and where it lands

These are the specs the [roadmap](/roadmap) commits to, in roadmap order, each
against the milestone that delivers it. None of it is written yet, which is what
the status column says on every row. A spec named here is a promise: if one is
missing, it is because I have not made that promise, not because I forgot it.

| Spec | Title | Milestone | Status |
|----|----|----|----|
| [NIP-65](https://github.com/nostr-protocol/nips/blob/master/65.md) | Per-author routing on other people's `kind:10002` lists. The library side is done above, the routing is not | Reach | 🚧 planned |
| [NIP-24](https://github.com/nostr-protocol/nips/blob/master/24.md) | Extra `kind:0` fields (`display_name`, `website`, `banner`, `bot`, `birthday`), carried through untouched when I rewrite a profile | Your identity, and your key | 🚧 planned |
| [NIP-50](https://github.com/nostr-protocol/nips/blob/master/50.md) | Search, which is a relay-side feature: a `search` string on an ordinary filter | Find anything, and keep it clean | 🚧 planned |
| [NIP-51](https://github.com/nostr-protocol/nips/blob/master/51.md) | Lists: bookmarks (`kind:10003`) and the mute list (`kind:10000`) | Find anything, and keep it clean | 🚧 planned |
| [NIP-24](https://github.com/nostr-protocol/nips/blob/master/24.md) | The `t` tag: a hashtag is a lowercase `t`, and NIP-01 single-letter indexing is what makes `#t` queryable | Find anything, and keep it clean | 🚧 planned |
| [Blossom BUD-02](https://github.com/hzrd149/blossom/blob/master/buds/02.md) | Blob upload, `PUT /upload`. This is the step that puts the bytes of a picture on a server | Media worth looking at | 🚧 planned |
| [Blossom BUD-03](https://github.com/hzrd149/blossom/blob/master/buds/03.md) | User Server List, `kind:10063`: which servers hold your blobs, most trusted first | Media worth looking at | 🚧 planned |
| [Blossom BUD-11](https://github.com/hzrd149/blossom/blob/master/buds/11.md) | Authorization for those requests: a signed `kind:24242` event carrying the verb and an expiry | Media worth looking at | 🚧 planned |
| [NIP-92](https://github.com/nostr-protocol/nips/blob/master/92.md) | `imeta` written onto a note I post, not only read off one. It is what makes the picture render in someone else's client | Media worth looking at | 🚧 planned |
| [NIP-B7](https://github.com/nostr-protocol/nips/blob/master/B7.md) | Finding a blob again: when a media URL ends in a sha256, retry it against the author's `kind:10063` servers and check the hash | Media worth looking at | 🚧 planned |
| [NIP-71](https://github.com/nostr-protocol/nips/blob/master/71.md) | Video events: `kind:21`, and `kind:22` for short portrait video | Media worth looking at | 🚧 planned |
| [NIP-57](https://github.com/nostr-protocol/nips/blob/master/57.md) | Zaps you can send: a `kind:9734` request to the recipient's LNURL callback, and the `kind:9735` receipt that follows the payment | Money, carefully | 🚧 planned |
| [NIP-47](https://github.com/nostr-protocol/nips/blob/master/47.md) | Nostr Wallet Connect, so that invoice is paid in the app: a `nostr+walletconnect://` link, over kinds 13194, 23194 and 23195 | Money, carefully | 🚧 planned |
| [NIP-17](https://github.com/nostr-protocol/nips/blob/master/17.md) | Private direct messages: `kind:14` chat, `kind:15` files, and the `kind:10050` relay list they are delivered to | Private messages | 🚧 planned |
| [NIP-59](https://github.com/nostr-protocol/nips/blob/master/59.md) | Gift wrap, which does the wrapping NIP-17 defers: a `kind:13` seal signed by me, inside a `kind:1059` wrap signed by a throwaway key | Private messages | 🚧 planned |
| [NIP-29](https://github.com/nostr-protocol/nips/blob/master/29.md) | Groups, which live on one relay: an `h` tag on every event, relay-published metadata (`kind:39000`), and join and leave requests (`kind:9021`, `kind:9022`) | Groups | 🚧 planned |

**Blossom is not a NIP.** It is a separate protocol with its own repository,
[hzrd149/blossom](https://github.com/hzrd149/blossom), and its documents are
called BUDs rather than NIPs. It sits in this table because it is where file
upload actually happens. A media list without it would be a list that cannot
post a picture.

NIP-B7 is the Nostr side of the same story, and it is easy to mistake for the
upload spec. It is not one. It only says how to find a blob again, so a client
that implemented it and nothing else could not upload anything at all. The older
upload NIP, [NIP-96](https://github.com/nostr-protocol/nips/blob/master/96.md),
is marked `unrecommended` in the NIPs repo and replaced by Blossom. I do not
implement it.

Zaps lean on something that is not a NIP either. The recipient's LNURL pay
endpoint hands back the invoice, and it is that server, not my app, that signs
and publishes the `kind:9735` receipt. A pubkey whose LNURL endpoint has not
opted into Nostr cannot be zapped at all.

Three milestones name no spec, and inventing one for them would be dishonest.
Notifications is assembled out of NIPs already on this page: NIP-10 replies,
NIP-18 reposts, NIP-25 reactions, NIP-27 mentions and NIP-57 zap receipts.
Packaging, updates and diagnostics are not protocol work. Neither is a light
theme, keyboard operation, or type that scales.

## Named, and not doing

Refusing is a promise too, and these are the ones I get asked about. The
[roadmap](/roadmap) has the rest.

- **[NIP-77](https://github.com/nostr-protocol/nips/blob/master/77.md)**, set
reconciliation. Relay support is thin and the protocol is easy to get subtly
wrong. Per-relay cursors capture most of the benefit against a store that
already answers a feed query in under a millisecond.
- **[NIP-96](https://github.com/nostr-protocol/nips/blob/master/96.md)**, HTTP
file storage. Marked `unrecommended` in the NIPs repo and replaced by Blossom.
Media goes through Blossom instead.
- **[NIP-60](https://github.com/nostr-protocol/nips/blob/master/60.md)** and
**[NIP-61](https://github.com/nostr-protocol/nips/blob/master/61.md)**, the
Cashu wallet and nutzaps. That is a second payment rail with a second wallet
behind it. Lightning zaps come first, and shipping those says nothing about
shipping these.
- **[NIP-13](https://github.com/nostr-protocol/nips/blob/master/13.md)**, proof
of work. No milestone needs it, and no mainstream relay I have checked
advertises a `min_pow_difficulty` above zero in its
[NIP-11](https://github.com/nostr-protocol/nips/blob/master/11.md) document.
If that changes, reading the number and mining to it is a small piece of work,
and I would rather do it then than promise it now.
72 changes: 51 additions & 21 deletions content/roadmap.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,55 @@ No dates. The order is a commitment; a calendar would not be.
libsecp256k1), the NIP-01 event model, NIP-19/21 encoding, NIP-06 derivation, and NIP-49
key storage. Passes the full official BIP-340 test-vector suite.
- **Transport and outbox**: RFC 6455 WebSocket, a relay connection state machine with NIP-01
subscriptions, a live TCP/TLS dialer, and the NIP-65 outbox model with zero hardcoded relays.
subscriptions, a live TCP/TLS dialer, and NIP-65 relay lists (kind 10002), the routing people
call the outbox model, with zero hardcoded relays.
- **Local-first store**: a zero-copy, memory-mapped LMDB event store with a bounded,
newest-first query planner. [Sub-millisecond feeds](/performance) that stay flat as the
store grows.
- **Signer protocol**: NIP-44 v2 encryption, the NIP-46 remote-signing ("bunker") protocol,
and NIP-42 client authentication.
- **[Notary](/signer)**, the native remote signer. Your key lives in a local daemon and
nothing gets signed quietly. A downloadable macOS app.
- **[Plaza](/plaza)**, the flagship client. Browse as a guest, sign in three ways, and read a
follow feed that renders from disk before the network answers.
newest-first planner for NIP-01 filters. [Sub-millisecond feeds](/performance) that stay
flat as the store grows.
- **Signer protocol**: NIP-44 v2 encryption, the NIP-46 remote-signing ("bunker") protocol
over kind 24133, and NIP-42 client authentication.
- **[Notary](/signer)**, the native remote signer. Your key lives in a local daemon, it hands
you a `bunker://` URL, and nothing gets signed quietly. A downloadable macOS app.
- **[Plaza](/plaza)**, the flagship client. Browse as a guest, sign in three ways (including a
NIP-46 bunker), and read a follow feed (your kind 3 list) that renders from disk before the
network answers.
- **Docs and benchmarks**: this site, the [NIP-support table](/nips), and reproducible
[performance numbers](/performance).

## What comes next, in order

1. **Notifications worth opening.** Who it was, what they did, the note it was about, and
what a zap carried. Today a reply is a line of text with no face and no content.
2. **Reach.** Following somebody means seeing them: per-author relay routing on the way in,
the outbox model on the way out, and older notes when you scroll past what is local.
what a zap carried: NIP-25 reactions, NIP-18 reposts, NIP-10 replies, and NIP-57 zap
receipts (kind 9735). Today a reply is a line of text with no face and no content.
2. **Reach.** Following somebody means seeing them: per-author routing from each author's
NIP-65 relay list (kind 10002) on the way in, the outbox model on the way out, and older
notes when you scroll past what is local.
3. **Ship it, and fix it.** A packaged Linux build, an update path that reaches people who
already installed, and enough diagnostics to act on a report. A release you cannot fix is
a liability.
4. **Your identity, and your key.** Edit your profile, export your key, and verify a backup
before you need it.
4. **Your identity, and your key.** Edit your kind 0 profile without dropping the NIP-24
fields another client set, show a NIP-05 address as identification rather than a badge,
and export your key as a NIP-49 `ncryptsec` you have verified before you need it. A third
way in is being evaluated: a FROST bunker in the shape of fiatjaf's
[promenade](https://git.fiatjaf.com/promenade), where the key is split across independent
signers and reached over NIP-46.
5. **Readable and usable by everyone.** Operable without a mouse, a light theme, and type
that scales.
6. **Find anything, and keep it clean.** Search people, notes and hashtags. Bookmarks, mutes,
and opening a note by address.
7. **Media worth looking at.** Post pictures, see the picture a quote quotes, and video.
8. **Money, carefully.** Zaps you can send and receive, and a wallet connection, with the
care that anything moving money deserves.
9. **Private messages.** NIP-17, on the local store, signing through Notary.
10. **Groups.** NIP-29, on relays that support it.
6. **Find anything, and keep it clean.** Search people, notes and hashtags: NIP-50 where a
relay supports it, and `#t` filters for the hashtags. NIP-51 bookmarks (kind 10003) and
mutes (kind 10000), and opening a note by address, from a NIP-19 `nevent` or `naddr`.
7. **Media worth looking at.** Post pictures: [Blossom](https://github.com/hzrd149/blossom)
BUD-02 upload to the servers in your kind 10063 list, with a NIP-92 `imeta` tag so they
render elsewhere, and NIP-B7 to re-find a blob by its sha256 when a host goes down. Then
the picture a quote quotes, and NIP-71 video (kinds 21 and 22).
8. **Money, carefully.** Zaps you can send and receive (NIP-57) and a NIP-47 wallet
connection, with the care that anything moving money deserves. The kind 9735 receipt is
the only proof a zap was paid, and it is the recipient's LNURL server that publishes it.
9. **Private messages.** NIP-17, gift wrapped per NIP-59 (a kind 13 seal inside a kind 1059
wrap), on the local store, signing through Notary, published only to the recipient's kind
10050 DM relays.
10. **Groups.** NIP-29, on relays that support it: every event carries an `h` tag with the
group id, and the relay hosting the group is the authority on who is in it.

## What this deliberately does not do

Expand All @@ -65,6 +81,20 @@ you price it.
answers a feed query in under a millisecond.
- **C-ABI bindings, WASM, and mobile.** Real work for a set of consumers that is currently
empty. Each becomes worth doing the day somebody names a project that needs it.
- **A proof of work difficulty slider (NIP-13).** Reading proof of work is cheap: count the
leading zero bits of the event id and honour the target committed in the `nonce` tag. A
slider is not, and none of the mainstream relays I checked ask for any difficulty, so the
setting would buy background mining, a cancel path and per-kind policy for nothing. Mining
when a relay actually asks, through `min_pow_difficulty` in its NIP-11 document or an `OK`
rejection that starts `pow:`, needs no setting at all.
- **Nutzaps (NIP-61).** They look like a variant of zaps and they are not: a kind 9321 nutzap
is Cashu ecash, and redeeming one needs a NIP-60 wallet (kind 17375) kept on relays plus a
mint list of your own. That is a second payment rail behind a second wallet, and item 8
already covers sending and receiving money.
- **NIP-96 HTTP file storage.** Supporting both upload paths looks like broader
compatibility. NIP-96 carries an `unrecommended` banner in its own spec and the NIPs index
strikes it through as replaced by Blossom, so uploads go to Blossom (item 7) and nowhere
else.
- **Web-of-trust distance filtering.** Computed over a store that holds only what you
happened to sync, the graph is wrong in a way that changes between launches, so the same
reply appears and disappears for reasons nobody can explain.
Loading