Git dentro de Buzz: patches, repos y la rama como sala

Por: Artiko
buzznostrgitnip-34patchescli

Git dentro de Buzz: patches, repos y la rama como sala

Hasta aquí el workspace fue conversación: canales, hilos, agentes, workflows. Este capítulo cierra el círculo con la parte que el resto de las herramientas suele dejar fuera de la sala: el código. En Buzz un repositorio no es un enlace a otro sitio, es un evento firmado más dentro del mismo log; un patch no es un adjunto, es un evento con kind propio; y una rama puede tener su propio canal donde el parche, el resultado de CI, la revisión del agente y la decisión de merge quedan uno debajo del otro.

En la tabla de estado del README, «Git events (NIP-34: patches, repo announcements, status)» y «Git hosting backend» están ambos en la columna “Works today”. Lo que sigue distingue con cuidado lo que ya funciona de lo que es rumbo declarado.

Tres capas que conviene no mezclar

Cuando alguien dice “git en Buzz” puede estar hablando de tres cosas distintas que conviven pero no dependen entre sí.

flowchart TD
    A["Transporte: git smart HTTP<br/>clone, fetch, push contra el relay"] --> D["Tu repositorio de trabajo"]
    B["Metadatos NIP-34: eventos firmados<br/>anuncio de repo, patches, estados"] --> D
    C["Conversación: canales, hilos,<br/>diffs, reacciones, workflows"] --> D
    D --> E["Un solo log de eventos firmados<br/>y un solo índice de búsqueda"]
  • El transporte es aburrido a propósito. Es git smart HTTP estándar: el relay expone GET /git/{owner}/{repo}/info/refs, POST /git/{owner}/{repo}/git-upload-pack y POST /git/{owner}/{repo}/git-receive-pack. Tu git clone y tu git push de siempre funcionan; lo único distinto es cómo se autentica.
  • Los metadatos son portables. El anuncio de repositorio, los patches y los estados son eventos NIP-34, el mismo estándar que leen clientes de terceros. Buzz los extiende con tags con prefijo buzz-, que otros clientes ignoran sin romperse.
  • La conversación es lo que hace la diferencia. Un diff enviado a un canal es un mensaje con kind propio, buscable, reaccionable y capaz de disparar un workflow.

Los kinds de git que vas a ver

Los diez primeros son eventos NIP-34 estándar; los dos últimos son propios de Buzz.

KindQué es
30617Anuncio de repositorio, reemplazable por d-tag
30618Estado del repositorio: refs actuales de ramas y tags
1617Patch, es decir la salida de git format-patch
1618Pull request
1619Actualización de PR, por ejemplo cambio del commit de cabecera
1621Issue
1630Estado: abierto
1631Estado: aplicado o fusionado
1632Estado: cerrado
1633Estado: borrador
40008Mensaje diff en formato unified diff dentro de un canal, propio de Buzz
30621Proyecto multi-repo, con tags a que apuntan a repos, propio de Buzz

La distinción entre 1617 y 40008 importa en la práctica. Un patch kind 1617 es una contribución formal a un repositorio anunciado, con serie, revisiones y estados. Un diff kind 40008 es un mensaje de canal que muestra un cambio de código: sirve para pedir una opinión, para que CI publique lo que acaba de construir, o para disparar un workflow. Los dos se firman y los dos quedan en el log, pero cumplen funciones distintas.

Anunciar un repositorio

El anuncio es lo que convierte un directorio con .git en una entidad del workspace. Se hace con buzz repos create.

buzz repos create --id checkout-api --name "Checkout API" \
  --description "Servicio de cobro" \
  --clone https://relay.ejemplo.com/git/<owner-hex>/checkout-api \
  --web https://relay.ejemplo.com/git/<owner-hex>/checkout-api \
  --nostr-relay wss://relay.ejemplo.com \
  --channel 0f5e2a1c-9d3b-4c77-8a21-6b0e4d9f1a33

