Modelo de amenazas
Este documento nombra a los adversarios de URnetwork y dice qué hace y qué no hace el sistema respecto de cada uno. Está escrito para ser atacado. Allí donde una propiedad es condicional, la condición está en la misma frase; allí donde algo es una intención de diseño que el código publicado todavía no impone, se etiqueta como intención de diseño, no como propiedad. Un resumen para lectores que no vayan a leer el registro completo precede a la sección 1.
La referencia es la propia explicación de Tor sobre los ataques que el encaminamiento onion no derrota: un adversario que observa ambos extremos de un circuito puede correlacionarlos por temporización, y Tor lo dice en público. URnetwork no incorpora tráfico de cobertura ni relleno, así que aquí ocurre lo mismo, y la sección 8 lo dice sin suavizarlo.
Estado de garantía, declarado una vez y aplicable a todo lo de abajo. No hay ninguna auditoría independiente del protocolo, del motor connect ni del código de servidor del operador — la capa sobre la que descansan las afirmaciones de privacidad. Dos evaluaciones de terceros de 2025 cubren otras superficies: una prueba de penetración independiente de terceros de la aplicación web y la API (25 de abril – 5 de mayo de 2025, con credenciales, manual, OWASP Top 10 más una revisión de controles ASVS), y una evaluación MASA AL2 de Leviathan Security Group sobre la app de Android (completada el 23 de mayo de 2025), que la superó y que la propia Leviathan acota como "no una evaluación de seguridad integral ni una prueba de penetración exhaustiva". Ninguna examinó los registros, la retención, la ruta de datos ni el protocolo. Toda afirmación de este documento es por tanto una afirmación sobre código fuente legible, que cualquiera puede comprobar y que nadie fuera del proyecto ha comprobado todavía en su conjunto.
Cómo leer las citas
Las afirmaciones citan path:line contra los árboles de trabajo en /Users/brien/urnetwork/{connect,sdk,server,proxy,extension} a fecha de 2026-08-07. Los números de línea se desplazan; los identificadores no. Allí donde un hallazgo se estableció en una pasada anterior de verificación de código, se cita review/verified/ARCHITECTURE.md o review/verified/PRIVACY-ENFORCEMENT.md, que llevan el mismo convenio.
Se usan tres etiquetas deliberadamente:
- Propiedad — el código la impone, y un atacante que controle a las demás partes sigue sin poder violarla.
- Disciplina — el código elige no hacer algo que es capaz de hacer. Un cambio de despliegue o un parche de una línea podría revertirlo.
- Intención de diseño — el diseño dice que este es el objetivo; el código publicado todavía no lo impone.
Resumen
Esta sección es para lectores que no vayan a leer el registro completo. Cada frase de ella está respaldada por una sección numerada de más abajo o por la declaración de garantía de más arriba.
Qué es el sistema. URnetwork es una red de privacidad en la que unos miembros retransmiten tráfico para otros. La ruta es tú → extensor → operador → proveedor → internet. El reparto de confianza es entre dos partes retransmisoras: el operador, que sabe quién eres, y el proveedor, que ve adónde va tu tráfico.
Las dos propiedades, acotadas por separado. En una ruta retransmitida el proveedor nunca recibe tu IP de origen real; ningún ajuste ni compilación de proveedor cambia eso (§2). El operador no puede leer la sesión sellada cliente-proveedor, que se publica activada por defecto en las cinco apps nativas (Android, iOS, macOS, Windows y Linux) bajo el nombre Cifrado poscuántico (§2.1). La extensión de navegador no tiene sesión sellada — su dispositivo cliente corre dentro del operador, que actúa como punto de traducción de protocolo, así que allí no puede existir un sellado (§2.4).
Los defectos de integridad: uno corregido, otro abierto. El sellado solía fallar en abierto en silencio en todos los modos. Corregido el 2026-08-10: con el Cifrado poscuántico activado, el cliente falla en cerrado y se niega a enviar o aceptar datos de aplicación en texto plano en lugar de degradarse a ellos (§2.2). Con el conmutador desactivado sigue rigiendo el antiguo comportamiento oportunista, y en ninguno de los dos modos hay app que informe de cuál de las dos cosas ocurrió. Sigue abierto: ¿puede el operador sustituir la clave de un proveedor? Hoy: sí (§7.4), así que la confidencialidad del sellado se sostiene frente a un operador pasivo y frente a un atacante en la ruta que retire el envoltorio, pero no frente a un operador dispuesto a fabricar una clave falsa. Los puntos 8 y 9 de verify/BEFORELAUNCH.md siguen la pista de ambos; §2.2 y §7.4 describen el estado actual.
Contra qué no defiende el sistema. Un adversario que observe tanto tu red de acceso como los proveedores que transportan tu tráfico puede correlacionar los dos extremos por tamaño y temporización (§8.1). Un observador pasivo global queda enteramente fuera del alcance (§8.3). URnetwork no incorpora relleno, ni tráfico de cobertura, ni mezclado; la sección 11 es la lista completa.
Garantía. No hay ninguna auditoría independiente del protocolo, del motor connect ni del código de servidor del operador; dos evaluaciones de terceros de 2025 cubren otras superficies: una prueba de penetración de la aplicación web y la API, y una evaluación MASA AL2 de Leviathan Security Group sobre la app de Android, que la superó.
Dónde vive el detalle. Las secciones 1 y 2 definen las partes, los tres modos de conexión y qué ve cada parte en cada modo. Las secciones 3 a 5 toman cada adversario por turno: un proveedor malicioso, un observador de red y el operador. Las secciones 6 y 7 cubren los proveedores gestionados por el operador, la connivencia y la sustitución de claves de proveedor. Las secciones 8 a 12 cubren la correlación de tráfico, los identificadores de dispositivo, los requerimientos legales, la lista de aquello contra lo que el sistema no defiende, y lo que este documento no pudo establecer. La sección 13 dice dónde reportar vulnerabilidades y correcciones.
1. Las partes
| Parte | Gestionada por | Posición |
|---|---|---|
| Cliente | El usuario | La instancia de la app o del SDK; en las rutas del navegador y del proxy corre en los servidores del operador (§2.4) |
| LB de entrada | El operador | nginx; estampa la dirección de cliente observada en X-UR-Forwarded-For (xops/gitops-unused/gitops-prod/urnetwork/connect/ingress.yaml:9 — el único manifiesto de entrada del árbol, y su directorio se llama gitops-unused, así que puede que no describa producción; §12) |
| Servicio connect | El operador | Uno o dos procesos en hosts distintos, unidos por una conexión de intercambio interna (server/connect/resident.go:2310-2327). No "un servidor de retransmisión" |
| API / plano de control | El operador | Autenticación, descubrimiento y clasificación de proveedores, contratos, la consulta de clave pública (server/api/api.go) |
| Proveedor | Cualquier miembro | Recibe paquetes retransmitidos y marca el destino desde su propia conexión (connect/ip.go:3932 RemoteUserNatProvider → LocalUserNat) |
| Extensor | Cualquier voluntario | El primer tramo de la ruta: un relé de reenvío TLS en una dirección independiente. Transporta la sesión cliente↔plataforma sin terminarla, así que ve la IP de conexión y no puede descifrar (connect/net_extender.go:51-55, connect/extender/extender.go:57-61) |
| Resolvedores DoH | Cloudflare, Google, Quad9, OpenDNS | La ruta de la app resuelve el DNS sobre HTTPS a través del túnel hacia uno de estos cuatro (connect/net_http_doh.go:150-155) |
| Procesadores de pago | Stripe, Apple, Google, Solana/Circle | Retienen identidad real para las cuentas de pago; el operador almacena las claves de unión (server/db_migrations.go:1712-1719, :4640, :2350-2356) |
El operador es BringYour, Inc., una sociedad de Delaware con dirección postal en San Francisco (docs/legal/ur.xyz/terms.md:16,25; docs/legal/terms.md:269-271). La sección 10 cubre qué significa eso para el proceso legal.
2. Los tres modos, y qué ve cada parte
Esta tabla es canónica y coincide con OVERVIEW.md. Todo lo demás de este documento es el mecanismo que hay detrás de ella.
| Modo | El operador ve | El proveedor ve | Valor por defecto y disponibilidad |
|---|---|---|---|
| Retransmitido sellado | conexión de cuenta/origen, asociación con proveedores, texto cifrado y tiempo/volumen | tráfico de destino, id de dispositivo/contrato, no tu IP de origen real | el valor por defecto nativo, de fábrica; solo apps nativas; ver §2.2 |
| Retransmitido estándar | conexión de cuenta/origen, asociación con proveedores, destinos interiores y bytes de los paquetes | tráfico de destino, id de dispositivo/contrato, no tu IP de origen real | las rutas del navegador y del proxy (§2.4), o el sellado desactivado; con el sellado activado, un proveedor con el que no se puede sellar se omite en vez de atenderte en este modo (§2.2) |
| Directo | menos intervención en la retransmisión | tu IP de origen real y el tráfico de destino | opcional, desactivando Anonimización fuerte (§2.3) |
De ahí se siguen dos afirmaciones asimétricas, y no son igual de fuertes:
La ceguera del proveedor a la identidad es una propiedad, y es incondicional en ambas rutas retransmitidas. El punto de entrada del proveedor toma ids, nunca una dirección: RemoteUserNatProvider.Receive está parametrizado por un TransferPath de tres ids de 16 bytes (connect/ip.go:4298-4303; connect/connect.go:45-49). Ningún protobuf de transporte lleva una dirección, ciudad o país de cliente — un grep sobre connect/protocol/*.proto buscando location|city|country|region no devuelve nada (review/verified/ARCHITECTURE.md §1.3). No hay ningún ajuste que desactive esto en una ruta retransmitida, y ninguna compilación de proveedor puede obtener la dirección que nunca se le envía.
La ceguera del operador al contenido es un valor por defecto, no un ajuste — sobre un mecanismo al que le queda un defecto. El mecanismo tiene nombre: Cifrado poscuántico, un control del panel de conexión de las apps de Android, iOS, macOS, Windows y Linux, que sella una sesión de extremo a extremo entre tu cliente y el proveedor (§2.1). Lo que es condicional ya no es si el usuario encuentra el conmutador, y desde el 2026-08-10 tampoco es si el sellado dejó de establecerse en silencio: con el conmutador activado, una conexión que no puede sellarse no transporta tráfico de aplicación en absoluto, en lugar de transportarlo en claro (§2.2). Lo que sigue siendo condicional es si el operador, que sirve las claves de proveedor contra las que se comprueba el sellado, las está sirviendo honestamente (§7.4). Lee eso antes de confiar en esta propiedad; no es visible para el usuario.
Valor por defecto en el lanzamiento: ACTIVADO. El propietario del producto dictaminó el 2026-08-07 que la sesión sellada se publica habilitada, así que la ceguera del operador es el valor por defecto y no una elección que el usuario tenga que hacer. Este documento está escrito contra ese estado de lanzamiento; en el momento de escribirlo el código sigue viniendo desactivado por defecto (punto 7 de verify/BEFORELAUNCH.md).
Esa brecha importa ahora más en una dirección que antes. Como la compuerta de fallo en cerrado está atada a ese mismo conmutador, activar el valor por defecto propaga ahora una garantía real a todos los usuarios en lugar de limitarse a ampliar el alcance de un fallo silencioso — lo contrario de lo que advertía esta sección cuando el sellado podía fallar en abierto en todos los modos. Lo que el valor por defecto sigue sin poder arreglar es §7.4, que es una propiedad del sellado en sí: un sellado activado por defecto en el que el operador puede interponerse como máquina en medio solo protege frente a un operador pasivo.
La extensión de navegador no tiene sesión sellada en absoluto (extension/src/utils/auth-params.ts:33, performance_profile: null). Eso es alcance, no un valor por defecto, y el dictamen de lanzamiento no lo cambia.
Allí donde el sellado está ausente — los endpoints del navegador y del proxy (§2.4), y las apps nativas con el sellado desactivado (§2.2) — el operador termina el TLS/QUIC del cliente y retransmite paquetes IP en texto plano (connect/protocol/ip.proto:9-16). Elige analizar solo la cabecera de encaminamiento — FilteredTransferFrame no contiene más que la ruta de transferencia (connect/protocol/transfer.proto:85-87; connect/transfer.go:1500-1509) —, lo cual es disciplina, no una barrera criptográfica.
2.1 Qué es el sellado, con precisión
Una sesión TLS 1.3 directamente entre cliente y proveedor, transportada como tramas de control ordinarias a través del mismo operador (connect/transfer_encrypt.go:44-51). El intercambio de claves es el híbrido X25519MLKEM768, con X25519 convencional como respaldo (connect/transfer_encrypt.go:373-381,406); el AEAD es AES-256-GCM sobre una clave exportada de 32 bytes (:285-297); las identidades son Ed25519 (:892-897), así que "poscuántico" se acota solo al intercambio de claves. El TLS mutuo es obligatorio y el certificado del par se comprueba en la capa de secuencia contra el compromiso del contrato en lugar de por la pila TLS (:388-406).
2.2 Falla en cerrado cuando lo pides, en abierto cuando no
Actualizado el 2026-08-10. Esta sección decía antes que el sellado falla en abierto en silencio y que no había opción de fallo en cerrado. Eso ya no es cierto, y el cambio es el endurecimiento más significativo de la historia de este documento — cerró el vector de degradación en ambas direcciones. Lo que sigue es el comportamiento actual, leído del código fuente.
El cifrado sigue siendo una propiedad binaria de Cipher() != nil, y un cifrador nulo sigue significando que el handshake no se completó: un handshake fallido, una prueba de identidad fallida o un contrato que nunca llevó la clave pública del par lo dejan nulo. El fallo de la prueba de identidad sigue siendo no fatal en la capa de sesión — la sesión "queda sin autenticar" (connect/transfer_encrypt.go:1977) en lugar de desmontarse.
Lo que cambió es lo que ocurre después. Ahora hay tres modos (connect/transfer_encrypt.go, EncryptionMode):
| Modo | Comportamiento |
|---|---|
EncryptionModeOff | El valor cero. Capa de sesión inerte, todo en texto plano. |
EncryptionModeOpportunistic | Sella en cuanto se establece una sesión, texto plano hasta entonces — o permanentemente si nunca llega a establecerse. El comportamiento histórico. |
EncryptionModeRequired | Nunca expone datos de aplicación en texto plano hacia o desde un par para el que se espera una sesión. |
Bajo EncryptionModeRequired la garantía se hace cumplir en cuatro puntos:
- Compuerta de entrada al envío (
connect/transfer.go:2796). Un Pack de aplicación espera al cifrador dentro del tiempo límite de quien llama y luego se rechaza, sin enviar — nunca se degrada — con unErrEncryptionRequiredNotEstablishedtipado. - Red de seguridad del envío (
connect/transfer.go:4125). Una trama que llega al escritor sin cifrador se rechaza en lugar de escribirse, lo que cubre la estrecha carrera en la que una sesión se desmonta entre el encolado y la escritura. - Compuerta de recepción (
connect/transfer.go:5844). Una trama de aplicación en texto plano procedente de un par para el que se espera una sesión se descarta y se audita — esta es la mitad que más importa, porque cierra la degradación en la que un atacante retira el envoltorio y el receptor aceptaría el texto plano en otro caso. La trama se acusa y se descarta en lugar de quedar sin acuse, ya que retener el acuse abriría un hueco en la secuencia ordenada y atascaría ambos lados. - Prefiltro de candidatos (
connect/ip_remote_multi_client.go,EncryptionCapabilityPrefilter). Un candidato de ventana del que la API de claves fuera de banda de la plataforma dice que nunca ha publicado una clave de identidad se hace fallar de inmediato, ya que nunca podrá completar el handshake. Solo acelera un fallo seguro; nunca admite a un candidato.
Las tramas de control del handshake, los acuses y los pares del plano de control están exentos por diseño — la compuerta cubre la carga útil de aplicación, no el andamiaje que la arranca.
Qué modo te toca lo decide el conmutador de Cifrado poscuántico. Cuando está activado, el cliente consumidor corre EncryptionModeRequired (connect/ip_remote_multi_client.go:9186-9197): un proveedor que no pueda establecer una sesión no transporta para ti tráfico de aplicación en absoluto, en lugar de transportarlo en claro. El coste declarado es la disponibilidad, aceptado deliberadamente. Los proveedores corren EncryptionModeOpportunistic (sdk/device_local_provider.go:98) para que un mismo proveedor pueda servir a consumidores sellados y no sellados; esa es una decisión de compatibilidad del lado respondedor y no debilita la garantía del iniciador.
Qué significa esto para el conmutador. Con el Cifrado poscuántico activado, el sellado ya no falla en abierto: falla en cerrado, a voces, tanto al enviar como al recibir. Con el Cifrado poscuántico desactivado, el comportamiento oportunista descrito en versiones anteriores de esta sección sigue aplicándose por completo — el tráfico puede fluir en texto plano si una sesión nunca se establece, y nada se lo dice al usuario. Así que la afirmación honesta es condicional, y el valor por defecto importa: ver la nota Valor por defecto en el lanzamiento de §2.
El comportamiento está cubierto por pruebas, no solo por comentarios — TestRequiredEncryptionFailsClosedAgainstPlaintextPeer, TestRequiredGateNonBlockingSendRefusesPreCipher, TestRequiredGateBoundedBudgetRefusesUnsent, TestRequiredSendRefusalTypedErrorAndEvent y TestRequiredContractFreeWithoutKeySourceFailsClosed.
Un comentario obsoleto que ignorar al auditar: la cabecera de completeHandshake en connect/transfer_encrypt.go:1650-1654 sigue afirmando sin matices que "el tráfico posterior fluye en texto plano". Eso solo es cierto en los modos Off y Opportunistic; las compuertas de arriba lo anulan bajo Required. El comentario es anterior a la corrección.
Sigue abierto: el usuario no puede ver qué modo le tocó a una conexión. No hay ningún indicador por conexión de sellado-o-no en ninguna app. Las únicas señales son líneas de log de dispositivo — peer identity proof verified — cipher is now usable (connect/transfer_encrypt.go:1954) en caso de éxito, un Errorf en caso de fallo, y un evento NotifyRequiredSendBlocked cuando la compuerta rechaza. El SDK expone un hook de cambio, así que lo que falta es interfaz, no fontanería.
2.3 Modo directo
Desactivar Anonimización fuerte establece AllowDirect, cuyo propio comentario de campo dice: // setting this to true exposes the real source IP to the provider (sdk/sdk.go:710-711). El flujo se fuerza a un stream P2P (connect/ip_remote_multi_client_probe.go:1176-1178) sobre un canal de datos WebRTC/ICE (connect/transport_p2p_webrtc.go:986), e ICE significa que ambos extremos aprenden la dirección del otro. El valor por defecto es desactivado — un perfil de rendimiento nulo da falso (connect/ip_remote_multi_client.go:1861-1863; sdk/local_state.go:607-616).
Dos casos forzados: los pares de la misma red (tus propios dispositivos) siempre permiten el directo (connect/ip_remote_multi_client.go:1822-1825), y los dispositivos alojados lo fuerzan a desactivado (sdk/device_local.go:1516-1533). Fíjate en que el modo directo retira al operador solo de la ruta de datos. Los contratos, la selección de proveedores y los mensajes de control siguen pasando por la plataforma, así que el operador sigue sabiendo qué proveedor usaste y cuántos bytes se movieron.
2.4 La ruta que la tabla no cubre: el navegador y los endpoints proxy
En la extensión de navegador, la app web y los endpoints HTTPS/SOCKS5/WireGuard, el operador ejecuta el cliente. La extensión aprovisiona un dispositivo proxy del lado del servidor (extension/src/utils/auth-params.ts:22-36, enable_socks/enable_http) y se apunta el navegador al endpoint proxy del operador — HTTPS CONNECT por defecto (extension/src/bridge/background.ts:220; extension/src/utils/proxy-manager.ts:264). La instancia del SDK que abre contratos y sostiene la ventana de proveedores corre en server/proxy, no en la máquina del usuario.
Por eso no puede existir ninguna sesión sellada en estas plataformas, como cuestión de arquitectura y no de esfuerzo de ingeniería aún no invertido. La sesión sellada es cliente↔proveedor, y su garantía depende de que el extremo cliente viva en el propio dispositivo del usuario. Un navegador o un cliente proxy corriente habla su propio protocolo (HTTPS CONNECT, SOCKS5, WireGuard) y no puede ejecutar el motor de la red, así que el operador ejecuta el dispositivo del cliente de forma remota y actúa como punto de traducción de protocolo entre los dos. Un sellado negociado desde ese dispositivo remoto empezaría dentro del operador — la parte a la que el sellado existe para cegar. La implementación aceptó este intercambio deliberadamente: estas superficies existen por comodidad en plataformas y protocolos que no pueden alojar el cliente completo, y la documentación no debe describirlas como selladas.
Consecuencias, dichas llanamente:
- La ceguera del proveedor a la identidad sigue sosteniéndose — el proveedor sigue recibiendo solo ids.
- La ceguera del operador no existe en esta ruta de ninguna forma. El operador termina la conexión proxy, recibe el host de destino desde CONNECT, y retiene las claves del cliente.
- El dispositivo alojado no instala ningún mux de mejora de DNS (
server/proxy/proxy_device.go:556,SetUpgradeMuxSettings(nil)), así que el DoH dentro del túnel de la ruta de la app (§3.2) no aplica aquí.
Lo que hace las veces del cifrado en esta ruta es la disciplina de almacenamiento: la ruta de datos del proxy no registra nada en las rutas que el cliente puede dirigir, y un test de regresión lo fija — proxy/socks5_nolog_test.go, TestClientDrivenTrafficNeverLogs, comprueba cero líneas de log en los casos malformado, sobredimensionado y no marcable. Dos salvedades honestas: el motivo declarado del test es la denegación de servicio por amplificación de logs y no la privacidad, y el manejador de recuperación de pánico sí registra una dirección de cliente (proxy/socks5_server.go:128-131). Ver review/verified/PRIVACY-ENFORCEMENT.md §2.2.
3. Adversario: un proveedor malicioso
Dentro del alcance, y asumido. Cualquiera puede proveer. No hay atestación, garantía depositada, comprobación de identidad ni resistencia Sybil en la participación como proveedor — una búsqueda en los árboles de server, connect y sdk de sybil|kyc|attestation no devuelve nada relevante. Un proveedor ejecuta código abierto en hardware que controla, así que asume que ejecuta una compilación modificada.
Qué ve. Todo lo que ve un ISP respecto del tráfico que transporta: IP y puerto de destino, tamaños y temporización de los paquetes, y el SNI de TLS allí donde el cliente lo envía en claro. Recibe paquetes IP en crudo y los marca él mismo (connect/protocol/ip.proto:9-16; connect/ip.go LocalUserNat). También ve el client_id transportado como el SourceId del contrato (connect/protocol/transfer.pb.go:1779; server/controller/connect_controller.go:461-463) — un identificador por plaza de ventana que solo el operador puede resolver a una cuenta, y nunca el device_id, que no tiene campo en el cable. Su vida útil, y las varias cosas que sí persisten entre sesiones, están en §9.
Qué puede hacer.
- HTTP en texto plano: leerlo y modificarlo. El puerto 80 se pasa a la salida sin cambios por defecto —
HttpUpgradeUnencryptedes el modo por defecto (connect/ip_mux_upgrade.go:38-47). Un proveedor está en la misma posición que un punto de acceso Wi-Fi hostil para cualquier tráfico que no vaya cifrado por sí mismo. HTTPS protege el contenido de la aplicación frente al proveedor; nada en URnetwork añade una segunda capa sobre el propio TLS del destino. - Descartar, retrasar o atascar tráfico de forma selectiva. Un proveedor que acusa recibo de tráfico pero no devuelve ninguno se etiqueta como agujero negro y se retira (
connect/ip_remote_multi_client.go:37-54), así que la denegación se detecta y se rodea — pero la detección es estadística, y el tráfico que vio antes de ser retirado ya lo había visto. - Mentir sobre el rendimiento para atraer tráfico. La clasificación usa la latencia y el rendimiento medidos (
server/model/network_client_location_model.go:2430-2447), y la medición es del operador, no un autoinforme del proveedor, así que esto está acotado — pero es una entrada de clasificación, no un control de integridad. - Correlacionar flujos dentro de su propia vista. La afinidad por sitio fija un sitio a un proveedor (
connect/ip_remote_multi_client.go:1234-1240), así que un solo proveedor ve una porción coherente de la navegación de un cliente para los sitios que sostiene.
Qué no puede hacer sin ayuda. Averiguar tu IP real en una ruta retransmitida, averiguar tu cuenta o tu correo, o atribuir un client_id a una persona. Eso requiere al operador (§5).
3.1 Qué restringe y qué no restringe ip_security
La capa ip_security del motor connect corre en la propia salida del proveedor y en el propio dispositivo del usuario, nunca de forma central en el operador — los campos IngressSecurityPolicyGenerator/EgressSecurityPolicyGenerator del operador existen pero nunca se asignan ni se leen (server/connect/resident.go:263-264; review/verified/PRIVACY-ENFORCEMENT.md §4.1). Bloquea firmas de BitTorrent y de intercambio de archivos, protocolos opacos no estándar, destinos listados por reputación y tráfico con patrón de ataque, y lo hace sobre una base deliberadamente delgada: la 5-tupla más un prefijo de carga útil acotado a 8 paquetes / 512 bytes.
Las tablas de reputación son generadas, no mantenidas a mano. connect/security y connect/blocker agregan feeds públicos de inteligencia sobre amenazas — el Feodo Tracker de botnets C2 de abuse.ch, Spamhaus DROP y DROPv6, las IPs comprometidas de Emerging Threats, Blocklist.de, CINS Army, TweetFeed, ViriBack, BruteForceBlocker — en tablas de rangos empaquetadas (46.789 rangos IPv4 a fecha de la instantánea de 2026-08-05; la cabecera de connect/ip_security_cfaa_block.go lleva la lista de feeds y la atribución). El pipeline de publicación regenera ambas tablas desde los feeds en vivo (build/all/run.sh, el paso CONNECT_IP_UPDATE), así que cada versión incorpora una instantánea actual y un cliente al día bloquea el espacio de direcciones listado más reciente. La instantánea envejece con la compilación instalada; las listas se actualizan por versión, no por el aire.
La cota del prefijo de carga útil está en connect/ip_security_dmca.go:117-119, con el nombre del servidor borrado de la clave de flujo (:366,386,424) y los contadores llevados por (version, protocol, port) con el campo de IP nunca habilitado (connect/ip_security.go:554,604-624).
Es un control de seguridad para proveedores honestos, no un control de seguridad contra los hostiles. Restringe qué sacará hacia fuera la conexión de un proveedor. No hace nada respecto de lo que un proveedor haga con el tráfico que sí transporta, corre en un proceso que el proveedor posee, y una compilación modificada puede desactivarlo. No lo leas como un límite a un proveedor malicioso.
Un detalle de reporte pertenece aquí porque es un canal de metadatos. Una coincidencia de firma de BitTorrent devuelve Incident, que llama a ReportAbuse (connect/ip.go:4481-4487), transmitiendo una trama de control PeerAudit al operador que lleva el id de dispositivo del par y un booleano — sin destino, sin dominio, sin contenidos (connect/transfer.go:870-876,6653-6683). El operador no tiene hoy manejador para ese tipo de mensaje, así que no se almacena nada al recibirlo (server/controller/connect_controller.go:236-250; un grep de todo el repositorio buscando PeerAudit en server devuelve cero resultados). Eso es una disciplina a un case de distancia de cambiar. Los descartes de tráfico opaco cifrado son silenciosos y sin informe (connect/ip_security_dmca.go:483-486).
3.2 DNS
En la ruta de la app y del SDK, el DNS plano sobre UDP/TCP 53 se intercepta y se resuelve por DoH a través del túnel (connect/ip_mux_upgrade.go:120-148, instalado por defecto en sdk/device_local.go:1120), hacia Cloudflare, Google, Quad9 u OpenDNS (connect/net_http_doh.go:150-155). Un proveedor ve por tanto una conexión HTTPS hacia un resolvedor público, no la consulta. Dos límites honestos:
- Una ventana de fuga de DNS en el arranque está documentada en el código fuente. Mientras el DoH del túnel todavía se está estableciendo, una consulta compite contra un resolvedor sobre la salida del host local, "a costa de una breve fuga de DNS durante el arranque" (
connect/ip_mux_upgrade.go:120-126,78-92). Tu red local y tu ISP pueden ver esas consultas. - El endpoint WireGuard entrega al cliente un resolvedor público fijo,
DNS = 1.1.1.1(server/model/network_client_proxy_model.go:760), y el puerto 53 se pasa sin inspeccionar por la política de seguridad (connect/ip_security_cfaa.go:113). En esa ruta las consultas DNS atraviesan al proveedor como DNS plano, y el proveedor puede leerlas y responderlas.
Los cuatro resolvedores DoH son terceros que ven consultas llegando desde la dirección de salida de un proveedor, sin vincular a tu cuenta. Esa es una dependencia real y no es configurable a cero: alguien tiene que responder el DNS.
4. Adversario: un observador de red
Cuatro posiciones, de la más débil a la más fuerte.
Tu red local y tu ISP. Ven que te conectas a connect.bringyour.com sobre TLS/QUIC, o a un extensor, más tamaños y temporizaciones. No ven los destinos dentro del túnel, salvo en la ventana de DNS de arranque documentada más arriba. Allí donde la plataforma está bloqueada, los extensores se presentan como servicios ordinarios en puertos verosímiles y los clientes rotan identidades con fragmentación aleatoria (connect/net_extender_profiles.go:14-30,50-57), y existe un transporte con forma de DNS para redes donde solo escapa el DNS (connect/transport_pt.go:18-45). Estos son mecanismos para atravesar cortafuegos locales y regionales, clasificados por debajo de los transportes directos (connect/transport.go:541-546). No son defensas frente al análisis de tráfico y nunca deberían describirse como tales.
Un operador de extensor. Cualquiera puede ejecutar uno, la app acepta una IP introducida a mano, y por defecto no se exige firma (connect/extender/extender.go:57-61). Un extensor es el par TCP, así que ve la IP del usuario que se conecta, y no puede descifrar lo que reenvía, porque la sesión TLS lo atraviesa hasta la plataforma en lugar de terminar en él (connect/net_extender.go:51-55). El tramo del extensor pone por tanto en la ruta a un observador de primer salto de tu dirección que no es el operador. Ese es el intercambio que hace el diseño para sacarte de un bloqueo, y merece la pena ser exacto sobre su tamaño: el tramo del extensor no añade cifrado ni anonimato — aprende lo que tu ISP ya sabe y nada más. En el cliente publicado los transportes directos compiten primero y los marcadores de extensor se expanden cuando aquellos fallan (connect/net_http.go:675); un cliente configurado con extensores personalizados los usa como su única ruta (connect/net_http.go:357,469).
Un observador de un solo extremo. Vigilar solo el lado del cliente da "este usuario está conectado a URnetwork" y un perfil de bytes/temporización. Vigilar solo la salida de un proveedor da destinos y un perfil de bytes/temporización sin identidad adjunta.
Un observador de ambos extremos. Ver §8. Este es el adversario al que URnetwork no derrota.
5. Adversario: el operador
El operador autentica a los clientes, selecciona y clasifica a los proveedores, redacta los contratos, distribuye las claves públicas y transporta los paquetes en las rutas retransmitidas. Asume en esta sección que es hostil o que está obligado.
Qué ve, sin ningún esfuerzo adicional: tu cuenta; tu dirección en el momento en que te conectas; una ciudad/región/país derivados de esa dirección por una consulta a una base de datos local — server/ip.go:201 abre desde disco un mmdb/ip-ipinfo.mmdb empaquetado e ip.go no hace ninguna llamada de red de ningún tipo, así que una dirección nunca se entrega a un servicio de geolocalización — persistidos por conexión (server/model/network_client_location_model.go:1392-1414); qué proveedores te sirvieron; recuentos de bytes; y en la ruta estándar, las direcciones de destino y los bytes de los paquetes dentro del túnel — aunque esos normalmente ya van dentro del propio HTTPS del destino.
Qué almacena. Los registros de conexión y de autenticación guardan un hash unidireccional con clave del bloque /29 (IPv4) o /56 (IPv6) circundante en lugar de la dirección (server/ip.go:46-67). Una precisión que importa a un auditor: es un único pepper de ámbito de proceso obtenido del vault, memoizado una vez, sin sal por fila y sin ningún mecanismo de rotación en todo el repositorio (server/ip.go:40-44); el espacio de claves IPv4 es de 2^29 bloques, así que cualquiera que tenga el pepper puede invertirlo por fuerza bruta. El secreto del pepper es toda la propiedad de seguridad. El subsistema /verify usa /48 para IPv6, no /56 (server/ip.go:74-85; server/model/verify_model.go:111). El puerto de origen se almacena en claro junto al hash (server/db_migrations.go:1934). La fila de auditoría de creación de cuenta registra el hash con pepper y el puerto, nunca el ip:port en crudo (server/model/network_model.go:961-975), y las filas de auditoría se retiran a los 180 días (server/model/audit_model.go:984, programado en server/taskworker/taskworker.go:57,253).
Qué no existe para poder ser almacenado. No aparece ningún destino, host, URL, SNI, puerto ni dominio en ninguna parte del registro de transferencias — un grep del archivo de migraciones completo buscando esos términos devuelve cero resultados (review/verified/PRIVACY-ENFORCEMENT.md §1.2). El registro HTTP pasa por una lista de permitidos de exactamente cinco cabeceras (server/http_log.go:15-29), fijada por test. Ninguna métrica de Prometheus lleva una IP, host, destino, puerto ni id de cliente; de 39 métricas declaradas solo tres están etiquetadas y cada etiqueta es un enum acotado (review/verified/PRIVACY-ENFORCEMENT.md §2.5). Las subidas de logs de la pestaña de soporte se vuelcan a io.Discard (server/controller/log_file_controller.go:13-16,76); solo persisten los metadatos.
5.1 Cuánto tiempo se conserva cada cosa, y adónde va
La retención la imponen barridos programados en server/taskworker, no una política. Las ventanas de abajo se leen de las constantes a las que llaman esos barridos, y son más cortas de lo que la política de privacidad — que no declara periodo de retención alguno — haría esperar a un lector.
| Datos | Se conservan | Se impone en |
|---|---|---|
| Las filas de conexión, y la ciudad/región/país, latencia y velocidad por conexión que cuelgan de ellas | 8 horas | taskworker/work/network_client_work.go:84; el borrado elimina en cascada network_client_location en la misma sentencia (model/network_client_model.go:2295-2320) |
| Un cliente de nivel superior inactivo (sin autenticación, sin conexión) → se desactiva | 30 días | TopLevelClientIdleExpiration (model/network_client_model.go:2289) |
| Un cliente desactivado → borrado en duro, con cascadas | +30 días | NetworkClientReapAfterDeactivate (:2269) |
| Así que un dispositivo sin usar, de extremo a extremo | ~60 días | los dos plazos encadenados |
| Contratos completados | 7 días | taskworker/work/subscription_work.go:161 |
| Filas de auditoría | 180 días | model/audit_model.go:984 |
Dos notas que un auditor debería tener. La ventana de inactividad se estrechó de 90 días a 30 el 2026-07-18; la constante lleva su propia justificación, y un comentario obsoleto en taskworker/work/network_client_work.go:83 todavía dice 90, que es la fuente más probable de esa cifra si te la encuentras en otra parte. Y RemoveLocationLookupResults se programa en cada ciclo pero es un no-op — su cuerpo y su llamada al modelo están ambos comentados y no existe tal tabla. Es un stub vestigial, no datos sin barrer; la ubicación por conexión se borra con la fila de conexión de arriba.
La geolocalización nunca sale de la máquina. La consulta de ciudad/región/país lee un mmdb/ip-ipinfo.mmdb empaquetado desde el disco local (server/ip.go:201), e ip.go no contiene ningún cliente HTTP ni hace ninguna llamada de red. Ninguna dirección se envía a un servicio de geolocalización, y ninguna información personal se comparte con ningún tercero. Los únicos terceros de la tabla de partes de este documento retienen datos que el usuario les entrega directamente al elegir esa vía: los resolvedores DoH a los que llegan las consultas DNS del propio usuario a través del túnel, y los procesadores de pago con los que un usuario se da de alta para que se le facture.
Huecos conocidos en la postura de "no registramos tu dirección", no gobernados por la verbosidad. server/http.go:415,455 construyen http.Server sin ErrorLog, así que la biblioteca estándar de Go escribe http: TLS handshake error from al stderr de producción en cada conexión a medio abrir — exactamente el agujero que proxy/http.go:176,250 cierra con ErrorLog: discardLog. El SNI suministrado por el cliente se registra a nivel ERROR (server/tls.go:98,195), una dirección de llamante en crudo en server/proxy/proxy_device.go:274, y la identidad del par WireGuard en server/proxy/server.go:699. Hasta que se corrijan, "nunca registramos tu IP" no es una afirmación que URnetwork pueda hacer, y este documento no la hace. Detalle completo: review/verified/PRIVACY-ENFORCEMENT.md §2.4.
Qué controla el operador que es fácil pasar por alto. El descubrimiento de proveedores es enteramente del lado del operador: FindProviders2 es el único endpoint vivo (server/api/api.go:67), la plataforma puntúa y clasifica a los candidatos (server/model/network_client_location_model.go:2250-2254,2430-2447), y el cliente toma el conjunto clasificado que se le da. El único control del lado del consumidor es una lista de bloqueo de ubicaciones (server/api/api.go:73-75). Un cliente no tiene forma de verificar que los proveedores que se le ofrecieron son independientes entre sí o del operador.
6. Proveedores gestionados por el operador, y connivencia operador–proveedor
6.1 ¿Puede el operador ejecutar proveedores?
Sí, y nada en el sistema lo marca ni lo impide. Proveer está abierto a cualquiera sin atestación ni garantía depositada, y no hay ningún concepto de proveedor propio, oficial o de titularidad del operador en ninguna parte del código — las búsquedas en server, connect y sdk de tal noción no devuelven nada. Un proveedor gestionado por el operador sería indistinguible del de un miembro.
Combinado con el punto de §5 de que el operador clasifica y devuelve el conjunto de proveedores, esto significa que el operador puede, en principio, colocar sus propios proveedores en la ventana de un usuario. No hay comprobación de diversidad del lado del cliente, ni atestación de independencia, ni medición publicada de la flota por una parte externa. Los contrapesos que existen son reales pero parciales: la ventana sostiene varios proveedores a la vez — un conjunto de calidad de 2–6 (tope duro 12) y un conjunto de velocidad de 1–2 (tope duro 4), ambos vivos en el perfil por defecto (connect/ip_remote_multi_client.go:138-158) — y los proveedores se retiran ante estadísticas insanas, detección de agujero negro, fallo de ping y rotación de vida útil por canal (:37-54, :610-616). Esos acotan cuánto ve cualquier proveedor individual. No acotan un conjunto de proveedores elegido por una sola parte.
6.2 Qué reconstruyen juntos operador y proveedor, por modo
Asume que el operador y uno o más proveedores de tu ventana comparten lo que retiene cada uno.
| Retransmitido estándar | Retransmitido sellado | Directo | |
|---|---|---|---|
| Tu identidad (cuenta, correo/cartera/pago) | operador | operador | operador |
| Tu IP real en el momento de conectar | operador | operador | operador, y el proveedor directamente |
| Destinos que visitaste | operador (paquetes interiores) y proveedor | solo el proveedor | proveedor |
| Vínculo entre los dos | trivial — una parte ya retiene ambos | el contrato: audit_contract_event empareja la identidad del cliente y la del proveedor por contrato (server/db_migrations.go:234-251) | trivial |
| Resultado | atribución completa de la navegación para lo que transportaran los proveedores en connivencia | atribución completa de la navegación para lo que transportaran los proveedores en connivencia | atribución completa de la navegación, más el proveedor conoce tu dirección sin ayuda |
La conclusión honesta: la sesión sellada no defiende contra la connivencia operador–proveedor. Elimina la capacidad independiente del operador de leer tu tráfico; no impide que un proveedor que ya ve tus destinos se los entregue a un operador que ya sabe quién eres. El registro del contrato que une a los dos no es una fuga — es la contabilidad sobre la que funciona la red.
Lo que el sellado sí compra frente a este adversario es un límite de alcance: con él activado, la vista propia del operador no contiene destinos, así que la connivencia exige la cooperación de los proveedores concretos que transportaron el tráfico concreto, en el momento en que se transportó, en lugar de una consulta contra registros que el operador retiene por sí solo. Esa es una diferencia significativa en esfuerzo y en lo que la divulgación obligada puede alcanzar de forma retroactiva (§10). No es inmunidad, y ninguna disposición de tres partes en la que una seleccione a las otras proporciona inmunidad.
La respuesta estructural del diseño, que no está desplegada. El protocolo del cable y el cliente admiten encadenar proveedores intermediarios adicionales — MaxMultihopLength = 8 (connect/connect.go:13,214-233), IntermediaryIds en CreateContract (connect/protocol/transfer.pb.go:1516), aceptación del cliente en connect/ip_remote_multi_client_api.go:296-312, fontanería de servidor en server/controller/connect_controller.go:560. El endpoint de descubrimiento vivo nunca puebla el campo — FindProvidersProvider se construye en exactamente dos sitios y ninguno lo establece (server/model/network_client_location_model.go:3186-3190,3296-3303) — así que la red publicada asigna cadenas de longitud uno. El encadenamiento de proveedores es una capacidad del protocolo, no una propiedad del despliegue, y citar el número 8 como recuento de saltos sería engañoso.
7. Claves de identidad de proveedor, y si el operador puede sustituir una
Esta es la sección que más merece la pena atacar, porque la respuesta es incómoda y el código fuente lo dice él mismo.
7.1 Emisión y vinculación
Cada connect.Client — un cliente proveedor, y cada uno de los clientes de ventana del usuario — sostiene un par de claves Ed25519 generado en el proceso por ClientKeyManager (connect/transfer_key.go:76-100, la única llamada a ed25519.GenerateKey de los tres árboles). Es de vida larga allí donde se persiste y se recarga una semilla, que es el cliente proveedor, y fresca por proceso allí donde no la hay, que son los clientes de ventana; §9 cubre la diferencia y por qué importa. La mitad privada nunca sale del proceso; una semilla de tamaño incorrecto es un error duro de construcción en lugar de una identidad fresca y silenciosa (:84-94). La mitad pública se publica a la plataforma en un mensaje de control ClientKey (connect/protocol/transfer.proto:501-513) y se almacena del lado del servidor en Redis en ckey_ sin caducidad — Redis es la fuente de verdad, no hay tabla SQL (server/model/network_client_key_model.go:14-59).
La vinculación es por tanto client_id → public key, y el operador emite el client_id y retiene el mapeo. No hay ancla externa: los ids no se derivan de las claves, y no hay registro de transparencia ni DHT. Ambas alternativas están recogidas en el diseño como consideradas y aplazadas (connect/DESIGNNOTES.md §3.7, "Deferred alternatives").
7.2 Rotación y revocación
Volver a publicar sobrescribe — el propio comentario de SetClientKey dice "llaveado por client_id (la rotación sobrescribe)" (server/controller/connect_controller.go:810-813). La entrada se elimina cuando el id de cliente se purga (network_client_key_model.go:61-71, llamado desde RemoveDisconnectedNetworkClients). No hay rotación programada, ni caducidad, ni lista de revocación. Un cliente proveedor persiste su material de claves y lo recarga, así que su identidad es estable entre reinicios (sdk/device_local.go:2874-2889,2926-2935; sdk/local_state.go:418-461) — y, en Android, deliberadamente estable también a través de un borrado por cierre de sesión automático (§9.2). Un cliente sin semilla persistida genera una identidad fresca en cada arranque de proceso, que es lo que hacen los clientes de ventana del usuario. Los cambios de clave a mitad de sesión se rechazan: SetPeerClientPublicKey es de gana-la-primera-escritura, y una clave posterior distinta se registra y se ignora (connect/transfer_encrypt.go:1787-1816).
7.3 Verificación, y las dos defensas
- Defensa 1, vinculación por certificado firmado. El proveedor firma su cadena de certificados TLS efímera con su clave Ed25519 y publica ambas (
connect/protocol/transfer.proto:486-498). La plataforma adjunta la cadena, la firma y la clave pública del proveedor a cada contrato que nombre a ese proveedor (:340-368,:388-406). El cliente admite la cadena solo si la firma verifica bajo la clave pública del proveedor, y después comprueba el certificado presentado en el handshake contra la cadena admitida (connect/transfer.go:3930-3934,4006-4018). - Defensa 2, prueba de identidad dentro del handshake. Tras el handshake TLS cada lado firma la salida del exportador de la RFC 5705 bajo la etiqueta
urnetwork-sequence-identity-proofy la envía como unEncryptedControl(connect/protocol/transfer.proto:460-476). El AEAD se retiene fuera de la ruta de envoltura hasta que la prueba del par verifica (connect/transfer_encrypt.go:1492-1498,:1719-1756), así que un MITM que termine el TLS en un tramo y rehaga el handshake en el otro produce exportadores discordantes y no puede falsificar la firma.
Ambas defensas verifican contra peerClientPublicKey — y ese valor se toma del contrato redactado por la plataforma (connect/transfer.go:6119-6129; connect/transfer_encrypt.go:1780-1791).
7.4 ¿Puede el operador sustituir la clave de un proveedor? Hoy: sí.
Dicho llanamente: un operador que sustituya el certificado, la firma del certificado y destination_client_public_key al unísono derrota ambas defensas y puede interponerse como máquina en medio en una sesión sellada. Las notas de diseño lo dicen con las palabras del propio repositorio (connect/DESIGNNOTES.md §3.7): "Agujero residual: una plataforma que sustituya cert + firma + destination_client_public_key al unísono sigue ganando en el lado de la vinculación del certificado; el movimiento de cierre es alimentar SetPeerClientPublicKey desde la consulta fuera de banda en lugar de desde el contrato. Esa comprobación cruzada es actualmente solo de registro."
El movimiento de cierre previsto existe y está cableado, pero no actúa. Una ruta GET /key/ no autenticada sirve la clave publicada (server/api/api.go:123-124; server/controller/connect_controller.go:826-849), y el SDK publicado instala un recuperador por sesión contra ella para cada cliente de ventana y cada cliente proveedor (sdk/device_local_provider.go:398-420, alcanzado desde sdk/device_local.go:3434 y :90). En el primer establecimiento de clave el cliente recupera la clave del par fuera de banda y compara. Ante una discordancia registra:
CONTRACT vs FETCHED peer client public key MISMATCH for <peerId>
— possible platform MITM (today: log only, contract value still trusted)(connect/transfer_encrypt.go:1871-1876). La clave suministrada por el contrato sigue siendo la de confianza (:520-525, :1839-1849). Promover el registro a rechazo se describe tanto en el comentario de ajustes como en las notas de diseño como el siguiente paso de endurecimiento — eso es intención de diseño, no una propiedad publicada.
Tres precisiones más que un auditor debería retener:
- El canal fuera de banda está fuera de banda respecto del pipeline de contratos, no respecto del operador. La recuperación va al mismo host de la API del operador (
sdk/device_local_provider.go:404) y lee el mismo Redis en el que escribe quien redacta el contrato. Incluso después de que el registro pase a ser rechazo, la comprobación atrapa a un operador que sea inconsistente entre dos de sus propios canales, no a uno que sea consistente. Cerrar eso exige un ancla que el operador no controle — ids derivados de claves o un registro de transparencia, ambos recogidos como aplazados. - La omisión es tan eficaz como la sustitución, y más silenciosa. La verificación del certificado se omite sin quedar fijada cuando el conjunto de confianza está vacío, incluso cuando los contratos llevan un
ProvideTlsCertificatevacío (connect/transfer.go:4010-4016), y la prueba de identidad no puede verificarse en absoluto cuando la clave pública del par está ausente (connect/transfer_encrypt.go:1706-1708). En cualquiera de los dos casos el cifrador nunca llega a ser usable y el tráfico corre en texto plano (§2.2) sin ninguna señal visible para el usuario. Un operador que quiera leer el tráfico de un usuario concreto mientras el conmutador está activado no necesita falsificar nada; necesita omitir un campo. - La autenticidad del contrato también está enraizada en el operador. Un proveedor verifica el HMAC del contrato usando una clave secreta de provisión que el proveedor genera y publica a la plataforma (
server/controller/connect_controller.go:768-779;sdk/device_local.go:2866-2872). El operador retiene una copia, que es lo que le permite redactar contratos siquiera. Eso es lo esperable de una autoridad contable, pero significa que "el contrato es genuino" no es una afirmación independiente del operador.
Qué socava y qué no socava esto. No toca la ceguera del proveedor a la identidad, que no depende de ninguna distribución de claves. Sí significa que la ceguera del operador al contenido, en la ruta sellada, descansa actualmente en que el operador distribuya las claves honestamente — lo cual es una propiedad de política con un mecanismo criptográfico construido casi del todo por detrás, no todavía una propiedad que se sostenga frente a un operador hostil. Cualquier documento de URnetwork que describa la sesión sellada como algo que hace incondicional la ceguera del operador la está exagerando, y este documento reemplaza tal redacción.
8. Correlación de temporización y de tráfico, y el observador pasivo global
8.1 No hay defensas frente al análisis de tráfico. Ninguna.
URnetwork no incorpora tráfico de cobertura, ni relleno, ni mezclado, ni retardo por lotes, ni conformado de tráfico sobre los datos del usuario. Esto se comprobó de forma exhaustiva en connect, sdk y server:
- No existe ningún generador de paja, señuelo, relleno ni tráfico de cobertura en ninguna parte.
- El AEAD preserva la longitud por construcción: la longitud del texto cifrado es nonce + texto plano + tag (
connect/transfer_encrypt.go:305,340). Ningún protobuf deconnect/protocol/lleva un campo de relleno, y el encuadre es un prefijo de longitud pelado de 4 bytes (connect/message_framer.go:28-31). - La agrupación del lado del envío es explícitamente de retardo cero: "No hay espera de agrupación: la secuencia solo toma un segundo Pack cuando ya está encolado" (
connect/transfer.go:387-392). Todojitterde los árboles es retroceso de reconexión o dispersión de vida útil de canal, no conformado de tráfico. La compresión de acuses (connect/transfer.go:272) retrasa solo los acuses de recibo, para recortar el volumen de mensajes de retransmisión. - El único lugar donde el tráfico del usuario se acompasa siquiera es el tope
WritePacketsPerSeconddel transporte con forma de DNS (connect/transport_pt.go:63,232-246), que existe para ser amable con los resolvedores y aplica solo al modo de transporte menos preferido. Sus consultas de "bombeo" en reposo tienen una longitud distinguible de las que llevan datos, así que no oculta ni el tamaño ni la tasa.
Por lo tanto: un adversario que observe el tráfico que entra en tu dispositivo y el tráfico que sale de los proveedores que lo transportan puede correlacionar los dos por tamaño y temporización. URnetwork no defiende contra ese adversario. Esta es la misma afirmación que hace Tor sobre la correlación de extremo a extremo, y aquí aplica con menos margen, porque el objetivo de diseño de URnetwork es la baja latencia — que es exactamente la propiedad que facilita la correlación. Una ruta de cuatro tramos acotada en latencia es un intercambio deliberado frente al retardo añadido de una mixnet, y este es el lado de ese intercambio que paga el usuario. Contar el tramo del extensor no lo cambia: ese salto reenvía la sesión cifrada hacia el operador sin terminarla, así que no añade ninguna capa que un observador pueda pelar ni ningún retardo en el que pueda perderte.
Dos cosas se confunden a veces con defensas y no lo son. Los extensores y los transportes conformados (connect/net_resilient.go:112-215, connect/transport_pt.go:18-45) apuntan al bloqueo basado en DPI, y la capa resiliente se desactiva a sí misma una vez el stream está establecido (net_resilient.go:102,146-161) — nunca toca los registros de la fase de datos. Las cachés de tickets de sesión TLS deliberadamente no se comparten entre rutas de salida para que un servidor no pueda enlazarlas a través de un ticket canjeado (connect/net_tls.go:38-42,99-103); eso es no vinculabilidad de tickets, no análisis de tráfico.
Nada en el código fuente reconoce la correlación de tráfico como una limitación: una búsqueda de traffic analysis, timing correlation, traffic correlation y global adversary en los tres árboles devuelve cero resultados. El modelo de amenazas interno del repositorio (connect/DESIGNNOTES.md §3.7) va enteramente sobre el MITM del operador. Este documento es el primer lugar donde se escribe el hueco.
8.2 La ventana multiproveedor no es un mecanismo anticorrelación
El tráfico sale normalmente por varios proveedores a la vez — habitualmente de tres a ocho entre las dos ventanas — y la afinidad por sitio mantiene un sitio dado en un proveedor (connect/ip_remote_multi_client.go:138-158,1234-1240). Eso limita genuinamente cuánto ve cualquier salida individual. Pero la razón de diseño de la ventana en el código fuente es la fiabilidad de principio a fin — mitigación de destinos malos, redimensionado ponderado por salud, detección de agujeros negros (:27-52) — y el único comentario que dice "menos afinidad es más privado" está sobre ClientAffinityTimeout, un mando que está comentado (:587-591, y de nuevo en los valores por defecto en :220), con DestinationAffinity publicándose en true por razones de fiabilidad (:270). La identidad de salida plural es un beneficio real y es justo reclamarlo; reclamarla como una defensa diseñada contra la correlación no está respaldado por el código.
8.3 El observador pasivo global: fuera del alcance
Un adversario pasivo global — capaz de observar una fracción grande de los enlaces de internet simultáneamente — queda fuera del alcance, y URnetwork no proporciona ninguna defensa contra él. Sin relleno y sin tráfico de cobertura, tal adversario correlaciona flujos a través de la retransmisión y de los proveedores directamente. Esta es la misma posición que toma Tor y, a diferencia de Tor, URnetwork ni siquiera añade un retardo por salto que elevaría el coste.
Lo que sí es genuinamente distinto, y merece la pena decir sin inflarlo: el conjunto de salidas es una población cambiante de conexiones residenciales independientes en lugar de los rangos de direcciones publicados de un solo operador, así que un adversario tiene que observar un conjunto de extremos más amplio y cambiante para cubrir a un solo usuario, y los extremos no pueden enumerarse desde una lista de servidores. Eso eleva el coste. No cambia el resultado para un adversario que ya puede ver ambos extremos.
9. Identificadores de dispositivo y vinculabilidad
9.1 Qué puede observar un proveedor sobre ti
El SourceId del contrato es un id de cliente por plaza de ventana, no tu dispositivo. StoredContract.SourceId es el client_id del llamante autenticado (server/controller/connect_controller.go:461-463; analizado por el proveedor en connect/transfer.go:6088-6106). El generador acuña un id de cliente, un jwt y un id de instancia frescos para cada entrada de ventana y lo retira al desmontarla — el código fuente lo dice directamente: "El generador de la api acuña un id de cliente de plataforma efímero (con un id de instancia fresco) para cada entrada de ventana y lo retira al desmontarla" (connect/ip_remote_multi_client_identity.go:10-15; acuñado en connect/ip_remote_multi_client_api.go:326-345, liberado en :410,473).
Pero esas identidades se reutilizan deliberadamente hasta cuatro horas, en todas las rutas — no solo en las alojadas. El dispositivo instala su propio almacén local de identidades salvo que esté alojado (sdk/device_local.go:1204-1208), y una identidad de ventana almacenada se reproduce contra el mismo destino hasta que caduca: windowIdentitiesStaleAfter = 4 * time.Hour (sdk/window_identity_store.go:44-52). El propósito es mantener vivos los flujos NAT de un proveedor a través de un reinicio (connect/ip_remote_multi_client_identity.go:17-23). El coste es que un proveedor puede ver reaparecer el mismo SourceId viniendo de ti a través de reinicios de la app dentro de esa ventana. Di cuatro horas, no "efímero".
La clave de identidad Ed25519 del cliente es fresca por cliente de ventana, en la ruta de salida. Los ajustes del cliente de ventana se construyen a partir de un DefaultClientSettingsWithBufferSize fresco (sdk/device_local.go:3434-3437), que no lleva ClientKeySeed (connect/transfer.go:166-183), así que ClientKeyManager genera un par de claves nuevo (connect/transfer_key.go:96). La instantánea persistida de la ventana almacena solo el id de cliente, el jwt y el id de instancia — ninguna semilla de clave (sdk/window_identity_store.go:46-52), así que una identidad restaurada vuelve a publicar una clave nueva. Con el Cifrado poscuántico desactivado, no se presenta ninguna prueba de identidad en absoluto (connect/ip_remote_multi_client.go:9149-9157).
La dirección de origen del túnel es por sesión y de baja entropía. El SDK asigna a su TUN una dirección RFC1918 aleatoria — "un 10.x.y.h aleatorio (RFC1918, con forma de DHCP)" (sdk/device_local.go:566-571,1070-1090) — generada con un octeto de host entre 2 y 254 sobre el 10.a.b.0/24 sin conflicto más pequeño (connect/tun.go:323-348). Se computa en el constructor y nunca se persiste, así que cambia en cada sesión. El proveedor sí la ve: es el campo de origen de cada paquete tunelizado y el proveedor llavea el estado NAT sobre ella (connect/ip.go:742,1009-1020,1151). Unos 253 valores son una huella débil por sesión, no un identificador entre sesiones.
9.2 La clave de identidad del rol de proveedor es duradera
Si además compartes tu conexión, tu cliente proveedor sostiene un par de claves Ed25519 de vida larga cuya semilla se persiste al almacenamiento local (sdk/local_state.go:418-461, .device_local_key_material, el JSON client_key_seed; aplicado en sdk/device_local_key_material.go:54-70). A diferencia de los clientes de ventana, esa clave se preserva deliberadamente a través de un borrado por cierre de sesión automático — el comentario de Android es explícito: "Limpiar un estado de autenticación caduco o parcial SIN rotar la identidad del dispositivo. El material de claves de identidad es de ámbito de dispositivo, no de ámbito de sesión" (android/.../MainApplication.kt:636-647). El refresco de token, la recuperación de autenticación parcial y el reinicio de sesión a los 30 días de inactividad producen por tanto un client_id nuevo y un device_id nuevo mientras la clave pública Ed25519 sigue siendo la misma. Solo un cierre de sesión explícito del usuario la rota (sdk/local_state.go:639-644).
Esa clave se publica, se sella en cada contrato que nombre a este cliente como destino (server/controller/connect_controller.go:413-415,468), y es legible por cualquiera: GET /key/ no está autenticado por diseño (server/api/api.go:123-124; server/controller/connect_controller.go:826-829). Así que cualquier parte — no solo un proveedor al que hayas servido — puede resolver cualquier id de cliente a su clave pública y agrupar los ids de cliente que comparten una. Para una cuenta de proveedor, eso es un asidero duradero entre sesiones, entre client_id y entre device_id. Este es un coste real de proveer, y no está documentado en ningún otro lugar del corpus.
9.3 En el operador, todo enlaza
- El
client_idnunca se revoca. El propio comentario del código: "los client_id son direcciones globalmente únicas equiparables a IPv6 / nunca se revocan una vez asignados, para preservar los registros de seguridad y de auditoría" (server/model/network_client_model.go:64-67). Un cliente de nivel superior se desactiva tras 30 días de inactividad (TopLevelClientIdleExpiration,:2289) y se borra en duro 30 días después (NetworkClientReapAfterDeactivate,:2269), en cascada hacia la fila del dispositivo y elckeyde Redis (:2537). - El
device_ides por inicio de sesión, no por hardware ni por instalación: se acuña uno fresco con cada cliente de nivel superior (:332-352), y el código señala la "rotación de identidad resultante (un device_id fresco por inicio de sesión)" (:2280). Los clientes de ventana lo heredan (:356-380) y nunca se envía a un proveedor. - El token de red es un JWT de 24 horas (
server/jwt/by_jwt.go:35-37,188), refrescado por el SDK a la mitad de su vida — unas 12 horas — con jitter (sdk/device_token_manager.go:107-124,165). Refrescar el token no rota el id de cliente; se vuelve a acuñar la misma reclamación (server/model/network_client_model.go:102-125). audit_contract_eventregistra la identidad del cliente y la del proveedor por contrato (server/db_migrations.go:234-251).client_reliabilityune el hash con clave del bloque de IP directamente a un id de cliente, por bloque: la clave primaria original de cuatro columnas (server/db_migrations.go:2083-2106) se redujo después a(block_number, client_address_hash, client_id)(:2191-2195), y la tabla particionada en vivo usa esas mismas tres (server/model/network_client_reliability_partition_model.go:194-195), connetwork_idsobreviviendo como carga útil de índice (:63,631-634). La retención es de 30 días (server/model/network_client_reliability_model.go:42,687-721; las particiones diarias se descartan enteras).network_client.auth_timees un último-visto duradero retenido 30 días (server/model/network_client_model.go:2269,2289); las filas de conexión desconectadas se purgan a las 8 horas (server/taskworker/work/network_client_work.go:85).- Identificadores a nivel de cuenta, solo en el operador: correo o teléfono en claro en
network_user.user_auth, el JWT completo del IdP de terceros para los inicios de sesión SSO, identificadores en claro en cada intento de inicio de sesión enuser_auth_attempt, direcciones de cartera, filas de pago, una arista persistente de quién-invitó-a-quién ennetwork_referral, y filas deaudit_provider_eventque llevan cadenas literales de país, región y ciudad contranetwork_idydevice_id, no atadas a la purga de conexiones a las 8 horas (review/verified/PRIVACY-ENFORCEMENT.md§1.4). - El
User-Agentse registra por la lista de permitidos de cinco cabeceras (server/http_log.go:15-29). Es la única entrada de las cinco que es una superficie de huella digital.
9.4 El puente WireGuard te enlaza entre proveedores
A cada cliente WireGuard se le asigna una dirección de túnel privada estable de un pool barajado de 10.000.000 de direcciones RFC1918 (server/model/network_client_proxy_model.go:1034,1038-1118), escrita en la configuración como la dirección de la interfaz (:756-771), y no se normaliza antes de la salida — el propio FIXME del código fuente dice "actualmente la ipv4 del cliente se hilvana hasta los proveedores de salida / esto puede permitir rastrear una única ipv4 de cliente a través de múltiples proveedores" (proxy/wg.go:23-25). Es una dirección privada, no tu IP real, y el sitio web de destino nunca la ve. Pero cada proveedor de tu ventana ve la misma dirección de origen, así que unos proveedores en connivencia pueden saber que esos flujos pertenecen a un solo cliente — precisamente la propiedad que la ventana multiproveedor proporciona por lo demás. Combinado con el resolvedor fijo 1.1.1.1 (§3.2) y la ausencia de sesión sellada en esa ruta, el puente WireGuard es la forma menos privada de usar la red. Las apps y el SDK son la más privada.
10. Requerimientos legales
Jurisdicción. BringYour, Inc. es una sociedad de Delaware (docs/legal/ur.xyz/terms.md:16,25) con dirección postal en San Francisco (docs/legal/terms.md:269-271). Los términos de ur.io fijan la ley aplicable y la sede judicial en el condado de Harris, Texas (docs/legal/terms.md:315-321); los términos de ur.xyz fijan Delaware (ur.xyz/terms.md:223-229). Todo ello es estadounidense, y todo ello es alcanzable por proceso legal obligatorio de EE. UU., incluido el proceso que llega con una orden de silencio. Los términos declaran que el operador "puede monitorizar y divulgar información cuando la ley o una orden gubernamental le obliguen a ello" (docs/legal/terms.md:204-206).
Qué alcanza un requerimiento al operador. En orden descendente de poder desanonimizador: quién es la cuenta — correo o teléfono en claro, el JWT del IdP de terceros para los inicios de sesión SSO, direcciones de cartera, y registros de pago que atan a una identidad real vía Stripe, Apple, Google Play o un tx_signature en cadena; cuándo se conectó y desde qué ciudad, por conexión; qué proveedores usó y por cuántos bytes; y un hash con clave del bloque /29 o /56 desde el que se conectó, más el puerto de origen en claro. Los terceros retienen más: los procesadores de pago nombrados en §1 retienen identidad que el operador nunca almacena, y un tx_signature de Solana resuelve a una cartera en una cadena pública.
Qué no alcanza un requerimiento, porque no existe. El registro de transferencias no tiene campo alguno de destino, host, URL, SNI, puerto ni dominio en ninguna parte (review/verified/PRIVACY-ENFORCEMENT.md §1.2). Los logs de soporte subidos se descartan al recibirlos. No hay ningún registro de navegación que entregar, en ninguna ruta, sellada o no — esta es la propiedad estructural más fuerte de este documento, y se sostiene se comporte quien se comporte.
Qué alcanza un requerimiento de forma prospectiva. Todo lo anterior va sobre registros almacenados. Un requerimiento que obligue a una conducta futura es otra cosa, y §7.4 es donde muerde: un operador obligado a interceptar a un usuario concreto podría, hoy, sustituir u omitir material de claves de proveedor y leer el tráfico de ese usuario incluso con el Cifrado poscuántico habilitado, con la única señal siendo una línea de log de dispositivo. La sesión sellada eleva el coste de la divulgación retrospectiva. No resiste, en su forma actual, a un operador obligado de cara al futuro.
La eliminación de cuenta es parcial. RemoveNetwork es una lista de borrado escrita a mano, no una cascada de base de datos (server/model/account_model.go:108-260). Retira las filas de network_user y sus registros de autenticación (contraseña, SSO, cartera, frase de recuperación), la fila network y la entrada del índice de nombres, y programa una baja de suscripción en Stripe. No borra account_payment, stripe_customer, apple_subscription_transaction, las filas solana_payment_intent completadas, network_referral, las filas de dispositivo ni las filas de auditoría; esas caducan según sus propios calendarios — los eventos de auditoría a los 180 días (server/model/audit_model.go:984,1018) — o, para los pagos en cadena completados, no caducan en absoluto (server/model/solana_payment_intent_model.go:386-400 borra solo los intentos con un tx_signature nulo). La eliminación además escribe una fila: un evento AuditEventTypeNetworkDeleted llaveado sobre el network_id eliminado (account_model.go:255-257). El "eliminar su cuenta y la información personal asociada" de la política de privacidad (docs/legal/privacy.md:69) es más amplio que lo que hace el código.
No hay canario de órdenes judiciales, ni informe de transparencia, ni proceso publicado para las fuerzas del orden. Verificada su ausencia en docs/ y en el código fuente del sitio ur.io. El único puntero de proceso legal que existe dirige a quien notifique un proceso a obtener el agente registrado de Delaware desde la División de Sociedades de Delaware (docs/legal/ur.xyz/terms.md:262); [email protected] está acotado a los informes de vulnerabilidades (docs/legal/vdp.md:42) y [email protected] es la dirección general de notificaciones contractuales. Los propios documentos comparativos de URnetwork ya conceden esto frente a competidores que sí publican uno, y este documento lo repite en lugar de suavizarlo.
La política de privacidad calla donde debería ser específica. Declara que "toda la información personal que recogemos de los usuarios y sobre ellos se limita a la dirección de correo electrónico o el número de teléfono" y que la recogida ocurre "directamente de ti cuando la proporcionas" (docs/legal/privacy.md:29-37).
Un borrador anterior de esta sección llamó a eso una descripción del sistema por debajo de lo real, sobre la base de que el código almacena más categorías de las que nombra la política. Ese planteamiento se retiró el 2026-08-09 por erróneo. Todo lo demás inventariado en este documento o bien es información de cuenta que el usuario crea al darse de alta o al pagar (la clave de unión de Stripe existe porque alguien aceptó que se le facturara), o bien no es información personal en absoluto: un puerto de origen, y un hash de bloque con clave que el operador no revierte. Que un hash con clave pudiera en principio ser invertido por su propio poseedor es una salvedad de ingeniería real — §5 la enuncia, y la falta de rotación es una debilidad genuina — pero un valor que nadie consulta no es una categoría de información personal que la política haya dejado de declarar.
Lo que de verdad le falta a la política es distinto y más acotado: ningún periodo de retención, ninguna declaración sobre registros, y ninguna sección sobre fuerzas del orden o divulgación gubernamental. Las cadenas retención, log, dirección IP, citación y proceso legal no aparecen en ella. Eso importa porque la disciplina de retención existe en el código y es más fuerte de lo que la política afirma — §5.1 expone las ventanas verificadas. Un usuario que lea hoy la política no puede sujetar al operador a ninguna de ellas.
11. Contra qué no defiende URnetwork
Tres cosas se sostienen, y merece la pena nombrarlas antes de la lista que sigue, para que la lista se lea como una frontera y no como un veredicto. En una ruta retransmitida un proveedor nunca averigua quién eres, y ningún ajuste ni compilación de proveedor cambia eso (§2). No hay campo alguno de destino, host, URL, SNI, puerto ni dominio en ninguna parte del registro de transferencias, así que no hay ningún registro de navegación que se pueda requerir, filtrar ni vender (§5, §10). Y cada una de estas afirmaciones es legible en código fuente público, que es la razón por la que este documento puede ser específico sobre sus propios fallos.
Todo lo de abajo es un fallo. Dicho sin rodeos; cada punto está desarrollado más arriba.
- Correlación de temporización y de tráfico de extremo a extremo. Sin relleno, sin tráfico de cobertura, sin mezclado. Un adversario que observe tanto tu red de acceso como los proveedores que transportan tu tráfico puede correlacionarlos. §8.1.
- Un adversario pasivo global. Enteramente fuera del alcance. §8.3.
- Connivencia operador–proveedor. El operador sabe quién eres, el proveedor sabe adónde fuiste, y el registro del contrato los une. El sellado estrecha lo que el operador retiene por sí solo; no rompe la unión. §6.2.
- Un operador obligado u hostil que sustituya u omita material de claves de proveedor. Hoy la comprobación cruzada que lo atraparía registra y continúa. §7.4.
- Degradación silenciosa de la sesión sellada — CON el Cifrado poscuántico DESACTIVADO. En ese modo cualquier fallo (de handshake, de prueba de identidad, de clave ausente) recae en texto plano sin indicación visible para el usuario. Corregido para el modo que lo pide, 2026-08-10: con el conmutador activado, el cliente falla en cerrado y se niega a enviar o aceptar datos de aplicación en texto plano. Lo que sobrevive en ambos modos es el indicador ausente — ninguna app muestra si una conexión dada está sellada. §2.2.
- Un proveedor malicioso manipulando tráfico sin cifrar. El HTTP en texto plano se pasa sin cambios; el proveedor está en la posición de un punto de acceso hostil. §3.
- Compromiso del extremo. Malware, un sistema operativo comprometido, una extensión de navegador hostil, o cualquiera con tu dispositivo desbloqueado. Nada en un producto de red aborda esto.
- Identificación del lado del destino. Las cookies, los inicios de sesión, la huella digital del navegador y el comportamiento de cuenta te identifican ante los sitios que visitas con independencia de cómo llegaran los paquetes. Cambiar tu dirección de salida no te hace anónimo ante un servicio en el que inicias sesión.
- Identidad por la vía de pago. Las vías de tarjeta y de tiendas de apps ponen tu identidad en manos del procesador aunque el operador no la almacene. El USDC en cadena deja un
tx_signaturepúblico. - Proveedores Sybil. Cualquiera puede proveer, sin atestación ni garantía depositada. Una sola parte puede ejecutar muchos proveedores, incluido el operador.
- La falta de sellado en las rutas del navegador y del proxy. La extensión de navegador no tiene ajuste de Cifrado poscuántico, y en esas rutas el operador ejecuta el cliente. §2.4.
- Vinculabilidad entre proveedores en el puente WireGuard. Una sola dirección de túnel estable a través de cada proveedor de tu ventana. §9.4.
- Un asidero de identidad duradero para cualquiera que además provea. La clave Ed25519 del cliente proveedor sobrevive por diseño a la rotación del id de cliente y del id de dispositivo, y
GET /key/no está autenticado, así que cualquier parte puede agrupar los ids de cliente que comparten una clave. §9.2. - El tráfico que ve la red local durante la ventana de arranque del DNS. Documentado en el código fuente como un coste aceptado. §3.2.
- Cualquier cosa que habría encontrado una auditoría de terceros. No se ha realizado ninguna sobre el protocolo ni sobre el código de servidor.
12. Lo que este documento no pudo establecer
Una afirmación no establecida en un modelo de amenazas es una afirmación que tampoco debería estar en la documentación. Estas quedan abiertas.
- Qué registra el LB de entrada. La configuración de cabeceras de nginx está en el árbol (
xops/.../connect/ingress.yaml:9) y el servicio connect limita la tasa sobre una dirección hasheada (server/connect/transport_rate_limit.go:65-80), pero la propia configuración de logs de acceso de nginx no se auditó. Cualquier afirmación de extremo a extremo de "no retenemos tu dirección" depende de ella. - Si las líneas de log con dirección y SNI no gobernadas de §5 llegan a almacenamiento duradero. Se escriben a stderr; adónde va el stderr en producción, y cuánto tiempo se conserva, es una cuestión de despliegue que esta revisión del código fuente no puede responder. El tailer de monitorización sí extrae patrones
ip:portde las líneas de log y los reemite dentro de hallazgos (server/monitor/tailer.go:136,328,333), lo cual es evidencia de que al menos algunas de esas líneas las leen sistemas que las retienen. - Si el dispositivo proxy del lado del servidor de WireGuard reescribe el origen del paquete antes de entregar los paquetes a los proveedores. La dirección de túnel estable y la vista del operador del extremo público real (
server/proxy/wg_handoff.go:56-67) están ambas verificadas; la ruta completa a través deserver/proxy/proxy_device.goyOpenProxyDeviceno se ha leído de principio a fin (review/verified/ARCHITECTURE.md, punto no verificable 3). - Si la resolución de nombres en la ruta del navegador puede llegar a ocurrir localmente. La extensión configura por defecto un proxy HTTPS CONNECT, que resuelve de forma remota, y la resolución de nombres de SOCKS pasa por el marcador DoH del túnel (
connect/tun.go:1155-1175). No se probó el comportamiento del DNS del proxy en cada navegador bajo todas las configuraciones. - La verbosidad real en producción.
BY_LOG_Vvale 0 por defecto (server/env.go:41-49), lo que suprime el registro de destinos gobernado porV(1)en cliente y servidor — pero el único manifiesto de despliegue del árbol lo pone a2(xops/gitops-unused/.../api/deployment.yaml:47-48), en un directorio llamadogitops-unused. Trata la verbosidad como una bandera de tiempo de ejecución, nunca como una garantía estructural. - Independencia de la flota. El operador publica recuentos en vivo de ciudades y países desde su propio agregado sobre proveedores válidos actualmente conectados (
server/model/network_client_location_model.go:1607-1641), y la ubicación la deriva el operador de la conexión que observa en lugar de declararla el proveedor (review/verified/ARCHITECTURE.md§6.4). Pero ninguna parte independiente ha medido la flota desde fuera, y nada verifica que los proveedores ofrecidos a un cliente concreto sean independientes entre sí o del operador. El mecanismo es comprobable en el código fuente; la población no es comprobable por un tercero hoy. - La vinculación de la clave de identidad en la creación de la cuenta. El diseño declara la expectativa de que "la vinculación (ClientId, clave pública) se registra en la creación de la cuenta" (
connect/transfer_key.go:20-30). Lo que se publica es un cliente publicando su clave a un Redis controlado por el operador, servida de vuelta por una API controlada por el operador. Si existe operativamente un registro más fuerte no pudo establecerse desde el código fuente; asume que no. - Si toda ruta de envío del dispositivo usa un cliente de ventana. El llaveado efímero de la ruta de ventana está verificado (§9.1). El cliente de nivel superior del dispositivo lleva la semilla duradera y habilita el cifrado incondicionalmente (
sdk/device_local_provider.go:90-98), así que cualquier envío que viaje sobre el cliente de nivel superior — relaciones entre pares de red, rutas de retorno de compañeros — va firmado con la clave estable y lleva elclient_idestable de nivel superior. Esas rutas no se enumeraron de forma exhaustiva. Lee "los identificadores de salida son efímeros" como acotado a la ruta del cliente de ventana. - Si
RolesyPrincipalse pueblan alguna vez para clientes de consumo ordinarios. Son cadenas asignadas por el operador selladas dentro de los bytes firmados del contrato y establecidas solo paraProvideMode_Network(connect/protocol/transfer.proto:408-415;server/controller/connect_controller.go:474-481). No se encontró evidencia de que las apps las pueblen, y no se auditó a cada llamante. Si alguna vez se poblaran para tráfico de consumo serían identificadores entre sesiones de primera clase visibles para un proveedor. - Persistencia del material de claves en hosts no móviles.
ClientKeySeedse referencia solo desdesdk/local_state.go,sdk/device_local_key_material.go,sdk/device_local.goysdk/cgo/. Los hosts que no llaman a esos obtienen una clave fresca por proceso; el comportamiento de Linux, Windows, la extensión y el proxy de servidor no se verificó individualmente. - Si la sede judicial de Texas de los Términos de ur.io es intencionada, dada una entidad de Delaware, una dirección de California y una sede de Delaware en los términos de ur.xyz. Ningún documento del árbol lo explica. Esta es una pregunta para los abogados, no un hallazgo.
13. Reportar
Vulnerabilidades: [email protected] y la política de divulgación en ur.io/vdp. Las correcciones a este documento, incluidos los desacuerdos con cualquier veredicto que contenga, son bienvenidas por la misma vía — un hallazgo independiente que contradiga una afirmación de aquí es más útil que la afirmación.