# Модель угроз

Этот документ называет противников URnetwork и говорит, что система делает и
чего не делает против каждого из них. Он написан, чтобы его атаковали. Там, где
свойство условно, условие стоит в том же предложении; там, где что-то является
замыслом конструкции, который поставляемый код ещё не принуждает, это помечено
как **замысел конструкции**, а не свойство. Сводка для читателей, которые не
станут читать полную запись, предшествует разделу 1.

Эталон — собственное
[объяснение Tor об атаках, которые луковая маршрутизация не побеждает](https://support.torproject.org/about-tor/security/attacks-on-onion-routing/):
противник, наблюдающий за обоими концами цепочки, может скоррелировать их по
таймингу, и Tor говорит это публично. URnetwork не поставляет ни маскирующего
трафика, ни дополнения, поэтому здесь верно то же самое, и раздел 8 говорит это
без смягчения.

**Статус гарантий, заявленный один раз и применимый ко всему ниже.**
Независимого аудита протокола, движка connect и серверного кода оператора — того
слоя, на котором держатся заявления о приватности, — не существует. Две сторонние
оценки 2025 года покрывают другие поверхности: независимый сторонний тест на
проникновение веб-приложения и API (25 апреля — 5 мая 2025, с учётными данными,
вручную, OWASP Top 10 плюс обзор контролей ASVS) и оценка Leviathan Security
Group MASA AL2 Android-приложения (завершена 23 мая 2025), которую оно прошло и
рамки которой сама Leviathan очерчивает как «не целостную оценку безопасности или
исчерпывающий тест на проникновение». Ни одна не изучала логирование, хранение,
путь данных или протокол. Поэтому каждое утверждение в этом документе — это
утверждение о читаемом исходном коде, который любой может проверить и который
никто вне проекта пока не проверил целиком.

## Как читать цитаты

Утверждения ссылаются на `path:line` относительно рабочих деревьев в
`/Users/brien/urnetwork/{connect,sdk,server,proxy,extension}` по состоянию на
2026-08-07. Номера строк дрейфуют; идентификаторы — нет. Там, где находка была
установлена в более раннем проходе проверки кода, ссылка идёт на
`review/verified/ARCHITECTURE.md` или `review/verified/PRIVACY-ENFORCEMENT.md`,
которые несут ту же конвенцию.

Три метки используются намеренно:

- **Свойство** — код это принуждает, и атакующий, контролирующий остальные
  стороны, всё равно не может это нарушить.
- **Дисциплина** — код выбирает не делать того, на что способен. Изменение
  развёртывания или однострочный патч могли бы это обратить.
- **Замысел конструкции** — конструкция говорит, что это цель; поставляемый код
  этого ещё не принуждает.

## Сводка

Этот раздел — для читателей, которые не станут читать полную запись. Каждое
предложение в нём подкреплено пронумерованным разделом ниже или заявлением о
гарантиях выше.

**Что представляет собой система.** URnetwork — сеть приватности, в которой
участники ретранслируют трафик для других участников. Путь: вы → экстендер →
оператор → провайдер → интернет. Разделение доверия — между двумя
ретранслирующими сторонами: оператором, который знает, кто вы, и провайдером,
который видит, куда идёт ваш трафик.

**Два свойства, очерченные раздельно.** На ретранслируемом пути провайдер
никогда не получает ваш реальный исходный IP; ни настройка, ни сборка провайдера
этого не меняют (§2). Оператор не может прочитать запечатанный сеанс
клиент↔провайдер, который поставляется включённым по умолчанию в пяти нативных
приложениях (Android, iOS, macOS, Windows и Linux) под названием Post Quantum
Encryption (§2.1). У браузерного расширения запечатанного сеанса нет — его
клиентское устройство работает внутри оператора, который выступает точкой
трансляции протокола, поэтому запечатывание там существовать не может (§2.4).

**Дефекты целостности — один исправлен, один открыт.** Запечатывание раньше
молча отказывало в открытую во всех режимах. **Исправлено 2026-08-10:** при
включённом Post Quantum Encryption клиент работает с отказом в закрытом
состоянии и отказывается отправлять или принимать прикладные данные открытым
текстом, а не понижается до них (§2.2). При выключенном переключателе прежнее
оппортунистическое поведение по-прежнему в силе, и ни в одном из режимов ни одно
приложение не сообщает, что именно произошло. Остаётся открытым: может ли
оператор подменить ключ провайдера? Сегодня: да (§7.4), поэтому
конфиденциальность запечатывания держится против пассивного оператора и против
срывающего обёртку на пути, но не против оператора, готового сочинить ложный
ключ. `verify/BEFORELAUNCH.md`, пункты 8 и 9, отслеживают оба; §2.2 и §7.4
описывают текущее состояние.

**От чего система не защищает.** Противник, наблюдающий и вашу сеть доступа, и
провайдеров, несущих ваш трафик, может скоррелировать два конца по размеру и
таймингу (§8.1). Глобальный пассивный наблюдатель вне рамок целиком (§8.3).
URnetwork не поставляет ни дополнения, ни маскирующего трафика, ни перемешивания;
раздел 11 — полный список.

**Гарантии.** Независимого аудита протокола, движка connect и серверного кода
оператора нет; две сторонние оценки 2025 года покрывают другие поверхности: тест
на проникновение веб-приложения и API и оценка Leviathan Security Group MASA AL2
Android-приложения, которую оно прошло.

**Где живут детали.** Разделы 1 и 2 определяют стороны, три режима соединения и
то, что видит каждая сторона в каждом режиме. Разделы с 3 по 5 берут по очереди
каждого противника: злонамеренного провайдера, сетевого наблюдателя и оператора.
Разделы 6 и 7 покрывают провайдеров, которых держит оператор, сговор и подмену
ключа провайдера. Разделы с 8 по 12 покрывают корреляцию трафика,
идентификаторы устройств, юридические требования, список того, от чего система
не защищает, и то, что этот документ установить не смог. Раздел 13 говорит, куда
сообщать об уязвимостях и поправках.

## 1. Стороны

| Сторона | Кем ведётся | Позиция |
|---|---|---|
| Клиент | Пользователь | Приложение или экземпляр SDK; на путях браузера и прокси он вместо этого работает на серверах оператора (§2.4) |
| Входной балансировщик | Оператор | nginx; штампует наблюдаемый адрес клиента в `X-UR-Forwarded-For` (`xops/gitops-unused/gitops-prod/urnetwork/connect/ingress.yaml:9` — единственный манифест ingress в дереве, в каталоге с названием `gitops-unused`, поэтому он может не описывать продакшен; §12) |
| Служба connect | Оператор | Один или два процесса на разных хостах, соединённые внутренним обменным подключением (`server/connect/resident.go:2310-2327`). Не «сервер-ретранслятор» |
| API / плоскость управления | Оператор | Аутентификация, обнаружение и ранжирование провайдеров, контракты, поиск публичного ключа (`server/api/api.go`) |
| Провайдер | Любой участник | Получает ретранслируемые пакеты и набирает назначение со своего собственного подключения (`connect/ip.go:3932` `RemoteUserNatProvider` → `LocalUserNat`) |
| Экстендер | Любой доброволец | Первое звено пути: TLS-пересылающий ретранслятор на независимом адресе. Несёт сеанс клиент↔платформа, не терминируя его, поэтому видит подключающийся IP и не может расшифровать (`connect/net_extender.go:51-55`, `connect/extender/extender.go:57-61`) |
| DoH-резолверы | Cloudflare, Google, Quad9, OpenDNS | Путь приложения резолвит DNS over HTTPS через туннель к одному из этих четырёх (`connect/net_http_doh.go:150-155`) |
| Платёжные процессоры | Stripe, Apple, Google, Solana/Circle | Держат настоящую личность для платных аккаунтов; оператор хранит ключи связи (`server/db_migrations.go:1712-1719`, `:4640`, `:2350-2356`) |

Оператор — **BringYour, Inc.**, корпорация штата Делавэр с почтовым адресом в
Сан-Франциско (`docs/legal/ur.xyz/terms.md:16,25`;
`docs/legal/terms.md:269-271`). Раздел 10 покрывает, что это значит для
юридического процесса.

## 2. Три режима и что видит каждая сторона

Эта таблица канонична и совпадает с `OVERVIEW.md`. Всё остальное в этом
документе — механизм за ней.

| Режим | Видит оператор | Видит провайдер | По умолчанию и доступность |
|---|---|---|---|
| **Ретранслируемый запечатанный** | аккаунт/исходное подключение, связь с провайдером, шифртекст и тайминг/объём | трафик назначений, идентификатор устройства/контракта, **не** ваш реальный исходный IP | нативное умолчание, из коробки; только нативные приложения; см. §2.2 |
| **Ретранслируемый стандартный** | аккаунт/исходное подключение, связь с провайдером, внутренние назначения и байты пакетов | трафик назначений, идентификатор устройства/контракта, **не** ваш реальный исходный IP | пути браузера и прокси (§2.4) или выключенное запечатывание; при включённом запечатывании провайдер, с которым сеанс запечатать не удалось, пропускается, а не обслуживается здесь (§2.2) |
| **Прямой** | меньше участия в ретрансляции | **ваш реальный исходный IP** и трафик назначений | по явному выбору, отключением Строгой анонимизации (§2.3) |

Далее следуют два асимметричных утверждения, и они не равны по силе:

**Слепота провайдера к личности — это свойство, и оно безусловно на обоих
ретранслируемых путях.** Входная точка провайдера принимает идентификаторы, а
не адрес: `RemoteUserNatProvider.Receive` параметризуется `TransferPath` из трёх
16-байтовых идентификаторов (`connect/ip.go:4298-4303`;
`connect/connect.go:45-49`). Ни один транспортный protobuf не несёт адреса
клиента, города или страны — grep по `connect/protocol/*.proto` на
`location|city|country|region` не возвращает ничего
(`review/verified/ARCHITECTURE.md` §1.3). Нет настройки, которая выключала бы это
на ретранслируемом пути, и никакая сборка провайдера не может получить адрес,
который ей никогда не отправляют.

**Слепота оператора к содержимому — это умолчание, а не настройка, — над
механизмом, у которого остался один дефект.** У механизма есть имя:
**Post Quantum Encryption**, контрол в шторке подключения приложений Android,
iOS, macOS, Windows и Linux, который запечатывает сквозной сеанс между вашим
клиентом и провайдером (§2.1). Условным остаётся уже не то, найдёт ли
пользователь переключатель, и с 2026-08-10 — уже не то, не установилось ли
запечатывание молча: при включённом переключателе соединение, которое не удаётся
запечатать, не несёт прикладного трафика вовсе, вместо того чтобы нести его
открытым текстом (§2.2). Условным остаётся то, честно ли оператор, который
отдаёт ключи провайдеров, против которых запечатывание сверяется, эти ключи
отдаёт (§7.4). Прочитайте этот пункт, прежде чем полагаться на это свойство;
пользователю он не виден.

**Умолчание на запуске: ВКЛЮЧЕНО.** Владелец продукта постановил 2026-08-07, что
запечатанный сеанс поставляется включённым, поэтому слепота оператора является
умолчанием, а не выбором, который должен сделать пользователь. Этот документ
написан против такого состояния на запуске; на момент написания код по умолчанию
всё ещё выключен (`verify/BEFORELAUNCH.md`, пункт 7).

Этот разрыв теперь важнее в одну сторону, чем прежде. Поскольку затвор отказа в
закрытом состоянии привязан к тому же самому переключателю, **включение
умолчания теперь распространяет реальную гарантию на каждого пользователя**, а
не просто расширяет охват молчаливого провала — обратное тому, о чём этот раздел
предупреждал, когда запечатывание могло отказать в открытую во всех режимах.
Чего умолчание по-прежнему не исправляет — это §7.4, свойство самого
запечатывания: включённое по умолчанию запечатывание, в котором оператор может
устроить машину посередине, защищает только против пассивного оператора.

У **браузерного расширения запечатанного сеанса нет вовсе**
(`extension/src/utils/auth-params.ts:33`, `performance_profile: null`). Это
рамки, а не умолчание, и постановление о запуске этого не меняет.

Везде, где запечатывания нет — эндпоинты браузера и прокси (§2.4) и нативные
приложения с выключенным запечатыванием (§2.2), — оператор терминирует
TLS/QUIC клиента и ретранслирует открытые IP-пакеты
(`connect/protocol/ip.proto:9-16`). Он *выбирает* разбирать только маршрутный
заголовок — `FilteredTransferFrame` не содержит ничего, кроме пути передачи
(`connect/protocol/transfer.proto:85-87`; `connect/transfer.go:1500-1509`), — и
это **дисциплина**, а не криптографический барьер.

### 2.1 Что такое запечатывание, точно

Сеанс TLS 1.3 напрямую между клиентом и провайдером, переносимый как обычные
управляющие кадры через того же оператора (`connect/transfer_encrypt.go:44-51`).
Обмен ключами — гибридный **X25519MLKEM768**, с обычным X25519 как откатом
(`connect/transfer_encrypt.go:373-381,406`); AEAD — AES-256-GCM поверх
32-байтового экспортированного ключа (`:285-297`); идентичности — Ed25519
(`:892-897`), поэтому «постквантовость» очерчивается только обменом ключами.
Взаимный TLS обязателен, а сертификат пира проверяется на слое
последовательности против обязательства контракта, а не стеком TLS
(`:388-406`).

### 2.2 Отказ в закрытом состоянии, когда вы его просите, отказ в открытую, когда нет

**Обновлено 2026-08-10. Этот раздел раньше говорил, что запечатывание молча
отказывает в открытую и что варианта отказа в закрытом состоянии нет. Это
больше не так, и данное изменение — самое значительное укрепление в истории
этого документа**: оно закрыло вектор понижения в обе стороны. Далее — текущее
поведение, прочитанное по исходникам.

Шифрование по-прежнему двоичное свойство `Cipher() != nil`, и nil-шифр
по-прежнему означает, что рукопожатие не завершилось: сорванное рукопожатие,
непройденное доказательство идентичности или контракт, так и не понёсший
публичный ключ пира, — всё это оставляет его nil. Провал доказательства
идентичности по-прежнему не фатален на слое сеанса: сеанс «оставляется
неаутентифицированным» (`connect/transfer_encrypt.go:1977`), а не рвётся.

**Изменилось то, что происходит дальше.** Теперь есть три режима
(`connect/transfer_encrypt.go`, `EncryptionMode`):

| Режим | Поведение |
|---|---|
| `EncryptionModeOff` | Нулевое значение. Слой сеанса инертен, всё открытым текстом. |
| `EncryptionModeOpportunistic` | Запечатывает, как только сеанс устанавливается, до тех пор — открытый текст; или навсегда, если он так и не установится. Историческое поведение. |
| `EncryptionModeRequired` | **Никогда не раскрывает прикладные данные открытым текстом ни к пиру, ни от пира, для которого сеанс ожидается.** |

В режиме `EncryptionModeRequired` гарантия обеспечивается в четырёх точках:

- **Входной затвор отправки** (`connect/transfer.go:2796`). Прикладной пакет
  ждёт шифра в пределах таймаута вызывающей стороны и затем *отклоняется,
  неотправленным* — никогда не понижается — с типизированной ошибкой
  `ErrEncryptionRequiredNotEstablished`.
- **Подстраховка отправки** (`connect/transfer.go:4125`). Кадр, который доходит
  до писателя без шифра, отклоняется, а не записывается, что покрывает узкую
  гонку, когда сеанс рвётся между постановкой в очередь и записью.
- **Затвор приёма** (`connect/transfer.go:5844`). *Прикладной кадр открытым
  текстом* от пира, для которого сеанс ожидается, отбрасывается и заносится в
  аудит — это та половина, которая важнее всего, потому что она закрывает
  понижение, при котором атакующий срывает обёртку, а получатель иначе принял бы
  открытый текст. Кадр подтверждается и отбрасывается, а не остаётся без
  подтверждения, поскольку удержание подтверждения оставило бы разрыв в
  упорядоченной последовательности и заклинило бы обе стороны.
- **Предфильтр кандидатов** (`connect/ip_remote_multi_client.go`,
  `EncryptionCapabilityPrefilter`). Кандидат окна, о котором внеполосный
  ключевой API платформы говорит, что тот никогда не публиковал ключ
  идентичности, проваливается немедленно, поскольку он всё равно не сможет
  завершить рукопожатие. Это лишь ускоряет неизбежный провал; допустить
  кандидата это не может никогда.

Управляющие кадры рукопожатия, подтверждения и пиры плоскости управления
исключены по замыслу: затвор покрывает прикладную полезную нагрузку, а не те
леса, которые её поднимают.

**Какой режим вам достанется, решает переключатель Post Quantum Encryption.**
Когда он включён, потребительский клиент работает в `EncryptionModeRequired`
(`connect/ip_remote_multi_client.go:9186-9197`): провайдер, который не может
установить сеанс, не несёт для вас прикладного трафика вовсе, вместо того чтобы
нести его открытым текстом. Заявленная цена — доступность, принятая намеренно.
Провайдеры работают в `EncryptionModeOpportunistic`
(`sdk/device_local_provider.go:98`), чтобы один провайдер мог обслуживать и
запечатанных, и незапечатанных потребителей; это выбор совместимости на стороне
отвечающего, и он не ослабляет гарантию инициатора.

**Что это значит для переключателя.** При включённом PQE запечатывание больше не
отказывает в открытую: оно отказывает в закрытом состоянии, громко, и на
отправке, и на приёме. При выключенном PQE оппортунистическое поведение,
описанное в прежних версиях этого раздела, действует в полной мере — трафик
может течь открытым текстом, если сеанс так и не установится, и пользователю об
этом ничего не сообщают. **Поэтому честная формулировка условна, и умолчание
имеет значение**: см. замечание **«Умолчание на запуске»** в §2.

Поведение покрыто тестами, а не только комментариями —
`TestRequiredEncryptionFailsClosedAgainstPlaintextPeer`,
`TestRequiredGateNonBlockingSendRefusesPreCipher`,
`TestRequiredGateBoundedBudgetRefusesUnsent`,
`TestRequiredSendRefusalTypedErrorAndEvent` и
`TestRequiredContractFreeWithoutKeySourceFailsClosed`.

Один устаревший комментарий, который при аудите стоит игнорировать: заголовок
`completeHandshake` в `connect/transfer_encrypt.go:1650-1654` всё ещё утверждает
без оговорок, что «последующий трафик течёт открытым текстом». Это верно только
в режимах `Off` и `Opportunistic`; в режиме `Required` затворы выше это
перекрывают. Комментарий старше исправления.

**Остаётся открытым: пользователь не видит, какой режим достался соединению.**
Индикатора «запечатано или нет» по конкретному подключению нет ни в одном
приложении. Единственные сигналы — строки лога устройства:
`peer identity proof verified — cipher is now usable`
(`connect/transfer_encrypt.go:1954`) при успехе, `Errorf` при провале и событие
`NotifyRequiredSendBlocked`, когда затвор отклоняет. SDK открывает хук изменения,
поэтому недостающая часть — это UI, а не проводка.

### 2.3 Прямой режим

Выключение **Строгой анонимизации** устанавливает `AllowDirect`, чей собственный
комментарий к полю гласит:
`// setting this to true exposes the real source IP to the provider`
(`sdk/sdk.go:710-711`). Поток принудительно переводится на P2P-стрим
(`connect/ip_remote_multi_client_probe.go:1176-1178`) поверх канала данных
WebRTC/ICE (`connect/transport_p2p_webrtc.go:986`), а ICE означает, что обе
конечные точки узнают адреса друг друга. Умолчание — выключено: nil-профиль
производительности даёт false (`connect/ip_remote_multi_client.go:1861-1863`;
`sdk/local_state.go:607-616`).

Два принудительных случая: пиры в той же сети (ваши собственные устройства)
всегда разрешают прямой режим (`connect/ip_remote_multi_client.go:1822-1825`), а
размещённые устройства принудительно его выключают
(`sdk/device_local.go:1516-1533`). Заметьте, что прямой режим убирает оператора
*только из пути данных*. Контракты, выбор провайдеров и управляющие сообщения
по-прежнему идут через платформу, поэтому оператор по-прежнему узнаёт, каким
провайдером вы пользовались и сколько байтов прошло.

### 2.4 Путь, который таблица не покрывает: эндпоинты браузера и прокси

В браузерном расширении, веб-приложении и на эндпоинтах HTTPS/SOCKS5/WireGuard
**клиента запускает оператор**. Расширение выделяет серверное прокси-устройство
(`extension/src/utils/auth-params.ts:22-36`, `enable_socks`/`enable_http`), и
браузер нацеливается на прокси-эндпоинт оператора — HTTPS CONNECT по умолчанию
(`extension/src/bridge/background.ts:220`;
`extension/src/utils/proxy-manager.ts:264`). Экземпляр SDK, который открывает
контракты и держит окно провайдеров, работает в `server/proxy`, а не на машине
пользователя.

Именно поэтому никакой запечатанный сеанс на этих платформах существовать не
может — как вопрос архитектуры, а не ещё не потраченных инженерных усилий.
Запечатанный сеанс — это клиент↔провайдер, и его гарантия зависит от того, что
клиентский конец живёт на собственном устройстве пользователя. Браузер или
обычный прокси-клиент говорит на собственном протоколе (HTTPS CONNECT, SOCKS5,
WireGuard) и не может выполнять движок сети, поэтому оператор запускает
устройство клиента удалённо и выступает точкой трансляции протокола между двумя.
Запечатывание, согласованное с такого удалённого устройства, начиналось бы внутри
оператора — той самой стороны, ради ослепления которой запечатывание существует.
Реализация приняла этот размен намеренно: эти поверхности существуют ради
удобства на платформах и в протоколах, которые не могут хостить полного клиента,
и документация не должна описывать их как запечатанные.

Последствия, сказанные прямо:

- Слепота провайдера к личности по-прежнему держится — провайдер по-прежнему
  получает только идентификаторы.
- Слепоты оператора на этом пути не существует ни в какой форме. Оператор
  терминирует прокси-соединение, получает хост назначения из CONNECT и держит
  ключи клиента.
- Размещённое устройство не устанавливает мультиплексор апгрейда DNS
  (`server/proxy/proxy_device.go:556`, `SetUpgradeMuxSettings(nil)`), поэтому DoH
  внутри туннеля на пути приложения (§3.2) здесь не применяется.

Место шифрования на этом пути занимает дисциплина хранения: прокси-путь данных
не логирует ничего на путях, управляемых клиентом, и это закреплено
регрессионным тестом — `proxy/socks5_nolog_test.go`,
`TestClientDrivenTrafficNeverLogs`, утверждает ноль строк лога по случаям
некорректных, слишком больших и недозваниваемых запросов. Две честные оговорки:
заявленный мотив теста — DoS через раздувание логов, а не приватность, а
обработчик восстановления после паники всё же логирует адрес клиента
(`proxy/socks5_server.go:128-131`). См.
`review/verified/PRIVACY-ENFORCEMENT.md` §2.2.

## 3. Противник: злонамеренный провайдер

**В рамках и предполагается.** Раздавать может любой. Ни аттестации, ни стейка,
ни проверки личности, ни устойчивости к Sybil на участие провайдера нет — поиск
по деревьям server, connect и sdk на `sybil|kyc|attestation` не возвращает ничего
релевантного. Провайдер выполняет открытый код на железе, которое контролирует,
поэтому исходите из того, что он выполняет модифицированную сборку.

**Что он видит.** Всё то, что видит интернет-провайдер для трафика, который он
несёт: IP и порт назначения, размеры и тайминги пакетов и TLS SNI там, где
клиент отправляет его в открытом виде. Он получает сырые IP-пакеты и набирает их
сам (`connect/protocol/ip.proto:9-16`; `connect/ip.go` `LocalUserNat`). Он также
видит `client_id`, переносимый как `SourceId` контракта
(`connect/protocol/transfer.pb.go:1779`;
`server/controller/connect_controller.go:461-463`) — идентификатор на слот окна,
который разрешить в аккаунт может только оператор, — и никогда `device_id`, у
которого нет поля на проводе. Его время жизни и несколько вещей, которые всё же
переживают сеансы, — в §9.

**Что он может сделать.**

- **Открытый HTTP: читать и изменять.** Порт 80 пропускается на выход без
  изменений по умолчанию — `HttpUpgradeUnencrypted` является режимом по
  умолчанию (`connect/ip_mux_upgrade.go:38-47`). Провайдер находится в той же
  позиции, что враждебная точка доступа Wi-Fi, для любого трафика, который сам не
  зашифрован. HTTPS защищает содержимое приложения от провайдера; ничто в
  URnetwork не добавляет второго слоя поверх собственного TLS назначения.
- **Избирательно отбрасывать, задерживать или тормозить трафик.** Провайдер,
  который подтверждает трафик, но не возвращает ничего, помечается чёрной дырой и
  удаляется (`connect/ip_remote_multi_client.go:37-54`), поэтому отказ
  обнаруживается и обходится — но обнаружение статистическое, а трафик, который
  он видел до удаления, всё равно был увиден.
- **Врать о производительности, чтобы привлечь трафик.** Ранжирование использует
  измеренные задержку и пропускную способность
  (`server/model/network_client_location_model.go:2430-2447`), и измерение —
  оператора, а не самоотчёт провайдера, поэтому это ограничено, — но это вход
  ранжирования, а не контроль целостности.
- **Коррелировать потоки внутри собственного вида.** Закрепление по сайтам
  привязывает сайт к одному провайдеру
  (`connect/ip_remote_multi_client.go:1234-1240`), поэтому отдельный провайдер
  видит связный срез браузинга одного клиента для тех сайтов, которые держит.

**Чего он не может без посторонней помощи.** Узнать ваш реальный IP на
ретранслируемом пути, узнать ваш аккаунт или почту либо приписать `client_id`
человеку. Для этого нужен оператор (§5).

### 3.1 Что `ip_security` ограничивает и чего не ограничивает

Слой `ip_security` движка connect работает на собственном выходе провайдера и на
собственном устройстве пользователя, никогда централизованно у оператора — поля
`IngressSecurityPolicyGenerator`/`EgressSecurityPolicyGenerator` у оператора
существуют, но никогда не присваиваются и не читаются
(`server/connect/resident.go:263-264`;
`review/verified/PRIVACY-ENFORCEMENT.md` §4.1). Он блокирует сигнатуры BitTorrent
и файлообмена, непрозрачные нестандартные протоколы, назначения из репутационных
списков и трафик атакующих шаблонов, и делает это на намеренно тонкой основе:
5-кортеж плюс префикс полезной нагрузки, ограниченный 8 пакетами / 512 байтами.

Репутационные таблицы генерируются, а не ведутся вручную. `connect/security` и
`connect/blocker` агрегируют публичные фиды threat-intelligence — abuse.ch Feodo
Tracker botnet C2, Spamhaus DROP и DROPv6, Emerging Threats compromised IPs,
Blocklist.de, CINS Army, TweetFeed, ViriBack, BruteForceBlocker — в упакованные
таблицы диапазонов (46 789 диапазонов IPv4 по снимку от 2026-08-05; заголовок
`connect/ip_security_cfaa_block.go` несёт список фидов и атрибуцию). Конвейер
релиза перегенерирует обе таблицы из живых фидов (`build/all/run.sh`, шаг
`CONNECT_IP_UPDATE`), поэтому каждый релиз поставляет актуальный снимок, а
обновлённый клиент блокирует самое свежее перечисленное адресное пространство.
Снимок стареет вместе с установленной сборкой; списки обновляются с релизом, а не
по воздуху.

Граница префикса полезной нагрузки — `connect/ip_security_dmca.go:117-119`, при
этом имя сервера вычищено из
ключа потока (`:366,386,424`), а счётчики ключуются по `(version, protocol, port)`
с полем IP, которое никогда не включается (`connect/ip_security.go:554,604-624`).

**Это контроль безопасности для честных провайдеров, а не контроль безопасности
против враждебных.** Он ограничивает то, что подключение провайдера будет *нести
наружу*. Он ничего не делает с тем, что провайдер делает с трафиком, который он
всё же несёт, он работает в процессе, которым владеет провайдер, и
модифицированная сборка может его отключить. Не читайте его как ограничение на
злонамеренного провайдера.

Одна деталь отчётности принадлежит этому месту, потому что она является каналом
метаданных. Совпадение сигнатуры BitTorrent возвращает Incident, что вызывает
`ReportAbuse` (`connect/ip.go:4481-4487`), передавая оператору управляющий кадр
`PeerAudit`, несущий идентификатор устройства пира и логический признак — без
назначения, без домена, без содержимого
(`connect/transfer.go:870-876,6653-6683`). У оператора сегодня нет обработчика
для этого типа сообщений, поэтому по получении ничего не сохраняется
(`server/controller/connect_controller.go:236-250`; grep по всему репозиторию на
`PeerAudit` в `server` возвращает ноль попаданий). Это **дисциплина**, до
изменения которой один оператор `case`. Отбрасывания непрозрачно-зашифрованного
молчаливы и без отчёта (`connect/ip_security_dmca.go:483-486`).

### 3.2 DNS

На пути приложения и SDK обычный DNS на UDP/TCP 53 перехватывается и резолвится
по DoH **через туннель** (`connect/ip_mux_upgrade.go:120-148`, устанавливается по
умолчанию в `sdk/device_local.go:1120`) к Cloudflare, Google, Quad9 или OpenDNS
(`connect/net_http_doh.go:150-155`). Поэтому провайдер видит HTTPS-соединение к
публичному резолверу, а не запрос. Два честных предела:

- **Окно утечки DNS при запуске задокументировано в исходниках.** Пока DoH через
  туннель ещё устанавливается, запрос идёт наперегонки с резолвером через выход
  **локального хоста**, «ценой краткой утечки DNS во время запуска»
  (`connect/ip_mux_upgrade.go:120-126,78-92`). Ваша локальная сеть и
  интернет-провайдер могут видеть эти запросы.
- **Эндпоинт WireGuard вручает клиенту фиксированный публичный резолвер**,
  `DNS = 1.1.1.1` (`server/model/network_client_proxy_model.go:760`), а порт 53
  пропускается политикой безопасности без инспекции
  (`connect/ip_security_cfaa.go:113`). На этом пути DNS-запросы проходят через
  провайдера как обычный DNS, и провайдер может их читать и на них отвечать.

Четыре DoH-резолвера — третьи стороны, которые видят запросы, приходящие с
выходного адреса провайдера, не связанные с вашим аккаунтом. Это настоящая
зависимость, и её нельзя настроить в ноль: на DNS кто-то должен отвечать.

## 4. Противник: сетевой наблюдатель

Четыре позиции, от слабейшей к сильнейшей.

**Ваша локальная сеть и интернет-провайдер.** Они видят, что вы подключаетесь к
`connect.bringyour.com` по TLS/QUIC или к экстендеру, плюс размеры и тайминги.
Они не видят назначений внутри туннеля, кроме как в задокументированном окне DNS
при запуске выше. Там, где платформа заблокирована, экстендеры представляются
обычными сервисами на правдоподобных портах, а клиенты ротируют персоны со
случайной фрагментацией (`connect/net_extender_profiles.go:14-30,50-57`), и
существует транспорт в форме DNS для сетей, откуда наружу выходит только DNS
(`connect/transport_pt.go:18-45`). Это механизмы **пересечения локальных и
региональных файрволов**, ранжированные ниже прямых транспортов
(`connect/transport.go:541-546`).
Это не защиты от анализа трафика, и их никогда не следует так описывать.

**Оператор экстендера.** Держать его может любой, приложение принимает вручную
введённый IP, и по умолчанию подпись не требуется
(`connect/extender/extender.go:57-61`). Экстендер — это TCP-пир, поэтому он видит
IP подключающегося пользователя, и он не может расшифровать то, что пересылает,
потому что сеанс TLS идёт через него к платформе, а не терминируется на нём
(`connect/net_extender.go:51-55`). Поэтому звено экстендера ставит на путь
наблюдателя вашего адреса на первом узле, который не является оператором. Это
размен, который делает конструкция, чтобы провести вас сквозь блокировку, и стоит
быть точным о его величине: звено экстендера не добавляет ни шифрования, ни
анонимности — оно узнаёт то, что ваш интернет-провайдер уже знает, и ничего
больше. В поставляемом клиенте прямые транспорты пробуются наперегонки первыми, а
наборщики экстендеров разворачиваются, когда те не срабатывают
(`connect/net_http.go:675`); клиент, настроенный с собственными экстендерами,
использует их как единственный маршрут (`connect/net_http.go:357,469`).

**Наблюдатель одного конца.** Наблюдение только за клиентской стороной даёт «этот
пользователь подключён к URnetwork» и профиль байтов/таймингов. Наблюдение только
за выходом провайдера даёт назначения и профиль байтов/таймингов без
прикреплённой личности.

**Наблюдатель обоих концов.** См. §8. Это тот противник, которого URnetwork не
побеждает.

## 5. Противник: оператор

Оператор аутентифицирует клиентов, выбирает и ранжирует провайдеров, авторствует
контракты, распространяет публичные ключи и несёт пакеты на ретранслируемых
путях. Для этого раздела исходите из того, что он враждебен или принуждён.

**Что он видит без всяких дополнительных усилий:** ваш аккаунт; ваш адрес в
момент подключения; город/регион/страну, выведенные из этого адреса
**локальным** запросом к базе — `server/ip.go:201` открывает встроенную
`mmdb/ip-ipinfo.mmdb` с диска, и `ip.go` не делает вообще никаких сетевых
вызовов, так что адрес никогда не передаётся сервису геолокации, — и
сохраняемые на каждое подключение
(`server/model/network_client_location_model.go:1392-1414`); какие провайдеры вас
обслуживали; счётчики байтов; и на стандартном пути — адреса назначения и байты
пакетов внутри туннеля, хотя те обычно уже находятся внутри собственного HTTPS
назначения.

**Что он хранит.** Записи о подключениях и аутентификации держат односторонний
хеш с ключом от окружающего блока /29 (IPv4) или /56 (IPv6), а не адрес
(`server/ip.go:46-67`). Точность, которая важна аудитору: это единый
общепроцессный **pepper** из хранилища, мемоизированный один раз, без соли на
строку и без какого-либо механизма ротации во всём репозитории
(`server/ip.go:40-44`); пространство ключей IPv4 — 2^29 блоков, поэтому любой,
кто держит pepper, может обратить его перебором. Секретность pepper и есть всё
свойство безопасности. Подсистема `/verify` использует /48 для IPv6, а не /56
(`server/ip.go:74-85`; `server/model/verify_model.go:111`). Исходный порт
хранится в открытом виде рядом с хешем (`server/db_migrations.go:1934`). Строка
аудита создания аккаунта записывает peppered-хеш и порт, никогда не сырую
`ip:port` (`server/model/network_model.go:961-975`), а строки аудита удаляются
через 180 дней (`server/model/audit_model.go:984`, запланировано в
`server/taskworker/taskworker.go:57,253`).

**Чего не существует, чтобы быть сохранённым.** Ни назначения, ни хоста, ни URL,
ни SNI, ни порта, ни домена нигде в реестре передач нет — grep по полному файлу
миграций на эти термины возвращает ноль попаданий
(`review/verified/PRIVACY-ENFORCEMENT.md` §1.2). HTTP-логирование пропускает
список разрешённых ровно из пяти заголовков (`server/http_log.go:15-29`),
закреплённый тестом. Ни одна метрика Prometheus не несёт IP, хост, назначение,
порт или идентификатор клиента; из 39 объявленных метрик только три размечены, и
каждая метка — ограниченный enum
(`review/verified/PRIVACY-ENFORCEMENT.md` §2.5). Загрузки логов из вкладки
поддержки сливаются в `io.Discard`
(`server/controller/log_file_controller.go:13-16,76`); сохраняются только
метаданные.

### 5.1 Как долго всё это хранится и куда уходит

Сроки хранения обеспечиваются плановыми зачистками в `server/taskworker`, а не
политикой. Окна ниже прочитаны из констант, к которым обращаются эти зачистки,
и они короче, чем дала бы основания ожидать политика конфиденциальности —
которая не называет вообще никакого срока хранения.

| Данные | Хранится | Чем обеспечено |
|---|---|---|
| Строки подключений и висящие на них город/регион/страна, задержка и скорость по каждому подключению | **8 часов** | `taskworker/work/network_client_work.go:84`; удаление тем же запросом каскадно затрагивает `network_client_location` (`model/network_client_model.go:2295-2320`) |
| Клиент верхнего уровня в простое (ни аутентификации, ни подключения) → помечается неактивным | **30 дней** | `TopLevelClientIdleExpiration` (`model/network_client_model.go:2289`) |
| Неактивный клиент → жёсткое удаление, с каскадами | **+30 дней** | `NetworkClientReapAfterDeactivate` (`:2269`) |
| Итого неиспользуемое устройство, от начала до конца | **~60 дней** | два предыдущих окна цепочкой |
| Завершённые контракты | **7 дней** | `taskworker/work/subscription_work.go:161` |
| Строки аудита | **180 дней** | `model/audit_model.go:984` |

Два замечания, которые нужны аудитору. Окно простоя **2026-07-18 было ужато с
90 дней до 30**; сама константа несёт своё обоснование, а устаревший
комментарий в `taskworker/work/network_client_work.go:83` по-прежнему говорит
90 — самый вероятный источник этой цифры, если вы встретите её где-то ещё. И
`RemoveLocationLookupResults` ставится в расписание каждый цикл, но это
**no-op** — и её тело, и её вызов модели закомментированы, и такой таблицы не
существует. Это рудиментарная заглушка, а не незачищенные данные; локация по
каждому подключению удаляется вместе со строкой подключения, как выше.

**Геолокация никогда не покидает машину.** Запрос города/региона/страны читает
встроенную базу `mmdb/ip-ipinfo.mmdb` с локального диска (`server/ip.go:201`),
а `ip.go` не содержит HTTP-клиента и не делает никаких сетевых вызовов. Ни один
адрес не отправляется сервису геолокации, и никакая персональная информация не
передаётся никакой третьей стороне. Единственные третьи стороны в таблице
сторон этого документа держат данные, которые пользователь вручает им сам,
выбирая этот путь: DoH-резолверы, до которых через туннель доходят собственные
DNS-запросы пользователя, и платёжные процессоры, у которых пользователь
регистрируется, чтобы ему выставляли счета.

**Известные пробелы в позиции «мы не логируем ваш адрес», не зависящие от
уровня подробности логов.** `server/http.go:415,455` конструируют `http.Server`
без `ErrorLog`, поэтому стандартная библиотека Go пишет
`http: TLS handshake error from <client IP>` в продакшен-stderr на каждом
полуоткрытом соединении — ровно та дыра, которую `proxy/http.go:176,250`
закрывает через `ErrorLog: discardLog`. Присланный клиентом SNI логируется на
уровне ERROR (`server/tls.go:98,195`), сырой адрес вызывающего — в
`server/proxy/proxy_device.go:274`, а идентичность пира WireGuard — в
`server/proxy/server.go:699`. Пока это не исправлено, «мы никогда не логируем ваш
IP» — не то заявление, которое URnetwork может сделать, и этот документ его не
делает. Полные детали: `review/verified/PRIVACY-ENFORCEMENT.md` §2.4.

**Что контролирует оператор и что легко упустить.** Обнаружение провайдеров
целиком на стороне оператора: `FindProviders2` — единственный живой эндпоинт
(`server/api/api.go:67`), платформа оценивает и ранжирует кандидатов
(`server/model/network_client_location_model.go:2250-2254,2430-2447`), а клиент
принимает тот ранжированный набор, который ему дают. Единственный контроль на
стороне потребителя — список блокируемых локаций (`server/api/api.go:73-75`). У
клиента нет способа проверить, что предложенные ему провайдеры независимы друг от
друга или от оператора.

## 6. Провайдеры, которых держит оператор, и сговор оператора с провайдером

### 6.1 Может ли оператор держать провайдеров?

**Да, и ничто в системе это не помечает и не предотвращает.** Раздача открыта для
любого без аттестации и стейка, и никакого понятия провайдера первой стороны,
официального или принадлежащего оператору, нигде в коде нет — поиски по `server`,
`connect` и `sdk` на такое понятие не возвращают ничего. Провайдер, которого
держит оператор, был бы неотличим от провайдера участника.

В сочетании с пунктом §5 о том, что оператор ранжирует и возвращает набор
провайдеров, это означает, что оператор может, в принципе, поместить собственных
провайдеров в окно пользователя. Ни проверки разнообразия на стороне клиента, ни
аттестации независимости, ни опубликованного измерения флота внешней стороной нет.
Существующие противовесы настоящие, но частичные: окно держит несколько
провайдеров сразу — окно качества из 2–6 (жёсткий потолок 12) и окно скорости из
1–2 (жёсткий потолок 4), оба живут в профиле по умолчанию
(`connect/ip_remote_multi_client.go:138-158`), — а провайдеры удаляются по
нездоровой статистике, обнаружению чёрной дыры, провалу пинга и ротации по
времени жизни канала (`:37-54`, `:610-616`). Это ограничивает, сколько видит один
провайдер. Это не ограничивает набор провайдеров, выбранный одной стороной.

### 6.2 Что оператор и провайдер реконструируют вместе, по режимам

Предположим, что оператор и один или несколько провайдеров в вашем окне делятся
тем, что каждый держит.

| | Ретранслируемый стандартный | Ретранслируемый запечатанный | Прямой |
|---|---|---|---|
| Ваша личность (аккаунт, почта/кошелёк/платёж) | оператор | оператор | оператор |
| Ваш реальный IP в момент подключения | оператор | оператор | оператор, **и** провайдер напрямую |
| Назначения, которые вы посещали | оператор (внутренние пакеты) **и** провайдер | **только провайдер** | провайдер |
| Связь между двумя | тривиально — одна сторона уже держит и то и другое | контракт: `audit_contract_event` спаривает идентичность клиента и провайдера на контракт (`server/db_migrations.go:234-251`) | тривиально |
| Результат | полная атрибуция браузинга для всего, что несли сговорившиеся провайдеры | полная атрибуция браузинга для всего, что несли сговорившиеся провайдеры | полная атрибуция браузинга, плюс провайдер знает ваш адрес без посторонней помощи |

**Честный вывод: запечатанный сеанс не защищает от сговора оператора с
провайдером.** Он убирает *независимую* способность оператора читать ваш трафик;
он не мешает провайдеру, который уже видит ваши назначения, передать их
оператору, который уже знает, кто вы. Запись контракта, соединяющая двоих, — не
утечка, а тот учёт, на котором работает сеть.

Что запечатывание всё же покупает против этого противника — это ограничение
охвата: с ним включённым собственный вид оператора не содержит назначений,
поэтому сговор требует сотрудничества конкретных провайдеров, которые несли
конкретный трафик, в то время, когда он нёсся, — а не запроса к записям, которые
оператор держит один. Это значимая разница в усилиях и в том, до чего может
ретроспективно достать принуждённое раскрытие (§10). Это не иммунитет, и никакое
устройство трёх сторон, где одна выбирает других, иммунитета не даёт.

**Структурный ответ конструкции, который не развёрнут.** Проводной протокол и
клиент поддерживают цепочки из дополнительных провайдеров-посредников —
`MaxMultihopLength = 8` (`connect/connect.go:13,214-233`), `IntermediaryIds` в
`CreateContract` (`connect/protocol/transfer.pb.go:1516`), приём на клиенте в
`connect/ip_remote_multi_client_api.go:296-312`, серверная проводка в
`server/controller/connect_controller.go:560`. Живой эндпоинт обнаружения никогда
не заполняет это поле — `FindProvidersProvider` конструируется ровно в двух
местах, и ни одно его не задаёт
(`server/model/network_client_location_model.go:3186-3190,3296-3303`), — поэтому
работающая сеть назначает цепочки длиной один. Цепочки провайдеров — возможность
протокола, а не свойство развёртывания, и цитировать число 8 как счёт узлов было
бы вводящим в заблуждение.

## 7. Ключи идентичности провайдеров и может ли оператор подменить один

Это раздел, который больше всего стоит атаковать, потому что ответ неудобен, и
исходники говорят это сами.

### 7.1 Выпуск и привязка

Каждый `connect.Client` — клиент провайдера и каждый из оконных клиентов
пользователя — держит пару ключей Ed25519, сгенерированную внутри процесса через
`ClientKeyManager` (`connect/transfer_key.go:76-100`, единственный вызов
`ed25519.GenerateKey` в трёх деревьях). Он долгоживущий там, где семя
сохраняется и перезагружается, — а это клиент провайдера, — и свежий на каждый
процесс там, где его нет, — а это оконные клиенты; §9 покрывает разницу и почему
она важна. Приватная половина никогда не покидает процесс; семя неверного
размера — жёсткая ошибка конструирования, а не молчаливая свежая идентичность
(`:84-94`). Публичная половина
публикуется на платформу в управляющем сообщении `ClientKey`
(`connect/protocol/transfer.proto:501-513`) и хранится на сервере в Redis по
ключу `ckey_<clientId>` **без срока истечения** — Redis является источником
истины, SQL-таблицы нет (`server/model/network_client_key_model.go:14-59`).

Поэтому привязка — это `client_id → public key`, а `client_id` выпускает
оператор, и он же держит соответствие. Внешнего якоря нет: идентификаторы не
выводятся из ключей, и нет ни лога прозрачности, ни DHT. Обе альтернативы
зафиксированы в конструкции как рассмотренные и отложенные
(`connect/DESIGNNOTES.md` §3.7, «Deferred alternatives»).

### 7.2 Ротация и отзыв

Повторная публикация перезаписывает — собственный комментарий `SetClientKey`
гласит: ключ «keyed on `client_id` (rotation overwrites)»
(`server/controller/connect_controller.go:810-813`). Запись удаляется, когда
идентификатор клиента подчищается (`network_client_key_model.go:61-71`,
вызывается из `RemoveDisconnectedNetworkClients`). **Ни плановой ротации, ни
срока истечения, ни списка отзыва нет.** Клиент провайдера сохраняет свой
ключевой материал и перезагружает его, поэтому его идентичность стабильна между
перезапусками (`sdk/device_local.go:2874-2889,2926-2935`;
`sdk/local_state.go:418-461`) — и, на Android, намеренно стабильна и через
автоматическую очистку при выходе (§9.2). Клиент без сохранённого семени
генерирует свежую идентичность при каждом старте процесса — так и делают оконные
клиенты пользователя. Смена ключа посреди сеанса отвергается:
`SetPeerClientPublicKey` работает по принципу «первая запись побеждает», а
отличающийся более поздний ключ логируется и игнорируется
(`connect/transfer_encrypt.go:1787-1816`).

### 7.3 Проверка и две защиты

- **Защита 1, привязка подписанного сертификата.** Провайдер подписывает свою
  эфемерную цепочку сертификатов TLS своим ключом Ed25519 и публикует и то и
  другое (`connect/protocol/transfer.proto:486-498`). Платформа прикрепляет
  цепочку, подпись и публичный ключ провайдера к каждому контракту, называющему
  этого провайдера (`:340-368`, `:388-406`). Клиент допускает цепочку, только
  если подпись проверяется под публичным ключом провайдера, а затем сверяет
  сертификат, предъявленный в рукопожатии, с допущенной цепочкой
  (`connect/transfer.go:3930-3934,4006-4018`).
- **Защита 2, доказательство идентичности внутри рукопожатия.** После
  рукопожатия TLS каждая сторона подписывает вывод экспортёра RFC 5705 под меткой
  `urnetwork-sequence-identity-proof` и отправляет его как `EncryptedControl`
  (`connect/protocol/transfer.proto:460-476`). AEAD удерживается от пути
  обёртывания, пока доказательство пира не проверится
  (`connect/transfer_encrypt.go:1492-1498`, `:1719-1756`), поэтому MITM, который
  терминирует TLS на одном звене и переустанавливает рукопожатие на другом,
  производит несовпадающие экспортёры и не может подделать подпись.

Обе защиты проверяют против `peerClientPublicKey` — а это значение берётся из
контракта, авторства платформы (`connect/transfer.go:6119-6129`;
`connect/transfer_encrypt.go:1780-1791`).

### 7.4 Может ли оператор подменить ключ провайдера? Сегодня: да.

**Сказано прямо: оператор, который синхронно подменяет сертификат, подпись
сертификата и `destination_client_public_key`, побеждает обе защиты и может
устроить машину посередине в запечатанном сеансе.** Заметки о конструкции
говорят это собственными словами репозитория (`connect/DESIGNNOTES.md` §3.7):
*«Residual hole: a platform that substitutes cert + signature +
`destination_client_public_key` in lockstep still wins on the cert-binding side;
the closing move is to feed `SetPeerClientPublicKey` from the OOB lookup rather
than the contract. That cross-check is currently log-only.»*

Задуманный закрывающий ход существует и проложен, но не действует.
Неаутентифицированный маршрут `GET /key/<client_id>` отдаёт опубликованный ключ
(`server/api/api.go:123-124`; `server/controller/connect_controller.go:826-849`),
и поставляемый SDK устанавливает против него получателя на каждый сеанс для
каждого оконного клиента и каждого клиента провайдера
(`sdk/device_local_provider.go:398-420`, достигается из
`sdk/device_local.go:3434` и `:90`). При первой установке ключа клиент забирает
ключ пира по другому каналу и сравнивает. При несовпадении он логирует:

```
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`). Ключу из контракта продолжают
доверять (`:520-525`, `:1839-1849`). Повышение «логировать» до «отказывать»
описано и в комментарии настроек, и в заметках о конструкции как следующий шаг
укрепления — а это **замысел конструкции**, а не поставленное свойство.

