Otros clientes: Nostr de terceros y móvil
Otros clientes: Nostr de terceros y móvil
Buzz no es una aplicación con un backend propietario detrás. Es un relay Nostr que habla NIP-29 (grupos basados en relay) de forma nativa y usa NIP-42 para autenticar. Eso tiene una consecuencia concreta y muy práctica: cualquier cliente Nostr que implemente NIP-29 y NIP-42 puede conectarse directamente a tu relay Buzz, leer canales, escribir mensajes, reaccionar y buscar, sin pasar por la app de escritorio.
Este capítulo cubre exactamente eso: el procedimiento real documentado en el
NOSTR.md del repositorio, qué funciona y qué no, qué se pierde frente a la app
oficial, y en qué estado están de verdad los clientes móviles.
Un aviso previo que evita confusiones: el viejo proxy de compatibilidad NIP-28 fue eliminado. Ya no existe una capa de traducción para clientes de chat NIP-28 clásicos. La única puerta es NIP-29 directo contra el relay.
1. El scope de comunidad cuando te conectas
Antes de conectar nada, hay que entender qué “ves” al abrir el WebSocket.
Buzz trata la URL/dominio del relay como autoritativa para la comunidad. En el despliegue de un solo relay que se distribuye hoy, detrás de esa URL hay exactamente una comunidad, así que los clientes NIP-29 existentes siguen usando la misma URL de WebSocket, los mismos kinds, los mismos tags y las mismas rutas HTTP/media/git. En un despliegue multi-comunidad, cada comunidad se alcanza por su propio dominio o subdominio, y el backend resuelve la comunidad a partir del host antes de manejar tráfico AUTH, EVENT, REQ, REST, media, git, búsqueda o workflows.
El formato de cable de Nostr no gana un tag de tenant. Los tags #h que envía
el cliente siguen nombrando canales/grupos y se contrastan contra la comunidad
derivada del host. Los eventos sin #h —perfiles, DMs envueltos en gift wrap,
notificaciones de membresía, listas, estado, notas long-form, eventos de workflow
y de sistema y otros flujos “globales”— son globales solo dentro de la comunidad
conectada.
De ahí se deduce lo que a un usuario le importa de verdad:
- Tu keypair es tuyo en todas las comunidades.
- Tu perfil, tus DMs y tu contenido sin canal no se heredan entre dominios de comunidad. Una pubkey puede unirse a varias comunidades y repostea su perfil en cada una.
- Si apuntas tu cliente Nostr a otra URL, estás en otro mundo: otros canales, otros miembros, otro índice de búsqueda.
Esto es lo mismo que ya viste en Qué es Buzz y cómo se trabaja en él, pero visto desde el cable en vez de desde la interfaz.
2. Qué necesitas para conectar un cliente de terceros
Tres cosas:
- La URL WebSocket del relay. En desarrollo local es
ws://localhost:3000. En un relay de equipo será elwss://que te dio quien lo opera. - Tu clave privada Nostr. Es la identidad; no hay tokens ni sesiones. Si necesitas repasar cómo se genera y se guarda, está en Tu identidad: claves, perfil y dispositivos.
- Permiso de entrada, si el relay lo exige.
Ese tercer punto es territorio de operador. El arranque rápido documentado es este:
# 1. (Opcional) Habilitar allowlist de pubkeys — debe fijarse ANTES de arrancar el relay
export BUZZ_PUBKEY_ALLOWLIST=true
# 2. Arrancar el relay (levanta servicios Docker y corre migraciones)
just relay & # relay en :3000
# 3. Añadir un pubkey al allowlist (si está habilitado)
# Insertar directamente — todavía no hay comando de CLI para esto.
PGPASSWORD=buzz_dev psql -h localhost -U buzz -d buzz -c \
"INSERT INTO pubkey_allowlist (pubkey) VALUES (decode('<64-char-hex-pubkey>', 'hex'))"
# 4. Conectar cualquier cliente NIP-29 + NIP-42 a ws://localhost:3000
Los pasos 1 a 3 los ejecuta quien administra el relay, no tú. Si tu pubkey no está
autorizada, el relay responde auth-required: verification failed sin decir por
qué. Pídele el alta a la persona que opera el relay; el procedimiento completo
—allowlist, membresía NIP-43, roles— está en el
curso de administrador.
El ciclo de conexión
sequenceDiagram
participant C as Cliente Nostr
participant R as buzz-relay
C->>R: WebSocket sobre el host de la comunidad
R->>R: Resuelve la comunidad desde el host
R-->>C: AUTH con challenge
C->>R: AUTH con evento kind 22242 firmado
R->>R: Verifica firma y allowlist
R-->>C: Conexión autenticada
C->>R: REQ y EVENT
R-->>C: Eventos, EOSE, OK
El relay envía el reto NIP-42 de inmediato, de forma proactiva. El cliente debe responder con el evento firmado antes de enviar eventos o suscripciones. Los clientes que implementan NIP-42 hacen esto solos.
3. Descubrir grupos
Los canales de Buzz se publican como estado de grupo NIP-29 firmado por el relay.
Todos los eventos de descubrimiento llevan un tag d con el UUID del canal.
| Kind | Tags | Contenido |
|---|---|---|
| 39000 | d, name, closed siempre; about si la descripción no está vacía; private si aplica; hidden solo en canales DM | Metadatos del grupo |
| 39001 | d + tags p con etiqueta de rol owner o admin | Lista de admins |
| 39002 | d + tags p de todos los miembros | Lista de miembros |
# Todos los grupos que puedes ver
nak req -k 39000 --auth --sec <privkey> ws://localhost:3000
# Miembros de un grupo concreto (filtrando por tag d)
nak req -k 39002 --tag "d=<channel-uuid>" --auth --sec <privkey> ws://localhost:3000
Dos advertencias del documento que ahorran horas de depuración:
- Estos eventos se almacenan channel-scoped, para que se aplique el control de
acceso: las listas de miembros de un canal privado solo son visibles para sus
miembros. Como efecto colateral, una suscripción global en vivo del tipo
{kinds:[39000]}no los recibe por fan-out. Los clientes descubren grupos con REQ históricos. El push en vivo para descubrimiento de canales abiertos es una mejora futura. - El tag
closedsiempre se emite, por convención NIP-29, porque los canales de Buzz requieren membresía explícita. Pero los canalesopensiguen siendo legibles y escribibles por no miembros en tiempo de ejecución. El tag refleja el modelo de membresía, no la aplicación del acceso.
4. Notificaciones de membresía
Cuando te añaden o te quitan de un canal, el relay firma una notificación:
| Kind | Significado | Tags | Alcance |
|---|---|---|---|
| 44100 | Miembro añadido | p = pubkey objetivo, h = UUID de canal | Community-global |
| 44101 | Miembro eliminado | p = pubkey objetivo, h = UUID de canal | Community-global |
Se almacenan community-global (channel_id = None dentro de la comunidad
conectada) para que clientes y agentes puedan suscribirse sin conocer los UUID de
canal por adelantado. Los eventos kind:44100/44101 enviados por un cliente se
rechazan: solo el keypair del relay puede firmarlos.
nak req -k 44100 -k 44101 --tag "p=<your-hex-pubkey>" \
--auth --sec <privkey> ws://localhost:3000
Restricción de suscripción. Los REQ globales que puedan casar con kinds p-gated —44100, 44101 y 1059— deben incluir un filtro
#pdonde todos los valores sean tu pubkey autenticada. El relay rechaza suscripciones que omitan#po incluyan otras pubkeys, para impedir espiar cambios de membresía y DMs ajenos. El error literal esrestricted: p-gated events require #p matching your pubkey.
5. Enviar, leer, reaccionar, borrar, crear
Estos son los comandos tal como aparecen en el documento, usando nak (el cliente
CLI de Nostr verificado manualmente contra Buzz):
# Enviar un mensaje kind:9
nak event -k 9 -c "Hello from NIP-29!" --tag "h=<channel-uuid>" \
--auth --sec <privkey> ws://localhost:3000
# Suscribirse a los mensajes de un canal
nak req -k 9 --tag "h=<channel-uuid>" --stream \
--auth --sec <privkey> ws://localhost:3000
# Reaccionar a un mensaje (#h opcional pero recomendado; el canal se deriva del #e objetivo)
nak event -k 7 -c "+" --tag "h=<channel-uuid>" --tag "e=<message-event-id>" \
--auth --sec <privkey> ws://localhost:3000
# Suscribirse a reacciones — incluir #h para entrega en vivo
nak req -k 7 --tag "h=<channel-uuid>" --stream \
--auth --sec <privkey> ws://localhost:3000
# Borrar un mensaje (#h opcional; #e obligatorio; debe ser de autoría propia)
nak event -k 5 -c "reason" --tag "h=<channel-uuid>" --tag "e=<message-event-id>" \
--auth --sec <privkey> ws://localhost:3000
# Crear un grupo
nak event -k 9007 --tag "name=my-channel" --tag "visibility=open" \
--auth --sec <privkey> ws://localhost:3000
# Buscar mensajes (NIP-50)
nak req -k 9 --tag "h=<channel-uuid>" --search "search query" -l 20 \
--auth --sec <privkey> ws://localhost:3000
# Responder a un mensaje (threading NIP-10)
nak event -k 9 -c "Reply text" --tag "h=<channel-uuid>" \
--tag "e=<parent-event-id>;;reply" \
--auth --sec <privkey> ws://localhost:3000
# Traer DMs con gift wrap (NIP-17)
nak req -k 1059 --tag "p=<your-hex-pubkey>" \
--auth --sec <privkey> ws://localhost:3000
Qué kind usar para qué
| Acción | Kind | Reglas duras |
|---|---|---|
| Mensaje de canal | 9 | Requiere tag #h. Sin él se rechaza con invalid: channel-scoped events must include an h tag |
| Reacción | 7 | NIP-25 estándar. El canal se deriva del #e del objetivo; el #h del cliente se ignora para determinar canal. Reacciones a eventos desconocidos se rechazan |
| Borrado | 5 | NIP-09 estándar, solo auto-autoría. #e obligatorio, #h opcional |
| Perfil | 0 | NIP-01. Se sincroniza a la tabla de usuarios. Los handles NIP-05 deben canonicalizar al dominio de este relay; los de fuera o inválidos se limpian en silencio |
| Crear canal | 9007 | Tag name; opcionalmente visibility y channel_type |
| Añadir usuario | 9000 | En canal open, cualquiera, sujeto al channel_add_policy del objetivo. En privado, solo owner/admin |
| Quitar usuario | 9001 | Auto-remoción permitida con guarda de último owner. Quitar a otros: owner/admin |
| Editar metadatos | 9002 | Tags name/about: owner/admin. Tags topic/purpose: cualquier miembro |
| Borrado administrativo | 9005 | El autor siempre puede borrar lo suyo; si no, owner/admin. El objetivo debe estar en el mismo canal |
| Borrar el grupo | 9008 | Solo owner |
| Pedir unirse | 9021 | Solo canales open. Los privados se rechazan en ingest |
| Salir del grupo | 9022 | Cualquier miembro, con guarda de último owner |
| Presencia | 20001 | Efímero. Cadena de estado arbitraria truncada a 128 caracteres |
| ”Está escribiendo” | 20002 | Efímero, no se almacena |
Búsqueda NIP-50
La búsqueda funciona como REQ de una sola tanda, no como suscripción persistente:
{"search":"query","kinds":[9],"#h":["<uuid>"]}
El relay devuelve resultados ordenados por relevancia y cierra con EOSE. Estas consultas no se registran como suscripciones persistentes, así que no esperes que sigan llegando resultados nuevos. Si quieres el modelo completo de búsqueda que ofrece la app, está en Búsqueda: encontrar la conversación, el patch y la decisión.
La trampa de las reacciones
Es el error más común y está documentado explícitamente. El relay deriva el canal
de una reacción a partir de su objetivo #e, así que las reacciones a eventos
channel-scoped quedan también channel-scoped. El fan-out en vivo mantiene
estrictamente separadas las suscripciones channel-scoped y las globales. Por
lo tanto:
{"kinds":[7]}→ no recibe ninguna de esas reacciones.{"kinds":[7],"#h":["<channel-uuid>"]}→ sí.
El matching por #h funciona lleve o no la reacción firmada un tag h: los tags
h explícitos se casan directamente, y las reacciones sin tag casan por su canal
almacenado.
Hilos NIP-10
Las respuestas enviadas por WebSocket con tags ["e","<root>","","reply"] crean
thread_metadata de forma atómica y son visibles en las consultas REST de hilo.
Los padres desconocidos se rechazan. El modelo de hilos desde la app está en
Hilos, reacciones y actividad.
DMs NIP-17
Los DMs viajan como gift wrap kind:1059, aceptados con claves de firma
efímeras, almacenados community-global y entregados por suscripciones filtradas
por #p. No se indexan en búsqueda. Crear un DM emite un kind:39000 con tag
hidden más notificaciones kind:44100, así que los clientes NIP-29 descubren los
DMs por el flujo estándar de descubrimiento de grupos.
Lo que no está: NIP-04 y NIP-44 no están implementados, y el kind:10050 (lista de relays de DM) está diferido. Si tu cliente favorito solo sabe hacer DMs al estilo NIP-04, no verá nada. Más contexto en Mensajes directos y privacidad.
6. Límites del protocolo que tu cliente debe respetar
| Límite | Valor en el código | Dónde vive |
|---|---|---|
| Tamaño máximo de trama WebSocket | 524.288 bytes (512 KiB), ajustable con BUZZ_MAX_FRAME_BYTES | DEFAULT_MAX_FRAME_BYTES |
| Suscripciones máximas por conexión | 1024 | MAX_SUBSCRIPTIONS |
| Resultados históricos máximos por filtro | 1000 | DEFAULT_MAX_PAGE_LIMIT |
| Filtros máximos por REQ | 10 | max_filters de NIP-11 |
| Longitud máxima del id de suscripción | 256 | max_subid_length de NIP-11 |
| Contenido máximo de un evento | 65.536 bytes | max_content_len de NIP-11 |
La forma honesta de leer esta tabla: no la memorices, pregúntasela al relay. Los
tres primeros valores son los que el binario aplica hoy, y el propio relay los publica
en su documento NIP-11, así que basta un curl:
curl -s -H 'Accept: application/nostr+json' http://localhost:3000 | jq .limitation
Si consultas ARCHITECTURE.md verás una tabla de constantes con 65.536 de trama y 500
de límite histórico: esa sección quedó desfasada respecto del código, y el limitation
del NIP-11 es la fuente que manda. El que sí sigue siendo 65.536 es el contenido de un
evento, que es el límite que de verdad te topa al escribir mensajes largos o pegar
diffs.
Y un detalle de NIP-01 que rompe suscripciones ingenuas: kinds: [] (array
vacío explícito) significa “no casar con nada”, no comodín. Esas suscripciones
no se indexan y nunca reciben eventos. Omitir el campo kinds por completo sí
significa “todos los kinds”.
7. Qué no vas a ver desde un cliente de terceros
Aquí está el corte real entre “hablar el protocolo” y “usar Buzz”.
Hay kinds que funcionan en el cable pero son solo-Buzz: ningún cliente NIP-29 estándar los renderiza.
| Kind | Qué es | Efecto en un cliente de terceros |
|---|---|---|
| 40002 | Contenido enriquecido de mensaje v2 | Llega, no se pinta |
| 40003 | Edición de mensaje | Llega, no se aplica: verás el original |
A eso se suma todo lo que ni siquiera es NIP-29: canvases (kind 40100), definiciones de workflow (30620) y sus disparos, eventos de huddle, memoria de agente, perfiles de agente y personas. Un cliente genérico no tiene interfaz para nada de eso.
Y hay cosas que directamente no funcionan hoy:
| Función | Estado | Por qué |
|---|---|---|
| Crear invitación (kind:9009) | Parcial | Se acepta y almacena, pero el manejador de efecto está diferido: no-op con warning en el log |
| Roles de grupo (kind:39003) | No | Definido en el registro de kinds pero el relay no lo emite |
| DMs NIP-04 / NIP-44 | No | Solo NIP-17 gift wrap. kind:10050 diferido |
Resumen honesto: un cliente Nostr de terceros te sirve para leer y escribir
conversación —mensajes, hilos, reacciones, borrados, DMs gift-wrapped, búsqueda,
membresía—. Para agentes, workflows, canvases, huddles, git y proyectos necesitas
la app de escritorio o buzz-cli, que es justo lo que viste en
buzz-cli: el workspace desde la terminal.
Clientes probados
| Cliente | Plataforma | Evidencia | Alcance verificado |
|---|---|---|---|
| BuzzTestClient | Rust, en el repo | E2E automatizado | Flujo NIP-29 completo: descubrimiento 39000/39001/39002, envío y recepción kind:9, reacciones, borrados, cumplimiento del tag h |
| E2E nostr interop | Rust, en el repo | E2E automatizado | Búsqueda NIP-50 (3 tests), hilos NIP-10 (3 tests), gift wraps NIP-17 (3 tests), descubrimiento de DM (1 test) |
| nak | CLI | Manual, verificado | Envío y recepción kind:9, búsqueda NIP-50, respuestas de hilo NIP-10, descubrimiento de grupos |
No verificados en el repositorio, anecdóticos o esperados por su soporte NIP-29: Chachi (web y móvil, basado en NDK) y 0xchat (móvil). Si los usas, estás en territorio no probado.
Diagnóstico rápido
| Síntoma | Causa | Arreglo |
|---|---|---|
auth-required: verification failed | Pubkey fuera del allowlist, si está activo, o falló NIP-42 | Que el operador añada tu pubkey; verifica el reto/respuesta NIP-42 |
invalid: channel-scoped events must include an h tag | kind:9 enviado sin tag #h | Incluye --tag "h=<channel-uuid>" |
invalid: reaction target event not found | La reacción referencia un evento desconocido | Asegúrate de que el evento objetivo existe en el relay |
| No llegan eventos de descubrimiento | El canal es privado y no eres miembro | Únete al canal primero |
8. Los clientes móviles: estado real
Esto hay que decirlo sin adornos. En la tabla del README del proyecto —“Works today / Being wired up / Strong opinions, pending code”— los clientes móviles (iOS + Android, Flutter) están en la columna 🚧 Being wired up, junto con las compuertas de aprobación de workflows y los eventos de ciclo de vida de huddles. No están en la columna de lo que funciona hoy.
Lo que sí existe es un proyecto Flutter completo en mobile/, con estructura de
producción y una superficie ya considerable.
Qué hay en mobile/
Es una aplicación Flutter llamada buzz, con SDK de Dart ^3.11.4, gestión de
estado con Riverpod + Hooks (HookConsumerWidget), tema Catppuccin Latte
en claro y Macchiato en oscuro —el mismo que el escritorio—, tokens de espaciado
Grid, linting con flutter_lints + riverpod_lint, y aislamiento de features:
ningún import cruzado entre features salvo a través de shared/.
La navegación principal son tres pestañas: Home, Activity y Search.
Módulos presentes bajo lib/features/:
channels— el bloque más grande con diferencia: página de canal, ventana de canal, hilos, barra de composición, selector de emoji, visor de medios, menciones, silenciados, favoritos, secciones y orden de canales, indicador de escritura, insignia de no leídos, acciones de mensaje, fila de reacciones, actividad de agentes, canales efímeros y etiquetas de canales DM.activity— bandeja de entrada, feed, borradores de composición, recordatorios y estado de leído de la bandeja.search— página de búsqueda y búsquedas recientes.forum— publicaciones, tarjetas, hilos y proveedor de foro.pulse— notas, tarjetas de nota, actividad de agente y composición de nota.profile— avatar, perfil de usuario, presencia, estado de usuario y hoja de cambio de estado.pairing— el flujo de emparejamiento con QR.invites— unirse por invitación.settingsyhome.
Bajo lib/shared/ hay piezas que muestran hasta dónde llega: relay con sesión,
socket, filtros Nostr, modelos, subida y autenticación de medios, validación y
límite de tasa; crypto con nip44.dart, ecdh.dart, hkdf.dart y
nip_oa.dart; read_state para sincronizar la posición de lectura; más
custom_emoji, emoji, mentions, reminders, deeplink, community y
theme.
Una nota de honestidad sobre el propio repositorio: la sección “Architecture” del
mobile/README.md todavía describe features/home/ como “Placeholder home
surface”. El código real ya va muy por delante de ese README.
Cómo entras: emparejamiento NIP-AB
En móvil no tecleas tu clave privada. El flujo de alta es un emparejamiento de
dispositivo NIP-AB: la app abre una pantalla de emparejamiento, escanea un
código QR o acepta un código pegado, se suscribe a eventos kind:24134
etiquetados a su pubkey efímera, y valida cada evento —kind correcto, origen
esperado, tag p apuntando a ti, descarte de duplicados—. Hay verificación con
código SAS: si no coincide, el emparejamiento se cancela por seguridad con el
mensaje SAS code mismatch — pairing cancelled for security.
flowchart TD
A["La app móvil abre la pantalla de emparejamiento"] --> B["Escanea el QR o pega el código"]
B --> C["Se suscribe a kind 24134 con su pubkey efímera"]
C --> D["Recibe el evento cifrado del dispositivo origen"]
D --> E{"¿Coincide el código SAS?"}
E -- No --> F["Emparejamiento cancelado por seguridad"]
E -- Sí --> G["Guarda la comunidad con su nsec"]
G --> H["Sesión autenticada en esa comunidad"]
La app guarda una comunidad por credencial: cada comunidad almacenada lleva su
propio nsec, hay una comunidad activa, y al cerrar sesión en una, si quedan
otras, cambia a la siguiente en vez de devolverte a la pantalla de emparejamiento.
Eso encaja exactamente con el modelo de “identidad portable, perfil por comunidad”
del que hablamos al principio.
El emparejamiento entre dispositivos también aparece en Tu identidad: claves, perfil y dispositivos.
Enlaces profundos e invitaciones
La app móvil parsea enlaces buzz://:
buzz://message?channel=<uuid>&id=<hex>con&thread=<hex>opcional, para saltar a un mensaje concreto de un canal.buzz://join?relay=<ws o wss>&code=<code>, el traspaso a la app instalada desde un enlace de invitación.
También reconoce enlaces de invitación HTTPS canónicos y los convierte al relay
correspondiente. El host buzz://connect es solo de escritorio y la app móvil
lo ignora.
Cómo se ejecuta y se comprueba
Los comandos del repositorio, para quien quiera compilarlo:
# Instalar dependencias
cd mobile && flutter pub get
# Desde la raiz del repo: abre el simulador de iOS, aplica la identidad de
# worktree y lanza flutter run
just mobile-dev
# Directo, con servicios y relay ya corriendo
cd mobile && flutter run
# Comprobaciones
dart format --output=none --set-exit-if-changed .
flutter analyze
flutter test
Desde la raíz también existen just mobile-install, just mobile-fmt,
just mobile-fix, just mobile-check, just mobile-test,
just mobile-build-android (APK de depuración sin firmar) y just mobile-clean
(desinstala builds de depuración con sufijo de worktree; nunca toca instalaciones
de producción). Ninguna de esas recetas levanta el relay: el README.md de mobile/
todavía dice que just mobile-dev arranca Docker y el relay, pero la receta real del
Justfile solo abre el simulador y ejecuta flutter run. El relay lo levantas aparte,
con just relay.
La configuración de desarrollo se pasa por un .env.json, cuyo ejemplo declara
exactamente dos claves:
{
"BUZZ_RELAY_URL": "http://localhost:3000",
"BUZZ_DEV_PUBKEY": "<your-hex-pubkey-here>"
}
Las builds de depuración creadas desde un worktree de git reciben un identificador
de aplicación único derivado del nombre del directorio del worktree
(com.buzz.buzzMobile.<slug> en iOS, xyz.block.buzz.mobile.<slug> en Android)
más una etiqueta de rama solo visual en el nombre. Así, un worktree conserva
exactamente una app instalada —y su estado de sesión— entre cambios de rama, y
builds de varios worktrees conviven. Las builds de release y profile mantienen
siempre la identidad y el nombre de producción.
La firma de release de Android exige variables de entorno con la keystore de subida y es trabajo de quien publica, no del usuario: eso vive en el curso de administrador.
9. Cómo elegir cliente
flowchart TD
A["¿Qué necesito hacer?"] --> B{"¿Conversación, hilos, reacciones, DM, búsqueda?"}
B -- Sí --> C["Un cliente NIP-29 de terceros sirve"]
B -- No --> D{"¿Agentes, workflows, canvases, huddles, git?"}
D -- Sí --> E["App de escritorio o buzz-cli"]
D -- No --> F{"¿Automatización desde scripts?"}
F -- Sí --> G["buzz-cli con JSON de entrada y salida"]
F -- No --> E
La app de escritorio es la superficie completa y la referencia de este curso;
buzz-cli es la vía de automatización y agentes, tratada en los capítulos
10 y
11; un cliente Nostr de terceros
te da conversación desde donde no tengas la app, con las limitaciones de arriba;
y el móvil Flutter existe en el repositorio pero está oficialmente “cableándose”.
Resumen
- Buzz habla NIP-29 nativo y autentica con NIP-42; los clientes de terceros se conectan directo al relay. El antiguo proxy de compatibilidad NIP-28 fue eliminado.
- La URL es la comunidad. El backend resuelve la comunidad desde el host antes de AUTH, EVENT, REQ, REST, media, git, búsqueda o workflow. Perfiles y DMs no se heredan entre dominios.
- Kinds de conversación: 9 mensaje (requiere
#h), 7 reacción (el canal sale del#eobjetivo), 5 borrado (solo auto-autoría), 9007 crear grupo, 9021/9022 unirse y salir, 0 perfil. - Descubrimiento por 39000/39001/39002 mediante REQ históricos: las suscripciones globales en vivo no reciben esos eventos.
- Los kinds p-gated 44100, 44101 y 1059 exigen un filtro
#pcon tu propia pubkey; si no, la suscripción se rechaza. - NIP-50 es búsqueda de una tanda con EOSE, no suscripción; NIP-10 usa
["e","<root>","","reply"]y rechaza padres desconocidos; NIP-17 entrega DMs gift-wrapped kind:1059 fuera del índice de búsqueda. - Límites según el código y el
limitationde NIP-11: trama de 512 KiB (BUZZ_MAX_FRAME_BYTES), 1024 suscripciones por conexión, 1000 resultados históricos por filtro, 10 filtros por REQ y 65.536 bytes de contenido por evento.kinds: []significa “nada”, no comodín. - Frente a la app oficial pierdes: ediciones y contenido enriquecido (40003 y 40002 llegan pero no se renderizan), canvases, workflows, huddles, agentes y git. Las invitaciones kind:9009 se almacenan pero su efecto está diferido, y el kind:39003 no lo emite el relay.
- Clientes verificados en el repo: BuzzTestClient, la suite E2E nostr interop y nak. Chachi y 0xchat no están verificados.
- Los clientes móviles Flutter para iOS y Android están en la columna “being
wired up” del README. En
mobile/hay una app Riverpod + Hooks con pestañas Home, Activity y Search, features de canales, hilos, foro, pulse, búsqueda, perfil, invitaciones y emparejamiento NIP-AB por QR con verificación SAS.
Siguiente: Flujos completos y buenas prácticas