📇Soporta verificación NIP-05 de Nostr en tu propio dominio
Nostr es un protocolo diseñado para ser descentralizado, resiliente, resistente a la censura y fácilmente extensible. Aprenderás a verificar tu identidad en Nostr conforme nip05 vía tu propio dominio.
Last updated
Introducción a Nostr
Si nunca has utilizado Nostr necesitarás entender algunos conceptos claves.
Lo primero que tienes que comprender es que Nostr es un protocolo diseñado para enviar mensajes –notas, principalmente– a través de Internet.
Nostr es el acrónimo de la frase en inglés “Notes and Other Stuff Transmitted by Relays", que se puede traducir por "Notas y otras cosas transmitidas por relés".
Nostr es código libre, un protocolo sencillo –de ahí su genialidad y resiliencia– y abierto, de forma que cualquiera puede participar, implementar o construir sobre el mismo.
Fue diseñado por fiatjat_ como un protocolo descentralizado para crear redes sociales incensurables al no existir una entidad centralizada que pueda prohibir o censurar algo.
Aunque es descentralizado no se basa en p2p o en tecnología blockchain.
Los usuarios interactúan con el protocolo a través de un cliente–web, app, aplicación de escritorio, etc.– y publican notas firmadas digitalmente y con un sello temporal haciendo uso de criptografía asimétrica. Al tratarse de un protocolo abierto y sencillo, cualquiera puede programar su propio cliente. De hecho, existen muchos clientes, lo que es magnífico, dado que evita el monopolio y, por ende, la censura.
Las notas son enviadas por el cliente del usuario hacia un conjunto de relés o retransmisores, también sencillos de implementar. Los retransmisores –relays, en inglés– proporcionan el almacenamiento y retransmisión de las notas. Cualquiera puede correr su propio retransmisor, lo que de nuevo garantiza la resiliencia del protocolo. Lo interesante aquí es que no necesitamos confiar en los relés, ya que la verificación de las notas se hace en el lado cliente. Además, podemos cambiar o añadir nuevos relés en cualquier momento. En la siguiente ilustración se aprecia la idea de múltiples relés o retransmisores para que los usurios de Nostr puedan comunicarse. Notad que, para ver los mensajes el uno del otro necesitan utilizar un mismo relé.
La firma y sellado temporal de las notas impide su manipulación o falsificación, garantizando su integridad y autenticidad. Se pueden verificar a través de la clave pública de su autor, esta clave pública identifica al autor, es posible buscar un usuario en Nostr a través de su clave pública.
Mi clave pública en Nostr es npub1hhml4uqkctuqdeuegg6ckvkt53ggkm84jgvy640e7spz6085aq0qlsmu6k, un tanto farragosa y complicada de recordar, pero para eso existen los alias o nick-names, el mío es Sec_Adviser. Si pulsas sobre mi alias, verás mi perfil en el cliente web primal.net de forma similar a como se aprecia en la siguiente ilustración. Quizás notes que mi usuario tiene una marca de verificación similar a la marca de verificación que muestra la red social X, antes conocida como Twitter.
Vamos a aprender a obtener la marca de verificación implementando nip05 en vuestro propio dominio y servidor.
Eventos del protocolo
Podríamos decir que en el protocolo Nostr solo existe un tipo de objeto: el evento. Los usuarios publican eventos de distintos tipos, con un sello temporal y firmados criptográficamente con la clave privada del usuario.
Las NIPs –Nostr Implementation Possibilities– son el estándar que define la implementación mínima que debe respetar cualquiera que pretenda interactuar con el protocolo, al tiempo que facilita extender sus capacidades mediante la adición de nuevos NIPs. En los NIPs existen partes de implementación obligatoria y partes opcionales. El formato de un evento conforme nip01 se define tal que, se cita literal:
// Formato de un evento Nostr{"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>,"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,"created_at": <unix timestamp in seconds>,"kind": <integer between 0 and 65535>,"tags": [ [<arbitrary string>...],// ... ],"content": <arbitrary string>, "sig": <64-bytes lowercase hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
}
Vamos a explicar el formato de un evento sobre un par de ejemplos reales, de forma que espero sea más didáctico y sencillo de entender. Si buscáis mi usuario –en base a mi clave pública que ya conocéis– en Nostr.band, obtendréis un resumen de mi perfil y los últimos resultados o notas publicadas por mí, algo similar a lo que se aprecia en la siguiente ilustración.
La nota anterior la vamos a denominar nota_respuesta. Además, si bajas un poco más entre los resultados, verás que hay un mensaje previo tal que:
Este último mensaje, que llamaremos nota_inicial, es un mensaje que publiqué 10 días atrás, en concreto el 18 de enero de 2024, mensaje al que he respondido hoy, 28 de enero, es por ello que el bocadillo de diálogo muestra un 1, indicando que hay una respuesta.
Como el protocolo es muy sencillo, he utilizado unas librerías en Python para capturar estos dos eventos que hemos denominado nota_inicialy nota_respuesta. A continuación se muestran ambos eventos o notas con el formato original en el que viajan por la Internet.
Primero el evento nota_inicial:
// Evento "nota_inicial"{"id":"cbfb2273264e1bb892236b24a1a1c0b65efad2856b5e704691e68360959b2c6b","pubkey":"bdf7faf016c2f806e79942358b32cba4508b6cf592184d55f9f4022d3cf4e81e","created_at":1705600279,"kind":1,"tags": [],"content":"Mensaje de prueba, para explicar el protocolo Nostr. Fecha: 18 enero 2024.", "sig": "604aa49495e036d1ed2bfb54f9c9b0103817df3bf4956d8e641507fe15df5a006857645a89a4553759e4bdae247eb97fda94eb2c701b540c9cb69504b944fa62"
}
Posteriormente, la respuesta dada a la nota previa en la nota_respuesta:
// Evento "nota_respuesta"{"id":"9ae0eb602be730065f172cd9fbfa0d5941779cd1265348823fae3581e1a46e19","pubkey":"bdf7faf016c2f806e79942358b32cba4508b6cf592184d55f9f4022d3cf4e81e","created_at":1706445257,"kind":1,"tags": [ ["e","cbfb2273264e1bb892236b24a1a1c0b65efad2856b5e704691e68360959b2c6b","","root" ], ["p","bdf7faf016c2f806e79942358b32cba4508b6cf592184d55f9f4022d3cf4e81e" ] ],"content":"Me respondo a mí mismo para ver el formato de evento Nostr en una respuesta.", "sig": "444dca6e94e4d43eba778f2b3b3a1cb25f39209fb0f8a0c626dd64aa16b22d1214f0341eace4cd116ca5bcb8eb91307fd90cb54b78251d13ab24e497ab4731f6"
}
Vamos a ver uno a uno los campos de un evento:
ID:un identificador del evento.
Pubkey: la clave pública del autor codificada en hexadecimal, 32 octetos de longitud.
Created_at: fecha en formato epoch, habitual de UNIX, epoch es el número de segundos transcurridos desde el 1 de enero de 1970 a las 00:00:00 UTC.
Kind: especifica cómo los clientes deben interpretar el propio evento y los campos que incluye. Un NIP puede especificar un nuevo tipo de kinds. Se trata de un entero de 16 bits, de forma que puede tomar 65536 valores distintos, pero vamos a los dos más básicos:
0: metadatos: en este tipo de eventos se incluye información en el campo "content" con en un formato que es la representación de un objeto json vía un tipo cadena –string–.
1:nota de texto: sonmensajes en texto plano, como un tuit.
Tags: las etiquetas permiten relacionar un evento con otro evento o usuario, añadir enlaces o contenidos multimedia.
Content: texto arbitrario, el mensaje.
Sig: firma criptográfica en hexadecimal del resultado de calcular el resumen criptográfico sha 256 del resto de campos de la nota.
Tanto en la nota_inicial como en la nota_respuesta el campo ID son distintos, esto es normal, ya que identifican de forma única a cada evento y esto permite, por ejemplo, buscar un evento concreto por su id.
El campo pubkey es idéntico en ambas notas, es lo esperado ya que ambos han sido creados por mí y necesitas poder identificar al autor y conocer su clave pública para poder verificar la autenticidad e integridad del mensaje. La clave pública está codificada en hexadecimal. Puedes obtener tu clave pública en hexadecimal en Nostr.band escogiendo "Copy pubkey" como se aprecia en la siguiente ilustración.
En nota_inicial el campo created_at vale 1705600279, mientras que en nota_respuesta el valor es 1706445257. Podemos convertir ese valor en segundos a una fecha fácilmente con el comando en Linux y/o MacOS así:
En ambos eventos el valor de la clave kind es 1, lo que significa que son notas o eventos de texto cuyo contenido viaja en el campo content.
En nota_inicial la clave tags no tiene valores asociados, sin embargo, en la nota_respuesta el campo o clave tags sí tiene valores asignados, esto ocurre porque es una respuesta a otro evento. A continuación el contenido de tags en la nota_respuesta:
// Valor campo tags en nota_respuesta:"tags": [ ["e","cbfb2273264e1bb892236b24a1a1c0b65efad2856b5e704691e68360959b2c6b","","root" ], ["p","bdf7faf016c2f806e79942358b32cba4508b6cf592184d55f9f4022d3cf4e81e" ] ],
El primer valor de tags es una "e" –etiqueta utilizada para referenciar a otro evento– que indica que el evento actual está relacionada con otro evento con id cbfb2273264e1bb892236b24a1a1c0b65efad2856b5e704691e68360959b2c6b, justamente el id del evento nota_inicial, y es que recoordemos que nota_respuesta es una respuesta a nota_inicial. Hay una segunda etiqueta añadida, "p" –utilizada para referenciar a otro usuario–, que indica este evento está relacionado con alguien identificado por la clave pública bdf7faf016c2f806e79942358b32cba4508b6cf592184d55f9f4022d3cf4e81e, que es mi clave pública en formato hexadecimal. Tanto "e" como "p" son consideradas etiquetas estándar. Hay otra etiqueta estándar, "a", que referencia a eventos reemplazables, pero no la veremos aquí, por ahora.
Para finalizar el campo o clave content contiene los mensajes o notas en texto plano y el campo sig la firma criptográfica que protege la integridad del mensaje de forma que puede comprobarse si es íntegro, quién y cuándo lo emitió.
Implementación verificación nip-05
En este apartado aprenderemos a implementar la verificación en Nostr o, en jerga técnica, como implementar NIP-05, especificación que, en pocas palabras, mapea las npub o claves públicas que identifican de forma única a un usuario de Nostr a identificadores de internet basados en DNS (a un correo electrónico, para que lo entiendas mejor, que no deja de ser otro identificador único). Si implementamos correctamente esta mejora, en tu perfil Nostr que aparecerá al utilizar cualquier cliente Nostr y buscar tu npub, podrás observar el tick de verificación junto a tu correo electrónico.
La implementación será sobre un servidor web basado en NGINX. La lógica de aplicación que nos demanda NIP-05 la desarrollaremos usando un lenguaje de scripting ligero y rápido conocido como LUA, lenguaje que por sus características es ideal para ser embebido en otras aplicaciones, como Roblox, Nmap, Apache HTTP Server o nuestro servidor NGINX.
El código fuente en LUA y la configuración del servidor web NGINX la podéis encontrar en mi repositorio Github, a saber:
Este repositorio contiene toda la lógica de aplicación en LUA necesaria para implementar la verificación NIP-05 en Nostr, así como los archivos de configuración necesarios que sirven como modelo para instalar tu propio servidor NGINX, en el que puedes configurar tu propio dominio. Aquí puedes encontrar ejemplos funcionales, con comentarios detallados, para configurar tu propio entorno.
Además, os voy a contar todo de una forma mucho más amena en un vídeo de apenas 45' en el que veremos:
Breve introducción.
Fundamentos de Nostr.
Eventos del protocolo.
Implementación verificación NIP-05 en LUA en CLI y sobre servidor web NGINX.
Con lo que has aprendido hasta el momento, estás más que capacitado para entender el vídeo, aunque... no te preocupes, repaso todo desde el principio y lo explicaré de forma muy práctica y con ejemplos. Sin más demora, adelante vídeo...
Espero que hayas disfrutado con el artículo y el vídeo. Ya conoces mi npub por si quieres seguirme o comentar algo.
Nos vemos en Nostr... ¡únete a la resistencia, te esperamos!