Три дальнейших уточнения, за которые аудитору стоит держаться:

1. **Канал «вне полосы» является внеполосным относительно конвейера контрактов,
   а не относительно оператора.** Запрос идёт на тот же API-хост оператора
   (`sdk/device_local_provider.go:404`) и читает тот же Redis, в который пишет
   автор контракта. Даже после того как «логировать» станет «отказывать»,
   проверка ловит оператора, который *непоследователен между двумя собственными
   каналами*, а не того, который последователен. Закрытие этого требует якоря,
   который оператор не контролирует, — идентификаторов, выведенных из ключей, или
   лога прозрачности, — обоих зафиксированных как отложенные.
2. **Пропуск так же эффективен, как подмена, и тише.** Проверка сертификата
   пропускается без фиксации, когда доверенный набор пуст, — в том числе когда
   контракты несут пустой `ProvideTlsCertificate`
   (`connect/transfer.go:4010-4016`), — а доказательство идентичности вообще не
   может быть проверено, когда публичный ключ пира отсутствует
   (`connect/transfer_encrypt.go:1706-1708`). В любом случае шифр никогда не
   становится пригодным, и трафик идёт открытым текстом (§2.2) без видимого
   пользователю сигнала. Оператору, который хочет прочитать трафик конкретного
   пользователя при включённом переключателе, не нужно ничего подделывать; ему
   нужно пропустить поле.
