From 47a7b5d7719dcca3efc5e394352f1cc436936f5d Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Fri, 31 Jul 2026 16:32:50 +0300 Subject: [PATCH 1/2] docs: give Plaza its own page, and its name everywhere it was anonymous The site described "the flagship client" in three places and never once said what it was called, which makes the one app I am building hardest to find the one nobody can look up. A page of its own now: reading before identity, where the key actually lives (a separate keyholder process, or your own signer, never the app), what is in it today, why replaceable events get read back before they are written, and the frame budget the feed holds while scrolling. The home page, the ecosystem list and the roadmap all name it and link to it. It says plainly that there is nothing to download yet and gives the source build instead. A one-line installer belongs on this page the day the first tag exists and not an hour before, because a curl that resolves to "no release published yet" is worse than no curl at all. --- content/_meta.js | 1 + content/ecosystem.mdx | 11 +++-- content/index.mdx | 2 +- content/plaza.mdx | 112 ++++++++++++++++++++++++++++++++++++++++++ content/roadmap.mdx | 3 +- 5 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 content/plaza.mdx diff --git a/content/_meta.js b/content/_meta.js index 3f9261c..ec17a59 100644 --- a/content/_meta.js +++ b/content/_meta.js @@ -15,6 +15,7 @@ export default { ecosystem: 'Ecosystem', architecture: 'Architecture', nips: 'NIP Support', + plaza: 'Plaza', signer: 'Signet', performance: 'Performance', roadmap: 'Roadmap', diff --git a/content/ecosystem.mdx b/content/ecosystem.mdx index 70bf72d..6fc06f9 100644 --- a/content/ecosystem.mdx +++ b/content/ecosystem.mdx @@ -22,17 +22,18 @@ More arrive as the ecosystem grows — they'll be listed here as they ship. Real products built entirely on the core — proof it holds up under product weight, and the reason the ecosystem exists. -### Signet — shipped +### Signet: shipped A NIP-46 remote signer for macOS. Your key lives in a local daemon; every signing request waits for your approval, and your `nsec` never leaves it. → [`zig-nostr/signet`](https://github.com/zig-nostr/signet) · [read more](/signer) -### The flagship client — in progress +### Plaza: in progress -A fast, local-first client: browse and post within two minutes, with the feed -rendered from disk and reconciled in the background. -A focused, polished vertical slice first — depth over breadth. +The flagship client: browse and post within two minutes, with the feed rendered +from disk and reconciled in the background. Your key lives in a separate +keyholder process, or in your own signer, never in the app. +→ [`zig-nostr/plaza`](https://github.com/zig-nostr/plaza) · [read more](/plaza) ### On the roadmap diff --git a/content/index.mdx b/content/index.mdx index 22af5dd..47e7bef 100644 --- a/content/index.mdx +++ b/content/index.mdx @@ -40,7 +40,7 @@ title: The Nostr protocol, natively in Zig
🖥️

Native apps

-

Signet ships today; a fast local-first client, a messenger, and more are on the way across desktop and mobile. Real products, all on one core.

+

Signet ships today, Plaza is the flagship client in progress, and a messenger and more are on the way across desktop and mobile. Real products, all on one core.

diff --git a/content/plaza.mdx b/content/plaza.mdx new file mode 100644 index 0000000..2ee87eb --- /dev/null +++ b/content/plaza.mdx @@ -0,0 +1,112 @@ +# Plaza, the flagship client + +**Plaza** is a fast, local-first Nostr client for macOS, built natively in Zig on +the [`zig-nostr`](https://github.com/zig-nostr/nostr) core. No browser, no +Electron, no interpreter in the binary: the toolkit draws every pixel, and the +event store runs inside the app's own process rather than behind an IPC boundary. + +Two things it tries to do well. You get from launch to reading in seconds and to +posting in about two minutes. And the feed comes off local disk, so it is +already there when the window opens and reconciles with relays behind you. + +## Reading first, identity later + +Plaza opens into a feed. Not a welcome wall, not a sign-up form: reading Nostr +never needed a key, so the app does not ask for one until you reach for +something that does. Browse as a guest for as long as you like. + +When you do want in, there are three ways and you pick the one that fits: + +- **Create an identity.** A key is minted for you, in seconds, with nothing to + write down up front. +- **Bring a key you already have.** It goes into the keyholder, not into the app. +- **Use your own signer.** Paste a `bunker://` link and Plaza signs through + [Signet](/signer) or any other NIP-46 signer, with the key never leaving it. + +Whatever you reached for when you were asked is remembered and finished for you +afterwards, so signing in never costs you the thing you were doing. + +## Your key is not in the app + +Plaza does not hold your secret key, even when it makes one for you. A separate +keyholder process owns it, reached over authenticated loopback, and the app asks +it to sign. The ceremony that mints or imports a key runs in its own window, so +you can see the process that is about to hold your key at the moment it starts +holding it. + +That is the same design as [Signet](/signer), and it is why connecting an +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 · notifications for what was aimed at you · profile pages with +follows and followers · your own relay list, read and write, routed by +[NIP-65](/nips) · settings, sessions, and sign-out without lock-in. + +Private messages are the milestone after this one. + +## Careful with what is yours + +Follow lists, relay lists and profiles are replaceable events: publishing one +replaces whatever the network already had. Plaza reads yours back before it +writes, keeps the parts it does not understand, and refuses to publish a list it +has not seen. An app that drops half your follows because a relay was slow to +answer is worse than an app that does nothing at all. + +## Fast on purpose + +The feed is a windowed list: it builds only the rows near the viewport, so its +cost follows the window rather than the length of the feed. Measured on the +ReleaseFast build while scrolling hard through a live feed: + +| Stage | p90 | Budget | +| --- | --- | --- | +| Rebuild | 54µs | 400µs | +| Layout | 432µs | 1500µs | +| Patch | 19µs | 200µs | + +A 120 Hz frame is 8333µs, so a hard scroll spends about a tenth of +one. Sixty notes mount 63 widget nodes rather than roughly 500. The numbers are +reproducible: `scripts/frame-budget.sh` in the repo measures them and fails on a +regression. + +See [performance](/performance) for the library numbers underneath. + +## Getting it + +Plaza is not tagged for download yet. Build it from source today: + +```sh +git clone https://github.com/zig-nostr/plaza +cd plaza +npm install -g @native-sdk/cli +native build . # a ReleaseFast binary in zig-out/bin/ +scripts/package-macos.sh # or the same artifact as Plaza.app +``` + +macOS on Apple Silicon. A one-line installer lands with the first tagged +release. + +Plaza is ad-hoc signed and not notarized on purpose. It signs notes with your +key, so the trust anchor is a build you can reproduce rather than an Apple +signature you cannot inspect. + +Linux and Windows build and test in CI. Packaged releases for them come later: +the toolkit renders through a software rasteriser off macOS today, and shipping +one now would misrepresent how Plaza performs. + +## Built on the core + +Everything Plaza does with Nostr comes from +[`zig-nostr/nostr`](https://github.com/zig-nostr/nostr): keys and BIP-340 +signing, the event model, relay transport and the outbox model, the local store, +and the NIP-46 signer protocol. Plaza is the product weight that proves the core +holds up. See the [architecture](/architecture) and the +[NIP-support table](/nips). + +Source, issues and milestones live at +[`zig-nostr/plaza`](https://github.com/zig-nostr/plaza). diff --git a/content/roadmap.mdx b/content/roadmap.mdx index 00fe096..15dd7e9 100644 --- a/content/roadmap.mdx +++ b/content/roadmap.mdx @@ -23,7 +23,8 @@ being built now, and the year ahead — roughly a milestone a month. ## In progress -- **The flagship client** — a fast, local-first native Nostr client. Two things done well: +- **[Plaza](/plaza)**, the flagship client: a fast, local-first native Nostr client. + Two things done well: - **Onboarding in under two minutes** — browse as a guest with no key; when you want to post, generate a key or connect [Signet](/signer); a curated starter pack means the feed is never empty. From 8204bef88a8404fe672391d1988f3728963c4e0e Mon Sep 17 00:00:00 2001 From: sepehr-safari Date: Fri, 31 Jul 2026 17:57:10 +0300 Subject: [PATCH 2/2] docs: correct what the Plaza page claims, against the app it describes Review checked every sentence against the code and found six that do not hold. Key custody was the bad one. "Plaza does not hold your secret key" was stated absolutely, and a bare source build falsifies it: there is no ceremony window in zig-out, so pasting an existing key lands in Plaza itself and is written to ~/.plaza. The app already knows this and changes its own wording for it. The page now says where the key goes on each path, and names the exception rather than leaving a reader to discover it. Follower counts. The page claimed profile pages show follows and followers. Plaza deliberately shows only a following count, with the reasoning written into profileCounts and a test holding it there: following is a number someone states about themselves, followers is one nothing on your disk can know. I turned the claim into the restraint, which is the more interesting thing to say anyway. Windows CI. "Linux and Windows build and test in CI" is false; the matrix is ubuntu-latest alone, because the relay transport needs libc getaddrinfo and Zig's standard library does not declare it for Windows. The same sentence is wrong in the app's README, fixed separately. Also: relay lists are rebuilt from the app's own slots, so "keeps the parts it does not understand" is true of follow lists and profiles and not of those. Plaza does not use the core's outbox model yet. And a remembered follow waits for a follow list to arrive rather than always completing, so the sentence promising otherwise is softened. The home page's app card still said "Flagship client" and pointed at the roadmap, which is exactly what the last commit claimed to have fixed everywhere. It says Plaza and links to the page. The roadmap's year ahead listed threads, reactions, quotes and media as future work while the new page lists them as shipped. Two public pages disagreeing about what exists is worse than either being wrong alone. --- content/ecosystem.mdx | 4 +-- content/index.mdx | 4 +-- content/plaza.mdx | 60 +++++++++++++++++++++++++++++-------------- content/roadmap.mdx | 3 ++- 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/content/ecosystem.mdx b/content/ecosystem.mdx index 6fc06f9..3b74111 100644 --- a/content/ecosystem.mdx +++ b/content/ecosystem.mdx @@ -31,8 +31,8 @@ request waits for your approval, and your `nsec` never leaves it. ### Plaza: in progress The flagship client: browse and post within two minutes, with the feed rendered -from disk and reconciled in the background. Your key lives in a separate -keyholder process, or in your own signer, never in the app. +from disk and reconciled in the background. Your key lives in Signet, a separate +process, or in your own signer. → [`zig-nostr/plaza`](https://github.com/zig-nostr/plaza) · [read more](/plaza) ### On the roadmap diff --git a/content/index.mdx b/content/index.mdx index 47e7bef..5f6ffc2 100644 --- a/content/index.mdx +++ b/content/index.mdx @@ -92,11 +92,11 @@ title: The Nostr protocol, natively in Zig
-

Flagship client

+

Plaza

Building

A fast, local-first client: browse and post within two minutes, with the feed rendered from disk.

- On the roadmap → + Learn more →
diff --git a/content/plaza.mdx b/content/plaza.mdx index 2ee87eb..4aa123d 100644 --- a/content/plaza.mdx +++ b/content/plaza.mdx @@ -18,21 +18,31 @@ something that does. Browse as a guest for as long as you like. When you do want in, there are three ways and you pick the one that fits: - **Create an identity.** A key is minted for you, in seconds, with nothing to - write down up front. -- **Bring a key you already have.** It goes into the keyholder, not into the app. + write down up front. Plaza never mints one itself: Signet does, in its own + process, and Plaza is only told the public half. +- **Bring a key you already have.** The packaged app hands your paste straight + to Signet through a separate ceremony window, so Plaza itself never sees it. - **Use your own signer.** Paste a `bunker://` link and Plaza signs through [Signet](/signer) or any other NIP-46 signer, with the key never leaving it. -Whatever you reached for when you were asked is remembered and finished for you -afterwards, so signing in never costs you the thing you were doing. +What you reached for when you were asked is remembered, and completed as soon as +signing in allows it, so signing in does not cost you the thing you were +doing. ## Your key is not in the app -Plaza does not hold your secret key, even when it makes one for you. A separate -keyholder process owns it, reached over authenticated loopback, and the app asks -it to sign. The ceremony that mints or imports a key runs in its own window, so -you can see the process that is about to hold your key at the moment it starts -holding it. +Plaza does not hold your secret key. Signet, a separate process shipped inside +the app, owns it and is reached over authenticated loopback; Plaza asks it to +sign and never sees the secret. Making a key goes the same way, so there is no +moment at which Plaza has held one. The ceremony that mints or imports runs in +its own window, which is how you get to see the process that is about to hold +your key at the moment it starts holding it. + +One honest exception, and the app says it out loud rather than in a footnote: a +bare source build has no ceremony window, so pasting an existing key there lands +in Plaza itself and is written to `~/.plaza`. In that state the sign-in card +stops promising otherwise and reads "Pasted here, and kept on this device." A +packaged build carries the window and does not have this path. That is the same design as [Signet](/signer), and it is why connecting an external signer is a first-class path rather than an advanced setting. @@ -43,19 +53,26 @@ A follow-based feed with a curated starter pack so it is never empty · real nam 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 · notifications for what was aimed at you · profile pages with -follows and followers · your own relay list, read and write, routed by +· reactions · notifications for what was aimed at you · profile pages with a +following count · your own relay list, read and write, routed by [NIP-65](/nips) · 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 the milestone after this one. ## Careful with what is yours Follow lists, relay lists and profiles are replaceable events: publishing one replaces whatever the network already had. Plaza reads yours back before it -writes, keeps the parts it does not understand, and refuses to publish a list it -has not seen. An app that drops half your follows because a relay was slow to -answer is worse than an app that does nothing at all. +writes and refuses to publish a list it has not seen, and for follow lists and +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. ## Fast on purpose @@ -95,16 +112,21 @@ Plaza is ad-hoc signed and not notarized on purpose. It signs notes with your key, so the trust anchor is a build you can reproduce rather than an Apple signature you cannot inspect. -Linux and Windows build and test in CI. Packaged releases for them come later: -the toolkit renders through a software rasteriser off macOS today, and shipping -one now would misrepresent how Plaza performs. +Linux builds and tests in CI on every change. Windows is not in the matrix yet: +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 Plaza. +Packaged releases for either come later anyway: the toolkit renders through a +software rasteriser off macOS today, and shipping one now would misrepresent how +Plaza performs. ## Built on the core Everything Plaza does with Nostr comes from [`zig-nostr/nostr`](https://github.com/zig-nostr/nostr): keys and BIP-340 -signing, the event model, relay transport and the outbox model, the local store, -and the NIP-46 signer protocol. Plaza is the product weight that proves the core +signing, the event model, relay transport, the local store, and the NIP-46 +signer protocol. The core's outbox model is there too, and per-author routing +lands in Plaza on a later milestone. Plaza is the product weight that proves the core holds up. See the [architecture](/architecture) and the [NIP-support table](/nips). diff --git a/content/roadmap.mdx b/content/roadmap.mdx index 15dd7e9..df56114 100644 --- a/content/roadmap.mdx +++ b/content/roadmap.mdx @@ -39,7 +39,8 @@ being built now, and the year ahead — roughly a milestone a month. Roughly one milestone a month, expanding the core and the ecosystem around it: 1. **Library 1.0** — a stable public API, an audited error model, and a fuzzed parser. -2. **A deeper client** — threads (NIP-10), reactions and reposts, quotes, and media (NIP-92). +2. **A deeper client**: reposts, richer media (NIP-92 `imeta`), search, and mute lists. + Threads, reactions, quotes and inline media are in [Plaza](/plaza) today. 4. **A secure messenger** — private direct messages (NIP-17 gift wrap) on the local store, signing through Signet. 5. **A document app** — Nostr-native notes and long-form (NIP-23), personal and shareable.