Nostr Compass #47
Deze week bracht Amethyst inline napplets uit met fixes uit een beveiligingsreview van embeds, koppelde Napplets de applet-identiteit aan poorten per document en maakten de nightlies van Marmots mdk groepsberichten strikter. Daarnaast veranderden relays, wallets, signeerbibliotheken en nieuwe apps, veelal in samengevoegde broncode die nog niet is uitgebracht.
Hoofdverhalen
Amethyst
Amethyst, de Android-client, bracht v1.21.0 uit, die napplets inline in notes draait op Android 11+ en op desktop. Fixes uit een beveiligingsreview van embeds dichten een pad waarlangs elke napplet met relaytoegang ontsleutelde DM’s kon lezen. Ontsleuteling vereist nu een aparte toestemming van de gebruiker en nostr.signEvent vereist toestemming van de signer, terwijl de Chromium-renderer op desktop nog steeds niet gesandboxed is. Een wijziging in de versleuteling per bijlagebestand geeft elke bijlage van een NIP-17 privé direct bericht een nieuwe AES-GCM-sleutel en nonce, terwijl batches met meerdere bestanden beide hergebruikten. De upstream risicobeschrijving is gebaseerd op codeanalyse zonder gereproduceerde exploit, en al verzonden bestanden worden niet hersteld.
Marmot Protocol (mdk)
mdk, Marmots op MLS gebaseerde kit voor versleutelde berichten op Nostr, publiceerde nightly-prereleases die alleen bedoeld zijn om te testen, zonder stabiele release in die lijn. In de nightly van 2 november wachten nieuwe verzendingen achter oudere berichten in de wachtrij binnen een groep, zodat versleutelingsgeneraties op volgorde blijven voor leden die de geschiedenis inhalen, al biedt dit geen garantie tegen verloren berichten. Een samengevoegde, standaard uitgeschakelde reparatie voor verloren Welcomes volgt de Marmot-heartbeatregel en verstuurt de exact bewaarde Welcome opnieuw zodra de heartbeat van het doel activiteit toont van minstens tien minuten na de laatste publicatie van de carrier. De heartbeatfrequentie of klokachterstand kan de reparatie een uur of langer vertragen, en voor voltooiing geldt geen bovengrens. Commando’s voor het koppelen van apparaten blijven achter de standaard uitgeschakelde feature dev-device-groups en staan uit in productie.
Napplets - Webpakketten
Napplets, de webpakketten voor gesandboxte Nostr-applets, bracht core 0.37.0, nap 0.36.0 en shim 0.35.0 uit, die de applet-identiteit binden aan een MessagePort per document. De geverifieerde (dTag, artifactHash)-identiteit is gekoppeld aan de poort die in de hello vanuit het aangemaakte iframe wordt meegegeven, en al het NAP-verkeer loopt via die poort. Een document dat via navigatie is geladen, krijgt geen toestemmingen. Shells die nog Window-messaging gebruiken, moeten upgraden, omdat er geen compatibiliteitspad is. Latere broncode vergrendelt het previewframe op het geverifieerde document met frame-src ’none'.
Getagde releases
Marble
Marble, een bètaclient gebouwd op Cosign threshold signing, kan relays als feeds lezen in 0.12.23. Een relay die via zijn adres wordt geopend, toont eerst zijn eigen beschrijving en daarna zijn posts, nieuwste eerst. Favoriete relays worden bewaard in een kind 10012-lijst en relaygroepen in kind 30002-sets, dezelfde lijsten die Jumble gebruikt. De release vernieuwt ook de reacties, met een lade met recente reacties en de eigen custom emoji van de gebruiker.
Sidecar
Sidecar, een browserextensie voor het publiceren van notes en webhighlights, kan zijn walletback-up van relays verwijderen in 1.16.6. Na een bevestiging vervangt het de versleutelde kopie door een lege en vraagt het relays om die te vergeten, terwijl de gekoppelde wallet behouden blijft. Er is geen garantie dat relays de back-up wissen. Sinds v1.16.5 verwijdert Sidecar EXIF-, XMP- en IPTC-metadata uit JPEG’s vóór het uploaden, en de wijziging in de uploadvoorbereiding blokkeert elke JPEG die niet kan worden opgeschoond.
Haps
Haps publiceert en installeert native pakketten onder Nostr-sleutels. Haps 0.1.25 levert bundels voor macOS, Linux en Windows met htree-helpers die met checksums zijn vastgepind en een SHA256SUMS-bestand dat alle archieven dekt. De broncode voor 0.1.24 schrijft geversioneerde Hashtree-referenties volgens de concept-specificatie van Hashtree. Haps 0.1.23 en eerder kunnen de nieuwe catalogi niet lezen zonder een legacy-overgangscatalogus of een bijgewerkte installer of Cargo-build.
Cosign
Cosign, de threshold-signingbibliotheek achter de Marble-bèta, levert een gedeeltelijke fix voor joins in 0.12.9: een join via een gescande uitnodiger vereist nu de bijdrage van die uitnodiger en stukken van minstens twee verschillende afzenders. Joins van telefoon naar telefoon blijven ongebonden, dus een aanvaller met twee sleutels volstaat nog steeds, en lege ontsleutelingsdelen slaan nog steeds de bewijsverificatie over. In 0.12.8 kan een server die de ledenlijst schrijft nog steeds share points met bekende geheimen kiezen. Telefoons krijgen fixes alleen via een Marble-release.
nostream
nostream, een TypeScript-relay met PostgreSQL als backend, voegt uitgaande peersynchronisatie toe in 3.12.0. De worker leest per peer de scopes uit de NIP-11-relayinformatie, authenticeert met NIP-42-clientauthenticatie en stemt af via NIP-77-negentropysynchronisatie, waarbij opgehaalde events worden gecontroleerd op ID, handtekening en scope. Peerspecifieke limieten gelden pas nadat een geconfigureerde peer AUTH heeft voltooid. Een bronrelease bewijst niet dat een relay deze heeft uitgerold.
diVine
diVine, een client voor korte loopende video’s, publiceerde stabiele Android 1.0.27 met interactieve publieke videostickers; beschikbaarheid voor iOS en macOS is onbevestigd. Een samengevoegde requery onder het venster draait pas nadat het eerste herstel van de geschiedenis is voltooid en leest gift wraps, NIP-04 verouderde versleutelde DM’s en verwijderingen in pagina’s van 100 berichten per relay. Deze doorloopt elke relay afzonderlijk, terwijl de bestaande history drain één cursor deelt en de oudere geschiedenis van een begrensde relay kan overslaan. De dekking is begrensd en herstelt niet de volledige geschiedenis.
Whistle
Whistle, een mobiele app voor het delen van locaties in groepen en chatten, bracht 2.0.1 uit, de eerste publieke release op Marmot-protocol v2. Bestaande groepen kunnen niet worden overgezet, dus groepen moeten opnieuw worden aangemaakt en elk lid moet updaten, terwijl npub-identiteiten behouden blijven. Scan-to-join vervangt uitnodigingscodes en -links. Een bekend probleem is dat chatberichten die offline zijn verstuurd mogelijk laat of helemaal niet aankomen, wat de release toeschrijft aan MarmotKit; het delen van locaties wordt niet beïnvloed.
In ontwikkeling
Quartz
Quartz, een Kotlin Multiplatform-bibliotheek voor Nostr, leverde in release 2.14.0 het hervatten van live abonnementen na een herverbinding. Elke relay hervat vanaf de nieuwste afgeleverde created_at, begrensd door de klok van het apparaat, en gift-wrap-filters reiken twee extra dagen terug om backdating op te vangen. Events met een oudere tijdstempel die een relay tijdens een storing heeft opgeslagen, kunnen nog steeds gemist worden. De samengevoegde maar nog niet uitgebrachte count-aware negentropy-vensters vergelijken een optioneel NIP-45-eventaantal met het lokale aantal en behandelen aantallen van relays als onbetrouwbaar.
Buzz
Buzz voegde een wijziging samen die verwijderingen van kind 5 en 9005 in één transactie vastlegt samen met de soft delete van het doel, zodat een fout niet langer kan leiden tot een geaccepteerde verwijdering terwijl het doel actief blijft. Historische half weggeschreven rijen (torn rows) worden niet hersteld. Het mobiele profiel van NIP-RS, een Buzz-concept voor leesstatus over apparaten heen, is samengevoegd en synchroniseert leesgrenzen tussen apparaten. Op oudere relays zonder read_state_snapshot werkt lokaal lezen nog steeds, maar stopt de synchronisatie tussen apparaten zonder terugvaloptie.
Alby Hub
Alby Hub, een self-custodial Lightning-node die NIP-47 Nostr Wallet Connect-apps bedient, voegde een fix voor databasemigratie samen die de ID-teller van elke tabel behoudt tijdens db_migrate of een Postgres-migratie. Daarvoor kon een nieuwe app de ID van een verwijderde app hergebruiken en diens transacties en geïsoleerde saldo erven. Er is geen getagde release of uitrol gevonden die de fix bevat. Een aparte wijziging in notificaties isoleert fouten per app, al worden mislukte notificaties nog steeds zonder nieuwe poging verworpen.
swift-nostr-client
swift-nostr-client, een Swift 6-bibliotheek voor Nostr, voegde een brekende wijziging samen waardoor RemoteSigner de connect-handshake uitvoert vóór het eerste commando. De nieuwe instelling connectsAutomatically staat standaard op true, en bij een mislukte handshake wordt het commando niet verzonden. Omdat geheimen voor NIP-46 remote signing maar één keer bruikbaar zijn, moeten apps die een bunker-sessie hervatten met een opgeslagen sleutelpaar van de client deze instelling op false zetten, anders presenteert het eerste commando het al verbruikte geheim. Er is nog geen release getagd.
Nieuwe projecten
Trikey
Trikey, een pre-alpha, niet-geaudite wallet waarvan de telefoonapp en de guardian communiceren via versleutelde berichten over Nostr-relays, pakt in release 0.1.10-alpha het bevriezen van de app bij het openen aan. Beide kanten houden nu een overzicht bij dat een herstart overleeft van al gelezen wrap-ID’s en slaan die over vóór enige handtekeningcontrole of ontsleuteling. Verloopaanwijzingen op verzoeken en meldingen garanderen niet dat relays iets verwijderen, en de prestatiemetingen van de uitgever zijn op een laptop gedaan.
Pacto
Pacto, een desktopapp voor het organiseren van gemeenschappen die legacy Marmot-groepen naast NIP-17-DM’s draait, bracht de ongesigneerde v0.7.5 uit, waarmee verzonden berichten tot één minuut na verzending bewerkt kunnen worden. De fixes voor DM-aflevering laten verzendingen direct mislukken als de pool geen vertrouwde relay bevat en sturen typindicatoren via vertrouwde relays. Open, niet samengevoegde pull requests zouden squad-events en versleutelde squad-documenten toevoegen.
Mialuna
Mialuna, een op media gerichte Flutter-client voor Nostr, publiceerde een releaseaankondiging voor versie 0.19.0 die verwijderverzoeken toevoegt voor de eigen stories van de gebruiker zodra die verlopen. De changelog-commit vermeldt verwijderverzoeken aan relays en verwijdering van Blossom-media. Bij accounts met een lokale sleutel gebeurt dit automatisch, terwijl accounts met Amber of een bunker wachten tot de gebruiker Delete now kiest. De APK en het gedrag ervan op apparaten zijn niet onafhankelijk gecontroleerd. Verwijderverzoeken kunnen niet garanderen dat relays of servers de gegevens wissen.
whereAreYou
whereAreYou, een Android-app die locaties deelt binnen kringen waar je via een QR-code of uitnodigingslink lid van wordt, versleutelt coördinaten met AES-256-GCM en verstuurt ze via publieke relays. Een commit voor ondertekening door leden voorkomt dat houders van de kringsleutel berichten plaatsen als een ander lid, en elk apparaat moet updaten. Bij de Base45-identiteitsback-up waarschuwt de auteur dat iedereen die de QR-code fotografeert de achtcijferige code offline zou kunnen raden, volgens zijn schatting binnen enkele tientallen minuten GPU-tijd; er is geen benchmark gepubliceerd. De bron bevat geen aanbeveling op het gebied van beveiliging.
Protocol- en specificatiewerk
Buzz definieert door de eigenaar geautoriseerde credential-routes voor agents
Buzz voegde een NIP-FA-concept voor door de eigenaar geautoriseerde agent-credentials samen, dat routes voor toekenning, assertie en intrekking definieert voor agents die via NIP-OA-eigenaarsattestatie zijn geautoriseerd. Een toekenning komt voort uit een RFC 8693-tokenuitwisseling die de sessie van de eigenaar koppelt aan een NIP-98 HTTP-authenticatiebewijs van de agent, en intrekking verbreekt de verbinding met de agent en voorkomt dat diens sleutel zich opnieuw inschrijft. NIP-FA is een Buzz-specificatie die alleen in de broncode is samengevoegd, zonder claim van activering in productie of interoperabiliteit.
Post-kwantumhandtekeningen voor events krijgen exacte validatieregels
Het al lang lopende, openstaande voorstel voor post-kwantumhandtekeningen kreeg exacte ML-DSA-44-validatieregels voor Nostr-events. Sleutels en handtekeningen hebben vaste lengtes, verifiers controleren de handtekening over de 32 ruwe digest-bytes van het event-ID met een lege context, en er is geen terugvalalgoritme toegestaan. De voorgestelde identiteit heeft geen NIP-19 bech32-codering, en nieuwe handtekeningen maken NIP-44-versleutelde payloads of NIP-59-gift wraps niet post-kwantumveilig.
Een voorgestelde ondertekende voorwaarde voorkomt verloren vervangingen van events
Een openstaande pull request van Buzz stelt een projectlokale ondertekende NIP-CS-voorwaarde voor voor vervangbare en adresseerbare events. Dit is geen officiële NIP. De ondertekende tag noemt het huidige head-ID of none, en de relay controleert die atomair onder een vergrendeling op de coördinaat. Een verouderde schrijfactie wordt geweigerd met een false OK of HTTP 409, en er wordt niets opgeslagen. Schrijfacties zonder tag werken zoals voorheen, en de tag biedt geen compare-and-swap-garantie over meerdere relays heen.
NIP Deep Dive: NIP-31 en NIP-73
Clients komen vaak inhoud tegen die ze niet van zichzelf begrijpen. NIP-31: Dealing with Unknown Event Kinds geeft een onbekend event-kind een leesbaar label, en NIP-73: External Content IDs benoemt onderwerpen buiten Nostr, zoals boeken of webpagina’s. De twee conventies zijn onafhankelijke, al bestaande specificaties (NIP-31-tekst, NIP-73-tekst).
NIP-31 is gemarkeerd als unrecommended
De huidige NIP-31-tekst opent met een waarschuwing die de NIP als unrecommended markeert omdat die “unnecessarily bloated” zou zijn, en de statusregel vermeldt draft, unrecommended en optional. Events die de conventie gebruiken staan al op relays, dus de werking hieronder beschrijft hoe clients ze lezen, zonder aanbeveling voor nieuwe protocollen.
Wat een alt-tag bevat
Volgens de spec voegt een auteur van een eigen kind dat niet als tekst bedoeld is een alt-tag toe met een korte samenvatting in platte tekst. Van clients die op kind 1 gericht zijn wordt niet verwacht dat ze andere kinds opvragen, maar gebruikers kunnen zulke events nog steeds vanuit notes aanhalen, waar een samenvatting context biedt. Een label kan geen stem uitbrengen en niet op een walletverzoek reageren. Voor rijkere interactie verwijst de spec naar NIP-89: Recommended Application Handlers.
Omdat alt een tag is, valt die onder de event-ID en handtekening uit NIP-01: Basic protocol flow description. Een geldige handtekening authenticeert een samenvatting die de auteur zelf bepaalt: ze laat zien dat de auteur het label heeft gepubliceerd, niet dat het klopt, dus misleidende samenvattingen zijn mogelijk. Tags zijn openbaar, dus een alt op een event met versleutelde inhoud legt de samenvatting bloot en kan onthullen wat de inhoud verbergt. Deze eigenschappen komen voort uit de envelop van NIP-01, buiten de normatieve tekst van NIP-31. Een alt-tag op het hoogste niveau staat bovendien los van het alt-veld voor afbeeldingsbeschrijvingen binnen imeta, en renderers parsen ze afzonderlijk.
Hoe twee clients alt lezen
In de kaart voor onbekende events van noStrudel op commit 7c68acf wordt de eerste alt-tag getrimd en wordt die het hoofdlabel, met een optionele regel Using <client>. Een ontbrekende of lege waarde levert Unknown event (kind ...) op, en daarnaast verschijnen onafhankelijk maximaal drie regels inhoud. Dezelfde component zet de client-tag om via NIP-89-handlersjablonen en toont Open wanneer er een deelbaar adres bestaat en Open in <client> wanneer een geldige handlerlink wordt gevonden. Uitleg en het doorsturen naar een app zijn afzonderlijke bedieningselementen.
UnknownEventDigest.kt van Amethyst op commit 553f761 geeft de voorkeur aan inhoud die het als proza classificeert en valt terug op alt. De parser trimt waarden en neemt de eerste niet-lege alt, dus een event waarvan de eerste alt leeg is, kan hier een ander label krijgen dan in noStrudel. Beide beschrijvingen geven de broncode op die commits weer, geen bevestigd releasegedrag.
Voorbeeld: een poll met een alt-label
De poll van kind 1068 hieronder is opgehaald van wss://relay.damus.io, en de ID en BIP340-handtekening kloppen volgens de regels van NIP-01. Een generieke renderer kan de tweetalige alt-tekst tonen, terwijl een client die polls ondersteunt de tags option en polltype leest.
{
"kind": 1068,
"id": "7d1df3922833b47645922de43c2edd83e928a0ad083cee592beeee2b262bb743",
"pubkey": "5f6dc275f3f9f8752d010a110c226148a9ba3399c5b63a0f469ee93b1394a53e",
"created_at": 1793626294,
"tags": [
[
"option",
"z25mgp",
"Windows"
],
[
"option",
"ncddq0",
"macOS"
],
[
"option",
"4wta6q",
"Linux"
],
[
"option",
"p9ykti",
"UNIX or FreeBSD/NetBSD/OpenBSD"
],
[
"polltype",
"singlechoice"
],
[
"alt",
"Poll: Which one do you usually use?\n普段どれを使っていますか?"
],
[
"client",
"Ditto",
"31990:781a1527055f74c1f70230f10384609b34548f8ab6a0a6caa74025827f9fdae5:ditto"
]
],
"content": "Which one do you usually use?\n普段どれを使っていますか?",
"sig": "f1378a723c65e83bd3105b593f10767903bde993788cfaf625583466fca21f1ef2be945bb2ca526aa9e898c93d5f1677a2f5ceaf70cb3a59c99f97eb179658fa"
}
NIP-73: onderwerpen en families
De NIP-73-specificatie (draft, optional, zonder unrecommended-markering) plaatst een extern onderwerp in een i-tag en de familie van de identifier in een k-tag, zodat clients één exact onderwerp of een hele familie kunnen opvragen. Een boek gebruikt ["i", "isbn:9780765382030"] met ["k", "isbn"], en een webpagina gebruikt haar URL met k ingesteld op web. NIP-73 definieert geen event-kind; andere events dragen de tags.
Normalisatie en URL-hints
Matching hangt af van identieke strings, dus de spec legt per familie een vorm vast: URL’s genormaliseerd zonder fragment, ISBN’s zonder koppeltekens, geohashes in kleine letters, ISO 3166-codes in hoofdletters. Een optioneel derde array-element bevat een URL-hint voor clients zonder eigen interpretatie van de identifier. De auteur kiest die hint, en die maakt geen deel uit van de identiteit. De hint bewijst ook geen eigendom en wijst geen canonieke site aan.
Wat een gedeelde identifier niet bewijst
Volgens NIP-73 brengen gelijke identifiers gesprekken samen en stellen ze verder niets vast. Een ondertekende reactie identificeert wie de reactie schreef, maar authenticeert het boek, de site of de podcastuitgever niet en laat niet zien wie die beheert. Aliassen, editiespecifieke ISBN’s en URL’s die verschillend normaliseren kunnen één onderwerp over meerdere threads verdelen. Context doet ertoe: NIP-22: Comment gebruikt hoofdletters I/K voor de root-scope en kleine letters i/k voor de parent, en NIP-39: External Identities in Profiles gebruikt i voor profielidentiteiten, dus niet elke i-tag is een NIP-73-onderwerp.
NIP-73 in parser- en renderercode
nip73.rs van rust-nostr op commit 4c8099a parseert bekende prefixen naar een getypeerde ExternalContentId en zet een optionele hint om in een getypeerde URL, waarbij het derde tag-element alleen wordt uitgegeven als er een hint is. De tests behouden ISBN’s met koppeltekens: de parser verwijdert de prefix en slaat de rest op zonder de normalisatie uit de spec toe te passen, dus getypeerd parsen garandeert geen canonieke strings. Chain-ID’s worden gescheiden van de familie, zodat ethereum:100:tx:<hash> op ethereum:tx wordt gemapt. Die revisie heeft geen landvariant iso3166.
DisplayExternalId.kt van Amethyst op commit 553f761 stuurt geohash- en hashtag-identifiers naar eigen weergaven. URL-identifiers krijgen ook een eigen weergave, terwijl andere ondersteunde typen naar een generieke scope-chip gaan. Een URL opent de eigen threadroute van de app voor die pagina. Met previews uitgeschakeld toont de normale weergave een gewone linkchip en wordt geen preview opgevraagd. Met previews ingeschakeld kan het ophalen van een ontbrekende preview contact maken met de doelsite. Door previews uit te zetten wordt dat verzoek bij normale weergave vermeden. Geen van beide bestanden stelt releasegedrag vast.
Voorbeeld: een reactie op een webpagina
Deze reactie van kind 1111, opgehaald van wss://nos.lol, plaatst dezelfde openbare Reddit-thread-URL in zowel het I/K-paar als het i/k-paar, met web als familie volgens NIP-22 en NIP-73. De ID en handtekening zijn geldig, wat aangeeft wie de reactie schreef en niets zegt over de gelinkte pagina.
{
"kind": 1111,
"id": "e4229e38d93fad5d612ae21c401573bf8e87f0462dc2bd653804a252f98a238a",
"pubkey": "ce5b8496c144a9dcd4990df6e265d9b60a21cde2515f3b206dc67b1c343790a9",
"created_at": 1793590574,
"tags": [
[
"I",
"https://www.reddit.com/r/writinghelp/comments/1thd194/what_tools_and_systems_do_you_use_for_tracking"
],
[
"K",
"web"
],
[
"i",
"https://www.reddit.com/r/writinghelp/comments/1thd194/what_tools_and_systems_do_you_use_for_tracking"
],
[
"k",
"web"
],
[
"client",
"LayerTake"
],
[
"L",
"layertake"
],
[
"l",
"pin",
"layertake"
],
[
"anchor",
"{\"type\":\"text\",\"exact\":\"tools and systems\",\"prefix\":\"help • 6mo ago u/TheFarSea What \",\"suffix\":\" do you use for tracking and org\"}"
]
],
"content": "Like this. I'm making this Public but could be just for me or friends/groups.",
"sig": "a979389480d6ca919e4ab7b1ba92e532bf281be45c5df222e17d7d4ca62ef07d52c1004c0ce88620663dfac012b306154cfd73b858225764c701c40e3d9b7dfe"
}