3. **Подлинность контракта тоже укоренена в операторе.** Провайдер проверяет
   HMAC контракта, используя секретный ключ раздачи, который провайдер
   генерирует и публикует на платформу
   (`server/controller/connect_controller.go:768-779`;
   `sdk/device_local.go:2866-2872`). Оператор держит копию — именно это и
   позволяет ему вообще авторствовать контракты. Это ожидаемо от учётной
   инстанции, но это значит, что «контракт подлинный» не является
   утверждением, независимым от оператора.

**Что это подрывает и чего не подрывает.** Это не затрагивает слепоту провайдера
к личности, которая не зависит ни от какого распространения ключей. Это всё же
означает, что слепота оператора к содержимому на запечатанном пути в настоящее
время держится на том, что оператор распространяет ключи честно, — а это
*политическое* свойство с криптографическим механизмом, построенным за ним
большей частью, но пока не свойство, которое держится против враждебного
оператора. Любой документ URnetwork, описывающий запечатанный сеанс как
делающий слепоту оператора безусловной, преувеличивает, и настоящий документ
такие формулировки замещает.

## 8. Тайминговая и трафиковая корреляция и глобальный пассивный наблюдатель

### 8.1 Защит от анализа трафика нет. Никаких.

URnetwork не поставляет **ни маскирующего трафика, ни дополнения, ни
перемешивания, ни задержки пакетирования, ни формирования трафика** на
пользовательских данных. Это проверялось исчерпывающе по `connect`, `sdk` и
`server`:

