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

1207 lines
86 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.
```bash
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:
```bash
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`)
```yaml
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` на локальный образ:
```yaml
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` **нет ни одного окна** — смотреть нечего.
**Проверка (на хосте Тайги):**
```bash
# 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):
```bash
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.** Если капча-солвер где-то и вспоминается — это не Кра́кен.
### Карантин на Кра́кене — идентичный Тайге
```sql
-- Кра́кен: /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 правильно:**
```python
# ВСЕГДА с 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:
```bash
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` на хосте Тайги ЕСТЬ, в контейнере НЕТ):
```bash
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
```bash
# 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`). Каталоги созданы:
```bash
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** (применяется без рестарта):
```bash
# 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**.
Правильно — отправить **полный** объект индексера:
```bash
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` удлиняется):
```bash
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`:
```bash
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`:
```bash
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):**
```bash
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` сверяет
> **пару (логин, пароль)**, а не хэш отдельно. Лечится генерацией своего:
> ```bash
> 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, снаружи):
```bash
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).