From b3349f31570b7f728b84ac8e060e9f42fc4c712d Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Sun, 4 Oct 2026 18:06:06 +0300 Subject: [PATCH 1/9] docs: the library builds on Windows, takes a search filter, and is fuzzed The architecture, getting started, NIP support and performance pages now cover the Windows build, NIP-50 search on the filter, NIP-19 case handling, the heap merge in the store and the parser fuzz targets. --- content/architecture.mdx | 20 +++++++++++++++++--- content/getting-started.mdx | 8 +++++++- content/nips.mdx | 7 ++++--- content/performance.mdx | 15 +++++++++++++++ 4 files changed, 43 insertions(+), 7 deletions(-) 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/getting-started.mdx b/content/getting-started.mdx index 637d6ec..730f619 100644 --- a/content/getting-started.mdx +++ b/content/getting-started.mdx @@ -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/nips.mdx b/content/nips.mdx index 61b3339..2d729eb 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 @@ -54,7 +55,7 @@ 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 | +| [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. The library's `Filter` carries it already, so the client side is what is left | 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 | diff --git a/content/performance.mdx b/content/performance.mdx index 42330e0..f5f19af 100644 --- a/content/performance.mdx +++ b/content/performance.mdx @@ -23,6 +23,19 @@ much history sits behind it. That's the **bounded query planner**: it walks the indexes newest-first and stops at `limit`, so latency tracks the page size you ask for, not the size of the store. +### A long follow list + +A client that follows hundreds of people queries for all of them at once, and the store merges one stream per author, newest first. The merge takes the newest candidate from a binary heap of the live streams rather than scanning all of them for every note it returns, so adding authors costs a sift of the heap and not another pass over every author. The order of the results is unchanged. This is a 60-note `kind:1` query with one stream per author, in microseconds, `ReleaseFast`, best of five runs, before and after the heap. + +| Authors followed | Before | After | +|----|----|----| +| 20 | 46.6 | 46.0 | +| 128 | 114.5 | 93.2 | +| 512 | 357.8 | 253.9 | +| 2,048 | 1,430.1 | 1,006.4 | + +What remains at large follow lists is mostly opening a cursor per author, which a heap does not change. + ## Reproduce the library numbers ```sh @@ -31,6 +44,8 @@ cd nostr BENCH_N=100000 zig build bench -Doptimize=ReleaseFast ``` +`BENCH_AUTHORS` sets how many authors the store is spread across, which is also how many streams the wide feed query merges. + The benchmark source is [`src/bench.zig`](https://github.com/zig-nostr/nostr/blob/main/src/bench.zig). Absolute numbers vary with hardware; the *shape*, flat feed latency as the From 062ff0b7f094933620fdcaa706b878e918d830f8 Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Sun, 4 Oct 2026 18:06:06 +0300 Subject: [PATCH 2/9] docs: deed on Windows and its store errors, and Notary in the menu bar The deed page gains the Windows install, the store directories it creates and why a store will not open, and uppercase NIP-19 codes; the Notary page describes staying in the macOS menu bar. --- content/deed.mdx | 30 +++++++++++++++++++++++++++--- content/notary.mdx | 10 ++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/content/deed.mdx b/content/deed.mdx index a2997db..0e16ec2 100644 --- a/content/deed.mdx +++ b/content/deed.mdx @@ -1,6 +1,6 @@ # 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. @@ -12,6 +12,24 @@ curl -fsSL https://raw.githubusercontent.com/zig-nostr/deed/main/scripts/install 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. +The script is for macOS and Linux. On Windows, download `deed--windows-x86_64.zip` (or `windows-aarch64` on an ARM machine) and the `.sha256` beside it from the [releases page](https://github.com/zig-nostr/deed/releases), then in PowerShell, from the folder they are in: + +```powershell +$zip = (Get-Item deed-*-windows-*.zip).Name +$want = (Get-Content "$zip.sha256").Split(' ')[0] +$got = (Get-FileHash $zip -Algorithm SHA256).Hash +if ($got -ne $want) { throw "the SHA-256 does not match, do not run this" } + +$dir = "$env:LOCALAPPDATA\deed" +Expand-Archive $zip -DestinationPath $dir -Force +$user = [Environment]::GetEnvironmentVariable('Path', 'User') +if (($user -split ';') -notcontains $dir) { + [Environment]::SetEnvironmentVariable('Path', "$user;$dir".TrimStart(';'), 'User') +} +``` + +That checks the download, unpacks `deed.exe` into `%LOCALAPPDATA%\deed`, and puts that folder on your `PATH` unless it is there already. Open a new terminal and `deed version` confirms it. The binary is not code-signed, so Windows may show a warning the first time it runs. + ## What it does | Verb | | @@ -51,6 +69,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 +88,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/notary.mdx b/content/notary.mdx index 1093576..efcaa26 100644 --- a/content/notary.mdx +++ b/content/notary.mdx @@ -59,6 +59,14 @@ The passphrase is asked for either way, including the encrypted form that does not strictly need it: an unlocked signer is the normal state, and whoever is at the keyboard then is not necessarily the person who set it up. +## Staying in the background + +On macOS, closing the window does not quit Notary. The signer keeps answering, and an `N` item in the menu bar shows how many requests are waiting. When a request arrives the window comes back to the front, and when you have answered the last one it goes away again. If you opened the window yourself, it stays open until you close it. + +The menu bar item has **Open Notary** to bring the window back by hand and **Quit Notary** to stop. Quitting stops the signer too, so nothing is left running with your key. If the signer stops by itself the window comes back and says so, rather than leaving a menu bar item that no longer answers anything. + +Notary has no Dock icon on macOS. A window that another app opened for its own keyholder behaves as it always did: it has a Dock icon, and closing it ends it. On Linux closing the window quits, because the toolkit's Linux host has no status item that could bring a hidden window back. + ## Install **macOS (Apple Silicon):** @@ -88,6 +96,8 @@ Either way one download brings up both halves: the window, and the daemon it spawns, which is where your key actually lives. Source, releases, and build instructions live at [`zig-nostr/notary`](https://github.com/zig-nostr/notary). +[`ARCHITECTURE.md`](https://github.com/zig-nostr/notary/blob/main/ARCHITECTURE.md) in the repository explains how the two processes talk, the daemon's modules, how the key is stored with NIP-49, and how the window supervises the daemon. + ## Supported operations `connect` · `sign_event` · `ping` · `get_public_key` · `nip44_encrypt` · `nip44_decrypt` · `logout` From e714254c9c3378a766fc0a58388014983c4e24be Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Sun, 4 Oct 2026 18:06:06 +0300 Subject: [PATCH 3/9] docs: bring the home page, ecosystem, roadmap and llms.txt up to date Platform lines, the shipped list and the Windows statements now match the code, and the roadmap no longer says Windows is missing from CI. --- content/ecosystem.mdx | 7 ++++--- content/index.mdx | 6 +++--- content/roadmap.mdx | 21 +++++++++++++-------- public/llms.txt | 6 +++--- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/content/ecosystem.mdx b/content/ecosystem.mdx index efeb26e..3994fc7 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,7 +27,7 @@ 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 @@ -38,7 +39,7 @@ process, or in your own signer. ### 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 diff --git a/content/index.mdx b/content/index.mdx index 779caa1..2d40caa 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 →
@@ -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 → diff --git a/content/roadmap.mdx b/content/roadmap.mdx index e527b8f..b1c406c 100644 --- a/content/roadmap.mdx +++ b/content/roadmap.mdx @@ -19,9 +19,17 @@ No dates. The order is a commitment; a calendar would not be. 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. +- **Windows, search and fuzzing in the library**: it builds for macOS, Linux and Windows, on + x86_64 and aarch64, with CI that builds and tests on all three. The filter carries the NIP-50 + `search` field, which the local store applies too. The parsers that read bytes someone else + wrote have fuzz targets, and the two crashes the first run found are fixed. - **[Notary](/notary)**, the native remote signer. Your key lives in a local daemon, it hands you a `bunker://` URL, and nothing gets signed quietly. A downloadable app for - macOS and Linux. + macOS and Linux. On macOS it stays in the menu bar and shows its window only when a request + needs a decision. +- **[deed](/deed)**, the nostr command line: keys, events, NIP-19 codes, NIP-44, relay queries + and publishing, and a local store that later runs read without a network. It builds for + macOS, Linux and Windows. - **[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. @@ -116,9 +124,8 @@ renderer gaps that made that unusable are fixed and filed upstream. What is stil is coverage: the bundled faces carry Latin and Cyrillic, so every other script, Greek included, is drawn as a solid block until a broader face ships. Shaping is a second problem behind it: the reference rasteriser reads no GSUB or GPOS, so Arabic and the Indic scripts -would still be wrong with a perfect face. Windows is not in CI at -all: the relay transport resolves hostnames through libc `getaddrinfo`, which Zig's standard -library does not declare for Windows. +would still be wrong with a perfect face. Windows is covered by the library and by deed, which +build there and are tested in CI. Plaza and Notary list macOS and Linux as their platforms. And the library at 1.0. It has been pre-1.0 on purpose while the API moved underneath it. Everything above lands there first, so by the time the rest of this is done the parts worth @@ -129,10 +136,8 @@ freezing have stopped moving. Naming these is half of a roadmap, because each looks obviously worth doing until you price it. -- **A packaged Windows release, for now.** The relay transport resolves hostnames through - libc `getaddrinfo`, which Zig's standard library does not declare for Windows, so nothing - depending on the library links there. That is a gap in the protocol library rather than in - any app, and Windows joins the matrix as soon as it closes. +- **Plaza and Notary for Windows.** The library and deed build for Windows, but the two + apps list macOS and Linux as their platforms, so neither has a Windows package. - **Multi-account.** It taxes every feature after it: per-account stores, relay pools, notification state, and switching without a reconnect storm. A read-only "view as" answers most of the real demand. diff --git a/public/llms.txt b/public/llms.txt index fe968c6..b9143f1 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -6,10 +6,10 @@ Every page on this site is also served as Markdown at its path with `.md` added ## Projects -- [nostr, the Zig library](/getting-started.md): keys and BIP-340 signatures via libsecp256k1, NIP-01 events, NIP-19, NIP-44, NIP-46, NIP-42, NIP-49, NIP-06, relay transport with deadlines, the NIP-65 outbox model, and a memory-mapped LMDB event store. Source: https://github.com/zig-nostr/nostr -- [deed, the nostr command line](/deed.md): keys, signing, verifying, NIP-19, NIP-44, relay queries, publishing, and a local store it can query offline. One binary for macOS and Linux. Source: https://github.com/zig-nostr/deed +- [nostr, the Zig library](/getting-started.md): keys and BIP-340 signatures via libsecp256k1, NIP-01 events, NIP-19, NIP-44, NIP-46, NIP-42, NIP-49, NIP-06, relay transport with deadlines, the NIP-65 outbox model, NIP-50 `search` on the filter, and a memory-mapped LMDB event store. Builds for macOS, Linux and Windows, and the parsers that read outside bytes have fuzz targets. Source: https://github.com/zig-nostr/nostr +- [deed, the nostr command line](/deed.md): keys, signing, verifying, NIP-19, NIP-44, relay queries, publishing, and a local store it can query offline. One binary for macOS, Linux and Windows. Source: https://github.com/zig-nostr/deed - [Plaza](/plaza.md): a fast, local-first nostr client for macOS and Linux, rendered from its own store. Source: https://github.com/zig-nostr/plaza -- [Notary](/notary.md): a native NIP-46 remote signer for macOS and Linux; the key stays in a local daemon on a machine you control. Source: https://github.com/zig-nostr/notary +- [Notary](/notary.md): a native NIP-46 remote signer for macOS and Linux; the key stays in a local daemon on a machine you control, and on macOS it waits in the menu bar until a request needs a decision. Source: https://github.com/zig-nostr/notary ## For agents From 733e1e0f9d917ff3cfb9a07101b3b8fe1206a988 Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Sun, 4 Oct 2026 19:32:25 +0300 Subject: [PATCH 4/9] docs: point the install lines at nostr v0.14.8, and Windows deed at its x86_64 zip The library's fetch lines named v0.14.1, and deed ships one Windows zip, for x86_64. --- content/deed.mdx | 2 +- content/getting-started.mdx | 4 ++-- content/index.mdx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/deed.mdx b/content/deed.mdx index 0e16ec2..aed1b12 100644 --- a/content/deed.mdx +++ b/content/deed.mdx @@ -12,7 +12,7 @@ curl -fsSL https://raw.githubusercontent.com/zig-nostr/deed/main/scripts/install 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. -The script is for macOS and Linux. On Windows, download `deed--windows-x86_64.zip` (or `windows-aarch64` on an ARM machine) and the `.sha256` beside it from the [releases page](https://github.com/zig-nostr/deed/releases), then in PowerShell, from the folder they are in: +The script is for macOS and Linux. On Windows, download `deed--windows-x86_64.zip` and the `.sha256` beside it from the [releases page](https://github.com/zig-nostr/deed/releases), then in PowerShell, from the folder they are in: ```powershell $zip = (Get-Item deed-*-windows-*.zip).Name diff --git a/content/getting-started.mdx b/content/getting-started.mdx index 730f619..f36bddd 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.8`). ## 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.8.tar.gz ``` Then wire the module in `build.zig`: diff --git a/content/index.mdx b/content/index.mdx index 2d40caa..6f22c1b 100644 --- a/content/index.mdx +++ b/content/index.mdx @@ -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.8.tar.gz ``` ```zig From b810083f3790cc6d3ac6463574832550e801eecc Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Sun, 4 Oct 2026 19:48:35 +0300 Subject: [PATCH 5/9] docs: deed's structured data lists Windows deed v0.4.1 ships a Windows zip. --- app/structured-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/structured-data.js b/app/structured-data.js index 0772ebd..3aedb10 100644 --- a/app/structured-data.js +++ b/app/structured-data.js @@ -46,7 +46,7 @@ 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.')], + 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.', { 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' })], } From 54368b743794d2dae624dcd3017020849470b33f Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Sun, 4 Oct 2026 20:14:27 +0300 Subject: [PATCH 6/9] docs: Plaza page and NIP table cover articles, search, uploads, sensitive notes and AUTH The Plaza page gains sections for search and addresses, articles, pictures, sensitive notes and relays that ask who you are, and says it carries Notary 0.11.1, and the apps table lists NIP-23, 36, 42, 50, Blossom and the relay hints and imeta Plaza now writes. --- content/nips.mdx | 26 ++++++++++----------- content/plaza.mdx | 58 +++++++++++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/content/nips.mdx b/content/nips.mdx index 2d729eb..0e13d7a 100644 --- a/content/nips.mdx +++ b/content/nips.mdx @@ -31,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 | @@ -55,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. The library's `Filter` carries it already, so the client side is what is left | 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 | @@ -71,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/plaza.mdx b/content/plaza.mdx index 21e37fc..ce526d4 100644 --- a/content/plaza.mdx +++ b/content/plaza.mdx @@ -104,8 +104,7 @@ doing. ## Your key is not in the app Plaza does not hold your secret key. There is no field in it that can hold one. -Notary, shipped inside the app, is started by Plaza as its own process and holds -the key there; Plaza asks it to sign and never sees the secret. +Notary, shipped inside the app (Plaza v0.25.0 carries Notary 0.11.1), is started by Plaza as its own process and holds the key there; Plaza asks it to sign and never sees the secret. The two talk over a channel nothing else on this Mac can reach: no shared file, no fixed port, and no name for anything else to find. Holding the channel is @@ -131,28 +130,37 @@ external signer is a first-class path rather than an advanced setting. ## What is in it today -A follow-based feed with a curated starter pack so it is never empty · real names -and avatars from `kind:0` · inline pictures and link previews · threads with the -chain above a note and replies nested under it · quotes and `nostr:` mentions -rendered inline · a composer with a mention picker and drafts that survive a quit -· reactions · bookmarks, public or private, saved as a list on your own relays so -they follow you to any other client · hashtags that open the notes carrying them, -read from your own machine first · deleting your own note, with a confirmation -that says what a deletion can and cannot do · notifications for what was aimed at -you · profile pages with a following count · your own relay list, read and write · -a feed routed by [NIP-65](/nips) to where the people you follow actually publish · -an address field that opens any note, person or place you paste into it, including -ones on relays you do not read · eight switches that take parts of the app away -for good · places you can enter and leave · settings, sessions, and sign-out -without lock-in. - -There is no follower count anywhere, on purpose. Following is a number someone -states about themselves, and it is the length of their own contact list. -Followers is not: nothing on your disk can know who follows a person, and the -honest options are an indexer's figure or none. Plaza does not print numbers it -cannot check. - -Private messages are two milestones out on the [roadmap](/roadmap). +A follow-based feed with a curated starter pack so it is never empty · reposts from people you follow, shown as the note itself · real names and avatars from `kind:0` · inline pictures and link previews · threads with the chain above a note and replies nested under it, including replies written as NIP-22 comments · quotes and `nostr:` mentions rendered inline · a composer with a mention picker and drafts that survive a quit, and a half written reply that survives leaving its thread · reactions · bookmarks, public or private, saved as a list on your own relays so they follow you to any other client · hashtags that open a page of the notes carrying them, read from your own machine first · long-form articles in a reader · search for people by name · pictures from a file, for a note, an avatar or a banner · a content warning on a note you write, and covers over the ones other people mark sensitive · deleting your own note, with a confirmation that says what a deletion can and cannot do · notifications for what was aimed at you · profile pages with a following count that load older notes as you reach the end · every field of your profile editable, the Lightning address included · your own relay list, read and write · a feed routed by [NIP-65](/nips) to where the people you follow actually publish · an address field that opens any note, person, article or place you paste into it, including ones on relays you do not read · eight switches that take parts of the app away for good · places you can enter and leave · settings, sessions, and sign-out without lock-in. + +There is no follower count anywhere, on purpose. Following is a number someone states about themselves, and it is the length of their own contact list. Followers is not: nothing on your disk can know who follows a person, and the honest options are an indexer's figure or none. Plaza does not print numbers it cannot check. + +Not there yet: playing a video where it sits, sending a zap, searching notes, and private messages. See the [roadmap](/roadmap). + +## Search and addresses + +The Search tile in the rail, or Cmd+L, takes a name and lists the profiles that match. The people already on your machine answer first, instantly and with the network off, with the people you follow at the top. Once you stop typing, three NIP-50 search relays are asked as well. Each result says which relay returned it, and every relay gets a line saying what it did, whether it found anyone or not. This searches people, not notes. + +The same field opens what you paste into it: an `npub` or `nprofile`, a `name@domain` NIP-05 address, a note, an article, or a place. An address that carries relay hints is asked there too, so a note on relays you do not read still opens. A secret key or a signer link typed into the field is never sent anywhere. + +Plaza gives hints back. An address it copies, and the reply, quote, repost and like it publishes, name a relay the note was seen on or one its author writes to, and name none when Plaza does not know one. The account menu copies a profile address that names the relays you write to. A relay hint in somebody else's note that points at a private or local address is not dialled. + +## Articles + +A long-form article (NIP-23) opens in a reader of its own: the byline, title, summary and cover with a date and a reading time, then the article, with its hashtags as topics you can press. An `naddr` for an article, in a note, in a quote or in the search field, opens it, asking the relays it names and the author's own. A draft is not opened, and Plaza reads articles without writing them. + +## Pictures + +The composer has Add picture, and the profile editor has an Upload link beside Picture and Banner. A card names the file, its size and the servers it will go to, and nothing is sent until you press Upload. Location and camera details are removed from the file first. It goes to a [Blossom](https://github.com/hzrd149/blossom) media server: the first one in your published list that takes it, or one of two built in when you have none. The Media servers section of Settings shows the list and edits it. + +A first server list for an account that has none on any relay is started only once the relays you write to have finished answering, and only after Plaza asks. A list it has not read is never written over a real one, as described under [Careful with what is yours](#careful-with-what-is-yours). + +## Sensitive notes + +A note whose author marked it sensitive (NIP-36) shows one line, "Content warning" and the reason when there is one, with a Show press, in place of its words, pictures and link preview. Nothing in it is fetched until you press Show, and Show uncovers that one note for the session. "Show sensitive notes without a warning" in Settings turns the covering off. When you write a note, "Add a content warning" in the composer adds one, with an optional reason. + +## Relays that ask who you are + +Some relays refuse to serve until you identify yourself (NIP-42). Plaza asks you once per relay, for each account: Allow or Don't allow. It asks only when a relay has actually refused something, so a relay that sends a challenge and gates nothing is never asked about. A relay's row in Settings shows your choice (ask first, identify or anonymous), and pressing it changes the choice. ## Where the feed comes from @@ -257,6 +265,8 @@ profiles it carries forward everything in yours that it does not model itself. An app that drops half your follows because a relay was slow to answer is worse than an app that does nothing at all. +When Follow, Mute or Bookmark cannot read your lists, Plaza says why and offers Try again. An account with no list on any relay can start one, after a question that says what it would replace, and only once the relays you write to have finished. Media server lists follow the same rule. + ## Fast on purpose The feed is a windowed list: it builds only the rows near the viewport, so its From bed2cd7c2ec5b901f75f73393549b065983c9793 Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Sun, 4 Oct 2026 20:14:27 +0300 Subject: [PATCH 7/9] docs: roadmap, home page, ecosystem, llms.txt and structured data describe the current Plaza Shipped Plaza work moves out of what comes next, leaving zaps you can send and searching notes as the open items, and the Plaza summaries mention articles, people search and picture upload. --- app/structured-data.js | 2 +- content/ecosystem.mdx | 6 +++--- content/index.mdx | 2 +- content/roadmap.mdx | 34 +++++++--------------------------- public/llms.txt | 2 +- 5 files changed, 13 insertions(+), 33 deletions(-) diff --git a/app/structured-data.js b/app/structured-data.js index 3aedb10..86e1eed 100644 --- a/app/structured-data.js +++ b/app/structured-data.js @@ -47,7 +47,7 @@ const byPage = { ], '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.', { 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.', { applicationCategory: 'SocialNetworkingApplication' })], + 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/ecosystem.mdx b/content/ecosystem.mdx index 3994fc7..187ea57 100644 --- a/content/ecosystem.mdx +++ b/content/ecosystem.mdx @@ -33,7 +33,7 @@ unless you ask for it. On macOS it waits in the menu bar and shows its window on ### 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) @@ -45,8 +45,8 @@ The nostr command line: keys, events, NIP-19 codes, NIP-44, signature checks, re ### 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/index.mdx b/content/index.mdx index 6f22c1b..f77c623 100644 --- a/content/index.mdx +++ b/content/index.mdx @@ -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 →
diff --git a/content/roadmap.mdx b/content/roadmap.mdx index b1c406c..9ab36e5 100644 --- a/content/roadmap.mdx +++ b/content/roadmap.mdx @@ -30,9 +30,7 @@ No dates. The order is a commitment; a calendar would not be. - **[deed](/deed)**, the nostr command line: keys, events, NIP-19 codes, NIP-44, relay queries and publishing, and a local store that later runs read without a network. It builds for macOS, Linux and Windows. -- **[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. +- **[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. Reposts from people you follow appear in it, hashtags open a page of their notes, and relay hints are carried in the addresses it copies and the replies, quotes, reposts and likes it publishes. A long-form article (NIP-23) opens in a reader. Search finds people by name, from the profiles on your machine first and then from three NIP-50 search relays. A picture can be added from a file to a note, an avatar or a banner, and uploads go to a Blossom server from your list. A note marked sensitive (NIP-36) is covered until you press Show, and the composer can add a content warning. A relay that asks who you are (NIP-42) is answered once you allow it, per relay and per account. Every profile field is editable, deleting your own note is there, and something Plaza cannot draw shows a card naming its kind. - **Places**: a link opens somebody's corner of Nostr, their relays and what they serve, and while you are in one you see it rather than your own feed. Visit, enter to keep it on a rail, or leave. A community can carry several feeds and switch between them, and one that @@ -52,22 +50,7 @@ No dates. The order is a commitment; a calendar would not be. ### Everything you do on the first day -Four things are already built and do not work. Reposts by people you follow never reach the -feed: Plaza publishes them and counts other people's, but nothing asks a relay for kind 6. -Relay hints are decoded and dropped, so a quoted note from outside your relays renders dead -even though its author said where it lives. Hashtags are drawn like every other link and -carry no target. The bookmark button does nothing. - -Then the ordinary parts. Pictures, which is one absence with three symptoms: no posting a -photo, no avatar, no banner. Uploading is [Blossom](https://github.com/hzrd149/blossom) -rather than a NIP: BUD-02 `PUT /upload`, authorised by a signed kind 24242 event (BUD-11), -aimed at the servers in your kind 10063 list (BUD-03), with a NIP-92 `imeta` tag so they -render elsewhere. A profile with more than three fields, because Plaza reads nine out of a -kind 0 and lets you edit three, and `lud16` is not one of them, so an account set up only in -Plaza cannot receive a zap at all. Zaps you can send, not only verify. Finding a person, both -locally over the profiles already on your disk and through NIP-50 where a relay supports it. -Deleting your own note, NIP-36 content warnings, and something drawn in place of a kind Plaza -cannot render. +Zaps you can send, not only verify. Plaza shows the receipts of other people's zaps, and sending one is not built. ### An app worth leaving open @@ -75,12 +58,12 @@ Nothing Plaza knows leaves the window today: no notification reaches you, and on you follow run dry there is nothing else to read. Notifications at the level of the system, which a native app holding live relay connections -can do with no server in the middle. Feeds with more than one shape: a hashtag, a single +can do with no server in the middle. Feeds with more than one shape: a single relay, a saved relay set, follow packs as real events rather than a hardcoded list. A store you can see and reclaim, because Plaza keeps everything it has ever ingested and gives you no way to see how large that has grown. Muting a word, a hashtag or a thread, and for an afternoon rather than forever. An update path, so people who installed are not stranded on -whatever version they downloaded. Operable without a mouse, a light theme, and type that +whatever version they downloaded. Keyboard operation of what Tab does not yet reach, a light theme, and type that scales. Places go further: the entered list as an encrypted event so it follows you between @@ -94,9 +77,7 @@ you agree to rather than something that happens. ### What the local store was for -Everything Plaza does reads from a database on your own disk, and none of it can be searched. -Finding a note you read last week, by words you remember, with the network off, is the thing -a client without a local store cannot offer. +Everything Plaza does reads from a database on your own disk, and the only thing it can search there today is people. Finding a note you read some time ago, by words you remember, with the network off, is the thing a client without a local store cannot offer. 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 @@ -104,8 +85,7 @@ relays. The seal's conversation key derives from the identity key, so the signer NIP-44 work; Notary and the library already do, and it is Plaza's own NIP-46 client that has to learn to ask for it. -NIP-23 long-form, which is a large part of what people write here and which Plaza cannot -display. NIP-71 video that actually plays, which in a renderer with no browser means platform +NIP-71 video that actually plays, which in a renderer with no browser means platform decode and compositing rather than an embed. And NIP-60 wallets with NIP-61 nutzaps, Cashu ecash sent to a pubkey: a Lightning zap needs an LNURL provider the recipient signed up for in advance, and an account four clicks old has none, so this is what makes a new person @@ -154,7 +134,7 @@ you price it. rejection that starts `pow:`, needs no setting at all. - **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 6) and nowhere + strikes it through as replaced by Blossom, so uploads go to Blossom 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 diff --git a/public/llms.txt b/public/llms.txt index b9143f1..f4acaaa 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -8,7 +8,7 @@ Every page on this site is also served as Markdown at its path with `.md` added - [nostr, the Zig library](/getting-started.md): keys and BIP-340 signatures via libsecp256k1, NIP-01 events, NIP-19, NIP-44, NIP-46, NIP-42, NIP-49, NIP-06, relay transport with deadlines, the NIP-65 outbox model, NIP-50 `search` on the filter, and a memory-mapped LMDB event store. Builds for macOS, Linux and Windows, and the parsers that read outside bytes have fuzz targets. Source: https://github.com/zig-nostr/nostr - [deed, the nostr command line](/deed.md): keys, signing, verifying, NIP-19, NIP-44, relay queries, publishing, and a local store it can query offline. One binary for macOS, Linux and Windows. Source: https://github.com/zig-nostr/deed -- [Plaza](/plaza.md): a fast, local-first nostr client for macOS and Linux, rendered from its own store. Source: https://github.com/zig-nostr/plaza +- [Plaza](/plaza.md): a fast, local-first nostr client for macOS and Linux, rendered from its own store. It reads long-form articles (NIP-23), searches for people by name (NIP-50), uploads pictures to Blossom servers, covers notes marked sensitive (NIP-36) and answers a relay's AUTH challenge (NIP-42) when you allow it. Source: https://github.com/zig-nostr/plaza - [Notary](/notary.md): a native NIP-46 remote signer for macOS and Linux; the key stays in a local daemon on a machine you control, and on macOS it waits in the menu bar until a request needs a decision. Source: https://github.com/zig-nostr/notary ## For agents From 73ec5803aa087c2f4f347c0bc56fc1d28ed0648b Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Sun, 4 Oct 2026 22:01:30 +0300 Subject: [PATCH 8/9] docs: point at nostr v0.14.9 and Notary 0.11.2 The patch releases that Plaza v0.25.0 carries. --- content/getting-started.mdx | 4 ++-- content/index.mdx | 2 +- content/plaza.mdx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/getting-started.mdx b/content/getting-started.mdx index f36bddd..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.8`). +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.8.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`: diff --git a/content/index.mdx b/content/index.mdx index f77c623..abab6d8 100644 --- a/content/index.mdx +++ b/content/index.mdx @@ -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.8.tar.gz +zig fetch --save https://github.com/zig-nostr/nostr/archive/refs/tags/v0.14.9.tar.gz ``` ```zig diff --git a/content/plaza.mdx b/content/plaza.mdx index ce526d4..09435e7 100644 --- a/content/plaza.mdx +++ b/content/plaza.mdx @@ -104,7 +104,7 @@ doing. ## Your key is not in the app Plaza does not hold your secret key. There is no field in it that can hold one. -Notary, shipped inside the app (Plaza v0.25.0 carries Notary 0.11.1), is started by Plaza as its own process and holds the key there; Plaza asks it to sign and never sees the secret. +Notary, shipped inside the app (Plaza v0.25.0 carries Notary 0.11.2), is started by Plaza as its own process and holds the key there; Plaza asks it to sign and never sees the secret. The two talk over a channel nothing else on this Mac can reach: no shared file, no fixed port, and no name for anything else to find. Holding the channel is From e9290a47a8096579dac81c81a15d0b2e83336d9c Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Sun, 4 Oct 2026 23:52:34 +0300 Subject: [PATCH 9/9] docs: install deed and Notary with one command each, near the top of their pages deed has one line for macOS and Linux and one for Windows; Notary has one for macOS and Linux. --- content/deed.mdx | 22 +++++++-------------- content/notary.mdx | 48 ++++++++++++++++------------------------------ 2 files changed, 24 insertions(+), 46 deletions(-) diff --git a/content/deed.mdx b/content/deed.mdx index aed1b12..2a55af0 100644 --- a/content/deed.mdx +++ b/content/deed.mdx @@ -6,29 +6,21 @@ A deed is two things at once: a signed instrument, and a thing done. So is a nos ## 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. - -The script is for macOS and Linux. On Windows, download `deed--windows-x86_64.zip` and the `.sha256` beside it from the [releases page](https://github.com/zig-nostr/deed/releases), then in PowerShell, from the folder they are in: +Windows, in PowerShell: ```powershell -$zip = (Get-Item deed-*-windows-*.zip).Name -$want = (Get-Content "$zip.sha256").Split(' ')[0] -$got = (Get-FileHash $zip -Algorithm SHA256).Hash -if ($got -ne $want) { throw "the SHA-256 does not match, do not run this" } - -$dir = "$env:LOCALAPPDATA\deed" -Expand-Archive $zip -DestinationPath $dir -Force -$user = [Environment]::GetEnvironmentVariable('Path', 'User') -if (($user -split ';') -notcontains $dir) { - [Environment]::SetEnvironmentVariable('Path', "$user;$dir".TrimStart(';'), 'User') -} +irm https://raw.githubusercontent.com/zig-nostr/deed/main/scripts/install.ps1 | iex ``` -That checks the download, unpacks `deed.exe` into `%LOCALAPPDATA%\deed`, and puts that folder on your `PATH` unless it is there already. Open a new terminal and `deed version` confirms it. The binary is not code-signed, so Windows may show a warning the first time it runs. +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 diff --git a/content/notary.mdx b/content/notary.mdx index efcaa26..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). +