- Нигде не существует генератора мякины, приманок, фиктивного или маскирующего
  трафика.
- AEAD сохраняет длину по построению: длина шифртекста — это nonce + открытый
  текст + тег (`connect/transfer_encrypt.go:305,340`). Ни один protobuf в
  `connect/protocol/` не несёт поля дополнения, а обрамление — это голый
  4-байтовый префикс длины (`connect/message_framer.go:28-31`).
- Объединение на стороне отправки явно без задержки: *«There is no batching wait:
  the sequence only takes a second Pack when it is already queued»*
  (`connect/transfer.go:387-392`). Каждый `jitter` в деревьях — это откат
  переподключения или разброс времени жизни канала, а не формирование трафика.
  Сжатие подтверждений (`connect/transfer.go:272`) задерживает только
  подтверждения, чтобы срезать объём ретрансляционных сообщений.
- Единственное место, где пользовательский трафик вообще темпируется, — это
  ограничитель `WritePacketsPerSecond` транспорта в форме DNS
  (`connect/transport_pt.go:63,232-246`), который существует ради дружелюбия к
  резолверу и применяется только к наименее предпочтительному режиму транспорта.
  Его холостые «прокачивающие» запросы отличимы по длине от несущих данные,
  поэтому он не скрывает ни размера, ни темпа.

