Nostr Compass #26
La organización Marmot Protocol abre tres nuevos repos para un borrador de protocolo v2 y un linaje de clientes nativos: un workspace Rust llamado darkmatter, una app iOS SwiftUI darkmatter-ios y una app Android Kotlin/Compose darkmatter-android. El Whitenoise Flutter original se archiva. Chama comprime diecisiete lanzamientos en una semana y cruza la línea de app independiente en v3.0.0 antes de aterrizar un rediseño completo de la UI de la sala de intercambio y storefronts por vendedor en v3.1.0, encima de shares Shamir solo del titular, sustitución de árbitro, enrutado de comunidad mundial y notificaciones de intercambio de extremo a extremo. Coracle lanza un servicio de relay alojado de pago respaldado por la pila de código abierto Caravel y zooid, con integración profunda de Flotilla planeada. Angor cambia a mainnet por defecto en v0.2.30 y aterriza una prueba de financiación UAT de 3 usuarios en v0.2.29. Amethyst aterriza 41 PRs no publicados continuando el trabajo de NIP-32 / NIP-F4 / Tor de la semana pasada. NIP-67 (pista de completitud EOSE) y autocompletado NIP-50 se fusionan, cerrando dos brechas de corrección de larga data en el protocolo de relay central. NIP-GART propone un formato de cable que preserva la privacidad para alertas de emergencia, y NIP-46 recoge un método de logout.
Historias principales
Marmot v2 (Dark Matter): borrador de protocolo, clientes nativos, app Flutter archivada
Esta semana surgieron tres nuevos repos bajo la organización GitHub marmot-protocol, formando juntos la forma temprana de progreso de un borrador del protocolo Marmot v2 y un linaje de clientes nativos que reemplaza la línea de app Flutter. darkmatter (Rust, creado el 13 de mayo, treinta y cuatro commits en los últimos siete días) alberga el borrador del protocolo v2 en spec/, un motor CGKA respaldado por OpenMLS en crates/cgka-engine, un simulador de conformidad con pruebas de propiedad y un modelo formal Tamarin para pruebas de convergencia. darkmatter-ios (Swift, creado el 25 de mayo) es un cliente SwiftUI respaldado por un xcframework UniFFI MarmotKit vendorizado generado desde el workspace Rust. darkmatter-android (Kotlin/Jetpack Compose, creado el 25 de mayo) se apoya en los mismos bindings Rust. El Whitenoise Flutter original ha sido marcado whitenoise-archive (“ARCHIVED: This was the original White Noise Flutter app”); un nuevo repo Dart whitenoise lleva la línea Flutter activa en paralelo.
Lee esto como un progreso temprano hacia un Marmot más fiable, no como un pivote terminado. El README de darkmatter se autodenomina “Candidate Marmot v2 protocol draft, CGKA engine, and conformance workspace” y dice directamente: “MDK remains the deployed Rust protocol implementation until this draft and engine are adopted.” Dentro del workspace, el crate cgka-engine está etiquetado 0.1.0, “single internal consumer, not semver-stable.” Cada página de especificación lleva “Status: draft for internal review”. Tres estrellas en el repo del workspace y cero en las apps iOS y Android confirman que el trabajo es pre-anuncio. La dirección, el alcance y la disciplina son la señal aquí; la disponibilidad para producción no es la afirmación.
El borrador del protocolo concreta los deltas v1-a-v2. La extensión MLS monolítica marmot_group_data de MIP-01, que ha llevado el nombre del grupo, descripción, pubkeys de admin, id de enrutado del grupo Nostr, lista de relays, datos de imagen del grupo y ajustes de mensajes efímeros bajo un solo paraguas desde el inicio de Marmot, se divide en componentes de app versionados: marmot.group.profile.v1 para nombre y descripción, marmot.group.admin-policy.v1 para pubkeys de admin, marmot.transport.nostr.routing.v1 para el nostr_group_id aleatorio y la lista canónica de relays, marmot.group.blossom.image.v1 para hash de imagen, clave de cifrado, nonce y clave de subida, y marmot.group.message-retention.v1 para los segundos de mensajes efímeros. Cada componente posee sus bytes exactos y su propia ruta de versionado, para que una futura característica pueda revisar un componente sin forzar al resto del estado del grupo a retransitar el consenso de la extensión MLS. Las credenciales MIP-00 también ganan un nuevo documento base account-identity-proof-v1.md, señalado como “new in v2 and breaking”. La prueba de identidad ahora vive en su propia superficie, separada de la construcción del KeyPackage.
Los deltas de biblioteca respaldan el retrabajo de especificación. cgka-engine es la nueva máquina de estado local del grupo: envuelve OpenMLS, posee los estados de época Stable, PendingPublish, Merging y Recovering, traduce las intenciones en commits MLS, devuelve valores tipados IngestOutcome y GroupEvent para cada envoltorio de transporte entrante, y explícitamente no distribuye transporte ni persistencia. Un trait TransportPeeler separa Nostr del motor, y un trait StorageProvider separa SQLite (mediante storage-sqlite, respaldado por SQLCipher) del motor. El MDK de hoy empaqueta todo esto junto; dividir las capas permite que un motor se ubique bajo un transporte de relay Nostr ahora y los también distribuidos transportes QUIC stream y broker más tarde, sin reescritura del modelo de convergencia. La propia convergencia está documentada como distributed-convergence.md y probada en un modelo Tamarin que cubre selección determinista de rama, elegibilidad regulada por política, replay de ancla retenida, rechazo de rama obsoleta, reordenado de entrega, duplicación, invalidación de salida de app, traspaso welcome/commit, consumo de propuesta y regulación saliente durante la sincronización. Las pruebas de propiedad Rust luego verifican que el motor sigue las mismas reglas con objetos OpenMLS reales y el arnés del simulador. Trabajo de fiabilidad con métodos formales de este alcance está ausente de la pila Marmot actual.
Ambos clientes nativos abandonan Flutter por kits de UI nativos de la plataforma. darkmatter-ios es SwiftUI puro con una Notification Service Extension que descifra los despertares de push MIP-05 en el dispositivo, vendoriza un paquete Swift MarmotKit generado desde el workspace Rust y se registra bajo el bundle ID y app group dev.ipf.darkmatter. darkmatter-android es Kotlin y Jetpack Compose, con una compilación impulsada por just que produce un APK arm64-v8a firmado y lee endpoints de telemetría desde local.properties. El README de Android indica el principio arquitectónico directamente: “Dark Matter owns protocol data and stores it in SQLite. The Android app should render that data, manage Android platform behavior, and keep UI lifecycle state. The Android app should not become a second database for Dark Matter data.” Eso refleja la disciplina de frontera que el README de cgka-engine aplica en la capa Rust, aplicada a la capa UI.
Los clientes nativos importan para Marmot porque la debilidad más citada del protocolo ha sido la fiabilidad móvil bajo condiciones de entrega irregulares: despertares de notificación perdidos, carreras de commit MLS durante fluctuaciones de red, límites de background-fetch que dejan varados los avances de época. SwiftUI y Compose dan a los clientes acceso directo a las primitivas de procesamiento en segundo plano de la plataforma que Flutter alcanza mediante un puente de plugin, y la ruta de binding UniFFI mantiene la lógica del protocolo en un único workspace Rust distribuido como una biblioteca estática en ambas plataformas. La línea Whitenoise Flutter continúa en el repo desarchivado whitenoise, por lo que el anuncio es aditivo: un nuevo linaje de cliente nativo corre junto a la app Flutter mientras la especificación v2 converge. El corte de producción desde MDK o la app Whitenoise actual espera a que el borrador, el motor y los clientes alcancen lanzamientos listos para producción.
Chama de v2.0.0 a v3.1.0: escrow P2P independiente en una semana
El cliente de escrow P2P nativo de Nostr introducido en Newsletter #25 en v1.3.0 publicó diecisiete lanzamientos etiquetados en los últimos siete días, terminando en v3.1.0 el 9 de junio con un rediseño de la UI de la sala de intercambio y storefronts por vendedor. El rastro de versiones cuenta la historia: v2.0.0 es la base BREAKING, luego v2.0.1, v2.0.2 y v2.0.3 cierran las brechas del pay-rail de financiación por WebView Fedi; v2.1.0, v2.2.0, v2.3.0 y v2.3.1 endurecen la capa del árbitro; v2.4.0, v2.5.0 y v2.6.0 añaden superficies de auto-custodia y enrutado de comunidad mundial; v2.7.0, v2.8.0, v2.9.0 y v2.10.0 superponen texto de clave en lenguaje llano, aplicaciones de grupo, arbitraje por plazo de disputa y reputación. v3.0.0 ata el paquete con notificaciones de intercambio de extremo a extremo, y v3.1.0 el 9 de junio redibuja la pantalla de intercambio en torno a una columna vertebral de progreso Reserved → Locked → Settled, tarjetas de acción coloreadas por rol y una clase de listado de storefront por vendedor (swaps curados, loanbooks y facturas) que se distribuyen.
El pivote arquitectónico vive en v2.0.0. El formato LOCK de escrow cambió para que cada share de una división Shamir 2-de-3 se cifre solo a su titular (sharePolicy holder-only-v1). El ecash al portador de la federación ya no se reconstruye desde un único participante solo, cerrando una ruta donde una parte maliciosa con su propio share y un share retenido por la federación podía completar el intercambio sin consentimiento. Los clientes pre-2.0 fallan ruidosamente con “can’t find your share”; el intercambio no puede completarse en un cliente obsoleto, y no se pierden fondos en el proceso. Un lock v2.0 requiere que cada parte esté en v2.x para liquidar. v2.0.0 también añadió storefronts multi-unidad y una vista Market solo en sats.
v2.1.0 introdujo la sustitución de árbitro: el share del árbitro en el índice Shamir 2 ahora se cifra en un orden de prioridad determinista sobre el pool de árbitros de la comunidad, para que un árbitro ausente pueda ser reemplazado sin varar el intercambio. v2.2.0 probó que la sustitución funcionó en producción en un intercambio de ₿121 y añadió respaldos de sustitución de curación. v2.3.0 cerró la última brecha de front-running de árbitro comprobando la membresía de comunidad del árbitro del listado en el momento del lock, y v2.3.1 cerró la carrera hermana donde un slot de árbitro autoasignado era una vista previa hasta que el lock los asentaba.
Las superficies de auto-custodia llegaron en v2.4.0 (frase de recuperación BIP-39 para la wallet de ecash Fedimint, almacenada cifrada en Nostr) y v2.5.0 (respaldo del nsec maestro que posee la identidad Nostr y la semilla de la wallet). v2.6.0 reelaboró el onboarding en torno a un selector de comunidad global para que los usuarios en países sin una Chama local se enruten a la federación más cercana; las compilaciones anteriores rebotaban al usuario sin fallback. v2.7.0 reescribió la pantalla de clave de recuperación en lenguaje llano (“la única clave a tu cuenta y al dinero en ella; Chama nunca la ve y no puede resetearla; si la pierdes, nadie puede recuperar tu cuenta”). v2.8.0 añadió aplicaciones de grupo, tematización oscura/clara y añadió dos nuevos kinds de evento (38120 roster, 38121 aplicación). v2.9.0 cambió la resolución de disputas en el plazo: los intercambios impugnados que llegan a su expiración ahora se resuelven por dictamen del árbitro; el comportamiento anterior auto-reembolsaba. El lanzamiento está marcado COORDINATED por lo que todas las partes en una disputa deben actualizar. v2.10.0 añadió valoraciones pulgar-arriba/pulgar-abajo por intercambio como un nuevo kind de evento 38123.
v3.0.0 es el hito donde la app deja de necesitar una comunidad coordinadora para operar. Las notificaciones de intercambio de extremo a extremo hacen ping al usuario solo en transiciones de estado accionables: la contraparte bloqueó los sats, el pago está listo para reclamar, la disputa requiere el dictamen del usuario como árbitro, o el intercambio se liquidó o expiró. Un interruptor en la pantalla Me activa o desactiva las notificaciones, y el prompt de permiso solo se dispara cuando el interruptor está habilitado. El dedup fire-once evita que una recarga de estado dispare una tormenta de alertas. También se cerró un bug del guardarraíl wrong-chama en PR #103, donde versiones anteriores podían estampar un listado con la etiqueta de una chama pero la federación de otra chama. Los bundles de escritorio para Windows y Linux se distribuyen con el lanzamiento; el dmg de macOS se retiene hasta que la firma y la notarización aterricen.
Chama ahora se une a Mostro y Shopstr como un marketplace nativo de Nostr, distinguido por arquitectura sin servidor, escrow Shamir 2-de-3 respaldado por Fedimint, cifrado de shares solo del titular y ser el único de los tres que distribuye un cliente de escritorio y móvil autocontenido sin una comunidad coordinadora.
Coracle Hosting: servicio de relay de pago más pila Caravel de código abierto
El 3 de junio Hodlbod anunció Coracle Hosting en hosting.coracle.social, un servicio de relay comunitario alojado que acepta pagos lightning recurrentes sobre NWC o tarjeta. El servicio está impulsado por Caravel, el frontend de facturación y aprovisionamiento de Coracle, y zooid, un runtime de relay que aloja muchos relays virtuales en una sola máquina. Ambos son de código abierto en el gitea auto-alojado de Coracle. Caravel se distribuye con integración opcional de livekit y Blossom que los operadores pueden activar por relay. Un nivel gratuito con límites de conteo de miembros permite a los operadores evaluar el servicio antes de comprometer los datos de pago.
Hodlbod es franco sobre el modelo de negocio: monetizar código abierto vendiendo una versión alojada de una pila que cualquier otro puede también ejecutar. El foso competitivo es la integración con Flotilla, que es el siguiente paso planeado. Flotilla posee la superficie de usuario, por lo que la opción alojada servida desde dentro de Flotilla se convierte en la ruta por defecto para cualquier usuario que prefiera infraestructura gestionada. Hodlbod ofreció añadir otros operadores de Caravel al selector de alojamiento alternativo de Flotilla si contactan, manteniendo la puerta abierta a un mercado de alojamiento federado.
Caravel se une a relay.tools como una plataforma pública de aprovisionamiento de relays Nostr con niveles de miembros de pago. relay.tools precede a Caravel y se distribuye como el servicio dominante de creación de relays hoy, con su propio directorio de relays comunitarios y flujos de unión de miembros o moderadores de pago. La característica distintiva de Caravel es la pila coordinada: el runtime del relay (zooid), el frontend de facturación y aprovisionamiento (Caravel mismo) y el selector del lado del cliente (integración con Flotilla, aún en vuelo) se distribuyen como un solo diseño. La otra característica distintiva es la densidad de muchos-relays-por-proceso de zooid, donde los relays de clientes comparten un único proceso host para que el operador amortice los costes de alojamiento entre muchas comunidades pequeñas. Este es el mismo argumento de densidad que hizo viable el shared web hosting a principios de los 2000, aplicado a la capa de relay de Nostr.
Lanzamientos
Angor v0.2.29 y v0.2.30: mainnet por defecto y prueba de financiación UAT de 3 usuarios
Angor v0.2.29 el 4 de junio y v0.2.30 el 8 de junio son los dos lanzamientos de esta semana para el protocolo descentralizado de financiación Bitcoin-y-Nostr. El cambio destacado de v0.2.30 es PR #893, que cambia la red por defecto a mainnet. Angor aún se distribuye como un lanzamiento alfa inestable, pero el cambio a mainnet por defecto señala que el protocolo ha superado la fase solo-testnet para los clientes de escritorio y móvil. v0.2.30 también aterriza un flujo de creación de proyecto móvil de un solo toque con subida de imagen y reset de scroll (PR #889) y resuelve una condición de carrera donde el spinner de la factura lightning podía colgarse (PR #890).
v0.2.29 añadió una prueba UAT de extremo a extremo en PR #881 cubriendo envío de fondos de 3 usuarios en 10 rondas con gastos no confirmados, la primera prueba de flujo de financiación multi-usuario en el conjunto de pruebas de Angor. El lanzamiento también añadió un plan de implementación para una CLI y servidor MCP de Angor (PR #792), con mejoras en el CLI para el flujo de trabajo de pruebas MCP en PR #880. PR #885 de DavidGershony corrigió una factura lightning Boltz que usaba la red incorrecta tras un cambio de red en runtime, un bug que habría aflorado en producción tras el cambio a mainnet por defecto de v0.2.30. Los ajustes ahora ofrecen una purga opcional del archivo de wallet de recuperación durante el borrado de datos (PR #883).
Sprout v0.3.15: refresco de TTL de canal efímero y comandos slash ACP
Sprout v0.3.15, publicado el 10 de junio, es el octavo lanzamiento en una serie que comenzó con v0.3.7 el 2 de junio. Newsletter #25 cubrió la serie v0.3.1 a v0.3.6 con la integración mesh-llm y el trabajo de secciones de canal; v0.3.7 a v0.3.15 son downstream de eso, centrados en pulido y algunas adiciones de cara al usuario. El cambio más visible al usuario es un refresco de TTL para canales efímeros en PR #902: cuando un usuario desarchiva un canal efímero, Sprout extiende el time-to-live del canal para que el desarchivado no re-archive inmediatamente bajo el temporizador de expiración original. Los emojis personalizados en móvil llegan en PR #906 junto con un rediseño de ajustes, y los conteos de reacción ahora se animan al cambiar (PR #904).
PR #905 corrige una brecha de larga data donde los nombres de visualización de múltiples palabras se rompían y la extracción de menciones nostr:npub de NIP-27 se descartaba silenciosamente. Una UI de equipo respaldada por directorio para escritorio se distribuye en PR #912 con comandos install, sync y reveal. Los comandos slash ahora pasan a los conectores ACP en PR #919, permitiendo a Sprout reenviar comandos estilo /help directamente a los runtimes de agente mientras la UI de Sprout se mantiene fuera de la ruta.
Wisp v1.1.1: integración de wallet Spark y guardia de pegado de nsec
Wisp v1.1.1, publicado el 5 de junio, aterriza una pantalla de conexión de wallet de dos niveles con sub-pantalla Spark en PR #548 y paridad del dashboard con la UI de wallet de iOS en PR #549. El lanzamiento incluye una guardia de pegado de nsec de todo el sistema que detecta un pegado con prefijo nsec1 en cualquier lugar de la app y bloquea al campo de aceptarlo, cerrando uno de los footguns más citados en la UX de Nostr. Inicio de sesión por escaneo QR más un modo solo lectura para npub y nprofile se distribuye en PR #552, permitiendo a un usuario navegar un perfil solo lectura. Los mensajes de zap ahora se renderizan como mini-posts en el drawer de engagement (PR #559) para que las notas de zap lleven su texto junto con la cantidad de sats. Un filtro de web of trust en las respuestas de hilo aterriza en PR #583, permitiendo a los usuarios ocultar spam de respuesta de cuentas fuera de su grafo de follow.
Nostria v3.1.46 y nospeak 1.1.3: retrabajo de notificaciones y reinicio de ICE
Nostria v3.1.46 el 7 de junio termina una serie de tres lanzamientos que retrabajó el contador de notificaciones para contar solo las nuevas notificaciones desde la última vista, eliminando una inflación de larga data donde cargar notificaciones más antiguas desplazándose aumentaba el conteo de la insignia. Nostria v3.1.45 corrigió un bug de pago dividido que afectaba a pagos lightning y de código QR y descartó una UI translúcida previamente planeada como inviable en el compositor de Android.
nospeak v1.1.3 el 4 de junio añade reinicio de ICE en estado FAILED para llamadas de voz 1 a 1. El comportamiento estándar de WebRTC descarta una llamada cuando los candidatos ICE se agotan sin una ruta alternativa; la ruta de reinicio de ICE renegocia candidatos para que la llamada se recupere de cambios transitorios de NAT o red. Las llamadas Android ahora mantienen la pantalla encendida durante las videollamadas.
Cambios sin publicar
Amethyst: 41 PRs continuando la pista NIP-32 / NIP-F4 / Tor
Amethyst fusionó 41 PRs esta semana sin cortar una etiqueta de lanzamiento, encima de los 52 PRs de la semana pasada y el trabajo de etiquetado de hashtags NIP-32 y podcast NIP-F4 cubierto en Newsletter #25. La rama activa sigue acumulando características para el próximo lanzamiento etiquetado, superponiendo pulido a las adiciones destacadas de la semana pasada: descubrimiento de etiquetadores de hashtags, pantalla de podcast, pistas de música y playlists, watchdog de auto-curación de Tor, firmadores efímeros para subidas anónimas y zaps en cadena con filtrado NIP-05. La producción de PRs de Amethyst sigue siendo la más alta de cualquier cliente Nostr, y la cola sin publicar es la hoja de ruta de facto para lo que otros clientes Nostr para Android necesitarán igualar.
Damus: rastreo de relays desde mensajes OK y changelog de v1.17
Damus PR #3786, fusionado el 3 de junio, añade los mensajes OK exitosos de un relay a la lista de post-relay. Las compilaciones anteriores de Damus poblaban la lista de relays vistos solo al recibir un mensaje genérico del relay, lo que significaba que un relay que reconocía el post pero no entregaba eventos de vuelta era invisible para el usuario. El cambio importa para los usuarios que quieren confirmar que su post aterrizó en su relay outbox preferido. PR #3796 corrige un ciclo AttributeGraph en Profile View, y PR #3725 aterriza el changelog de v1.17 antes del próximo lanzamiento etiquetado.
Shopstr: doble publicación NIP-34
El repo de shopstr en ngit fue anunciado en Nostr esta semana como un repo git NIP-34, uniéndose a los repos rastreados de ngit. El repo GitHub del cliente de tienda sigue siendo la superficie principal de desarrollo; el anuncio NIP-34 hace disponible una ruta paralela de colaboración git-sobre-Nostr. Este es el segundo proyecto importante de marketplace Nostr en doble-publicar a NIP-34 tras Mostro, y continúa la migración gradual de metadatos de proyectos al transporte git de Nostr.
Hermes-Marmot: pasarela de agente IA sobre MLS
hermes-marmot, un plugin para el Hermes Agent, conecta la superficie de mensajería de un agente IA con grupos Marmot (MLS-sobre-Nostr) usando mdk-python, los bindings Python al Marmot Development Kit de Rust. El plugin permite a un usuario enviar DM a un agente IA desde cualquier cliente Nostr que hable mensajes MLS kind 445, incluido Whitenoise. Los DMs entrantes usan desenvoltura de gift-wrap NIP-59 mediante los bindings Python de nostr-sdk, y las bienvenidas entrantes fluyen a través de UnwrappedGift.from_gift_wrap a mdk.process_welcome y mdk.accept_welcome. El control de acceso se ejecuta mediante MARMOT_ALLOWED_USERS (una lista blanca de npubs separada por comas) o MARMOT_ALLOW_ALL_USERS=true para acceso de desarrollo abierto.
El repo es nuevo (última actualización el 27 de mayo) y pequeño. Su significado es arquitectónico: es el primer puente público entre un runtime de agente LLM y un canal de mensajería Nostr cifrado con MLS, y el primer uso en producción de mdk-python más allá de Whitenoise mismo. El patrón apunta hacia la comunicación agente-a-agente donde ambos endpoints poseen claves MLS y el relay solo ve texto cifrado.
Actualizaciones de NIP y trabajo de especificación de protocolo
NIP-67 pista de completitud EOSE (PR #2317) fusionado
PR #2317 de mattn se fusionó el 6 de junio, añadiendo NIP-67 al protocolo. El NIP extiende el mensaje de relay EOSE con un tercer elemento opcional: ["EOSE", <subscription_id>, "finish"] señala que cada evento almacenado que coincide con el filtro se ha entregado, mientras que un ["EOSE", <subscription_id>] desnudo no lleva ninguna reclamación de completitud. Un relay que omite la pista está diciéndole al cliente que puede haber más; un relay que omite el anuncio NIP-67 en NIP-11 mantiene el comportamiento actual bajo la heurística legada existente. El cambio es compatible hacia atrás en ambas direcciones: los clientes legados ignoran el elemento final del array, y los relays legados lo omiten.
La motivación en la especificación fusionada es doble. Primero, pérdida silenciosa de datos: un cliente pide las últimas 500 notas contra un relay con un límite interno de 300 eventos, el relay devuelve 300 eventos y el cliente (usando la heurística estándar received < limit) concluye que el resultado está completo. Las notas coincidentes 201.ª hasta la N.ª más antigua permanecen en el relay sin leer, con el cliente ciego a ese hecho. Segundo, viajes de ida y vuelta obligatorios desperdiciados: cuando un relay limita las respuestas a 300 eventos, cualquier suscripción que agota el límite requiere un segundo REQ con until=<oldest_created_at> puramente para confirmar la completitud, incluso cuando el filtro coincide exactamente con 300 eventos. Ambos modos de fallo son pagados por cada cliente en cada suscripción con límite agotado. La pista "finish" es una cadena opcional en un mensaje existente y elimina ambos costes.
Extensión de autocompletado NIP-50 (PR #2357) fusionado
PR #2357 de Alex Gleason se fusionó el 6 de junio, añadiendo un token autocomplete:true/false a la búsqueda NIP-50. La extensión permite a un cliente marcar una consulta como una búsqueda typeahead para que el relay use coincidencia de prefijo, con búsqueda de texto completo como predeterminado para consultas sin el token. El relay de Ditto lo implementa para follow packs, listas y cualquier evento con un tag title, devolviendo coincidencias contra el prefijo del título; la ruta de búsqueda por defecto ejecuta puntuación de texto completo. Sin este token, las UIs estilo autocompletado no tenían forma de comunicar la intención de búsqueda por prefijo y los relays tenían que adivinar por la forma de la consulta. El token es una pista por búsqueda, no una capacidad a nivel de relay, por lo que un relay puede implementarlo para una clase de evento (títulos) sin reclamar soporte general de autocompletado.
Alertas de emergencia y difusiones de ubicación NIP-GART (PR #2374)
PR #2374 de disinqa, abierto el 9 de junio, define un formato de cable que preserva la privacidad en Nostr para alertas de emergencia y difusiones de ubicación dirigidas a un grupo de destinatarios de confianza. El objetivo de diseño declarado es ocultar la identidad del remitente, la membresía del grupo y el payload a los operadores de relay mientras se mantiene los eventos seguros contra replay y verificables por firma de extremo a extremo. El número de NIP está aún por determinar, la propuesta es borrador temprano. El caso de uso es el patrón estándar de alerta de emergencia: un usuario bajo amenaza difunde un ping de ubicación que solo un grupo pre-compartido de contactos de confianza puede descifrar, con el relay ciego al remitente, al conjunto de destinatarios y al payload. Los detalles del formato de cable viven en el PR y probablemente evolucionarán a medida que los mantenedores revisen.
Método logout NIP-46 (PR #2373)
PR #2373 de hzrd149, abierto el 8 de junio, añade un método logout a NIP-46 para que un cliente pueda decirle a un bunker explícitamente que la sesión ha terminado. Hasta ahora, la única forma de terminar una sesión de bunker era esperar el timeout de la sesión o dejar de usar la conexión, ambas dejan al bunker manteniendo el estado de la sesión para un cliente que se ha ido. La propuesta es corta (un nuevo método) y es el tipo de cambio de mantenimiento que hace las integraciones de bunker de larga vida más limpias.
Propuesta híbrida relay-P2P NIP-95 circulada como formato largo
Una especificación NIP-95 de formato largo circuló como un post kind:30023 de npub 91bea5cd9361504c409aaf459516988f68a2fcd482762fd969a7cdc71df4451c el 4 de junio bajo el título Protocolo Híbrido Relay-P2P via WebRTC. El documento en portugués define un protocolo híbrido de relay peer-to-peer donde los clientes Nostr se conectan entre sí directamente mediante WebRTC para mensajería en vivo mientras continúan usando relays para la recuperación de eventos almacenados y la entrega offline. El autor enmarcó explícitamente la especificación como “LLM-ready”, proporcionando definiciones de mensajes, flujos lógicos, esquemas de datos y reglas de estado a un nivel de detalle que permite a un modelo de IA generar código cliente o servidor funcional. La propuesta aún no ha aterrizado como un PR de NIP; la circulación mediante kind:30023 es el precursor habitual a un pull request formal a nostr-protocol/nips.
NIP-44 v3 recoge un segundo firmador: Clave porta la especificación
El despliegue de NIP-44 v3 de v6.2.0 de Amber de la semana pasada se distribuyó por delante de cualquier PR de NIPs fusionado, dejando v3 como una extensión específica de Amber que otros clientes tenían que reflejar para interoperar. Ese enmarque de implementación única cambió esta semana. Clave, el firmador remoto NIP-46 iOS basado en push, aterrizó un port independiente de NIP-44 v3 el 3 y 4 de junio a través de ocho commits. Las primitivas criptográficas se distribuyen en tres commits: capa de claves HKDF + ECDH, el algoritmo de padding v3 y una API pública de alto nivel más Context de cifrado. Encima de esos, la superficie NIP-46 sigue en cableado de despacho RPC dentro de LightSigner y un esquema PendingRequest que lleva el contexto v3 (kind más scope), para que el firmador pueda registrar para qué kind de evento y caso de uso se aprobó el payload v3.
Clave diverge de Amber en la superficie de cara al usuario. Un esquema de concesión de permisos con niveles de sensibilidad permite a los usuarios conceder cifrado v3 para un kind de evento y scope particular a un nivel de sensibilidad elegido. En el primer encuentro, prompts de aprobación conscientes del contexto v3 con una tarjeta explicativa única introducen v3 a los usuarios. El trabajo está en main y está conectado al proyecto Xcode pero no está publicado; la compilación etiquetada más reciente es v0.2.0-build79 del 12 de mayo.
Dos implementaciones independientes aterrizan NIP-44 v3 en rutas de producción antes de que el PR de NIPs se fusione, lo que fortalece el caso para el formato de cable subyacente que el PR del protocolo formalizará. Las pruebas de interoperabilidad entre implementaciones ahora se convierten en la ruta a la convergencia de la especificación, con la superficie de aprobación Android de Amber y el modelo de niveles de sensibilidad iOS de Clave como los dos puntos de referencia. Otros firmadores remotos cableando v3 (noauth de nsec.app ha estado inactivo desde mayo de 2025, y otros bunkers no han anunciado trabajo v3) endurecerían el consenso aún más.
Actividad NIP-34: Iris adopta la pila con un nuevo transporte hashtree
Iris publicó anuncios de repo NIP-34 para hashtree el 8 de junio y iris-apps, iris-drive y iris-chat-rs el 9 de junio, anunciando URLs de clonado bajo un nuevo esquema htree:// servido desde wss://temp.iris.to. El transporte hashtree es una alternativa direccionable por contenido a los clonados enrutados por GRASP, y estos cuatro anuncios son sus primeros usos públicos. Los repos llevan descripciones vacías y los detalles arquitectónicos aún están emergiendo, pero la elección de publicar mediante anuncio NIP-34 (sobre un manifiesto Iris-interno personalizado) señala que Iris se compromete con la pila git-sobre-Nostr NIP-34 más amplia.
NIP deep dive: NIP-67 (Pista de completitud EOSE)
NIP-67 cierra una de las brechas de corrección más antiguas en NIP-01. La especificación original define EOSE como el límite entre eventos almacenados y eventos de suscripción en vivo para un REQ, pero nunca especificó si el relay había terminado de entregar todas las coincidencias almacenadas o se había detenido a mitad de camino por un límite interno. Cada relay aplica un límite por suscripción (comúnmente 300 a 1000 eventos) independiente del limit del cliente, y los clientes no han tenido forma de observar ese límite.
El workaround estándar era comparar el conteo recibido contra el limit solicitado. Si received < limit, tratar el resultado como completo; de lo contrario, paginar con until=<oldest_created_at>. Ambas ramas están rotas. La rama received < limit trunca silenciosamente: un cliente pidiendo 500 notas contra un relay limitado a 300 ve 300 eventos, concluye que el resultado está completo porque 300 < 500 y nunca obtiene el resto. Los eventos retenidos en el relay no pueden señalizar “más disponible” a través de ningún mensaje existente. La paginación como segunda rama es derrochadora: un filtro que coincide exactamente con el límite requiere un segundo REQ para confirmar la completitud, devolviendo cero eventos mientras consume un escaneo de filtro completo en el relay.
La corrección de NIP-67 es una cadena opcional en el mensaje EOSE:
["EOSE", "<sub_id>", "finish"] // explícito: todos los eventos almacenados entregados
["EOSE", "<sub_id>"] // ninguna reclamación de completitud
Un relay que anuncia NIP-67 en supported_nips de NIP-11 y emite un EOSE desnudo está diciéndole al cliente que hay más. Un relay que omite el anuncio mantiene el comportamiento actual, y el cliente recae en la heurística existente. Los clientes legados ignoran el elemento final del array. La compatibilidad hacia atrás se mantiene en ambas direcciones, sin nuevos verbos ni kinds de evento.
Lo que hace a NIP-67 digno de examinar es el alcance que deliberadamente restringe. La especificación no define ningún cursor o token de paginación, por lo que la paginación basada en until sigue siendo el mecanismo. Los límites de relay permanecen donde están, y el NIP no requiere exposición de ellos. NIP-67 preserva el significado de EOSE como el límite almacenado-a-en-vivo y solo añade una señal sí-o-no en el límite: “tengo más para ti” versus “eso es todo”. Esta superficie mínima es por qué el PR se fusionó tras un período de revisión relativamente corto para una extensión NIP-01, y por qué mattn nota explícitamente en el PR que se usó traducción por IA para el texto en inglés. El cambio es lo suficientemente pequeño como para que la incertidumbre de traducción no importe.
Ejemplo de intercambio consciente de NIP-67 entre un cliente y un relay que aplica límites. Anuncio NIP-11 del relay:
{
"id": "a5f87fe2d4c8b9a0e3f1c4d5e6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f",
"pubkey": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"created_at": 1781136000,
"kind": 11,
"tags": [],
"content": "{\"supported_nips\":[1,11,50,67]}",
"sig": "f1e2d3c4b5a6978869504132c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f80192a3b4c5d6e7f80192a3b4c5d6e7f80192a3b4c5d6e7f80192a3b4c5d6e7f80192a3b4c5"
}
El intercambio a nivel de cable que sigue:
→ ["REQ", "abc", {"kinds":[1],"limit":500}]
← [...300 EVENT messages...]
← ["EOSE", "abc"] // sin "finish": límite alcanzado, más disponible
→ ["REQ", "def", {"kinds":[1],"limit":300,"until":1780900000}]
← [...178 EVENT messages...]
← ["EOSE", "def", "finish"] // completo explícito
La respuesta de 178 eventos previamente habría disparado un tercer REQ para confirmar la completitud. Con NIP-67 el cliente se detiene ahí.
NIP-67 también es notable como una enmienda a NIP-01 aterrizando con un consenso raro. La mayoría de los cambios a NIP-01 atraen largos hilos de debate porque la pequeña superficie del protocolo es portante para cada implementación. NIP-67 se fusionó tras un período de revisión extendido (aproximadamente siete semanas de apertura a fusión), sugiriendo que cuando un cambio a NIP-01 es lo suficientemente pequeño y el modo de fallo es lo suficientemente concreto (pérdida silenciosa de datos, viaje de ida y vuelta obligatorio desperdiciado), los mantenedores del protocolo están dispuestos a extender el vocabulario central de mensajes.
NIP deep dive: NIP-50 (Búsqueda)
NIP-50 define el campo de filtro search en los mensajes REQ, permitiendo a los clientes pedir a un relay que filtre eventos por coincidencia de texto completo contra una cadena de consulta. La especificación base fusionada es deliberadamente mínima: el campo search es una cadena, cada relay decide su propia semántica de búsqueda (qué campos están indexados, cómo funciona la puntuación, si se aplica stemming) y los relays anuncian soporte NIP-50 en su documento NIP-11. Los clientes controlan el algoritmo de búsqueda solo a través de la propia cadena de consulta.
Este minimalismo es a la vez la fortaleza y la limitación de NIP-50. La fortaleza es que cualquier relay puede implementar búsqueda a cualquier nivel de calidad: un escaneo básico de subcadenas satisface la especificación, y un relay ejecutando Elasticsearch o Meilisearch la satisface igualmente. La limitación es que los clientes carecen de una forma de expresar la intención de búsqueda. Una UI typeahead de menciones de perfil quiere coincidencia de prefijo contra nombres de visualización; una búsqueda de texto completo de contenido quiere puntuación tokenizada de texto completo a través del cuerpo de la nota. El mismo campo search lleva ambos, y el relay debe adivinar por la forma de la consulta.
PR #2357 añade el primer token de extensión NIP-50: autocomplete:true o autocomplete:false incrustado en la consulta de búsqueda señaliza qué modo quiere el cliente. El relay de Ditto implementa el token para follow packs, listas y cualquier evento con un tag title, cambiando a coincidencia de prefijo cuando autocomplete:true está presente. El token vive inline en la consulta (los campos de filtro separados quedan intactos), por lo que viaja con la cadena de búsqueda y no requiere ninguna actualización del protocolo de cable:
search: "fiat autocomplete:true"
Las pistas con forma de token como esta son cómo NIP-50 siempre ha manejado los dialectos específicos de relay. Los relays ya soportaban tokens como language:en y domain:example.com. Cada uno sigue siendo específico del relay, con cada relay documentando su propio dialecto. El PR #2357 de NIP-50 eleva autocomplete de un token privado de relay a uno bendecido por la especificación, allanando el camino para búsqueda consciente de typeahead entre relays.
Ejemplo REQ NIP-50 con el token autocomplete, apuntando a un relay que indexa títulos de perfil kind 0:
{
"id": "b7c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f7081a2b3c4d5e6f70819a2b3c4d5",
"pubkey": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"created_at": 1781136000,
"kind": 1,
"tags": [
["client", "example-mention-picker"]
],
"content": "Sent search: kinds=[0], search=\"fiat autocomplete:true\", limit=10",
"sig": "12d3e4f5061728394a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f7081a2b3c4d5e6f70819a2b3c4d5e6f7081a2b3c4d5e6f708192"
}
El REQ real a nivel de cable:
["REQ", "mention-picker", {"kinds":[0],"search":"fiat autocomplete:true","limit":10}]
Un relay que no reconoce el token trata autocomplete:true como parte de la cadena literal de búsqueda y recae en coincidencia de texto completo, devolviendo resultados correctos (aunque clasificados diferente). La degradación elegante hace que el token sea seguro para incluir incondicionalmente para clientes que prefieren coincidencia de prefijo cuando está disponible.
La siguiente extensión NIP-50 probable es control de ranking por kind: una pista que dice “ordenar por created_at descendente” versus la puntuación de relevancia por defecto. Varios relays ya aceptan sort:newest como un token privado de relay, y se aplica la misma ruta de elevación que trajo autocomplete a la especificación. La búsqueda sigue siendo una de las pocas primitivas Nostr donde los relays compiten en la calidad del resultado; la fiabilidad de entrega es la misma en todos los relays conformes. Los tokens incrementales permiten a los clientes explotar esa competencia de calidad sin forzar a los relays a distribuir una nueva especificación pesada.