Nostr Compass #37
- Top Stories
- Postr launches as a small Android composer
- Infans encrypts family tracking and co-parent sync over Nostr
- walls.rip Ghost Chat brings PGP-encrypted chat to public Nostr relays
- pakstr 0.13.0 through 0.15.0 makes Zapstore publication explicit
- Heterodyne specifies portable personas and encrypted social communication
- Releases
- Unreleased Changes
- NIP Updates and Protocol Spec Work
- Six Years of Nostr Augusts
Welcome back to Nostr Compass, your weekly guide to Nostr.
This week: Shopstr @Shopstr Markets keeps remote-signer and wallet secrets out of browser storage, Routstr SDK @routstr verifies relay-sourced provider discovery, Postr @postr launches as a small Android composer, Infans (maintainer Turkey: @Turkey) encrypts family tracking and co-parent sync, walls.rip carries PGP-encrypted chat through public Nostr relays, and pakstr makes Zapstore publication explicit. nostr-tools (maintainer: @fiatjaf) binds gift-wrap rumors to their seals. Releases cover subscription isolation, profile statuses, and relay-scoped leave markers. Protocol work reaches comment-thread rollout, wallet-connect fee and payment-lookup drafts, napplet display requests, and experimental same-account enrollment. The issue closes with Six Years of Nostr Augusts.
Top Stories
Postr launches as a small Android composer
Postr is a deliberately small Android composer for kind 1 notes. Private-key custody stays in Amber (maintainer: @greenart7c3), an Android NIP-55 (local signer) and NIP-46 signer. Version 1.0.0 ships a durable outbox that survives lost connectivity and process death, account-specific private drafts, and Blossom attachments with verified hashes and scoped upload authorization.
A post succeeds after Postr reads back the identical signed event and checks its signature. Retries keep the same event id. Publication uses the author’s NIP-65 (relay list) write relays plus encrypted bootstrap relays, or a custom per-account list. A signed NIP-34 (git-over-Nostr) repository announcement and a matching kind 0 project profile are published on relay.ngit.dev. Feeds, analytics, advertising, and key storage stay outside the app.
Infans encrypts family tracking and co-parent sync over Nostr
Co-parents can keep feeding, sleep, and growth records on their own phones and share them without a family-data vendor. Infans is an Android baby tracker that treats a local Room database as the source of truth and publishes encrypted kind 30078 NIP-78 (application-specific data) events for backup and partner sync. Its repository labels the local cipher NIP-44 (payload encryption), but the implementation uses AES-256-GCM while NIP-44 v2 requires ChaCha20 with HMAC-SHA256, so local-mode payloads should not be presented as NIP-44-compatible.
Partner sync uses d-tag baby-tracker-sync, while self-backups use baby-tracker-backup. Async notes travel inside the partner payload. The documented Amber NIP-55 (local signer) path delegates signing and encryption to the signer, but the repository provides no interoperability test showing that every backup and partner-sync path produces NIP-44-v2 ciphertext. The repository presents neither a medical-device claim nor a third-party security review.
walls.rip Ghost Chat brings PGP-encrypted chat to public Nostr relays
walls.rip is an anonymous communication toolkit whose Ghost Chat mode creates or imports an OpenPGP identity in the browser. Its open source client encrypts each message to the recipient’s PGP public key. Readable conversation remains in local session storage on the device; the application has no chat account or central message database.
The transport is real Nostr, but it is deliberately app-specific. Ghost Chat publishes armored ciphertext as kind 1 events to five default relays and labels each event with a stable room tag derived from the recipient’s PGP fingerprint. That gives developers a concrete example of using relays as censorship-resistant message transport, while also showing why decentralized delivery alone is not metadata-private or interoperable with NIP-17 direct messages.
pakstr 0.13.0 through 0.15.0 makes Zapstore publication explicit
After July’s 0.3.1 packaging and Amber work, pakstr is a CLI that turns a folder of web assets into a signed Android APK and publishes it to Zapstore with a Nostr key. 0.13.0 adds automatic release versioning. The 0.13.1 through 0.13.3 follow-ups repair Blossom publication: authorization now uses base64url, uploads carry a Content-Digest, and the Zapstore application event is published before the Blossom upload.
0.14.0 validates the Zapstore publisher before a publish proceeds. 0.15.0 writes listing metadata onto kind 32267 application events and puts release notes in kind 30063 release-event content, so a packaged app’s Zapstore record can carry name, summary, and notes without a separate manual listing step.
Heterodyne specifies portable personas and encrypted social communication
Heterodyne is a specification-first protocol family for portable personas, authenticated communication, own-device control, and social interaction. Its current README composes four existing layers: Nostr signed events, Radicle (peer-to-peer git) durable storage, Marmot (MLS group messaging over Nostr) for encrypted direct and group conversations, and KERI (Key Event Receipt Infrastructure) key-event logs for identity rotation. A persona is described as a cold-root Nostr npub plus an accepted KERI log; routine signing uses rotating epoch keys, while Radicle node identities are dual-proof delegated.
The family splits that work into four independently versioned 0.x drafts. Core owns identity, key-event-log verification, canonical Nostr bytes, and the Radicle repository substrate; Comms owns Nostr-native envelopes, privacy tiers, publishing, and Marmot conversations; and Social owns public following, interactions, and lists. Control, for own-device enrollment and grants, is incomplete and cannot be claimed. These documents remain drafts that may break before 1.0, and this issue introduces the family before any Heterodyne client release has landed.
Releases
Nostr Java v2.0.8: subscription isolation and portable NIP-44
A gift-wrap query against a relay holding five events was returning zero, two, or six events at random, because Nostr Java, a Java library for talking to relays and encrypting Nostr payloads, delivered every inbound frame to every listener on the connection. Version 2.0.8 routes EVENT, EOSE, and CLOSED to the subscription those frames name, so one query’s end-of-stored-events signal can no longer close another. Connection-scoped frames such as NOTICE, OK, and AUTH still reach every listener.
NIP-44 (payload encryption) in the same release no longer needs a JCE provider registered in the process. Encryption used to work only after a key had been generated in that JVM, which registered BouncyCastle as a side effect, and it failed on Android, where adding a provider named “BC” is a no-op. Both cipher paths now use BouncyCastle’s lightweight ChaCha20 engine, and key generation no longer mutates process-wide JCE state. Callers that relied on the library to register the provider must register it themselves. The NIP-44 JCE-provider dependence is the issue this closes.
NoorNote v1.3.6: profile statuses and classified listings
NoorNote is a Nostr client for desktop, web, and Android. A week after 1.3.4 added encrypted community joins, version 1.3.6 shows NIP-38 (user statuses) under a profile’s NIP-05 (domain-verified) name: the optionally expiring kind 30315 addressable events that carry a one-line general or music status. Clicking that line sets the viewer’s own status.
Classified listings from NIP-99 (kind 30402 marketplace offers) now render throughout the app, so the marketplace addon is needed only to buy and sell. Private petname notes on profiles also show in warning orange, with a filled note icon and an orange avatar ring.
nostrord v2.9.0: relay-scoped group state and media
Leaving a NIP-29 (relay-managed groups) group on one host used to suppress the same group id on every other relay, because nostrord, a cross-platform client for relay-hosted communities, keyed its leave and delete markers by bare id. Leave and delete markers scoped per relay keep those suppressions on the host that produced them, so a group that shares an id across two relays is no longer left or dropped as a pair. A join that the relay rejects as already a member now counts as success and clears the local marker, which had been an absorbing state: the self-heal cleared one slot while cold start restored the other.
Version 2.9.0 also renders markdown image embeds that other clients write as , instead of showing the markdown punctuation around an already-detected URL. Direct messages pick up NIP-17 (gift-wrapped private DMs) kind 15 file rumors, so an encrypted attachment sent from Jumble is downloaded, decrypted, and shown, and outbound attachments are encrypted before upload. This tag now ships the NIP-4e encryption-key work covered last week. The proposal remains unmerged, and nostrord says its implementation follows deployed Jumble behavior where that behavior differs from the draft.
Unreleased Changes
Shopstr keeps remote-signer and wallet secrets out of browser storage
Shopstr is a web marketplace for NIP-99 classified listings. After last month’s payment-integrity work, it stops writing serialized bunker signer secrets to localStorage. A NIP-46 (remote signing) bunker payload had included the live bunker:// URL and the generated app private key, so any script in the Shopstr origin could resume the remote-signing session. Bunker data now stays in runtime memory for the current session, leftover bunker payloads are removed when they are found, and non-bunker signer types keep their previous storage behavior.
The matching NWC change does the same for NIP-47 (wallet-connect) credentials. Shopstr had stored the full nostr+walletconnect:// string, including the secret used for wallet actions, as ordinary browser data and reused it at checkout. Connection strings and wallet metadata now stay in memory, and older stored copies are deleted when local data is read. Scripts that already run in the Shopstr origin during an active session can still see those in-memory values.
Routstr verifies relay-sourced provider discovery
A single malicious relay could previously decide which inference providers a Routstr client trusted. Routstr SDK is the TypeScript library behind Routstr, a marketplace that discovers AI providers on Nostr and pays them with Cashu. This week’s discovery fix verifies every relay-delivered provider announcement, model list, and review (kinds 38421, 38423, and 38425) before any consumer sees it, so a review that names a trusted pubkey but carries a garbage signature no longer enters ranking.
Far-future timestamps are dropped before “latest review” selection. Events more than fifteen minutes ahead of the local clock are removed on the live path and when reading the persistent store, which stops a forged created_at from outranking validly signed reviews across restarts. If trusted reviews are unavailable, the review gate fails closed and excludes unreviewed providers from payment ranking until reviews arrive. Operators can still force-enable a provider by hand.
nostr-tools binds gift-wrap rumors to their seals
Unwrapping a NIP-59 (gift wrap) event used to decrypt the wrap, decrypt the seal, and return the inner rumor without checking who the seal came from. nostr-tools is a JavaScript library of Nostr protocol helpers. This week’s unwrap fix requires the wrap to be kind 1059, the seal to be kind 13 with a valid signature, and the rumor’s pubkey to equal the seal’s pubkey. Decrypting the seal already proves control of seal.pubkey. Without the last check, anyone could seal a rumor that named someone else as author and have a client attribute the message to that victim.
NIP-17 (gift-wrapped private DMs) uses the same unwrap path, so the bind applies to private DMs. Batch unwrap now skips a wrap that fails those checks instead of throwing, because gift wraps are unsolicited and one hostile event would otherwise discard the rest of a relay query.
Haven adds signed relay administration and a local notes browser
Haven (maintainer: @utxo the webmaster 🧑💻) is a self-hosted Nostr relay and Blossom media server. Its newly merged administration console exposes NIP-86 management calls on each relay endpoint, with every request authenticated by a NIP-98 event from the configured owner. Operators can manage bans, allowlists, kind rules, relay names, and stored media without giving the relay a signing key. A read-only notes browser keeps encrypted kinds opaque and loads remote media only after a click, avoiding an automatic request that would reveal the operator’s IP address to an outside host.
The same Haven change adds persistent traffic charts and fixes a default-LMDB failure in which counting stored events could loop forever, pin a CPU core, and block later statistics calls. Haven now uses the backend counter where it terminates and a bounded event walk otherwise. The project added its first 23 tests around event paging, deletion, metrics persistence, owner checks, and URL-bound request signatures.
Amethyst moves Blossom authorization off image-loading threads
Amethyst @amethyst, an Android Nostr client, stops waiting for Blossom read authorization on OkHttp dispatcher threads. The interceptor now starts signing away from the network thread, while the image fetcher awaits one shared signature per host and retries the protected blob request. A burst of gated images therefore no longer occupies every per-host connection slot while a signer responds.
The same Amethyst patch brings the token encoding into line with BUD-11: Base64url without padding, server scope, and no blob-specific x tag, allowing one token to cover multiple blobs on the same host. New concurrency tests exercise caching, expiry, signed retries, and sixteen simultaneous callers sharing one signature.
NIP Updates and Protocol Spec Work
NIPs
Snort and Ditto now use NIP-22 (comment threads) for ordinary text replies, converging on kind 1111 while retaining compatibility paths; this does not establish a protocol-wide single reply kind. After the June amendment removed the prohibition on using NIP-22 against kind 1 notes, a merged addition to NIP-30 (custom emoji) lists kind 1111 among the events that may carry emoji tags, with a shortcode in content resolved by that tag. Snort, a web Nostr client, now writes every reply as kind 1111, loads those comments through uppercase E/A root-scope tags, and still accepts an opt-in NIP-10 (kind 1 reply tags) path for older notes. Ditto, a combined Mastodon server and Nostr relay, publishes every reply as a NIP-22 comment, kind 1111 for text and kind 1244 for voice, while continuing to render existing kind 1 replies. Clients that only understand NIP-10 will not see the new form. Top-level posts remain kind 1.
A NIP-47 (Nostr Wallet Connect) pay_invoice request currently has no standard way for the client to specify a routing-fee ceiling. An open fee-ceiling proposal adds an optional max_fee parameter, in millisatoshis, to pay_invoice. Wallets that honor the budget MUST NOT send a payment whose routing cost exceeds amount + max_fee and MUST return FEE_LIMIT_EXCEEDED, defined as no debit and no payment attempted. Supporting implementations MUST include fees_paid in the response so the client can reconcile. Implementations without fee-limit support ignore the unknown parameter, and clients should treat a missing fees_paid field as a sign the cap may not have been enforced. The change adds no event kinds and remains a proposal until it merges.
An open NIP-32 language-label proposal would standardize ["l", "<BCP-47>", "lang"] for author-declared text language. Because the single-letter l tag is already relay-indexable, clients could request a Japanese feed with {"#l":["ja"]} without a relay upgrade or unreliable post-download language detection. The draft also migrates the language examples in NIP-66 relay reports, NIP-68 image metadata, and NIP-71 audio tracks to the same namespace. Labels remain unverified author assertions, and the change is not merged.
Nostr Wallet Connect
After a timeout, reconnect, or missed notification, a wallet-connect client needs a way to ask for one payment record without knowing which Bitcoin payment protocol created it. An open payment-lookup draft in the NWC extension repository defines optional NWC-09 lookup_payment beside NIP-47 core. The request uses exactly one selector: a stable wallet-scoped transaction_id, the BOLT11-compatible payment_hash and/or invoice fields already used by lookup_invoice, or a payment_type plus a typed lookup object defined by another extension. A successful result returns a common envelope (transaction_id, type, state, payment_type, amount in msats, timestamps, optional fees_paid and metadata, and a discriminated details object) and MUST resolve to exactly one record visible to that connection. The wallet MUST NOT reveal whether an inaccessible record exists, and a selector that matches several visible records returns MULTIPLE_MATCHES. States are pending, accepted, settled, failed, expired, and canceled. The same proposal adds NWC-12 BOLT12 offer and payment details that reuse that envelope. Both documents are still drafts.
NAPs
An open NAP-DISPLAY draft would let a napplet ask its host for pixel displays it is allowed to use. It builds on the separately unmerged NIP-5D web-applets proposal, which Newsletter #17 introduced and which remains outside the merged NIP set. The draft defines display.list, which would return opaque stable identifiers with logical width, height, and a runtime-chosen type (lcd, eink, led-matrix, or other), and display.push, which would submit a non-empty batch of coordinate-addressed three-byte sRGB pixels. Runtime discovery would map logical RGB onto native color depth, orientation, and refresh and MAY rotate, reorder, quantize, dither, or coalesce updates. Shell policy would control which displays a napplet may list or write and MAY reject, rate-limit, or cap batches. Before applying any pixel, the runtime would validate the whole batch, so a failed push would change nothing on the device. Success would mean the batch was accepted, not that hardware refresh had finished.
Marmot
An open Marmot experiment would replace the withdrawn External Commit draft for same-account enrollment with a bounded Commit shape. Marmot, the MLS group-messaging protocol on Nostr, assigns data-less component 0x800d (marmot.same-account-membership.v1) as a negotiated behavior marker in that draft. While it is required, a current leaf may author either exactly one inline same-account Add or one to four inline sibling Removes, each with a normal UpdatePath and ordinary convergence priority, and every Commit MUST leave at most five current leaves for any one account. Pairing uses a short-lived sponsor-displayed QR (marmot-pairing-v1:) whose secret feeds HKDF-SHA256 and ChaCha20-Poly1305 over a carrier-independent channel. Local-only kind 453 proofs bind the session to the shared account key and are never relayed. After a matching Welcome, the joiner’s first application payload is a non-rendered kind 452 acknowledgement bound to the Welcome and GroupInfo digests, so a byte-identical Welcome can be recovered without consuming the KeyPackage again. The paired sponsor is the joiner’s trust root for that branch and does not prove global finality. A companion account-sync document remains exploratory and non-interoperable. The experiment is not part of the adopted base profile.
Six Years of Nostr Augusts
Augusts follow one interoperability problem: how a client names a target and attaches feedback. The original protocol repository recorded no August 2021 commits, so the signed-event core sat still. NIP-25 (reactions) then left the kind-1-only box in 2022. Regular replaceable records gained empty-identifier naddr and a coordinates in 2023; in 2024, the separate parameterized-replaceable class was renamed addressable events without a wire-format change. Reactions moved onto external media in 2025. NIP-22 (comment threads) kind 1111 reached producing clients in 2026. The progression is from an idle protocol document to a shared reply and reaction vocabulary that works across notes, replaceable records, and off-network objects.
August 2021
The August 2021 commit window on the original protocol repository is empty. The last change before that inactive month was the June 18 NIP-05 draft, which added DNS-domain identifiers as a human-readable pointer to a public key. NIP-05 (domain identifiers) later moved to a well-known JSON file, but in mid-2021 it was still a DNS TXT lookup. August did not extend that identifier work or add a new event kind or relay message.
The same empty window appears in the tools that already existed beside the specification. noscl, a command-line client created in January 2021, recorded no August commits; neither did go-nostr or nostr-tools. Protocol activity resumed only at year-end, when the repository assigned NIP-09 (event deletion requests) and replaced the DNS scheme with a well-known JSON identifier file. August 2021 is the inactive stage between the June identifier draft and December’s deletion and well-known-JSON work, while the signed-event and relay model held as written.
August 2022
On August 19, a NIP-25 edit expanded kind 7 reaction targets from kind 1 text notes to other notes. The kind 7 event and the +/- convention were already in the draft. That interoperability change let a like, dislike, or emoji attach to a profile, a follow list, or any later event kind that reused the same e and p tags.
The current NIP-25 specification keeps that generalization: a reaction indicates user reactions to other events, and an addressable target also receives an a tag with kind:pubkey:d-tag coordinates. Amethyst, an Android client, implements that contract in its reaction builder. Its builder accepts any event, writes e, p, and k tags, and adds an a tag when the target is an addressable event. This generalized reaction targets beyond kind 1; later August changes added stable coordinates and comment-context tags.
Relay software was also turning tag rules into storage behavior. On August 17, nostr-rs-relay stopped treating every hexadecimal-looking tag value as a binary index key. It limited that optimization to single-letter tags and lowercase hexadecimal values, preserving ordinary text tags instead of decoding them into a form that filters could not match. The same month therefore joined two sides of interoperability: specifications broadened what an interaction could target, while a relay corrected how those target tags were indexed and retrieved.
August 2023
On August 24, NIP-19 (bech32 identifiers) defined how to encode a non-parameterized replaceable event as an naddr. The identifier field, the d tag, became an empty string for kinds that replace by pubkey and kind alone, such as metadata and contact lists. Five days later, NIP-01 (the base event and relay protocol) added the matching a-tag format: kind:pubkey: with a trailing colon and no identifier. Clients could now point at a replaceable record without waiting for a specific event id that the next replace would invalidate.
The current NIP-19 text still tells implementers to use an empty string for those replaceable events. nostr-tools, the JavaScript identifier library, encodes that field through naddrEncode, so a caller can pass an empty identifier and produce a shareable coordinate. The August 2023 work turned replaceable state into something a comment, a reaction, or a share link could name after the underlying event had been superseded. The next August standardized terminology for the related parameterized-replaceable class, while later comment tags reused the coordinate grammar as A and a.
Private payloads were becoming portable at the same time. On August 24, rust-nostr added NIP-44 encrypt and decrypt functions to its JavaScript bindings, exposing the versioned conversation-key scheme to web applications alongside native Rust callers. On August 22, Amethyst separated NIP-44 encryption from the messaging event format, reflecting the protocol split between how content is encrypted and how an application transports it. Stable coordinates made public objects easier to reference; reusable encryption APIs made private content easier to move between implementations without coupling it to one message kind.
The same month also brought funding for adjacent key-isolation, interface, and education work. An August 17 OpenSats grant round allocated its Nostr Fund grants to Amber, shared Nostr interface design, and Nostr use-case education. Amber’s grant focused on keeping signing keys in a dedicated Android application through NIP-46, while the design and education grants addressed onboarding and reusable application patterns. The wider Nostr system was advancing through specification commits, key isolation, interface work, and funded developer education as shared infrastructure.
August 2024
On August 20, the specifications renamed “parameterized replaceable event” to “addressable event” across NIP-01 and sixteen other documents, including long-form articles, live activities, lists, calendars, and classified listings. The wire format did not change. kind:pubkey:d-tag remained the coordinate. What changed is that every specification that already used those coordinates now used the same word for them.
That vocabulary is the one current implementations ship. NIP-01 stores addressable events as the latest record per kind, pubkey, and d tag. NIP-19 calls an naddr “a nostr addressable event coordinate.” Amethyst’s reaction path, cited above, types the target as AddressableEvent before writing the a tag. The 2023 coordinate extension and 2024 terminology change both use the kind:pubkey:d-tag coordinate grammar, while NIP-01 continues to distinguish regular replaceable events from addressable events. A later comment can therefore fetch an addressable discussion by uppercase A without caring which event id currently occupies that address.
Storage protocols were applying the same preference for explicit identifiers. On August 27, Blossom’s BUD-04 allowed one authorization event to carry multiple blob-hash x tags, so a client could authorize a bounded batch of uploads, mirrors, or deletions without pretending that the hashes described one object. Four days later, the project clarified its blob descriptor and added an example. Nostr events were coordinating content-addressed media operations while the bytes stayed on media servers, separating signed authorization from storage transport.
On August 29, remote signing became more tolerant of imperfect relay sets. go-nostr changed its NIP-46 client so one faulty relay could not block a request sent through other configured relays: relay connections and publication attempts run independently, and the call proceeds once any connection succeeds. On August 19, OpenSats also announced long-term support for Amethyst creator Vitor Pamplona, including work on NIP-17 private messages, cross-platform libraries, and the outbox model. The protocol vocabulary, resilient transport, privacy work, and sustained maintenance funding were converging around the same goal: clients that could keep working across devices and uneven relay conditions.
August 2025
On August 22, NIP-25 gained external-content reactions. A reaction to something that is not a native Nostr event must be kind 17 and must carry NIP-73 (external content identifiers) k and i tags, replacing the older website r tag. The examples in the merged text are a web URL (k=web) and a podcast episode identified by show GUID and item GUID, with Fountain URLs as hints. Reactions had left kind 1 in 2022. They now left the Nostr event set.
Fountain 1.3, published August 15, 2025, shipped those likes before the specification merge and said they are powered by Nostr so other podcast apps can read them. Today’s NIP-25 document still uses Fountain’s podcast GUID example. By August 2025, a reaction coordinate could name a podcast episode or a web page with the same identifier grammar a comment later uses for an external root.
August 2026
This August moved comment threads into the clients that write ordinary replies. The June amendment, later merged, removed the line that had told clients not to use NIP-22 comments on short notes. NIP-30 (custom emoji) then added kind 1111 beside notes, reactions, and user statuses, so a comment can carry the same emoji tags those other kinds already used. The specification work is the permission. The client work is the rollout.
Snort, a web client, now publishes NIP-22 comments for kind 1 targets by default, subscribes to threads through uppercase E/A root tags, and accepts kind 1111 in notifications. Ditto, a community web client, publishes every reply as a NIP-22 comment, kind 1111 for text and 1244 for voice, including replies to kind 1 notes, while still reading NIP-10 (note threading) replies. The six-year shift is visible in those defaults: 2022 generalized the reaction, 2023 and 2024 named the coordinate, 2025 pointed reactions off-network, and 2026 made the comment the shared reply event across those same targets.
Private-group infrastructure was defining recovery as an interoperability requirement. Marmot’s August 13 durability and restart contract specifies which local MLS and publication state must survive a restart, and requires clients to reconcile persisted state before continuing group operations. That extends the August progression beyond naming a target: a mature client must also preserve enough cryptographic and delivery state to resume safely after interruption. Shared event shapes matter only when implementations can recover the state needed to use them.
Send a NIP-17 DM to share a project or news item through the Nostr Compass project.
Write a comment