Detalles que hay que respetar: --id es el d-tag y solo admite [a-zA-Z0-9._-], entre 1 y 64 caracteres; --clone y --nostr-relay son repetibles, para publicar varios espejos o varios relays de descubrimiento; y --channel es opcional en la sintaxis, pero no en la práctica.

Ese último punto es la trampa más común del capítulo. El tag buzz-channel es la lista de control de acceso de git: el relay autoriza clone, fetch y push por pertenencia al canal enlazado. Un repo anunciado sin ese tag, por ejemplo desde un cliente NIP-34 genérico, devuelve 404 a todo el mundo hasta que su autor lo enlace. Si te pasa, la reparación es un comando:

buzz repos bind --id checkout-api --channel 0f5e2a1c-9d3b-4c77-8a21-6b0e4d9f1a33

bind reemplaza cualquier enlace previo: mover un repo de canal es mover quién puede clonarlo. Para leer están buzz repos get --id checkout-api --owner <owner-hex> y buzz repos list --owner <owner-hex> --limit 50. Si omites --owner en get, coincide con cualquier dueño; si lo omites en list, lista los tuyos.

Protecciones de rama

Las reglas de protección viven en el mismo evento de anuncio, como tags buzz-protect, y el relay las aplica en la capa de transporte de git. Se manejan sobre tus propios repositorios:

buzz repos protect list --id checkout-api

buzz repos protect set --id checkout-api --ref refs/heads/main \
  --push admin --no-force-push --no-delete

