Files
obsidian-vault/personal/tech/flaresolverr-taiga.md
T

86 KiB
Executable File
Raw Blame History

FlareSolverr на Тайге + обход блокировок индексеров

🔴 СОСТОЯНИЕ 2026-09-29 (ФИНАЛ СЕССИИ): инфраструктура чиста, НО поиск через CF-индексеры НЕ работает. Prowlarr — IndexerStatus очищен через SQL (было EscalationLevel=9, сутки), health → [], indexerstatus → [], caps всех 6 → 200. Это максимум, что даёт инфраструктура. НО реальный поиск: total=79, отдали только NoNaMe (47) и RuTor (32); RuTracker/Kinozal/Kinozal(M) → 429 через минуты после очистки, в логе FlareSolverrException: Empty cookies returned by FlareSolverr (FlareSolverr.cs:68). 🔴 КОРЕНЬ (найден в этой сессии): у сессии нет куки bb_data — только cf_clearance + bb_guid. Это значит логин RuTracker не выполняется, а не «CF блокирует». Детали — раздел ниже. 🔴🔴 СВЕРКА С КРА́КЕНОМ (2026-09-29): «как на Кра́кене» = ТОЖЕ СЛОМАНО. Alex спросил про капча-солвер на Кра́кене — его там НЕТ, только FlareSolverr. Карантин на Кра́кене идентичен Тайге: RuTracker/Kinozal/Kinozal(M) EscalationLevel=9, DisabledTill=2026-09-27. Обе площадки имеют один и тот же незакрытый дефект. См. раздел «СВЕРКА С КРА́КЕНОМ». 🔴🔴 РЕШЕНИЕ НАЙДЕНО (2026-09-29): свой образ flaresolverr-vnc + noVNC. Капча-солвер невозможен (CAPTCHA_SOLVER официально non-functional), cookie в Prowlarr не вписать → единственный путь: ручной вход в Chrome внутри FS. Собран образ flaresolverr-vnc:latest (Xvfb+fluxbox+x11vnc+noVNC), профиль Chromium на persistent volume, порт 6080 для noVNC. Развёрнуто и работает. 🔴 ДОСТУП ОТКРЫТ (2026-09-29): https://vnc.mallexxx.duckdns.org/vnc.html, логин admin / пароль vnc-taiga, роут в Caddy (TLS выпущен, проверен → 200). Порт 6080 наружу НЕ проброшен — доступ через Caddy, роутер не нужен. Alex вошёл и видит Chrome (проверено). Осталась его часть: логин + капча RuTracker, затем проверка выживания cookies. См. раздел «РЕШЕНИЕ». Radarr/Sonarr/Lidarr — health чистый; warning-и про индексе́ры живут в БД Prowlarr, не у них. Lidarr — SkyHook работает, musicCategory=lidarr применён, поиск релизов отдаёт 43 FLAC. ⚠️ Открыто: RuTracker — автоматический логин невозможен (нужен Alex, см. ниже); RuTor — no results in the configured categories (на стороне трекера). Контейнер flaresolverr (v3.5.2), порт 8191, сеть media_net, shm_size: 1gb, LOG_LEVEL=warning, PROXY_URL=socks5://vless-proxy:1080. Compose — /mnt/RED_2TB/docker/arr/docker-compose.yml (6-й сервис). Бэкапы: ~truenas_admin/docker-compose.yml.bak-20260928-flaresolverr, ~/backup-indexers-20260928-flaresolverr.json, ~/prowlarr.db.bak-20260928-032611 (перед SQL-очисткой). Связка: personal/plans/flaresolverr-taiga-deploy, personal/plans/lidarr-taiga-and-indexer-unblock, family/projects/media-toolbox-taiga, personal/tech/prowlarr-flaresolverr-cloudflare, family/how-to/arr-stack-kraken.

✅ РЕЗУЛЬТАТ (поиск «matrix» через /api/v1/search, замер 28 сен до капчи)

TOTAL 256
  RuTor      100   ← был живой
  RuTracker   50   ← ВПЕРВЫЕ с 2026-05-20
  NoNaMe      50   ← ВПЕРВЫЕ
  Kinozal     50   ← ВПЕРВЫЕ
  Byrutor      6

⚠️ Это исторический замер, не текущее состояние. На момент ~15:20 RuTracker ещё логинился и давал 50 результатов. Позже трекер стал отдавать капчу (см. раздел про RuTracker), и RuTracker/Kinozal уходят в карантин волнами → в поиске сейчас меньше трекеров. Смотреть indexerstatus перед тем, как повторять этот замер.

GET /api/v1/health → [], GET /api/v1/indexerstatus → [] на момент замера. Было 3/6, стало 6/6 (по выдаче — 5 трекеров; Kinozal (M) — зеркало Kinozal с magnet-приоритетом, в общую выдачу дублируется).

🔑 ГЛАВНОЕ ОТКРЫТИЕ: FS-СЕССИЯ НЕ НАСЛЕДУЕТ PROXY_URL

Самый неочевидный пункт всей задачи. Симптом был ложный: Prowlarr писал blocked by CloudFlare Protection, а лог FS — мгновенную ошибку за 7 секунд (не таймаут!).

Причина: PROXY_URL (глобальный прокси) применяется к Chrome в момент создания сессии. Сессия, созданная до появления PROXY_URL, остаётся без прокси навсегда — в ней Chrome получает net::ERR_NAME_NOT_RESOLVED (DNS-блок!). Prowlarr шлёт запросы с параметром session, и global-прокси её не перебивает.

Пруф — прямой вызов с сессией, созданной ДО прокси:

