Bitácora del censo de nodos
Sobre este texto
Lo ha escrito Claude, un modelo de lenguaje, a partir de los commits, los diarios de trabajo y las mediciones de los repositorios del proyecto. El trabajo —montar el crawler, romper cada techo, medir el gasto, equivocarse— es de un humano, que ha verificado las cifras que aparecen aquí.
Un lector nos hizo ver que la versión anterior “sonaba a respuesta de un LLM”. Tenía razón. La reacción no ha sido disimularlo mejor, sino declararlo y cambiar el formato a uno donde la autoría automática no estorba: una bitácora. Aquí no hay tesis que defender ni prosa que admirar, solo entradas fechadas con lo que se observó, lo que se cambió y el número que salió.
El artículo con análisis, opinión y firma humana vendrá aparte.
Antes del registro (mayo – julio de 2026)
Punto de partida: un fork de ayeowch/bitnodes, el código detrás del
bitnodes.io original, sin mantenimiento aguas arriba.
Techo 1: ~1.390 nodos. Máquina de 2 vCPU. Un demonio Tor clavado al 100% de CPU descartaba cerca de un millón de peticiones de circuito cada diez minutos. Los snapshots oscilaban entre 55 y 1.384. Detunear el crawler estabilizó la cifra en ~1.390. Aguantó semanas.
Techo 2: ~4.170 nodos. Resize a 8 vCPU. La cifra se triplicó y se quedó quieta dos meses. Nada en los datos sugería que fuese un artefacto.
2026-07-16 — La composición delata el artefacto
De los ~4.170 nodos, 12 eran onion. El hueco frente a los ~20.000 que
reportaba otro tracker era casi todo .onion. El sampling al 25% y un único
Tor saturado venían de mitigar un incidente de mayo y nunca se habían
revisado tras el resize.
2026-07-17 — Pool de seis Tor, y el techo no se mueve
Sampling onion al 100% y seis demonios Tor. Onion: 12 → 226 en dos horas. Total: sigue en ~4.170. Cada onion ganado desplazaba un IPv4, uno a uno.
Ese 1:1 fue el dato que lo explicó todo: un snapshot solo puede contener tantos nodos como sockets simultáneamente abiertos, es decir procesos de ping × workers por proceso. 7 × 600 = 4.200, contra 4.170 observados.
Subida a 12 × 2.000 = 24.000 slots. Resultado en cinco horas: 11.600 nodos, con la población IPv4 (~6,4k) completa por primera vez.
2026-07-18 — I2P no necesita ancho de banda, necesita semillas
Tras 16 horas con soporte I2P activo: cero nodos I2P. La cola de pendientes
tenía cero direcciones .b32.i2p. Los peers de clearnet casi nunca
gossipean direcciones I2P, así que el anillo no arranca solo.
Sembradas 512 destinos de las semillas fijas de Bitcoin Core. La cola saltó a ~3.900 pendientes. Nodos alcanzables: seguían en cero.
2026-07-19 — El bug que hacía invisible a I2P
Instrumentados los intentos de conexión. El serializador de direcciones
clasificaba .b32.i2p como IPv4 porque contiene puntos, y llamaba a
inet_pton(AF_INET, …), que reventaba el handshake justo después de abrir
el stream SAM.
Cada conexión I2P moría de una forma que, desde fuera, parecía “no hay nodos
I2P”. Arreglado: addrv2 recibe sus 32 bytes y el campo version legacy una
dirección nula, como hace Bitcoin Core. Resultado: ~4.700 nodos I2P
alcanzables, una población que casi ningún tracker público cuenta.
2026-07-19 — Tor es de un solo hilo
Onion decaía durante días. ps mostraba seis Tor al 475% de CPU y parecía
saturación. El sar de día completo mostró la máquina al 36% de idle.
No saturaba: cada demonio Tor es mono-hilo y topa en un núcleo por muchos que tenga la máquina. La palanca son más demonios, no una máquina mayor. Se pasó a nueve.
2026-07-19 — Instantáneo y ventana miden cosas distintas
Añadida la unión deslizante de nodos únicos por red, que es como cuenta el tracker de referencia. En ventana de 8 días: 27.063 nodos únicos frente a los 22.232 del otro. Las conexiones Tor son transitorias, así que un snapshot instantáneo infra-cuenta onion por construcción.
2026-07-21 — El disco se llena y se lleva a I2P por delante
Disco al 94%. Convergieron tres cosas sobre un volumen de 15 GB: logs de depuración inflados a 3,5 GB, 4,6 GB de ficheros temporales de Redis huérfanos de mayo, y snapshots más grandes al añadir I2P.
i2pd cayó, y con él el bridge SAM: I2P a cero. Tor se quedó sin poder cachear el consensus: onion a 18.
2026-07-22 — Congelación silenciosa de 21,5 horas
El dashboard sirvió un snapshot de hace 21,5 horas sin que nada “fallara”: todos los procesos vivos, todas las unidades de systemd en verde.
Causa: el restart() del crawler enviaba una transacción de ~123.000
comandos Redis en un único send() de varios megabytes. Bajo carga se
atascó hasta que TCP se rindió, a los ~16 minutos, y la excepción sin capturar
mató el greenlet del planificador justo después de dejar el estado en
“arrancando”. Los 1.200 workers se pausaron educadamente para siempre.
Diagnóstico: py-spy no ve greenlets, así que hubo que inyectar código en el proceso vivo con gdb para volcar las 1.200 pilas.
Lección anotada: monitorizar la edad del último export, no la salud de los procesos.
2026-07-23 — El anti-DoS de los guards estrangula crawlers
Tras un reinicio, onion no rampaba: 17 → 21 → 0 en un día, con los demonios Tor casi ociosos. Los logs: 247.230 circuitos caducados frente a 212 completados, con “Guard is failing more circuits than usual”.
Un crawler concentra toda la creación de circuitos en uno o dos entry guards
por demonio, que es justo el patrón que la defensa anti-DoS por IP
estrangula. Sin requisitos de anonimato, el arreglo es una línea de torrc:
UseEntryGuards 0.
Con la misma carga: onion de 3 a 2.532 en dos horas, y meseta trece horas después en ~10.700-10.800 conexiones onion simultáneas. Récord previo: 3.394. Total en meseta: ~22.100 nodos.
2026-08-01 — La factura de egress es otro techo
La detección de anomalías de coste del proveedor cloud marcó la instancia: transferencia un 2.307% por encima de lo esperado, 330-350 GB/día constantes, 24/7. El perfil se leyó como “instancia comprometida usada como proxy o relay” y la máquina acabó en cuarentena. El dashboard cayó.
Falso positivo: el “malware” era el crawler. La rampa coincidía al día con el escalado de los stages anteriores.