buzz repos protect remove --id checkout-api --ref refs/heads/main
  • --push acepta exactamente owner, admin o member: el rol mínimo de canal que puede empujar a los refs que coincidan.
  • --no-force-push rechaza actualizaciones que no sean fast-forward.
  • --no-delete rechaza el borrado de los refs coincidentes.
  • --require-patch exige el flujo de patches NIP-34 en lugar de pushes directos.
  • --ref es un patrón completo, del estilo refs/heads/main o refs/heads/*.

Dos advertencias literales de la documentación del CLI: protect set reemplaza todas las reglas del patrón exacto, así que toda restricción que omitas del comando queda eliminada; y protect list reporta las reglas guardadas malformadas en un campo validation_error, para que el dueño pueda quitarlas y repararlas.

Agrupar repos en un proyecto

El trabajo real casi nunca cabe en un repositorio. El grupo se expresa con buzz projects, respaldado por el kind 30621.

buzz projects create plataforma \
  --repo checkout-api --repo 30617:<otro-owner-hex>:checkout-web \
  --name "Plataforma" --visibility listed \
  --channel 0f5e2a1c-9d3b-4c77-8a21-6b0e4d9f1a33

buzz projects add-repo plataforma --repo 30617:<owner-hex>:checkout-jobs
buzz projects get plataforma --owner <owner-hex>

--repo acepta el id corto de un repo de Buzz o la coordenada completa 30617:<owner-hex>:<repo-d>, y es repetible y obligatorio en create, add-repo y remove-repo. --visibility acepta listed o unlisted. buzz projects update exige al menos un campo a cambiar, entre --name, --clear-name, --description, --clear-description, --channel, --clear-channel, --visibility y --clear-visibility.

Lo importante del modelo, dicho en la documentación de diseño: «Un proyecto apunta a repos. Eso es todo lo que hace». El firmante del proyecto no gana ninguna autoridad sobre los repos miembros: ni edición, ni borrado, ni push, ni administración. Agregar el repo de otra persona a tu proyecto es tu afirmación firmada de que los dos van juntos, y no cambia nada sobre quién puede empujar ahí. La política de push siempre se lee del evento del repo, nunca del proyecto.

La rama como sala

Este es el flujo que da nombre al capítulo. El README lo describe así: «Abres una rama de feature. Aparece un canal. Los patches aterrizan como eventos NIP-34, CI publica resultados, un agente corre una primera revisión, los compañeros reaccionan a las partes que les importan, y la decisión de merge aterriza en la misma sala que la evidencia».

sequenceDiagram
    participant Dev as Persona
    participant Canal as Canal de la rama
    participant CI as Agente de CI
    participant Rev as Agente revisor
    participant Team as Equipo

    Dev->>Canal: primer mensaje y patch kind 1617
    Canal->>CI: dispara el workflow
    CI->>Canal: resultado del build y de los tests
    Canal->>Rev: el patch aparece en su suscripción
    Rev->>Canal: primera revisión automática
    Team->>Canal: reacciones y comentarios en hilo
    Dev->>Canal: patch v2, corrige la revisión
    Team->>Canal: aprobación
    Dev->>Canal: estado merged, kind 1631
    Canal->>Canal: el canal se archiva como registro

El valor no es la automatización en sí, es que no hay salto de pestaña. El canal es a la vez el pull request, el tablero de CI y el hilo de discusión. Y como todo son eventos del mismo tipo, la búsqueda encuentra en una sola consulta la conversación, el patch, la corrida del workflow y la aprobación.

Sobre el estado real de esto: el hosting de git y los eventos NIP-34 funcionan hoy —el README los pone a los dos en la columna ✅ Works today—. La creación automática de un canal al abrir una rama, el coordinador de merge, el project binding completo y la capa de issues NIP-34 aparecen en la tabla de estado de VISION_PROJECTS.md marcados como 📋 diseñados, no como enviados. Cuidado con no leer de más esa tabla: el buzz-channel que describimos arriba sí funciona hoy —es la ACL de git efectiva—, y el CLI ya trae los grupos projects e issues aunque la forja alrededor no exista. Ese documento es la declaración de rumbo del proyecto: describe hacia dónde va la forja, y conviene leerlo como tal. Lo que sí puedes armar hoy, a mano, es exactamente el mismo flujo, y está al final del capítulo.

Enviar un diff desde la terminal

Este es el comando que más vas a usar. Manda un mensaje de código a un canal, con metadatos que la interfaz sabe renderizar. Cuatro flags son obligatorios: --channel, --diff, --repo y --commit. El valor - en --diff significa leer de stdin, la convención habitual del CLI.

Los opcionales, todos reales y todos útiles:

FlagPara qué sirve
--fileRuta de un único archivo dentro del repo
--parent-commitSHA del commit padre, para contexto de diff a tres vías
--source-branchNombre de la rama de origen
--target-branchNombre de la rama de destino
--prNúmero de pull request, entero
--langPista de lenguaje; si se omite se deduce de la extensión del archivo
--descriptionDescripción legible del cambio
--reply-toId de evento al que responder, con lo que el diff nace dentro de un hilo

Un envío completo, del tipo que un agente publica al terminar una tarea:

git diff main...HEAD -- src/auth/pkce.rs | buzz messages send-diff \
  --channel 0f5e2a1c-9d3b-4c77-8a21-6b0e4d9f1a33 --diff - \
  --repo https://relay.ejemplo.com/git/<owner-hex>/checkout-api \
  --commit $(git rev-parse HEAD) --parent-commit $(git rev-parse HEAD~1) \
  --source-branch feat/pkce --target-branch main \
  --file src/auth/pkce.rs --lang rust \
  --description "Implementa PKCE en el flujo OAuth2" \
  --reply-to <event-id-del-hilo>

Tres cosas que conviene tener presentes:

  1. Hay un límite de tamaño. El contenido de un diff se valida contra un tope de 61 440 bytes, más bajo que el tope general de mensaje, que es de 65 536 bytes. Un diff gigante no entra: manda el patch por buzz patches send o corta el cambio.
  2. La interfaz lo renderiza como tarjeta, no como texto. El componente de escritorio DiffMessage muestra el archivo, la descripción, el host del repositorio, el SHA corto de siete caracteres y, si hay --repo y --commit, un enlace directo a <repo>/commit/<sha>. Los diffs largos se truncan con un botón para expandirlos.
  3. Dispara workflows. El trigger diff_posted de los workflows en YAML escucha exactamente el kind 40008: es el gancho para que CI reaccione sin que nadie apriete nada. Ver el capítulo 11.

Patches NIP-34: la contribución formal

Cuando el cambio va dirigido a un repositorio anunciado y no solo a la conversación, el evento correcto es el patch kind 1617.

git format-patch -1 HEAD --stdout | buzz patches send \
  --repo-owner <owner-hex> --repo-id checkout-api --patch-file - --root

Los flags de buzz patches send:

FlagSignificado
--repo-ownerPubkey hex de 64 caracteres del dueño del repo
--repo-idIdentificador del repo, el d-tag
--patch-fileRuta a un archivo de git format-patch, o - para stdin
--eucEarliest unique commit del repositorio
--toPubkey destinataria adicional, repetible
--reply-toId del patch anterior de la serie, o del root original si es revisión
--rootMarca este patch como el primero de una serie nueva
--root-revisionMarca este patch como el primero de una revisión nueva de una serie existente
--commitId del commit que produce este patch al aplicarse
--parent-commitId del commit padre
--commit-pgp-sigFirma PGP del commit
--committerIdentidad, en el formato name|email|timestamp|tz-offset-minutes

Ojo con la diferencia entre --patch-file y --diff del comando anterior: --patch-file nombra un archivo, donde - significa stdin y cualquier otro valor es una ruta del disco; --diff recibe el contenido literal, y - también es stdin.

Para leer y listar están buzz patches get --event <event-id-hex> y buzz patches list --repo-owner <owner-hex> --repo-id checkout-api, este último con --author y --limit opcionales.

El ciclo de vida de un patch

Los estados son los kinds 1630 a 1633 y se publican con buzz patches status:

stateDiagram-v2
    [*] --> draft: kind 1633
    [*] --> open: kind 1630
    draft --> open: listo para revisión
    open --> merged: kind 1631
    open --> closed: kind 1632
    closed --> open: se retoma
    merged --> [*]
    closed --> [*]
buzz patches status --root <event-id-del-primer-patch> --status merged \
  --repo-owner <owner-hex> --repo-id checkout-api \
  --merge-commit <sha> --applied-as-commit <sha> \
  --content "Aplicado en main tras la revisión de Ana."

Reglas exactas del comando:

  • --status solo acepta open, merged, closed o draft.
  • --repo-owner requiere --repo-id y viceversa; van siempre en pareja.
  • --merge-commit, --revision, --applied-as-commit y --q solo son válidos con --status merged; los dos últimos son repetibles. --q acepta <id>, <id>:<relay-url> o <id>:<relay-url>:<pubkey>.
  • --to agrega destinatarios además del dueño del repo, que se etiqueta solo cuando das --repo-owner. --content acepta markdown, y - para leerlo de stdin.

Pull requests e issues

Existen como grupos propios del CLI, con los kinds 1618, 1619 y 1621:

buzz pr open --repo-owner <owner-hex> --repo-id checkout-api \
  --subject "Fix bug" --body-file - --commit $(git rev-parse HEAD) \
  --clone https://relay.ejemplo.com/git/<owner-hex>/checkout-api \
  --branch-name fix-bug

buzz issues create --repo-owner <owner-hex> --repo-id checkout-api \
  --title "Timeout en el retry de cobro" --content - < descripcion.md

En pr open, --clone es repetible y obligatorio, y --body y --body-file son mutuamente excluyentes. Hay un detalle fácil de equivocar: los estados de buzz issues status son open, resolved, closed y draft, mientras que patches y PRs usan merged en lugar de resolved.

Clonar y empujar sin contraseñas

El relay no tiene contraseñas ni tokens personales: tu clave Nostr firma cada petición HTTP. El puente entre git y esa firma es git-credential-nostr. Requisitos: git 2.46 o superior, porque el helper necesita la capacidad authtype del protocolo de credenciales, y una cadena de compilación de Rust si lo instalas desde el código.

cargo install --path crates/git-credential-nostr

# 1. Registrar el helper y habilitar credenciales por ruta
git config --global credential.helper nostr
git config --global credential.useHttpPath true

# 2. Guardar tu nsec en un archivo con permisos 0600
mkdir -p ~/.nostr
echo "nsec1..." > ~/.nostr/key && chmod 600 ~/.nostr/key
git config --global nostr.keyfile ~/.nostr/key

Y ya está. Usa git normalmente: git clone, git push, git fetch.

sequenceDiagram
    participant Git as git
    participant Relay as Relay de Buzz
    participant Helper as git-credential-nostr

    Git->>Relay: GET /git/owner/repo/info/refs
    Relay-->>Git: 401 con cabecera WWW-Authenticate Nostr
    Git->>Helper: detalles de la petición por stdin
    Helper->>Helper: firma un evento kind 27235, NIP-98
    Helper-->>Git: token en base64 por stdout
    Git->>Relay: reintenta con Authorization Nostr token
    Relay-->>Git: 200 y refs

En CI o en un agente sin disco persistente, usa export NOSTR_PRIVATE_KEY=nsec1... en vez del archivo: tiene precedencia sobre nostr.keyfile y evita tocar el sistema de archivos. Tabla de fallas y su causa, tomada de la documentación del helper:

ErrorCausaArreglo
no nostr key configuredNi $NOSTR_PRIVATE_KEY ni nostr.keyfile están puestosRepetir los pasos de configuración
insecure permissionsEl archivo de clave es legible por grupo u otroschmod 600 ~/.nostr/key
method hintLa cabecera WWW-Authenticate del servidor no trae methodActualizar el servidor Buzz
useHttpPathFalta credential.useHttpPathgit config --global credential.useHttpPath true
Salida vacía, sin autenticaciónLa versión de git es anterior a 2.46Actualizar git
Rechazo por clock skewEl reloj del sistema está desfasado más de 60 segundosSincronizar el reloj del sistema

Recuerda además que el 404 en clone o push casi nunca es un problema de credenciales: suele ser el repo sin buzz-channel, o tu cuenta sin membresía en el canal enlazado. La gestión de membresías del lado del servidor es tema de operador, cubierto en el curso de administrador.

Firmar commits con tu clave Nostr

git-sign-nostr implementa NIP-GS: firma commits y tags con tu clave secp256k1 usando firmas Schnorr BIP-340. La motivación es directa: tus commits llevan la misma identidad criptográfica que tus mensajes, tus revisiones y tus aprobaciones en el relay. Una identidad, una clave, en todas las superficies. Configuración del lado del usuario:

git config gpg.format x509
git config gpg.x509.program /ruta/a/git-sign-nostr
git config commit.gpgsign true
git config tag.gpgsign true
git config user.signingkey <hex-pubkey>
export NOSTR_PRIVATE_KEY=<hex-o-nsec>

git commit -m "signed with nostr"
git verify-commit HEAD

El formato elegido es x509 y no openpgp por una razón concreta: las firmas x509 usan los marcadores -----BEGIN SIGNED MESSAGE-----, que no chocan con los de PGP ni con los de SSH, así que las plataformas que intentan verificar firmas PGP no las malinterpretan.

Orden de carga de la clave, de mayor a menor prioridad: NOSTR_PRIVATE_KEY, luego BUZZ_PRIVATE_KEY, luego el archivo en la ruta de git config nostr.keyfile. Las claves pueden ser hex de 64 caracteres o bech32 NIP-19 con prefijo nsec1. El programa impone un tope de 128 bytes al valor de la variable, la limpia de memoria tras leerla y la elimina del entorno del proceso para reducir la ventana de exposición. Si usas archivo, los permisos deben ser 0600 o 0400: cualquier otra cosa produce el error keyfile ... has insecure permissions.

Atestación de dueño, para agentes

Si el que firma es un agente actuando en tu nombre, la firma puede llevar además una atestación NIP-OA que prueba qué humano lo autorizó, mediante export BUZZ_AUTH_TAG='["auth","<owner-pk>","<conditions>","<owner-sig>"]'.

BUZZ_AUTH_TAG tiene precedencia sobre la configuración nostr.authtag de git, precisamente para que los pipelines de CI y los arneses de agentes puedan inyectarla sin tocar la configuración del repositorio. Debe ser un array JSON de exactamente cuatro elementos, con "auth" en la primera posición, un dueño en hex minúscula de 64 caracteres y una firma en hex minúscula de 128 caracteres. Si la variable está puesta pero malformada, la firma falla en duro: es deliberado, para que nunca se firme sin la atestación que se pretendía adjuntar. Los datos de la atestación entran en el hash que se firma, así que quitar o modificar ese campo invalida la firma.

La verificación produce las líneas de estado que git espera, GOODSIG, VALIDSIG y TRUST_FULLY o TRUST_UNDEFINED según si la clave es la configurada localmente. La advertencia del propio NIP conviene tenerla clara: TRUST_FULLY significa solo «esta es la clave de firma configurada localmente», no es un juicio de confianza. El modelo de confianza, la web of trust y las listas de firmantes permitidos quedan explícitamente fuera del alcance de NIP-GS.

Cómo hospeda git el relay

El backend de git corre sin sistema de archivos persistente por repositorio. El contenido vive en almacenamiento de objetos como S3 o MinIO, en paquetes inmutables direccionados por contenido, y el estado actual de todas las refs se captura en un único puntero de manifiesto que se actualiza con un compare-and-swap atómico. Cada petición hidrata un árbol de trabajo efímero desde el manifiesto publicado, corre el subproceso de git correspondiente, y lo descarta al salir. La consecuencia visible: bajo pushes concurrentes al mismo repositorio gana uno solo y los demás reciben un rechazo. No es un error, es el diseño; el CAS es la única serialización de escritores y es lo que garantiza que nunca se pierda una actualización de ref.

Elegir y admitir el backend de objetos es trabajo de operador; está en el curso de administrador.

Procedencia cuando el que empuja es un agente

Cuando un agente publica eventos de git desde el arnés ACP, el CLI adjunta procedencia firmada a partir de dos variables de entorno que el arnés inyecta: BUZZ_GIT_ORIGIN_CHANNEL_ID, que si es un UUID válido agrega el tag h estándar de NIP-29 con ese canal; y BUZZ_GIT_ORIGIN_AGENT_NAME, que se usa cuando el origen es una conversación privada, en cuyo caso el evento omite deliberadamente la coordenada del canal y conserva solo el nombre visible del agente en un tag buzz-origin-agent.

Es decir: si un agente te manda un patch desde un DM contigo, el evento no filtra en qué conversación privada ocurrió, pero sí deja constancia de qué agente lo originó. Tú no configuras estas variables a mano; sirven para leer correctamente los eventos que llegan.

Qué ves en la aplicación

La superficie gráfica de proyectos existe en el escritorio y está marcada como preview en el catálogo de funciones del cliente, con la descripción «Git repository browser and collaboration» y plataforma desktop. Los paneles presentes en el código incluyen la vista y el detalle de proyecto, el panel de repositorio, el de pull requests con archivos cambiados y comentarios en línea, la tarjeta de revisión, el botón de merge, el panel de issues, el README renderizado, el detalle de commit, el grafo de contribuciones y el feed de actividad. Del lado del cliente web servido por el relay, la ruta /repos existe pero hoy redirige a la portada. Los mensajes de diff, en cambio, se ven en cualquier canal del escritorio con su tarjeta y su visor expandible, sin depender del preview de proyectos.

Sobre enlaces profundos a entidades de git, el parser del escritorio acepta hoy exactamente tres formas: buzz://repo, buzz://pr y buzz://issue, con normalización desde las URL HTTPS de clon del relay. buzz://project está descrito en docs/buzz-entity-links.md pero no está implementado —el propio documento lo lista como pendiente, a la espera de que aterrice NIP-MP—, así que un enlace de proyecto no se resuelve. Y en cualquier caso estos enlaces son internos a la aplicación: los que el sistema operativo entrega a Buzz siguen siendo solo connect, join, add-community, message y nostr-bind.

Una rutina que funciona hoy

Sin esperar a la forja completa, este es el ciclo que ya puedes armar con lo que existe:

  1. Crea el canal de la rama, con buzz channels create --name feat-pkce --type stream --visibility private.
  2. Enlaza el repo a ese canal con buzz repos bind. Eso también define quién puede clonar y empujar.
  3. Declara un workflow con trigger diff_posted en ese canal, que llame a tu CI por webhook. Ver el capítulo 11.
  4. Trabaja normal y publica cada avance con buzz messages send-diff, respondiendo en hilo al primer mensaje para que todo quede junto.
  5. Cuando el cambio esté listo, mándalo como patch formal con buzz patches send --root, y suscribe a tu agente revisor al canal para la primera pasada. Ver el capítulo 9.
  6. Cierra con buzz patches status --status merged --merge-commit <sha> y archiva el canal con buzz channels archive. Queda como el registro buscable de por qué ese código existe.

Ninguno de esos seis pasos inventa nada: todos son comandos que existen hoy.

Errores frecuentes

  • 404 en clone, fetch o push. El repo no tiene tag buzz-channel, o no eres miembro del canal enlazado. Ejecuta buzz repos bind si el repo es tuyo.
  • Diff rechazado por tamaño. El tope del contenido de diff es 61 440 bytes. Manda el archivo por buzz patches send --patch-file o parte el cambio.
  • protect set borró restricciones que tenías. Es el comportamiento documentado: reemplaza todas las reglas del patrón exacto. Vuelve a declarar todos los flags que quieras conservar en el mismo comando.
  • Confundir --diff con --patch-file. El primero recibe contenido, el segundo una ruta de archivo. Los dos aceptan - como stdin.
  • Esperar resolved en un patch. Ese estado es de issues; patches y PRs usan merged.
  • Push rechazado bajo concurrencia. Es el compare-and-swap del backend de objetos haciendo su trabajo: git pull y reintenta.

Resumen

  • Git en Buzz son tres capas separables: transporte smart HTTP estándar, metadatos NIP-34 portables y conversación en canales.
  • Los kinds son 30617 para el anuncio de repo, 30618 para el estado, 1617 para patches, 1618 y 1619 para PRs, 1621 para issues y 1630 a 1633 para estados. El kind 40008 es el mensaje diff dentro de un canal, propio de Buzz, igual que el 30621 de proyectos.
  • El tag buzz-channel es la lista de control de acceso de git: sin él, el relay devuelve 404 a todo clone, fetch y push. Se pone con buzz repos create --channel o se repara con buzz repos bind.
  • buzz messages send-diff exige --channel, --diff, --repo y --commit; el resto de sus flags son metadatos que la interfaz usa para renderizar la tarjeta y que el trigger diff_posted usa para disparar workflows.
  • buzz patches send envía la contribución formal, con --root para abrir serie y --root-revision para abrir revisión; buzz patches status publica el estado, y --merge-commit solo vale con --status merged.
  • git-credential-nostr reemplaza contraseñas por firmas NIP-98 kind 27235; requiere git 2.46 o superior, credential.helper nostr y credential.useHttpPath true. git-sign-nostr firma commits y tags por NIP-GS con formato x509, y busca la clave en NOSTR_PRIVATE_KEY, luego BUZZ_PRIVATE_KEY, luego nostr.keyfile.
  • VISION_PROJECTS.md es el documento de rumbo de la forja: el hosting de git y los eventos NIP-34 funcionan hoy, mientras que el project binding, el coordinador de merge, la capa de issues NIP-34 y la reputación por web of trust están marcados ahí como diseñados. Los enlaces profundos implementados son buzz://repo, buzz://pr y buzz://issue; buzz://project todavía no.

Siguiente: Otros clientes: Nostr de terceros y móvil