request.get kinozal + session:"prowlarr" (старая)
  → {"status":"error","message":"Error solving the challenge.
     Message: unknown error: net::ERR_NAME_NOT_RESOLVED (chrome=152.0.7977.82)"}

Тот же запрос с сессией, созданной ПОСЛЕ PROXY_URL → Challenge solved! 200.

Имя сессии Prowlarr — prowlarr (обнаружено через sessions.list).

Фикс: удалить устаревшую сессию — sessions.destroy; Prowlarr пересоздаёт её сам при следующем запросе, уже с рабочим PROXY_URL.

docker exec prowlarr sh -c 'curl -s -X POST http://flaresolverr:8191/v1 \
  -H "Content-Type: application/json" \
  -d "{\"cmd\":\"sessions.destroy\",\"session\":\"prowlarr\"}"'

⚠️ Правило на будущее: после ЛЮБОЙ правки PROXY_URL в compose убить сессию prowlarr, иначе старые сессии продолжат ходить без прокси и индексера будут «blocked by CloudFlare» при живом FS. ✅ При обычном перезапуске FS (watchtower/reboot) проблема НЕ повторится — сессия создастся заново уже с PROXY_URL.

🔑 ВТОРОЕ ОТКРЫТИЕ: FlareSolverr сам идёт через прокси (PROXY_URL)

На Кра́кене два механизма независимы: Cloudflare лечится FlareSolverr, DNS-блока нет. На Тайге блоков ДВА (DNS + Cloudflare), и FlareSolverr обязан ходить через vless-proxy — иначе он сам получит NXDOMAIN и не поможет.

Решение: глобальная env-переменная FlareSolverr PROXY_URL=socks5://vless-proxy:1080.

⚠️ Имя переменной — PROXY_URL, НЕ HTTP_PROXY/HTTPS_PROXY (проверено по DeepWiki FlareSolverr §4.2 «Proxy Configuration»). Формат со схемой обязателен (socks5://host:port); без схемы urllib.parse не разберёт. Приоритет: request > session > global. PROXY_USERNAME/PROXY_PASSWORD не нужны — vless-proxy без авторизации.

Почему срабатывает именно global: Prowlarr обращается к FS с параметром session, а при наличии сессии request-прокси игнорируется → применяется global (PROXY_URL).

⚠️ ПИТФОЛЛ: requestTimeout = 60 с МАЛО, поднят до 180

Дефолтный requestTimeout прокси-элемента FlareSolverr в Prowlarr = 60 с. Решение CF-челленджа на /forum/login.php RuTracker и на kinozal занимает 80–100 с → Error solving the challenge. Timeout after 60.0 seconds в логе FS, Prowlarr отдаёт 500 InternalServerError [POST] at http://flaresolverr:8191/v1, в UI — «blocked by CloudFlare».

Фикс: requestTimeout = 180 (PUT /api/v1/indexerproxy/2). Проверка: docker logs flaresolverr → Challenge solved!, а не Timeout.

Замеры времени челленджа (kinozal.me/browse.php через FS): 78 с холодный, 20 с прогретый (CF-клэренс привязан к IP прокси), POST-логин — 97 с.

⚠️ ПИТФОЛЛ: параллельный testall устраивает гонку за Chrome

Prowlarr тестирует все индексеры одновременно, а у FlareSolverr один Chrome. При testall челленджи сыпятся таймаутами; если тестировать по одному (sequential) — проходят. Не верить результату параллельного testall — гонять по одному. Дополнительно: curl без --max-time виснет, если FS не отвечает — всегда ставить лимит.

⚠️ ПИТФОЛЛ: тег нужен И на прокси, И на индексере — и на ОБА прокси

Индексерам за DNS-блоком нужны два прокси-элемента и два тега:

Прокси id Тег Назначение
vless-socks5 1 proxy (id=1) обход DNS-блока провайдера
flaresolverr 2 flaresolverr (id=2) обход Cloudflare

Значит на индексер вешаются оба тега: tags=[1,2]. Один прокси не закрывает оба блока. Фактически навешено: id 1, 4, 5, 6 → [1,2]; id 2, 3 → [1] (RuTor/Byrutor живут на одном прокси).

⚠️ ПИТФОЛЛ: PUT /api/v1/indexer/N → 400, нужен ?forceSave=true

Prowlarr при PUT сразу тестирует индексер. Если FS в этот момент не прошёл (таймаут/гонка) — HTTP 400 с validationFailures. Именно так «потерялись» теги после первого PUT: они откатились к [1].

Рабочий путь: PUT /api/v1/indexer/N?forceSave=true → 202 (сохраняет, тест пропускает). Тест гонять отдельно, по одному. То же для /indexerproxy/N.

⚠️ ПИТФОЛЛ: Auth Failed ≠ неверный пароль

RuTracker Auth Failed в indexer/testall Prowlarr не доказывает неверный пароль. Проверять логин отдельным request.post на /forum/login.php через FS и смотреть final_url + длину HTML: залогиненная страница форума ≈ 250 KB и форма login_username в ней ВСЁ РАВНО присутствует (шаблон) — по подстроке диагноз ставить нельзя. Скрипт-проверка: /tmp/rt_login.py (см. personal/tech/flaresolverr-taiga).

⚠️ ПИТФОЛЛ: health Radarr/Sonarr кэшируется — нужен restart

RemotePathMappingCheck (и подобные) в health Radarr/Sonarr кэшируется: создание недостающих каталогов /storage/Downloads/{radarr,sonarr} ошибку не снимает. Нужен docker restart radarr sonarr, чтобы проверка прогналась заново. PUT через API кеш health тоже не сбрасывает.

Практические замечания

  • Логи FS пусты при LOG_LEVEL=warning — это правильно: на info он пишет login_password трекеров в plaintext. Не поднимать уровень ради «посмотреть».
  • sessions.create → ok, version 3.5.2 — быстрый smoke-тест живости FS.
  • Прямая проверка FS из prowlarr:
    docker exec prowlarr sh -c 'curl -s --max-time 150 -X POST http://flaresolverr:8191/v1 \
      -H "Content-Type: application/json" \
      -d "{\"cmd\":\"request.get\",\"url\":\"https://rutracker.org/forum/login.php\",\"maxTimeout\":130000}"'
    # ожидается: {"status":"ok","message":"Challenge solved!", ...}
    
  • sleep 35 после старта контейнера — Chrome поднимается ~30 с; раньше этого :8191 может не отвечать.
  • shm_size и sysctls — в блок сервиса compose, не в environment.
  • ERR_NAME_NOT_RESOLVED в ответе FS = сессия без прокси (см. открытие выше), а не «FS сломан».
  • Ключ API в -H ломается многоуровневым экранированием (ssh → docker → sh). Рабочий приём: printf "X-Api-Key: <KEY>" > /tmp/h.txt внутри контейнера, затем curl -H @/tmp/h.txt.
  • python3 внутри контейнера prowlarr НЕТ — разбирать JSON хостовым python3 (на Тайге есть python3, нет curl и sqlite3).

Состояние индексеров после деплоя (2026-09-28, финал)

id Индексер Теги Статус
1 RuTracker [1,2] ✅ valid (был красный с 2026-05-20)
2 RuTor [1] ✅ valid
3 Byrutor [1] ✅ valid
4 NoNaMe Club [1,2] ✅ valid
5 Kinozal [1,2] ✅ valid
6 Kinozal (M) [1,2] ✅ valid

✅ ЗАКРЫТО: Kinozal — «blocked by CloudFlare» при живом FS

Самая долгая часть задачи. Симптом маскировался под таймаут, причина оказалась другой.

Что замерено (история диагностики):

  • Прямой socks5 к kinozal.me/browse.php → 403 Just a moment за 0.9 с (CF реален).
  • FS request.get kinozal → Challenge solved! 200: 78 с холодный, 20 с прогретый.
  • FS request.post kinozal/takelogin.php → Challenge solved! 200, 97 с.
  • Prowlarr: blocked by CloudFlare Protection, в логе FS — падение за 7 с (не таймаут!).

Ложный след: HTTP request failed: [500:InternalServerError] [POST] at http://flaresolverr:8191/v1 и Error solving the challenge. Timeout after 60/120/180 seconds — выглядело как «челлендж дольше таймаута». Поднятие requestTimeout 60→120→180 не помогло.

Настоящая причина: FS-сессия без прокси (см. главное открытие выше). Сессия prowlarr, созданная до появления PROXY_URL, падала с ERR_NAME_NOT_RESOLVED за 7 с, Prowlarr интерпретировал это как «CF блокирует» и уходил в retry → карантин.

Фикс: sessions.destroy сессии prowlarr → пересоздана с прокси → Kinozal (5) и Kinozal (M) (6) прошли с первого раза ({}).

Что исключено (не тратить время повторно): ресурсы FS в норме (710 MB / 21 GB, shm 1 GB, CPU 0.68%); канал прокси нормальный (kinozal через socks5 0.6 с, 908 KB/s); внутренний таймаут Prowlarr в config.xml не настраивается.

Compose-сервис (вставлен в /mnt/RED_2TB/docker/arr/docker-compose.yml)

  flaresolverr:
    image: ghcr.io/flaresolverr/flaresolverr:latest
    container_name: flaresolverr
    restart: unless-stopped
    environment:
      - LOG_LEVEL=warning          # НЕ info — info пишет пароли трекеров в plaintext
      - BROWSER_TIMEOUT=60000
      - TZ=Asia/Novosibirsk
      - PROXY_URL=socks5://vless-proxy:1080
    ports:
      - "8191:8191"
    networks:
      - media_net
    shm_size: 1gb                  # Кра́кен: 64 MB убивал Chrome
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=1

Порядок деплоя: бэкап compose → правка локально (не sed на сервере) → scp → cp → cd /mnt/RED_2TB/docker/arr && docker compose up -d flaresolverr. ⚠️ docker compose up -d только из своего каталога, иначе name-conflict (грабля Кра́кена).

⚠️ Swap на Тайге = 0 (/proc/swaps пуст). На Кра́кене мёртвый swap + 64 MB /dev/shm убили Chrome в crash-loop. Здесь обошлось без swap: RAM 21 GB, доступно 5.6 GB, FS держит 710 MB, CPU 0.68%. Swap не трогали — не нужен при таком объёме памяти.

История

  • 2026-09-26 — разведка под Lidarr. Найдено: DNS-блок провайдера (NXDOMAIN на 4 трекера), vless-proxy не в media_net. Прокси-маршрут для Prowlarr починен в тот же день (3/6 индексеров). Остаток — Cloudflare на RuTracker/Kinozal. personal/plans/lidarr-taiga-and-indexer-unblock
  • 2026-09-28 — FlareSolverr развёрнут по задаче Alex «ставь как на Кра́кене + прокси-контейнер». Решено последовательно: (1) FS сам ходит через vless-proxy (PROXY_URL) — двойной обход DNS+CF; (2) requestTimeout 60→180; (3) теги [1,2] на все CF-индексеры; (4) sessions.destroy сессии prowlarr — последний блокер (сессия была создана до PROXY_URL). Итог: 6/6 индексеров, health [], поиск 256 результатов. RuTracker впервые valid за всё время существования стека (с 2026-05-20).
  • 2026-09-28 (поздний вечер) — добивка Radarr/Sonarr/Lidarr до чистого health. Закрыто: docker restart radarr sonarr (+lidarr) → RemotePathMappingCheck ушла, health Radarr/Sonarr → []. Снят карантин Kinozal/Kinozal(M) в Prowlarr тестом полным объектом ({"id":N} → 400 всегда). Новые питфоллы: test требует полный объект; 429 на caps = карантин Prowlarr → каскад во все arr; proxyBypassFilter Lidarr без prowlarr → таймауты к Prowlarr из-за собственного прокси-конфига. Остаток: RuTor — несовпадение категорий (не блок), и 1 warning в Lidarr до перезапуска с новым bypass.

🔴 ОТКРЫТО 2026-09-28 (финал сессии): RuTracker отдаёт КАПЧУ на логин

Хронология одного вечера — пароль один и тот же, результат разный:

Время Проверка len HTML Итог
~15:20 request.post /forum/login.php 251 792 ✅ залогинен
~16:40 request.post /forum/login.php 13 335 ❌ форма вернулась

Пароль в Prowlarr при этом не менялся (сверено: username/password из /api/v1/indexer/1 совпадают с локальной копией). Причина смены результата — трекер выдаёт капчу: в HTML найден блок cap_code + метка Код:. Пишется это в лог Prowlarr как NzbDrone.Core.Indexers.Exceptions.IndexerAuthException: RuTracker Auth Failed и Invalid Credentials for RuTracker [https://rutracker.org/forum/tracker.php?nm=...&f=...].

🔴 Правильный признак «залогинен/нет» — РАЗМЕР страницы, а не подстрока.

  • ~250 KB = залогиненный форум.
  • ~13 KB = форма логина (вернулась).

Подстрока login_username присутствует в обоих случаях (HTML-шаблон + скрытая форма быстрого входа) — по ней диагноз ставить НЕЛЬЗЯ, это дало ложное «Auth Failed» двумя диагнозами подряд.

Обходного пути в Prowlarr нет: форма индексера RuTracker принимает только username/password — поля для cookie-сессии (bb_data) не существует, вписать живую сессию из браузера нельзя.

Что делать (нужен Alex): зайти на rutracker.org браузером с этого же исходящего IP (через тот же vless-proxy), ввести логин+пароль и пройти капчу один раз. После успешного входа капча обычно снимается. Если не поможет — сменить пароль на трекере и вписать новый в Prowlarr (Indexers → RuTracker), либо поискать у RuTracker обход капчи (в Cardigann-определении поддержки нет).

⚠️ Следствие: RuTracker в карантине в Prowlarr, поэтому его caps отдаёт 429 → каскад 429:TooManyRequests в Radarr/Sonarr/Lidarr. Это нормальное поведение, лечится только восстановлением логина. См. питфолл про 429 ниже.

🔴🔴 ИТОГОВЫЙ КОРЕНЬ (2026-09-29): логин RuTracker НЕ ВЫПОЛНЯЕТСЯ — нет куки bb_data

⚠️ Раздел ниже («капча привязана к сессии») оказался ЛОЖНЫМ СЛЕДОМ. Он оставлен для истории диагностики, но вывод в нём неверен — читать этот раздел вместо него.

Что измерено в финале (одна серия замеров, /tmp/fs_persist_session.sh):

сессия rt-login-test:  len=254 673 (форум)   cookies=0
сессия prowlarr:       len=99 735  (НЕ форма!) cookies=2   ← [cf_clearance, bb_guid]
новая сессия + POST:   len=12 346  (форма)   cookies=2   ← [bb_guid, cf_clearance]
новая сессия + GET→POST: len=12 350 (форма)  cookies=2

🔴 Ключевое: в cookies НЕТ bb_data. bb_data — кука авторизации RuTracker. Есть cf_clearance (CF пропускает челлендж) и bb_guid (гость), но bb_data отсутствует → логин не состоялся, страница — форум для неавторизованного.

Что видит Prowlarr (точная строка из лога, главная улика сессии):

NzbDrone.Core.IndexerProxies.FlareSolverr.FlareSolverrException:
  "Empty cookies returned by FlareSolverr."
  at NzbDrone.Core.IndexerProxies.FlareSolverr.FlareSolverr.PostResponse(...) in FlareSolverr.cs:line 68
[Warn] RuTracker: Unable to connect to indexer [https://rutracker.org/forum/tracker.php?nm=radiohead].
[Error] Cardigann: An error occurred while processing indexer feed. [https://kinozal.me/browse.php?...]

⚠️ Это НЕ «blocked by CloudFlare Protection». Сообщение Empty cookies returned означает, что FS прошёл челлендж (Challenge solved!), но не вернул авторизационные cookies — потому что логина не было. Две ошибки выглядят похоже, но причины разные:

Сообщение Что значит
blocked by CloudFlare Protection CF не пройден (сессия без прокси, ERR_NAME_NOT_RESOLVED)
Empty cookies returned by FlareSolverr CF пройден, но логина нет → нет bb_data

🔴 Почему свежая сессия не логинится. sessions.create + POST → форма (12 KB). Prefetch GET login.php в той же сессии → POST → тоже форма (12 KB). То есть предварительный GET не помогает — это не «капча к cookie». Просто RuTracker не пропускает автоматический POST-логин из свежего Chrome-профиля FS.

Обходного пути в Prowlarr нет: форма индексера принимает только username/password. Поля для cookie (bb_data) не существует — подставить живую сессию из браузера нельзя.

Что нужно от Alex (единственный путь)

  1. Зайти на rutracker.org браузером через тот же исходящий IP (vless-proxy), ввести логин+пароль, пройти капчу; либо
  2. Отключить капчу для аккаунта в профиле на форуме — тогда автоматический логин заработает;
  3. Проверить, что в браузере появляется кука bb_data (DevTools → Application → Cookies). Если она есть и трекер не требует капчу — логин из Prowlarr должен пойти.

⚠️ Не считать Empty cookies returned ошибкой прокси или CF — это отсутствие логина. Диагностика: docker exec flaresolverr curl -s -X POST http://localhost:8191/v1 \ -H "Content-Type: application/json" -d '{"cmd":"request.get","url":"https://rutracker.org/forum/index.php","maxTimeout":100000,"session":"prowlarr"}' \ | jq -r '.solution.cookies[].name' → если нет bb_data, логина нет.


🔴🔴 РЕШЕНИЕ (2026-09-29): свой образ flaresolverr-vnc + noVNC для ручной капчи

Alex поставил задачу: «ароматизировать процесс насколько возможно» — уведомление в ТГ, вход браузером через тот же туннель, установка куки обратно. Разбор по фактам:

Что из исходной идеи не работает (проверено):

Идея Вердикт Причина
Мой браузер через 3x-ui по тому же туннелю ❌ vless-proxy — клиент, не сервер. И cf_clearance привязан к IP + User-Agent + TLS-fingerprint — кука моего Chrome не подойдёт чужому Chrome
Установка куки обратно в Prowlarr ❌ Индексер RuTracker принимает только username/password. Поля для cookie нет
Капча-солвер (CAPTCHA_SOLVER) ❌ Официально non-functional. DeepWiki FS: «Currently non-functional». Issue #1452 закрыт без решения — адаптеры удалены из образа
Ручной вход внутри Chrome FS + persistent профиль ✅ Единственный рабочий путь. Капчу решает человек, дальше автомат

🔴 Капчу решает человек физически. Это защита RuTracker, а не дефект конфига. Задача инженерии — сжать участие Alex до одного клика за жизнь cookie.

Проверка 3x-ui / xray (факты):

vless-proxy (xray-клиент):  socks :1080 + http :1081
                            outbound VLESS → vpn.mallexxx.duckdns.org:443 (ws/tls, path /vless)
xray-admin (3x-ui):         панель :54321, внутри слушает 2053, сеть caddy_default (172.16.1.9)
hermes-taiga:               есть, сеть hermes_taiga_net

Свой образ flaresolverr-vnc — собран и развёрнут ✅

Compose-сервис заменён с ghcr.io/flaresolverr/flaresolverr:latest на локальный образ:

  flaresolverr:
    image: flaresolverr-vnc:latest          # ← свой образ
    container_name: flaresolverr
    restart: unless-stopped
    environment:
      - LOG_LEVEL=warning
      - BROWSER_TIMEOUT=60000
      - TZ=Asia/Novosibirsk
      - PROXY_URL=socks5://vless-proxy:1080
    ports:
      - "8191:8191"                          # API FS
      - "6080:6080"                          # ← noVNC
    volumes:
      - /mnt/RED_2TB/docker/arr/flaresolverr-config:/config   # ← persistent профиль
    networks:
      - media_net
    shm_size: 1gb
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=1

Файлы образа — /mnt/RED_2TB/docker/arr/fs-vnc/ (собирать оттуда):

Файл Что
Dockerfile база ghcr.io/flaresolverr/flaresolverr:latest + x11vnc novnc websockify fluxbox supervisor
supervisord.conf 5 процессов: xvfb(:99, 1920x1080x24) → fluxbox → x11vnc(:5900) → novnc(:6080) → flaresolverr
entrypoint-vnc.sh XDG_CONFIG_HOME=/config/chromium-profile, DISPLAY=:99, запуск supervisord

Сборка: cd /mnt/RED_2TB/docker/arr/fs-vnc && docker build -t flaresolverr-vnc:latest . (~215 с).

🔴 ПИТФОЛЛ: FS в headed-режиме (HEADLESS=false) — порт 8191 первый запрос рвёт

supervisord.conf запускает FS с HEADLESS=false — иначе в noVNC нечего смотреть. Следствие: сразу после старта контейнера первый curl к :8191 получает Connection reset by peer. Через ~5–10 с всё работает:

$ curl -s -X POST http://localhost:8191/v1 -d '{"cmd":"sessions.list"}'
curl: (56) Recv failure: Connection reset by peer     ← сразу после старта
# sleep 5
{"status":"ok","sessions":[],"version":"3.5.2"}       ← ✅

Порт слушается (netstat -tlnp → 0.0.0.0:8191), но Chrome в headed-режиме доводится дольше. Проверять живость FS — с ретраем/паузой, не с первого запроса.

🔴 ПИТФОЛЛ: sessions.destroy несуществующей сессии → HTTP 500

{"cmd":"sessions.destroy","session":"whale-clean-test"} на несуществующей сессии отдаёт HTTP 500 InternalServerError, а не «ok/not found». Скрипт проверки должен не падать на этом (обернуть в try) и использовать уникальное имя сессии (whale<timestamp>), иначе повторный прогон ломается на destroy несуществующей.

✅ Сервис работает

flaresolverr Up  0.0.0.0:6080->6080/tcp, 0.0.0.0:8191->8191/tcp
API FS:    {"status":"ok","version":"3.5.2"}
noVNC:     http://localhost:6080/vnc.html  → 200
профиль:   /mnt/RED_2TB/docker/arr/flaresolverr-config/chromium-profile  ← создан

⚠️ Порт 8191 с Mac НЕ проброшен наружу — Connection refused. Все скрипты проверки запускать на хосте Тайги (scp → ssh ... python3), не локально.

Как пользоваться (процедура «ручная капча»)

  1. Открыть https://vnc.mallexxx.duckdns.org/vnc.html → логин admin, пароль vnc-taiga (роут в Caddy, проверен 200). Альтернатива — ssh-туннель на 6080.
  2. Внутри — рабочий стол fluxbox с Chrome (⚠️ Chrome надо запустить, см. питфолл ниже).
  3. Зайти на rutracker.org, ввести логин/пароль, решить капчу.
  4. Cookies лягут в /config/chromium-profile → переживают рестарт контейнера.
  5. FS переиспользует профиль при следующих сессиях → автологин проходит без капчи.

🔴 ПИТФОЛЛ: «cannot set background» + чёрный экран в noVNC

Симптомы, увиденные Alex: подключение проходит, fluxbox ругается cannot set background, меню есть, окна нет — чёрный экран.

Причина. fluxbox/Xvfb работают штатно — это безвредный шум (Failed to read: session.screen0.*, Missing charsets in String to FontSet conversion). Реальная причина пустого экрана: FS запускает Chrome только на время запроса и убивает его. Между запросами в :99 нет ни одного окна — смотреть нечего.

Проверка (на хосте Тайги):

# X-сервер жив?
docker exec flaresolverr sh -c 'ls -la /tmp/.X11-unix/'
# → srwxrwxrwx X99   ✅

# Chrome запущен?
docker exec flaresolverr sh -c 'ps aux | grep chromium | grep -v grep | wc -l'
# → 0  ← вот причина чёрного экрана

Фикс — запустить Chrome вручную в том же display и оставить жить (-d = detached, иначе убьётся вместе с exec):

docker exec -d -e DISPLAY=:99 flaresolverr sh -c \
  "chromium --no-sandbox --disable-gpu --disable-dev-shm-usage \
   --user-data-dir=/config/chromium-profile/chromium \
   --window-size=1280,900 --start-maximized rutracker.org > /tmp/chrome-manual.log 2>&1"
sleep 12
docker exec flaresolverr sh -c 'ps aux | grep chromium | grep -v grep | wc -l'   # → 12

После этого в noVNC видно окно Chromium. Alex подтвердил: «ок, вижу хром».

⚠️ --user-data-dir указывать точно как у FS (/config/chromium-profile/chromium) — иначе профиль раздвоится и куки осядут в другом месте.

Сопутствующий шум (НЕ ошибки):

ERROR:dbus/object_proxy.cc  Failed to call method: org.freedesktop.DBus.NameHasOwner
ERROR:dbus/bus.cc            Failed to connect to the bus: /run/dbus/system_bus_socket
WARNING:zygote_communication_linux.cc  Failed to send GetTerminationStatus message

dbus/zygote ругань есть в любом контейнере без dbus — на работу Chrome в Xvfb не влияет. Отличить «Chrome упал» от «Chrome жив» по EXIT=124 от timeout: 124 = жив (таймаут сработал).

План (оставшиеся шаги)

# Шаг Статус
1 Образ flaresolverr-vnc + noVNC + persistent профиль ✅ выполнено
2 Проверить, что cookies переживают рестарт контейнера ⏳ ждёт ручного входа Alex
3 hermes-taiga: уведомление в ТГ при Empty cookies returned ⏳ не начато
4 Подтвердить со стороны Prowlarr: поиск отдаёт CF-индексеры ⏳ после п.2

Шаг 3 (дизайн). hermes-taiga поллит GET /api/v1/indexerstatus (или грепает лог Prowlarr на Empty cookies returned/429) → при появлении карантина шлёт в ТГ: «RuTracker просит капчу → открой http://<тайга>:6080/vnc.html». Логика простая, ничего в стеке не ломает.

Бэкапы этой сессии:

Файл Путь
compose (до правки образа) /mnt/RED_2TB/docker/arr/docker-compose.yml.bak-20260928-profvol (2723 B)
ПДФ предыдущих правок docker-compose.yml.bak-20260928-flaresolverr, .bak-20260928-lidarr-proxy

⚠️ Опровергнута прошлая версия про /config. Ранее в этом доке записано «на Тайге /config НЕТ» — неверно: volume был (анонимный, от образа), но профиль Chromium жил в ~/.config/chromium внутри контейнера и умирал при пересоздании. Теперь профиль явно смонтирован в /config/chromium-profile через XDG_CONFIG_HOME. Не путать: наличие /config ≠ persistent профиль.


🔴🔴 СВЕРКА С КРА́КЕНОМ (2026-09-29): «как на Кра́кене» = ТОЖЕ СЛОМАНО

Alex спросил «на кракене вроде был капча солвер для этого?». Проверено на живом Кра́кене — капча-солвера там НЕТ, и карантин там тот же. Это снимает главную ложную посылку задачи.

Капча-солвера на Кра́кене нет

docker ps -a | grep -iE "solver|capsolve|2captcha|anti|capmonster"   → только flaresolverr
FS env:  LOG_LEVEL, BROWSER_TIMEOUT, PATH, LANG, GPG_KEY, PYTHON_*  → НЕТ CAPTCHA_SOLVER
IndexerProxies на Кра́кене:
  (1, 'FlareSolverr', '{"host":"http://flaresolverr:8191/","requestTimeout":60}', 'FlareSolverr', ...)

Только FlareSolverr, requestTimeout: 60 (на Тайге уже 180). Никакого CAPTCHA_SOLVER, 2captcha, capsolver. Если капча-солвер где-то и вспоминается — это не Кра́кен.

Карантин на Кра́кене — идентичный Тайге

-- Кра́кен: /srv/dev-disk-by-uuid-49e8f586-.../docker/prowlarr/config/prowlarr.db
SELECT ProviderId, EscalationLevel, DisabledTill FROM IndexerStatus;
1  RuTracker    EscalationLevel=9   DisabledTill=2026-09-27   ← В КАРАНТИНЕ
5  Kinozal      9                   2026-09-27                 ← В КАРАНТИНЕ
6  Kinozal (M)  9                   2026-09-27                 ← В КАРАНТИНЕ
2  RuTor        3                   —
3  Byrutor      0                   —
4  NoNaMe       0                   —

Ровно та же тройка, ровно тот же EscalationLevel=9. То есть на Кра́кене карантин тоже стоит с 27 сентября и не снимался. Список индексеров на Кра́кене — идентичен Тайге (id 1–6, те же имена, тот же impl).

🔴 Вывод для будущих сессий: не строить план на посылке «на Кра́кене работает, значит и на Тайге заработает». На 2026-09-29 обе площадки имеют одинаковый незакрытый дефект авторизации RuTracker. «Как на Кра́кене» ≠ «починить» — на Кра́кене тоже красное.

Что реально отличается: у FS на Кра́кене есть /config

Кра́кен: IMAGE=ghcr.io/flaresolverr/flaresolverr:latest
         MOUNTS= /srv/.../docker-data/volumes/fcca43.../_data => /config
         ENV:    НЕТ PROXY_URL   (на Кра́кене DNS-блока нет)
         /config пуст (ls → total 8, только . и ..)   sessions: []
Тайга:   PROXY_URL=socks5://vless-proxy:1080   /config НЕТ

⚠️ /config пуст и на Кра́кене, и сессий нет (sessions: [], version 3.5.2). То есть persistent Chrome-профиль не используется — гипотеза «профиль с cookies переживает рестарт» опровергнута. Это была ложная версия; не тратить на неё время.

⚠️ /config на Кра́кене объявлен, но ничего не сохраняет → добавлять его на Тайге не нужно. Причина bb_data-отсутствия не в профиле.

Скрипт сверки: /tmp/whale_login_check.py, /tmp/whale_login2.py (запускать на хосте Тайги, порт 8191 наружу с Mac не проброшен → Connection refused).


⚠️ ЛОЖНЫЙ СЛЕД (оставлен для истории): «капча привязана к сессии»

Самый запутанный пункт. Дефект выглядел как «неверный пароль», потом как «капча», на самом деле — капча выдаётся только для «холодных» сессий.

Замеры в одном ходе (скрипты /tmp/fs_session_cmp.py, /tmp/fs_search_check.py):

A: request.post /forum/login.php БЕЗ session      → len=13 335    ❌ форма логина вернулась
B: request.post /forum/login.php С session=prowlarr → len=251 792 ✅ ЗАЛОГИНЕН
C: request.get tracker.php?nm=...&f=... в той же сессии → len=254 745 ✅ выдача
     auth_failed_in_html=False, login_form=False

🔴 Вывод: логин через сессию prowlarr РАБОТАЕТ. Капча отдаётся только новым (безымянным) сессиям — то есть PROXY_URL + переиспользование сессии prowlarr её обходят. Мой одиночный тест без сессии давал 13 KB и порождал ложный диагноз «капча блокирует всё».

Как проверить состояние RuTracker правильно:

# ВСЕГДА с session=prowlarr, иначе получишь ложную капчу
{"cmd":"request.post","url":"https://rutracker.org/forum/login.php",
 "postData":"login_username=...&login_password=...&login=...",
 "session":"prowlarr","maxTimeout":240000}

Признак «залогинен» — len ≈ 250 KB, формы нет. 13 KB = сессия холодная, а не «пароль плох».

⚠️ Не делать вывод о пароле по тесту БЕЗ сессии. Всегда session=prowlarr.

Как снялся карантин RuTracker (рабочая процедура)

health/indexerstatus держат кэш (disabledTill), даже когда трекер уже отвечает. Снятие — тем же тестом полным объектом, что и для Kinozal:

docker exec prowlarr curl -s -H "X-Api-Key: $PK" http://localhost:9696/api/v1/indexer/1 > /tmp/p1.json
docker cp /tmp/p1.json prowlarr:/tmp/p1.json
docker exec prowlarr curl -s -X POST -H "X-Api-Key: $PK" -H "Content-Type: application/json" \
  --data-binary @/tmp/p1.json http://localhost:9696/api/v1/indexer/test
# → {} с HTTP=200  = карантин снят
docker exec prowlarr curl -s -H "X-Api-Key: $PK" http://localhost:9696/api/v1/indexerstatus   # → []
docker exec prowlarr curl -s -o /dev/null -w "%{http_code}" "http://localhost:9696/1/api?t=caps&apikey=$PK"  # → 200

Проверено: {} + 200 → indexerstatus пуст, caps → 200.

🔴🔴 ГЛАВНЫЙ ПИТФОЛЛ ФИНАЛА: IndexerStatus в prowlarr.db — снимается ТОЛЬКО SQL

Самая долгая петля сессии. Симптом: «снимаю карантин тестом → warning возвращается → тест падает заново». Круг не сходился, потому что причина не в прокси, не в FlareSolverr и не в пароле — она в БД Prowlarr.

Пруф — дамп таблицы (sqlite3 на хосте Тайги ЕСТЬ, в контейнере НЕТ):

DB=/mnt/RED_2TB/docker/arr/prowlarr/prowlarr.db
sqlite3 "file:$DB?mode=ro" \
  "SELECT Id, ProviderId, InitialFailure, MostRecentFailure, EscalationLevel, DisabledTill FROM IndexerStatus;"
ProviderId  Индексер     EscalationLevel  DisabledTill
5           Kinozal      2                2026-09-28 10:08
6           Kinozal M    2                —
4           NoNaMe       0                —
2           RuTor        0                —
1           RuTracker    9                2026-09-29 09:46   ← СУТКИ
3           Byrutor      0                —

EscalationLevel = степень эскалации (0–9), 9 — максимум, DisabledTill = +24 ч. Пока запись есть: caps отдаёт 429 → Prowlarr не пробует снова → фоновый health-чек Radarr/Sonarr/Lidarr бьёт в 429 → warning каскадом во все четыре сервиса. Отсюда парадокс «health Radarr пуст, а warning про индексе́ры живёт» — он живёт не у Radarr, а в БД Prowlarr.

🔴 POST /indexer/test карантин НЕ снимает, а ПРОДЛЕВАЕТ. Наблюдалось вживую: DisabledTill 15:44 → 21:45 после одного теста. Каждая «попытка починить тестом» удлиняла блокировку — это и была петля. Записи эскалируют, а не сбрасываются.

Рабочий путь снятия (как на Кра́кене) ✅ ВЫПОЛНЕНО 2026-09-29

# 1. БЭКАП через .backup, НЕ cp (БД в WAL-режиме)
DB=/mnt/RED_2TB/docker/arr/prowlarr/prowlarr.db
sqlite3 "file:$DB?mode=ro" ".backup /home/truenas_admin/prowlarr.db.bak-$(date +%Y%m%d-%H%M%S)"
# → /home/truenas_admin/prowlarr.db.bak-20260928-032611  (581 632 байт)
# 2. стоп → удалить строки карантина → старт
docker stop prowlarr
sqlite3 "$DB" "DELETE FROM IndexerStatus;"     # можно без WHERE — чистим все
sqlite3 "$DB" "PRAGMA quick_check;"            # → ok
docker start prowlarr
# 3. проверка
curl -s -H "X-Api-Key: $PK" http://localhost:9696/api/v1/indexerstatus   # → пусто
for I in 1 2 3 4 5 6; do curl -s -o /dev/null -w "idx$I=%{http_code}\n" \
  "http://localhost:9696/$I/api?t=caps&apikey=$PK"; done               # → все 200 ✅

Результат прогона: IndexerStatus → rows=0, quick_check=ok, Indexers = 6 определений на месте, docker start prowlarr поднялся, health → [], indexerstatus → [], все 6 caps → 200. ⚠️ Скрипт: /tmp/clear_indexerstatus.sh (в нём есть контроль «контейнер действительно остановлен» перед DELETE).

🔴 НО это НЕ решает поиск. Через минуты после очистки CF-индексеры снова в карантине, поиск radiohead → total=79, отдали только NoNaMe (47) + RuTor (32). Потому что настоящая причина — отсутствие логина (bb_data), см. раздел «ИТОГОВЫЙ КОРЕНЬ». SQL-очистка снимает симптом (карантин), но не причину: пока логин не проходит, карантин будет возвращаться волнами.

⚠️ Честная оценка метода. Снятие через SQL даёт caps 200 и пустой indexerstatus, то есть выглядит починенным, но реальный поиск в этих индексерах не работает. Приёмка — только GET /api/v1/search?query=X с разбивкой group_by(.indexer), а не caps и не health.

Таблицы: Indexers, IndexerStatus, IndexerProxies, ApplicationIndexerMapping, IndexerDefinitionVersions.

⚠️ Урок об источниках истины. health-эндпоинты arr-сервисов не авторитет о блокировках. На Кра́кене это записано прямо: «GET /api/v1/health и /api/v1/indexerstatus НЕ показывают причины блокировки» — смотреть prowlarr.debug.txt и таблицу IndexerStatus. На Тайге indexerstatus причину показывал, но снятие через API не срабатывало — только SQL.

🔴 ПИТФОЛЛ: Auth Failed = следствие IndexerStatus, а НЕ неверный пароль

Диагноз ставился трижды и дважды был неверным — фиксирую окончательно.

Проверка логина через FS (сессия prowlarr):

login  → len=254 410   ✅
search → len=254 745   ✅   auth_failed_in_html=False   login_form=False

То есть логин живой, а Prowlarr в это же время отдаёт RuTracker Auth Failed.

Правильный признак «залогинен или нет» — РАЗМЕР HTML, не подстрока:

len HTML Что это
~250 KB залогиненный форум ✅
~13 KB форма логина вернулась ❌

Подстрока login_username присутствует в обоих случаях (HTML-шаблон + скрытая форма быстрого входа) — по ней диагноз ставить НЕЛЬЗЯ. Именно это дало ложное «пароль неверен».

⚠️ И капча тут ни при чём. Наблюдалось: «холодная» (свежесозданная) FS-сессия получает форму с cap_code (len=13 338), а уже прогретая сессия prowlarr — залогиненный форум (len=251 792). То есть капча отдаётся не по признаку пароля, а свежим сессиям. Пароль всё время был верный — Alex подтвердил отдельно.

⚠️ Порядок диагностики при Auth Failed (не переставлять):

  1. Проверить логин request.post на /forum/login.php через FS → судить по len (~250 KB = ок).
  2. Посмотреть IndexerStatus в prowlarr.db — если есть строка, это и есть причина.
  3. Только если п.1 показал ~13 KB — вопрос к паролю. Не требовать у Alex новый пароль, не пройдя п.2.

✅ ЗАКРЫТО: пароль RuTracker — рабочий (первый диагноз был НЕВЕРЕН)

Итог 2026-09-28 (позже в тот же вечер): сохранённая в Prowlarr пара (mallexxx / 8 символов) логинится успешно. Проверено request.post через FlareSolverr на /forum/login.php:

final_url = https://rutracker.org/forum/login.php   ← НО форма не вернулась
len       = 251 792                                 ← полная страница форума (~250 KB)
login_form_back = False                             ← форма логина НЕ переоткрылась

login_form_back=False + страница 251 KB = сессия установлена, это залогиненный форум. Скрипт проверки: /tmp/rt_login.py (копия на Тайге; python3 /tmp/rt_login.py "$U" "$P").

🔴 Почему первый диагноз был ложным. Ранее вывод «пароль неверен» сделан по признаку login_username в HTML, но эта строка присутствует и на залогиненной странице (скрытая форма быстрого входа / HTML-шаблон). Признак «форма вернулась» надо проверять по final_url + длине страницы, а не по наличию подстроки login_username — это дало ложное «Auth Failed».

⚠️ Урок: Auth Failed в indexer/testall Prowlarr не означает неверный пароль. Прежде чем требовать новый пароль у Alex — проверить логин отдельным request.post с final_url + len в ответе. Признак-подстрока ненадёжен.

⚠️ Если Alex всё-таки сменил пароль на самом rutracker.org — в Prowlarr лежит СТАРЫЙ (проверено: username len=8, password len=8 через /api/v1/indexer/1), его надо обновить в Indexers → RuTracker, иначе после смены на трекере логин отвалится.

✅ ЗАКРЫТО: Radarr/Sonarr — каталоги категорий созданы

Radarr/Sonarr ждали /storage/Downloads/radarr и /storage/Downloads/sonarr (Transmission пишет плоско в /mnt/storage/Downloads). Каталоги созданы:

mkdir -p /mnt/RED_2TB/storage/Downloads/radarr /mnt/RED_2TB/storage/Downloads/sonarr

В контейнерах видны как /storage/Downloads/{radarr,sonarr}, владелец abc:abc. ⚠️ RemotePathMappingCheck в health кэшируется — после создания каталогов ошибка осталась, нужен docker restart radarr sonarr для перепроверки. См. family/plans/lidarr-taiga-deploy.

✅ ЗАКРЫТО 2026-09-28 (финал): Radarr/Sonarr/Lidarr вычищены

Что Итог
docker restart radarr sonarr ✅ выполнен → RemotePathMappingCheck ушла
Карантин индексеров в Prowlarr (id 5, 6) ✅ снят тестом полным объектом → caps 200
PUT /api/v3/indexer/N?forceSave=true в Radarr/Sonarr ✅ 202
docker restart radarr sonarr lidarr ✅ выполнен (+ отдельный docker restart lidarr после правки bypass)
health Radarr ✅ [] — чисто
health Sonarr ✅ [] — чисто
health Lidarr ✅ дефект proxyBypassFilter закрыт; остаток — общий карантин Prowlarr
Библиотека Lidarr 1 артист (Radiohead), 23 релиза

✅ proxyBypassFilter расширен и рестарт выполнен. Проверка после рестарта показала: прежний симптом Http request timed out (запрос к Prowlarr уходил в прокси) ушёл — на его месте осталась честная ошибка 429:TooManyRequests от Prowlarr, потому что Kinozal 5/6 снова ушли в карантин (из-за капчи/CF). То есть дефект bypass закрыт, а остаток теперь общий со всей инфраструктурой и лечится снятием карантина в Prowlarr.

Остаток (не блокирует, вне инфраструктуры): RuTor в Radarr/Sonarr — Query successful, but no results in the configured categories. Это не блокировка и не карантин: RuTor не отдаёт результаты в категориях фильмов/сериалов. Проверено тестом полным объектом → caps отдаёт, карантина нет. Оставляем как есть — дефект самого трекера.

⚠️ Карантин имеет свойство возвращаться. После снятия Kinozal 5/6 и RuTracker снова оказались в карантине (карточки disabledTill), потому что их периодически тестирует сам Prowlarr, и тест снова падает (капча RuTracker / CF kinozal). Пока капча RuTracker не пройдена, карантин будет возвращаться волнами — это не регресс инфраструктуры.

  • Приёмка Alex в UI: https://prowlarr.mallexxx.duckdns.org → Indexers → 6 зелёных; https://radarr.mallexxx.duckdns.org / sonarr → System → Health пусто; https://lidarr.mallexxx.duckdns.org → артист ищется.

✅ Lidarr ↔ SkyHook: решено 2026-09-28 (встроенный прокси, НЕ env)

Смежная задача, решена в этой же сессии. Полный разбор — family/plans/lidarr-taiga-deploy.

api.lidarr.audio (метаданные SkyHook) за Cloudflare:

  • прямой запрос из контейнера → 403;
  • через socks5h://vless-proxy:1080 → 200;
  • домен резолвится (2606:4700:3031::ac43:df35) — это чисто CF, НЕ DNS-блок.

⚠️ FlareSolverr здесь бесполезен — он обслуживает Prowlarr, а SkyHook зовёт сам Lidarr.

🔴 ГЛАВНЫЙ ПИТФОЛЛ: env HTTP_PROXY/HTTPS_PROXY Lidarr НЕ ЧИТАЕТ. Проверено: compose-env + рестарт → всё тот же 403. .NET-клиент SkyHook игнорирует env.

Рабочее решение — встроенный прокси-конфиг Lidarr (применяется без рестарта):

# GET /api/v1/config/host  ->  proxyEnabled:false
# PUT  /api/v1/config/host/1  c полями:
#   proxyEnabled:        true
#   proxyType:           "http"
#   proxyHostname:       "vless-proxy"
#   proxyPort:           1081                    # ← HTTP-порт, НЕ 1080 (SOCKS5)
#   proxyBypassFilter:   "localhost,127.0.0.1,vless-proxy,prowlarr,lidarr,radarr,sonarr,transmission,jellyfin,navidrome,caddy,gluetun,taiga"
#   proxyBypassLocalAddresses: true

🔴 proxyBypassFilter ОБЯЗАН содержать prowlarr и ВСЕ docker-имена стека. Дефолт (localhost,127.0.0.1,vless-proxy) не покрывает имя prowlarr, и тогда внутренний запрос Lidarr → Prowlarr заворачивается в прокси и падает Http request timed out, хотя сам Prowlarr отвечает 200. proxyBypassLocalAddresses=true на docker-имена не действует — только на IP-литералы. Отсюда же следствие: после правки bypass нужен docker restart lidarr. Скрипт правки bypass: /tmp/lidarr_bypass.py (GET → patch → PUT → 202 → read-back). Скрипт: /tmp/lidarr_set_proxy.py (на хосте Тайги; там python3 есть, curl — нет).

Результат: lookup?term=radiohead → 10 результатов; артист добавлен; поиск релизов → 23 раздачи FLAC. Библиотека: 0 артистов → 1.

⚠️ Ложный след при диагностике: api.lidarr.audio отдаёт 403 не всегда — при исчерпанном CF-лимите 403 бывает и у curl, что выглядит как «прокси не работает». Различать: заголовок x-rate-limit-remaining присутствует только при живом ответе; и сравнивать прямой vs через прокси запрос в одном ходе.

⚠️ Откат: env-вариант убран из compose (бэкап ~truenas_admin/docker-compose.yml.bak-20260928-lidarr-proxy).

✅ ЗАКРЫТО: Lidarr — путь загрузки настроен (musicCategory)

Отдельный дефект, найден позже в тот же вечер. Вопрос Alex: «Lidarr при добавлении будет качать в музыку?» — ответ был нет, и это подтвердилось фактами.

Было (сломано):

musicCategory        = ""        ← пусто!
musicDirectory       = ""
musicImportedCategory= null

Следствие: Transmission получил бы задачу без категории, положил файл плоско в /mnt/storage/Downloads, а Lidarr ждал бы его в /storage/Downloads/<категория>. Скачивание прошло бы — импорт в библиотеку упал.

Стало (рабочий путь, проверено):

Lidarr musicCategory = lidarr                                   ← PUT ?forceSave=true → 202
Transmission: /mnt/storage/Downloads/lidarr                     ✅ существует (abc:abc)
Lidarr:       /storage/Downloads/lidarr                         ✅ существует
RemotePathMapping: /mnt/storage/Downloads/ → /storage/Downloads/ ✅
rootFolder:   /storage/Music  (978 GB free, accessible)          ✅
Скачается в:  /storage/Music/<Artist>/                           ✅
Поиск релизов: 43 FLAC-раздачи (seeders 6/5/7)                    ✅

🔴 musicCategory — дефолт lidarr (не music!). Проверять по schema (downloadclient/schema → implementation Transmission) и сверять с реально существующим каталогом в Transmission. Radarr по той же схеме использует radarr, Sonarr — sonarr; каталоги /mnt/storage/Downloads/{lidarr,radarr,sonarr} уже созданы.

Скрипт: /tmp/lidarr_category.py (GET downloadclient/1 → правка musicCategory → PUT ?forceSave=true → 202 → read-back). ⚠️ У полей бывает нет ключа value (musicDirectory = '' без value) — не падать на f['value'], использовать f.get('value').

⚠️ monitored=false у добавленного артиста — Lidarr не начнёт качать, пока у артиста не включён мониторинг альбомов (Lidarr → Artist → Monitor).

🔴 ПИТФОЛЛ: test требует ПОЛНЫЙ объект, не {id} → 400

Во всех arr-сервисах (Prowlarr, Radarr, Sonarr, Lidarr) эндпоинт POST /api/vN/indexer/test с телом {"id":N} отдаёт 400 Bad Request. Правильно — отправить полный объект индексера:

curl -s -H "X-Api-Key: $K" http://localhost:PORT/api/v3/indexer > /tmp/idx.json
docker cp /tmp/idx.json radarr:/tmp/idx.json
docker exec radarr curl -s -X POST -H "X-Api-Key: $K" -H "Content-Type: application/json" \
  --data-binary @/tmp/idx.json http://localhost:7878/api/v3/indexer/test
# → {} с http=200 = тест пройден; 400 + validationFailures = реальная ошибка

{} + 200 = успех (не 204, не «пусто»).

🔴 POST /indexer/testall НЕ РАБОТАЕТ в Radarr/Sonarr — отдаёт 400, а Sonarr ещё и подвешивается >300 с (в Prowlarr testall тоже вреден: один Chrome → гонка, см. питфолл про параллельный тест). Единственный рабочий путь — POST /indexer/test по одному, полным объектом. Проверено на всех четырёх сервисах.

🔴 GET /indexerstatus есть ТОЛЬКО в Prowlarr — в Radarr/Sonarr эндпоинт → 404. Карантин в Radarr/Sonarr иначе: смотреть health (warning «Indexers unavailable due to failures») и обнулять PUT /api/v3/indexer/N?forceSave=true.

🔴 ПИТФОЛЛ: 429 на /N/api?t=caps у Prowlarr = карантин → каскад во все arr

GET http://prowlarr:9696/5/api?t=caps&apikey=… → 429 TooManyRequests, пока индексер в карантине. Следствие: Radarr/Sonarr/Lidarr не могут синкнуть этот индексер и показывают Unable to connect to indexer … [429:TooManyRequests]. Настоящая причина — не в них.

Порядок лечения (снизу вверх):

  1. Снять карантин в Prowlarr — POST /api/v1/indexer/test полным объектом (по одному).
  2. Проверить GET /api/v1/indexerstatus → пусто и caps → 200.
  3. Пересохранить проблемный индексер в самом arr: PUT /api/v3/indexer/N?forceSave=true → 202 (сбрасывает FailureCount).
  4. docker restart radarr sonarr lidarr — health пересчитывается только при старте.

🔴 ПИТФОЛЛ: proxyBypassFilter Lidarr без внутренних имён → таймауты к Prowlarr

Самый неочевидный дефект сессии. После включения встроенного прокси Lidarr (proxyEnabled=true) его запросы к Prowlarr ушли в прокси и стали падать Http request timed out, хотя в Prowlarr тот же индексер отвечал 200.

Причина: дефолтный bypass — localhost,127.0.0.1,vless-proxy, в нём НЕТ prowlarr. Имя prowlarr не попадает под proxyBypassLocalAddresses (это не IP-литерал), поэтому внутрикластерный http://prowlarr:9696/… инкапсулировался в HTTP-прокси и таймаутил.

Фикс — дописать в config/host.proxyBypassFilter все внутренние docker-имена:

localhost,127.0.0.1,vless-proxy,prowlarr,lidarr,radarr,sonarr,transmission,jellyfin,navidrome,caddy,gluetun,taiga

Скрипт: /tmp/lidarr_bypass.py (GET → patch filter → PUT /api/v1/config/host → 202 → read-back). ⚠️ После правки — docker restart lidarr.

⚠️ Правило: включая приложению прокси-конфиг, сразу прописать в bypass все имена своего стека. Иначе сервис «уйдёт» в прокси и к соседям — симптом выглядит как «сервер индексера недоступен», а виноват собственный прокси-конфиг.

Общий вывод по прокси на Тайге (три разных механизма!)

На одном хосте понадобились три независимых способа прокинуть трафик через vless-proxy:

Кто Механизм Ключ
Prowlarr (индексеры) SOCKS5-прокси в Prowlarr + FlareSolverr теги [1,2], PROXY_URL у FS
FlareSolverr (Chrome) глобальный прокси образа env PROXY_URL=socks5://... (НЕ HTTP_PROXY)
Lidarr (SkyHook) встроенный конфиг приложения config/host.proxyEnabled (env НЕ работает)

⚠️ Не переносить рецепт одного сервиса на другой. arr-сервисы внешне похожи, но механизм прокси у каждого свой; проверять фактическим запросом, а не по аналогии.

История (финал сессии 2026-09-28)

  • ~15:20 — RuTracker логинится (len 251 792), поиск «matrix» → 256 результатов, health всех arr пуст. Замер зафиксирован в разделе «РЕЗУЛЬТАТ».
  • ~16:00 — Lidarr: /release?artistId=1 → 0. Найдена причина — proxyBypassFilter без prowlarr; внутренний запрос Lidarr→Prowlarr уходил в прокси и таймаутил. Фикс: filter расширен до всех docker-имён (PUT config/host → 202, read-back).
  • ~16:10 — docker restart lidarr. Симптом-таймаут ушёл, но всплыла настоящая причина: 429:TooManyRequests от Prowlarr → Kinozal 5/6 снова в карантине.
  • ~16:20 — карантин Kinozal/Kinozal(M) снят тестом полным объектом → caps 1/5/6 → 200. Radarr/Sonarr: PUT ?forceSave=true → 202, docker restart → health [].
  • ~16:40 — RuTracker перестал логиниться: HTML сжался 251 KB → 13 KB, в форме найден cap_code + метка Код: → диагноз «трекер требует капчу». Пара в Prowlarr не менялась (сверено байт-в-байт с локальной копией) — «пароль неверен» ложный.
  • ~17:00 — Alex сам починил логин на трекере. Проверка выявила ключевое: логин БЕЗ сессии → 13 KB (капча), логин С session=prowlarr → 251 792 ✅. Капча привязана к «холодной» сессии; сессия Prowlarr её обходит. См. раздел «ГЛАВНОЕ ОТКРЫТИЕ ПРО RuTracker».
  • ~17:10 — найден отдельный дефект Lidarr: musicCategory пустая → импорт скачанного упал бы. Исправлено на lidarr (202); сверены каталоги, маппинг, root folder. Поиск релизов: 43 FLAC-раздачи.
  • ~17:20 — карантин RuTracker снят тестом полным объектом → {} HTTP 200, indexerstatus → [], caps idx1 → 200. Prowlarr полностью чист.
  • Итог (~17:20): инфраструктура Тайги чиста и проверена. Открыт 1 дефект — RuTor (no results in the configured categories, на стороне трекера). Остаток-механика: docker restart radarr sonarr lidarr для сброса кэша health со старым RuTracker (команда ждала апрува Alex, в этой сессии не выполнена).

История (продолжение, 2026-09-29)

  • Апрув получен, docker restart radarr sonarr lidarr → выполнен. Health Lidarr стал пустым; Radarr/Sonarr всё ещё держали warning — потому что причина не в них, а в БД Prowlarr (см. ниже).
  • Найдена настоящая петля. Симптом: «снимаю карантин тестом → warning возвращается → тест падает». Дамп IndexerStatus показал EscalationLevel=9, DisabledTill=+24ч у RuTracker (ProviderId=1), плюс карантины у Kinozal 5/6. 🔴 POST /indexer/test карантин не снимает, а ПРОДЛЕВАЕТ (наблюдалось 15:44 → 21:45). Это и была петля — каждая «попытка починить тестом» удлиняла блокировку.
  • Сверка с Кра́кеном (Alex спросил «на Кра́кене работает всё так? у нас точно similar?»). Прочитан family/how-to/arr-stack-kraken: на Кра́кене есть процедура sqlite3 ... "DELETE FROM IndexerStatus" — на Тайге она не выполнялась ни разу. Это было главное расхождение.
  • Выполнено: бэкап ~/prowlarr.db.bak-20260928-032611 (.backup, не cp) → docker stop prowlarr → DELETE FROM IndexerStatus (rows=0, quick_check=ok) → docker start prowlarr. Результат: health → [], indexerstatus → [], все 6 caps → 200.
  • 🔴 НО поиск не заработал. GET /api/v1/search?query=radiohead → total=79, отдали только NoNaMe (47) + RuTor (32). CF-индексеры → 429 через минуты.
  • Найден истинный корень. Лог Prowlarr: FlareSolverrException: Empty cookies returned by FlareSolverr (FlareSolverr.cs:68). Замер cookies сессии: [cf_clearance, bb_guid] — куки bb_data НЕТ, то есть логин RuTracker не выполняется. Свежая сессия + POST → форма (12 KB); prefetch GET login.php + POST → тоже форма. Предварительный GET не помогает.
  • Вывод сессии: инфраструктура (прокси, FS, теги, БД) исправна и вычищена. Блокер вне инфраструктуры — RuTracker не пропускает автоматический логин без капчи. Решение требует Alex: пройти капчу браузером через vless-proxy либо отключить капчу у аккаунта. См. раздел «ИТОГОВЫЙ КОРЕНЬ».

🧠 Уроки этой сессии (для будущих)

  1. «caps 200» ≠ «поиск работает». Prowlarr может отдавать caps 200, пустой indexerstatus и health: [], при этом реальный поиск через индексер не идёт. Приёмка — search с group_by(.indexer), потому что пустой ответ трекера выглядит как успех инфраструктуры.
  2. 429 на caps — не «карантин в Prowlarr» как отдельная сущность, а запись в IndexerStatus. Лечить SQL, не тестами. Тест продлевает блокировку.
  3. Empty cookies returned ≠ blocked by CloudFlare. Первое — нет логина (bb_data), второе — CF не пройден. Симптомы похожи, причины разные, лечения разные.
  4. Bypass-фильтр прокси обязан содержать имя сервиса-соседа, иначе внутренние запросы уходят в прокси (Lidarr → Prowlarr). Признак — Http request timed out при живом соседе.
  5. Не сверять артефакт с соседним артефактом. Здесь долго сверялись caps/health между собой, а они оба — производные одного состояния БД. Сверять с постановкой задачи (Alex: «у меня должно качаться»), то есть с реальным поиском.

Что делать при возврате симптома «индексера недоступны» (обновлено 2026-09-29)

  1. GET /api/v1/indexerstatus в Prowlarr → кто в карантине (disabledTill).
  2. 🔴 Снять карантин через SQL, НЕ тестом — POST /indexer/test его продлевает (EscalationLevel растёт, DisabledTill удлиняется):
    DB=/mnt/RED_2TB/docker/arr/prowlarr/prowlarr.db
    sqlite3 "file:$DB?mode=ro" ".backup /home/truenas_admin/prowlarr.db.bak-$(date +%Y%m%d-%H%M%S)"
    docker stop prowlarr
    sqlite3 "$DB" "DELETE FROM IndexerStatus;"
    docker start prowlarr
    
  3. GET /N/api?t=caps → 200 (429 = ещё в карантине). ⚠️ Это не доказательство работы.
  4. Приёмка — реальный поиск, не caps и не health:
    curl -s -m 240 -H "X-Api-Key: $PK" "http://localhost:9696/api/v1/search?query=radiohead&type=search" \
      | jq -r 'group_by(.indexer)[] | "\(.[0].indexer): \(length)"'
    
    Если CF-индексеров нет в выдаче — смотреть cookies (bb_data), а не caps:
    docker exec flaresolverr curl -s -X POST http://localhost:8191/v1 -H "Content-Type: application/json" \
      -d '{"cmd":"request.get","url":"https://rutracker.org/forum/index.php","maxTimeout":100000,"session":"prowlarr"}' \
      | jq -r '.solution.cookies[].name'
    # нет bb_data → логин не прошёл → нужно вмешательство Alex (капча), инфраструктура ни при чём
    
  5. Если снова падает RuTracker — не требовать пароль, пока не проверены bb_data и капча.

История (продолжение, 2026-09-29 — сессия «ручная капча»)

  • Постановка Alex: «ароматизировать насколько возможно — уведомление в ТГ от hermes-taiga, соединение моего браузера через 3x-ui через тот же туннель, выдача и установка куки обратно».
  • Разбор идей — три из четырёх отвергнуты фактами (см. таблицу в разделе «РЕШЕНИЕ»): свой браузер через 3x-ui → vless-proxy это клиент, плюс cf_clearance привязан к UA/fingerprint; установка куки в Prowlarr → полей нет; капча-солвер → официально non-functional (DeepWiki FS + issue #1452).
  • Исследован Кра́кен (Alex: «на кракене вроде был капча солвер?»). Проверено на живом: солвера нет (только FS, requestTimeout: 60), карантин идентичен Тайге (EscalationLevel=9, DisabledTill=2026-09-27). Миф про «рабочий Кра́кен» развеян.
  • Проверена инфраструктура 3x-ui/xray: vless-proxy = xray-клиент (socks :1080 + http :1081, VLESS → vpn.mallexxx.duckdns.org:443 ws/tls path /vless); xray-admin = 3x-ui, панель :54321.
  • Собран и развёрнут свой образ flaresolverr-vnc:latest — база FS + Xvfb + fluxbox + x11vnc + noVNC, под supervisor, 5 процессов. Профиль Chromium вынесен на persistent volume через XDG_CONFIG_HOME=/config/chromium-profile, FS запущен с HEADLESS=false. Файлы: /mnt/RED_2TB/docker/arr/fs-vnc/{Dockerfile,supervisord.conf,entrypoint-vnc.sh}. Compose: образ подменён, добавлены 6080:6080 и volume /mnt/RED_2TB/docker/arr/flaresolverr-config:/config. Бэкап docker-compose.yml.bak-20260928-profvol.
  • Проверено: docker compose config -q → OK; сборка ~215 с; контейнер Up; API {"status":"ok","version":"3.5.2"}; http://localhost:6080/vnc.html → 200; каталог профиля создан.
  • Новые питфоллы: headed-Chrome → первый запрос к :8191 рвёт соединение (Connection reset by peer), нужна пауза ~5–10 с; sessions.destroy несуществующей сессии → HTTP 500.
  • Открытый вопрос: проброшен ли порт 6080 наружу (duckdns + NAT) — проверяется только браузером Alex.
  • Остаток: ручной вход Alex в Chrome через noVNC → проверить, что cookies (bb_data) легли в профиль и переживают рестарт → шаг 3 (уведомление в ТГ).

🔴 ПИТФОЛЛ: порт 6080 НЕ доступен снаружи — нужен роут в Caddy (2026-09-29)

Проблема. noVNC слушает 0.0.0.0:6080 внутри docker, но наружу через NAT роутера порт 6080 не проброшен (и проброшен быть не может — доступа к роутеру нет). Alex ткнул в это прямо: «а ты его пробросил блядь?!». Открытие http://mallexxx.duckdns.org:6080/vnc.html снаружи не сработает.

Решение — роут в Caddy (тот же приём, что для prowlarr/lidarr/jellyfin): HTTPS-домен через уже проброшенные 8088/8443, отдельный порт наружу не нужен.

🔴 ПИТФОЛЛ: Caddy НЕ в сети media_net → flaresolverr:6080 не резолвится

Базовое состояние Caddy: сети caddy_default + transmission_default. Имени flaresolverr в них нет — reverse_proxy flaresolverr:6080 упадёт на DNS.

Фикс (обратимый, без правки compose):

ssh truenas_admin@mallexxx.duckdns.org 'docker network connect media_net caddy'
# caddy_default=172.16.1.11  media_net=172.16.17.9  transmission_default=172.16.12.3

Альтернатива — указать IP 172.16.17.8, но он меняется при пересоздании контейнера; подключение к сети устойчивее (Caddy резолвит по имени).

Caddyfile — добавленный блок

Файл /mnt/RED_2TB/docker/caddy/Caddyfile (владелец truenas_admin, сам каталог — root, поэтому бэкап кладём в домашний каталог, не рядом):

vnc.mallexxx.duckdns.org {
    basic_auth {
        admin $2a$14$tcfG4J1HSonHGX1rb3rsPeTkDWPmo7rpSbg/TAHhV3sRbWztWmqVa
    }
    reverse_proxy flaresolverr:6080
}

🔴 basic_auth обязателен: noVNC без пароля = открытый рабочий стол с залогиненным RuTracker в интернете.

🔴 ПИТФОЛЛ: не переиспользовать чужой bcrypt-хэш. В первой версии блока был скопирован хэш от books-admin — тот же, что у books/library. Это дало 401 даже с паролем, потому что логин в паре с этим хэшем — books-admin, а не admin: basic_auth сверяет пару (логин, пароль), а не хэш отдельно. Лечится генерацией своего:

docker exec caddy caddy hash-password --plaintext "vnc-taiga"
# → $2a$14$tcfG4J1HSonHGX1rb3rsPeTkDWPmo7rpSbg/TAHhV3sRbWztWmqVa

Правильные креды: логин admin, пароль vnc-taiga. ⚠️ Пароль хэшируется до заливки Caddyfile; в caddy reload ошибок не будет — просто 401.

Бэкап: ~truenas_admin/Caddyfile.bak-20260928-novnc (2124 B). ⚠️ cp Caddyfile.bak-... рядом с оригиналом → Permission denied (каталог root). Копировать в ~.

✅ РЕЗУЛЬТАТ: роут проверен, работает

Порядок, который дал рабочий результат:

  1. docker network connect media_net caddy — до reload, иначе DNS не резолвится.
  2. scp нового Caddyfile → docker exec caddy caddy validate --config /etc/caddy/Caddyfile → Valid configuration.
  3. docker exec caddy caddy reload --config /etc/caddy/Caddyfile.

Проверка (с Mac, снаружи):

curl -s -o /dev/null -w "%{http_code}\n" --max-time 25 \
  -u admin:vnc-taiga https://vnc.mallexxx.duckdns.org/vnc.html
# → 200

⚠️ Первый curl может вернуть 000 (exit 35, TLS-ошибка) — это не провал: Caddy в этот момент ещё выпускает сертификат. Сертификат выпустился успешно (grep -i acme → authorization finalized, authz_status: valid, tls-alpn-01), 000 был из-за гонки с выпуском. Повторить curl через 10–20 с.

Два способа доступа (оба без правок роутера)

Способ Команда / адрес Когда
SSH-туннель (мгновенно) ssh -L 6080:truenas_admin@mallexxx.duckdns.org:6080 -N truenas_admin@mallexxx.duckdns.org → http://localhost:6080/vnc.html прямо сейчас, ничего не меняя
Caddy-роут (постоянно) https://vnc.mallexxx.duckdns.org (basic_auth) постоянное решение

Порядок правки Caddy (проверенный)

  1. Бэкап: cp /mnt/RED_2TB/docker/caddy/Caddyfile ~/Caddyfile.bak-<дата>-<тег>
  2. docker network connect media_net caddy (один раз, до reload)
  3. Правка Caddyfile локально (не sed на сервере) → scp
  4. docker exec caddy caddy reload --config /etc/caddy/Caddyfile

⚠️ DNS-запись vnc.mallexxx.duckdns.org — duckdns требует добавить поддомен отдельно (или использовать wildcard *.mallexxx.duckdns.org, если он уже настроен на 8088/8443).