GRAIN v0.8.0
A Nostr client - and the library it's built on
Release candidate 6.18.2026
Full Changelog: https://github.com/0ceanSlim/grain/compare/v0.7.1...v0.8.0-rc1
---
v0.7 turned the relay into something you operate from a browser. v0.8 turns grain into a full Nostr client - an importable Go client library that speaks the outbox model, and a web client built on top of it that manages your profile, relays, media servers, and encryption, all signed with your own key.
The headline is client/core: a standalone, outbox-model client engine you can import into your own Go app. It owns a shared relay pool, resolves each user's relay lists, and routes every read and publish to the right relays under the gossip / outbox model - you read a user's notes from *their* outbox, and a reply you publish reaches the *parent author's* inbox. grain's own web frontend is the reference consumer: everything the UI does, it does through this library, so the client is both a usable app and a worked example of building on the engine.
Native NIP-44 encryption (v2 + v3), NIP-42 relay AUTH, NIP-65/17/51/37 relay lists, NIP-89 client tags, and Blossom + NIP-96 media all landed this cycle.
---
The client library (client/core)
An importable, outbox-model Nostr client engine in pure Go - no cgo, no web/HTTP dependencies. A downstream app builds its own client on it instead of reimplementing relay routing.
- Outbox routing, automatic. Name the intent and the engine picks the relays: reads come from a user's NIP-65 outbox, a published note reaches the author's outbox plus each mentioned recipient's inbox, and metadata resolves from the indexers. A leased connection pool keeps the routing additive - switching a session's relays adds connections rather than tearing the pool down.
- A role model for relays. Every relay carries a
Rolebitmask - outbox / inbox / DM-inbox (per-target, from NIP-65 / NIP-17), search / blocked / favorite / private (the user's own NIP-51/37 lists), and locally-configured indexer / broadcast / trusted roles. - Streaming fetches. A multi-relay streaming primitive delivers events on a channel as each relay answers, de-duplicated by id - the basis for lazy-hydrating feeds.
- Pluggable seams. Supply your own
Signer(local key, or bring a NIP-46 / hardware signer),Logger(any*slog.Logger), andRelayListStore(default in-memory; plug a database for persistence). A read-only context needs no signer at all. context.Contextthroughout. Both the read/fetch and publish paths take a context for caller-set deadlines and cancellation.- Fixed-relay opt-out. For a pinned single-/few-relay client, pin a fixed read/write set - which deliberately disables the outbox model. Off by default and discouraged.
The frontend's header shows a live x/y relays pool indicator, and /api/v1/client/status exposes the pool's stats.
For developers - the importable surface
import "github.com/0ceanslim/grain/client/core"
client := core.NewClient(core.DefaultConfig()) // owns the shared pool
signer, _ := core.NewEventSigner(privKeyHex) // or your own Signer
uc := client.NewUserContext(signer.PublicKey(), core.WithSigner(signer))
notes := uc.FetchNotes(ctx, author, core.WithLimit(20)) // author's outbox
reply, results, _ := uc.Reply(ctx, parent, "well said!") // outbox parent's inbox
UserContextfacade with intent methods (FetchNotes/StreamNotes/Reply/Publish/SignAndPublish).- Inspectable routing - ask the engine *which* relays it would use without performing the operation.
- The full surface is documented in docs/client-library-guide.md, with compile-checked examples in
client/core/example_test.gothat run in CI, so the public API can't drift without breaking the build. - Tracked under #56 and #77.
---
Your profile, edited in place
The profile page is now a real editor for your own kind-0 metadata.
- Click into your name, bio, picture, banner, or any field and edit in place - no separate form. An Advanced editor exposes the raw content fields and drag-reorderable tags.
- Edits are signed and published with your key, routed to the indexers (so anyone can find your metadata) plus your own relays.
- A live publish toast counts up per-relay acceptances (NIP-20
OK) as each relay answers - and surfaces signer errors instead of failing silently.
---
Relay management
A new relay-management surface in settings, plus a browser for every relay grain knows about.
- Per-category relay lists - outbox/inbox (NIP-65), DM (NIP-17), and the NIP-51/37 lists - each editable and published with one signed event. A fixed-relay override is there for pinned clients.
- Known-relays browser - browse every relay the engine has seen, with live status, NIP-11 info on expand (name, software, supported NIPs, auth/payment flags), and a "sort: fastest" TCP-latency ping. Stage any relay straight into one of your lists.
- Add-relay autocomplete - every add-relay input completes from the known set as you type.
- Login hydration warms the relay-list and media caches at sign-in, so settings render instantly instead of resolving cold.
---
Media servers & uploads
Manage your media servers and upload straight from the client.
- Resolve and edit your Blossom (kind 10063) and legacy NIP-96 (kind 10096) server lists from settings.
- A reusable upload module with a pre-upload modal: pick your primary server, mirror to others with per-server checkboxes, preview the file, and see an ephemeral-storage warning before you commit. Wired into both the profile editor and the admin image fields.
- Uploads are signed client-side - sha256 + a Blossom (BUD-01) or NIP-96 authorization, signed with your key - and stream a live per-server progress toast.
---
Encryption - NIP-44 (v2 + v3)
Native NIP-44 conversation encryption, validated against the official test vectors.
- v2 is the deployed standard and the default; v3 (the in-progress draft, opt-in) binds the event kind and a scope into the MAC.
- This is the primitive behind private relay lists: a NIP-37 (10013) / encrypted NIP-51 list now has a decrypt button that reveals its private entries on demand, with your signer - grain itself never sees the plaintext.
---
Relay AUTH - NIP-42
The client now answers relay AUTH challenges with your signer.
- Relays that issue a NIP-42 challenge show up in a "relays requesting AUTH" list; one click signs a kind-22242 event and authenticates you for the session. Authenticated relays stay trusted until you remove them, and a fresh challenge re-prompts automatically.
---
Client tags - NIP-89
- Published events are stamped with a
["client", "grain"]tag, and any foreign client tag is stripped first so a re-published event never leaks another app's attribution. There's a user-facing opt-out slider, plus admin defaults.
---
Live homepage feed
- The homepage streams events hitting this relay live over a direct WebSocket - author avatar + name, readable kind label, relative time, and a snippet for notes. Newest sort to the top; scroll to lazy-load older events. Click through to the event or the author's profile.
- Built on a new SSE push channel (relay browser) and a live-sync own-event subscription, so your own actions reflect on the page as they happen.
---
New-user onboarding
- Sign in with a brand-new key and the profile page opens as an empty, editable profile (instead of an error), a permanent "set up relays" banner guides you to configure your relays, and you can publish your first metadata immediately.
---
Docs & dashboard
- The API docs at
/api/docsnow cover the client endpoints, and "Try it out" signs its NIP-98 auth with your connected signer - no pasting tokens. - The admin dashboard gained a client-config section (
grain_updateclient), and Relay information split into its own section with Operations moved to the top.
For operators - upgrade notes
- No config migration is required; all client-library seams default to today's behavior (in-memory caches, grain's own logging).
- The OpenAPI spec is generated from swag annotations at build time and embedded via
//go:embed; the build seeds a placeholder so generation is self-contained. - This is a release candidate - please file anything you hit before the final
v0.8.0.
---