Medición del reparto real, por deltas de bytes enviados por proceso en ventanas de 60 segundos, contrastados con la consola del router i2pd:

Más del 99% de la factura es maquinaria de overlay: construcción de
túneles I2P con un 67% de éxito, lookups de leasesets, mantenimiento de
NetDb, y del lado de Tor unas 1.000 conexiones TLS nuevas por minuto, que
son el precio directo de UseEntryGuards 0. Tránsito para terceros: cero.
El tráfico del protocolo Bitcoin es un error de redondeo.
Cada stage dejó firma en la curva de coste: 0,30 $/día antes de escalar,
1,60 $/día con el presupuesto completo de sockets, 9-15 $/día con
I2P y los nueve Tor, y 22 $/día desde el día exacto de UseEntryGuards 0.

A ~0,09 $/GB son ~660 $/mes solo de transferencia. Es un fenómeno de hyperscaler, no de “la nube”: esos mismos ~10,7 TB/mes van incluidos en un VPS europeo de 20 €, y son irrelevantes en hardware propio. Decisión tomada ese día: migrar fuera. Crawler parado mientras tanto.
2026-08-07 — Retractación: la métrica de nodos únicos estaba mal
El dashboard publicaba desde julio una estimación de “nodos únicos” que ponderaba cada dirección alcanzable 1/N, donde N era el número de tipos de red que ese nodo anunciaba. Retirada. El endpoint responde ahora 410 con el motivo.
Dos cosas estaban mal. La entrada: N salía de las claves que cachean la
respuesta a GETADDR, es decir las direcciones que un peer conoce de
otros, no las suyas. Medía la diversidad de su libreta de direcciones. Como
Bitcoin Core guarda direcciones onion e I2P pueda o no marcarlas, un nodo
solo-clearnet con libreta variada se ponderaba 1/3 o 1/4 y contaba como
fracción de máquina. La estimación se desinflaba en silencio y los números
parecían plausibles.
Y arreglar la entrada no la habría salvado: deduplicar entre redes exige
vincular la IPv4 de un nodo con su .onion o su .b32.i2p, y ese vínculo no
se publica nunca. Core se autoanuncia con una dirección de la red del peer
con el que habla. La imposibilidad de vincular es el objetivo de diseño de
Tor y de I2P.
No hay recuento deduplicado de nodos en el dashboard, y no debería haberlo en ningún otro. La cifra comparable es la de ventana deslizante, que cuenta direcciones distintas por red y no pretende contar máquinas.
2026-08-13 / 15 — Prueba A/B: MaxCircuitDirtiness
Un lector señaló que subir MaxCircuitDirtiness en Tor reduciría el churn
de handshakes. El manual lo respalda: para servicios ocultos, la caducidad
se mide desde el último uso, no el primero, así que los 10 minutos por
defecto son un temporizador de inactividad. El crawler revisita cada onion
cada 30 minutos, o sea que cada ciclo encontraba todos los circuitos
caducados.
Antes de poder probarlo hubo que arreglar algo: el crawler elegía demonio Tor al azar en cada intento, así que una revisita caía en el demonio con el circuito caliente una vez de cada nueve. Ahora cada dirección onion va siempre al mismo demonio, elegido por hash estable.
Tirada de 38 horas con los ocho demonios arrancados en frío a la vez, cuatro
tratados con MaxCircuitDirtiness 3600 y cuatro de control con el valor por
defecto. Comparación sobre 25,4 horas de meseta y 11.832 muestras:
| egress | conexiones sostenidas | bytes por conexión | |
|---|---|---|---|
| tratado | 28,65 MB/min | ~2.435 | 12.338 |
| control | 35,52 MB/min | ~5.764 | 6.462 |
| diferencia | -19,4% | -57,8% | +90,9% |
Conclusión: no subas MaxCircuitDirtiness en un crawler. El valor por
defecto de 10 minutos gana.
El -19,4% de egress no es un ahorro: el brazo tratado gastó menos porque hizo menos trabajo, no porque fuera más eficiente. Transportó un 58% menos de conexiones. Medido por unidad de trabajo, que es lo único que importa cuando se compara coste, salió al doble de caro.
El porqué: retener circuitos una hora deja unos 6.400 circuitos abiertos por demonio frente a 3.900, y esa ocupación estrangula el caudal. Cada demonio tratado transportaba menos de la mitad de flujos que uno de control.
Lo que sí quedó demostrado es el mecanismo que motivó la prueba, y es grande: por los contadores del propio Tor, un demonio tratado abrió 1.747 conexiones salientes nuevas por hora frente a 4.686 del control, un 63% menos de churn de handshakes. La idea era correcta; lo que falla es el balance a este valor.
Por eso el descarte es de este ajuste, no de la idea. Para que un circuito sobreviva a la revisita, la caducidad tiene que superar los 30 minutos del ciclo: por debajo de eso no se reutiliza nada y se vuelve al comportamiento por defecto. El hueco por explorar es estrecho y concreto —35 o 40 minutos, justo por encima del ciclo pero lejos de la hora—, a ver si conserva parte de ese 63% sin estrangular el caudal. Sin probar todavía.
Lo que queda en pie
- El número de nodos alcanzables de un tracker es una propiedad del tracker. Cada techo de esta bitácora parecía la red hasta que se rompió.
- Comparar trackers es comparar infraestructuras. Las cifras de IPv4 convergen porque IPv4 es barato de enumerar; las de onion e I2P divergen porque dependen de capacidad de circuitos, estrangulamiento de guards y disponibilidad de semillas.
- Ver los overlays tiene precio, y no se paga en bytes de Bitcoin.
- Si la cuenta se estanca, antes de concluir “esto es la red”: composición, presupuesto de sockets, estrangulamiento aguas arriba.
- Y una medida publicada solo vale lo que valga el método declarado a su lado. La entrada del 7 de agosto es la prueba.
Qué hay en el fork
- I2P como cuarto anillo — cliente SAM v3 escrito desde cero, ramas I2P
en
crawl/ping/resolve, y siembra de destinos. - El arreglo del handshake I2P — la razón real de que I2P reportara cero.
- Pool multi-Tor —
tor_proxiesmultilínea, con afinidad por dirección desde agosto. - Fiabilidad del pipeline —
restart()troceado en lugar de una transacción de varios megabytes, más un planificador supervisado.
Instancia en vivo: pesquisa.hacknodes.xyz (dashboard, API REST, servidor MCP). Código: alt-bitnodes y el fork del crawler, ambos MIT. Los snapshots en crudo y el archivo histórico están publicados.
Encantados de que alguien rebata las cifras.
alt-bitnodes es un proyecto open source independiente (MIT) mantenido por el autor. HackNodes Lab es su consultora de seguridad; el observatorio no es un producto comercial.