**Следовательно: противник, который наблюдает трафик, входящий в ваше
устройство, и трафик, выходящий от провайдеров, которые его несут, может
скоррелировать два по размеру и таймингу. URnetwork от такого противника не
защищает.** Это то же самое утверждение, которое Tor делает о сквозной
корреляции, и здесь оно применимо с меньшим запасом, потому что конструктивная
цель URnetwork — низкая задержка, а это ровно то свойство, которое облегчает
корреляцию. Четырёхзвенный путь с ограниченной задержкой — намеренный размен
против добавленной задержки микснета, и это та сторона размена, которую платит
пользователь. Счёт звена экстендера этого не меняет: тот узел пересылает
зашифрованный сеанс дальше к оператору, не терминируя его, поэтому он не
добавляет слоя, который наблюдателю пришлось бы снимать, и не добавляет
задержки, в которой наблюдатель мог бы вас потерять.

Две вещи иногда принимают за защиты, и они ими не являются. Экстендеры и
формируемые транспорты (`connect/net_resilient.go:112-215`,
`connect/transport_pt.go:18-45`) нацелены на блокировку по DPI, а устойчивый слой
отключает сам себя, как только поток установлен
(`net_resilient.go:102,146-161`), — он никогда не трогает записи фазы данных.
Кеши сеансовых билетов TLS намеренно не делятся между выходными путями, чтобы
сервер не мог связать их через погашенный билет
(`connect/net_tls.go:38-42,99-103`); это несвязываемость билетов, а не анализ
трафика.

