# Проверка автономного WebRTC `/voice` через codex-proxy

Переносимый запуск: команды ниже выполняются из корня комплекта, не из каталога этой страницы. Подготовка описана в [AUDIO-README.md](</experiments/voice-ai/archive/c/DOCS/voice-systems-textbook-2026-10-02/voice-systems-textbook/AUDIO-README.md>).

Дата: 30 сентября 2026, Europe/Moscow. Область — отдельный Python-клиент,
который использует существующую сеть acodex. Desktop, CLI /voice и app-server
не запускались; production-конфигурация gateway не менялась.

## Результат

Разговор через proxy воспроизведён именно как WebRTC V3. Получены финальные
реплики обеих сторон и аудиоответ. HTTPS signaling и WSS sideband прошли
через HTTP CONNECT/MITM proxy, а WebRTC-аудио прошло по UDP вне этого proxy.
Положительный результат не был получен заменой V3 на обычный Realtime WebSocket.

Под «мимо proxy» понимается обход нашего прикладного HTTP gateway. Системные
NAT/VPN и физический маршрут пакетов после хоста отдельно не исследовались.

## Живые пробы

Вход — собственный `~/Documents/raven.flac`, перекодированный ffmpeg в WAV
PCM16 mono 48 кГц. Локального распознавания не было. Выбран голос cove;
наличие других имён в каталоге не считается их живой проверкой.

| Наблюдение | Первая проба, 22:14 MSK | Итоговый повтор, 22:50 MSK |
| --- | --- | --- |
| POST `/backend-api/codex/realtime/calls` | 201, route=proxy | 201, route=proxy |
| WSS `/v1/live/<call_id>` | 101, route=proxy | 101, route=proxy |
| CONNECT до chatgpt.com:443 и api.openai.com:443 | оба 200 | оба 200 |
| TCP peer relay | 178.20.41.87:18081 | 178.20.41.87:18081 |
| Выбранный WebRTC peer | UDP 135.220.40.201:3478 | UDP 135.220.40.201:3478 |
| RTP sent / received | 735 / 650 | 665 / 551 |
| Декодированные / ненулевые сэмплы | 599040 / 143935 | 525120 / 115631 |
| Длина финального user / assistant текста | 96 / 48 символов | 98 / 43 символа |
| UDP sendto attempts к выбранному peer | 839 | 754 |
| WAV | PCM16 mono48k, 12,48 с | PCM16 mono48k, 10,94 с |
| Размер WAV | 1198124 байта | 1050284 байта |
| Полное ffmpeg-декодирование | exit 0 | exit 0 |
| Основной auth.json до/после | неизменен | неизменен |
| Relay handlers после close | 0 | 0 |

UDP audit считает вызовы sendto до выполнения syscall. В них могут входить
ICE/STUN, DTLS и RTCP, не только RTP. Поэтому эти числа не обязаны совпадать
со счётчиком packets_sent. Доставку и пригодность медиа подтверждают вместе
selected ICE, RTP в обе стороны, decoded audio и финальные реплики сервиса.

IP/порт peer — результаты этих запусков, не постоянные endpoint-настройки.
Они не прописаны в клиенте. Расшифрованный аудиофайл не сохраняет сетевой Opus:
это PCM WAV, полученный после декодирования RTP.

## Частные артефакты проверки

