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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions content/_meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default {
ecosystem: 'Ecosystem',
architecture: 'Architecture',
nips: 'NIP Support',
plaza: 'Plaza',
signer: 'Signet',
performance: 'Performance',
roadmap: 'Roadmap',
Expand Down
11 changes: 6 additions & 5 deletions content/ecosystem.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 Signet, a separate
process, or in your own signer.
→ [`zig-nostr/plaza`](https://github.com/zig-nostr/plaza) · [read more](/plaza)

### On the roadmap

Expand Down
6 changes: 3 additions & 3 deletions content/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ title: The Nostr protocol, natively in Zig
<div className="zn-card">
<div className="zn-icon">🖥️</div>
<h3>Native apps</h3>
<p><a href="/signer">Signet</a> 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.</p>
<p><a href="/signer">Signet</a> ships today, <a href="/plaza">Plaza</a> 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.</p>
</div>
</div>

Expand Down Expand Up @@ -92,11 +92,11 @@ title: The Nostr protocol, natively in Zig
</div>
<div className="zn-card">
<div className="zn-app-head">
<h3>Flagship client</h3>
<h3>Plaza</h3>
<span className="zn-tag zn-tag-building">Building</span>
</div>
<p>A fast, local-first client: browse and post within two minutes, with the feed rendered from disk.</p>
<a className="zn-applink" href="/roadmap">On the roadmap →</a>
<a className="zn-applink" href="/plaza">Learn more →</a>
</div>
<div className="zn-card">
<div className="zn-app-head">
Expand Down
134 changes: 134 additions & 0 deletions content/plaza.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# 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. 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.

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. 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.

## 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 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 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

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&micro;s | 400&micro;s |
| Layout | 432&micro;s | 1500&micro;s |
| Patch | 19&micro;s | 200&micro;s |

A 120&nbsp;Hz frame is 8333&micro;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 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, 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).

Source, issues and milestones live at
[`zig-nostr/plaza`](https://github.com/zig-nostr/plaza).
6 changes: 4 additions & 2 deletions content/roadmap.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -38,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.
Expand Down
Loading