Ничто в исходниках не признаёт корреляцию трафика ограничением: поиск по
`traffic analysis`, `timing correlation`, `traffic correlation` и `global
adversary` по всем трём деревьям возвращает ноль попаданий. Внутрирепозиторная
модель угроз (`connect/DESIGNNOTES.md` §3.7) целиком посвящена MITM оператора.
Настоящий документ — первое место, где этот пробел записан.

### 8.2 Окно из нескольких провайдеров не является механизмом против корреляции

Трафик обычно выходит через несколько провайдеров сразу — обычно от трёх до
восьми по двум окнам, — а закрепление по сайтам держит данный сайт на одном
провайдере (`connect/ip_remote_multi_client.go:138-158,1234-1240`). Это
действительно ограничивает, сколько видит любой отдельный выход. Но
конструктивное обоснование окна в исходниках — это надёжность от начала до
конца: смягчение плохих назначений, изменение размера по здоровью, обнаружение
чёрных дыр (`:27-52`), — а единственный комментарий, гласящий «lower affinity is
more private», сидит на `ClientAffinityTimeout` — крутилке, которая
**закомментирована** (`:587-591`, и снова в умолчаниях в `:220`), тогда как
`DestinationAffinity` поставляется в true по соображениям надёжности (`:270`).
Множественная личность выхода — настоящее преимущество, и заявлять его честно;
заявлять его как спроектированную защиту от корреляции кодом не поддерживается.

### 8.3 Глобальный пассивный наблюдатель: вне рамок

Глобальный пассивный противник — способный наблюдать значительную долю каналов
интернета одновременно — **вне рамок, и URnetwork не даёт против него никакой
защиты.** Без дополнения и без маскирующего трафика такой противник коррелирует
потоки через ретранслятор и провайдеров напрямую. Это та же позиция, которую
занимает Tor, и, в отличие от Tor, URnetwork даже не добавляет задержки на узел,
которая подняла бы цену.

Что действительно отличается и что стоит заявить, не раздувая: набор выходов —
это меняющаяся популяция независимых домашних подключений, а не опубликованные
адресные диапазоны одного оператора, поэтому противник должен наблюдать более
широкий и смещающийся набор конечных точек, чтобы покрыть одного пользователя, и
эти конечные точки нельзя перечислить по списку серверов. Это поднимает цену. Это
не меняет исхода для противника, который уже может видеть оба конца.

## 9. Идентификаторы устройств и связываемость

### 9.1 Что провайдер может наблюдать о вас

**`SourceId` контракта — это идентификатор клиента на слот окна, а не ваше
устройство.** `StoredContract.SourceId` — это `client_id` аутентифицированного
вызывающего (`server/controller/connect_controller.go:461-463`; разбирается
провайдером в `connect/transfer.go:6088-6106`). Генератор чеканит свежие
идентификатор клиента, jwt и идентификатор экземпляра для каждой записи окна и
удаляет их при разборке — исходники говорят это прямо: *«The api generator mints
an ephemeral platform client id (with a fresh instance id) for every window entry
and removes it on teardown»* (`connect/ip_remote_multi_client_identity.go:10-15`;
чеканится в `connect/ip_remote_multi_client_api.go:326-345`, освобождается в
`:410,473`).

**Но эти идентичности намеренно переиспользуются до четырёх часов, на каждом
пути — не только на размещённых.** Устройство устанавливает собственное локальное
хранилище идентичностей, если только оно не размещённое
(`sdk/device_local.go:1204-1208`), и сохранённая оконная идентичность
переигрывается против того же назначения, пока не протухнет:
`windowIdentitiesStaleAfter = 4 * time.Hour`
(`sdk/window_identity_store.go:44-52`). Цель — держать NAT-потоки провайдера
живыми через перезапуск (`connect/ip_remote_multi_client_identity.go:17-23`).
Цена в том, что провайдер может видеть, как один и тот же `SourceId` появляется
от вас снова через перезапуски приложения в пределах этого окна. Говорите
«четыре часа», а не «эфемерно».

**Ключ идентичности клиента Ed25519 свежий на каждый оконный клиент, на выходном
пути.** Настройки оконного клиента строятся из свежего
`DefaultClientSettingsWithBufferSize` (`sdk/device_local.go:3434-3437`), который
не несёт `ClientKeySeed` (`connect/transfer.go:166-183`), поэтому
`ClientKeyManager` генерирует новую пару ключей (`connect/transfer_key.go:96`).
Сохранённый снимок окна хранит только идентификатор клиента, jwt и идентификатор
экземпляра — без семени ключа (`sdk/window_identity_store.go:46-52`), поэтому
восстановленная идентичность публикует новый ключ. С выключенным Post Quantum
Encryption доказательство идентичности не предъявляется вовсе
(`connect/ip_remote_multi_client.go:9149-9157`).

**Исходный адрес туннеля — на сеанс и с низкой энтропией.** SDK назначает своему
TUN случайный адрес RFC1918 — «a random 10.x.y.h (RFC1918, DHCP-shaped)»
(`sdk/device_local.go:566-571,1070-1090`) — генерируемый с октетом хоста в 2..254
над наименьшей неконфликтующей `10.a.b.0/24` (`connect/tun.go:323-348`). Он
вычисляется в конструкторе и никогда не сохраняется, поэтому меняется каждый
сеанс. Провайдер **действительно** его видит: это поле источника каждого
туннелированного пакета, и провайдер ключует на нём состояние NAT
(`connect/ip.go:742,1009-1020,1151`). Примерно 253 значения — это слабый
отпечаток на сеанс, а не межсеансовый идентификатор.

### 9.2 Ключ идентичности в роли провайдера долговечен

Если вы также раздаёте своё подключение, ваш **провайдерский** клиент держит
долгоживущую пару ключей Ed25519, чьё семя сохраняется в локальное хранилище
(`sdk/local_state.go:418-461`, `.device_local_key_material`, JSON
`client_key_seed`; применяется в `sdk/device_local_key_material.go:54-70`). В
отличие от оконных клиентов, этот ключ **намеренно сохраняется через
автоматическую очистку при выходе** — комментарий в Android явен: *«Clear a stale
or partial auth state WITHOUT rotating the device identity. The identity key
material is device-scoped, not session-scoped»*
(`android/.../MainApplication.kt:636-647`). Поэтому обновление токена,
восстановление после частичной аутентификации и повторный вход после 30 дней
простоя производят новый `client_id` **и** новый `device_id`, тогда как публичный
ключ Ed25519 остаётся тем же. Ротирует его только явный выход пользователя
(`sdk/local_state.go:639-644`).