Рабочий каталог создан `mktemp -d`, права 0700:
`локальный источник

- `input.wav` — нормализованный вход.
- `answer.wav`, `routes.json` — первая проба.
- `answer-final.wav`, `routes-final.json` — итоговый повтор.

Аудио и JSON имеют права 0600; они не включены в учебник или Git. JSON содержит
IP/порты и временные метки, но не Bearer, Basic, call ID, SDP, cookies, аудио
или содержимое личных реплик. Перед публичной передачей журнал всё равно
нужно просмотреть. Временные файлы могут быть удалены обслуживанием системы;
инструкция не зависит от их дальнейшего наличия.

## Как установлено разделение маршрутов

`RoutedClient` передаёт proxy в aiohttp явно: наличие NO_PROXY не может сделать
этот запрос прямым. Но это только настройка. Дальше `ConnectRelay` наблюдает
реальный TCP peer, ответ на CONNECT и объём переданного TLS-потока. Aiohttp
trace отдельно сообщает реальный endpoint-статус. Вместе они подтверждают,
что клиентские HTTPS/WSS вошли в выбранный gateway.

aiortc не получает HTTP proxy. После подключения читается nominated ICE pair,
а CPython audit наблюдает sendto к этому же UDP peer. RTP и успешный аудиоответ
подтверждают работающую медиаветку вне CONNECT relay. Это не Wireshark-запись
и не серверный packet capture; payload не перехватывается.

Журнал намеренно не приравнивает CONNECT 200 к успеху endpoint: proxy может
принять CONNECT, после чего целевой HTTPS вернёт 401/403/429. Такой отказ
попадёт в отдельное client_response/voice_failed. Автоматического direct
fallback нет.

## Авторизация и источник знаний о gateway

<a id="proxy-context"></a>

Исторический чат "Codex Proxy Setup", ID `01a0a9a2-bcdd-7523-8145-dfc1997b1a96`,
прочитан для восстановления смысла acodex, отдельного marker-home и общего
Desktop OAuth. Затем действующий `~/.local/bin/acodex` изучен readonly с
редактированием credential-bearing URL только в выводе. Wrapper повторно
использует его env-only режим, не парсит shell как текст и не запускает CLI.

`proxy.rb` извлечён readonly из сохранённого локального образа
`codex-proxy-transfer:1.1.113-487b1b3`, image ID
`sha256:aa62cfa9eec3fe93095546731ec795f2cee6cb66b4c43ec8ad55e6d6aed42116`.
Контейнер для чтения файлов не запускался и затем был удалён; образ не менялся.
Источник показал handle_connect, forward_websocket_request и подстановку
Bearer/account ID в substituted_header_value. Marker 0000 обрабатывается
отдельным bridge для responses/alpha search, не voice.

Это свидетельство поведения сохранённого образа 1.1.113, а не текущего source
production gateway. Из клиентских 201/101 нельзя установить его активный
email, token fingerprint или дальнейший исходящий proxy. Server logs и
конфигурация active account в этой задаче не читались и не переключались.
Приёмка доказывает рабочий разговор через существующий gateway, не идентичность
конкретного удалённого аккаунта.

## Автоматические проверки

35 проверок в proxy-каталоге прошли: 16 core-регрессий и 19 проверок маршрутов.
В числе последних — целый локальный V3-цикл с настоящими TLS CONNECT,
WebSocket Upgrade, Opus/DTLS-SRTP, captions и WAV; реального сервиса в нём нет.

Отдельно покрыты:

- отказ 407 и мёртвый proxy без прямого fallback;
- whitelist target и отмена незавершённого CONNECT;
- HTTPS и WSS при NO_PROXY=*;
- повторное использование одного CONNECT к одному origin и два CONNECT к двум origin;
- явный direct sideband только по диагностической опции;
- TLS CA rejection и WSS redirect refusal;
- Basic только в CONNECT, отсутствие secrets/payload в журнале;
- приватная атомарная публикация без перезаписи;
- неправильный WAV до сети, readonly auth и независимость скопированного каталога;
- mute/unmute, сбой устройства, captions, отказ делегации и освобождение peer/http.

После живых проб сетевой контракт не менялся. Добавленная защита cleanup вокруг
замороженного core проверена отдельными случаями speaker.abort и WSS.close с
ошибкой: даже при них HTTP закрывается, фоновые задачи отменяются и устройство
получает close. Весь локальный V3 end-to-end повторён на итоговом коде.

DOM-модель JavaScript прошла: семь глав, вложенные deep links, focus/scroll,
history events, возврат к исходной схеме и копирование команд. Это проверка
алгоритма, не настоящих кликов, клавиатуры и layout в браузере.

Повтор существующих аудио-наборов тоже прошёл: TTS demos — 26, voice probes — 7,
Realtime STT — 12, remote TTS — 19, direct WebRTC — 16. В audio-textbook — 12
проверок, включая три новые для proxy-учебника. Вместе с 35 proxy/core это
127 Python-тестов; core-контракты намеренно проверяются и в direct, и в proxy
снимке, поэтому этот итог не означает 127 независимых сценариев.

| Проверка документа | Результат |
| --- | --- |
| 7 глав, уникальные ID, структура и локальные ссылки | PASS |
| 15 блоков SVG ведут к пояснениям и коду | PASS |
| JavaScript syntax и четыре блока Bash | PASS |
| Deep links, Back/Forward events и фокус в DOM-модели | PASS |
| 2 отдельные SVG: XML, render librsvg/ffmpeg и просмотр PNG | PASS; явного обрезания блоков/подписей не обнаружено |
| Оригинальный учебник и его DOM-навигация | PASS, прежние тесты не сломаны |
| Настоящий browser layout, mobile, print, console и keyboard | НЕ ПРОВЕРЕНО; URL-политика |
| Bash syntax, git diff --check, pip check | PASS |

SVG-артефакты проверки находятся в `локальный источник
Это только картинки схем, не скриншоты работающего HTML-приложения.

Команды воспроизведения:

```bash
# Выполняйте из корня распакованного комплекта.
source ./scripts/audio-textbook/env.sh
"$voice_venv/bin/python" -W error::ResourceWarning \
  -m unittest discover -s "$repo/scripts/webrtc-voice-proxy" \
  -p 'test_*.py' -v
env -u NODE_OPTIONS node "$repo/scripts/webrtc-voice-proxy/test-navigation.cjs"
python3 -m unittest discover -s "$repo/scripts/audio-textbook" -p 'test_*.py' -v
```

## Непроверенное и неизменённое

Не проверялись физический микрофон/динамики, нативный CLI /voice через proxy,
живой direct sideband для call чужого gateway-аккаунта, полный AEC, весь
upstream интернет-маршрут, другие голоса, другие HTTP/WSS STT/TTS пути через
proxy. Последние имеют отдельные direct-проверки в основном аудиоучебнике;
успех V3 нельзя переносить на них автоматически.

Desktop/mobile/print HTML UI не проверялся: file URL был заблокирован
URL-политикой; обход через другой браузер, CDP, headless или HTTP-сервер не
применялся. Статическая структура/DOM-модель и отдельный SVG-render не заменяют
эту приёмку.

Не менялись Desktop, proxy/direct launchers, acodex, основной вход, CA,
установленный payload и production gateway. Не делались commit/push/deploy.

Инструкция: [README](</experiments/voice-ai/archive/c/DOCS/voice-systems-textbook-2026-10-02/voice-systems-textbook/scripts/webrtc-voice-proxy/README.md>).
Кликабельные схемы: [отдельный учебник](</experiments/voice-ai/archive/c/DOCS/voice-systems-textbook-2026-10-02/voice-systems-textbook/docs/codex-voice-proxy-textbook.html>).
