ADR 004 — Replicación nativa de Raiznet en lugar de Hypercore
Estado: Aceptado
Fecha: 2026-06
Contexto
El diseño original de Raiznet adoptaba el stack Holepunch como base de replicación: Hypercore (logs firmados de solo anexado), Hyperswarm (descubrimiento por DHT y hole punching), Autobase (multiescritor), Hyperdrive (distribución de contenido). Tres hechos cambiaron el panorama:
- Nunca se integró. El nodo de la Fase 1 es HTTP + SQLite; no existe código Hypercore en el proyecto.
- El nodo está migrando a Rust, apuntando a hardware ARM pequeño (binario estático, presupuesto de ~250 MB de RAM). No hay implementación Rust mantenida y protocol-complete de Hypercore 10/11, y no hay implementación Rust utilizable del DHT de Hyperswarm. Rastrear un protocolo móvil, definido en JS, desde un segundo lenguaje consumiría el proyecto sin beneficio de producto.
- Ningún nodo Raiznet necesita interoperar con el ecosistema JS de Hypercore. La red está hecha de nodos Raiznet; la compatibilidad con pares Holepunch no tiene caso de uso.
Lo que Raiznet de hecho exige son tres propiedades, no un stack específico: (a) datos firmados, de solo anexado y verificables; (b) descubrimiento de pares; (c) conectividad a través de NAT/CGNAT sin un gateway central obligatorio.
Decisión
Parte 1 — Datos: log de eventos firmado nativo de Raiznet. La fuente de verdad pasa a ser un log de eventos de solo anexado por autor, encadenado por hash, con cada evento firmado (Ed25519). SQLite permanece como índice derivado (ADR-002); la codificación binaria canónica permanece Protobuf cuando entre (ADR-001). Hypercore no se usa, porta ni emula.
Parte 2 — Conectividad: en capas, construida sobre una base Rust existente.
- Sync v1 — pares configurados. Pull HTTP(S) entre pares conocidos (resumen de
heads+ búsqueda por rangos de(author, seq)). Cubre LAN, VPN/Tailscale y nodos de IP pública con cero dependencias nuevas. Entra primero. - Sync v2 — transporte de marcado-por-pubkey. Construido sobre una base P2P existente en vez de escrito desde cero. Candidato principal: iroh — IDs de nodo Ed25519 (coincidiendo con el modelo de identidad de Raiznet), conexiones QUIC, hole punching integrado con relays auto-alojables y gossip por topic. Candidato de respaldo: rust-libp2p (Kademlia, mDNS, GossipSub, AutoNAT/DCUtR/Relay v2). La adopción está condicionada a un spike de campo: dos nodos estableciendo conectividad sobre enlaces reales de 4G/CGNAT rural, midiendo las tasas de conexión directa vs relay.
Justificación
- Ser dueño del formato de datos, heredar la red. El log de eventos es donde viven la soberanía y las garantías de calidad científica de Raiznet — debe ser nuestro, y es lo bastante pequeño para especificar y probar con un corpus de fixtures. La travesía de NAT, la migración de conexión y la coordinación de relays son lo opuesto: complejidad máxima, diferenciación cero, y ya resueltas por proyectos Rust mantenidos.
- Los relays no son gateways. El hole punching nunca es 100% — bajo CGNAT simétrico (común en 4G rural), todo sistema (Hyperswarm incluido) recae en relays. En este diseño, cualquier nodo alcanzable de la comunidad puede actuar como relay; el tráfico no depende de infraestructura operada por Arateki. El principio de "sin gateway obligatorio" se preserva — el mismo papel que los nodos DHT desempeñan en Hyperswarm.
- El local-first no se ve afectado. Un nodo
local_onlynunca toca el descubrimiento ni los relays; un ESP32 más un portátil en la LAN sigue siendo una Raiznet válida.
Trade-offs
- Perdemos el DHT global listo de Hyperswarm; el descubrimiento empieza más simple (pares configurados, mDNS, luego el descubrimiento del transporte v2).
- Sin interoperabilidad con pares JS de Hypercore (sin caso de uso conocido).
- iroh es pre-1.0 y su API aún se mueve; el riesgo está contenido tras la frontera del crate
raiznet-sync, con rust-libp2p como respaldo. El compromiso final con el v2 solo ocurre tras el spike de campo de CGNAT.
Consecuencias
CLAUDE.md,README.mdy estos docs ya no describen el stack Holepunch como base; los términos conceptuales que sobreviven (topics, filtros, catálogos, replicación total, semántica de solo anexado) son independientes del transporte y permanecen sin cambios.- El plan de migración Rust implementa esto en fases: log de eventos (Fase 7), sync v1 + v2 (Fase 8), codificación canónica Protobuf (Fase 9) — cada una con su propio plan detallado antes de la ejecución.
- La distribución de contenido
Material(antes Hyperdrive) se especificará más adelante, sobre las mismas primitivas de log de eventos + transferencia.