Этот ключ публикуется, запечатывается в каждый контракт, называющий этого клиента
назначением (`server/controller/connect_controller.go:413-415,468`), и читаем
кем угодно: `GET /key/<client_id>` не аутентифицирован по замыслу
(`server/api/api.go:123-124`;
`server/controller/connect_controller.go:826-829`). Поэтому любая сторона — не
только провайдер, которого вы обслуживали, — может разрешить любой идентификатор
клиента в его публичный ключ и сгруппировать идентификаторы клиентов, которые
делят один ключ. Для провайдерского аккаунта это долговечная межсеансовая,
меж-`client_id`, меж-`device_id` ручка. Это настоящая цена раздачи, и она нигде
больше в корпусе не задокументирована.

### 9.3 У оператора связывается всё

- `client_id` не отзывается никогда. Собственный комментарий кода: *«client_ids
  are globally unique addressess tantamount to IPv6 / they are never revoked once
  allocated, to preserve security and audit records»*
  (`server/model/network_client_model.go:64-67`). Клиент верхнего уровня
  деактивируется после 30 дней простоя (`TopLevelClientIdleExpiration`, `:2289`)
  и жёстко удаляется через 30 дней после этого
  (`NetworkClientReapAfterDeactivate`, `:2269`), каскадом на строку устройства и
  на `ckey` в Redis (`:2537`).
- `device_id` — **на каждый вход**, а не на железо или установку: свежий чеканится
  с каждым клиентом верхнего уровня (`:332-352`), и код отмечает возникающую
  «identity churn (a fresh device_id per login)» (`:2280`). Оконные клиенты его
  наследуют (`:356-380`), и провайдеру он не отправляется никогда.
- Сетевой токен — это 24-часовой JWT (`server/jwt/by_jwt.go:35-37,188`),
  обновляемый SDK на половине срока жизни — примерно 12 часов — с джиттером
  (`sdk/device_token_manager.go:107-124,165`). **Обновление токена не ротирует
  идентификатор клиента**; та же заявка чеканится заново
  (`server/model/network_client_model.go:102-125`).
- `audit_contract_event` записывает идентичность клиента и провайдера на контракт
  (`server/db_migrations.go:234-251`). `client_reliability` соединяет хеш блока IP
  с ключом напрямую с идентификатором клиента, по блокам: исходный
  четырёхколоночный первичный ключ (`server/db_migrations.go:2083-2106`) был
  позже сокращён до `(block_number, client_address_hash, client_id)`
  (`:2191-2195`), а живая партиционированная таблица использует те же три
  (`server/model/network_client_reliability_partition_model.go:194-195`), причём
  `network_id` выживает как полезная нагрузка индекса (`:63,631-634`). Хранение —
  30 дней (`server/model/network_client_reliability_model.go:42,687-721`; дневные
  партиции сбрасываются целиком).
- `network_client.auth_time` — это долговечное «последний раз виден», хранимое 30
  дней (`server/model/network_client_model.go:2269,2289`); строки отключённых
  подключений подчищаются через 8 часов
  (`server/taskworker/work/network_client_work.go:85`).
- Идентификаторы уровня аккаунта, только у оператора: почта или телефон в
  открытом виде в `network_user.user_auth`, полный JWT стороннего IdP для входов
  по SSO, идентификаторы в открытом виде на каждой попытке входа в
  `user_auth_attempt`, адреса кошельков, платёжные строки, стойкое ребро
  «кто кого пригласил» `network_referral` и строки `audit_provider_event`,
  несущие буквальные строки страны, региона и города против `network_id` и
  `device_id`, не привязанные к 8-часовой подчистке подключений
  (`review/verified/PRIVACY-ENFORCEMENT.md` §1.4).
- `User-Agent` логируется списком разрешённых из пяти заголовков
  (`server/http_log.go:15-29`). Это единственная запись из пяти, которая является
  поверхностью для снятия отпечатков.

### 9.4 Мост WireGuard связывает вас между провайдерами

Каждому клиенту WireGuard выделяется стабильный приватный туннельный адрес из
перемешанного пула в 10 000 000 адресов RFC1918
(`server/model/network_client_proxy_model.go:1034,1038-1118`), записываемый в
конфигурацию как адрес интерфейса (`:756-771`), и он **не нормализуется перед
выходом** — собственный FIXME в исходниках гласит: *«currently the client ipv4 is
threaded to the egress providers / this can allow tracing a single client ipv4
across multiple providers»* (`proxy/wg.go:23-25`). Это приватный адрес, а не ваш
реальный IP, и сайт назначения его никогда не видит. Но каждый провайдер в вашем
окне видит один и тот же исходный адрес, поэтому **сговорившиеся провайдеры могут
определить, что эти потоки принадлежат одному клиенту** — ровно то свойство,
которое окно из нескольких провайдеров в остальном даёт. В сочетании с
фиксированным резолвером `1.1.1.1` (§3.2) и отсутствием запечатанного сеанса на
этом пути мост WireGuard — наименее приватный способ пользоваться сетью.
Приложения и SDK — самый приватный.

## 10. Юридические требования

**Юрисдикция.** BringYour, Inc. — корпорация штата Делавэр
(`docs/legal/ur.xyz/terms.md:16,25`) с почтовым адресом в Сан-Франциско
(`docs/legal/terms.md:269-271`). Условия ur.io устанавливают применимое право и
подсудность в округе Харрис, Техас (`docs/legal/terms.md:315-321`); условия
ur.xyz устанавливают Делавэр (`ur.xyz/terms.md:223-229`). Всё это США, и всё это
достижимо американским принудительным процессом, включая процесс, который
приходит с приказом о неразглашении. Условия заявляют, что оператор «may monitor
and disclose information where required to do so by law or government order»
(`docs/legal/terms.md:204-206`).

**До чего достаёт требование к оператору.** В убывающем порядке
деанонимизирующей силы: кто такой аккаунт — почта или телефон в открытом виде,
JWT стороннего IdP для входов по SSO, адреса кошельков и платёжные записи,
которые привязываются к настоящей личности через Stripe, Apple, Google Play или
ончейн `tx_signature`; когда он подключался и из какого города, на каждое
подключение; какими провайдерами пользовался и на сколько байтов; и хеш с ключом
от блока /29 или /56, из которого он подключался, плюс исходный порт в открытом
виде. Третьи стороны держат больше: платёжные процессоры, названные в §1, держат
личность, которую оператор никогда не хранит, а Solana `tx_signature` разрешается
в кошелёк в публичной цепочке.

**До чего требование не достаёт, потому что этого не существует.** В реестре
передач нигде нет поля назначения, хоста, URL, SNI, порта или домена
(`review/verified/PRIVACY-ENFORCEMENT.md` §1.2). Загруженные логи поддержки
отбрасываются по получении. Записи браузинга, которую можно было бы выдать, нет
ни на одном пути, запечатанном или нет, — это сильнейшее структурное свойство в
этом документе, и оно держится независимо от того, ведёт ли себя кто-либо
правильно.

**До чего требование достаёт на будущее.** Всё вышесказанное — про хранимые
записи. Требование, которое принуждает к будущему поведению, — другое дело, и
§7.4 — то место, где оно кусается: оператор, принуждённый перехватить конкретного
пользователя, мог бы сегодня подменить или пропустить ключевой материал
провайдера и читать трафик этого пользователя даже при включённом Post Quantum
Encryption, причём единственным сигналом была бы строка лога устройства.
Запечатанный сеанс поднимает цену ретроспективного раскрытия. В нынешней форме
он не сопротивляется принуждённому оператору, действующему вперёд.

**Удаление аккаунта частично.** `RemoveNetwork` — это написанный вручную список
удалений, а не каскад базы данных (`server/model/account_model.go:108-260`). Он
удаляет строки `network_user` и их записи аутентификации (пароль, SSO, кошелёк,
сид-фраза), строку `network` и запись в индексе имён и планирует отписку Stripe.
Он не удаляет `account_payment`, `stripe_customer`,
`apple_subscription_transaction`, завершённые строки `solana_payment_intent`,
`network_referral`, строки устройств и строки аудита; те устаревают по
собственным расписаниям — события аудита через 180 дней
(`server/model/audit_model.go:984,1018`) — или, для завершённых ончейн-платежей,
не устаревают вовсе (`server/model/solana_payment_intent_model.go:386-400`
удаляет только намерения с null `tx_signature`). Удаление к тому же *записывает*
строку: событие `AuditEventTypeNetworkDeleted`, ключуемое на удалённый
`network_id` (`account_model.go:255-257`). Формулировка политики
конфиденциальности «delete their account and associated personal information»
(`docs/legal/privacy.md:69`) шире того, что делает код.

**Ни warrant canary, ни отчёта о прозрачности, ни опубликованного процесса для
правоохранителей нет.** Проверено как отсутствующее по `docs/` и исходникам
сайта ur.io. Единственный указатель на юридический процесс где бы то ни было
направляет вручающего процессуальные документы за делавэрским
зарегистрированным агентом в Отдел корпораций штата Делавэр
(`docs/legal/ur.xyz/terms.md:262`); `security@ur.io` очерчен отчётами об
уязвимостях (`docs/legal/vdp.md:42`), а `notice@ur.io` — общий адрес для
договорных уведомлений. Собственные сравнительные документы URnetwork уже
уступают в этом конкурентам, которые такое публикуют, и настоящий документ это
повторяет, а не смягчает.

**Политика конфиденциальности молчит там, где должна быть конкретной.** Она
заявляет, что «all personal information that we collect from and about users is
limited to e-mail address or telephone number» и что сбор происходит «directly
from you when you provide it» (`docs/legal/privacy.md:29-37`).

Более ранняя редакция этого раздела называла это недоописанием на том
основании, что код хранит больше категорий, чем называет политика. Эта
формулировка отозвана 2026-08-09 как неверная. Всё остальное, что
инвентаризирует настоящий документ, — это либо данные аккаунта, которые
пользователь создаёт, регистрируясь или платя (ключ связи со Stripe существует
потому, что кто-то согласился, чтобы ему выставляли счета), либо вовсе не
персональная информация: исходный порт и хеш блока с ключом, который оператор
не обращает. Может ли хеш с ключом в принципе быть обращён его собственным
держателем — реальная инженерная оговорка: §5 её называет, и отсутствующая
ротация — настоящая слабость; но значение, которое никто не запрашивает, — не
категория персональной информации, которую политика не объявила.

