diff --git a/app/structured-data.js b/app/structured-data.js
index 0772ebd..86e1eed 100644
--- a/app/structured-data.js
+++ b/app/structured-data.js
@@ -46,8 +46,8 @@ const byPage = {
library,
],
'getting-started': [library],
- deed: [app('deed', 'deed', 'deed', 'The nostr command line: keys, events, NIP-19, NIP-44, relay queries, publishing, and a local store that later runs read without a network.')],
- plaza: [app('Plaza', 'plaza', 'plaza', 'A fast, local-first Nostr client for macOS and Linux, rendered from its own store and reconciled with relays in the background.', { applicationCategory: 'SocialNetworkingApplication' })],
+ deed: [app('deed', 'deed', 'deed', 'The nostr command line: keys, events, NIP-19, NIP-44, relay queries, publishing, and a local store that later runs read without a network.', { operatingSystem: 'macOS, Linux, Windows' })],
+ plaza: [app('Plaza', 'plaza', 'plaza', 'A fast, local-first Nostr client for macOS and Linux, rendered from its own store and reconciled with relays in the background. It reads long-form articles, searches for people by name and uploads pictures to Blossom servers.', { applicationCategory: 'SocialNetworkingApplication' })],
notary: [app('Notary', 'notary', 'notary', 'A native NIP-46 remote signer for macOS and Linux: the key stays in a local daemon on a machine you control.', { applicationCategory: 'SecurityApplication' })],
}
diff --git a/content/architecture.mdx b/content/architecture.mdx
index 82506d8..2c17114 100644
--- a/content/architecture.mdx
+++ b/content/architecture.mdx
@@ -24,7 +24,8 @@ official BIP-340 test-vector suite. On top of it:
- **RFC 6455 WebSocket** framing and handshake, and a stream-generic relay
connection state machine with NIP-01 subscriptions.
-- A live **TCP/TLS dialer** that resolves hostnames through the system resolver.
+- A live **TCP/TLS dialer**. Hostnames resolve through libc `getaddrinfo` on macOS and Linux and through std's own resolver on Windows, and an IP literal is parsed first so it never reaches a resolver. `dialDiagnosed` is `dial` that also records the HTTP status the relay answered the upgrade with, so a relay that says 503 when busy, 401 or 429 can be told apart from one that is down.
+- **NIP-50 search** on the filter: `Filter.search` is sent to relays as `"search"`, and the local store applies it as a case-insensitive substring of the content.
- **NIP-65 outbox model**: relay lists with read/write routing and *zero*
hardcoded relays, so events are published and fetched where they actually
live.
@@ -36,8 +37,9 @@ A zero-copy, memory-mapped **LMDB** event store (`store.zig`):
- A compact binary event record with secondary indexes (author, kind,
`created_at`, tag).
- A **bounded, newest-first query planner**: it walks the indexes via reverse
- cursors and a k-way merge, stopping at `limit`, so feed latency stays flat as
- the store grows (see [Performance](/performance)).
+ cursors and a k-way merge that takes the newest candidate from a binary heap,
+ stopping at `limit`, so feed latency stays flat as the store grows (see
+ [Performance](/performance)).
- Validate-on-insert ingestion: dedup, replaceable / parameterized-replaceable
upserts, and NIP-09 deletions.
- A direct-message conversation index, local-first reconciliation, and a
@@ -56,3 +58,15 @@ The layer the native signer is built on:
access behind auth.
The native signer built on this layer is **[Notary](/notary)**.
+
+## Platforms
+
+The library builds for macOS, Linux and Windows, on x86_64 and aarch64, and CI builds and tests it on all three. On Windows the dialer waits for write room with a poll request to the AFD driver, because std opens its sockets on that driver and never registers them with Winsock, and a key file takes its directory's default access list because there are no mode bits. macOS and Linux keep `getaddrinfo` and `poll`.
+
+## How it is tested
+
+Tests sit beside the code and `zig build test` runs them all. The cryptography is checked against the official vectors (the BIP-340 suite, the NIP-44 v2 vectors, and the NIP-19, NIP-49 and RFC 6455 worked examples). The transport is driven by a scripted in-memory relay, with a few tests on a real loopback socket for the parts a fake cannot show. The store runs on a real LMDB file in a temporary directory.
+
+The parsers that read bytes someone else wrote have fuzz targets: WebSocket frames and handshake responses, relay messages, events, bech32 and NIP-19, NIP-44 payloads, NIP-46 messages and URIs, relay lists and URLs, mnemonics, `ncryptsec` files and filters. Each asserts that nothing crashes or leaks and, where one exists, an invariant, such as an event parsed, serialized and parsed again being the same event. `zig build test` runs each target over a seed corpus, and `zig build test --fuzz -Doptimize=ReleaseSafe` fuzzes them until stopped.
+
+The repository's [`ARCHITECTURE.md`](https://github.com/zig-nostr/nostr/blob/main/ARCHITECTURE.md) goes through the modules file by file, how an event travels from a key to a relay to the store, and the threading and cancellation rules.
diff --git a/content/deed.mdx b/content/deed.mdx
index a2997db..2a55af0 100644
--- a/content/deed.mdx
+++ b/content/deed.mdx
@@ -1,16 +1,26 @@
# 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.
+**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, Linux and Windows, 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
+macOS and Linux:
+
```sh
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.
+Windows, in PowerShell:
+
+```powershell
+irm https://raw.githubusercontent.com/zig-nostr/deed/main/scripts/install.ps1 | iex
+```
+
+Each checks the download against the SHA-256 published beside it, and if the digest does not match it installs nothing and says so. On macOS and Linux deed goes into `~/.local/bin`. On Windows it goes into `%LOCALAPPDATA%\deed`, which is added to your `PATH`, so `deed version` works in a new terminal. Neither needs root or administrator rights.
+
+`--version ` installs a particular release and `--prefix ` puts it somewhere else; on Windows they are `-Version` and `-Prefix` when the script runs as a file, and `$env:DEED_VERSION` and `$env:DEED_PREFIX` for the piped line. Windows on ARM gets the x86_64 build, which Windows 11 runs under emulation. The Windows binary is not code-signed, so Windows may show a warning the first time it runs.
## What it does
@@ -51,6 +61,10 @@ deed req -k 1 -l 50 --store ~/.deed/db --local # again, dialling n
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.
+A store is one file. `--store` creates it the first time, and the directories above it as `mkdir -p` would, and a leading `~` is your home directory even when it was quoted. `--local` only reads, so it opens a store that already exists and says "there is no store at" a path that has none, rather than leaving an empty one behind. When a store will not open, the message says why: a directory in the way, no permission, a read-only file system, a full disk, or a file that is not a store. On Windows the file takes 1 GiB of disk from the first run, because LMDB sizes it to its whole map up front there; on macOS and Linux it grows with what is kept.
+
+NIP-19 codes are read the way a QR code carries them: `deed decode NPUB1...` and `NOSTR:NPUB1...` work as the lowercase forms do, and a code that mixes cases is refused.
+
## 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](https://github.com/zig-nostr/deed/tree/main/examples/jev) fetches the newest thousand notes from five relays, drops the app data some clients publish as notes, and has [Jev](https://docs.typesafe.ai), 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.
@@ -66,10 +80,12 @@ On one run, 629 of 1,000 notes were text worth judging, 157 of those were droppe
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](/performance#deed).
+## How it is built
+
+[`ARCHITECTURE.md`](https://github.com/zig-nostr/deed/blob/main/ARCHITECTURE.md) explains how deed is put together: where each command lives, how a run flows, the store, the exit codes and how it is tested. CI builds deed and runs its tests on Linux, macOS and Windows, and on Windows it also makes a key, signs an event and verifies it through a pipe with the binary it built.
+
## 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`](https://github.com/zig-nostr/deed).
diff --git a/content/ecosystem.mdx b/content/ecosystem.mdx
index efeb26e..187ea57 100644
--- a/content/ecosystem.mdx
+++ b/content/ecosystem.mdx
@@ -8,7 +8,8 @@ apps, so every layer benefits from the same well-tested foundation.
**[`zig-nostr/nostr`](https://github.com/zig-nostr/nostr)**: secp256k1 keys and
BIP-340 signing, the NIP-01 event model, relay transport with the outbox model, a
-zero-copy local-first store, and the NIP-46 signer protocol. It's the credibility
+zero-copy local-first store, and the NIP-46 signer protocol. It builds for macOS,
+Linux and Windows, and its parsers are fuzzed. It's the credibility
anchor for everything else, and it keeps growing: more NIPs and capabilities land
with each milestone. See [NIP support](/nips) and the [architecture](/architecture).
@@ -26,26 +27,26 @@ and the reason the ecosystem exists.
A native NIP-46 signer for macOS and Linux: Zig, no Electron, no WebView. Your key lives in
a local daemon, nothing gets signed quietly, and your `nsec` never leaves it
-unless you ask for it.
+unless you ask for it. On macOS it waits in the menu bar and shows its window only when a request needs a decision.
→ [`zig-nostr/notary`](https://github.com/zig-nostr/notary) · [read more](/notary)
### Plaza: shipped
The flagship client: read without an account, post in four clicks, with the feed
-rendered from disk and reconciled in the background. Your key lives in Notary, a separate
+rendered from disk and reconciled in the background. It opens long-form articles, searches for people by name, adds pictures from a file and covers notes marked sensitive. Your key lives in Notary, a separate
process, or in your own signer.
→ [`zig-nostr/plaza`](https://github.com/zig-nostr/plaza) · [read more](/plaza)
### deed: shipped
-The nostr command line: keys, events, NIP-19 codes, NIP-44, signature checks, relays, and a local store that later runs read without a network. One binary of under 3 MB for macOS and Linux.
+The nostr command line: keys, events, NIP-19 codes, NIP-44, signature checks, relays, and a local store that later runs read without a network. One binary of under 3 MB for macOS, Linux and Windows.
→ [`zig-nostr/deed`](https://github.com/zig-nostr/deed) · [read more](/deed)
### On the roadmap
Both apps are downloadable and both keep growing. What comes next lands inside
-them rather than as new apps: private messages, zaps and a wallet, groups, search
-and media all belong to the client people already have.
+them rather than as new apps: private messages, zaps and a wallet, groups, searching
+notes and playing video all belong to the client people already have.
See the [roadmap](/roadmap) for the order, and for what is deliberately not being
built.
diff --git a/content/getting-started.mdx b/content/getting-started.mdx
index 637d6ec..8eef7f1 100644
--- a/content/getting-started.mdx
+++ b/content/getting-started.mdx
@@ -1,14 +1,14 @@
# Getting Started
`zig-nostr` is a Nostr protocol library for [Zig](https://ziglang.org). It
-targets Zig **0.16** and is in **active development** (`v0.14.1`).
+targets Zig **0.16** and is in **active development** (`v0.14.9`).
## Install
Fetch the library into your project's `build.zig.zon`:
```sh
-zig fetch --save https://github.com/zig-nostr/nostr/archive/refs/tags/v0.14.1.tar.gz
+zig fetch --save https://github.com/zig-nostr/nostr/archive/refs/tags/v0.14.9.tar.gz
```
Then wire the module in `build.zig`:
@@ -21,6 +21,10 @@ your_module.addImport("nostr", nostr.module("nostr"));
The library vendors and compiles bitcoin-core's libsecp256k1 and LMDB from
source, so no system packages are required.
+## Platforms
+
+The library builds for macOS, Linux and Windows, on x86_64 and aarch64, and CI builds it and runs the tests on all three systems. To check a Windows build from another system, `zig build test -Dtarget=x86_64-windows-gnu` compiles the test binary and then stops with an error, because a non-Windows host cannot run it. A store takes its full map size on disk on Windows (1 GiB by default) from the first open, where on macOS and Linux it grows with what is kept.
+
## Quickstart: sign and verify an event
```zig
@@ -81,7 +85,7 @@ The `nostr` module re-exports focused namespaces:
- `nip44`: v2 payload encryption
- `nip46`: remote signing ("bunker") protocol
- `nip42`: client authentication to relays
-- `relay` / `websocket` / `message` / `filter`: live transport & subscriptions
+- `relay` / `websocket` / `message` / `filter`: live transport & subscriptions, with NIP-50 `search` on the filter
- `nip65`: relay lists and outbox routing
- `liveness`: when a relay connection has gone quiet, and what to do about it
- `store`: the local-first, memory-mapped event store
@@ -95,3 +99,5 @@ git clone https://github.com/zig-nostr/nostr
cd nostr
zig build test # run the suite
```
+
+To fuzz the parsers that read bytes from outside (frames, relay messages, events, NIP-19, NIP-44, NIP-46 and more) until you stop it, use `zig build test --fuzz -Doptimize=ReleaseSafe`.
diff --git a/content/index.mdx b/content/index.mdx
index 779caa1..abab6d8 100644
--- a/content/index.mdx
+++ b/content/index.mdx
@@ -30,7 +30,7 @@ title: The Nostr protocol, natively in Zig
📦
Core library
-
The foundation, keys, events, transport, the outbox model, and a local-first store. Actively growing toward full Nostr coverage, milestone after milestone.
+
The foundation, keys, events, transport, the outbox model, and a local-first store. It builds on macOS, Linux and Windows, and it is actively growing toward full Nostr coverage, milestone after milestone.
🧩
@@ -87,7 +87,7 @@ title: The Nostr protocol, natively in Zig
Notary
Shipped
- A native NIP-46 signer for macOS and Linux: Zig, no Electron. Your nsec stays in a local daemon, and nothing gets signed quietly.
+ A native NIP-46 signer for macOS and Linux: Zig, no Electron. Your nsec stays in a local daemon, and nothing gets signed quietly. On macOS it waits in the menu bar until a request needs you.
Learn more →
@@ -95,7 +95,7 @@ title: The Nostr protocol, natively in Zig
Plaza
Shipped
- A fast, local-first client: read without an account, post in four clicks, with the feed rendered from disk. A downloadable app for macOS and Linux.
+ A fast, local-first client: read without an account, post in four clicks, with the feed rendered from disk. It reads long-form articles, finds people by name and adds pictures from a file. A downloadable app for macOS and Linux.
Learn more →
@@ -103,7 +103,7 @@ title: The Nostr protocol, natively in Zig
deed
Shipped
- The nostr command line: sign, verify, encode, encrypt, query relays and publish, with a local store that answers without a network. One small binary for macOS and Linux.
+ The nostr command line: sign, verify, encode, encrypt, query relays and publish, with a local store that answers without a network. One small binary for macOS, Linux and Windows.
Learn more →
@@ -117,7 +117,7 @@ title: The Nostr protocol, natively in Zig
```sh
-zig fetch --save https://github.com/zig-nostr/nostr/archive/refs/tags/v0.14.1.tar.gz
+zig fetch --save https://github.com/zig-nostr/nostr/archive/refs/tags/v0.14.9.tar.gz
```
```zig
diff --git a/content/nips.mdx b/content/nips.mdx
index 61b3339..0e13d7a 100644
--- a/content/nips.mdx
+++ b/content/nips.mdx
@@ -10,12 +10,13 @@ official specification vectors.
| [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'/'/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 |
+| [19](https://github.com/nostr-protocol/nips/blob/master/19.md) | bech32-encoded entities: `npub`, `nsec`, `note`, `nprofile`, `nevent`, `naddr`. A code is read in all lowercase or all uppercase, and one that mixes the two is refused | ✅ done |
+| [21](https://github.com/nostr-protocol/nips/blob/master/21.md) | `nostr:` URI scheme, with the scheme matched in any case, so the `NOSTR:` a QR code carries decodes | ✅ 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 over `kind:24133` | ✅ done |
| [49](https://github.com/nostr-protocol/nips/blob/master/49.md) | Private key encryption (`ncryptsec`) | ✅ done |
+| [50](https://github.com/nostr-protocol/nips/blob/master/50.md) | Search: the `search` field of a filter, sent to relays as `"search"`. Relays decide what matches, so the library's own matching ignores it, and the local store applies it as a case-insensitive substring of the content | ✅ done |
| [65](https://github.com/nostr-protocol/nips/blob/master/65.md) | Relay list metadata, `kind:10002`, what people call the outbox model | ✅ done |
That is the library surface, and all of it is done. What the roadmap commits to
@@ -30,15 +31,21 @@ rather than library surface.
| NIP | Title | Where |
|----|----|----|
| [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 |
+| [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, and the search field resolves one to the person it names |
+| [19](https://github.com/nostr-protocol/nips/blob/master/19.md) | bech32-encoded entities, `nostr:` links | opens `npub`, `nprofile`, `note`, `nevent` and `naddr`. An address Plaza copies carries up to two relay hints, and the account menu copies an `nprofile` that names the relays you write to |
+| [10](https://github.com/nostr-protocol/nips/blob/master/10.md) | Reply threading, marked `e` tags | threads read by their markers, and replies you post with a `root` marker and a relay hint when Plaza knows one |
+| [18](https://github.com/nostr-protocol/nips/blob/master/18.md) | Reposts, `kind:6` and `kind:16` | reposts are read and shown in the feed as the note itself, counted on a note, and a row in notifications. A repost Plaza publishes carries the note's relay hint, and a quote is published as a `q` tag |
| [22](https://github.com/nostr-protocol/nips/blob/master/22.md) | Comments, `kind:1111`, the other way to write a reply | read in threads, on a profile, and as a notification |
| [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 | pressing one opens the notes carrying it, read from this machine first and then asked of relays. Written onto notes you post, so yours are findable too |
| [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 |
+| [27](https://github.com/nostr-protocol/nips/blob/master/27.md) | `nostr:` mentions in content | a reference to a person is shown as the name, and the first reference to a note or an article becomes a quote card. A quote you write is composed as one |
+| [23](https://github.com/nostr-protocol/nips/blob/master/23.md) | Long-form content, `kind:30023` | an article opens in a reader, from a press or from an `naddr`. A draft (`kind:30024`) is not opened, and Plaza does not write articles |
+| [36](https://github.com/nostr-protocol/nips/blob/master/36.md) | Sensitive content, the `content-warning` tag | a note that carries the tag is covered until you press Show, with nothing in it fetched, and the composer adds the tag when you ask |
+| [42](https://github.com/nostr-protocol/nips/blob/master/42.md) | Relay authentication | Plaza answers a relay's AUTH challenge only after the relay has refused something for want of one, and only once you have allowed it for that relay and account |
+| [50](https://github.com/nostr-protocol/nips/blob/master/50.md) | Search | three search relays are asked for people by name, and each result says which relay returned it. Profiles only, not notes |
+| [Blossom](https://github.com/hzrd149/blossom) | Media servers: BUD-02 upload, BUD-03 server list (`kind:10063`), BUD-06 preflight, BUD-11 authorization (`kind:24242`) | a picture goes to the first server in your list that takes it, or to one of two built in when you have none. Plaza checks a server first with BUD-06, signs the BUD-11 token, and reads and edits your list |
| [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 read off the tag, so an image reserves its box before it loads |
+| [92](https://github.com/nostr-protocol/nips/blob/master/92.md) | Media attachments, `imeta` | dimensions and placeholder read off the tag, so an image reserves its box before it loads, and written onto a picture Plaza uploads (url, type, hash, size, dimensions, blurhash and description) |
| [51](https://github.com/nostr-protocol/nips/blob/master/51.md) | Lists: bookmarks (`kind:10003`) and mutes (`kind:10000`) | bookmarks public or private, and a mute list that is read before it is ever written |
| [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 |
| [71](https://github.com/nostr-protocol/nips/blob/master/71.md) | Video events, `kind:21` and `kind:22` | a video is recognised as one and drawn as one. Playing it in place is still to come |
@@ -54,11 +61,6 @@ it.
| Spec | Title | Milestone | Status |
|----|----|----|----|
| [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 |
-| [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-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 |
@@ -70,9 +72,8 @@ it.
**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.
+called BUDs rather than NIPs. It sits in the apps table above because it is where
+file upload actually happens, and Plaza uploads a picture through it today.
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
diff --git a/content/notary.mdx b/content/notary.mdx
index 1093576..675f918 100644
--- a/content/notary.mdx
+++ b/content/notary.mdx
@@ -6,6 +6,21 @@ the NIP-46 "bunker" protocol, so your `nsec` lives in a local daemon on a machin
you control. Nothing gets signed quietly: you see what a client is asking for
before it happens, and the key never leaves the daemon unless you ask for it.
+## Install
+
+```sh
+curl -fsSL https://raw.githubusercontent.com/zig-nostr/notary/main/scripts/install.sh | bash
+```
+
+That works on macOS (Apple Silicon) and Linux (x86_64 and aarch64). It downloads the latest release for your system, verifies its SHA-256, installs it and starts it: `Notary.app` in `/Applications` on macOS, and everything under `~/.local` on Linux, so nothing needs root. One download brings up both halves: the window, and the daemon it spawns, which is where your key actually lives.
+
+- Linux needs GTK 4 and a recent distribution: Ubuntu 23.10+, Debian 13+ or Fedora 39+. Ubuntu 22.04 and Debian 12 are too old. The installer checks both before it downloads anything.
+- The macOS build is ad-hoc signed, not notarized, on purpose: Notary holds your key, so the trust anchor is a build you can reproduce rather than an Apple signature.
+- On macOS, choose **Quit Notary** in the menu bar before you install over a running copy.
+- Options go after `bash -s --`: `--version `, `--archive `, `--prefix ` and `--no-open`. The [README](https://github.com/zig-nostr/notary#install) explains each.
+
+Source, releases, and build instructions live at [`zig-nostr/notary`](https://github.com/zig-nostr/notary).
+
