Back to Freedom.Tech Back
All Grain releasesAll versions
Release Thu, Jun 18, 2026 6 min read

Grain 0.8.0-rc1

Streaming fetches. A multi-relay streaming primitive delivers events on a channel as each relay answers, de-duplicated - 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), and RelayListStore (d

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 Role bitmask - 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), and RelayListStore (default in-memory; plug a database for persistence). A read-only context needs no signer at all.
  • context.Context throughout. 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
  • UserContext facade 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.go that 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/docs now 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.

---

Issues: #56 #74 #77 #80 #83 #87 #90 #98 #99 #100 #101