То, чего политике действительно не хватает, — иное и более узкое: **ни срока
хранения, ни заявления о логировании, ни раздела о правоохранителях или
раскрытии государству.** Строки *retention*, *log*, *IP address*, *subpoena* и
*legal process* в ней не встречаются. Это важно потому, что дисциплина хранения
существует в коде и строже, чем заявляет политика, — проверенные окна излагает
§5.1. Пользователь, читающий политику сегодня, не может держать оператора ни за
одно из них.

## 11. От чего URnetwork не защищает

Три вещи держатся, и их стоит назвать до списка, который следует далее, чтобы
список читался как граница, а не как приговор. На ретранслируемом пути провайдер
никогда не узнаёт, кто вы, и ни настройка, ни сборка провайдера этого не меняют
(§2). Ни поля назначения, хоста, URL, SNI, порта или домена нигде в реестре
передач нет, поэтому нет записи браузинга, которую можно было бы истребовать,
слить или продать (§5, §10). И каждое из этих утверждений читаемо в публичных
исходниках — поэтому настоящий документ и может быть конкретным о собственных
провалах.

Всё, что ниже, — провалы. Сказаны без хеджирования; каждый пункт раскрыт выше.

1. **Сквозная тайминговая и трафиковая корреляция.** Ни дополнения, ни
   маскирующего трафика, ни перемешивания. Противник, наблюдающий и вашу сеть
   доступа, и провайдеров, несущих ваш трафик, может их скоррелировать. §8.1.
2. **Глобальный пассивный противник.** Вне рамок целиком. §8.3.
3. **Сговор оператора с провайдером.** Оператор знает, кто вы, провайдер знает,
   куда вы ходили, а запись контракта их соединяет. Запечатывание сужает то, что
   оператор держит один; оно не ломает соединения. §6.2.
4. **Принуждённый или враждебный оператор, подменяющий или пропускающий
   ключевой материал провайдера.** Сегодня перекрёстная проверка, которая это
   поймала бы, логирует и продолжает. §7.4.
5. **Молчаливое понижение запечатанного сеанса — ПРИ ВЫКЛЮЧЕННОМ Post Quantum
   Encryption.** В этом режиме любой провал (рукопожатия, доказательства
   идентичности, отсутствующего ключа) откатывается к открытому тексту без
   видимого пользователю указания. **Исправлено для режима, который этого
   просит, 2026-08-10:** при включённом переключателе клиент работает с отказом
   в закрытом состоянии и отказывается отправлять или принимать прикладные
   данные открытым текстом. Что переживает оба режима — это отсутствующий
   индикатор: ни одно приложение не показывает, запечатано ли конкретное
   соединение. §2.2.
6. **Злонамеренный провайдер, вмешивающийся в незашифрованный трафик.** Открытый
   HTTP пропускается без изменений; провайдер находится в позиции враждебной
   точки доступа. §3.
7. **Компрометация конечной точки.** Вредоносное ПО, скомпрометированная ОС,
   враждебное браузерное расширение или кто угодно с вашим разблокированным
   устройством. Ничто в сетевом продукте этого не адресует.
8. **Идентификация на стороне назначения.** Cookies, входы в аккаунты, снятие
   отпечатков браузера и поведение аккаунта идентифицируют вас перед сайтами,
   которые вы посещаете, независимо от того, как пришли пакеты. Смена адреса
   выхода не делает вас анонимным для сервиса, в который вы вошли.
9. **Личность на платёжном рельсе.** Карты и рельсы магазинов приложений
   помещают вашу личность у процессора, даже если оператор её не хранит. USDC в
   блокчейне оставляет публичную `tx_signature`.
10. **Sybil-провайдеры.** Раздавать может любой, без аттестации и стейка. Одна
    сторона может держать много провайдеров, включая оператора.
11. **Отсутствие запечатывания на путях браузера и прокси.** У браузерного
    расширения нет настройки Post Quantum Encryption, и на этих путях клиента
    запускает оператор. §2.4.
12. **Межпровайдерская связываемость на мосту WireGuard.** Один стабильный
    туннельный адрес на каждого провайдера в вашем окне. §9.4.
13. **Долговечная ручка идентичности для любого, кто также раздаёт.** Ключ
    Ed25519 провайдерского клиента переживает ротацию client-id и device-id по
    замыслу, а `GET /key/<client_id>` не аутентифицирован, поэтому любая сторона
    может сгруппировать идентификаторы клиентов, которые делят ключ. §9.2.
14. **Трафик, который локальная сеть видит во время окна DNS при запуске.**
    Задокументирован в исходниках как принятая цена. §3.2.
15. **Всё, что нашёл бы сторонний аудит.** Ни одного не проводилось ни по
    протоколу, ни по серверному коду.

## 12. Что этот документ установить не смог

Неустановленное утверждение в модели угроз — это утверждение, которого не должно
быть и в документации. Эти пункты открыты.

- **Что логирует входной балансировщик.** Конфигурация заголовков nginx есть в
  дереве (`xops/.../connect/ingress.yaml:9`), а служба connect ограничивает темп
  по хешированному адресу (`server/connect/transport_rate_limit.go:65-80`), но
  собственная конфигурация access-логов nginx не проверялась. Любое сквозное
  утверждение «мы не сохраняем ваш адрес» зависит от неё.
- **Достигают ли долговременного хранилища негейченные строки логов из §5,
  несущие адрес и SNI.** Они пишутся в stderr; куда идёт stderr в продакшене и
  как долго он хранится — вопрос развёртывания, на который этот обзор исходников
  ответить не может. Мониторинговый тейлер действительно выскребает шаблоны
  `ip:port` из строк лога и переиспускает их в находки
  (`server/monitor/tailer.go:136,328,333`), что является свидетельством, что как
  минимум часть этих строк читается системами, которые их сохраняют.
- **Переписывает ли серверное прокси-устройство WireGuard источник пакета перед
  передачей пакетов провайдерам.** Стабильный туннельный адрес и вид оператора на
  реальную публичную конечную точку (`server/proxy/wg_handoff.go:56-67`) оба
  проверены; полный путь через `server/proxy/proxy_device.go` и `OpenProxyDevice`
  не был прочитан из конца в конец (`review/verified/ARCHITECTURE.md`,
  непроверяемый пункт 3).
- **Может ли разрешение имён на пути браузера когда-либо происходить локально.**
  Расширение по умолчанию настраивает прокси HTTPS CONNECT, который резолвит
  удалённо, а разрешение имён SOCKS идёт через DoH-набиратель туннеля
  (`connect/tun.go:1155-1175`). Поведение прокси-DNS в каждом браузере при каждой
  конфигурации не тестировалось.
- **Фактическая продакшен-подробность логов.** `BY_LOG_V` по умолчанию 0
  (`server/env.go:41-49`), что подавляет гейченное `V(1)` логирование назначений
  на клиенте и сервере, — но единственный манифест развёртывания в дереве ставит
  его в `2` (`xops/gitops-unused/.../api/deployment.yaml:47-48`), в каталоге с
  названием `gitops-unused`. Считайте подробность логов флагом времени
  выполнения, а не структурной гарантией.
- **Независимость флота.** Оператор публикует живые счётчики городов и стран из
  собственной агрегации по подключённым в данный момент действительным
  провайдерам (`server/model/network_client_location_model.go:1607-1641`), и
  локация выводится оператором из подключения, которое он наблюдает, а не
  объявляется провайдером (`review/verified/ARCHITECTURE.md` §6.4). Но ни одна
  независимая сторона не измеряла флот извне, и ничто не проверяет, что
  провайдеры, предложенные конкретному клиенту, независимы друг от друга или от
  оператора. Механизм проверяем в исходниках; популяция третьей стороной сегодня
  непроверяема.
- **Привязка ключа идентичности при создании аккаунта.** Конструкция заявляет
  ожидание, что «the (ClientId, public key) binding is registered at account
  creation» (`connect/transfer_key.go:20-30`). Поставляется же клиент,
  публикующий свой ключ в контролируемый оператором Redis и получающий его
  обратно через контролируемый оператором API. Существует ли более сильная
  регистрация операционно, установить из исходников не удалось; исходите из
  того, что нет.
- **Использует ли каждый путь отправки устройства оконного клиента.** Эфемерное
  ключевание оконного пути проверено (§9.1). Клиент верхнего уровня устройства
  несёт долговечное семя и включает шифрование безусловно
  (`sdk/device_local_provider.go:90-98`), поэтому любая отправка, которая едет
  через клиент верхнего уровня — отношения сетевых пиров, обратные пути
  компаньонов, — подписывается стабильным ключом и несёт стабильный `client_id`
  верхнего уровня. Эти пути не перечислялись исчерпывающе. Читайте «выходные
  идентификаторы эфемерны» как очерченное путём оконного клиента.
- **Заполняются ли `Roles` и `Principal` когда-либо для обычных потребительских
  клиентов.** Это назначаемые оператором строки, запечатываемые в подписанные
  байты контракта и устанавливаемые только для `ProvideMode_Network`
  (`connect/protocol/transfer.proto:408-415`;
  `server/controller/connect_controller.go:474-481`). Свидетельств, что
  приложения их заполняют, не найдено, и не каждый вызывающий был проверен. Если
  бы они когда-либо заполнялись для потребительского трафика, они были бы
  первоклассными межсеансовыми идентификаторами, видимыми провайдеру.
- **Сохранность ключевого материала на немобильных хостах.** `ClientKeySeed`
  упоминается только из `sdk/local_state.go`,
  `sdk/device_local_key_material.go`, `sdk/device_local.go` и `sdk/cgo/`. Хосты,
  которые их не вызывают, получают свежий ключ на процесс; поведение Linux,
  Windows, расширения и серверного прокси не проверялось по отдельности.
- **Намеренна ли техасская подсудность в Условиях ur.io**, при делавэрском
  юрлице, калифорнийском адресе и делавэрской подсудности в условиях ur.xyz. Ни
  один документ в дереве этого не объясняет. Это вопрос к юристам, а не находка.

## 13. Сообщение о проблемах

Уязвимости: `security@ur.io` и политика раскрытия на
[ur.io/vdp](https://ur.io/vdp). Поправки к настоящему документу, включая
несогласие с любым вердиктом в нём, приветствуются тем же путём — независимая
находка, которая противоречит утверждению отсюда, полезнее самого утверждения.
