The weekly companion to Overview - Identity & Pseudonymity, covering August 31 – September 6, 2026 (since the August 30 update). Three threads converged this week. First: the NIPs repository shipped four merged PRs in six days, and every one of them now travels under the NIP-42 AUTH wire — NIP-67 gained the "auth" EOSE hint (PR #2371, merged Sep 1, opened by fiatjaf Jun 6 and stalled for 87 days before this), NIP-84 expanded its reference surface to NIP-73 i tags and loosened r (PR #2454, merged Sep 1, opened by staab of Coracle), NIP-78 became a relay-keyworded specification requiring AUTH for kinds 78 and 30078 (PR #2458, merged Sep 3, second-implementation evidence from alexgleason on Ditto), and PR #2460 (mattn, merged Sep 4) clarified that limit: 0 skips stored events while leaving the subscription live. Second: strfry 1.1.2 shipped Aug 27 (one day before last week's window) and fixed a WebSocket continuation-frame memory-DoS reachable from any unauthenticated client, plus a reconnect loop that wedged the uv event loop on strfry's hub thread. Third: Leon Acosta and QuantaKrypto published "Post-quantum Nostr identities, from your seed where possible" on Sep 4, the longest write-up to date of why the kind 10203 attestation is a workaround — a necessary one — for an identity layer that never declared its own algorithm. The companion piece, "Post-quantum direct messages should not be opt-in", frames the same problem as a structural failure of cryptographic agility: Nostr versioned its encryption (NIP-44 carries a version byte) and left its keys unversioned, so PQ has to ride as a bolt-on whose negotiation is itself the scar tissue. The shape of the week: relay-side AUTH stops being optional in the spec text; a relay-class WebSocket DoS gets a CVE-grade patch; and the most thoughtful argument yet appears for why the kind 10203 attestation is correct as a workaround and wrong as a destination.
TL;DR — This Week
- NIPs repository merged four PRs in six days (Sep 1 – Sep 4), and AUTH is the connective tissue. NIP-67 PR #2371 (fiatjaf, opened Jun 6, merged Sep 1, 87 days open) adds a third hint value
"auth"to EOSE — relays send a NIP-42 AUTH challenge before the EOSE that carries the hint, telling the client "more stored events are waiting behind authentication". NIP-78 PR #2458 (iqbqioza, merged Sep 3) gains the new## AUTHsection requiring NIP-42 for kinds 78 and 30078, therelayheader keyword, and a guidance paragraph against using these kinds as a generic interchange format. NIP-84 PR #2454 (staab, merged Sep 1) splits the reference section into three:a/efor Nostr events,i(per NIP-73) for structured non-Nostr sources like ISBNs and DOIs, andras the catch-all that may now hold a URL or text. NIP-01 PR #2460 (mattn, merged Sep 4) clarifieslimit: 0as "skip stored events, keep subscription live", matching strfry, nostr-rs-relay, Khatru, Nostream and rnostr plus all seven public relays tested. AUTH moved from a relay feature into the normative layer of the spec. - strfry 1.1.2 (Aug 27) fixed three bugs, two reachable from any connected client. The most severe: a WebSocket DoS in
handleFragmentof the vendored uWebSockets (golpesubmodule) where continuation frames were each tested against the payload limit but the accumulatingfragmentBufferwas never checked — a client could send FIN-less continuation frames indefinitely and grow the relay's buffer without ceiling. The fix is five lines insrc/WebSocket.cppthat test the buffer length after each append and force-close withpayload size exceededpastmaxPayload. Second: a negentropy-session crash when a client opened a new session with asubIdwhose previous query was still in flight — fixed by clearingqueriesandviewsbefore the new session registers. Third: a reconnect loop that wedged the uv event loop onstd::this_thread::sleep_forand armed the 5000ms connection timer against a clock already made stale by the sleep, so the timer fired the moment it was set and tore down the socket that had just opened. The replacement defers with a non-blockinguS::Timerand splitsconnectNow()out. The WebSocket DoS needs no authentication and costs the relay memory rather than the sender — this is the one worth acting on. - Nostr WoT + QuantaKrypto published the PQ-identity writeup of the year. "Post-quantum Nostr identities, from your seed where possible" (Leon Acosta, joint with QuantaKrypto, Sep 4) makes two arguments the Overview - Identity & Pseudonymity overview has been tracking but never spelled out at this depth: (a) the obvious derivation
pq_key = KDF(nsec)is circular because Shor recoversnsecfromnpuband the attacker reruns the same KDF, so the post-quantum key falls with the identity; the fix is to derive secp256k1 and ML-KEM/ML-DSA as siblings from the BIP-39 seed, with domain-separated HKDF-SHA256 (info = "nip-pqc/v1/ml-kem-1024/0"and"nip-pqc/v1/ml-dsa-87/0"); (b) the kind 10203 attestation is the right artifact because the identity layer cannot host algorithm identifiers — most clients rebuild kind 0 from fields they know about, so apq_kemfield would silently vanish on the first profile edit. The companion piece "Post-quantum direct messages should not be opt-in" argues the corollary: because identity cannot declare its algorithms, PQ has to ride aswindow.nostr.nip44.encrypt(pubkey, plaintext, { scheme: 'pq', recipientKemKey }), JavaScript discards the third argument, and silent downgrades are the predictable failure mode. The honest framing: kind 10203 works, needs no relay changes, and should be replaced by something structural. NIP #2434 (post-quantum identity keys, ConorClancy89, opened Aug 11) is now 26 days frozen with only fiatjaf's early redirect comment. - Nostr WoT newsroom published four pieces in five days — NIP-67 adds an auth hint to EOSE (Sep 1), NIP-84 adds i tags to highlights and loosens r (Sep 2), strfry 1.1.2 fixes a WebSocket memory DoS (Sep 3), NIP-78 puts custom app data behind AUTH (Sep 4). Four pieces, all sourced to primary commits and PR diffs, with the AUTH thread visible across three of them.
- NIP/identity watch list updated: PR #2434 (PQ identity keys) — 26 days frozen; PR #2431 (NIP-07 pinning) — frozen; PR #2439 (NIP-86 assign/unassign) — frozen; PR #2440 (NIP-29 roles/permissions decoupling) — frozen. New merges this window: #2371 (NIP-67 auth hint), #2454 (NIP-84 i tags + loose r), #2458 (NIP-78 AUTH + relay keyword), #2460 (NIP-01 limit:0 semantics). NIPs PR #2447 (NIP-17 gift-wrap meta on outer event) still draft; PR #2452 (NIP-89 app handler), #2453 (NIP-50 search synonyms) opened last week still draft.
The freedom-tech takeaway: AUTH stops being optional in the spec text this week. Four NIP merges, and three of them route through NIP-42's relay-side authentication gate — the EOSE hint tells the client when to authenticate, NIP-78 turns authentication into a SHOULD for the user's own app data, and NIP-01's limit: 0 clarification formalizes the live-subscription contract that NIP-78 relies on. The relay-side defense is being built in the open: a WebSocket DoS that needed no authentication to grow the relay's memory was patched in 5 lines of uWebSockets. And the most rigorous argument yet for why the kind 10203 attestation exists was published on the same days — not as a destination, but as a workaround for an identity layer that never declared its own algorithm.
The Week in Detail
The four-PR AUTH wave: NIP-67, NIP-78, NIP-84, NIP-01
NIP-67 PR #2371 — "auth" hint — was opened by fiatjaf on June 6, 2026, reviewed by pablof7z and Semisol across multiple long threads (the privacy-leak debate around June 7–9 is the loudest), and merged 87 days later on September 1 by fiatjaf himself. The change is small on paper — 12 lines added, 16 removed across two files — and it touches both NIP-67 and NIP-42. The substance:
The existing two NIP-67 hints answer a question about the relay's storage. "finish" says the relay has sent every stored event matching the subscription's filters, so the client should stop paginating. "more" says the relay is holding matching stored events it did not send, so the client should paginate to get them. The new value answers a question about the client instead:
"auth": the relay may have more stored events matching the subscription's filters if the user performsAUTH(NIP-42).
That is a different kind of incompleteness. "more" describes a result truncated by the relay's own limits, which paginating will eventually exhaust. "auth" describes a result truncated by who the client is, which paginating will never fix. Before this hint existed, the two were indistinguishable from the client side: an unauthenticated subscription that returned a partial view of a relay's data ended with the same EOSE as one that returned everything.
The normative sentence is the part with teeth. Relays MUST ensure an AUTH challenge is sent before the EOSE that carries the "auth" hint. NIP-42 picks up a matching auth hint in EOSE section that restates the same rule from the authentication side and points at NIP-67 for the definition. Relays MAY use the hint; if they do, the AUTH challenge goes first. The example added ends with ["EOSE", "sub4", ["auth", "finish"] — both values at once. Read together they say the relay has sent everything it will send at the client's current authentication level, and that a higher one may reveal more. "finish" is therefore relative to the requester, not an absolute statement about the relay's storage.
The reason this matters for the identity layer: clients that read both AUTH signals now have a unified model — auth-required in CLOSED/OK (NIP-42) for hard refusals, auth hint in EOSE (NIP-67) for soft "more available behind AUTH". A NIP-78 read flow that gets ["auth"] now knows exactly what to do: ask the user, run NIP-42, requery. Before this PR, the same flow had to either re-query everything or guess.
NIP-78 PR #2458 — NIP-78: require AUTH for kind 30078 — was opened by iqbqioza on Sep 2 and merged Sep 3 by fiatjaf, with alexgleason (Ditto) committing the second implementation evidence the spec needs. The interesting bit is the shape of the merge: alexgleason commented "Very good." on Sep 2, then disclosed "I updated Ditto Relay to require AUTH for NIP-78 events (relay.ditto.pub). Need a second implementation to merge this." iqbqioza asked whether Ditto covered both kind 78 and kind 30078; alexgleason replied "Oh wow, I didn't know kind 78 existed. I only did 30078." and committed a second change. The PR title was misleading — only 30078 was in the diff when first opened — so iqbqioza updated the title and the normative sentence to cover both. fiatjaf merged within 24 hours.
The new section is three sentences under a new ## AUTH heading:
Because this data is private to the user, relays SHOULD require clients to perform the NIP-42
AUTHflow before accepting or servingkind 78andkind 30078events, and SHOULD only serve these events to the authenticated owner, i.e. the client must authenticate with the same pubkey as the event author.
Two obligations sit in that sentence, and they are not the same one stated twice. The first covers the gate: run the authentication handshake before the relay will take one of these events in, or hand one out. The second covers the match: having authenticated, the client only gets the events it wrote. A relay that required AUTH and then served every user's stored settings to anyone who bothered to log in would satisfy the first and fail the second.
The pubkey comparison is against the event author, not against a tag. The nearest existing precedent — NIP-17 gift wrap — keys on the recipient in a p tag because the gift-wrap author is a throwaway key and the author field is useless. NIP-78 events are signed by the user, so the author field is the owner.
The second half of the change is a paragraph inserted near the top, before the event definitions: "These kinds are not meant to be used as a generic interchange format for data that should be public or exchanged between different applications. Applications that need such interoperability should use dedicated kinds instead." This is guidance rather than a normative rule (no MUST/SHOULD), but it is the part that has practical teeth once relays act on the section below. Data written to 30078 on the assumption that other applications would read it stops being readable by them the moment a relay starts enforcing owner-only reads. The spec now says in advance that such use was out of scope.
The header keyword line changed from draft optional to draft optional relay. NIP-78 previously described only what clients put in an event; it now tells relays what to do with one. Nineteen spec files currently carry the relay keyword, including NIP-01, NIP-42, NIP-17. NIP-78 is now in that list.
The Ditto implementation is the operative answer to "how quickly will enforcement spread?" The user's own stored NIP-78 data on relay.ditto.pub is now owner-only-readable. The fallback path for a client that hits this on an unaware query is the lighter ["auth", "finish"] hint NIP-67 just added: the relay returns nothing rather than CLOSED, and signals that authenticating would return more.
NIP-84 PR #2454 — Add i tags to highlights, clarify that r is whatever — was opened by staab (Coracle) on Sep 1 with the disclosure that drove the change: "I've been publishing highlights for a long time with a non-url source which amethyst and jumble fail to interpret because they assume it's a valid url. The sources I'm highlighting don't have a URL, so I can't follow the spec." Merged the same day by pablof7z with fiatjaf review.
The old text in the reference section was two sentences covering two cases: a or e tags for Nostr events, r tags for URLs. The replacement is a three-item list: a/e for Nostr events, i (per NIP-73) for structured non-Nostr sources like ISBNs and DOIs, and r as the catch-all that may now hold a URL or text. Two things happened at once — a case was added (the NIP-73 i vocabulary: isbn:, doi:, isan:, geo:, iso3166:, podcast:guid:, blockchain tx/address forms, web for normalized URL), and an existing case was redefined (r is no longer the URL tag).
What r loses is the only tracker-stripping guidance in NIP-84 — a SHOULD to clean trackers and obvious non-useful query-string fragments from highlighted URLs was also removed with no relocation. Clients that strip tracking parameters from highlight sources are now doing so on their own judgement rather than on a SHOULD in the specification. NIP-73's normalized web URL form, which the i path now covers, drops the fragment but says nothing about query strings. There is also a leftover: the closing paragraph of NIP-84 still reads that r-tag URLs from the comment MUST have a mention attribute and the source URL MUST have the source attribute — that language assumes r holds a URL, which the reference section above it no longer requires. pablof7z noted in a follow-up that they have been posting with i for ISBN anyway; the PR's job is to make that practice normative.
The second normative change is in the quote highlight section. A comment tag turns a highlight into a quote highlight, and the rendering rule went from MUST ("rendered like a quote repost with the highlight as the quoted note") to SHOULD. The reason — one highlight action should not produce two notes — survives in the surrounding prose; a client that renders quote highlights some other way is no longer out of compliance. The remaining edit is a typo fix (creation and multiple notes to creation of multiple notes) in that same paragraph.
NIP-01 PR #2460 — Clarify zero limit behavior — was opened by mattn on Sep 4 and merged Sep 4 by fiatjaf with alexgleason review. The change is a one-paragraph clarification that limit: 0 skips stored events while the relay still sends EOSE and keeps the subscription active for future matching events. The PR description is the proof: "This matches strfry, nostr-rs-relay, Khatru, Nostream, and rnostr, as well as all seven public relays whose behavior was conclusively tested." The norm lands after the implementations converged on it — NIP-01 is now stating what was already true. The downstream effect: clients that want "live only, no history" can use limit: 0 with confidence it does the right thing on every mainstream relay. The NIP-78 AUTH flow on a freshly-AUTH'd subscription now has a documented limit: 0 baseline it can rely on for "show me new events of my own kind 30078 since I AUTHed" without a separate since cursor.
The connective tissue across all four: AUTH stops being optional in the spec text this week. Three of the four merged PRs route through NIP-42's relay-side authentication gate. The EOSE hint tells the client when to authenticate; NIP-78 turns authentication into a SHOULD for the user's own app data; NIP-01's limit: 0 clarification formalizes the live-subscription contract that NIP-78's "show me my own data" flow relies on.
strfry 1.1.2 — three fixes, two reachable from any client
strfry 1.1.2 was tagged Aug 27, 2026 — one day before last week's window opened — but the newsroom wrote it up on Sep 3 because the WebSocket-DoS fix is the kind of thing that needs to be on operator dashboards now. Five commits separate it from 1.1.1, touching four paths: CHANGES, the golpe submodule pointer, src/WSConnection.h and src/apps/relay/RelayNegentropy.cpp.
The WebSocket continuation-frame DoS is the headline. strfry vendors its WebSocket stack through golpe, and the tag bumps that submodule, which in turn bumps uWebSockets. The commit adds five lines to handleFragment in src/WebSocket.cpp:
if (webSocket->fragmentBuffer.length() > group->maxPayload) {
forceClose(webSocketState, (char*)"payload size exceeded", 21);
return true;
}
The WebSocket protocol lets a single logical message arrive as a first frame followed by any number of continuation frames, with a final frame carrying the FIN bit. Until it is complete, the pieces accumulate in fragmentBuffer. The payload limit was being applied to frames, not to the buffer they were being appended to, so a client that sent continuation frames and never set FIN could push the buffer up indefinitely while every individual frame stayed under the limit. The new check tests the accumulated length after each append and force-closes with payload size exceeded once it passes maxPayload.
This is the one worth acting on. It needs no authentication and no unusual protocol behavior beyond declining to finish a message, and the cost falls on relay memory rather than on the sender. Relay operators running strfry should be on 1.1.2 before the next DoS run, not after. The submodule bump means a rebuild has to pull it explicitly — a cached checkout won't pick it up.
The negentropy-session crash is the second fix. Negentropy is the set reconciliation protocol strfry uses for syncing, and each session is keyed by the subscription id the client chose. When a client opened a new session reusing an id whose previous query was still being processed, the relay crashed. The fix is three lines in RelayNegentropy.cpp:
queries.removeSub(connId, subId);
views.removeView(connId, subId);
Both calls sit immediately before the log line that records a new negentropy session, so a reused id now displaces whatever was under it instead of colliding with it. The subscription and the view are removed as a pair, which matches how they are created.
The reconnect loop is the third fix and the largest diff of the five commits. The old code took a delay before retrying a failed connection and implemented it with std::this_thread::sleep_for on the hub thread. That thread also runs the uv event loop. Sleeping on it stalls the loop's cached clock for the length of the delay, and the hub.connect() call that follows then arms its 5000ms connection timeout against a clock that is already stale. The timer is expired the moment it is set, fires on the next loop tick, and tears down the socket that had just been opened, before the WebSocket handshake completes.
The first connection attempt used a delay of zero and so never slept, which is why it worked. Every reconnect after a disconnect failed the same way, permanently. The process keeps retrying and never exits, so a supervisor sees a running program and does not restart it. The replacement defers the retry with a non-blocking uS::Timer on the hub's loop, leaving the loop clock accurate, and splits the connect itself into connectNow(). A follow-up commit stores the pending timer on the connection so that terminate() can stop and close it rather than leaving it armed against an object that is going away.
This path is the one used by strfry's stream and sync commands — so the symptom was a router or sync process that survived a relay restart in name only. Stream-based fanout pipelines (the canonical Nostr relay-to-relay topology) are exactly the deployment where this matters most.
The PQ-identity writeup of the year
The two Sept 4 pieces from Nostr WoT are the week-end deeper read. The first, "Post-quantum Nostr identities, from your seed where possible", is a joint proposal with QuantaKrypto that the Overview - Identity & Pseudonymity overview has been tracking but never spelled out at this depth. Two arguments drive it:
(a) The obvious derivation is circular. The natural thing to try is pq_key = KDF(nsec). An adversary recovers nsec from npub (Shor's algorithm on secp256k1), runs the same KDF, and has your post-quantum key too. You have added computation and no security. Paul Millr — whose @noble/post-quantum most of this ecosystem depends on — opened issue #1971 on exactly this in July 2025. It has been running ever since with fiatjaf, Vitor Pamplona and Mike Dilger in the thread. The circular-derivation objection has killed every proposal in that thread. Working prototypes exist; a NIP does not.
(b) The fix is a change of shape, not of algorithm. NIP-06 already derives the Nostr key from a BIP-39 mnemonic at m/44'/1237'/0'/0/0. That derivation runs through HMAC-SHA512 and is one-way — given the resulting private key, you cannot walk back to the seed. So take the same seed and expand it a second time, with domain separation, into post-quantum keys:
kem_seed = HKDF-SHA256(seed, info = "nip-pqc/v1/ml-kem-1024/0", 64 bytes)
dsa_seed = HKDF-SHA256(seed, info = "nip-pqc/v1/ml-dsa-87/0", 32 bytes)
Both keys are children of the seed. Neither is a child of the other. An adversary who breaks secp256k1 and recovers the Nostr private key learns nothing about the seed (that would mean inverting HMAC-SHA512) and therefore nothing about the post-quantum keys. Messages encrypted to the ML-KEM key stay confidential permanently, even after the identity key is gone. And the user never touches the words — the same mnemonic restores the Nostr key and both post-quantum keys.
The piece is honest about what this doesn't do: an adversary with a quantum computer can still sign events as you and publish a replacement attestation. This protects past messages permanently. It does not protect future messages against someone who has already broken secp256k1. Any proposal claiming otherwise is overselling, and the post is careful to say so.
The 24-words piece is the second honest admission. BIP-39 expands any mnemonic to a 64-byte seed, so a 12-word phrase derives working post-quantum keys. But a 12-word phrase carries only 128 bits, and Grover gives a quadratic speedup on unstructured search — the seed, not the lattice, becomes the weakest link. So the first concrete change is boring: generate 24 words by default. That change is already open against the nostr-wot-extension.
12-word identities are not abandoned, but they are handled differently. A 12-word phrase carries 128 bits, so the proposal refuses to derive from it rather than issue a key that looks full-strength and is not. Those accounts generate an independent post-quantum key instead, and their attestation is tagged origin: independent with no seed-strength claim, so a reader can tell the two provenances apart. The cost is real: an independent key cannot be recovered from the mnemonic, so it needs its own separate backup. Same situation as the large number of Nostr identities that have no mnemonic at all — most of them.
Where the key goes is the third honest admission. Tempting answer: a field in your kind 0 profile, since that content is free-form JSON. Wrong answer. Most clients rebuild the profile object from the fields they know about and republish it. The first time a user changes their display name in a client that hasn't implemented this, the post-quantum key silently vanishes. The user still thinks they're reachable; senders stop seeing a key and quietly fall back to classic encryption. So the keys live in a dedicated replaceable event, kind:10203, signed by the normal key. Nothing else has a reason to overwrite it, and it carries what kind 0 can't — algorithm identifiers, seed strength, versioning for rotation.
Proving you hold the key is the fourth: a secp256k1 signature proves you published those bytes but doesn't prove you hold the post-quantum keys. Someone could advertise a key they can't decrypt with — by bug or on purpose — and people would send them messages nobody can read. So the ML-DSA key counter-signs, over a message binding your npub and both post-quantum keys together. The reasoning follows Vitor Pamplona's Key Set Declaration proposal, which makes the same argument about unilateral key claims.
Hybrid, on purpose: the post-quantum shared secret is never used alone. It gets combined with the existing NIP-44 conversation key through a KDF, so the result is no weaker than either input. Lattice schemes are young and they do break — HAWK, a third-round NIST candidate, fell in July. A flaw in a new scheme must not make Nostr messaging worse than today.
And critically: "The whole thing rides inside the existing gift wrap machinery. Outer layers stay secp256k1, so post-quantum messages cross today's relay network unmodified and clients that ignore all of this are unaffected. No relay changes. No flag day." That last sentence is the identity-week consequence: the AUTH work that landed this week (NIP-67 hint, NIP-78 SHOULD) is independent of this PQ proposal — no relay change is needed for kind 10203 to land.
The companion piece, "Post-quantum direct messages should not be opt-in", makes the structural argument: Nostr versioned its encryption (NIP-44 carries a version byte) and left its keys unversioned. That inconsistency is internal to Nostr, not a matter of hindsight. Versioning was a known idea, applied to the payload and skipped on the key.
The PQ consequence is mechanical:
Additive is optional by definition. Optional means somebody has to choose. That is what "opt-in" means here: not a stance on user consent, but the absence of anywhere else to put the decision.
Opt-in security protects the wrong people — it reaches people who already read about the threat, already understand harvest-now-decrypt-later, and already went looking. The journalist, the activist, the person whose messages will matter to somebody in fifteen years, are exactly the population least likely to have found a checkbox. And this particular protection has an expiry attached: confidentiality against a future break can only be applied before the message is sent.
The piece frames the signer negotiation as scar tissue:
Because post-quantum had to be a bolt-on, it rides on an optional third argument to an existing call:
window.nostr.nip44.encrypt(pubkey, plaintext, { scheme: 'pq', recipientKemKey })JavaScript discards surplus arguments. A signer that has never heard of post-quantum takes that call, ignores the third argument, encrypts the ordinary way, and returns a perfectly valid ciphertext. No error. Nothing in the return value distinguishes it.
A client that assumed support would then badge the message as quantum-protected when it is not. So we had to add a way to ask:
window.nostr.nip44.schemes // ["nip44", "pq"]That marker is not a feature. It is scar tissue. In a protocol where identity declared its own algorithms, no client would need to interrogate a signer about what it can do, because the identity would already say.
The conclusion — "Put the algorithm identifier in the identity on day one, before you have any users. It costs almost nothing while the network is empty and becomes nearly impossible once every client, relay and stored event assumes one curve" — is the kind of line that should be quoted in the Migration Plan — PQ Encryption for Existing Nostr-Clients file next time it is updated.
The Obelisk three-state classification the post introduces is worth recording:
| State | What holds |
|---|---|
| Extra safe | Unreadable, nobody sees who you talk to, and a future quantum computer could not open it |
| Safe | Unreadable and nobody sees who you talk to, but a quantum computer could open it one day |
| Basic | Unreadable, but the servers carrying it see who you talk to |
NIP-17 gift wrap already hides who is talking to whom, which is genuine protection working now. Collapsing that into "quantum-safe or not" would erase it and turn the ordinary case into a permanent warning nobody reads.
The honest framing matters: kind 10203 works, needs no relay changes, and should be replaced by something structural. The NIPs PR #2434 freeze (26 days, since Aug 11, with only fiatjaf's redirect comment) is the price the structural layer is paying for being unable to declare itself.
The week in summary, three threads deeper
Thread 1 — AUTH stops being optional. Four NIPs PRs merged in six days. Three of them route through NIP-42's authentication gate. The EOSE hint tells the client when to authenticate. NIP-78 turns authentication into a SHOULD for the user's own app data. NIP-01's limit: 0 clarification formalizes the live-subscription contract that NIP-78's "show me my own data" flow relies on. The relay-side enforcement story is being built in the open, with second-implementation evidence (Ditto on NIP-78) gating the merge. The interesting operational point: the same week saw a WebSocket DoS in strfry that needed no authentication and cost the relay memory. AUTH additions tighten the authorized path; relay-class buffer-bugs like the uWebSockets fix tighten the unauthenticated path. Both are getting attention in the same week.
Thread 2 — Hardware-ish relay hardening. strfry 1.1.2 is a relay-side hardening release, not a feature release. WebSocket frame accounting, negentropy session lifecycle, and uv loop clock hygiene are not user-visible. The exposure surface they close — a memory-DoS reachable from any connected client, a session collision crash from a reused subscription id, a reconnect loop that wedges the hub thread — is the kind of thing that shows up in postmortems after the next relay outage. The golpe submodule bump is the mechanism: the fix lives in the vendored uWebSockets, not in strfry's own code, and a rebuild has to pull it. Operators running strfry from a cached checkout who skip the submodule update stay on the vulnerable code.
Thread 3 — PQ Nostr as a structural argument. The two Sept 4 Nostr WoT pieces are the most rigorous write-ups of the kind-10203 attestation and its limits to date. The argument is not "kind 10203 is the answer" — it is "kind 10203 is a workaround for an identity layer that never declared its own algorithm, and the workaround works, needs no relay changes, and should be replaced by something structural". The proposed NIP-PR #2434 still hasn't moved in 26 days. Three independent implementations exist (nostr-wot-extension 0.4.0, NDK PR #713, Gleez blog reference); three open standardization proposals (kind 10203 attestation, key-set declaration, payto/identity alt); and one structural debate (Leon Acosta + QuantaKrypto's kind 10203 specification vs the Vitor Pamplona Key Set Declaration alternative). The window for the structural layer to pick one is closing slowly. The AUTH work in NIPs this week is independent of all of it — kind 10203 lands without touching NIP-42.
Watch List (Updated)
| Item | Source | Status | Note |
|---|---|---|---|
| NIP-67 PR #2371 — "auth" EOSE hint | github.com/nostr-protocol/nips | Merged Sep 1 | Opened Jun 6 by fiatjaf, 87-day window; third hint value, AUTH challenge-before-EOSE ordering |
| NIP-78 PR #2458 — AUTH required for kinds 78/30078 | github.com/nostr-protocol/nips | Merged Sep 3 | Two-implementation evidence (Ditto + iqbqioza engine); relay keyword added |
| NIP-84 PR #2454 — i tags + loose r | github.com/nostr-protocol/nips | Merged Sep 1 | NIP-73 i-tag vocabulary for non-Nostr sources; tracker-stripping SHOULD removed; closing paragraph has leftover mention/source language |
NIP-01 PR #2460 — limit: 0 semantics |
github.com/nostr-protocol/nips | Merged Sep 4 | Matches strfry, nostr-rs-relay, Khatru, Nostream, rnostr + seven public relays |
| strfry 1.1.2 | github.com/hoytech/strfry | Tagged Aug 27 | WebSocket continuation-frame DoS fix; negentropy session crash fix; reconnect-loop fix |
| "Post-quantum Nostr identities, from your seed where possible" | nostr-wot.com/blog | Published Sep 4 | Leon Acosta + QuantaKrypto; sibling-derivation rationale; 24-word vs 12-word handling; kind 10203 framed as workaround |
| "Post-quantum direct messages should not be opt-in" | nostr-wot.com/blog | Published Sep 4 | Same authors; structural argument for cryptographic agility; Obelisk three-state classification |
| Nostr WoT newsroom (4 pieces, Sep 1–4) | nostr-wot.com/news | Published | Three of four cover the AUTH thread; one covers strfry hardening |
| NIP #2434 (PQ identity keys) | github.com/nostr-protocol/nips | 26 days frozen | Only fiatjaf's early redirect comment; implementations lead by 3 |
| NIP #2431 (NIP-07 pinning) | github.com/nostr-protocol/nips | Frozen | Companion to #2434 |
| NIP #2439 (NIP-86 assign/unassign) | github.com/nostr-protocol/nips | Frozen | Identity-adjacent |
| NIP #2440 (NIP-29 roles/permissions) | github.com/nostr-protocol/nips | Frozen | Identity-adjacent |
| NIP-17 outer-event metadata (#2447) | github.com/nostr-protocol/nips | Draft | Outer envelope hints for clients |
| NIP-89 app handler (#2452) | github.com/nostr-protocol/nips | Draft (last week) | Application metadata — not identity |
| NIP-50 search synonyms (#2453) | github.com/nostr-protocol/nips | Draft (last week) | Search ranking signal |
| NIP-65 relay-list dedupe (#2454) | github.com/nostr-protocol/nips | Note: PR #2454 is NIP-84, not NIP-65 | Watch list housekeeping — last week's column was wrong; #2454 was the NIP-84 merge this week |
| New: NIP-17 review thread on kind:1059 + p-tag scoping | github.com/nostr-protocol/nips | No PR open this week | Semisol's June discussion on AUTH-with-reason touched it; worth re-reading now that NIP-67 hint lands |
| New: NIP-78 second-implementation roster | alexgleason/Ditto, iqbqioza/nostr-engine | Implementing | Both relays updated; relay-tools cookiecutter implementation pending |
Sources & Cross-References
Primary sources
- NIP-67 PR #2371 — "auth" EOSE hint (opened Jun 6 by fiatjaf, merged Sep 1)
- NIP-78 PR #2458 — require AUTH for kind 30078 (opened Sep 2 by iqbqioza, merged Sep 3; second-implementation evidence from alexgleason/Ditto)
- NIP-84 PR #2454 — Add i tags to highlights (opened Sep 1 by staab/Coracle, merged Sep 1 by pablof7z)
- NIP-01 PR #2460 — Clarify zero limit behavior (opened Sep 4 by mattn, merged Sep 4 by fiatjaf)
- strfry 1.1.2 tag (Aug 27)
- strfry compare 1.1.1...1.1.2 — five commits
- uWebSockets commit 1ef9168 —
handleFragmentfix - "Post-quantum Nostr identities, from your seed where possible" (Sep 4, Leon Acosta + QuantaKrypto)
- "Post-quantum direct messages should not be opt-in" (Sep 4, Leon Acosta)
- nostr-wot-extension PR #12 — 24-word default
- NIPs PR #2434 — NIP-XX: Post-Quantum Identity Keys (opened Aug 11 by ConorClancy89; references issue #1971)
- QuantaKrypto Obelisk writeup (referenced in the Nostr WoT post)
- Obelisk guide: Quantum-safe DMs
- Nostr-wot newsroom pieces:
- NIP-67 adds an auth hint to EOSE (Sep 1)
- NIP-84 adds i tags to highlights and loosens r (Sep 2)
- strfry 1.1.2 fixes a WebSocket memory DoS (Sep 3)
- NIP-78 puts custom app data behind AUTH (Sep 4)
Related weekly updates
- Identity and Pseudonymity Sunday — August 30 2026 Weekly Update — three-NIP block (NIP-22 deletion, NIP-30 emoji, NIP-A3 payto rewrite); Foundation Passport Nostr Signer; Gleez PQ Nostr reference architecture
- Identity and Pseudonymity Sunday — August 23 2026 Weekly Update — DCP refutation (ePrint 2026/1693); NDK PR #713 second PQ implementation; Amber 6.5.x; Cambium 0.4.x
- Identity and Pseudonymity Sunday — August 16 2026 Weekly Update — nostr-wot-extension 0.4.0 ships production PQ identity keys; World ID 4.0; LFDT DTG
- Identity and Pseudonymity Sunday — August 09 2026 Weekly Update — Buzz agent-as-keypair; Concord replaces Marmot; FROST consumer onboarding
- Post-Quantum Saturday — September 05 2026 Weekly Update — ML-KEM draft-10 / ML-DSA-in-TLS appeal 59 days; ML-DSA in RFC Ed Queue; SVP / WeaveTLS / McEliece
- WoT Thursday — September 03 2026 Weekly Update — kind:10203 plateau 22→24; GnuPG 2.5.22; Sequoia 16 PQC commits
Related vault files
- Nostr — primary protocol reference
- Overview - Identity & Pseudonymity — key-rooted identity architecture; updated
updated:stamp is this week - Overview - Decentralized Identity — formal DID/VC stack; companion file to the key-rooted lens
- Overview - Web of Trust — PGP and Nostr follow graphs, the nostr-wot-extension lineage
- Nostr Key Custody and Recovery — NIP-46 bunkers, Amber, Frostr/Bifrost, threshold signing, migration
- Remote Signing — NIP-46 bunker topologies; the third hardware path (Passport) lands here too
- Pubky — Ed25519 + Mainline DHT (PKARR), alternative identity architecture
- Verifiable Credentials — profile claims riding the same identity surface; NIP-A3 (kind 10133) is the payto cousin
- AI Agent Identity on Nostr — agent-as-keypair; Foundation Passport could sign kind 30085 events for agents
- Buzz — Block's agent workspace; Obelisk is a QuantaKrypto alternative
- Amethyst — NIP-84 highlight reader; consumer of the kind 1111 / i tag vocabulary
- Coracle — staab's client; PR #2454 origin
- Ditto — alexgleason's relay; second-implementation evidence for NIP-78 AUTH
- DCP — Simon's claimed post-quantum signature scheme; ePrint 2026/1693 refutation context
- Marmot Protocol — MLS over Nostr; NIP-104
- Migration Plan — PQ Encryption for Existing Nostr-Clients — Gleez + nostr-wot-extension 0.4.0 + NDK PR #713 + kind 10203 spec = four references now
- Hybrid KEM Migration — interop salt/info coordination problem; now a four-implementation problem (Gleez HKDF
nip44-hybrid-v1/nostr-hybrid-chat, NDK PR #713, nostr-wot-extension 0.4.0, Obelisk kind 10203) - Hardware Signers — Heartwood, Amber, Passport are the canonical three
- DID Methods — did:nostr, did:pubky; kind 10203 (NIP-A3 cousin) as the missing algorithm-identifier slot
- Cryptographic Agility — new related concept worth a vault page; "Put the algorithm identifier in the identity on day one" is the line that anchors it
- uWebSockets — relay-side WebSocket stack; the strfry DoS fix landed in this vendored library