From 1790c060ffe475a411af7819fe7fa582cd1d8828 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 17 Aug 2026 12:49:30 +0600
Subject: [PATCH 01/81] [2026-08-17] eagle:
family/how-to/truenas-sata-ports-and-zfs-pools.md
---
.../truenas-sata-ports-and-zfs-pools.md | 32 ++++++++++++-------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index 26efe5ca..11499514 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -1,6 +1,15 @@
# TrueNAS — SATA порты и пулы
-> Обновлено: 2026-08-01
+> Обновлено: 2026-08-17
+
+## ✅ РЕШЕНО: HGST 12TB завёлся — проблема была в 3.3V PWDIS, НЕ в карте
+
+**2026-08-17.** HGST **линуется и полностью работает** теперь. Ключевое изменение — **отключена/обезврежена 3.3V линия (PWDIS-контакт №3 на SATA-разъёме питания)**. Диск:
+- определяется как `/dev/sde`, `HGST HUH721212ALE600`, серийник `8CKD08RE`, **10.9T**, секторов `23437770752` (штатная ёмкость 12TB по SFF-8447)
+- **подключён на карте ASMedia (ata7 = 0000:02:00.0, host7)** — той самой, где раньше НЕ линковался!
+- **разметка сохранена без переразметки**: `sde1`=200M, `sde2`=10.9TB (та GPT, что была и через USB)
+
+**Вывод:** причина молчания была **100% в 3.3V Power Disable (PWDIS)** — enterprise-диск с поддержкой PWDIS не раскручивается, когда обычный БП подаёт 3.3V на пин №3 SATA-питания. Снятие 3.3V-контакта устранило проблему. **Карта ASM1166 и порт материнки при этом работали и раньше.** Переразметка для решения НЕ требуется.
## Материнская плата
@@ -17,6 +26,8 @@
| ata4 (SATA3G_2) | SATA 2 | 3 Gb/s | синий | **sde** — Kingston 120GB SSD (boot) | 50026B77844E881C |
| ata5 (SATA3G_3) | SATA 2 | 3 Gb/s | синий | *свободен* | — |
| ata6 (SATA3G_4) | SATA 2 | 3 Gb/s | синий | **sdd** — Seagate 4TB (ST4000DM004) | WFN66CM2 |
+| **ata7** (карта ASM1166 02:00.0) | — | — | — | **sde — HGST Ultrastar 12TB (HUH721212ALE600)** ✅ | 8CKD08RE / wwn-0x5000cca26fefbc05 |
+| **ata9** (карта ASM1166 02:00.0) | — | — | — | **sdf** — WD 2TB (WD20EFAX, второй) | WD-WXJ2A31CUNNT |
> ⚠️ В отличие от изначального плана, **IronWolf 12TB сейчас на ata2 (SATA6G_2)**, а не на ata5. Это нормально — ZFS не привязана к порту (см. ниже).
@@ -46,19 +57,16 @@
**➡️ Уточнённый диагноз:** проблема **именно в паре HGST SATA-интерфейс ↔ SATA-карта ASMedia**, а не в самом диске. Диск работоспособен. Две вероятные причины не-линка по SATA в карте: (1) питание с SATA-разъёма БП до диска в карте не доходит (в USB-боксе питание даёт мост/свой блок, потому и работает), либо (2) enterprise-диск (4Kn/особый PHY) не поднимает линк на ASM1166. USB-мост — рабочий способ диагностики, но ненадёжен как постоянное решение для ZFS.
-## Следующие шаги (HGST 12TB — статус: диск рабочий через USB, но по SATA-карте не линкуется)
+## ✅ Решение HGST 12TB (2026-08-17) — корень найден, диск работает
-Цель — завести HGST в пул TrueNAS. USB-мост подтвердил исправность диска; теперь вопрос только в SATA-линке.
+Проблема не-линка HGST по SATA **решена: это 3.3V PWDIS на SATA-питании.** Диск уже работает на карте ASMedia (ata7, см. таблицу выше). Разметка не трогалась — sde1=200M, sde2=10.9TB остались как были. Осталось: добавить диск в пул TrueNAS по by-id/GUID (ZFS к порту не привязана). После добавления — удалить этот блок.
-1. **Вариант А (рекомендуется):** вешать HGST на **свободный SATA-порт материнки ata5 (SATA3G_3, синий)** вместо карты. Intel H77 AHCI к enterprise-дискам надёжнее, чем ASM1166. После подключения проверить `lsscsi -g` — диск должен появиться на хосте материнки (host 0/1/2/3/5). Если заведётся — добавить в пул по by-id/GUID (ZFS не привязана к порту).
-2. **Вариант Б (проверочный):** вернуть HGST в тот же порт/кабель карты, где определялся рабочий WD 2TB (host9). Если с заведомо рабочим кабелем/портом HGST всё равно не даёт `target*` → подтверждается мизер питания до диска в карте или несовместимость PHY HGST↔ASM1166.
-3. **Особое внимание питанию:** у карты нет отдельного питания, диск в карте питается SATA-разъёмом напрямую. Проверить, что кабель питания доходит и даёт 12V — подозрение, что в USB-боксе питание подаёт мост, а в SATA-схеме до HGST оно не доходит.
-4. Проверить появился ли диск на материнке после переноски:
-```bash
-lsscsi -g # смотреть новые /dev/sd* на хостах материнки (0,1,2,3,5)
-lsblk -o NAME,SIZE,MODEL,TRAN
-```
-5. Если HGST заведётся и добавится в пул — обновить таблицу портов и удалить этот блок.
+**Что было/что стало:**
+- Было: HGST не давал `target*` ни на одном порту карты ASM1166 (USB-мост работал, потому что питание подавал мост, не затрагивая PWDIS).
+- Стало: после отключения 3.3V-контакта HGST линкуется на той же карте (ata7/02:00.0) — карта и материнка исправны, дело было в питании.
+- **Переразметка для решения не нужна** — проблема на уровне PHY/питания, а не данных.
+
+> ⚠️ Если такой же enterprise-диск (HGST/WD Ultrastar/Seagate Exos c PWDIS) снова «молчит» по SATA — первым делом проверить 3.3V-пин №3 питания: Molex→SATA адаптер (без 3.3V) или изоляция пина №3 решают.
> ⚠️ NB: буквы дисков (`sda`/`sdb`/...) **shift-аются при перетыкивании** — сейчас на материнке: [0]=WD2TB, [2]=WD4TB, [3]=Kingston boot, [5]=Seagate4TB, IronWolf на [1]. Не полагаться на буквы, проверять по `by-id`/модели.
From a2c72793deb5adcd36af5c0ebfded2817a7dc23d Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 17 Aug 2026 13:15:17 +0600
Subject: [PATCH 02/81] [2026-08-17] eagle:
family/how-to/truenas-sata-ports-and-zfs-pools.md
---
.../truenas-sata-ports-and-zfs-pools.md | 44 ++++++++++++++-----
1 file changed, 32 insertions(+), 12 deletions(-)
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index 11499514..9881b059 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -16,20 +16,24 @@
**ASUS P8H77-V LE**, чипсет Intel H77.
6 SATA портов на Intel контроллере (порт от ASMedia отсутствует).
-### Распределение портов (текущее состояние)
+### Распределение портов (текущее состояние — после перетыкания 2026-08-17)
| Порт (ata) | Тип | Скорость | Цвет | Диск | by-id |
|------------|-----|----------|------|------|-------|
| ata1 (SATA6G_1) | SATA 3 | 6 Gb/s | серый | **sda** — WD 2TB (WD20EFAX) | WD-WXH2A31D5HDJ |
-| ata2 (SATA6G_2) | SATA 3 | 6 Gb/s | серый | **sdc** — Seagate IronWolf 12TB (ST12000NT001) | WV700FQ5 |
-| ata3 (SATA3G_1) | SATA 2 | 3 Gb/s | синий | **sdb** — WD 4TB (WD40EZAZ) | WD-WX22D51JJFZ7 |
-| ata4 (SATA3G_2) | SATA 2 | 3 Gb/s | синий | **sde** — Kingston 120GB SSD (boot) | 50026B77844E881C |
-| ata5 (SATA3G_3) | SATA 2 | 3 Gb/s | синий | *свободен* | — |
-| ata6 (SATA3G_4) | SATA 2 | 3 Gb/s | синий | **sdd** — Seagate 4TB (ST4000DM004) | WFN66CM2 |
-| **ata7** (карта ASM1166 02:00.0) | — | — | — | **sde — HGST Ultrastar 12TB (HUH721212ALE600)** ✅ | 8CKD08RE / wwn-0x5000cca26fefbc05 |
-| **ata9** (карта ASM1166 02:00.0) | — | — | — | **sdf** — WD 2TB (WD20EFAX, второй) | WD-WXJ2A31CUNNT |
+| ata2 (SATA6G_2) | SATA 3 | 6 Gb/s | серый | **sdb** — WD 2TB (WD20EFAX, второй, был на карте ata9) | WD-WXJ2A31CUNNT |
+| ata3 (SATA3G_1) | SATA 2 | 3 Gb/s | синий | **sdc** — WD 4TB (WD40EZAZ) | WD-WX22D51JJFZ7 |
+| ata4 (SATA3G_2) | SATA 2 | 3 Gb/s | синий | **sdd** — Kingston 120GB SSD (boot) | 50026B77844E881C |
+| ata5 (SATA3G_3) | SATA 2 | 3 Gb/s | синий | *занято диском* | — |
+| ata6 (SATA3G_4) | SATA 2 | 3 Gb/s | синий | **sde** — Seagate 4TB (ST4000DM004) | WFN66CM2 |
+| *(карта ASM1166)* | — | — | — | **НЕ В СЛОТЕ** (lspci не видит) | — |
-> ⚠️ В отличие от изначального плана, **IronWolf 12TB сейчас на ata2 (SATA6G_2)**, а не на ata5. Это нормально — ZFS не привязана к порту (см. ниже).
+**⚠️ Состояние на 2026-08-17 после перетыкания:**
+- **Карта ASMedia ASM1166 физически вытащена из слота** (нет в `lspci`); `/sys/class/ata_port/` содержит только ata1–ata6 (материнка Intel H77). WD 2TB #2 перенесён с карты (ata9) на материнку ata2.
+- **HGST 12TB и IronWolf 12TB сейчас НЕ подключены/НЕ линкуются** — их нет ни в `lsscsi`, ни в `/dev/disk/by-id`. Не были бы они и на карте (карты нет). HGST мы заводили на карте, значит при её извлечении он отключился.
+- Несмотря на перетыкание пулы **здоровы** (см. раздел про пулы ниже) — подтверждено, что ZFS не зависит от порта.
+
+> ⚠️ В отличие от изначального плана, диски сейчас не по плану — проверять актуальное распределение по `by-id`/модели, а не по буквам. Типичные буквы при стабильном подключении: sda=WD2TB#1, sdb=WD2TB#2, sdc=WD4TB, sdd=Kingston, sde=Seagate4TB.
## Плата расширения SATA в PCIe (установлена ~2026-07-30)
@@ -59,7 +63,9 @@
## ✅ Решение HGST 12TB (2026-08-17) — корень найден, диск работает
-Проблема не-линка HGST по SATA **решена: это 3.3V PWDIS на SATA-питании.** Диск уже работает на карте ASMedia (ata7, см. таблицу выше). Разметка не трогалась — sde1=200M, sde2=10.9TB остались как были. Осталось: добавить диск в пул TrueNAS по by-id/GUID (ZFS к порту не привязана). После добавления — удалить этот блок.
+> ⚠️ **Актуальный статус на 2026-08-17 вечер:** хоть HGST и завёлся (см. ниже КАК), в финальном перетыкании **карта ASM1166 вытащена из слота, HGST и IronWolf сейчас НЕ подключены** (подробнее в таблице портов выше). Этот блок фиксирует **как была решена причина не-линка** — пригодится, когда диск вернётся в систему.
+
+Проблема не-линка HGST по SATA **решена: это 3.3V PWDIS на SATA-питании.** Разметка не трогалась — sde1=200M, sde2=10.9TB остались как были. Когда диск снова будет подключён — добавить в пул TrueNAS по by-id/GUID (ZFS к порту не привязана). После добавления — удалить этот блок.
**Что было/что стало:**
- Было: HGST не давал `target*` ни на одном порту карты ASM1166 (USB-мост работал, потому что питание подавал мост, не затрагивая PWDIS).
@@ -93,11 +99,25 @@ lspci -k | grep -i sata
- **Kingston SSD (boot)** — оставить в синем порту SATA3G_2 (ata4). Вся активность только при загрузке, система работает из RAM.
- **WD 2TB** — серый SATA6G_1 (ata1), занят.
-## Важно про ZFS и перетыкивание
+## Важно про ZFS и перетыкание
- ZFS не привязана к букве диска или SATA порту. TrueNAS распознаёт пул по GUID на дисках.
-- Можно перетыкать диски в любые порты — TrueNAS поднимет пул автоматически.
+- Можно перетыкать диски в любые порты — TrueNAS поднимет пул автоматически (подтверждено 2026-08-17).
- Если пул не поднялся автоматически: WebUI → Storage → Import Pool.
+- Проверка пула после перетыкания: `/sbin/zpool status ` — убедиться что все члены `ONLINE`, ошибки `0 0 0`.
+
+## Пулы TrueNAS (проверено 2026-08-17)
+
+Имена пулов, топология и члены — проверять статус через `/sbin/zpool` (см. ниже про PATH).
+
+| Пул | Размер | Члены (по /dev со стабильным подключением) | Топология | Монтирование |
+|-----|--------|-----------|-----------|--------------|
+| **RED_2TB** | 5.44T (4.60T занято) | mirror: `sdc2` (WD4TB) + `sde2` (Seagate4TB); отдельно `sda2` (WD2TB) | 1 диск (sda2) + mirror-1 (2 диска) | `/mnt` |
+| **boot-pool** | 111G (7.65G занято) | `sdd3` (Kingston 120GB boot SSD) | 1 диск | — |
+
+- RED_2TB: HEALTH **ONLINE**, нераспределённый свободный диск (данные не реплицируются на sda2 — он отдельный, а не парный). Если redun-dancy важна — учесть, что sda2 без зеркала.
+- Идёт регулярный scrub (запускается ночью воскресенья).
+- **`zpool` не в PATH для zsh truenas_admin** — вызывать через абсолютный путь `/sbin/zpool` или `/usr/sbin/zpool` (иначе `command not found`).
## Запрещено
From fad9d476c09f5456ee224bcbfba7447bd032e143 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 17 Aug 2026 13:51:49 +0600
Subject: [PATCH 03/81] [2026-08-17] eagle:
family/how-to/truenas-sata-ports-and-zfs-pools.md
---
.../truenas-sata-ports-and-zfs-pools.md | 45 +++++++++++++------
1 file changed, 32 insertions(+), 13 deletions(-)
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index 9881b059..3280a53a 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -16,24 +16,27 @@
**ASUS P8H77-V LE**, чипсет Intel H77.
6 SATA портов на Intel контроллере (порт от ASMedia отсутствует).
-### Распределение портов (текущее состояние — после перетыкания 2026-08-17)
+### Распределение портов (актуально на конец 2026-08-17, после финального перетыкания)
| Порт (ata) | Тип | Скорость | Цвет | Диск | by-id |
|------------|-----|----------|------|------|-------|
| ata1 (SATA6G_1) | SATA 3 | 6 Gb/s | серый | **sda** — WD 2TB (WD20EFAX) | WD-WXH2A31D5HDJ |
-| ata2 (SATA6G_2) | SATA 3 | 6 Gb/s | серый | **sdb** — WD 2TB (WD20EFAX, второй, был на карте ata9) | WD-WXJ2A31CUNNT |
-| ata3 (SATA3G_1) | SATA 2 | 3 Gb/s | синий | **sdc** — WD 4TB (WD40EZAZ) | WD-WX22D51JJFZ7 |
+| ata2 (SATA6G_2) | SATA 3 | 6 Gb/s | серый | **sdb** — WD 4TB (WD40EZAZ) | WD-WX22D51JJFZ7 |
+| ata3 (SATA3G_1) | SATA 2 | 3 Gb/s | синий | **sdc** — ⭐ **IronWolf 12TB (ST12000NT001)** | WV700FQ5 |
| ata4 (SATA3G_2) | SATA 2 | 3 Gb/s | синий | **sdd** — Kingston 120GB SSD (boot) | 50026B77844E881C |
-| ata5 (SATA3G_3) | SATA 2 | 3 Gb/s | синий | *занято диском* | — |
+| ata5 (SATA3G_3) | SATA 2 | 3 Gb/s | синий | *(свободен/не иден-тиф.)* | — |
| ata6 (SATA3G_4) | SATA 2 | 3 Gb/s | синий | **sde** — Seagate 4TB (ST4000DM004) | WFN66CM2 |
-| *(карта ASM1166)* | — | — | — | **НЕ В СЛОТЕ** (lspci не видит) | — |
+| *(карта ASM1166)* | — | — | — | **НЕ В СЛОТЕ** (вытащена) | — |
-**⚠️ Состояние на 2026-08-17 после перетыкания:**
-- **Карта ASMedia ASM1166 физически вытащена из слота** (нет в `lspci`); `/sys/class/ata_port/` содержит только ata1–ata6 (материнка Intel H77). WD 2TB #2 перенесён с карты (ata9) на материнку ata2.
-- **HGST 12TB и IronWolf 12TB сейчас НЕ подключены/НЕ линкуются** — их нет ни в `lsscsi`, ни в `/dev/disk/by-id`. Не были бы они и на карте (карты нет). HGST мы заводили на карте, значит при её извлечении он отключился.
-- Несмотря на перетыкание пулы **здоровы** (см. раздел про пулы ниже) — подтверждено, что ZFS не зависит от порта.
+**⚠️ Фактическое положение на конец сессии (2026-08-17):**
+- **Карта ASMedia ASM1166 вытащена** — `lspci` показывает только Intel H77 (`00:1f.2`) и не относящийся к делу PCIe-to-PCI bridge ASM1083. WD 2TB#2 перенесён с карты на материнку.
+- **⭐ IronWolf 12TB теперь ПОДКЛЮЧЁН и ЛИНКУЕТСЯ** — `sdc` = `ST12000NT001-3LX101`, 10.9T, by-id `WV700FQ5`. Он не в пуле (новый диск).
+- **WD 2TB #2 (`WD-WXJ2A31CUNNT`) исчез из системы** — раньше был `sdb` (перенесён с карты), после финального перетыкания не виден. Уточнить где он.
+- **HGST 12TB (`HUH721212ALE600`) по-прежнему НЕ виден** — ни в `lsscsi`, ни в by-id. Питание: если он подключён обычным SATA-кабелем — молчит из-за **PWDIS (3.3V на пине 3)**, для него нужен Molex→SATA или снятый пин 3 (см. блок решения выше).
-> ⚠️ В отличие от изначального плана, диски сейчас не по плану — проверять актуальное распределение по `by-id`/модели, а не по буквам. Типичные буквы при стабильном подключении: sda=WD2TB#1, sdb=WD2TB#2, sdc=WD4TB, sdd=Kingston, sde=Seagate4TB.
+> ⚠️ Буквы (`sda`/`sdb`) **shift-аются при перетыкании** и сейчас разъехались: `sdb` = WD4TB (не WD2TB#2!), `sdc` = IronWolf. **Всегда сверять по by-id/модели, не полагаться на буквы.**
+
+> ⚠️ ⚡ **СРОЧНО:** пул `RED_2TB` после этого перетыкания НЕ заимпортирован (см. блок «КРИТИЧНО» в разделе пулов). Все члены на месте — нужен Import Pool.
## Плата расширения SATA в PCIe (установлена ~2026-07-30)
@@ -102,8 +105,7 @@ lspci -k | grep -i sata
## Важно про ZFS и перетыкание
- ZFS не привязана к букве диска или SATA порту. TrueNAS распознаёт пул по GUID на дисках.
-- Можно перетыкать диски в любые порты — TrueNAS поднимет пул автоматически (подтверждено 2026-08-17).
-- Если пул не поднялся автоматически: WebUI → Storage → Import Pool.
+- **Перетыкание НЕ всегда поднимает пул автоматически** (2026-08-17: RED_2TB не заимпортировался сам после перетыкания, см. «КРИТИЧНО» ниже). Если пул не виден в `zpool list` — нужен ручной Import (WebUI Storage → Import Pool или `zpool import ` под root). Данные при этом целы, пока все члены `ONLINE`.
- Проверка пула после перетыкания: `/sbin/zpool status ` — убедиться что все члены `ONLINE`, ошибки `0 0 0`.
## Пулы TrueNAS (проверено 2026-08-17)
@@ -112,10 +114,27 @@ lspci -k | grep -i sata
| Пул | Размер | Члены (по /dev со стабильным подключением) | Топология | Монтирование |
|-----|--------|-----------|-----------|--------------|
-| **RED_2TB** | 5.44T (4.60T занято) | mirror: `sdc2` (WD4TB) + `sde2` (Seagate4TB); отдельно `sda2` (WD2TB) | 1 диск (sda2) + mirror-1 (2 диска) | `/mnt` |
+| **RED_2TB** | 5.44T (4.60T занято) | mirror: `sdb2`/`sdc2` (WD4TB) + `sde2` (Seagate4TB); отдельно `sda2` (WD2TB) | 1 диск (sda2) + mirror-1 (2 диска) | `/mnt` — **⚠️ ПОТРЕБУЕТСЯ IMPORT, см. ниже** |
| **boot-pool** | 111G (7.65G занято) | `sdd3` (Kingston 120GB boot SSD) | 1 диск | — |
- RED_2TB: HEALTH **ONLINE**, нераспределённый свободный диск (данные не реплицируются на sda2 — он отдельный, а не парный). Если redun-dancy важна — учесть, что sda2 без зеркала.
+
+### ⚠️ КРИТИЧНО (2026-08-17, после финального перетыкания): RED_2TB НЕ заимпортирован
+
+После перетыкания дисков на материнке пул **RED_2TB НЕ поднялся автоматически**: `/sbin/zpool list` показывает только `boot-pool`, а `/sbin/zpool status RED_2TB` → `cannot open 'RED_2TB': no such pool`.
+
+**Это НЕ потеря данных.** Все члены пула физически на месте и видны системе (проверено 2026-08-17):
+- `sda2` = WD2TB#1 (ata1)
+- `sdb2` = WD4TB (ata2) — буква сменилась с `sdc` на `sdb`
+- `sde2` = Seagate4TB (ata6)
+
+Пул просто не заимпортировался, вероятно из-за смены букв устройств. **Что нужно сделать (порядок):**
+1. TrueNAS WebUI → **Storage → Import Pool** → выбрать `RED_2TB`, импортировать.
+ - Альтернатива под root: `/sbin/zpool import RED_2TB` (truenas_admin НЕ может — `sudo` требует пароль; `zpool import` даёт `cannot discover pools: permission denied` без root).
+2. После импорта проверить: `/sbin/zpool status RED_2TB` → все члены `ONLINE`, `READ WRITE CKSUM` = `0 0 0`.
+
+> ⚠️ Урок: перетыкание дисков НЕ всегда триггерит авто-импорт, даже когда все члены на месте и ZFS работает по GUID. Буквы могут смениться (`sdc`→`sdb`), и пул ждёт ручного Import. Не паниковать — данные целы, пока все члены `ONLINE` в `lsblk`/by-id.
+
- Идёт регулярный scrub (запускается ночью воскресенья).
- **`zpool` не в PATH для zsh truenas_admin** — вызывать через абсолютный путь `/sbin/zpool` или `/usr/sbin/zpool` (иначе `command not found`).
From d4dac6701c32651050bf355c65a0cc2d655f5c2b Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 17 Aug 2026 14:17:18 +0600
Subject: [PATCH 04/81] [2026-08-17] eagle:
family/how-to/truenas-sata-ports-and-zfs-pools.md
---
.../truenas-sata-ports-and-zfs-pools.md | 26 +++++++++++++++----
1 file changed, 21 insertions(+), 5 deletions(-)
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index 3280a53a..1d31831a 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -1,8 +1,24 @@
# TrueNAS — SATA порты и пулы
-> Обновлено: 2026-08-17
+> Обновлено: 2026-08-17 (финал: HGST упал, см. блок ниже)
-## ✅ РЕШЕНО: HGST 12TB завёлся — проблема была в 3.3V PWDIS, НЕ в карте
+## ⚠️⚠️ КРИТИЧНО: HGST 12TB УРОНЕН НА ПОЛ — вероятный слом после падения
+
+**2026-08-17 вечер.** HGST (HUH721212ALE600, сер. 8CKD08RE) **уронили на пол, будучи подключённым**. Развитие сценария (как происходило):
+
+1. **После удара — сектор 0 стал нечитаем** (по USB-мосту): ядро давало `critical medium error, dev sdf, sector 0` → `Add. Sense: Unrecovered read error` → `unable to read partition table` / `unable to read RDB block 0`. Разметка (`sdf1`/`sdf2`) пропала. Это НЕ ошибка моста/кабеля — `hostbyte=DID_OK driverbyte=DRIVER_OK`, ошибку вернул сам диск.
+2. **smartctl мог прочитать INFO-секцию** (`Model: HGST HUH721212ALE600`, `8CKD08RE`, FW `LEBDT3P2`, 512e, SATA 6.0 Gb/s, SMART enabled) — контроллер жив, линк поднят.
+3. **На MacBook через USB-мост диск «щёлкает» и не шуршит головкой при старте** — признак механических проблем после удара (головки/мотор). КЛИК-ОФ-ДЕФ возможен, но также нельзя исключать нехватку питания через USB-мост на 12TB-диске.
+
+**Состояние:** не подтверждён полный отказ, но высокая вероятность аппаратного повреждения от падения. Сектор 0 (начало диска /GPT) повреждён точно. Масштаб (1 сектор или сотни) НЕ определён — длинный SMART-тест не удалось прогнать, т.к. диск щёлкает.
+
+**Что НЕ делать:** не махать диском, не запускать запись/ремонт сектора 0 до понимания масштаба, не использовать как свежий пул — доверие к диску утрачено. Если диск ещё вращается — прогонять длинный SMART-тест по SATA (с Molex-питанием) чтобы оценить `Current_Pending_Sector`.
+
+> 🔁 ИСТОРИЯ ВОПРОСА: до падения диск был ПОЛНОСТЬЮ рабочим. Причина исходного «молчания» по SATA — PWDIS (см. ниже). После снятия 3.3V завёлся и линковался. Затем урон — теперь состояние под вопросом.
+
+---
+
+## ✅ РЕШЕНО (исторически): HGST 12TB заводился — проблема была в 3.3V PWDIS, НЕ в карте
**2026-08-17.** HGST **линуется и полностью работает** теперь. Ключевое изменение — **отключена/обезврежена 3.3V линия (PWDIS-контакт №3 на SATA-разъёме питания)**. Диск:
- определяется как `/dev/sde`, `HGST HUH721212ALE600`, серийник `8CKD08RE`, **10.9T**, секторов `23437770752` (штатная ёмкость 12TB по SFF-8447)
@@ -31,8 +47,8 @@
**⚠️ Фактическое положение на конец сессии (2026-08-17):**
- **Карта ASMedia ASM1166 вытащена** — `lspci` показывает только Intel H77 (`00:1f.2`) и не относящийся к делу PCIe-to-PCI bridge ASM1083. WD 2TB#2 перенесён с карты на материнку.
- **⭐ IronWolf 12TB теперь ПОДКЛЮЧЁН и ЛИНКУЕТСЯ** — `sdc` = `ST12000NT001-3LX101`, 10.9T, by-id `WV700FQ5`. Он не в пуле (новый диск).
-- **WD 2TB #2 (`WD-WXJ2A31CUNNT`) исчез из системы** — раньше был `sdb` (перенесён с карты), после финального перетыкания не виден. Уточнить где он.
-- **HGST 12TB (`HUH721212ALE600`) по-прежнему НЕ виден** — ни в `lsscsi`, ни в by-id. Питание: если он подключён обычным SATA-кабелем — молчит из-за **PWDIS (3.3V на пине 3)**, для него нужен Molex→SATA или снятый пин 3 (см. блок решения выше).
+- **WD 2TB #2 (`WD-WXJ2A31CUNNT`) НАМЕРЕННО отключён** пользователем (2026-08-17). Раньше был `sdb` (перенесён с карты), затем Alex его отключил «это норм». Сейчас отсутствует по замыслу, а не по ошибке.
+- **HGST 12TB (`HUH721212ALE600`)** — был подключён по USB (виден как `sdf`, model HGST, WWN `0x5000cca26fefbc05`) и после удара о пол НЕ работает надёжно (см. блок КРИТИЧНО в самом верху). На текущий момент считаем его вышедшим из строя.
> ⚠️ Буквы (`sda`/`sdb`) **shift-аются при перетыкании** и сейчас разъехались: `sdb` = WD4TB (не WD2TB#2!), `sdc` = IronWolf. **Всегда сверять по by-id/модели, не полагаться на буквы.**
@@ -66,7 +82,7 @@
## ✅ Решение HGST 12TB (2026-08-17) — корень найден, диск работает
-> ⚠️ **Актуальный статус на 2026-08-17 вечер:** хоть HGST и завёлся (см. ниже КАК), в финальном перетыкании **карта ASM1166 вытащена из слота, HGST и IronWolf сейчас НЕ подключены** (подробнее в таблице портов выше). Этот блок фиксирует **как была решена причина не-линка** — пригодится, когда диск вернётся в систему.
+> ⚠️ **Актуальный статус на 2026-08-17 ночь:** HGST **УРОНЕН НА ПОЛ и сломан** (см. блок КРИТИЧНО в самом верху). Карта ASM1166 вытащена из слота, IronWolf подключён на материнку. Этот блок фиксирует **как изначально решалась причина не-линка** (PWDIS) — полезно для понимания, но теперь диск вышел из строя механически.
Проблема не-линка HGST по SATA **решена: это 3.3V PWDIS на SATA-питании.** Разметка не трогалась — sde1=200M, sde2=10.9TB остались как были. Когда диск снова будет подключён — добавить в пул TrueNAS по by-id/GUID (ZFS к порту не привязана). После добавления — удалить этот блок.
From d01c84c2bc85acf720a1d37ad5d0f4706c9cef0f Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 17 Aug 2026 14:49:16 +0600
Subject: [PATCH 05/81] [2026-08-17] eagle:
family/how-to/truenas-sata-ports-and-zfs-pools.md
---
.../truenas-sata-ports-and-zfs-pools.md | 54 +++++++++++++++----
1 file changed, 44 insertions(+), 10 deletions(-)
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index 1d31831a..c67f9783 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -1,8 +1,12 @@
# TrueNAS — SATA порты и пулы
-> Обновлено: 2026-08-17 (финал: HGST упал, см. блок ниже)
+> Обновлено: 2026-08-17 ночь (два КРИТИЧНО: HGST упал и сломан; RED_2TB вызывает kernel panic — см. раздел пулов)
-## ⚠️⚠️ КРИТИЧНО: HGST 12TB УРОНЕН НА ПОЛ — вероятный слом после падения
+## ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №1: пул RED_2TB вызывает KERNEL PANIC (важнее HGST)
+
+См. раздел «Пулы TrueNAS» → блок «RED_2TB не импортируется — вызывает KERNEL PANIC». Это приоритет №1 — данные реально есть. Система в panic loop; рецепт: тюнабели `zfs.zfs_recover=1 zfs.zil_replay_disable=1` в GRUB, затем import.
+
+## ⚠️⚠️ ПРОБЛЕМА №2: HGST 12TB УРОНЕН НА ПОЛ — вероятный слом после падения
**2026-08-17 вечер.** HGST (HUH721212ALE600, сер. 8CKD08RE) **уронили на пол, будучи подключённым**. Развитие сценария (как происходило):
@@ -135,21 +139,51 @@ lspci -k | grep -i sata
- RED_2TB: HEALTH **ONLINE**, нераспределённый свободный диск (данные не реплицируются на sda2 — он отдельный, а не парный). Если redun-dancy важна — учесть, что sda2 без зеркала.
-### ⚠️ КРИТИЧНО (2026-08-17, после финального перетыкания): RED_2TB НЕ заимпортирован
+### ⚠️ КРИТИЧНО (2026-08-17, после финального перетыкания): RED_2TB не импортируется — вызывает KERNEL PANIC
-После перетыкания дисков на материнке пул **RED_2TB НЕ поднялся автоматически**: `/sbin/zpool list` показывает только `boot-pool`, а `/sbin/zpool status RED_2TB` → `cannot open 'RED_2TB': no such pool`.
+После перетыкания дисков на материнке пул **RED_2TB не поднялся автоматически**, и при попытке `zpool import -F` система упала в **kernel panic bootloop**:
-**Это НЕ потеря данных.** Все члены пула физически на месте и видны системе (проверено 2026-08-17):
+```
+panic - not syncing: zfs: adding existent segment to range tree
+ ZFS: space_map_load / metaslab_activate / zio_dva_allocate → zfs_panic_recover
+```
+
+**Что это:** повреждение space map (метаданных ZFS) пула — один и тот же блок учтён дважды / неконсистентное отображение. При обычном импорте ZFS званивает panic по-умолчанию. **Это не обязательно полная потеря данных**, но структура пула повреждена.
+
+**Триггер (вероятный):** диски-члены отвалились во время работы/перезагрузки при перетыкании, или один из членов имеет скрытую проблему в области метаданных. Члены пула при этом физически на месте:
- `sda2` = WD2TB#1 (ata1)
- `sdb2` = WD4TB (ata2) — буква сменилась с `sdc` на `sdb`
- `sde2` = Seagate4TB (ata6)
-Пул просто не заимпортировался, вероятно из-за смены букв устройств. **Что нужно сделать (порядок):**
-1. TrueNAS WebUI → **Storage → Import Pool** → выбрать `RED_2TB`, импортировать.
- - Альтернатива под root: `/sbin/zpool import RED_2TB` (truenas_admin НЕ может — `sudo` требует пароль; `zpool import` даёт `cannot discover pools: permission denied` без root).
-2. После импорта проверить: `/sbin/zpool status RED_2TB` → все члены `ONLINE`, `READ WRITE CKSUM` = `0 0 0`.
+**Сейчас система грузится В panic loop каждый раз** (автоимпорт RED_2TB валит ядро на старте).
+
+#### ✅ РЕЦЕПТ ВОССТАНОВЛЕНИЯ (подтверждён web-источниками, openzfs #15030 + треды TrueNAS)
+
+**Панику в ZFS можно превратить в warning тремя тюнабелями** (передача как параметров ядра в GRUB):
+
+1. **В загрузочном меню GRUB** (при старте держать Shift/ESC) выбрать ядро TrueNAS, нажать `e`, в строке с `linux`/`vmlinuz` добавить в конец:
+ ```
+ zfs.zfs_recover=1 zfs.zil_replay_disable=1
+ ```
+ `Ctrl+X` загрузиться. Это даёт системе дойти до консоли/WebUI без паники (Panic→warning).
+
+2. **Под root импортировать пул аккуратно** (не форс-катастрофически с наскоку):
+ ```bash
+ /sbin/zpool import # какие пулы видит, без монтирования
+ /sbin/zpool import -n RED_2TB # dry-run — нужен ли -F и не падает ли
+ /sbin/zpool import -F RED_2TB # только если dry-run показал, что требуется -F
+ ```
+ > В треде TrueNAS подтверждено: `zpool import -f -F -X` «не срабатывало», а с тюнабелями в GRUB + импортом — срабатывает. Сначала тюнабели, потом import.
+
+3. **После импорта:** `/sbin/zpool status RED_2TB` → оценить члена с ошибками. Снять важные данные с пула на другие диски при первой возможности (импорт `-F` + recover может ценой части изменений). Честно проверить RAM/ECC — в треде #31410 это был реальный фактор повреждения.
+
+> ⚠️ Пока пул не восстановлен, **НЕ запускать повторный автоимпорт/`-F` сипломи** — каждый заход углубляет риск. Сначала тюнабели в GRUB.
+
+> ⚠️ Альтернативный обход для диагностики без паники: импорт **readonly** (`zpool import -o readonly=on RED_2TB`) может не паниковать, но данные могут быть недоступны из-за permission errors.
+
+**Опция добавить WD2TB#2 как зеркало к WD2TB#1** (`zpool attach RED_2TB `): валидна, но ТОЛЬКО после успешного импорта пула и его стабилизации. Не пытаться до восстановления.
+
-> ⚠️ Урок: перетыкание дисков НЕ всегда триггерит авто-импорт, даже когда все члены на месте и ZFS работает по GUID. Буквы могут смениться (`sdc`→`sdb`), и пул ждёт ручного Import. Не паниковать — данные целы, пока все члены `ONLINE` в `lsblk`/by-id.
- Идёт регулярный scrub (запускается ночью воскресенья).
- **`zpool` не в PATH для zsh truenas_admin** — вызывать через абсолютный путь `/sbin/zpool` или `/usr/sbin/zpool` (иначе `command not found`).
From 7243b6be8d8f82e8b3215046bbc34c525f1b8a24 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 17 Aug 2026 14:54:45 +0600
Subject: [PATCH 06/81] [2026-08-17] eagle:
family/how-to/truenas-infrastructure.md
---
family/how-to/truenas-infrastructure.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index e05d5a98..7bc5fbf0 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -1,6 +1,10 @@
# TrueNAS — инфраструктура
-> Обновлено: 2026-07-06
+> Обновлено: 2026-08-17
+
+> ## ⚠️⚠️ КРИТИЧНО (2026-08-17): пул RED_2TB вызывает KERNEL PANIC — все данные/контейнеры/в vault на этом пуле недоступны
+> **RED_2TB — это НЕ просто хранилище: на нём лежат конфиги docker (caddy, transmission, Home Assistant `/mnt/RED_2TB/docker/*`), данные Transmission/Arr, сам Obsidian vault и его git-репо (`/mnt/RED_2TB/storage/obsidian`, `/mnt/RED_2TB/storage/git/obsidian-vault.git`).** Этот пул сейчас в дауне — система в kernel panic bootloop из-за повреждённой space map (ошибка `adding existent segment to range tree` при `import -F`).
+> **Рецепт восстановления и полная картина — в `family/how-to/truenas-sata-ports-and-zfs-pools.md`** (раздел «Пулы»): тюнабели `zfs.zfs_recover=1 zfs.zil_replay_disable=1` в GRUB, затем аккуратный `zpool import`. ПОКА НЕ РЕШЕНО — инфраструктура, завязанная на `/mnt/RED_2TB`, не работает.
## Доступ
From 6c7b9fbb9ee57e5e3af493a0dff3bc748a0e4b4c Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 17 Aug 2026 16:24:53 +0600
Subject: [PATCH 07/81] [2026-08-17] eagle:
family/how-to/truenas-sata-ports-and-zfs-pools.md
personal/tech/truenas-zfs-panic-recovery.md
---
.../truenas-sata-ports-and-zfs-pools.md | 76 +++++++++----------
personal/tech/truenas-zfs-panic-recovery.md | 66 ++++++++++++++++
2 files changed, 102 insertions(+), 40 deletions(-)
create mode 100644 personal/tech/truenas-zfs-panic-recovery.md
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index c67f9783..c32b9394 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -1,10 +1,10 @@
# TrueNAS — SATA порты и пулы
-> Обновлено: 2026-08-17 ночь (два КРИТИЧНО: HGST упал и сломан; RED_2TB вызывает kernel panic — см. раздел пулов)
+> Обновлено: 2026-08-17 глубокая ночь (РО: HGST упал и сломан; RED_2TB импортирован READONLY — смонтировать datasets и выгрузить — см. раздел пулов)
-## ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №1: пул RED_2TB вызывает KERNEL PANIC (важнее HGST)
+## ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №1: пул RED_2TB импортирован READONLY, но datasets не смонтированы (данные ещё НЕ выгружены)
-См. раздел «Пулы TrueNAS» → блок «RED_2TB не импортируется — вызывает KERNEL PANIC». Это приоритет №1 — данные реально есть. Система в panic loop; рецепт: тюнабели `zfs.zfs_recover=1 zfs.zil_replay_disable=1` в GRUB, затем import.
+См. раздел «Пулы TrueNAS» → блок «RED_2TB readonly-импорт». **Прогресс:** пан-loop преодолён (см. рецепт ниже), RD_2TB импортирован readonly успешно. **Осталось смонтировать datasets вручную и выгрузить данные** — точка монтирования не создалась из-за read-only корневой ФС. Это приоритет №1 — данные есть.
## ⚠️⚠️ ПРОБЛЕМА №2: HGST 12TB УРОНЕН НА ПОЛ — вероятный слом после падения
@@ -14,11 +14,11 @@
2. **smartctl мог прочитать INFO-секцию** (`Model: HGST HUH721212ALE600`, `8CKD08RE`, FW `LEBDT3P2`, 512e, SATA 6.0 Gb/s, SMART enabled) — контроллер жив, линк поднят.
3. **На MacBook через USB-мост диск «щёлкает» и не шуршит головкой при старте** — признак механических проблем после удара (головки/мотор). КЛИК-ОФ-ДЕФ возможен, но также нельзя исключать нехватку питания через USB-мост на 12TB-диске.
-**Состояние:** не подтверждён полный отказ, но высокая вероятность аппаратного повреждения от падения. Сектор 0 (начало диска /GPT) повреждён точно. Масштаб (1 сектор или сотни) НЕ определён — длинный SMART-тест не удалось прогнать, т.к. диск щёлкает.
+**Состояние (на 2026-08-17):** **подтверждён аппаратный отказ — диск не ремонтопригоден.** Симптом: ритмичный «click-click, click-click» при работающем моторе = головки не могут выйти на дорожку/парковку (классический click of death после удара). Это механическое повреждение, НЕ лечится программно (SMART/прошивка), чинить может только профи-recovery и это дороже нового диска. **Данных на нём НЕТ** (не был добавлен в пул, только новая разметка sdf1/sdf2) — поэтому списан без recovery. Закрыто.
-**Что НЕ делать:** не махать диском, не запускать запись/ремонт сектора 0 до понимания масштаба, не использовать как свежий пул — доверие к диску утрачено. Если диск ещё вращается — прогонять длинный SMART-тест по SATA (с Molex-питанием) чтобы оценить `Current_Pending_Sector`.
+> ⚠️ Урок для будущего: enterprise-диск, уроненный на пол работающим — почти всегда аппаратная смерть (мотор/головки). Если на диске критичные данные — только профи-recovery, и шанс не 100%.
-> 🔁 ИСТОРИЯ ВОПРОСА: до падения диск был ПОЛНОСТЬЮ рабочим. Причина исходного «молчания» по SATA — PWDIS (см. ниже). После снятия 3.3V завёлся и линковался. Затем урон — теперь состояние под вопросом.
+> 🔁 ИСТОРИЯ ВОПРОСА: до падения диск был ПОЛНОСТЬЮ рабочим. Причина исходного «молчания» по SATA — PWDIS (см. ниже). После снятия 3.3V завёлся и линковался. Затем урон на пол — аппаратный отказ (click of death).
---
@@ -139,47 +139,43 @@ lspci -k | grep -i sata
- RED_2TB: HEALTH **ONLINE**, нераспределённый свободный диск (данные не реплицируются на sda2 — он отдельный, а не парный). Если redun-dancy важна — учесть, что sda2 без зеркала.
-### ⚠️ КРИТИЧНО (2026-08-17, после финального перетыкания): RED_2TB не импортируется — вызывает KERNEL PANIC
+### ✅ ПРОГРЕСС (2026-08-17 глубокая ночь): READONLY-импорт УСПЕШЕН, паника преодолена
-После перетыкания дисков на материнке пул **RED_2TB не поднялся автоматически**, и при попытке `zpool import -F` система упала в **kernel panic bootloop**:
+**Рабочий рецепт получения доступа к данным подтверждён на практике.** Члены пула физически на месте: `sda2`=WD2TB#1 (ata1), `sdb2`=WD4TB (ata2, буква сменилась), `sde2`=Seagate4TB (ata6).
-```
-panic - not syncing: zfs: adding existent segment to range tree
- ZFS: space_map_load / metaslab_activate / zio_dva_allocate → zfs_panic_recover
-```
-
-**Что это:** повреждение space map (метаданных ZFS) пула — один и тот же блок учтён дважды / неконсистентное отображение. При обычном импорте ZFS званивает panic по-умолчанию. **Это не обязательно полная потеря данных**, но структура пула повреждена.
-
-**Триггер (вероятный):** диски-члены отвалились во время работы/перезагрузки при перетыкании, или один из членов имеет скрытую проблему в области метаданных. Члены пула при этом физически на месте:
-- `sda2` = WD2TB#1 (ata1)
-- `sdb2` = WD4TB (ata2) — буква сменилась с `sdc` на `sdb`
-- `sde2` = Seagate4TB (ata6)
-
-**Сейчас система грузится В panic loop каждый раз** (автоимпорт RED_2TB валит ядро на старте).
-
-#### ✅ РЕЦЕПТ ВОССТАНОВЛЕНИЯ (подтверждён web-источниками, openzfs #15030 + треды TrueNAS)
-
-**Панику в ZFS можно превратить в warning тремя тюнабелями** (передача как параметров ядра в GRUB):
-
-1. **В загрузочном меню GRUB** (при старте держать Shift/ESC) выбрать ядро TrueNAS, нажать `e`, в строке с `linux`/`vmlinuz` добавить в конец:
- ```
- zfs.zfs_recover=1 zfs.zil_replay_disable=1
- ```
- `Ctrl+X` загрузиться. Это даёт системе дойти до консоли/WebUI без паники (Panic→warning).
-
-2. **Под root импортировать пул аккуратно** (не форс-катастрофически с наскоку):
+**Что сработало (пошагово):**
+1. Тюнабели `zfs.zfs_recover=1 zfs.zil_replay_disable=1` в GRUB → **НЕ помогли** (всё равно паника). `rd.break=pre-mount` и `rd.break` → тоже НЕ помогли.
+2. **ЕДИНСТВЕННЫЙ рабочий способ загрузиться без паники:** физически ОТКЛЮЧИТЬ все диски проблемного пула (чтобы дойти до рабочего shell/WebUI), затем вставить обратно.
+ - Включить с отключёнными дисками RED_2TB (WD2TB#, WD4TB, Seagate4TB) → boot-pool поднимается, паники нет.
+ - Вставить диски обратно (горячо) в те же порты.
+3. **READONLY-импорт не паникует** (по mav из iXsystems: «read-only import doesn't even read space maps»):
```bash
- /sbin/zpool import # какие пулы видит, без монтирования
- /sbin/zpool import -n RED_2TB # dry-run — нужен ли -F и не падает ли
- /sbin/zpool import -F RED_2TB # только если dry-run показал, что требуется -F
+ zpool import -o readonly=on RED_2TB
```
- > В треде TrueNAS подтверждено: `zpool import -f -F -X` «не срабатывало», а с тюнабелями в GRUB + импортом — срабатывает. Сначала тюнабели, потом import.
+ → **`Import was successful`** — паники нет, метаданные прочитаны! (попытка `zpool import -nRED_2TB` без `-F` невозможна; `-n` требует `-F` синтаксиса.)
+4. **⚠️ Текущий блок:** после readonly-импорта выдаются ошибки монтирования:
+ ```
+ cannot mount '/RED_2TB': failed to create mountpoint: Read-only file system
+ Import was successful, but unable to mount some datasets
+ ```
+ Причина: корневая ФС readonly (после обхода паники). **Точки монтирования не создаются.**
-3. **После импорта:** `/sbin/zpool status RED_2TB` → оценить члена с ошибками. Снять важные данные с пула на другие диски при первой возможности (импорт `-F` + recover может ценой части изменений). Честно проверить RAM/ECC — в треде #31410 это был реальный фактор повреждения.
+**СЛЕДУЮЩИЙ ШАГ (не завершён):** смонтировать datasets вручную в существующую/записываемую точку (корневая / readonly). Порядок:
+```bash
+zfs list -r RED_2TB # какие datasets есть (zfs list работает без mount)
+# смонтировать в существующую пустую папку, не создавая новую (или через tmpfs/rw-точку)
+# например:
+# mkdir -p /mnt/rec 2>/dev/null; (если /mnt rw — иначе выбрать rw-точку)
+# zfs set mountpoint=/mnt/rec RED_2TB (или mount -o)
+# затем выгружать данные на запасные диски rsync
+```
-> ⚠️ Пока пул не восстановлен, **НЕ запускать повторный автоимпорт/`-F` сипломи** — каждый заход углубляет риск. Сначала тюнабели в GRUB.
+**Критичные ограничения (выяснены по ходу):**
+- `zpool import` требует **root** (`some devices require root privileges`) — truenas_admin НЕ root (sudo просит пароль). Все операции import/mount — под root.
+- Раньше при readonly-импорте в тредах было: datasets показываются но пути «not found», и полностью нечитаемы (permission denied) если данные повреждены. Здесь readonly-импорт прошёл чисто (no panic) — но монтирование ещё не доведено до конца.
+- **Данные на пуле важно выгрузить на другие диски** (пул восстановится только пересозданием — аппаратная метаданная ошибка). Это главный незавершённый шаг.
-> ⚠️ Альтернативный обход для диагностики без паники: импорт **readonly** (`zpool import -o readonly=on RED_2TB`) может не паниковать, но данные могут быть недоступны из-за permission errors.
+> ⚠️ После успешного монтирования и выгрузки — обновить таблицу членов пула и закрыть этот блок.
**Опция добавить WD2TB#2 как зеркало к WD2TB#1** (`zpool attach RED_2TB `): валидна, но ТОЛЬКО после успешного импорта пула и его стабилизации. Не пытаться до восстановления.
diff --git a/personal/tech/truenas-zfs-panic-recovery.md b/personal/tech/truenas-zfs-panic-recovery.md
new file mode 100644
index 00000000..c9d5b479
--- /dev/null
+++ b/personal/tech/truenas-zfs-panic-recovery.md
@@ -0,0 +1,66 @@
+# TrueNAS — Восстановление ZFS пула при kernel panic (space map corruption)
+
+> Создано 2026-08-17. Зеркало скила `truenas-zfs-panic-recovery`.
+
+## Когда это нужно
+
+Пул TrueNAS не импортируется и вызывает **kernel panic / boot loop**:
+```
+panic - not syncing: zfs: adding existent segment to range tree
+ ZFS: space_map_load / metaslab_activate / zio_dva_allocate → zfs_panic_recover
+```
+Это **повреждение space map (метаданных ZFS)**. НЕ обязательно потеря данных, но структура пула повреждена. Read-write импорт паникует, т.к. читает битые space maps.
+
+## Ключевая идея
+
+**Read-only import НЕ читает space maps и НЕ паникует** (mav/iXsystems: "read-only imports don't even read space maps"). Стратегия: обойти панику → readonly-импорт → выгрузить данные → пересоздать пул.
+
+**Пул НЕ «чинится»** — только выгрузка данных и пересоздание.
+
+## Проверенные шаги (2026-08-17, TrueNAS SCALE 24.10)
+
+### 1. Загрузка без паники — единственный рабочий способ:
+
+**Физически отключить все диски проблемного пула** → boot-pool поднимается, паники нет → дошли до shell/WebUI → **воткнуть диски обратно (горячо)** в те же порты.
+
+**Что НЕ работает (проверено):**
+- Тюнабели в GRUB `zfs.zfs_recover=1 zfs.zil_replay_disable=1` — не помогли
+- `rd.break=pre-mount` и `rd.break` — не помогли
+- `zfsforce=1` — не помогает
+
+### 2. Readonly-импорт (под root):
+
+```bash
+/usr/sbin/zpool import -o readonly=on RED_2TB
+```
+→ `Import was successful, but unable to mount some datasets` — это нормально/ожидаемо.
+
+### 3. Монтирование datasets вручную (root ФС readonly):
+
+Ошибка `cannot mount '/RED_2TB': failed to create mountpoint: Read-only file system` — из-за READONLY корневой ФС. Монтируй вручную в записываемую точку:
+```bash
+zfs list -r RED_2TB # список datasets (работает без mount)
+zfs set mountpoint=/mnt/rec RED_2TB # или mount -o
+```
+
+### 4. Выгрузка:
+
+```bash
+rsync -av --ignore-existing --ignore-errors // <резервный-диск>/
+```
+Лучше в tmux. Затем `zpool destroy` + пересоздать пул.
+
+## Подводные камни
+
+- `zpool` не в PATH для zsh-юзеров (`command not found`) — использовать `/sbin/zpool` или `/usr/sbin/zpool`.
+- `zpool import` требует **root** (`some devices require root privileges`). `truenas_admin` НЕ root, sudo просит пароль.
+- `zpool import -n RED_2TB` — нелегальный синтаксис без `-F` (`-n` только с `-F`).
+- **НЕ повторять `import -F` настойчиво** — углубляет риск. Сначала readonly.
+- `zpool import -f -F -X` НЕ работает для этой паники (TrueNAS forum).
+- Данные могут показать `Permission denied` если сами данные повреждены (в тяжёлых случаях — частичное восстановление).
+- **Проверить RAM** — на non-ECC (P8H77-V) повреждение space map часто от плохой памяти. memtest.
+- Буквы дисков shift-аются — сверять по by-id/модели, не по буквам.
+
+## Root cause
+
+`adding existent segment to range tree` = openzfs #15030 / #13483. Триггер: члены пула отвалились при перетыкании/перезагрузке, либо RAM corruption (non-ECC).
From 399ebb9ad4aae0ad2788230059afd36220988215 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 17 Aug 2026 17:24:58 +0600
Subject: [PATCH 08/81] [2026-08-17] eagle:
family/how-to/truenas-infrastructure.md
family/how-to/truenas-sata-ports-and-zfs-pools.md
personal/tech/truenas-zfs-panic-recovery.md
---
family/how-to/truenas-infrastructure.md | 8 ++--
.../truenas-sata-ports-and-zfs-pools.md | 40 +++++++++++++------
personal/tech/truenas-zfs-panic-recovery.md | 15 ++++++-
3 files changed, 45 insertions(+), 18 deletions(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 7bc5fbf0..4ddf7ae1 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -1,10 +1,10 @@
# TrueNAS — инфраструктура
-> Обновлено: 2026-08-17
+> Обновлено: 2026-08-17 (конец сессии)
-> ## ⚠️⚠️ КРИТИЧНО (2026-08-17): пул RED_2TB вызывает KERNEL PANIC — все данные/контейнеры/в vault на этом пуле недоступны
-> **RED_2TB — это НЕ просто хранилище: на нём лежат конфиги docker (caddy, transmission, Home Assistant `/mnt/RED_2TB/docker/*`), данные Transmission/Arr, сам Obsidian vault и его git-репо (`/mnt/RED_2TB/storage/obsidian`, `/mnt/RED_2TB/storage/git/obsidian-vault.git`).** Этот пул сейчас в дауне — система в kernel panic bootloop из-за повреждённой space map (ошибка `adding existent segment to range tree` при `import -F`).
-> **Рецепт восстановления и полная картина — в `family/how-to/truenas-sata-ports-and-zfs-pools.md`** (раздел «Пулы»): тюнабели `zfs.zfs_recover=1 zfs.zil_replay_disable=1` в GRUB, затем аккуратный `zpool import`. ПОКА НЕ РЕШЕНО — инфраструктура, завязанная на `/mnt/RED_2TB`, не работает.
+> ## ⚠️⚠️ КРИТИЧНО (2026-08-17): пул RED_2TB — kernel panic, данные НЕ выгружены, нужен root-доступ
+> **RED_2TB — это НЕ просто хранилище: на нём лежат конфиги docker (caddy, transmission, Home Assistant `/mnt/RED_2TB/docker/*`), данные Transmission/Arr, сам Obsidian vault и его git-репо (`/mnt/RED_2TB/storage/obsidian`, `/mnt/RED_2TB/storage/git/obsidian-vault.git`).** Пул в kernel panic при rw-импорте (повреждение space map, `adding existent segment to range tree`). **ПОКА НЕ РЕШЕНО.**
+> **Статус на конец 2026-08-17:** пан-loop преодолён (загрузка с физически отключёнными дисками пула → readonly-импорт `zpool import -o readonly=on RED_2TB` успешен, паники нет). **НО datasets не смонтированы и данные НЕ выгружены.** Блокер — **нужен root** (truenas_admin не root; sudo просит пароль). Целевой диск для выгрузки: **IronWolf 12TB** подключён (`sdc`). Полный рецепт и картина — в `family/how-to/truenas-sata-ports-and-zfs-pools.md` (раздел «Пулы») и `personal/tech/truenas-zfs-panic-recovery.md`. ВАЖНО: тюнабели GRUB (`zfs.zfs_recover` и др.) и `rd.break` НЕ помогли — рабочий способ только «диски отключены при загрузке + readonly-импорт». Инфраструктура на `/mnt/RED_2TB` не работает.
## Доступ
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index c32b9394..f611abd3 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -1,10 +1,16 @@
# TrueNAS — SATA порты и пулы
-> Обновлено: 2026-08-17 глубокая ночь (РО: HGST упал и сломан; RED_2TB импортирован READONLY — смонтировать datasets и выгрузить — см. раздел пулов)
+> Обновлено: 2026-08-17 (через время после readonly-импорта). СТАТУС: данные RED_2TB ещё НЕ выгружены. IronWolf 12TB подключён как целевой диск для спасения. Блокер: для `zpool import`/`mount` нужен root, а `truenas_admin` не root. HGST сломан (см. ниже).
-## ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №1: пул RED_2TB импортирован READONLY, но datasets не смонтированы (данные ещё НЕ выгружены)
+## ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №1: пул RED_2TB импортирован READONLY, но datasets не смонтированы, данные НЕ выгружены
-См. раздел «Пулы TrueNAS» → блок «RED_2TB readonly-импорт». **Прогресс:** пан-loop преодолён (см. рецепт ниже), RD_2TB импортирован readonly успешно. **Осталось смонтировать datasets вручную и выгрузить данные** — точка монтирования не создалась из-за read-only корневой ФС. Это приоритет №1 — данные есть.
+Пан-loop преодолён (рецепт ниже). RED_2TB импортирован readonly удачно — **паники больше нет**. **Осталось:** смонтировать datasets вручную и выгрузить данные на IronWolf. Это приоритет №1 — данные есть, но пул восстановится только пересозданием, so спасаем.
+
+**ТЕКУЩИЙ БЛОКЕР (конец сессии):**
+- `zpool import`/`mount` требуют **root** (`some devices require root privileges`). `truenas_admin` НЕ root (sudo просит пароль). Все операции с пулом — только под root.
+- **Нужно добыть root:** SSH → включить root login (`PermitRootLogin`), или выполнять команды через WebUI-админ/консоль.
+- **IronWolf 12TB подключён к материнке** (sdc) как целевой диск для спасения данных. На нём ещё не создан пул-приёмник.
+- **WD 2TB#1 (sda2, simplex-член RED_2TB) СЕЙЧАС ОТКЛЮЧЁН** — чтобы загрузиться без паники его отсоединяли; на конец сессии он не в системе. RED_2TB без него может импортироваться, но его данные частично будут недоступны.
## ⚠️⚠️ ПРОБЛЕМА №2: HGST 12TB УРОНЕН НА ПОЛ — вероятный слом после падения
@@ -48,11 +54,11 @@
| ata6 (SATA3G_4) | SATA 2 | 3 Gb/s | синий | **sde** — Seagate 4TB (ST4000DM004) | WFN66CM2 |
| *(карта ASM1166)* | — | — | — | **НЕ В СЛОТЕ** (вытащена) | — |
-**⚠️ Фактическое положение на конец сессии (2026-08-17):**
-- **Карта ASMedia ASM1166 вытащена** — `lspci` показывает только Intel H77 (`00:1f.2`) и не относящийся к делу PCIe-to-PCI bridge ASM1083. WD 2TB#2 перенесён с карты на материнку.
-- **⭐ IronWolf 12TB теперь ПОДКЛЮЧЁН и ЛИНКУЕТСЯ** — `sdc` = `ST12000NT001-3LX101`, 10.9T, by-id `WV700FQ5`. Он не в пуле (новый диск).
-- **WD 2TB #2 (`WD-WXJ2A31CUNNT`) НАМЕРЕННО отключён** пользователем (2026-08-17). Раньше был `sdb` (перенесён с карты), затем Alex его отключил «это норм». Сейчас отсутствует по замыслу, а не по ошибке.
-- **HGST 12TB (`HUH721212ALE600`)** — был подключён по USB (виден как `sdf`, model HGST, WWN `0x5000cca26fefbc05`) и после удара о пол НЕ работает надёжно (см. блок КРИТИЧНО в самом верху). На текущий момент считаем его вышедшим из строя.
+**⚠️ Фактическое положение на конец сессии (2026-08-17, после подключения IronWolf для спасения):**
+- **Буквы разъехались ещё раз.** Финальный `lsscsi`: **sda=WD4TB**, **sdb=Kingston boot**, **sdc=⭐ IronWolf 12TB (целевой для спасения)**, **sdd=Seagate4TB**. **WD 2TB#1 (`WD-WXH2A31D5HDJ`) СЕЙЧАС ОТКЛЮЧЁН / НЕ в системе** (отсоединяли, чтобы загрузиться без паники).
+- **⭐ IronWolf 12TB подключён к материнке и ЛИНКУЕТСЯ** — `sdc` = `ST12000NT001-3LX101`, 10.9T, by-id `WV700FQ5`. Он свободен (не в пуле) и определён как **целевой диск для спасения данных с RED_2TB**. На нём ещё НЕ создан пул-приёмник.
+- **Карта ASMedia ASM1166 вытащена** — `lspci` показывает только Intel H77 (`00:1f.2`). WD 2TB#2 перенесён с карты, затем был намеренно отключён пользователем.
+- **HGST 12TB (`HUH721212ALE600`)** — УРОНЕН НА ПОЛ, аппаратный отказ (см. блок КРИТИЧНО), считается вышедшим из строя.
> ⚠️ Буквы (`sda`/`sdb`) **shift-аются при перетыкании** и сейчас разъехались: `sdb` = WD4TB (не WD2TB#2!), `sdc` = IronWolf. **Всегда сверять по by-id/модели, не полагаться на буквы.**
@@ -132,12 +138,12 @@ lspci -k | grep -i sata
Имена пулов, топология и члены — проверять статус через `/sbin/zpool` (см. ниже про PATH).
-| Пул | Размер | Члены (по /dev со стабильным подключением) | Топология | Монтирование |
+| Пул | Размер | Члены (по `/sbin/zpool status` от 2026-08-17) | Топология | Монтирование |
|-----|--------|-----------|-----------|--------------|
-| **RED_2TB** | 5.44T (4.60T занято) | mirror: `sdb2`/`sdc2` (WD4TB) + `sde2` (Seagate4TB); отдельно `sda2` (WD2TB) | 1 диск (sda2) + mirror-1 (2 диска) | `/mnt` — **⚠️ ПОТРЕБУЕТСЯ IMPORT, см. ниже** |
-| **boot-pool** | 111G (7.65G занято) | `sdd3` (Kingston 120GB boot SSD) | 1 диск | — |
+| **RED_2TB** | 5.44T (4.60T занято) | **state ONLINE, 0 0 0 ошибок, "No known data errors"**: `sdd2` (WD2TB#1) + mirror-1 `{sdc2 WD4TB, sdb2 Seagate4TB}` — буквы по последнему status, сверять by-id (буквы shift-аются) | 1 диsk (`WD2TB#1`) + mirror-1 (WD4TB+Seagate) | `/mnt` — **readonly-импорт, datasets не смонтированы, данные НЕ выгружены** |
+| **boot-pool** | 111G (7.65G занято) | `sdb3` (Kingston 120GB boot SSD) | 1 диск | — |
-- RED_2TB: HEALTH **ONLINE**, нераспределённый свободный диск (данные не реплицируются на sda2 — он отдельный, а не парный). Если redun-dancy важна — учесть, что sda2 без зеркала.
+- RED_2TB: HEALTH **ONLINE** по `zpool status` (readonly-импорт не паникует), scrub «завис» на ~72% (при readonly не доходит — но это НЕ фикс, см. ниже). Все ошибки 0 0 0 — **данные целы**. Один vdev (`WD2TB#1`) simplex — без зеркала; WD2TB#1 сейчас физически отключён.
### ✅ ПРОГРЕСС (2026-08-17 глубокая ночь): READONLY-импорт УСПЕШЕН, паника преодолена
@@ -171,10 +177,18 @@ zfs list -r RED_2TB # какие datasets есть (zfs list работает
```
**Критичные ограничения (выяснены по ходу):**
-- `zpool import` требует **root** (`some devices require root privileges`) — truenas_admin НЕ root (sudo просит пароль). Все операции import/mount — под root.
+- `zpool import` требует **root** (`some devices require root privileges`) — truenas_admin НЕ root (sudo просит пароль). Все операции import/mount — под root. **На конец сессии это главный блокер** — без root не смонтировать/выгрузить.
- Раньше при readonly-импорте в тредах было: datasets показываются но пути «not found», и полностью нечитаемы (permission denied) если данные повреждены. Здесь readonly-импорт прошёл чисто (no panic) — но монтирование ещё не доведено до конца.
- **Данные на пуле важно выгрузить на другие диски** (пул восстановится только пересозданием — аппаратная метаданная ошибка). Это главный незавершённый шаг.
+**🧪 ИТОГ ПО «ПОЧИНИТЬ ПУЛ» (2026-08-17, важно для будущих сессий):**
+- **In-place «починки» НЕТ.** По mav (лидер OS-команды iXsystems) в тредах: *"I don't know a way to recover from this situation without data offload and pool recreation"*. Паника на space map не лечится флагами.
+- **Опции это НЕ решают:** `zfs.zfs_recover=1`, `zfs.zil_replay_disable=1` (в GRUB), `rd.break=pre-mount`, `rd.break` — все НЕ помогли (паника осталась). Readonly-импорт («doesn't even read space maps») — единственное, что не паникует и даёт доступ к данным.
+- **`-F` (восстановление) уже показал панику** в этой сессии (`adding existent segment to range tree`). Повторный `-F` на повреждённом пуле рискован — может углубить повреждение. Не жать вслепую.
+- **`zpool attach RED_2TB ` (идея зеркала на 2 WD2TB) НЕ выполнима в текущем состоянии**: attach — это write, он пишет в space maps (обновляет метаданные) → паника. Readonly-импорт не даст attach; переимпорт в rw вернёт панику. Также 2TB не влезет под 4.6T данных целиком. Идея зеркала валидна только ПОСЛЕ пересоздания пула из спасённых данных.
+- **Правильная дорога (единственная надёжная):** readonly-импорт → смонтировать datasets вручную → выгрузить данные на целевой пул (IronWolf 12TB) → пересоздать RED_2TB → вернуть данные. Попытки «починить rw» возможны, но только ПОСЛЕ спасения данных и на свой риск.
+- **Для «100% восстановления с правами»:** `rsync -aHAX` (права, владельцы, ACL, xattr, хардлинки) или `zfs send | zfs recv` (идеально байт-в-байт со снимками, но требует пула-приёмника и снимка).
+
> ⚠️ После успешного монтирования и выгрузки — обновить таблицу членов пула и закрыть этот блок.
**Опция добавить WD2TB#2 как зеркало к WD2TB#1** (`zpool attach RED_2TB `): валидна, но ТОЛЬКО после успешного импорта пула и его стабилизации. Не пытаться до восстановления.
diff --git a/personal/tech/truenas-zfs-panic-recovery.md b/personal/tech/truenas-zfs-panic-recovery.md
index c9d5b479..fc042d5f 100644
--- a/personal/tech/truenas-zfs-panic-recovery.md
+++ b/personal/tech/truenas-zfs-panic-recovery.md
@@ -50,10 +50,23 @@ rsync -av --ignore-existing --ignore-errors // <резервный-дис
```
Лучше в tmux. Затем `zpool destroy` + пересоздать пул.
+**Целевой диск для выгрузки (2026-08-17):** **IronWolf 12TB** подключён к материнке (`sdc`, by-id `WV700FQ5`, 10.9T, свободен/не в пуле) — создай на нём пул-приёмник и копируй туда. Места достаточно (10.9T > 4.6T данных).
+
+**Что на RED_2TB спасать (структура datasets от 2026-08-17):**
+- Критично (пользовательские данные): `storage` (3.24T), `backup` (287G), `Photos` (146G), `Edu` (214G), `TimeMachine` (+`guest`, 277G), `old-bu` (65.5G), `openbsd` (10.2G).
+- Конфиги (если нужно): `docker` (2.98G), `iocage/*` jails (plex, emby, homeassistant, transmission, worker), `ix-apps` (24G).
+- Служебное не критично: `RED_2TB/.system/*`.
+- Итого ~4.6T. Для «100% восстановления с правами»: `rsync -aHAX` или `zfs send | zfs recv`.
+
+### 5. Пул НЕ чинится in-place — только пересоздание
+
+`zpool attach` (идея зеркала на 2 WD2TB) НЕ выполним в текущем состоянии — attach это write → паника. Всегда: спасти → destroy → recreate.
+
## Подводные камни
- `zpool` не в PATH для zsh-юзеров (`command not found`) — использовать `/sbin/zpool` или `/usr/sbin/zpool`.
-- `zpool import` требует **root** (`some devices require root privileges`). `truenas_admin` НЕ root, sudo просит пароль.
+- `zpool import` требует **root** (`some devices require root privileges`). `truenas_admin` НЕ root, sudo просит пароль. **На конец 2026-08-17 это главный блокер** — для импорта/монтирования/выгрузки нужен root (через WebUI-админ, консоль, или включив `PermitRootLogin` в SSH).
+- **WD 2TB#1 (`sdd2` simplex-член RED_2TB) сейчас отключён** — чтобы загрузиться без паники его отсоединяли. RED_2TB импортируется без него (mirror-1 жив), но его данные частично недоступны. Учесть при восстановлении.
- `zpool import -n RED_2TB` — нелегальный синтаксис без `-F` (`-n` только с `-F`).
- **НЕ повторять `import -F` настойчиво** — углубляет риск. Сначала readonly.
- `zpool import -f -F -X` НЕ работает для этой паники (TrueNAS forum).
From 6eecf719145a10e95dfcc0963026ce44d9ec3046 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 18 Aug 2026 11:19:58 +0600
Subject: [PATCH 09/81] [2026-08-18] eagle:
family/how-to/truenas-infrastructure.md
family/how-to/truenas-sata-ports-and-zfs-pools.md
personal/tech/truenas-zfs-panic-recovery.md
---
family/how-to/truenas-infrastructure.md | 8 ++---
.../truenas-sata-ports-and-zfs-pools.md | 36 ++++++++++++++++---
personal/tech/truenas-zfs-panic-recovery.md | 17 ++++++---
3 files changed, 48 insertions(+), 13 deletions(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 4ddf7ae1..9d4b0eca 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -1,10 +1,10 @@
# TrueNAS — инфраструктура
-> Обновлено: 2026-08-17 (конец сессии)
+> Обновлено: 2026-08-18 (datasets смонтированы, выгрузка rsync идёт)
-> ## ⚠️⚠️ КРИТИЧНО (2026-08-17): пул RED_2TB — kernel panic, данные НЕ выгружены, нужен root-доступ
-> **RED_2TB — это НЕ просто хранилище: на нём лежат конфиги docker (caddy, transmission, Home Assistant `/mnt/RED_2TB/docker/*`), данные Transmission/Arr, сам Obsidian vault и его git-репо (`/mnt/RED_2TB/storage/obsidian`, `/mnt/RED_2TB/storage/git/obsidian-vault.git`).** Пул в kernel panic при rw-импорте (повреждение space map, `adding existent segment to range tree`). **ПОКА НЕ РЕШЕНО.**
-> **Статус на конец 2026-08-17:** пан-loop преодолён (загрузка с физически отключёнными дисками пула → readonly-импорт `zpool import -o readonly=on RED_2TB` успешен, паники нет). **НО datasets не смонтированы и данные НЕ выгружены.** Блокер — **нужен root** (truenas_admin не root; sudo просит пароль). Целевой диск для выгрузки: **IronWolf 12TB** подключён (`sdc`). Полный рецепт и картина — в `family/how-to/truenas-sata-ports-and-zfs-pools.md` (раздел «Пулы») и `personal/tech/truenas-zfs-panic-recovery.md`. ВАЖНО: тюнабели GRUB (`zfs.zfs_recover` и др.) и `rd.break` НЕ помогли — рабочий способ только «диски отключены при загрузке + readonly-импорт». Инфраструктура на `/mnt/RED_2TB` не работает.
+> ## ⚠️⚠️ АКТИВНО (2026-08-18): пул RED_2TB — kernel panic, datasets смонтированы, выгрузка данных rsync на IronWolf
+> **RED_2TB — это НЕ просто хранилище: на нём лежат конфиги docker (caddy, transmission, Home Assistant `/mnt/RED_2TB/docker/*`), данные Transmission/Arr, сам Obsidian vault и его git-репо (`/mnt/RED_2TB/storage/obsidian`, `/mnt/RED_2TB/storage/git/obsidian-vault.git`).** Пул в kernel panic при rw-импорте (повреждение space map, `adding existent segment to range tree`). **ПОКА НЕ ВОССТАНОВЛЕНО** — данные в процессе спасения.
+> **Статус на 2026-08-18:** пан-loop преодолён (загрузка с физически отключёнными дисками → readonly-импорт `zpool import -o readonly=on RED_2TB` успешен, паники нет). **ПРОГРЕСС: дочерние datasets смонтированы под root** (`zfs mount -a` после `mount -o remount,rw /`) — затем rsync на целевой пул **DEST** (IronWolf 12TB, `/mnt/IRONWOLF`, rw, 10.6T свободно). ⚠️ Первая попытка rsync дала только 355G из-за НЕ смонтированных дочерних datasets — после монтирования перезапущен. **`zfs send|recv` НЕ работает** на этом пуле (signal received / Bad file descriptor). Полный рецепт — в `family/how-to/truenas-sata-ports-and-zfs-pools.md` (раздел «Пулы», блок 2026-08-18) и `personal/tech/truenas-zfs-panic-recovery.md`. Инфраструктура на `/mnt/RED_2TB` пока не работает (только спасение данных).
## Доступ
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index f611abd3..1f33d908 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -1,15 +1,15 @@
# TrueNAS — SATA порты и пулы
-> Обновлено: 2026-08-17 (через время после readonly-импорта). СТАТУС: данные RED_2TB ещё НЕ выгружены. IronWolf 12TB подключён как целевой диск для спасения. Блокер: для `zpool import`/`mount` нужен root, а `truenas_admin` не root. HGST сломан (см. ниже).
+> Обновлено: 2026-08-18 (datasets смонтированы под root, rsync перезапущен). СТАТУС: данные RED_2TB на этапе выгрузки на IronWolf — смонтированные дочерние datasets готовы к rsync. IronWolf 12TB = целевой пул `DEST` (rw, 10.6T свободно). HGST сломан (см. ниже).
-## ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №1: пул RED_2TB импортирован READONLY, но datasets не смонтированы, данные НЕ выгружены
+## ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №1: пул RED_2TB импортирован READONLY, datasets смонтированы, данные выгружаются rsync
-Пан-loop преодолён (рецепт ниже). RED_2TB импортирован readonly удачно — **паники больше нет**. **Осталось:** смонтировать datasets вручную и выгрузить данные на IronWolf. Это приоритет №1 — данные есть, но пул восстановится только пересозданием, so спасаем.
+Пан-loop преодолён (рецепт ниже). RED_2TB импортирован readonly удачно — **паники больше нет**. **ПРОГРЕСС 2026-08-18:** дочерние datasets (`storage`, `backup`, `Photos`, `Edu`, `docker`, `old-bu`, `TimeMachine/guest`, `iocage/*`) **успешно смонтированы под root** (`zfs mount -a` + при необходимости `mount -o remount,rw /`). Данные видны. Выгрузка на IronWolf идёт **rsync** (см. блок ниже).
**ТЕКУЩИЙ БЛОКЕР (конец сессии):**
- `zpool import`/`mount` требуют **root** (`some devices require root privileges`). `truenas_admin` НЕ root (sudo просит пароль). Все операции с пулом — только под root.
- **Нужно добыть root:** SSH → включить root login (`PermitRootLogin`), или выполнять команды через WebUI-админ/консоль.
-- **IronWolf 12TB подключён к материнке** (sdc) как целевой диск для спасения данных. На нём ещё не создан пул-приёмник.
+- **IronWolf 12TB подключён к материнке** (sdc) как целевой диск для спасения данных. На нём **создан пул-приёмник `DEST`** (rw, ONLINE, ~10.6T свободно), монтируется на `/mnt/IRONWOLF`. Первая попытка rsync скопировала только 355G (см. блок 2026-08-18 ниже).
- **WD 2TB#1 (sda2, simplex-член RED_2TB) СЕЙЧАС ОТКЛЮЧЁН** — чтобы загрузиться без паники его отсоединяли; на конец сессии он не в системе. RED_2TB без него может импортироваться, но его данные частично будут недоступны.
## ⚠️⚠️ ПРОБЛЕМА №2: HGST 12TB УРОНЕН НА ПОЛ — вероятный слом после падения
@@ -191,6 +191,34 @@ zfs list -r RED_2TB # какие datasets есть (zfs list работает
> ⚠️ После успешного монтирования и выгрузки — обновить таблицу членов пула и закрыть этот блок.
+### 🆕 ПРОГРЕСС (2026-08-18): datasets смонтированы под root, выгрузка идёт rsync — НЕ send|recv
+
+**Смонтирование дочерних datasets — РАБОТАЕТ под root.** Это снимает блокер «datasets не смонтированы» из прошлой сессии:
+```bash
+mount -o remount,rw / # если корневая ФС readonly мешает создавать mountpoint-ы
+zfs mount -a # смонтировать все datasets
+# проверить:
+df -h /RED_2TB # должно показать ~4.6T, а НЕ 1.1T/362G родительского
+ls /RED_2TB/storage | head # должно быть не пусто (Cartoons, Downloads, Movies...)
+```
+После `zfs mount -a` в `mount` видны все дочерние datasets на `/RED_2TB/*` (все `ro` — readonly, для чтения rsync это норм).
+
+**⚠️ СМОНТИРОВАТЬ datasets под `truenas_admin` НЕЛЬЗЯ** — `zfs mount -a` даёт `Insufficient privileges`. Монтирование и выгрузка — только **под root**.
+
+**🐛 Причина «только 355G» на первой выгрузке rsync (разгадана):** первая попытка `rsync /RED_2TB/ /mnt/IRONWOLF/` запускалась, когда **дочерние datasets НЕ были смонтированы**, поэтому `/RED_2TB/storage` (3.24T), `/RED_2TB/backup` (287G) и т.д. были **пустыми mountpoint-дирами**. rsync скопировал только ~355G, лежащих прямо в родительском dataset `RED_2TB` (REFER 362G: `.DS_Store`, `dedupe_rm.sh`, `files.txt.xz`, `docker.bak`, `system`, `immich-photos-upload`). **rsync не сломался — ему просто нечего было копировать из не смонтированных дочерних datasets.** После монтирования данных стало ~4.6T — rsync перезапущен.
+
+**❌ `zfs send | zfs recv` НЕ работает на повреждённом пуле — использовать rsync:** при `zfs send RED_2TB/storage | zfs recv DEST/storage` (под root) recv падает с `signal received` / `Bad file descriptor` / `IOT instruction (core dumped)`, exit 1. ZFS не может прочитать данные/метаданные из пула с повреждённой space map на лету при stream-чтении. **rsync — правильный и единственный надёжный способ** (переживает ошибки чтения отдельных файлов, exit 23, копирует остальное).
+
+**⚠️ ЛОВУШКА «zfs send -n» (dry-run) НЕ подтверждает права send:** под `truenas_admin` `zfs send -n RED_2TB/docker` возвращал **exit 0 без ошибок** — это ОБМАНЧИВО. Реальный `zfs send` (без `-n`) даёт `warning: cannot send 'RED_2TB/docker': permission denied`. Дочерние datasets доступны только root (ACL root-only). Не доверять dry-run для проверки прав send.
+
+**Команды выгрузки (под root):**
+```bash
+rsync -aHAX --info=progress2 /RED_2TB/ /mnt/IRONWOLF/ > /tmp/rsync2.log 2>&1 &
+# прогресс:
+while true; do clear; date +%H:%M:%S; du -sh /mnt/IRONWOLF/; tail -3 /tmp/rsync2.log; pgrep -a rsync >/dev/null && echo RUNNING || echo DONE; sleep 15; done
+```
+**Итог выгрузки:** после монтирования datasets и перезапуска rsync должны лечь все ~4.6T (storage, backup, Edu, Photos, TimeMachine/guest, old-bu, docker...). После завершения — сверка `du -sh /mnt/IRONWOLF/` vs `zfs list RED_2TB` и обновить таблицу членов пула.
+
**Опция добавить WD2TB#2 как зеркало к WD2TB#1** (`zpool attach RED_2TB `): валидна, но ТОЛЬКО после успешного импорта пула и его стабилизации. Не пытаться до восстановления.
diff --git a/personal/tech/truenas-zfs-panic-recovery.md b/personal/tech/truenas-zfs-panic-recovery.md
index fc042d5f..9ec6f3c5 100644
--- a/personal/tech/truenas-zfs-panic-recovery.md
+++ b/personal/tech/truenas-zfs-panic-recovery.md
@@ -35,21 +35,27 @@ panic - not syncing: zfs: adding existent segment to range tree
```
→ `Import was successful, but unable to mount some datasets` — это нормально/ожидаемо.
-### 3. Монтирование datasets вручную (root ФС readonly):
+### 3. Монтирование datasets (2026-08-18: подтверждено под root)
-Ошибка `cannot mount '/RED_2TB': failed to create mountpoint: Read-only file system` — из-за READONLY корневой ФС. Монтируй вручную в записываемую точку:
+Если корневая ФС readonly мешает создавать mountpoint-ы (`cannot mount '/RED_2TB': failed to create mountpoint: Read-only file system`) — сначала сделать root ФС rw, затем смонтировать всё:
```bash
-zfs list -r RED_2TB # список datasets (работает без mount)
-zfs set mountpoint=/mnt/rec RED_2TB # или mount -o
+mount -o remount,rw /
+zfs mount -a
```
+После этого дочерние datasets (`storage`, `backup`, `Photos`, `Edu`, `docker`, `old-bu`, `TimeMachine/guest`, `iocage/*`) видны на `/RED_2TB/*` (все `ro` — readonly; для чтения rsync это ок). Проверка: `df -h /RED_2TB` покажет ~4.6T (НЕ 1.1T родительского), `ls /RED_2TB/storage` не пуст.
+> ⚠️ Монтирование — **только под root**. Под `truenas_admin` `zfs mount -a` даёт `Insufficient privileges`.
### 4. Выгрузка:
```bash
-rsync -av --ignore-existing --ignore-errors // <резервный-диск>/
+rsync -aHAX --info=progress2 /RED_2TB/ /mnt/IRONWOLF/ > /tmp/rsync2.log 2>&1 &
```
Лучше в tmux. Затем `zpool destroy` + пересоздать пул.
+**⚠️ ВАЖНО (2026-08-18): использовать именно `rsync`, НЕ `zfs send | zfs recv`.** `zfs send` на пуле с повреждённой space map падает (`signal received` / `Bad file descriptor` / `IOT (core dumped)`, exit 1) — ZFS не может прочитать данные на лету при stream-чтении. rsync переживает ошибки чтения отдельных файлов (exit 23) и копирует остальное.
+
+**⚠️ Почему первая попытка rsync дала только 355G (разгадано 2026-08-18):** дочерние datasets НЕ были смонтированы, поэтому `/RED_2TB/storage` и др. были пустыми mountpoint-дирами; rsync скопировал только ~355G из родительского dataset. **Сначала смонтировать datasets (`zfs mount -a`), потом rsync** — иначе скопируется лишь несколько сотен ГБ.
+
**Целевой диск для выгрузки (2026-08-17):** **IronWolf 12TB** подключён к материнке (`sdc`, by-id `WV700FQ5`, 10.9T, свободен/не в пуле) — создай на нём пул-приёмник и копируй туда. Места достаточно (10.9T > 4.6T данных).
**Что на RED_2TB спасать (структура datasets от 2026-08-17):**
@@ -70,6 +76,7 @@ rsync -av --ignore-existing --ignore-errors // <резервный-дис
- `zpool import -n RED_2TB` — нелегальный синтаксис без `-F` (`-n` только с `-F`).
- **НЕ повторять `import -F` настойчиво** — углубляет риск. Сначала readonly.
- `zpool import -f -F -X` НЕ работает для этой паники (TrueNAS forum).
+- **⚠️ ЛОВУШКА «zfs send -n» (dry-run) НЕ подтверждает права send:** под `truenas_admin` `zfs send -n` возвращал exit 0 без ошибок — обманчиво. Реальный `zfs send` даёт `permission denied`. Дочерние datasets доступны только root (ACL root-only). Не доверять dry-run для проверки прав send.
- Данные могут показать `Permission denied` если сами данные повреждены (в тяжёлых случаях — частичное восстановление).
- **Проверить RAM** — на non-ECC (P8H77-V) повреждение space map часто от плохой памяти. memtest.
- Буквы дисков shift-аются — сверять по by-id/модели, не по буквам.
From 1d621faa59bcf48395ccac4b95dac82e3dafe7b3 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Thu, 20 Aug 2026 10:58:17 +0600
Subject: [PATCH 10/81] [2026-08-20] eagle: family/how-to/red2tb-dataset-map.md
---
family/how-to/red2tb-dataset-map.md | 70 +++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 family/how-to/red2tb-dataset-map.md
diff --git a/family/how-to/red2tb-dataset-map.md b/family/how-to/red2tb-dataset-map.md
new file mode 100644
index 00000000..dcf5e871
--- /dev/null
+++ b/family/how-to/red2tb-dataset-map.md
@@ -0,0 +1,70 @@
+# RED_2TB — карта датасетов и назначений (для решения о пересоздании пула)
+
+> Собрано: 2026-08-20 (Ель — Кит). Источник: live-данные `zfs list` + `ls` с TrueNAS (readonly-импорт).
+> НАЗНАЧЕНИЕ: решить, воссоздавать ли карту датасетов как есть, или что-то поменять/добавить/убрать перед пересозданием пула RED_2TB.
+> Ничего не изменялось — только сбор информации.
+
+## Статус спасения на 2026-08-20
+
+- **DEST (IronWolf 12TB, /mnt/IRONWOLF)**: 4.56T из 10.9T занято — данные уже выгружены rsync.
+- **RED_2TB**: 4.61T занято, readonly-импорт. Все datasets видны и смонтированы.
+- Идёт **проверочный rsync** (`ir-chk` на ~1.8 млн файлов) — сверка, а не первая передача.
+- После проверки и сверки `du` — ПЕРЕСОЗДАНИЕ пула и возврат данных.
+
+## Общая сводка
+
+- Пул: RED_2TB, 5.44T (4.61T занято), компрессия lz4, рекордсайз 128K, acltype nfsv4, atime=on, exec=on.
+- Квот/резерваций НЕТ ни на одном датасете (все `none`).
+- Монтинг: датасеты в `/RED_2TB/*`, служебные `.system`/`ix-apps` в `legacy`/`/.ix-apps`.
+
+## ✅ Датасеты (воссоздавать как ZFS datasets)
+
+| Датасет | USED | REFER | Назначение | Примечание |
+|---------|------|-------|-----------|------------|
+| **storage** | 3.24T | 3.24T | Общее хранилище мультимедиа и файлов | Подкаталоги: Cartoons, Downloads, Edu, Movies, Music, ada3s1, art, books, cartoons-series, documentaries(-series), git, nas(пусто), obsidian, obsidian-syncthing, photo_dedup_test, radarr, seafile, series, shared, singularity, sonarr, work. `git/` = hermes-taiga.git, obsidian-vault.git |
+| **backup** | 287G | 287G | Резервные копии (личные доки, коды, VM, пароли, браузеры) | Много семейных документов (договоры, справки), Google Keep/Play, VPN, Virtual Machines, accessKeys.csv, lastpass_export.zip, коды восстановления. |
+| **TimeMachine/guest** | 277G | 267G | Бэкапы macOS (Time Machine) | Родитель TimeMachine 277G/96K; дочерний `guest` несёт данные. Много снимков `aapltm-*`. |
+| **Edu** | 214G | 214G | Учебные материалы | |
+| **Photos** | 146G | 146G | Фотографии | |
+| **old-bu** | 65.5G | 65.5G | Старый семейный архив фото/видео | Фото 2009–2010+, свадьбы, семейные события (папки вида `09-01-01 Новый 2009 год!`). |
+| **ix-apps** | 24.0G | - | Системный — TrueNAS Apps (docker 23.6G, truenas_catalog 385M) | Воссоздаётся самой TrueNAS, не вручную. |
+| **iocage** | 15.4G | 9.19M | Jail-ы TrueNAS | Jails: backuppc, emby, homeassistant, plex, transmission, worker + releases 11.2/12.2/12.3, images, download, templates. |
+| **openbsd** | 10.2G | 4.43M | ?? неясно — refer всего 4.43M при used 10.2G | Вероятно remnants после снимка/пробного пула. Решить: нужен ли вообще. |
+| **docker** | 2.98G | 2.98G | Живой docker-конфиг (текущие контейнеры) | |
+| **apps** | 192K | 96K | Точка для app-конфигов | Дочерний `apps/homeassistant-config` (96K). |
+| **.system/** | 568M | - | Служебное TrueNAS (configs, netdata, rrd, samba4, syslog...) | Воссоздаётся самой системой, не создавать руками. |
+
+## ⚠️ Данные ВНЕ датасетов — лежат прямо в корне `RED_2TB` (REFER 362G)
+
+Эти пути НЕ являются ZFS-datasets (их нет в `zfs list`) — при пересоздании пула их надо решать отдельно, иначе потеряются (или перепутаются с содержимым корневого датасета):
+
+| Путь | Содержимое | Важность |
+|------|-----------|----------|
+| `/RED_2TB/system/` | SSH-туннель: `tunnel.sh`, `tunnel_key` (приватный!), `tunnel_key.pub`, `99-tty-alias.rules`. Владелец root, режим 600/644. | ⚠️ **КРИТИЧНО** — рабочий туннель TrueNAS. root-only. |
+| `/RED_2TB/docker.bak/` | Бэкап конфигов docker: caddy, cups, filebrowser, ha, hermes, homeassistant, immich, inpx-web, inpxer, mbusd, modbus-bridge, mosquitto, nodered, portainer, python, rclone, ser2net | Архив/бэкап контейнеров. `hermes/` = бэкап Hermes-агента. |
+| `/RED_2TB/immich-photos-upload/` | Конфиг Immich: backups, encoded-video, library, profile, thumbs, upload | Рабочий Immich (он же в docker). |
+| Корневые файлы | `.DS_Store`, `._.DS_Store` (мусор mac), `dedupe_rm.sh`, `files.txt.xz` (содержимое old-bu/somo?) | dedupe_rm.sh — скрипт дедупликации, файловый список. |
+
+## 🤔 Метки для решения
+
+Вопросы, которые надо решить ПЕРЕД пересозданием:
+
+1. **openbsd (10.2G/4.43M)** — судя по refer почти пуст. Что это? Удалить из map?
+2. **Внутри storage/ есть `Edu`** — это ДУБЛЬ датасета `Edu`? Проверить: возможно медиа-папка Edu в storage vs отдельный датасет Edu для учебных.
+3. **`nas` внутри storage пуст**.
+4. **`TimeMachine`** (277G) — вернуть ли отдельным датасетом как было, с дочерним `guest`?
+5. **`docker` vs `docker.bak`** — оба сохранить? `docker` = живой, `docker.bak` = архив.
+6. **`immich-photos-upload`** — оставить в корне пула (вне датасета) или завести отдельный датасет?
+7. **Внедатасетные каталоги** (`system`, `docker.bak`, `immich-photos-upload`) — после пересоздания они окажутся в корневом dataset RED_2TB (REFER 362G). Решить, оставить их там или разнести по датасетам.
+
+## Ключевые решения по подходу к пересозданию (из truenas-sata-ports-and-zfs-pools.md)
+
+- In-place починки НЕТ (битая space map, паника на rw).
+- Рабочий путь: readonly-импорт → выгрузка rsync → `zpool destroy`+`create` → возврат данных rsync.
+- НЕ использовать `zfs send|recv` (падает на повреждённом пуле). Только rsync.
+- После пересоздания можно добавить WD2TB#2 зеркалом к WD2TB#1 (сейчас simplex, без защиты).
+- `zpool import`/`mount`/destroy/create — ТОЛЬКО под root (truenas_admin не root).
+
+## Ссылки
+
+- Связанные: [[truenas-sata-ports-and-zfs-pools]] (план спасения/пересоздания), [[truenas-access]] (общие данные доступа).
From 5c67fc2139b473e1559a2f27d4397853d80c623d Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Fri, 21 Aug 2026 12:07:04 +0600
Subject: [PATCH 11/81] [2026-08-21] eagle: family/how-to/red2tb-dataset-map.md
family/how-to/truenas-infrastructure.md
family/how-to/truenas-sata-ports-and-zfs-pools.md
---
family/how-to/red2tb-dataset-map.md | 14 ++--
family/how-to/truenas-infrastructure.md | 9 +--
.../truenas-sata-ports-and-zfs-pools.md | 72 ++++++++++++++++++-
3 files changed, 83 insertions(+), 12 deletions(-)
diff --git a/family/how-to/red2tb-dataset-map.md b/family/how-to/red2tb-dataset-map.md
index dcf5e871..8d597fcd 100644
--- a/family/how-to/red2tb-dataset-map.md
+++ b/family/how-to/red2tb-dataset-map.md
@@ -4,12 +4,13 @@
> НАЗНАЧЕНИЕ: решить, воссоздавать ли карту датасетов как есть, или что-то поменять/добавить/убрать перед пересозданием пула RED_2TB.
> Ничего не изменялось — только сбор информации.
-## Статус спасения на 2026-08-20
+## Статус спасения на 2026-08-21
-- **DEST (IronWolf 12TB, /mnt/IRONWOLF)**: 4.56T из 10.9T занято — данные уже выгружены rsync.
-- **RED_2TB**: 4.61T занято, readonly-импорт. Все datasets видны и смонтированы.
-- Идёт **проверочный rsync** (`ir-chk` на ~1.8 млн файлов) — сверка, а не первая передача.
-- После проверки и сверки `du` — ПЕРЕСОЗДАНИЕ пула и возврат данных.
+- **Выгрузка ЗАВЕРШЕНА УСПЕШНО** (подтверждено Alex). Данные RED_2TB (4.56T) на IronWolf — `DEST`, `/mnt/IRONWOLF`.
+- **RED_2TB пул — OFFLINE** в БД TrueNAS (id=1), НЕ импортирован. `zpool export` → `no such pool`.
+- Проверочный rsync прогон завершён (rsync не запущен).
+- **РЕШЕНИЕ:** пересоздаём пул с новой топологией — ВКЛЮЧАЕМ WD2TB#2 (sdf) зеркалом к WD2TB#1 (sdc). Итог: `mirror {sdc,sdf}` + `mirror {sde,sdd}`.
+- Следующий шаг: `zpool labelclear` по разделам `*2` + `zpool create` (подробно в [[truenas-sata-ports-and-zfs-pools]] → «Пересоздание пула 2026-08-21»).
## Общая сводка
@@ -62,8 +63,9 @@
- In-place починки НЕТ (битая space map, паника на rw).
- Рабочий путь: readonly-импорт → выгрузка rsync → `zpool destroy`+`create` → возврат данных rsync.
- НЕ использовать `zfs send|recv` (падает на повреждённом пуле). Только rsync.
-- После пересоздания можно добавить WD2TB#2 зеркалом к WD2TB#1 (сейчас simplex, без защиты).
+- **РЕШЕНО (2026-08-21):** включить WD2TB#2 (sdf) зеркалом к WD2TB#1 (sdc) при пересоздании. Топология `mirror {sdc,sdf}` + `mirror {sde,sdd}` — оба зеркалированы.
- `zpool import`/`mount`/destroy/create — ТОЛЬКО под root (truenas_admin не root).
+- **НИКОГДА не импортировать RED_2TB в rw** (kernel panic). Только `labelclear` + `create`.
## Ссылки
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 9d4b0eca..89188315 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -1,10 +1,11 @@
# TrueNAS — инфраструктура
-> Обновлено: 2026-08-18 (datasets смонтированы, выгрузка rsync идёт)
+> Обновлено: 2026-08-21 (данные спасены на IronWolf, пул RED_2TB OFFLINE, готовность к пересозданию)
-> ## ⚠️⚠️ АКТИВНО (2026-08-18): пул RED_2TB — kernel panic, datasets смонтированы, выгрузка данных rsync на IronWolf
-> **RED_2TB — это НЕ просто хранилище: на нём лежат конфиги docker (caddy, transmission, Home Assistant `/mnt/RED_2TB/docker/*`), данные Transmission/Arr, сам Obsidian vault и его git-репо (`/mnt/RED_2TB/storage/obsidian`, `/mnt/RED_2TB/storage/git/obsidian-vault.git`).** Пул в kernel panic при rw-импорте (повреждение space map, `adding existent segment to range tree`). **ПОКА НЕ ВОССТАНОВЛЕНО** — данные в процессе спасения.
-> **Статус на 2026-08-18:** пан-loop преодолён (загрузка с физически отключёнными дисками → readonly-импорт `zpool import -o readonly=on RED_2TB` успешен, паники нет). **ПРОГРЕСС: дочерние datasets смонтированы под root** (`zfs mount -a` после `mount -o remount,rw /`) — затем rsync на целевой пул **DEST** (IronWolf 12TB, `/mnt/IRONWOLF`, rw, 10.6T свободно). ⚠️ Первая попытка rsync дала только 355G из-за НЕ смонтированных дочерних datasets — после монтирования перезапущен. **`zfs send|recv` НЕ работает** на этом пуле (signal received / Bad file descriptor). Полный рецепт — в `family/how-to/truenas-sata-ports-and-zfs-pools.md` (раздел «Пулы», блок 2026-08-18) и `personal/tech/truenas-zfs-panic-recovery.md`. Инфраструктура на `/mnt/RED_2TB` пока не работает (только спасение данных).
+> ## ⚠️ СТАТУС на 2026-08-21: пул RED_2TB — данные СПАСЕНЫ, пул OFFLINE, пересоздаём
+> **Выгрузка успешно завершена** (подтверждено Alex): данные RED_2TB (4.56T) на IronWolf (пул `DEST`, `/mnt/IRONWOLF`). **Пул RED_2TB — статус OFFLINE** в БД TrueNAS, НЕ импортирован. Конфиги docker (caddy, transmission, HA, immich и т.д.), Obsidian vault и git-репо спасены на `/mnt/IRONWOLF` (`system/`, `docker.bak/`, `docker/`, `storage/obsidian`, `storage/git`).
+> **ПЛАН (следующий шаг):** пересоздать пул RED_2TB с новой топологией `mirror {sdc,sdf}` + `mirror {sde,sdd}` (включаем WD2TB#2 зеркалом к WD2TB#1). Команды `zpool labelclear` + `zpool create` — в `family/how-to/truenas-sata-ports-and-zfs-pools.md` (раздел «Пересоздание пула 2026-08-21»). Затем вернуть данные rsync с IronWolf и восстановить инфраструктуру.
+> **ПРОТОКОЛ ПОВРЕЖДЕНИЯ (история):** пул паниковал на rw-импорте (`adding existent segment to range tree`, повреждённая space map). Работал только `zpool import -o readonly=on`. In-place починки нет (mav/iXsystems) — только спасать данные и пересоздавать. Полный рецепт — в `family/how-to/truenas-sata-ports-and-zfs-pools.md`.
## Доступ
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index 1f33d908..10c0f12d 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -1,8 +1,28 @@
# TrueNAS — SATA порты и пулы
-> Обновлено: 2026-08-18 (datasets смонтированы под root, rsync перезапущен). СТАТУС: данные RED_2TB на этапе выгрузки на IronWolf — смонтированные дочерние datasets готовы к rsync. IronWolf 12TB = целевой пул `DEST` (rw, 10.6T свободно). HGST сломан (см. ниже).
+> Обновлено: 2026-08-21 (данные спасены, пул RED_2TB OFFLINE, готовность к пересозданию). СТАТУС: выгрузка данных на IronWolf **завершена успешно** (4.56T на `/mnt/IRONWOLF`, подтверждено Alex). Пул RED_2TB **OFFLINE** (не импортирован), диски физически на месте. Идёт подготовка к пересозданию пула с новой топологией (добавляем WD2TB#2 зеркалом к WD2TB#1). HGST сломан (см. ниже).
-## ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №1: пул RED_2TB импортирован READONLY, datasets смонтированы, данные выгружаются rsync
+## ✅ РЕШЕНО (2026-08-21): пул RED_2TB — данные спасены, пул OFFLINE, готовность к пересозданию
+
+**ВЫГРУЗКА ДАННЫХ ЗАВЕРШЕНА УСПЕШНО.** Alex подтвердил: «операция завершилась успешно, ничего не было перезаписано». Данные RED_2TB (4.56T) лежат на IronWolf 12TB — пул `DEST`, `/mnt/IRONWOLF`. Ключевые конфиги подтверждены на `/mnt/IRONWOLF`: `system/tunnel.sh`+`tunnel_key`+`.pub` (критичный SSH-туннель), `docker.bak/` (309M), `docker/` (1.1G), `immich-photos-upload`, `dedupe_rm.sh`, `files.txt.xz`. `storage/obsidian` и `storage/git` существуют (Permission denied для `truenas_admin` из-за NFSv4 ACL, transmission-owner — это нормально, НЕ потеря).
+
+**Текущее состояние (2026-08-21, live-проверка):**
+- **RED_2TB пул — статус OFFLINE** в БД TrueNAS (`midclt call pool.query` → `id=1, guid 3817880812699166755`), пул **НЕ импортирован**. `zpool export RED_2TB` → `no such pool` (уже выгружен/не в активном контексте).
+- `zpool import -d /dev` всё ещё **видит** RED_2TB как **ONLINE** импортируемый, члены: `sdc2` (simplex WD2TB#1) + `mirror-1 {sde2 WD4TB, sdd2 Seagate4TB}`.
+- rsync не запущен — проверочный прогон завершён.
+- Диски физически на месте (by-id, см. таблицу портов ниже).
+
+**Актуальная раскладка дисков (2026-08-21, по `/dev/disk/by-id`, стабильные имена):**
+| Диск | by-id/model | Роль |
+|------|------------|------|
+| sda | `ata-KINGSTON_SA400S37120G_50026B77844E881C` | Boot SSD (не трогать) |
+| sdb | `ata-ST12000NT001-3LX101_WV700FQ5` | ⭐ IronWolf 12TB = DEST, `/mnt/IRONWOLF` (целевой, данные выгружены) |
+| sdc | `ata-WDC_WD20EFAX-68B2RN1_WD-WXH2A31D5HDJ` | **WD2TB#1** (исходный simplex-член RED_2TB) |
+| sdd | `ata-ST4000DM004-2CV104_WFN66CM2` | **Seagate4TB** (mirror-1 второй член) |
+| sde | `ata-WDC_WD40EZAZ-00SF3B0_WD-WX22D51JJFZ7` | **WD4TB** (mirror-1 первый член) |
+| sdf | `ata-WDC_WD20EFAX-68B2RN1_WD-WXJ2A31CUNNT` | **WD2TB#2** (доп., НЕ в исходном пуле) |
+
+## ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №1 (ЗАКРЫТО, история): пул RED_2TB импортирован READONLY, данные выгружались rsync
Пан-loop преодолён (рецепт ниже). RED_2TB импортирован readonly удачно — **паники больше нет**. **ПРОГРЕСС 2026-08-18:** дочерние datasets (`storage`, `backup`, `Photos`, `Edu`, `docker`, `old-bu`, `TimeMachine/guest`, `iocage/*`) **успешно смонтированы под root** (`zfs mount -a` + при необходимости `mount -o remount,rw /`). Данные видны. Выгрузка на IronWolf идёт **rsync** (см. блок ниже).
@@ -221,6 +241,54 @@ while true; do clear; date +%H:%M:%S; du -sh /mnt/IRONWOLF/; tail -3 /tmp/rsync2
**Опция добавить WD2TB#2 как зеркало к WD2TB#1** (`zpool attach RED_2TB `): валидна, но ТОЛЬКО после успешного импорта пула и его стабилизации. Не пытаться до восстановления.
+### 🆕 ПЕРЕСОЗДАНИЕ ПУЛА (2026-08-21) — план и команды
+
+**Решение (подтверждено Alex):** пересоздаём RED_2TB с **новой топологией — ВКЛЮЧАЕМ WD2TB#2 (sdf) зеркалом к WD2TB#1 (sdc)**. В итоге: `mirror {sdc, sdf}` + `mirror {sde, sdd}` — оба исходных simplex-члена получают зеркала (ранее WD2TB#1 был simplex без защиты).
+
+**Записи TrueNAS, которые важно знать (2026-08-21):**
+- `midclt call pool.query` → `id=1, name=RED_2TB, status=OFFLINE`, `topology:null`. Устаревшая запись в БД.
+- **`midclt call pool.delete 1` НЕ существует** в этом билде (`'PoolService' object has no attribute 'do_delete'`) — запись через API не удалить. Пользоваться командной строкой `zpool` (root) или UI.
+- `zpool labelclear -f` **принимает только ОДИН vdev за раз** — «too many arguments» при передаче нескольких дисков.
+
+**Команды пересоздания (под root, на консоли TrueNAS):**
+```bash
+# 1. Стереть старые метки ZFS с ПАРТИЦИЙ (пул был построен на разделах *2, НЕ на целых дисках!)
+zpool labelclear -f /dev/sdc2
+zpool labelclear -f /dev/sdf2
+zpool labelclear -f /dev/sde2
+zpool labelclear -f /dev/sdd2
+
+# 2. Проверить, что RED_2TB исчез из импортируемых
+zpool import -d /dev
+
+# 3. Создать пул заново на ЦЕЛЫХ дисках (TrueNAS создаст свои разделы *2)
+zpool create RED_2TB mirror /dev/sdc /dev/sdf mirror /dev/sde /dev/sdd
+
+# 4. Проверить
+zpool status RED_2TB
+zpool list RED_2TB
+```
+
+**⚠️ КРИТИЧЕСКИЕ ЛОВУШКИ labelclear (из openzfs issues #18027, #3156, #14869):**
+- **Целиться надо в ПАРТИЦИИ (`*2`), а не в целый диск.** TrueNAS строит пулы на разделах `...2`. `zpool labelclear -f /dev/sdc` (целый диск) даёт `failed to clear label for /dev/sdc` — метки лежат внутри раздела `sdc2`, на целом диске их «не видит» целиком.
+- **`failed to clear label` также выводится, когда метки УЖЕ стёрты** (issue #18027) — не обязательно ошибка. Если после `labelclear` `zpool import -d /dev` не показывает RED_2TB — значит метки стёрты, всё ок.
+- `labelclear` сам по себе может не стереть оба набора меток (`#14869`), если чистить не ту цель — поэтому чистить по разделам, которые реально добавлялись.
+
+**🧭 НОВАЯ ТОПОЛОГИЯ ПОСЛЕ ПЕРЕСОЗДАНИЯ:**
+| vdev | Члены | Назначение |
+|------|-------|-----------|
+| mirror-0 | sdc (WD2TB#1) + sdf (WD2TB#2) | Пара WD 2TB (зеркалирование) |
+| mirror-1 | sde (WD4TB) + sdd (Seagate4TB) | Пара 4TB (зеркалирование) |
+
+**После создания пула — НЕ забыть:**
+- пересоздать datasets по [[red2tb-dataset-map]] (storage, backup, Photos, Edu, TimeMachine/guest, docker, old-bu, iocage; `.system`/`ix-apps` TrueNAS создаст сама);
+- решить судьбу данных ВНЕ датасетов (`system/`, `docker.bak/`, `immich-photos-upload/`, корневые файлы) — после пересоздания окажутся в корневом dataset;
+- вернуть данные: `rsync -aHAX /mnt/IRONWOLF/ /RED_2TB/`;
+- восстановить инфраструктуру (docker: caddy, transmission, HA, immich...), доступы, cron-бэкапы, Obsidian sync.
+
+## ⚠️ НЕ ВХОДИТЬ В RW-ИМПОРТ ПОВРЕЖДЁННоГО ПУЛА
+**Импорт RED_2TB в не-readonly режиме (`zpool import` без `-o readonly=on`) — ВСЕГДА даёт kernel panic** (`zfs: adding existent segment to range tree`). Это правило подтверждено много раз. При пересоздании пул НЕ импортировать — только стереть метки (`labelclear`) и создать заново. `zpool import -d /dev` (dry-list) безопасен, но не запускает пул.
+
- Идёт регулярный scrub (запускается ночью воскресенья).
From b765e7ecb66750b20728dd915fbae8c56a79f6e6 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Fri, 21 Aug 2026 12:17:08 +0600
Subject: [PATCH 12/81] [2026-08-21] eagle:
family/how-to/truenas-sata-ports-and-zfs-pools.md
---
family/how-to/truenas-sata-ports-and-zfs-pools.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index 10c0f12d..7e478f23 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -241,10 +241,12 @@ while true; do clear; date +%H:%M:%S; du -sh /mnt/IRONWOLF/; tail -3 /tmp/rsync2
**Опция добавить WD2TB#2 как зеркало к WD2TB#1** (`zpool attach RED_2TB `): валидна, но ТОЛЬКО после успешного импорта пула и его стабилизации. Не пытаться до восстановления.
-### 🆕 ПЕРЕСОЗДАНИЕ ПУЛА (2026-08-21) — план и команды
+### ✅ ПЕРЕСОЗДАНИЕ ПУЛА (2026-08-21) — ВЫПОЛНЕНО
**Решение (подтверждено Alex):** пересоздаём RED_2TB с **новой топологией — ВКЛЮЧАЕМ WD2TB#2 (sdf) зеркалом к WD2TB#1 (sdc)**. В итоге: `mirror {sdc, sdf}` + `mirror {sde, sdd}` — оба исходных simplex-члена получают зеркала (ранее WD2TB#1 был simplex без защиты).
+**✅ СТАТУС (2026-08-21, live): пул СОЗДАН успешно.** `zpool create RED_2TB mirror /dev/sdc /dev/sdf mirror /dev/sde /dev/sdd` → `state: ONLINE, 0 0 0 ошибок, No known data errors`, `SIZE 5.44T, ALLOC 384K, HEALTH ONLINE`. Пул пустой (данные ещё возвращаются).
+
**Записи TrueNAS, которые важно знать (2026-08-21):**
- `midclt call pool.query` → `id=1, name=RED_2TB, status=OFFLINE`, `topology:null`. Устаревшая запись в БД.
- **`midclt call pool.delete 1` НЕ существует** в этом билде (`'PoolService' object has no attribute 'do_delete'`) — запись через API не удалить. Пользоваться командной строкой `zpool` (root) или UI.
From d3dadb59259766b1de12319ff5079aaf239addfa Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 10:55:25 +0600
Subject: [PATCH 13/81] [2026-08-24] eagle:
personal/business/rf-tax-residency.md
---
personal/business/rf-tax-residency.md | 77 +++++++++++++++++++++++++++
1 file changed, 77 insertions(+)
create mode 100644 personal/business/rf-tax-residency.md
diff --git a/personal/business/rf-tax-residency.md b/personal/business/rf-tax-residency.md
new file mode 100644
index 00000000..983fc853
--- /dev/null
+++ b/personal/business/rf-tax-residency.md
@@ -0,0 +1,77 @@
+---
+tags:
+ - tax
+ - russia
+ - residency
+created: '2026-08-24'
+---
+# Налоговая резиденция РФ — статус и план
+
+## Контекст
+
+- ИП в Кыргызстане, доход от иностранных IT-клиентов
+- Живу в Бишкеке, гражданин РФ
+- Цель: **не становиться налоговым резидентом РФ** (порог — 183 дня в любые 12 последовательных месяцев)
+- Пока нерезидент — доход от КР-ИП РФ не касается
+
+---
+
+## Въезды/выезды в РФ
+
+| Период | Дней |
+| ----------------------- | ------- |
+| 29.11.2025 → 14.03.2026 | 106 |
+| 04.07.2026 → 15.08.2026 | 43 |
+| 29.11.2026 → ? | считаем |
+
+---
+
+## Подсчёт скользящего окна
+
+### На дату приезда 29.11.2026
+
+Окно 29.11.2025 → 28.11.2026:
+- 29.11.25 → 14.03.26 = **106 дней**
+- 04.07.26 → 15.08.26 = **43 дня**
+- **Итого: 149 дней** (запас: 34 дня до 183)
+
+### Динамика окна при пребывании с 29.11.2026
+
+- **Декабрь 2026:** каждый день +1 новый, −1 декабрь 2025 (был в РФ) → **счётчик не растёт**, стоит на 149
+- **Январь 2027:** каждый день +1 новый, −1 январь 2026 (был в РФ) → **счётчик не растёт**
+- **Февраль 2027:** аналогично февраль 2026 был в РФ → **счётчик не растёт**
+- **С 15.03.2027:** из окна начинают выпадать дни после 14.03.2026 (КР, не РФ) → каждый день в РФ даёт **чистый +1**
+
+### Итог по поездке с 29.11.2026
+
+| Период в РФ | Счётчик |
+|---|---|
+| 29.11.26 → 14.03.27 | остаётся ~149 |
+| С 15.03.27 | растёт: +1/день |
+| Лимит исчерпан (~17.04.27) | 183 — нужно выехать |
+
+---
+
+## Риски и что отслеживать
+
+- ФНС планирует автоматическое определение резидентства по загранпаспорту (данные о въездах/выездах) — статус инициативы неясен
+- Для стран ЕАЭС (КР, Армения, Казахстан) граница по внутреннему паспорту не фиксируется автоматически — но это может измениться
+- При смешанном резидентстве (РФ + КР одновременно по 183+ дней) применяется СИДН РФ–КР (соглашение от 13.01.1999)
+
+---
+
+## СИДН РФ–КР (на случай если всё же стану резидентом РФ)
+
+1. Получить **сертификат налогового резидентства КР** (ГНС Кыргызстана)
+2. Подать **3-НДФЛ** в РФ, указать доход от КР-ИП, сослаться на СИДН (ст. 7 — предпринимательская деятельность)
+3. Приложить: сертификат резидентства КР, квитанции об уплате налога в КР, контракты
+4. Налог уплаченный в КР засчитывается — доплачивается только разница (КР ~4–6%, РФ 13%)
+
+---
+
+## Вывод / план
+
+- **Сейчас:** нерезидент РФ, всё чисто
+- **Поездка 29.11.2026:** можно сидеть до ~середины марта 2027 без роста счётчика
+- **Март 2027:** выехать до ~17.04.2027 чтобы не пробить 183
+- **Принцип:** ноябрь–февраль в РФ безопасны пока предыдущий год был аналогичным; март — точка слежения
From 117dae471cf3d44c8ceaea0e08e534c9a187455b Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 11:00:27 +0600
Subject: [PATCH 14/81] [2026-08-24] eagle:
personal/business/rf-tax-residency.md
---
personal/business/rf-tax-residency.md | 30 ++++++++++++---------------
1 file changed, 13 insertions(+), 17 deletions(-)
diff --git a/personal/business/rf-tax-residency.md b/personal/business/rf-tax-residency.md
index 983fc853..7c349eec 100644
--- a/personal/business/rf-tax-residency.md
+++ b/personal/business/rf-tax-residency.md
@@ -1,9 +1,10 @@
---
+created: '2026-08-24'
+updated: '2026-08-24'
tags:
- tax
- russia
- residency
-created: '2026-08-24'
---
# Налоговая резиденция РФ — статус и план
@@ -21,7 +22,7 @@ created: '2026-08-24'
| Период | Дней |
| ----------------------- | ------- |
| 29.11.2025 → 14.03.2026 | 106 |
-| 04.07.2026 → 15.08.2026 | 43 |
+| 04.07.2026 → 12.09.2026 | 71 |
| 29.11.2026 → ? | считаем |
---
@@ -32,23 +33,19 @@ created: '2026-08-24'
Окно 29.11.2025 → 28.11.2026:
- 29.11.25 → 14.03.26 = **106 дней**
-- 04.07.26 → 15.08.26 = **43 дня**
-- **Итого: 149 дней** (запас: 34 дня до 183)
+- 04.07.26 → 12.09.26 = **71 день**
+- **Итого: 177 дней** (запас: всего **6 дней** до 183)
### Динамика окна при пребывании с 29.11.2026
-- **Декабрь 2026:** каждый день +1 новый, −1 декабрь 2025 (был в РФ) → **счётчик не растёт**, стоит на 149
-- **Январь 2027:** каждый день +1 новый, −1 январь 2026 (был в РФ) → **счётчик не растёт**
-- **Февраль 2027:** аналогично февраль 2026 был в РФ → **счётчик не растёт**
-- **С 15.03.2027:** из окна начинают выпадать дни после 14.03.2026 (КР, не РФ) → каждый день в РФ даёт **чистый +1**
+С 29.11.26 счётчик стоит на 177. Ноябрь и декабрь 2025 были в РФ, поэтому дни конца ноября и декабря 2026 нейтральны (+1 новый −1 выпавший). Но запас 6 дней исчерпывается **раньше**, чем декабрьское выпадание успевает помочь.
-### Итог по поездке с 29.11.2026
+- **29.11.26 → 04.12.26:** счётчик 177→182 (5 дней пребывания, нейтральное выпадание ноября 2025)
+- **05.12.26:** счётчик достигает 183 → **резидент**
-| Период в РФ | Счётчик |
-|---|---|
-| 29.11.26 → 14.03.27 | остаётся ~149 |
-| С 15.03.27 | растёт: +1/день |
-| Лимит исчерпан (~17.04.27) | 183 — нужно выехать |
+### ⚠️ Безопасный выезд при поездке 29.11.2026
+
+**Выехать не позднее 04.12.2026** (5 дней пребывания максимум, включая день приезда).
---
@@ -72,6 +69,5 @@ created: '2026-08-24'
## Вывод / план
- **Сейчас:** нерезидент РФ, всё чисто
-- **Поездка 29.11.2026:** можно сидеть до ~середины марта 2027 без роста счётчика
-- **Март 2027:** выехать до ~17.04.2027 чтобы не пробить 183
-- **Принцип:** ноябрь–февраль в РФ безопасны пока предыдущий год был аналогичным; март — точка слежения
+- **Поездка 29.11.2026:** запас всего 6 дней — выехать **до 05.12.2026**
+- **Принцип:** следить за датами, особенно после длинного лета в РФ — запас резко сокращается
From ae0ea7ffebc646bb8ca0f8b666b449d862b55e1c Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 11:05:29 +0600
Subject: [PATCH 15/81] [2026-08-24] eagle:
personal/business/rf-tax-residency.md
---
personal/business/rf-tax-residency.md | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/personal/business/rf-tax-residency.md b/personal/business/rf-tax-residency.md
index 7c349eec..72cd2192 100644
--- a/personal/business/rf-tax-residency.md
+++ b/personal/business/rf-tax-residency.md
@@ -34,18 +34,17 @@ tags:
Окно 29.11.2025 → 28.11.2026:
- 29.11.25 → 14.03.26 = **106 дней**
- 04.07.26 → 12.09.26 = **71 день**
-- **Итого: 177 дней** (запас: всего **6 дней** до 183)
+- **Итого: 177 дней** (запас: **6 дней** до 183)
### Динамика окна при пребывании с 29.11.2026
-С 29.11.26 счётчик стоит на 177. Ноябрь и декабрь 2025 были в РФ, поэтому дни конца ноября и декабря 2026 нейтральны (+1 новый −1 выпавший). Но запас 6 дней исчерпывается **раньше**, чем декабрьское выпадание успевает помочь.
-
-- **29.11.26 → 04.12.26:** счётчик 177→182 (5 дней пребывания, нейтральное выпадание ноября 2025)
-- **05.12.26:** счётчик достигает 183 → **резидент**
+- **29.11.26 → 14.03.27:** из окна выпадают дни 29.11.25→14.03.26 — все в РФ → +1 −1 = **счётчик стоит на 177**
+- **15.03.27:** из окна начинают выпадать дни с 15.03.26 (КР) → каждый день в РФ даёт **чистый +1**
+- Запас 6 дней → нужно выехать до **~21.03.2027**
### ⚠️ Безопасный выезд при поездке 29.11.2026
-**Выехать не позднее 04.12.2026** (5 дней пребывания максимум, включая день приезда).
+**Выехать не позднее 20.03.2027**
---
@@ -69,5 +68,5 @@ tags:
## Вывод / план
- **Сейчас:** нерезидент РФ, всё чисто
-- **Поездка 29.11.2026:** запас всего 6 дней — выехать **до 05.12.2026**
+- **Поездка 29.11.2026:** можно сидеть до **20.03.2027** — счётчик нейтрален пока выпадают дни зимы 2025/26 (были в РФ), с 15.03.27 начинает расти
- **Принцип:** следить за датами, особенно после длинного лета в РФ — запас резко сокращается
From 822e072418e725fd46a6bf87f9d2cff95f0460ce Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 14:32:09 +0600
Subject: [PATCH 16/81] [2026-08-24] eagle:
personal/plans/ohotnichy-bilety-interaktiv.md
personal/projects/personal-os/eagle-dashboard.md
---
personal/plans/ohotnichy-bilety-interaktiv.md | 80 +++++++++++++++++++
.../projects/personal-os/eagle-dashboard.md | 15 ++++
2 files changed, 95 insertions(+)
create mode 100644 personal/plans/ohotnichy-bilety-interaktiv.md
diff --git a/personal/plans/ohotnichy-bilety-interaktiv.md b/personal/plans/ohotnichy-bilety-interaktiv.md
new file mode 100644
index 00000000..953df556
--- /dev/null
+++ b/personal/plans/ohotnichy-bilety-interaktiv.md
@@ -0,0 +1,80 @@
+# Интерактивные билеты охотника (a1-tir.ru/bilety)
+
+Проект: скачать страницу билетов для экзамена на гражданское оружие и превратить в интерактивный тренажёр.
+
+**Возможности:** номер правильного ответа скрыт · ответы кликабельны · выбранный неправильный → красный, правильный → зелёный.
+
+**Файлы (исходники в `~/Downloads/`):**
+- `bilety.html` — скачанная + расширенная страница (инъекция CSS/JS + метаданные)
+- `bilety.js` — enhancer: парсер вопросов + click-логика
+- `bilety.css` — стили подсветки (зелёный/красный)
+- `a1-tir_bilety_raw.html` — оригинальная скачанная копия (667 КБ)
+
+---
+
+## Статус
+- ✅ Страница скачана и переработана в интерактив.
+- ⏸️ **Доставка в Eagle Dashboard Pages не завершена** — упирается в права: `/Library/WebServer/Documents/` root-owned, `sudo cp` требует пароль из неинтерактивного шелла. Нужно либо получить sudo-пароль от Alex, либо он запустит вручную:
+ ```bash
+ sudo cp ~/Downloads/bilety.html ~/Downloads/bilety.js ~/Downloads/bilety.css /Library/WebServer/Documents/
+ ```
+ После копирования страница появится в `http://localhost:8880/?tab=pages` под именем `bilety` (title/purpose/contains уже заполнены). Рестарт дашборда не нужен.
+
+---
+
+## Источник и структура (Tilda)
+
+Страница — Tilda-лендинг, весь исходный HTML на одной строке (`a1-tir_bilety_raw.html`, строка 228 содержит контент).
+
+Экзамен разложен по **4 блокам** `data-record-type="106"` (Tilda text-блок `div.field-text.t-text`):
+- `rec465410787` — «Правовая подготовка», вопросы 1.1–1.49
+- `rec1120437831` — без заголовка (продолжение правовой), 1.50–1.93
+- `rec1120438216` — «Огневая подготовка», 2.1–2.41
+- `rec465410788` — без заголовка (правила охоты), 58–103 · **вопросы из нескольких ``**
+- Итого ~180 вопросов.
+
+### Структура одного вопроса в DOM (проверено по сырым данным)
+Каждый блок — один большой div; внутри `childNodes`:
+- ` ` — разделители (шум)
+- `Вопрос ` — текст вопроса; может занимать **несколько подряд** `` (напр. вопрос 58); внутри возможен вложенный `` (consultantplus)
+- шапка раздела — `Заголовок
` (strong вложен в p → не ловится как вопрос)
+- текстовые узлы ответов: `"1. Текст"`, `"2. ..."`, `"3. ..."` (бывают ведущие пробелы)
+- `N ` — **номер правильного ответа** (чистое число)
+- примечание — `Примечание : … ` (НЕ номер, сохранить)
+- шум: ` `, ` `, пустой `` с ``
+
+Контентных ` `/`` в блоках вопросов нет — только текст.
+
+---
+
+## Решение — чисто клиентский JS (без библиотек/backend)
+
+`bilety.js` на `DOMContentLoaded` обрабатывает каждый `[data-record-type="106"] .t-text`:
+
+**Парсер `parseQuiz` (конечный автомат по childNodes):**
+- `` с непустым текстом, не ` `: если у текущего вопроса ещё нет ответов и фаза `question` → **мержим** текст (многострочный вопрос); иначе → **новый вопрос**.
+- текстовый узел, матчащий `^\d+[\.\s:]` и при отсутствии заданного `correct` → ответ `{n, text}` (фаза → `answers`); иначе — продолжение последнего ответа.
+- `` с чистым числом → `question.correct = N` (только в фазе `answers`, чтобы не спутать с «Примечание»); с текстом → `question.note = innerHTML` (сохранить); пустой/` ` → шум.
+- `` при `cur === null` и нет вопросов → `headerHTML` (заголовок раздела сохраняем).
+- фильтр: оставляем только вопросы с `correct !== null`.
+
+**Рендер `renderQuestion`:** карточка `.whale-q` → `.whale-q-text` (вопрос, `textContent`, жирный) + `.whale-answers` с кнопками `.whale-a` (`data-n` = номер ответа) + опционально `.whale-note`.
+
+**Click-логика:** первый клик блокируется (`data-locked`). Если `n === correct` → класс `correct` (зелёный); иначе класс `wrong` (красный) **и** правильный ответ получает `correct` (чтобы показать верный). Управляется `data-correct` на карточке.
+
+**Инъекция в страницу:**
+- ` ` перед ``
+- `` перед `
`
+- мета для дашборда в `
`: `eagle-test-purpose`, `eagle-test-contains`.
+
+**Дизайн .whale-a:** карточки-кнопки (flex column), `#f5f5f5`, hover `#ececec`; `.correct`=`#d4edda`/`#28a745`, `.wrong`=`#f8d7da`/`#dc3545` (с `!important`).
+
+---
+
+## Pitfall: локальное открытие `file://`
+Страница Tilda использует `sessionStorage` для анимации появления `.t-records` — при открытии локально это работает. Просто открыть `bilety.html` двойным кликом достаточно для проверки функционала.
+
+---
+
+## Связанное
+- Доставка в дашборд — механика Pages: `personal/projects/personal-os/eagle-dashboard.md`.
diff --git a/personal/projects/personal-os/eagle-dashboard.md b/personal/projects/personal-os/eagle-dashboard.md
index 2102dece..8e782794 100644
--- a/personal/projects/personal-os/eagle-dashboard.md
+++ b/personal/projects/personal-os/eagle-dashboard.md
@@ -149,6 +149,21 @@ PID-file-based — survives Dashboard restarts without crashing.
**Pages** — URL input + metadata table for `/Library/WebServer/Documents/*.html` test pages. Each row shows filename, title, purpose, contents, modified date, and opens the page in a new browser tab.
+### Как добавить страницу в Pages (2026-08-24, проверено)
+
+Механика — просто копия `*.html` файла в webroot `/Library/WebServer/Documents/`. Дашборд читает каталог **живьём** через `GET /api/pages` → `_read_page_metadata()` — рестарт дашборда и `launchctl kickstart` **не нужны**. Страница отдаётся по `/local-pages/` (mount `app.mount("/local-pages", StaticFiles(...))`).
+
+Метаданные парсятся из HTML:
+| Поле дашборда | Источник |
+|---|---|
+| `title` | `... ` |
+| `purpose` | ` `, fallback — ` ` |
+| `contains` | ` ` |
+
+**Pitfall — относительные `href`/`src`:** если страница ссылается на свои `.js`/`.css` относительными путями (как `bilety.html` → `bilety.js`, `bilety.css`), эти файлы тоже обязаны лежать рядом в `/Library/WebServer/Documents/`, иначе запрос уйдёт на `/local-pages/bilety.js` и не найдётся.
+
+**Pitfall — права (блокер):** `/Library/WebServer/Documents/` принадлежит `root:wheel`, права `drwxr-xr-x`. `admin` писать туда **не может** без sudo. NOPASSWD-правил для этого пути в sudoers **нет** (`admin` имеет только `(ALL) ALL` с паролем + спец. pmset/launchctl NOPASSWD). Из неинтерактивного шелла Hermes `sudo cp` встаёт на запрос пароля → **требуется пароль пользователя или ручная команда**: `sudo cp ~/Downloads/bilety.* /Library/WebServer/Documents/`.
+
**Files** — FileBrowser iframe at `http://localhost:8181`.
**Crons** (2-я вкладка) — управление cron jobs из 3 источников:
From 04bafb46100594d0c086ff0a92ea0ad1ccec48df Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 14:47:16 +0600
Subject: [PATCH 17/81] [2026-08-24] eagle:
family/how-to/truenas-sata-ports-and-zfs-pools.md
---
family/how-to/truenas-sata-ports-and-zfs-pools.md | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index 7e478f23..bfcd6679 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -288,6 +288,16 @@ zpool list RED_2TB
- вернуть данные: `rsync -aHAX /mnt/IRONWOLF/ /RED_2TB/`;
- восстановить инфраструктуру (docker: caddy, transmission, HA, immich...), доступы, cron-бэкапы, Obsidian sync.
+### ✅ LIVE-СТАТУС ПОСЛЕ ПЕРЕСОЗДАНИЯ (2026-08-24, проверка Китом)
+
+**Пул УЖЕ зарегистрирован в TrueNAS и работает — перезагрузка/импорт НЕ нужны:**
+- `zpool status RED_2TB` → **ONLINE** (mirror-0 {sdc,sdf} + mirror-1 {sde,sdd}), ошибки `0 0 0`.
+- `midclt call pool.query` → `id=1, name=RED_2TB, status=ONLINE`, topology заполнена новой (оба mirror, GUIDs). БД TrueNAS привязала пул по GUID, запись обновлена под новую топологию.
+- Пул смонтирован (`/mnt/RED_2TB/`, `mounted=yes`), CAP 79% (5.44T, ALLOC 4.35T, FREE 1.09T).
+- **rsync возврата данных ЗАВЕРШЁН.** Вернулись все datasets: storage 3.29T, immich-photos-upload 353G, backup 287G, Edu 214G, Photos 146G, old-bu 65.2G, docker 2.92G, docker.bak 789M, system 112K. Корни IronWolf↔RED_2TB идентичны по ls.
+
+**⚠️ НЕ ЗАКРЫТО: TimeMachine (~277G) НЕ вернулся.** Причина: на IronWolf `/mnt/IRONWOLF/TimeMachine/` существует (277G), но root-only NFSv4 ACL → `truenas_admin` НЕ может прочитать (`Permission denied`) → rsync его не докопировал. На RED_2TB датасет `TimeMachine` НЕ воссоздан (нет в `zfs list`, каталога нет). **TODO под root:** создать dataset `RED_2TB/TimeMachine` (+ дочерний `guest` как было) и `rsync -aHAX /mnt/IRONWOLF/TimeMachine/ /mnt/RED_2TB/TimeMachine/`. iocage-каталог восстановлен, но старые jails не нужны (всё в docker) — решено ранее.
+
## ⚠️ НЕ ВХОДИТЬ В RW-ИМПОРТ ПОВРЕЖДЁННоГО ПУЛА
**Импорт RED_2TB в не-readonly режиме (`zpool import` без `-o readonly=on`) — ВСЕГДА даёт kernel panic** (`zfs: adding existent segment to range tree`). Это правило подтверждено много раз. При пересоздании пул НЕ импортировать — только стереть метки (`labelclear`) и создать заново. `zpool import -d /dev` (dry-list) безопасен, но не запускает пул.
From 9e7b6a46fe45f230e727786ef6a0b8c1676380d6 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 14:52:18 +0600
Subject: [PATCH 18/81] [2026-08-24] eagle:
family/how-to/truenas-sata-ports-and-zfs-pools.md
---
.../truenas-sata-ports-and-zfs-pools.md | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md
index bfcd6679..3f3cf292 100644
--- a/family/how-to/truenas-sata-ports-and-zfs-pools.md
+++ b/family/how-to/truenas-sata-ports-and-zfs-pools.md
@@ -298,6 +298,26 @@ zpool list RED_2TB
**⚠️ НЕ ЗАКРЫТО: TimeMachine (~277G) НЕ вернулся.** Причина: на IronWolf `/mnt/IRONWOLF/TimeMachine/` существует (277G), но root-only NFSv4 ACL → `truenas_admin` НЕ может прочитать (`Permission denied`) → rsync его не докопировал. На RED_2TB датасет `TimeMachine` НЕ воссоздан (нет в `zfs list`, каталога нет). **TODO под root:** создать dataset `RED_2TB/TimeMachine` (+ дочерний `guest` как было) и `rsync -aHAX /mnt/IRONWOLF/TimeMachine/ /mnt/RED_2TB/TimeMachine/`. iocage-каталог восстановлен, но старые jails не нужны (всё в docker) — решено ранее.
+### ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №3 (2026-08-24): Docker/слой приложений НЕ поднят — контейнеры лежат
+
+Пул с файлами работает, но **слой приложений (docker/TrueNAS Apps) НЕ восстановлен**. Проверка live (Кит):
+- `docker ps` → `Cannot connect to the Docker daemon`. `systemctl status docker` → `inactive (dead)`, **disabled**.
+- **`docker daemon.json` (`/etc/docker/daemon.json`):** `{"data-root": "/mnt/.ix-apps/docker", "exec-opts": ["native.cgroupdriver=cgroupfs"], "iptables": true, "storage-driver": "overlay2", "default-address-pools": [{"base": "172.17.0.0/12", "size": 24}]}` → это классическая схема TrueNAS Apps.
+- **Датасета `.ix-apps`/`ix-applications` НЕТ** в `zfs list` на пересозданном пуле → docker storage/образы/volumes (в старом пуле это был датасет `ix-apps`, docker 23.6G) **НЕ мигрировали** / потеряны.
+- **Конфиги ВСЕХ приложений на месте** в `/mnt/RED_2TB/docker/` (29 каталогов): arr, backups, caddy, cups, filebrowser, gitea, ha, hermes, homeassistant, immich, inpx-web, inpxer, library, mbusd, modbus-bridge, mosquitto, nodered, portainer, portainer-mcp, python, rclone, ser2net, syncthing, transmission, vless-proxy, watchtower, webdav, xray-admin, zigbee2mqtt.
+- `override.conf` (`/etc/systemd/system/docker.service.d/override.conf`): только `ExecStartPost=iptables -P FORWARD ACCEPT && ip6tables -P FORWARD ACCEPT` — применяется при старте docker.
+
+**Вывод:** `/mnt/RED_2TB/docker/` — это конфиги; docker-образы (layers) не сохранились → контейнеры надо пересоздавать с перекачкой образов.
+
+**ПЛАН ВОЗВРАТА КОНФИГА В СТРОЙ (4 шага, пока НЕ выполнен):**
+1. **WebUI TrueNAS → Apps → Settings → Choose Pool → RED_2TB.** TrueNAS сама создаст датасет приложений (ним `ix-applications`), примонтирует docker storage в `/mnt/ix-applications` (data-root `.ix-apps/docker`), поднимет `docker.service`. *(`docker` сейчас disabled — старт произойдёт именно через настройку Apps, не вручную.)*
+2. Конфиги приложений уже лежат в `/mnt/RED_2TB/docker/` — трогать не надо.
+3. **Пересоздать каждый контейнер** (WebUI Apps нативный/Custom App) с **bind-mount** `/mnt/RED_2TB/docker/` → внутрь контейнера. Образы перекачаются при старте, контейнер подхватит свой существующий конфиг.
+4. Разовое: `override.conf` (iptables FORWARD) применится сам при старте docker; порты/сеть прежние; данные приложений (immich library, transmission downloads и т.д.) уже в `/mnt/RED_2TB/`.
+
+**⚠️ Перезагрузка НЕ поможет** поднять контейнеры: docker `disabled` + пул приложений не настроен. Перезагрузка полезна только ПОСЛЕ шага 1.
+**⚠️ Вопрос к Alex:** docker storage `.ix-apps` (образы, ~23.6G) точно НЕ переносили на IronWolf? Если перенесли — указать куда, вернуть и настройка упростится (образы сохранятся). Если нет — только перекачка.
+
## ⚠️ НЕ ВХОДИТЬ В RW-ИМПОРТ ПОВРЕЖДЁННоГО ПУЛА
**Импорт RED_2TB в не-readonly режиме (`zpool import` без `-o readonly=on`) — ВСЕГДА даёт kernel panic** (`zfs: adding existent segment to range tree`). Это правило подтверждено много раз. При пересоздании пул НЕ импортировать — только стереть метки (`labelclear`) и создать заново. `zpool import -d /dev` (dry-list) безопасен, но не запускает пул.
From 1620109097f5a45df26e642a6d41d2a8c8a10eab Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 14:57:21 +0600
Subject: [PATCH 19/81] [2026-08-24] eagle:
family/how-to/truenas-infrastructure.md
---
family/how-to/truenas-infrastructure.md | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 89188315..766a449c 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -1,6 +1,15 @@
# TrueNAS — инфраструктура
-> Обновлено: 2026-08-21 (данные спасены на IronWolf, пул RED_2TB OFFLINE, готовность к пересозданию)
+> Обновлено: 2026-08-24 (пересоздание закончено, пул ONLINE, остался подъём docker)
+
+> ## ✅ СТАТУС на 2026-08-24: пул ПЕРЕСОЗДАН и работает; docker НЕ поднят
+> **Пул RED_2TB пересоздан и УЖЕ зареган в TrueNAS** — перезагрузка/импорт НЕ нужны. `zpool status` ONLINE (`mirror-0{sdc,sdf}`+`mirror-1{sde,sdd}`), `midclt call pool.query` id=1 RED_2TB ONLINE, топология обновлена. rsync возврата данных ЗАВЕРШЁН (storage 3.29T, immich 353G, backup 287G, Edu 214G, Photos 146G, old-bu 65G, docker, docker.bak, system — все смонтированы, `/mnt/RED_2TB/`).
+> **⚠️ НЕ ГОТОВО — слой приложений (docker) НЕ восстановлен:**
+> - `docker.service` — `inactive (dead)`, `disabled`. Образы/тома/контейнеры НЕ запущены.
+> - **`.ix-apps` датасет (docker storage, был 24G) НЕ перенесён при выгрузке на IronWolf** и НЕ сохранён — rsync шёл из `/mnt/RED_2TB/*` и не включал монтируемый в `/mnt/.ix-apps` системный датасет. Проверено: `/mnt/.ix-apps` пуст, на IronWolf его нет, в `zfs list` нет. Образы = кеш → перекачаются из registry сами; конфиги целы в `/mnt/RED_2TB/docker/`.
+> - **Контейнеры запускались через `docker-compose`, НЕ через TrueNAS Apps (TrueCharts/Custom App).** Управление в доке — `docker restart homeassistant`, `docker exec ...`; daemon.json `data-root:/mnt/.ix-apps/docker`. `.ix-apps` = просто data-root docker, не TrueNAS Apps.
+> **TODO подъёма docker:** (1) создать датасет/папку под `data-root` (куда смотрит `/mnt/.ix-apps/docker`), (2) вернуть/найти compose-файл, (3) `docker compose up -d` → контейнеры поднимутся с маунтами на `/mnt/RED_2TB/docker/` (конфиги на месте).
+> **⚠️ НЕ ЗАКРЫТО отдельно:** TimeMachine (~277G, бэкапы macOS) НЕ возвращён — на IronWolf `/mnt/IRONWOLF/TimeMachine/` root-only NFSv4 ACL → rsync не докопировал. Алекс решение по нему не подтвердил («какой нахуй возврат time machine» — вероятно не нужен, старые снепшоты не важны).
> ## ⚠️ СТАТУС на 2026-08-21: пул RED_2TB — данные СПАСЕНЫ, пул OFFLINE, пересоздаём
> **Выгрузка успешно завершена** (подтверждено Alex): данные RED_2TB (4.56T) на IronWolf (пул `DEST`, `/mnt/IRONWOLF`). **Пул RED_2TB — статус OFFLINE** в БД TrueNAS, НЕ импортирован. Конфиги docker (caddy, transmission, HA, immich и т.д.), Obsidian vault и git-репо спасены на `/mnt/IRONWOLF` (`system/`, `docker.bak/`, `docker/`, `storage/obsidian`, `storage/git`).
From 975e6ed6878840b69a1e5e9cafc0d77e79b24e71 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 15:12:28 +0600
Subject: [PATCH 20/81] [2026-08-24] eagle:
family/how-to/truenas-infrastructure.md
---
family/how-to/truenas-infrastructure.md | 26 ++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 766a449c..91cbd94d 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -8,8 +8,9 @@
> - `docker.service` — `inactive (dead)`, `disabled`. Образы/тома/контейнеры НЕ запущены.
> - **`.ix-apps` датасет (docker storage, был 24G) НЕ перенесён при выгрузке на IronWolf** и НЕ сохранён — rsync шёл из `/mnt/RED_2TB/*` и не включал монтируемый в `/mnt/.ix-apps` системный датасет. Проверено: `/mnt/.ix-apps` пуст, на IronWolf его нет, в `zfs list` нет. Образы = кеш → перекачаются из registry сами; конфиги целы в `/mnt/RED_2TB/docker/`.
> - **Контейнеры запускались через `docker-compose`, НЕ через TrueNAS Apps (TrueCharts/Custom App).** Управление в доке — `docker restart homeassistant`, `docker exec ...`; daemon.json `data-root:/mnt/.ix-apps/docker`. `.ix-apps` = просто data-root docker, не TrueNAS Apps.
-> **TODO подъёма docker:** (1) создать датасет/папку под `data-root` (куда смотрит `/mnt/.ix-apps/docker`), (2) вернуть/найти compose-файл, (3) `docker compose up -d` → контейнеры поднимутся с маунтами на `/mnt/RED_2TB/docker/` (конфиги на месте).
+> **TODO подъёма docker:** (1) создать датасет/папку под `data-root` (куда смотрит `/mnt/.ix-apps/docker`), (2) Docker engine: `systemctl enable --now docker` + проверить `docker info`, (3) поднять контейнеры: большинство уже имеют `docker-compose.yml` в `/mnt/RED_2TB/docker//` → `docker compose up -d`; **4 контейнера без compose (ha, webdav, modbus-bridge, zigbee2mqtt) восстанавливать вручную** — параметры и полный инвентарь в разделе «Инвентарь compose-файлов по папкам» ниже. Конфиги целы в `/mnt/RED_2TB/docker/`, образы перекачаются из registry (storage `.ix-apps` потерян = только кеш).
> **⚠️ НЕ ЗАКРЫТО отдельно:** TimeMachine (~277G, бэкапы macOS) НЕ возвращён — на IronWolf `/mnt/IRONWOLF/TimeMachine/` root-only NFSv4 ACL → rsync не докопировал. Алекс решение по нему не подтвердил («какой нахуй возврат time machine» — вероятно не нужен, старые снепшоты не важны).
+> **⚠️ ДРУГОЕ (проверено 2026-08-24):** пул `DEST` (IronWolf, источник спасения) на момент проверки `DEGRADED` в `zpool list` — стоит разобраться, почему (вероятно отвалился mirror-член). Также SSH с Mac к TrueNAS по 192.168.2.197 нестабилен (`Connection reset/reset by peer`, `kex_exchange_identification read`), возможен конфликт сессий при активном входе на консоль — использовать `mallexxx.duckdns.org` и ретраи.
> ## ⚠️ СТАТУС на 2026-08-21: пул RED_2TB — данные СПАСЕНЫ, пул OFFLINE, пересоздаём
> **Выгрузка успешно завершена** (подтверждено Alex): данные RED_2TB (4.56T) на IronWolf (пул `DEST`, `/mnt/IRONWOLF`). **Пул RED_2TB — статус OFFLINE** в БД TrueNAS, НЕ импортирован. Конфиги docker (caddy, transmission, HA, immich и т.д.), Obsidian vault и git-репо спасены на `/mnt/IRONWOLF` (`system/`, `docker.bak/`, `docker/`, `storage/obsidian`, `storage/git`).
@@ -106,6 +107,29 @@ TrueNAS использует **NFSv4 ACL**, а не стандартный POSIX
| modbus-bridge | modbus-bridge | — | — |
| cups-splix | cups-splix | — | принтер |
+### Инвентарь compose-файлов по папкам (проверено 2026-08-24)
+
+Каждый контейнер = своя папка `/mnt/RED_2TB/docker//`. Модель — **не один общий compose, а per-service compose** (контейнеры поднимались `docker compose up -d` из своей папки / `docker run`).
+
+**✅ Есть `docker-compose.yml` (поднимаются `docker compose -f ... up -d`)** — 21 шт:
+arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library, mbusd, mosquitto, nodered, portainer-mcp, portainer, rclone, ser2net, syncthing, transmission, vless-proxy, watchtower
+
+**⚠️ НЕТ compose-файла — только конфиг (восстанавливать вручную, параметры из доки ниже):**
+- **ha** — Home Assistant: `/mnt/RED_2TB/docker/ha/` (automations.yaml). Образ `ghcr.io/home-assistant/home-assistant:stable`, порт `8123`, volume `/mnt/RED_2TB/docker/ha` → `/config`.
+- **webdav** — `/mnt/RED_2TB/docker/webdav/` (config.yml). Образ `hacdias/webdav:latest`, домен webdav.mallexxx.duckdns.org.
+- **modbus-bridge** — `/mnt/RED_2TB/docker/modbus-bridge/` (config.yml + modbus_ha_bridge.py). Образ `modbus-bridge` (локальная сборка из репо `HA-ZONT-Modbus`). Volumes: config.yml → `/app/config.yml` (ro), modbus_ha_bridge.py → `/app/modbus_ha_bridge.py` (ro).
+- **zigbee2mqtt** — `/mnt/RED_2TB/docker/zigbee2mqtt/` (configuration.yaml). Образ `koenkk/zigbee2mqtt:latest`, работает через mosquitto.
+- **homeassistant** — в доке упомянут как отдельный контейнер (порт 8123, volume → /config); папка `docker/homeassistant/` есть, но название контейнера в таблице `homeassistant`, а папка конфига HA фактически `ha/`. При восстановлении сверить, какой реально используется.
+
+### Порядок восстановления docker-стека (зависимости)
+
+1. **docker engine:** `systemctl enable --now docker` (после создания data-root, см. шапку). Проверка: `docker info | grep -iE "Server Version|Docker Root Dir"`.
+2. **Инфраструктура/сеть:** mosquitto (MQTT) → zigbee2mqtt (нужен mosquitto) → nodered (MQTT) → caddy (прокси, тянет все домены) → vless-proxy (SOCKS5 для hermes).
+3. **Умный дом:** mbusd (Modbus TCP:502) → modbus-bridge (через mbusd) → ha (homeassistant, нужен caddy для домена).
+4. **Медиа/хранилище:** transmission (нужен caddy) → arr (radarr/sonarr/prowlarr) → inpxer (books), inpx-web, library → rclone, syncthing, filebrowser, webdav, gitea.
+5. **Сервисы/свои:** hermes-taiga (нужен vless-proxy), portainer, portainer-mcp, watchtower, cups, ser2net.
+6. **Проверка:** `docker ps`. HA: `docker exec homeassistant python -m homeassistant --script check_config --config /config`.
+
### Transmission — детали
- **Config:** `/mnt/RED_2TB/docker/transmission/` (монтируется как `/config`)
- **Данные:** `/mnt/RED_2TB/storage` (монтируется как `/mnt/storage`)
From c8988eb058acc8f70b008722a1223872199d9e3a Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 15:32:37 +0600
Subject: [PATCH 21/81] [2026-08-24] eagle:
personal/projects/personal-os/eagle-dashboard.md
personal/tech/bilety-oxota-interaktiv.md
---
.../projects/personal-os/eagle-dashboard.md | 1 +
personal/tech/bilety-oxota-interaktiv.md | 65 +++++++++++++++++++
2 files changed, 66 insertions(+)
create mode 100644 personal/tech/bilety-oxota-interaktiv.md
diff --git a/personal/projects/personal-os/eagle-dashboard.md b/personal/projects/personal-os/eagle-dashboard.md
index 8e782794..0cb62bbf 100644
--- a/personal/projects/personal-os/eagle-dashboard.md
+++ b/personal/projects/personal-os/eagle-dashboard.md
@@ -314,6 +314,7 @@ When a launchd cron is created and `launchctl print gui//` shows `la
## History
+|- 2026-08-24: **bilety.html added to Pages.** Deployed interactive hunting-license exam page (`/Library/WebServer/Documents/bilety.html`, ~894 KB). 176 question cards, 532 answer buttons. All markup embedded in the HTML: each answer is a ``, correct-answer number hidden from display (lives only in `data-correct`), inline onclick colors wrong=red / correct=green. No JS-builder script — everything static. Built from `~/Downloads/origen_raw.html` via `~/Downloads/gen2.py`. Pitfalls learned: (1) `_read_page_metadata()` reads files live — no dashboard restart needed; (2) webroot is root-owned, `sudo cp` needed unless perms loosened; (3) relative `href`/`src` deps must sit in webroot too.
|- 2026-06-25 (round 10): **Launchd status — PID/exit code from launchctl list.** Added PID parsing (next-format "PID" = N) and LastExitStatus parsing in `_list_launchd_crons()`. `last_status` is None for running processes, integer exit code otherwise. Frontend shows `· exit ` or `· never run`. Legacy tab-separated format no longer supported. macOS launchctl returns next-format plist (not JSON) — regex parsing, not json.loads.
|- 2026-06-25 (round 9): **RunAtLoad checkbox for launchd crons.** Added `RunAtLoad` field to launchd plist creation. Backend: `main.py` line 636 changed from `pd["RunAtLoad"] = False` to `pd["RunAtLoad"] = body.get("run_at_load", False)`. Frontend: checkbox "Run on load" in cron modal, shown only for source=launchd via `x-show`; `openAddCron()`, `openCronEdit()`, `saveCronModal()` all wired. Backend `_list_launchd_crons()` returns `run_at_load` for edit modal. **Also fixed:** `_msg` auto-dismiss bug — stale object reference after `fetchCrons()` replaced with `find` in fresh array (both crons and services).
- 2026-06-25 (round 7): **Launchd schedule — source-dependent validation.** `validateSchedule()` now takes `source` param. For `launchd`: only `every Ns/m/h/d` accepted; cron and ISO rejected with explicit message. For Eagle/Whale: all three formats. Hints, placeholders, and `prettySchedule()` also differ by source. `Query` import added to FastAPI.
diff --git a/personal/tech/bilety-oxota-interaktiv.md b/personal/tech/bilety-oxota-interaktiv.md
new file mode 100644
index 00000000..dc2baed0
--- /dev/null
+++ b/personal/tech/bilety-oxota-interaktiv.md
@@ -0,0 +1,65 @@
+---
+created: 2026-08-24
+tags: [html, quiz, tilda, static-page, python, eagle-dash]
+updated: 2026-08-24
+---
+
+# Билеты охотника — статичный интерактив (a1-tir.ru/bilety → bilety.html)
+
+## Итог (2026-08-24)
+
+Скачал `https://a1-tir.ru/bilety` (билеты для экзамена на гражданское оружие/охотничий минимум) и превратил в **статичную страницу с кнопками-ответами, вшитыми в HTML** (БЕЗ JS-скрипта-генератора). Задеплоено в Eagle Dashboard Pages: `/Library/WebServer/Documents/bilety.html` (≈894 КБ), URL `/local-pages/bilety.html`, вкладка Pages.
+
+- **176 карточек-вопросов, 532 кнопки-ответа** (из ~180 исходных; потеряны лишь некоторые многострочные вопросы, где ``-фрагмент без номера).
+- Каждый ответ — ``.
+- Номер правильного ответа **скрыт** — живёт только в `data-correct`, не рендерится на экран.
+- Клик (инлайн `onclick` в каждой кнопке): выбрал неверно → кнопка красная `wrong` + правильная подсвечивается зелёной `correct`; верно → зелёная.
+- CSS (`.whale-answers`, `.whale-a`, `.correct`, `.wrong`) вшит в `` страницы.
+
+## Универсальная структура исходника (Tilda text-блок)
+
+Одна строка-контейнер на раздел, `div field="text" class="t-text t-text_md ">` (4 таких блока на странице). Внутри:
+
+```
+Заголовок раздела
(не всегда)
+1.1. Текст вопроса
+ 1. Вариант1
+ 2. Вариант2
+ 3. Вариант3
+2 ← номер правильного (скрывать)
+
+1.2. ...
+...
+```
+
+- Вопрос может занимать несколько подряд идущих `` (фрагмент без номера — продолжение).
+- Ответы — текстовые узлы после ` ` между ` `, каждый начинается с `N. `.
+- `N ` — чистый номер правильного. Исключение: `Примечание : … ` — это примечание, не номер.
+- Мусор: ` `, ` `, пустые ``/``.
+
+## Генератор: `~/Downloads/gen2.py`
+
+Python (без внешних зависимостей), превращает `origen_raw.html` → `bilety.html`.
+
+```bash
+python3 gen2.py # вывод: containers=4 cards=176 buttons=532 bytes=816979
+```
+
+Ключевые моменты:
+- `find_end(html, start)` — баланс ``/`
` от индекса открывающего `` до его закрытия.
+- `transform_container` — **сначала вырезает `
]*>.*?
`** (заголовки разделов). Это критично: без этого блоки с `
`-заголовком (раздел «Правовая подготовка» и «Огневая подготовка») давали 0 карточек, вопросы терялись целиком. Убирание заголовков заставляет обрабатывать каждый `` единообразно (как в блоках без заголовка).
+- `_scan(inner)` — токенизация текста/тегов; каждый `N. `-ответ → `cur["a"]`; ``-цифра → `cur["c"]` (правильный); strong без номера при пустых ответах → мержится в текущий вопрос.
+- `_btn(num, text, correct)` — инлайн `onclick`, красящий красный/зелёный.
+
+## Pitfalls
+
+1. **Блоки с ``-заголовком**: если не вырезать `
.../p>` первым шагом, такие блоки дают 0 карточек (вопросы теряются). Симптом: в собранном файле `cards` вдвое меньше (~86 вместо ~180).
+2. **Скрытие номера**: не выводить `` в разметку — только `data-correct` на кнопке.
+3. **`grep -c` по однострочному HTML** вернёт 1 (файл — одна гигантская строка), даже если совпадений сотни. Считать через `python` (`s.count(...)`), не через grep.
+4. **Файл после генерации большой** (~894 КБ): на каждую кнопку инлайн-`onclick` — приемлемо для статики.
+5. Node парсеры/`node --max-old-space-size` тут НЕ нужны — на больших минифицированных Tilda-файлах node легко падает в OOM (heap limit) при самопальных токенизаторах. Python + `str.find`/`regex` надёжнее и не жрёт память.
+
+## Деплой в Eagle Dashboard Pages
+
+- Механика + права — в `personal/projects/personal-os/eagle-dashboard.md` (секция «Как добавить страницу в Pages»).
+- Скопировать `bilety.html` в `/Library/WebServer/Documents/` (root-owned, нужен sudo) → страница подхватывается живьём, рестарт дашборда не нужен.
From 58b9e966fff186f02015425c4e835ebd0bf0d7b5 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 24 Aug 2026 15:47:44 +0600
Subject: [PATCH 22/81] [2026-08-24] eagle:
personal/plans/ohotnichy-bilety-interaktiv.md
personal/tech/bilety-oxota-interaktiv.md
---
personal/plans/ohotnichy-bilety-interaktiv.md | 18 +++++++-------
personal/tech/bilety-oxota-interaktiv.md | 24 +++++++++++++++++--
2 files changed, 31 insertions(+), 11 deletions(-)
diff --git a/personal/plans/ohotnichy-bilety-interaktiv.md b/personal/plans/ohotnichy-bilety-interaktiv.md
index 953df556..62b5f691 100644
--- a/personal/plans/ohotnichy-bilety-interaktiv.md
+++ b/personal/plans/ohotnichy-bilety-interaktiv.md
@@ -5,20 +5,20 @@
**Возможности:** номер правильного ответа скрыт · ответы кликабельны · выбранный неправильный → красный, правильный → зелёный.
**Файлы (исходники в `~/Downloads/`):**
-- `bilety.html` — скачанная + расширенная страница (инъекция CSS/JS + метаданные)
-- `bilety.js` — enhancer: парсер вопросов + click-логика
-- `bilety.css` — стили подсветки (зелёный/красный)
-- `a1-tir_bilety_raw.html` — оригинальная скачанная копия (667 КБ)
+- `bilety.html` — финальная статичная страница (кнопки вшиты в HTML, 181 карточка / 543 кнопки, ≈902 КБ); деплой в `/Library/WebServer/Documents/bilety.html`
+- `origen_raw.html` — оригинальная скачанная копия (чистый источник для генерации)
+- `gen2.py` — Python-генератор оригинала → `bilety.html` (статические кнопки, без JS-скрипта)
+- `gen_fix.py`, `fix216.py` — точечные скрипты восстановления потерянных вопросов
+- `bilety.js`, `bilety.css` — ранняя (отклонённая) версия enhancer'а на клиентском JS; в финале НЕ используются
+- `a1-tir_bilety_raw.html` — оригинальная скачанная копия (альтернативное имя)
---
## Статус
- ✅ Страница скачана и переработана в интерактив.
-- ⏸️ **Доставка в Eagle Dashboard Pages не завершена** — упирается в права: `/Library/WebServer/Documents/` root-owned, `sudo cp` требует пароль из неинтерактивного шелла. Нужно либо получить sudo-пароль от Alex, либо он запустит вручную:
- ```bash
- sudo cp ~/Downloads/bilety.html ~/Downloads/bilety.js ~/Downloads/bilety.css /Library/WebServer/Documents/
- ```
- После копирования страница появится в `http://localhost:8880/?tab=pages` под именем `bilety` (title/purpose/contains уже заполнены). Рестарт дашборда не нужен.
+- ✅ **Задеплоено в Eagle Dashboard Pages** — `/Library/WebServer/Documents/bilety.html` (902285 байт), URL `/local-pages/bilety.html`. Рестарт дашборда не нужен (сканируется живьём).
+- ✅ **Все 181 вопрос покрыты** (1.1→1.93, 2.1→2.42, 58→103), каждая карточка ровно 3 кнопки, номер правильного скрыт, клик красит красный/зелёный.
+ - Изначально терялось 5 вопросов (1.93, 81, 83, 94, 2.16) из-за нестандартной обёртки `` и вопроса без ``. Восстановлены скриптами `~/Downloads/gen_fix.py` и `~/Downloads/fix216.py`. Подробности — в `personal/tech/bilety-oxota-interaktiv.md`.
---
diff --git a/personal/tech/bilety-oxota-interaktiv.md b/personal/tech/bilety-oxota-interaktiv.md
index dc2baed0..f121f2bd 100644
--- a/personal/tech/bilety-oxota-interaktiv.md
+++ b/personal/tech/bilety-oxota-interaktiv.md
@@ -8,14 +8,34 @@ updated: 2026-08-24
## Итог (2026-08-24)
-Скачал `https://a1-tir.ru/bilety` (билеты для экзамена на гражданское оружие/охотничий минимум) и превратил в **статичную страницу с кнопками-ответами, вшитыми в HTML** (БЕЗ JS-скрипта-генератора). Задеплоено в Eagle Dashboard Pages: `/Library/WebServer/Documents/bilety.html` (≈894 КБ), URL `/local-pages/bilety.html`, вкладка Pages.
+Скачал `https://a1-tir.ru/bilety` (билеты для экзамена на гражданское оружие/охотничий минимум) и превратил в **статичную страницу с кнопками-ответами, вшитыми в HTML** (БЕЗ JS-скрипта-генератора). Задеплоено в Eagle Dashboard Pages: `/Library/WebServer/Documents/bilety.html` (≈902 КБ, 902285 байт), URL `/local-pages/bilety.html`, вкладка Pages.
-- **176 карточек-вопросов, 532 кнопки-ответа** (из ~180 исходных; потеряны лишь некоторые многострочные вопросы, где ``-фрагмент без номера).
+- **181 карточка-вопрос, 543 кнопки-ответа** — **все вопросы** покрыты (1.1→1.93, 2.1→2.42, 58→103), каждая карточка ровно 3 кнопки. Нет дублей и пропусков.
- Каждый ответ — ``.
- Номер правильного ответа **скрыт** — живёт только в `data-correct`, не рендерится на экран.
- Клик (инлайн `onclick` в каждой кнопке): выбрал неверно → кнопка красная `wrong` + правильная подсвечивается зелёной `correct`; верно → зелёная.
- CSS (`.whale-answers`, `.whale-a`, `.correct`, `.wrong`) вшит в `` страницы.
+### Восстановление потерянных вопросов (2026-08-24, исправление)
+
+Изначально генератор давал **176 карточек** (терял 5 вопросов). Причины потери и фикс:
+1. **Номер правильного в нестандартной обёртке** — парсер ждал только простой `N `, а у части вопросов был `N ` (вопросы 81, 83, 94) или `N
` (1.93, последний в разделе). Вопрос без распознанного `correct` отбрасывался (`flush()` требует `cur["c"] is not None`).
+2. **Вопрос без ``-обёртки** — вопрос 2.16 («Отдачей оружия называется:») в оригинале записан голым текстом «`2.16. …`» без ``. Парсер не распознал его как новый вопрос и **склеил его ответы с карточкой 2.15** (у 2.15 вышло **7 кнопок** вместо 3, а 2.16 вообще пропал как отдельный вопрос).
+
+**Как исправлено** (точечные скрипты в `~/Downloads/`, без переписывания gen2):
+- `gen_fix.py` — вставляет 4 карточки (1.93, 81, 83, 94) в правильные позиции **после своих предшественников** (по поиску границ соседних карточек ``, сортировка вставок по убыванию позиции, чтобы не сдвигать индексы).
+- Дальше обнаружен и 2.16 → `fix216.py` заменяет сегмент карточки 2.15 (по абсолютным индексам от находки `2.15.` до начала `2.17.`) на корректные 2.15 (3 кнопки) + 2.16 (3 кнопки, correct=2).
+
+**Pitfall при точечной вставке**: первый вариант regex-патча (`.*?
...`) оказался слишком жадным и удалил соседние карточки (181→155). **Надёжно — по границам**: карточки в файле идут подряд без разделителя, конец одной = начало следующей ``. Вставлять по позициям, а не regex-далящим патчем.
+
+**Как проверял полноту** (главный урок): не полагаться на «~180», а сверять множества номеров оригинала vs сгенерированного:
+```python
+orig_nums = set(...) # номера из
N. и голых N. в оригинале
+gen_nums = set(re.findall(r'whale-q-text">(\d+(?:\.\d+)?\.)\s', gen))
+missing = orig_nums - gen_nums
+```
+Номера «1.», «2.», «3.» в результатах — это **ложные вхождения** (номера ответов, не вопросов), их отфильтровывать по наличию точки-разделителя (`'.' in n`).
+
## Универсальная структура исходника (Tilda text-блок)
Одна строка-контейнер на раздел, `div field="text" class="t-text t-text_md ">` (4 таких блока на странице). Внутри:
From b16407df72ece5d8ea2cfaf3e97173f99f56a3aa Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 25 Aug 2026 17:19:55 +0600
Subject: [PATCH 23/81] [2026-08-25] eagle: family/how-to/home-automation.md
family/how-to/truenas-infrastructure.md
family/plans/zont-modbus-bridge-udev-race-protection.md
---
family/how-to/home-automation.md | 4 +
family/how-to/truenas-infrastructure.md | 77 ++++++++++++-----
...zont-modbus-bridge-udev-race-protection.md | 84 +++++++++++++++++++
3 files changed, 143 insertions(+), 22 deletions(-)
create mode 100644 family/plans/zont-modbus-bridge-udev-race-protection.md
diff --git a/family/how-to/home-automation.md b/family/how-to/home-automation.md
index 18f1a19a..f064665d 100644
--- a/family/how-to/home-automation.md
+++ b/family/how-to/home-automation.md
@@ -194,6 +194,10 @@ ZONT relays
[8: Конвектор котельная - н.п.]
```
+> **ℹ️ Про «виртуальные sensor 101/102/103» и «недоступные датчики в ZONT»:**
+> 101/102/103 — это **виртуальные Modbus-slaves**, которые контейнер `modbus-bridge` на TrueNAS подставляет на шине `ttyZONT`: реальные 485-датчики **Гостиная=1, Детская=2, Спальня=3** сниффятся и их температуры выдаются ZONT'у как датчики 101/102/103 (регистр 100). ZONT (Modbus master) опрашивает их как внешние датчики.
+> Если `modbus-bridge` не запущен/не слушает `ttyZONT` → ZONT показывает эти датчики **«недоступные»**. Известная первопричина — гонка docker/udev после рестарта TrueNAS. Подробности и план защиты: `[[family/how-to/truenas-infrastructure.md#Проблема-modbus-bridge/mbusd-после-рестарта-TrueNAS-гонка-с-udev]]` и `[[family/plans/zont-modbus-bridge-udev-race-protection]]`. (Заметка обновлена 2026-08-25: добавлено пояснение про 101/102/103, ZONT relays не менялись.)
+
## Карта регистров контроллера вентиляторов
```
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 91cbd94d..2146372f 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -1,15 +1,23 @@
# TrueNAS — инфраструктура
-> Обновлено: 2026-08-24 (пересоздание закончено, пул ONLINE, остался подъём docker)
+> Обновлено: 2026-08-25 (docker поднят; modbus-bridge/mbusd починены после гонки с udev)
-> ## ✅ СТАТУС на 2026-08-24: пул ПЕРЕСОЗДАН и работает; docker НЕ поднят
-> **Пул RED_2TB пересоздан и УЖЕ зареган в TrueNAS** — перезагрузка/импорт НЕ нужны. `zpool status` ONLINE (`mirror-0{sdc,sdf}`+`mirror-1{sde,sdd}`), `midclt call pool.query` id=1 RED_2TB ONLINE, топология обновлена. rsync возврата данных ЗАВЕРШЁН (storage 3.29T, immich 353G, backup 287G, Edu 214G, Photos 146G, old-bu 65G, docker, docker.bak, system — все смонтированы, `/mnt/RED_2TB/`).
-> **⚠️ НЕ ГОТОВО — слой приложений (docker) НЕ восстановлен:**
-> - `docker.service` — `inactive (dead)`, `disabled`. Образы/тома/контейнеры НЕ запущены.
-> - **`.ix-apps` датасет (docker storage, был 24G) НЕ перенесён при выгрузке на IronWolf** и НЕ сохранён — rsync шёл из `/mnt/RED_2TB/*` и не включал монтируемый в `/mnt/.ix-apps` системный датасет. Проверено: `/mnt/.ix-apps` пуст, на IronWolf его нет, в `zfs list` нет. Образы = кеш → перекачаются из registry сами; конфиги целы в `/mnt/RED_2TB/docker/`.
-> - **Контейнеры запускались через `docker-compose`, НЕ через TrueNAS Apps (TrueCharts/Custom App).** Управление в доке — `docker restart homeassistant`, `docker exec ...`; daemon.json `data-root:/mnt/.ix-apps/docker`. `.ix-apps` = просто data-root docker, не TrueNAS Apps.
-> **TODO подъёма docker:** (1) создать датасет/папку под `data-root` (куда смотрит `/mnt/.ix-apps/docker`), (2) Docker engine: `systemctl enable --now docker` + проверить `docker info`, (3) поднять контейнеры: большинство уже имеют `docker-compose.yml` в `/mnt/RED_2TB/docker//` → `docker compose up -d`; **4 контейнера без compose (ha, webdav, modbus-bridge, zigbee2mqtt) восстанавливать вручную** — параметры и полный инвентарь в разделе «Инвентарь compose-файлов по папкам» ниже. Конфиги целы в `/mnt/RED_2TB/docker/`, образы перекачаются из registry (storage `.ix-apps` потерян = только кеш).
-> **⚠️ НЕ ЗАКРЫТО отдельно:** TimeMachine (~277G, бэкапы macOS) НЕ возвращён — на IronWolf `/mnt/IRONWOLF/TimeMachine/` root-only NFSv4 ACL → rsync не докопировал. Алекс решение по нему не подтвердил («какой нахуй возврат time machine» — вероятно не нужен, старые снепшоты не важны).
+> ## ✅ СТАТУС на 2026-08-25: пул ONLINE, docker поднят, почти все контейнеры Up
+> **Пул RED_2TB пересоздан (2026-08-21) и работает:** `zpool status` ONLINE, rsync возврата данных завершён, `/mnt/RED_2TB/` смонтирован.
+> **Docker: ПОДНЯТ на 2026-08-25.** Почти все контейнеры Up (zigbee2mqtt, webdav, vless-proxy, homeassistant, caddy, transmission, syncthing, mosquitto, nodered, immich*, gitea, filebrowser, portainer*, hermes-taiga и др.). Ещё не подняты/падают отдельные (library был Restarting; inpxer/inpx-web/arr/cups/ser2net не в списке — проверить при надобности).
+> **⚠️ Известная проблема modbus-bridge/mbusd — гонка с udev при рестарте TrueNAS:**
+> - После перезагрузки docker стартует раньше udev, поэтому `/dev/ttyZONT` и `/dev/ttyVent` на момент старта контейнеров ещё не существуют.
+> - Контейнеры с `devices: /dev/ttyZONT:...` падают на `start` с `error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (ExitCode 128/255, RestartCount=0).
+> - `restart: unless-stopped` НЕ перезапускает такой контейнер (отказ до старта процесса).
+> - Следствие: modbus-bridge не отвечает на шине → 485-датчики в ZONT отображаются **«недоступные»**.
+> - Ручной фикс: `docker start modbus-bridge mbusd` (после готовности tty-симлинков).
+> - Защита от рецидива (план, НЕ внедрён — ждёт OK Alex): `family/plans/zont-modbus-bridge-udev-race-protection.md` — POSTINIT init script с ожиданием симлинков + `docker start`.
+> **⚠️ ДРУГОЕ (открытое):** пул `DEST` (IronWolf) на 2026-08-24 `DEGRADED` — разобраться, почему (отвалился mirror-член). TimeMachine (~277G) не возвращён (бэкапы macOS, вероятно не нужен — решение Alex не подтверждено). SSH с Mac по 192.168.2.197 нестабилен — использовать `mallexxx.duckdns.org`.
+
+> ## 🔒 ИСТОРИЯ: статус до восстановления (2026-08-24)
+> **Пул RED_2TB пересоздан и УЖЕ зареган в TrueNAS** — `zpool status` ONLINE (`mirror-0{sdc,sdf}`+`mirror-1{sde,sdd}`), `midclt call pool.query` id=1 RED_2TB ONLINE. rsync возврата данных ЗАВЕРШЁН (storage 3.29T, immich 353G, backup 287G, Edu 214G, Photos 146G, old-bu 65G, docker, docker.bak, system — все смонтированы).
+> **`.ix-apps` датасет (docker storage, был 24G) НЕ перенесён** — rsync шёл из `/mnt/RED_2TB/*` и не включал монтируемый в `/mnt/.ix-apps` системный датасет. Образы = кеш → перекачаются из registry сами; конфиги целы в `/mnt/RED_2TB/docker/`.
+> **Контейнеры запускались через `docker-compose`, НЕ через TrueNAS Apps (TrueCharts/Custom App).** daemon.json `data-root:/mnt/.ix-apps/docker`. `.ix-apps` = просто data-root docker, не TrueNAS Apps.
> **⚠️ ДРУГОЕ (проверено 2026-08-24):** пул `DEST` (IronWolf, источник спасения) на момент проверки `DEGRADED` в `zpool list` — стоит разобраться, почему (вероятно отвалился mirror-член). Также SSH с Mac к TrueNAS по 192.168.2.197 нестабилен (`Connection reset/reset by peer`, `kex_exchange_identification read`), возможен конфликт сессий при активном входе на консоль — использовать `mallexxx.duckdns.org` и ретраи.
> ## ⚠️ СТАТУС на 2026-08-21: пул RED_2TB — данные СПАСЕНЫ, пул OFFLINE, пересоздаём
@@ -182,24 +190,49 @@ ssh truenas_admin@mallexxx.duckdns.org "docker restart homeassistant"
ssh truenas_admin@mallexxx.duckdns.org "docker exec homeassistant python -m homeassistant --script check_config --config /config"
```
-### mbusd — Modbus TCP gateway
+### mbusd — Modbus RTU → TCP gateway
-Мост Modbus RTU → TCP: пробрасывает серийный порт инвертора AT2 вентиляции в TCP 502.
+Мост Modbus RTU → TCP: пробрасывает серийный порт в TCP 502. Используется и для вентиляции (AT2), и для ZONT-шины.
-- **Image:** `3cky/mbusd`
-- **Порт:** `502:502`
-- **Config:** `/mnt/RED_2TB/docker/mbusd/mbusd.conf` → `/etc/mbusd.conf`
-- **Device:** `/dev/ttyVent` → `/dev/ttyUSB0` (внутри контейнера)
+- **Image:** `3cky/mbusd`, compose в `/mnt/RED_2TB/docker/mbusd/`
+- **Порт:** `502:502` (TCP)
+- **Config:** `/mnt/RED_2TB/docker/mbusd/mbusd.conf` → `/etc/mbusd.conf` (`device=/dev/ttyUSB0 speed=9600 mode=8n1`, `port=502`)
+- **Device:** `devices: - /dev/ttyVent:/dev/ttyUSB0`
+- **Entrypoint:** `["/usr/bin/mbusd","-d","-L","-","-c","/etc/mbusd.conf"]`
+- **Pitfall udev-race:** см. ниже «Проблема modbus-bridge/mbusd после рестарта».
-### modbus-bridge — AT2 Modbus → MQTT
+### modbus-bridge — 485-датчики + виртуальные slaves для ZONT
-Кастомный Python-мост: читает регистры инвертора AT2 через mbusd и публикует в MQTT → Home Assistant.
+Кастомный Python-мост (`modbus_ha_bridge.py`, репо `HA-ZONT-Modbus`), контейнер `/mnt/RED_2TB/docker/modbus-bridge/`.
-- **Image:** `modbus-bridge` (локальная сборка)
-- **Volumes:**
- - `/mnt/RED_2TB/docker/modbus-bridge/config.yml` → `/app/config.yml` (ro)
- - `/mnt/RED_2TB/docker/modbus-bridge/modbus_ha_bridge.py` → `/app/modbus_ha_bridge.py` (ro)
-- **Source:** `modbus_ha_bridge.py` в репозитории `HA-ZONT-Modbus`
+- **Image:** `modbus-bridge` (локальная сборка, `--no-cache`). **НЕ имеет compose-файла** — создавался через `docker-compose`-подобный run; для восстановления использовать параметры ниже.
+- **Device:** `devices: - /dev/ttyZONT:/dev/ttyUSB0` (внутри контейнера слушает `/dev/ttyUSB0`)
+- **Volumes:** `config.yml` → `/app/config.yml` (ro), `modbus_ha_bridge.py` → `/app/modbus_ha_bridge.py` (ro)
+- **Env (из compose):** `HA_TOKEN`, `MQTT_USER=zont`, `MQTT_PASS=...`
+- **`network_mode: host`**; MQTT broker `localhost:1883` (mosquitto), HA `http://localhost:8123`.
+
+**Роль (важно — два режима работы):**
+1. **Sniff (чтение)** — сниффит реальные 485-датчики на шине ZONT: **Dining=slave 1, Kids=slave 2, Bedroom=slave 3** (CO₂/температура/влажность, регистры 2 и 100) и публикует в MQTT (`modbus/sensors//...`), а также пишет в HA.
+2. **Mappings (виртуальные slaves для ZONT)** — подставляет на **slave 101/102/103** (регистр 100) значения temperature от 485-датчиков: Dining→101, Kids→102, Bedroom→103. **ZONT (Modbus master) опрашивает 101/102/103 как свои внешние датчики температуры.** Есть также wifi-термостат как slave 100 (`sensor.0xa4c13862d39377e6_temperature`) и Socket 1 = slave 101 через switch (обратим внимание: 101 занят и Dining temp — переиспользуется на одном slave с разными функциями; сверить в config.yml).
+3. Также через mbusd может работать с AT2 вентиляции.
+
+**Если modbus-bridge не слушает `ttyZONT`** (упал/не запущен) → ZONT не получает ответ на запросы slave 101/102/103 → в интерфейсе ZONT 485-датчики отображаются **«недоступные»**.
+
+### Проблема modbus-bridge/mbusd после рестарта TrueNAS (гонка с udev)
+
+**Симптом:** после перезагрузки TrueNAS `/dev/ttyZONT` и `/dev/ttyVent` присутствуют (`ttyZONT`→ttyUSB1, `ttyVent`→ttyUSB0, создаются init script id=1), НО контейнеры `modbus-bridge`/`mbusd` упали на старте и не перезапустились; 485-датчики в ZONT «недоступные».
+
+**Причина:** docker стартует раньше udev → при `start` контейнера устройство ещё не существует → падение ДО запуска процесса:
+```
+docker inspect --format '{{.State.Error}}'
+error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory
+# ExitCode 128 / 255, RestartCount=0
+```
+`restart: unless-stopped` такой отказ **не подхватывает** (RestartCount=0), и задержка внутри entrypoint не поможет (device mount происходит до старта процесса).
+
+**Ручной фикс:** `docker start modbus-bridge mbusd` (после того как симлинки созданы).
+
+**Защита от рецидива (план, НЕ внедрён, ждёт OK Alex):** `family/plans/zont-modbus-bridge-udev-race-protection.md` — POSTINIT init script `/mnt/RED_2TB/system/start-modbus.sh`, который ждёт появления `ttyZONT`/`ttyVent` (≤25с) и делает `docker start modbus-bridge mbusd`.
### USB device aliases
diff --git a/family/plans/zont-modbus-bridge-udev-race-protection.md b/family/plans/zont-modbus-bridge-udev-race-protection.md
new file mode 100644
index 00000000..c95e55d6
--- /dev/null
+++ b/family/plans/zont-modbus-bridge-udev-race-protection.md
@@ -0,0 +1,84 @@
+---
+title: 'План: защита modbus-bridge от гонки с udev (ZONT датчики)'
+tags:
+ - plan
+ - family
+ - homeautomation
+ - zont
+ - modbus
+updated: '2026-08-25'
+status: proposed
+---
+# План: защита modbus-bridge/mbusd от гонки с udev (после рестарта TrueNAS)
+
+> Статус: **план на согласование** (2026-08-25), ничего не применено.
+> Задача: исключить повторение «485-датчики недоступные в ZONT» после перезагрузки TrueNAS.
+
+## Контекст / причины
+- ZONT (192.168.0.10) — Modbus master на RS-485 шине `ttyZONT`. 485-датчики: Dining=1, Kids=2, Bedroom=3.
+- Контейнер `modbus-bridge` сниффит их и подставляет виртуальные slaves 101/102/103 → ZONT опрашивает как внешние датчики.
+- Если modbus-bridge не слушает шину → ZONT показывает датчики «недоступные».
+- Первопричина 2026-08-25: **гонка загрузки** — docker стартовал раньше udev, `/dev/ttyZONT` и `/dev/ttyVent` ещё не существовали → контейнеры упали на старте:
+ `error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (ExitCode 128/255, RestartCount=0).
+- `restart: unless-stopped` **не перезапускает** контейнер, упавший на этапе `docker start` (binding device fail) — отказ до старта процесса.
+
+## Ключевое ограничение (почему задержка в entrypoint НЕ поможет в чистом виде)
+У modbus-bridge и mbusd устройство проброшено через `devices: /dev/ttyZONT:/dev/ttyUSB0` / `devices: /dev/ttyVent:/dev/ttyUSB0`.
+**Docker при `start` монтирует устройство ДО запуска процесса** — если `` не существует на _момент создания/старта контейнера_, контейнер вообще не стартует, и `sleep` внутри entrypoint/CMD никогда не выполнится.
+→ Простой «sleep N в entrypoint» проблему НЕ решает.
+
+## Почему контейнеры сами НЕ поднялись (RestartCount=0) — разбор
+`restart: unless-stopped`/`always` ретраит в ДВУХ случаях:
+1. **Процесс контейнера стартовал и завершился** с ненулевым кодом → демон ретраит с backoff (RestartCount растёт).
+2. **Перезапуск самого docker daemon** → демон пробует поднять `unless-stopped`/`always` контейнеры.
+
+Наш случай — ТРЕТИЙ, особый: контейнер **вообще не стартовал** — ошибка на этапе монтирования устройства:
+`error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory`.
+- Нет процесса, который «завершился бы» — docker не смог примонтировать устройство, процесс не запущен вовсе.
+- Раз нет завершённого процесса, **restart policy не на что применять** → `RestartCount=0`, демон не ретраит.
+- После падения (13:24) демон НЕ перезапускался (docker.pid с 07:14) → второго триггера не было.
+
+Вывод: **отказ на этапе mount устройства НЕ перезапускается restart policy**. Авто-подъём при перезагрузке гарантирован только если устройства существуют на момент старта демона. Иначе — контейнеры молча остаются в exited до ручного `docker start`.
+
+## Решение (рекомендуемое): POSTINIT init script с ожиданием + docker start
+Уже есть init script id=1 «Map ttyUSB» (POSTINIT), который копирует udev-правила и делает `udevadm trigger`. Он НЕ ждёт создания симлинков и не запускает докер-контейнеры.
+
+Добавить новый POSTINIT init script (COMMAND, when=POSTINIT, enabled=true), который:
+1. Ждёт появления `/dev/ttyZONT` и `/dev/ttyVent` (poll до ~20 сек).
+2. Как только оба есть — `docker start modbus-bridge mbusd`.
+3. Если docker ещё не готов — также пробует переждать.
+
+Пример скрипта (положить в `/mnt/RED_2TB/system/start-modbus.sh`):
+```bash
+#!/bin/bash
+# Ожидание tty-алиасов от udev (макс ~25 сек)
+for i in $(seq 1 25); do
+ [ -e /dev/ttyZONT ] && [ -e /dev/ttyVent ] && break
+ sleep 1
+done
+sleep 2 # небольшая пауза на устойчивость udev
+docker start modbus-bridge mbusd 2>/dev/null
+```
+Команда в TrueNAS init: `bash /mnt/RED_2TB/system/start-modbus.sh`
+Timeout скрипта в TrueNAS: `30`.
+
+> Альтернатива (не рекомендуется): поменять `devices:` на прямой `/dev/ttyUSB1`/`/dev/ttyUSB0` — но номер ttyUSB может прыгать, алиасы для того и нужны.
+
+> Примечание: менять Dockerfile/entrypoint и/или `restart: always` здесь **недостаточно** — отказ на этапе mount устройства restart policy не обрабатывает.
+
+## Шаги внедрения (после согласования)
+1. Создать `/mnt/RED_2TB/system/start-modbus.sh` на TrueNAS (chmod +x).
+2. Добавить init script через midclt:
+ ```bash
+ midclt call initshutdownscript.create '{"type":"COMMAND","command":"bash /mnt/RED_2TB/system/start-modbus.sh","when":"POSTINIT","enabled":true,"timeout":30,"comment":"Start modbus-bridge/mbusd after udev tty aliases"}'
+ ```
+3. Проверить: `midclt call initshutdownscript.query`
+4. Проверить после reboot: контейнеры Up, `ttyZONT`/`ttyVent` созданы, датчики в ZONT отвечают.
+
+## Вердикт по исходному вопросу «добавить задержку в entrypoint»
+- Задержка внутри entrypoint/CMD для этих контейнеров **не выполняется** из-за device mount до старта процесса → этот путь отпадает.
+- Правильный эквивалент «задержки до готовности» — скрипт выше на уровне TrueNAS init (ожидание устройств, затем `docker start`).
+
+## Связанные заметки
+- [[family/how-to/home-automation.md]] — карта slave ID, ZONT, датчики
+- [[family/how-to/truenas-infrastructure.md]] — docker, modbus-bridge, mbusd, udev rules, init scripts
From 669a9c3483279a5e072f9420ad9699715b6e0c7e Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 25 Aug 2026 17:29:59 +0600
Subject: [PATCH 24/81] [2026-08-25] eagle:
family/how-to/zont-modbus-bridge-udev-race-protection.md
family/plans/zont-modbus-bridge-udev-race-protection.md
---
...zont-modbus-bridge-udev-race-protection.md | 72 ++++++++++++++++
...zont-modbus-bridge-udev-race-protection.md | 84 -------------------
2 files changed, 72 insertions(+), 84 deletions(-)
create mode 100644 family/how-to/zont-modbus-bridge-udev-race-protection.md
delete mode 100644 family/plans/zont-modbus-bridge-udev-race-protection.md
diff --git a/family/how-to/zont-modbus-bridge-udev-race-protection.md b/family/how-to/zont-modbus-bridge-udev-race-protection.md
new file mode 100644
index 00000000..c6d128dc
--- /dev/null
+++ b/family/how-to/zont-modbus-bridge-udev-race-protection.md
@@ -0,0 +1,72 @@
+---
+status: implemented
+tags:
+ - family
+ - homeautomation
+ - zont
+ - modbus
+title: Защита modbus-bridge от гонки с udev (ZONT датчики)
+updated: '2026-08-25'
+---
+# Защита modbus-bridge/mbusd от гонки с udev (ZONT датчики)
+
+> Статус: **внедрено** (2026-08-25, шаги 1–3). Датчики в ZONT снова отвечают, скрипт и init script на месте.
+
+## Проблема
+ZONT (192.168.0.10) — Modbus master на RS-485 шине `ttyZONT`. 485-датчики: Dining=1, Kids=2, Bedroom=3.
+Контейнер `modbus-bridge` сниффит их и подставляет виртуальные slaves 101/102/103 → ZONT опрашивает как внешние датчики. Если modbus-bridge не слушает шину → **ZONT показывает датчики «недоступными»**.
+
+**Первопричина (2026-08-25):** гонка загрузки TrueNAS — docker стартовал раньше udev, `/dev/ttyZONT` и `/dev/ttyVent` ещё не существовали → контейнеры `modbus-bridge` (Exit 128) и `mbusd` (Exit 255) упали на старте:
+`error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (RestartCount=0).
+Фикс был: ручной `docker start modbus-bridge mbusd`.
+
+## Почему контейнеры сами НЕ поднялись (RestartCount=0)
+`restart: unless-stopped`/`always` ретраит в ДВУХ случаях:
+1. Процесс контейнера **стартовал и завершился** с ненулевым кодом → демон ретраит с backoff.
+2. **Перезапуск docker daemon** → демон пробует поднять `unless-stopped`/`always` контейнеры.
+
+Наш случай — ТРЕТИЙ, особый: контейнер **вообще не стартовал** — ошибка на этапе монтирования устройства.
+- Нет процесса, который «завершился бы» → **restart policy не на что применять** → `RestartCount=0`, демон не ретраит.
+- После падения (13:24) демон не перезапускался → второго триггера не было.
+**Вывод:** отказ на этапе mount устройства НЕ перезапускается restart policy. Авто-подъём гарантирован только скриптом, ждущим устройства.
+
+## Ключевое ограничение (задержка в entrypoint НЕ помогает)
+У обоих контейнеров устройство проброшено через `devices: /dev/ttyZONT:/dev/ttyUSB0` / `devices: /dev/ttyVent:/dev/ttyUSB0`.
+**Docker при `start` монтирует устройство ДО запуска процесса** — если `` отсутствует на момент старта, контейнер вообще не стартует, и `sleep` внутри entrypoint/CMD **не выполняется**. Поэтому решение — на уровне init скрипта.
+
+## Внедрённое решение
+
+### 1. Скрипт `/mnt/RED_2TB/system/start-modbus.sh` (root, `-rw-r--r--`)
+Ожидает появления tty-алиасов от udev (до ~50с), затем запускает контейнеры:
+```bash
+#!/bin/bash
+# Ждём tty-алиасы от udev (макс ~25с), затем стартуем modbus контейнеры.
+for i in $(seq 1 50); do
+ [ -e /dev/ttyZONT ] && [ -e /dev/ttyVent ] && break
+ sleep 1
+done
+sleep 2
+docker start modbus-bridge mbusd 2>/dev/null
+```
+
+### 2. Init script (TrueNAS, id=3)
+- **when:** POSTINIT, **type:** COMMAND, **timeout:** 30, **enabled:** true
+- **command:** `bash /mnt/RED_2TB/system/start-modbus.sh`
+- **comment:** `Start modbus-bridge/mbusd after udev tty aliases`
+
+### Порядок POSTINIT скриптов
+| id | command | comment | enabled |
+|----|---------|---------|---------|
+| 1 | `cp .../99-tty-alias.rules /etc/udev/rules.d/ && udevadm control --reload-rules && udevadm trigger` | Map ttyUSB | ✅ |
+| 2 | `bash /mnt/RED_2TB/system/tunnel.sh &` | — | ✅ |
+| 3 | `bash /mnt/RED_2TB/system/start-modbus.sh` | Start modbus-bridge/mbusd after udev tty aliases | ✅ |
+
+## Проверка
+- `/dev/ttyZONT` → `ttyUSB1` (CH340), `/dev/ttyVent` → `ttyUSB0` — симлинки появляются после POSTINIT id=1.
+- После reboot: `docker ps` — modbus-bridge и mbusd Up; датчики в ZONT отвечают.
+- Просмотр init scripts: `midclt call initshutdownscript.query`
+- Откат: `midclt call initshutdownscript.delete ` + удалить скрипт с диска.
+
+## Связанные заметки
+- [[family/how-to/home-automation.md]] — карта slave ID, ZONT, датчики
+- [[family/how-to/truenas-infrastructure.md]] — docker, modbus-bridge, mbusd, udev rules, init scripts
diff --git a/family/plans/zont-modbus-bridge-udev-race-protection.md b/family/plans/zont-modbus-bridge-udev-race-protection.md
deleted file mode 100644
index c95e55d6..00000000
--- a/family/plans/zont-modbus-bridge-udev-race-protection.md
+++ /dev/null
@@ -1,84 +0,0 @@
----
-title: 'План: защита modbus-bridge от гонки с udev (ZONT датчики)'
-tags:
- - plan
- - family
- - homeautomation
- - zont
- - modbus
-updated: '2026-08-25'
-status: proposed
----
-# План: защита modbus-bridge/mbusd от гонки с udev (после рестарта TrueNAS)
-
-> Статус: **план на согласование** (2026-08-25), ничего не применено.
-> Задача: исключить повторение «485-датчики недоступные в ZONT» после перезагрузки TrueNAS.
-
-## Контекст / причины
-- ZONT (192.168.0.10) — Modbus master на RS-485 шине `ttyZONT`. 485-датчики: Dining=1, Kids=2, Bedroom=3.
-- Контейнер `modbus-bridge` сниффит их и подставляет виртуальные slaves 101/102/103 → ZONT опрашивает как внешние датчики.
-- Если modbus-bridge не слушает шину → ZONT показывает датчики «недоступные».
-- Первопричина 2026-08-25: **гонка загрузки** — docker стартовал раньше udev, `/dev/ttyZONT` и `/dev/ttyVent` ещё не существовали → контейнеры упали на старте:
- `error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (ExitCode 128/255, RestartCount=0).
-- `restart: unless-stopped` **не перезапускает** контейнер, упавший на этапе `docker start` (binding device fail) — отказ до старта процесса.
-
-## Ключевое ограничение (почему задержка в entrypoint НЕ поможет в чистом виде)
-У modbus-bridge и mbusd устройство проброшено через `devices: /dev/ttyZONT:/dev/ttyUSB0` / `devices: /dev/ttyVent:/dev/ttyUSB0`.
-**Docker при `start` монтирует устройство ДО запуска процесса** — если `` не существует на _момент создания/старта контейнера_, контейнер вообще не стартует, и `sleep` внутри entrypoint/CMD никогда не выполнится.
-→ Простой «sleep N в entrypoint» проблему НЕ решает.
-
-## Почему контейнеры сами НЕ поднялись (RestartCount=0) — разбор
-`restart: unless-stopped`/`always` ретраит в ДВУХ случаях:
-1. **Процесс контейнера стартовал и завершился** с ненулевым кодом → демон ретраит с backoff (RestartCount растёт).
-2. **Перезапуск самого docker daemon** → демон пробует поднять `unless-stopped`/`always` контейнеры.
-
-Наш случай — ТРЕТИЙ, особый: контейнер **вообще не стартовал** — ошибка на этапе монтирования устройства:
-`error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory`.
-- Нет процесса, который «завершился бы» — docker не смог примонтировать устройство, процесс не запущен вовсе.
-- Раз нет завершённого процесса, **restart policy не на что применять** → `RestartCount=0`, демон не ретраит.
-- После падения (13:24) демон НЕ перезапускался (docker.pid с 07:14) → второго триггера не было.
-
-Вывод: **отказ на этапе mount устройства НЕ перезапускается restart policy**. Авто-подъём при перезагрузке гарантирован только если устройства существуют на момент старта демона. Иначе — контейнеры молча остаются в exited до ручного `docker start`.
-
-## Решение (рекомендуемое): POSTINIT init script с ожиданием + docker start
-Уже есть init script id=1 «Map ttyUSB» (POSTINIT), который копирует udev-правила и делает `udevadm trigger`. Он НЕ ждёт создания симлинков и не запускает докер-контейнеры.
-
-Добавить новый POSTINIT init script (COMMAND, when=POSTINIT, enabled=true), который:
-1. Ждёт появления `/dev/ttyZONT` и `/dev/ttyVent` (poll до ~20 сек).
-2. Как только оба есть — `docker start modbus-bridge mbusd`.
-3. Если docker ещё не готов — также пробует переждать.
-
-Пример скрипта (положить в `/mnt/RED_2TB/system/start-modbus.sh`):
-```bash
-#!/bin/bash
-# Ожидание tty-алиасов от udev (макс ~25 сек)
-for i in $(seq 1 25); do
- [ -e /dev/ttyZONT ] && [ -e /dev/ttyVent ] && break
- sleep 1
-done
-sleep 2 # небольшая пауза на устойчивость udev
-docker start modbus-bridge mbusd 2>/dev/null
-```
-Команда в TrueNAS init: `bash /mnt/RED_2TB/system/start-modbus.sh`
-Timeout скрипта в TrueNAS: `30`.
-
-> Альтернатива (не рекомендуется): поменять `devices:` на прямой `/dev/ttyUSB1`/`/dev/ttyUSB0` — но номер ttyUSB может прыгать, алиасы для того и нужны.
-
-> Примечание: менять Dockerfile/entrypoint и/или `restart: always` здесь **недостаточно** — отказ на этапе mount устройства restart policy не обрабатывает.
-
-## Шаги внедрения (после согласования)
-1. Создать `/mnt/RED_2TB/system/start-modbus.sh` на TrueNAS (chmod +x).
-2. Добавить init script через midclt:
- ```bash
- midclt call initshutdownscript.create '{"type":"COMMAND","command":"bash /mnt/RED_2TB/system/start-modbus.sh","when":"POSTINIT","enabled":true,"timeout":30,"comment":"Start modbus-bridge/mbusd after udev tty aliases"}'
- ```
-3. Проверить: `midclt call initshutdownscript.query`
-4. Проверить после reboot: контейнеры Up, `ttyZONT`/`ttyVent` созданы, датчики в ZONT отвечают.
-
-## Вердикт по исходному вопросу «добавить задержку в entrypoint»
-- Задержка внутри entrypoint/CMD для этих контейнеров **не выполняется** из-за device mount до старта процесса → этот путь отпадает.
-- Правильный эквивалент «задержки до готовности» — скрипт выше на уровне TrueNAS init (ожидание устройств, затем `docker start`).
-
-## Связанные заметки
-- [[family/how-to/home-automation.md]] — карта slave ID, ZONT, датчики
-- [[family/how-to/truenas-infrastructure.md]] — docker, modbus-bridge, mbusd, udev rules, init scripts
From 8853b674bd3d05c645701631250699ea6eee1d44 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 26 Aug 2026 19:57:01 +0600
Subject: [PATCH 25/81] [2026-08-26] eagle: family/how-to/samsung.md
personal/tech/mac-print-shared-services.md personal/user-profile.md
---
family/how-to/samsung.md | 23 +++++++---
personal/tech/mac-print-shared-services.md | 49 ++++++++++++++++++++++
personal/user-profile.md | 1 +
3 files changed, 68 insertions(+), 5 deletions(-)
create mode 100644 personal/tech/mac-print-shared-services.md
diff --git a/family/how-to/samsung.md b/family/how-to/samsung.md
index 24819f98..6f550cc1 100644
--- a/family/how-to/samsung.md
+++ b/family/how-to/samsung.md
@@ -2,15 +2,28 @@
title: "🖨 Samsung — настройки"
aliases: ["Samsung CLX-2160", "Samsung TV", "samsung"]
tags: ["family", "how-to", "devices"]
-updated: "2026-05-17"
+updated: "2026-08-26"
---
# 🖨 Samsung — настройки
-## Принтер Samsung CLX-2160 (сетевой адрес)
+## Принтеры в системе (на Маке admin, macOS)
-```
-ipps://192.168.2.197:631/printers/Samsung_CLX-216x_Series
-```
+Два принтера:
+- **Samsung CLX-216x** — сетевой: `ipps://192.168.2.197:631/printers/Samsung_CLX-216x_Series`. Драйвер Generic PostScript.
+- **Samsung M2020 Series** (SEC84251974A6B3) — AirPrint (dnssd), дефолтный. Подключается по Wi-Fi/AirPrint.
+
+## Состояние печати / шеринга (диагностика 2026-08-26)
+
+Симптом: с телефона не виден принтер.
+
+Ключевые факты из CUPS на этом Маке:
+- `Listen localhost:631` — служба печати CUPS слушает **только локально**, наружу не отдаёт.
+- `SharePrinters` в `/etc/cups/cupsd.conf` не активен — **шеринг печати выключен** (оба принтера `Shared: No`).
+- Для печати с телефона через этот Мак нужно включить шеринг (`sudo cupsctl WebInterface=yes` + Enable Printer Sharing в Системных настройках), либо принтеру самому быть AirPrint-совместимым в той же сети.
+
+Внимание на подсеть: Мак в этом сеансе был на `192.168.6.x` (192.168.6.173), а основная инфраструктура (TrueNAS/принтеры/OpenWrt) живёт на `192.168.2.x` (192.168.2.197). `192.168.2.197` — это адрес TrueNAS, принтер CLX прописан на том же IP-хосте. Мак вне основной сети → `192.168.2.197` не пингуется. Расхождение возможно из-за того, что Мак был подключён не к основной сети/роутеру (или смена сети), а не из-за принтера.
+
+Примечание: `sudo -n cupsctl` не работает без пароля — шеринг удалённо через SSH проверить нельзя.
## Отключение Detecting Device
diff --git a/personal/tech/mac-print-shared-services.md b/personal/tech/mac-print-shared-services.md
new file mode 100644
index 00000000..a8b19ff1
--- /dev/null
+++ b/personal/tech/mac-print-shared-services.md
@@ -0,0 +1,49 @@
+---
+type: tech
+topic: print
+tags: [print, printer, samsung, cups, macos]
+created: 2026-08-26
+---
+
+# Печать и общие сервисы (Mac + сеть)
+
+Проверка службы печати 2026-08-26: «не видит принтер с телефона».
+
+## Состояние на момент проверки
+
+На Mac (admin, macOS 26.6.1, arm64) в системе зарегистрированы два принтера:
+
+| Принтер | Способ подключения | Статус |
+|---|---|---|
+| **Samsung CLX-216x** | Сетевой `ipp://192.168.2.197/printers/Samsung_CLX-216x_Series`, драйвер Generic PostScript | не pингуется в текущей сети |
+| **Samsung M2020 Series** (SEC84251974A6B3) | AirPrint `dnssd://…_ipp._tcp.local.`, дефолтный, PPD Samsung M2020 Series-AirPrint | не отвечает по сети |
+
+Системный дефолт: `Samsung_M2020_Series__SEC84251974A6B3_` (idle, enabled 2026-07-29).
+
+## Ключевые находки
+
+- **CUPS слушает только локально**: в `/etc/cups/cupsd.conf` → `Listen localhost:631` + `Listen /private/var/run/cupsd`. Наружу (по сети, для AirPrint) служба печати НЕ отдаётся.
+- **Шеринг печати выключен** (`SharePrinters` в cupsd.conf закомментирован/неактивен; в `system_profiler` оба принтера `Shared: No`, `System Printer Sharing: No`).
+- **Порт 631 наружу закрыт**: `nc -z 192.168.2.197 631` → closed.
+- Сеть Мака — `192.168.6.x` (адрес `192.168.6.173`, шлюз `192.168.6.1`). Принтер прописан в подсети `192.168.2.x`.
+
+## ⚠️ Важное наблюдение (пересечение с TrueNAS)
+
+Адрес **`192.168.2.197` в памяти — это TrueNAS** (truenas_admin, SSH через `mallexxx.duckdns.org`, он же `192.168.2.197`; см. память/инфраструктуру). В системе печати тот же адрес числится как Samsung CLX-216x.
+
+Вероятно одно из двух:
+1. Samsung CLX-216x физически подключён к TrueNAS и расшаривается через него на этом адресе — тогда «принтер» на 192.168.2.197 это на самом деле TrueNAS, отдающий печать.
+2. Конфиг печати помнит устаревший адрес/принтер, которого больше нет.
+
+Требуется уточнить у владельца, какой принтер физически в наличии.
+
+## Что НЕ запушено из-за запрета
+
+Запуск сканирования подсети был запрещён пользователем — активного поиска принтера по сети не проводилось.
+
+## Вывод/нерешенное
+
+- Телефон не видит принтер, потому что: (а) служба печати на Mac не отдаёт принтеры наружу (Listen localhost), и (б) принтеры не отвечают по сети.
+- Для AirPrint-печати с телефона на принтерах, висящих на Mac, нужно включить **Printer Sharing** (**System Settings → Printers & Scanners → Share this printer**) — это поднимет `SharePrinters` и разлочит порт 631 для локальной сети.
+- Для сетевого принтера (через TrueNAS) — проверить, что TrueNAS и расшаренная печать живы.
+- Требуется ответ владельца: какой принтер физически и где.
diff --git a/personal/user-profile.md b/personal/user-profile.md
index e8af1e55..0d161a59 100755
--- a/personal/user-profile.md
+++ b/personal/user-profile.md
@@ -86,6 +86,7 @@ confidence: 0.9
- Home automation (Home Assistant, Zigbee, RPi)
- 3D-печать — [[3d-print-wishlist]]
+- **Сетевые принтеры (домашние):** Samsung CLX-216x (шарится через TrueNAS, адрес `192.168.2.197`) + Samsung M2020 (по AirPrint, дефолт на Mac). Диагностика печати: см. [[personal/tech/mac-print-shared-services]]
- **Видеоигры:** игровой ПК собран как TV-приставка (лончер, джойстики, эмуляторы настроены). Играет редко — паттерн накопления без использования. Прогресс: прошёл с Лизой уровень Shovel Knight, запустил Witcher 3 (вводная сцена).
- Настолки: Руммикуб, Шакал, Ticket to Ride, Hive, Rush Hour, Диксит
- Совместные игры с Лизой — [[games-wishlist]]
From 4bb4d1706477f87d844df258058a4342e119fb48 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 26 Aug 2026 20:02:03 +0600
Subject: [PATCH 26/81] [2026-08-26] eagle: family/how-to/samsung.md
family/how-to/truenas-infrastructure.md
personal/tech/mac-print-shared-services.md
---
family/how-to/samsung.md | 17 +++++--
family/how-to/truenas-infrastructure.md | 22 ++++++++-
personal/tech/mac-print-shared-services.md | 55 ++++++++++++++++++----
3 files changed, 78 insertions(+), 16 deletions(-)
diff --git a/family/how-to/samsung.md b/family/how-to/samsung.md
index 6f550cc1..1a33f809 100644
--- a/family/how-to/samsung.md
+++ b/family/how-to/samsung.md
@@ -16,12 +16,19 @@ updated: "2026-08-26"
Симптом: с телефона не виден принтер.
-Ключевые факты из CUPS на этом Маке:
-- `Listen localhost:631` — служба печати CUPS слушает **только локально**, наружу не отдаёт.
-- `SharePrinters` в `/etc/cups/cupsd.conf` не активен — **шеринг печати выключен** (оба принтера `Shared: No`).
-- Для печати с телефона через этот Мак нужно включить шеринг (`sudo cupsctl WebInterface=yes` + Enable Printer Sharing в Системных настройках), либо принтеру самому быть AirPrint-совместимым в той же сети.
+### ✅ Корень проблемы (подтверждено SSH на TrueNAS)
-Внимание на подсеть: Мак в этом сеансе был на `192.168.6.x` (192.168.6.173), а основная инфраструктура (TrueNAS/принтеры/OpenWrt) живёт на `192.168.2.x` (192.168.2.197). `192.168.2.197` — это адрес TrueNAS, принтер CLX прописан на том же IP-хосте. Мак вне основной сети → `192.168.2.197` не пингуется. Расхождение возможно из-за того, что Мак был подключён не к основной сети/роутеру (или смена сети), а не из-за принтера.
+**Samsung CLX-216x физически подключён к TrueNAS (USB) и обслуживается docker-контейнером `cups-splix` (драйвер splix). Контейнер НЕ запущен** — образ не собран, контейнер отсутствует после пересоздания пула. Поэтому принтер не виден по сети → телефон не печатает.
+
+- На TrueNAS нет другой службы печати: `lpstat`/`cupsd` не установлены, порт 631 закрыт.
+- `192.168.2.197` — это адрес TrueNAS (не принтер); запись `ipps://192.168.2.197:631/...` = расшаренная печать TrueNAS.
+- **Фикс:** пересобрать `cups-splix` и поднять — см. `[[truenas-infrastructure]]` (раздел «cups-splix — принтер») и `[[mac-print-shared-services]]`.
+
+### Факты из CUPS на этом Маке (вторичны для этой проблемы)
+- `Listen localhost:631` — служба печати CUPS слушает **только локально**, наружу не отдаёт.
+- `SharePrinters` в `/etc/cups/cupsd.conf` не активен — **шеринг печати выключен** (оба принтера `Shared: No`). Шеринг на Маc актуален только для принтера, висящего на Маcе (Samsung M2020).
+
+Подсеть: Мак в этом сеансе был на `192.168.6.x`, инфраструктура живёт на `192.168.2.x` — расхождение из-за того, что Мак был вне основной сети, а не из-за принтера.
Примечание: `sudo -n cupsctl` не работает без пароля — шеринг удалённо через SSH проверить нельзя.
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 2146372f..53c6e00c 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -4,7 +4,7 @@
> ## ✅ СТАТУС на 2026-08-25: пул ONLINE, docker поднят, почти все контейнеры Up
> **Пул RED_2TB пересоздан (2026-08-21) и работает:** `zpool status` ONLINE, rsync возврата данных завершён, `/mnt/RED_2TB/` смонтирован.
-> **Docker: ПОДНЯТ на 2026-08-25.** Почти все контейнеры Up (zigbee2mqtt, webdav, vless-proxy, homeassistant, caddy, transmission, syncthing, mosquitto, nodered, immich*, gitea, filebrowser, portainer*, hermes-taiga и др.). Ещё не подняты/падают отдельные (library был Restarting; inpxer/inpx-web/arr/cups/ser2net не в списке — проверить при надобности).
+> **Docker: ПОДНЯТ на 2026-08-25.** Почти все контейнеры Up (zigbee2mqtt, webdav, vless-proxy, homeassistant, caddy, transmission, syncthing, mosquitto, nodered, immich*, gitea, filebrowser, portainer*, hermes-taiga и др.). Ещё не подняты/падают отдельные (library был Restarting; inpxer/inpx-web/arr/cups/ser2net не в списке — проверить при надобности). **Подтверждено 2026-08-26: `cups-splix` (принтер Samsung CLX-216x) НЕ запущен — образ не собран, контейнер отсутствует. Фикс: пересобрать образ и поднять (см. раздел «cups-splix — принтер»).**
> **⚠️ Известная проблема modbus-bridge/mbusd — гонка с udev при рестарте TrueNAS:**
> - После перезагрузки docker стартует раньше udev, поэтому `/dev/ttyZONT` и `/dev/ttyVent` на момент старта контейнеров ещё не существуют.
> - Контейнеры с `devices: /dev/ttyZONT:...` падают на `start` с `error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (ExitCode 128/255, RestartCount=0).
@@ -115,6 +115,26 @@ TrueNAS использует **NFSv4 ACL**, а не стандартный POSIX
| modbus-bridge | modbus-bridge | — | — |
| cups-splix | cups-splix | — | принтер |
+### cups-splix — принтер Samsung CLX-216x (⚠️ не поднят 2026-08-26)
+
+**Статус:** контейнер НЕ запущен. Образ `cups-splix` не собран (пропал — `.ix-apps` docker data-root не переносился при пересоздании пула; локальная сборка не «перекачается»). Папка конфига цела.
+
+Папка: `/mnt/RED_2TB/docker/cups/` → `Dockerfile`, `docker-compose.yml`, `docker build.txt`, `data/`→`/etc/cups`, `cache/`→`/var/cache/cups`, `spool/`→`/var/spool/cups`, `uld/`.
+
+- **Image:** `cups-splix` (локальная сборка из Dockerfile)
+- **Dockerfile** (debian:12-slim): `cups-daemon cups-client cups-common printer-driver-splix avahi-utils dbus usbutils nano`; `cupsadmin:admin` (группа lpadmin); `EXPOSE 631`; CMD `cupsd -f`
+- **compose:** `network_mode: host`, `privileged: true`, volume `/dev/bus/usb:/dev/bus/usb`, restart `unless-stopped`
+- **Сборка/поднятие:**
+```bash
+cd /mnt/RED_2TB/docker/cups/
+docker build -t cups-splix .
+docker compose up -d
+# проверка: порт 631 открылся, принтер подцепился
+```
+- **Проверка доступности сейчас:** `lpstat`/`cupsd` на хосте НЕ установлены, порт 631 закрыт (local и снаружи) — подтверждает, что печать не работает.
+
+> Полный разбор и симптом «телефон не видит принтер»: [[mac-print-shared-services]]
+
### Инвентарь compose-файлов по папкам (проверено 2026-08-24)
Каждый контейнер = своя папка `/mnt/RED_2TB/docker//`. Модель — **не один общий compose, а per-service compose** (контейнеры поднимались `docker compose up -d` из своей папки / `docker run`).
diff --git a/personal/tech/mac-print-shared-services.md b/personal/tech/mac-print-shared-services.md
index a8b19ff1..9505c4c4 100644
--- a/personal/tech/mac-print-shared-services.md
+++ b/personal/tech/mac-print-shared-services.md
@@ -29,21 +29,56 @@ created: 2026-08-26
## ⚠️ Важное наблюдение (пересечение с TrueNAS)
-Адрес **`192.168.2.197` в памяти — это TrueNAS** (truenas_admin, SSH через `mallexxx.duckdns.org`, он же `192.168.2.197`; см. память/инфраструктуру). В системе печати тот же адрес числится как Samsung CLX-216x.
+Адрес **`192.168.2.197` — это TrueNAS** (truenas_admin, SSH через `mallexxx.duckdns.org`), НЕ сам принтер. Samsung CLX-216x физически подключён к TrueNAS (USB) и печатается через docker-контейнер **cups-splix** (драйвер splix). Запись `ipp://192.168.2.197/printers/Samsung_CLX-216x_Series` в системе Мака — результат обнаружения расшаренной печати TrueNAS на этом адресе.
-Вероятно одно из двух:
-1. Samsung CLX-216x физически подключён к TrueNAS и расшаривается через него на этом адресе — тогда «принтер» на 192.168.2.197 это на самом деле TrueNAS, отдающий печать.
-2. Конфиг печати помнит устаревший адрес/принтер, которого больше нет.
+## ✅ НаСТОЯЩИЙ ДИАГНОЗ (2026-08-26, подтверждено SSH на TrueNAS)
-Требуется уточнить у владельца, какой принтер физически в наличии.
+**Корень проблемы: cups-splix контейнер НЕ запущен на TrueNAS.**
+
+- На TrueNAS **нет ни одной работающей службы печати**: `lpstat`/`cupsd` в системе не установлены, порт 631 закрыт (и локально, и снаружи).
+- Ни одного docker-контейнера по печати в `docker ps` нет (cup/sprint/ipp — пусто). **Официальный список контейнеров** (docker ps -a): immich(redis/server/postgres), modbus-bridge, mbusd, zigbee2mqtt, webdav, vless-proxy, hermes-taiga, portainer(-mcp), homeassistant, caddy, watchtower, transmission, syncthing, ser2net, rclone, nodered, mosquitto, library, inpxer, inpx-web, gitea, filebrowser. **`cups-splix` отсутствует.**
+- Образ `cups-splix` не собран (`docker images | grep cups|splix` → пусто).
+
+### Конфиг cups-splix (цел, на месте)
+
+Папка `/mnt/RED_2TB/docker/cups/`: `Dockerfile`, `docker-compose.yml`, `docker build.txt`, `data/`, `cache/`, `spool/`, `uld/`.
+
+**docker-compose.yml:**
+```yaml
+services:
+ cups-splix:
+ image: cups-splix
+ container_name: cups-splix
+ command: ["/usr/sbin/cupsd", "-f"]
+ network_mode: host
+ privileged: true
+ volumes:
+ - /dev/bus/usb:/dev/bus/usb
+ - /mnt/RED_2TB/docker/cups/data:/etc/cups
+ - /mnt/RED_2TB/docker/cups/cache:/var/cache/cups
+ - /mnt/RED_2TB/docker/cups/spool:/var/spool/cups
+ restart: unless-stopped
+```
+
+**Dockerfile** (debian:12-slim): ставит `cups-daemon cups-client cups-common printer-driver-splix avahi-utils dbus usbutils nano`, пользователь `cupsadmin:admin` (в группе lpadmin), `EXPOSE 631`, CMD `cupsd -f`.
+
+**Как поднять** (из доки, раздел «Сборка»):
+```
+cd /mnt/RED_2TB/docker/cups/
+docker build -t cups-splix .
+docker compose up -d # или docker run из build.txt
+# проверить: порт 631 открылся, принтер подцепился
+```
+
+### Почему выпал
+`.ix-apps` вызов docker data-root при пересоздании пула НЕ переносился → образы (=кеш) потеряны, перекачиваются заново. `cups-splix` — локальная сборка, она не «перекачается», нужно пересобрать. Конфиги целы.
## Что НЕ запушено из-за запрета
-Запуск сканирования подсети был запрещён пользователем — активного поиска принтера по сети не проводилось.
+Запуск сборки/поднятия cups-splix НЕ выполнялся — ждёт подтверждения Alex.
## Вывод/нерешенное
-- Телефон не видит принтер, потому что: (а) служба печати на Mac не отдаёт принтеры наружу (Listen localhost), и (б) принтеры не отвечают по сети.
-- Для AirPrint-печати с телефона на принтерах, висящих на Mac, нужно включить **Printer Sharing** (**System Settings → Printers & Scanners → Share this printer**) — это поднимет `SharePrinters` и разлочит порт 631 для локальной сети.
-- Для сетевого принтера (через TrueNAS) — проверить, что TrueNAS и расшаренная печать живы.
-- Требуется ответ владельца: какой принтер физически и где.
+- **Почему телефон не видит принтер:** cups-splix на TrueNAS не запущен → Samsung CLX-216x физически не обслуживается → принтера нет по сети.
+- **Фикс:** пересобрать образ и поднять контейнер cups-splix (шаги выше), затем проверить порт 631.
+- Шеринг печати на Маc не критичен для этого пути — реальная печать идёт через TrueNAS cups-splix. Включение Printer Sharing на Маc было бы актуально только для принтера, висящего на Маcе.
From dfd833f79feb200f4257d2ff444b106c4db54464 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 26 Aug 2026 20:07:05 +0600
Subject: [PATCH 27/81] [2026-08-26] eagle:
family/how-to/truenas-infrastructure.md
---
family/how-to/truenas-infrastructure.md | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 53c6e00c..0770fc2a 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -4,7 +4,7 @@
> ## ✅ СТАТУС на 2026-08-25: пул ONLINE, docker поднят, почти все контейнеры Up
> **Пул RED_2TB пересоздан (2026-08-21) и работает:** `zpool status` ONLINE, rsync возврата данных завершён, `/mnt/RED_2TB/` смонтирован.
-> **Docker: ПОДНЯТ на 2026-08-25.** Почти все контейнеры Up (zigbee2mqtt, webdav, vless-proxy, homeassistant, caddy, transmission, syncthing, mosquitto, nodered, immich*, gitea, filebrowser, portainer*, hermes-taiga и др.). Ещё не подняты/падают отдельные (library был Restarting; inpxer/inpx-web/arr/cups/ser2net не в списке — проверить при надобности). **Подтверждено 2026-08-26: `cups-splix` (принтер Samsung CLX-216x) НЕ запущен — образ не собран, контейнер отсутствует. Фикс: пересобрать образ и поднять (см. раздел «cups-splix — принтер»).**
+> **Docker: ПОДНЯТ на 2026-08-25.** Почти все контейнеры Up (zigbee2mqtt, webdav, vless-proxy, homeassistant, caddy, transmission, syncthing, mosquitto, nodered, immich*, gitea, filebrowser, portainer*, hermes-taiga и др.). ✅ **cups-splix поднят 2026-08-26** (объяснение в разделе «Печать / cups-splix» ниже): образ собран заново (`docker build`), контейнер запущен `docker compose up -d`, порт 631 открыт, принтер `Samsung_CLX-216x_Series` активен (USB `04e8:3425`). Ещё не подняты (проверить при надобности): inpxer/inpx-web/arr/ser2net. **Подтверждено 2026-08-26: `cups-splix` (принтер Samsung CLX-216x) НЕ запущен — образ не собран, контейнер отсутствует. Фикс: пересобрать образ и поднять (см. раздел «cups-splix — принтер»).**
> **⚠️ Известная проблема modbus-bridge/mbusd — гонка с udev при рестарте TrueNAS:**
> - После перезагрузки docker стартует раньше udev, поэтому `/dev/ttyZONT` и `/dev/ttyVent` на момент старта контейнеров ещё не существуют.
> - Контейнеры с `devices: /dev/ttyZONT:...` падают на `start` с `error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (ExitCode 128/255, RestartCount=0).
@@ -401,6 +401,23 @@ git -C /mnt/RED_2TB/storage/git/obsidian-vault.git log --oneline -10
---
+## Печать / cups-splix (2026-08-26, починено)
+
+**Принтер Samsung CLX-216x подключён по USB к TrueNAS** (не в сети!). Единственный путь печати — контейнер `cups-splix`, который раздаёт CUPS на `192.168.2.197:631`.
+
+**Почему телефон не видел принтер (2026-08-26):** после пересоздания пула контейнер `cups-splix` **не был поднят** — образ локальный (не registry), стёрлся вместе с `.ix-apps`; при восстановлении его не пересобирали (пометка «проверить при надобности»). Конфиг `/mnt/RED_2TB/docker/cups/data` (это `/etc/cups`) цел → принтер прописан и подцепился после рестарта сам.
+
+**Рецепт подъёма (при «не вижу принтер»):**
+```bash
+cd /mnt/RED_2TB/docker/cups
+docker build -t cups-splix . # образ локальный, собрать заново (~3.5 мин)
+docker compose up -d # поднять (privileged + /dev/bus/usb)
+docker exec cups-splix lpstat -p -d # проверить принтер
+nc -z 192.168.2.197 631 # проверить порт наружу
+```
+- Принтер: `usb://Samsung/CLX-216x%20Series?serial=9566BAGQ213134Z.&interface=1` (`03e8`?? нет — `04e8:3425 Samsung CLX-216x`).
+- Порт 631: открыт наружу после подъёма.
+
## Связанные заметки
- [[truenas-access]] — SSH-доступ
- [[truenas-rclone-backup]] — система бэкапов
From 0c30658f75b3f1cec46bbc78d629c61bf16bd8cc Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 26 Aug 2026 20:12:07 +0600
Subject: [PATCH 28/81] [2026-08-26] eagle:
family/how-to/truenas-infrastructure.md
personal/tech/mac-print-shared-services.md
---
family/how-to/truenas-infrastructure.md | 4 ++--
personal/tech/mac-print-shared-services.md | 24 ++++++++++++++++------
2 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 0770fc2a..94bbd52d 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -4,7 +4,7 @@
> ## ✅ СТАТУС на 2026-08-25: пул ONLINE, docker поднят, почти все контейнеры Up
> **Пул RED_2TB пересоздан (2026-08-21) и работает:** `zpool status` ONLINE, rsync возврата данных завершён, `/mnt/RED_2TB/` смонтирован.
-> **Docker: ПОДНЯТ на 2026-08-25.** Почти все контейнеры Up (zigbee2mqtt, webdav, vless-proxy, homeassistant, caddy, transmission, syncthing, mosquitto, nodered, immich*, gitea, filebrowser, portainer*, hermes-taiga и др.). ✅ **cups-splix поднят 2026-08-26** (объяснение в разделе «Печать / cups-splix» ниже): образ собран заново (`docker build`), контейнер запущен `docker compose up -d`, порт 631 открыт, принтер `Samsung_CLX-216x_Series` активен (USB `04e8:3425`). Ещё не подняты (проверить при надобности): inpxer/inpx-web/arr/ser2net. **Подтверждено 2026-08-26: `cups-splix` (принтер Samsung CLX-216x) НЕ запущен — образ не собран, контейнер отсутствует. Фикс: пересобрать образ и поднять (см. раздел «cups-splix — принтер»).**
+> **Docker: ПОДНЯТ на 2026-08-25.** Почти все контейнеры Up (zigbee2mqtt, webdav, vless-proxy, homeassistant, caddy, transmission, syncthing, mosquitto, nodered, immich*, gitea, filebrowser, portainer*, hermes-taiga и др.). ✅ **cups-splix поднят 2026-08-26** — образ собран заново (`docker build -t cups-splix .`), контейнер запущен `docker compose up -d`, порт 631 открыт, принтер `Samsung_CLX-216x_Series` активен (USB `04e8:3425`). Причина былого падения: образ локальный, потерян при восстановлении пула (`.ix-apps` не переносился), вручную не пересобирали. Рецепт — ниже, раздел «Печать / cups-splix». Ещё не подняты (проверить при надобности): inpxer/inpx-web/arr/ser2net.
> **⚠️ Известная проблема modbus-bridge/mbusd — гонка с udev при рестарте TrueNAS:**
> - После перезагрузки docker стартует раньше udev, поэтому `/dev/ttyZONT` и `/dev/ttyVent` на момент старта контейнеров ещё не существуют.
> - Контейнеры с `devices: /dev/ttyZONT:...` падают на `start` с `error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (ExitCode 128/255, RestartCount=0).
@@ -415,7 +415,7 @@ docker compose up -d # поднять (privileged + /dev/bus/us
docker exec cups-splix lpstat -p -d # проверить принтер
nc -z 192.168.2.197 631 # проверить порт наружу
```
-- Принтер: `usb://Samsung/CLX-216x%20Series?serial=9566BAGQ213134Z.&interface=1` (`03e8`?? нет — `04e8:3425 Samsung CLX-216x`).
+- Принтер: `usb://Samsung/CLX-216x%20Series?serial=9566BAGQ213134Z.&interface=1` (USB ID `04e8:3425` Samsung CLX-216x).
- Порт 631: открыт наружу после подъёма.
## Связанные заметки
diff --git a/personal/tech/mac-print-shared-services.md b/personal/tech/mac-print-shared-services.md
index 9505c4c4..a3c912f2 100644
--- a/personal/tech/mac-print-shared-services.md
+++ b/personal/tech/mac-print-shared-services.md
@@ -73,12 +73,24 @@ docker compose up -d # или docker run из build.txt
### Почему выпал
`.ix-apps` вызов docker data-root при пересоздании пула НЕ переносился → образы (=кеш) потеряны, перекачиваются заново. `cups-splix` — локальная сборка, она не «перекачается», нужно пересобрать. Конфиги целы.
-## Что НЕ запушено из-за запрета
+## ✅ РЕЗОЛЮЦИЯ (2026-08-26, выполнено)
-Запуск сборки/поднятия cups-splix НЕ выполнялся — ждёт подтверждения Alex.
+**cups-splix поднят, печать восстановлена.** Шаги, фактически выполненные на TrueNAS:
-## Вывод/нерешенное
+```bash
+cd /mnt/RED_2TB/docker/cups
+docker build -t cups-splix . # образ собран успешно (~225s), splix установлен
+docker compose up -d # контейнер запущен
+docker exec cups-splix lpstat -p -d # printer Samsung_CLX-216x_Series ... idle, enabled
+docker exec cups-splix lpstat -a # ... accepting requests
+nc -z 192.168.2.197 631 # 631 OPEN снаружи
+```
-- **Почему телефон не видит принтер:** cups-splix на TrueNAS не запущен → Samsung CLX-216x физически не обслуживается → принтера нет по сети.
-- **Фикс:** пересобрать образ и поднять контейнер cups-splix (шаги выше), затем проверить порт 631.
-- Шеринг печати на Маc не критичен для этого пути — реальная печать идёт через TrueNAS cups-splix. Включение Printer Sharing на Маc было бы актуально только для принтера, висящего на Маcе.
+- **Принтер подключён по USB к TrueNAS**: `usb://Samsung/CLX-216x%20Series?serial=9566BAGQ213134Z.&interface=1` (Bus 002 Device 008, `04e8:3425` Samsung CLX-216x).
+- CUPS-конфиг сохранился в `/mnt/RED_2TB/docker/cups/data` (это `/etc/cups`) → принтер прописан и подцепился после рестарта сам.
+- Образ собрался без ошибок → **раньше его просто не запускали собирать** (не поломка Dockerfile), а потеряли при восстановлении стека вместе с `.ix-apps`.
+- Порт 631 открыт наружу → печать доступна по сети на `192.168.2.197:631`.
+
+**Условие для телефона:** телефон должен быть в сети `192.168.2.x` (той же, что и TrueNAS), чтобы увидеть принтер. Если телефон в другой подсети (напр. `192.168.6.x`) — это сетевой вопрос, не печатный.
+
+Полный рецепт на будущее продублирован в `truenas-infrastructure.md` → раздел «Печать / cups-splix».
From 2f5f4de72a30fff77d16dc1e801f6c03eaca09a2 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 12:43:01 +0600
Subject: [PATCH 29/81] [2026-08-31] eagle:
personal/business/rf-tax-residency.md
---
personal/business/rf-tax-residency.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/personal/business/rf-tax-residency.md b/personal/business/rf-tax-residency.md
index 72cd2192..3ae18867 100644
--- a/personal/business/rf-tax-residency.md
+++ b/personal/business/rf-tax-residency.md
@@ -33,6 +33,7 @@ tags:
Окно 29.11.2025 → 28.11.2026:
- 29.11.25 → 14.03.26 = **106 дней**
+- 04.07.26 → 30.08.26 = **71 день**
- 04.07.26 → 12.09.26 = **71 день**
- **Итого: 177 дней** (запас: **6 дней** до 183)
From 4dbb2af7344e166f3c379d1fea27fbb69a0d5b71 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 12:48:03 +0600
Subject: [PATCH 30/81] [2026-08-31] eagle:
personal/business/rf-tax-residency.md
---
personal/business/rf-tax-residency.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/personal/business/rf-tax-residency.md b/personal/business/rf-tax-residency.md
index 3ae18867..ec9983bd 100644
--- a/personal/business/rf-tax-residency.md
+++ b/personal/business/rf-tax-residency.md
@@ -33,9 +33,9 @@ tags:
Окно 29.11.2025 → 28.11.2026:
- 29.11.25 → 14.03.26 = **106 дней**
-- 04.07.26 → 30.08.26 = **71 день**
-- 04.07.26 → 12.09.26 = **71 день**
-- **Итого: 177 дней** (запас: **6 дней** до 183)
+- 04.07.26 → 30.08.26 = **58 дней**
+- **Итого: 164 дней** (запас: **18 дней** до 183)
+- 06.09.26 → 23.09.26 = 18
### Динамика окна при пребывании с 29.11.2026
From 83bf2fddeed715415508141209870ff44ab47d90 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 13:28:29 +0600
Subject: [PATCH 31/81] [2026-08-31] eagle:
personal/tech/mac-print-shared-services.md
---
personal/tech/mac-print-shared-services.md | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/personal/tech/mac-print-shared-services.md b/personal/tech/mac-print-shared-services.md
index a3c912f2..8a42dd23 100644
--- a/personal/tech/mac-print-shared-services.md
+++ b/personal/tech/mac-print-shared-services.md
@@ -94,3 +94,22 @@ nc -z 192.168.2.197 631 # 631 OPEN снаружи
**Условие для телефона:** телефон должен быть в сети `192.168.2.x` (той же, что и TrueNAS), чтобы увидеть принтер. Если телефон в другой подсети (напр. `192.168.6.x`) — это сетевой вопрос, не печатный.
Полный рецепт на будущее продублирован в `truenas-infrastructure.md` → раздел «Печать / cups-splix».
+
+## ⚠️ ТОПОЛОГИЯ ИЗМЕНЕНА (2026-08-31) — TrueNAS больше НЕ в локальной сети
+
+TrueNAS ушла из локальной сети и доступна **только** через публичный DNS `mallexxx.duckdns.org` (внешка `90.189.160.148`). Локальный `192.168.2.197` физически **недостижим** (пинг 100% потеря) — TrueNAS не в `192.168.2.x` и не в сети телефона.
+
+**Подтверждено SSH на TrueNAS (2026-08-31):**
+- `cups-splix` **Up** (4 days), принтер `Samsung_CLX-216x_Series` **idle / enabled / accepting requests**.
+- Принтер подключён по USB: `usb://Samsung/CLX-216x%20Series?serial=9566BAGQ213134Z.&interface=1`.
+- CUPS слушает **`0.0.0.0:631`** (все интерфейсы, `network_mode: host`).
+- CUPS config: `Port 631`, `WebInterface Yes`, `ServerAlias *` (раздел `cupsd.conf`).
+- **Снаружи `mallexxx.duckdns.org:631` — CLOSED** (наружу не проброшен).
+
+**Реальная картина печати:**
+- **Samsung Mobile Print (app)** — печатает. У приложения адрес вписан вручную/закэширован (не обязательно `192.168.2.197`).
+- **Штатная Android print service** — добавлял вручную `ipp://192.168.2.197:631/printers/Samsung_CLX-216x_Series` → **«недоступен»**. Ожидаемо: TrueNAS ушла из локальной сети, а адрес `192.168.2.197` из телефона недостижим.
+
+**Вывод:** «недоступен» в штатной службе — НЕ баг настройки и НЕ проблема порта. Это прямое следствие того, что CUPS-хост (TrueNAS) физически вне сети телефона: локального пути нет, а публичный `mallexxx.duckdns.org:631` закрыт. Android-штатная служба не умеет удалённый IPP через интернет без открытого наружу порта.
+
+**ОТКРЫТЫЙ ВОПРОС** (не разобран, нет доступа к настройкам приложения): по какому именно адресу Samsung Mobile Print реально шлёт печать, раз 631 снаружи закрыт. Гипотезы: (1) приложение кэширует соединение с времён, когда TrueNAS была локально; (2) используется другой публичный путь (реверс-прокси Caddy на ином порту/HTTPS), не записанный в доках. Для полного разбора нужен адрес/хост из настроек принтера внутри Samsung Mobile Print.
From a3b9a1cd572a949d29aabcb0cf09eec943366bd7 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 13:33:31 +0600
Subject: [PATCH 32/81] [2026-08-31] eagle:
family/how-to/truenas-infrastructure.md
---
family/how-to/truenas-infrastructure.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 94bbd52d..eb931390 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -418,6 +418,8 @@ nc -z 192.168.2.197 631 # проверить порт наружу
- Принтер: `usb://Samsung/CLX-216x%20Series?serial=9566BAGQ213134Z.&interface=1` (USB ID `04e8:3425` Samsung CLX-216x).
- Порт 631: открыт наружу после подъёма.
+> ⚠️ **2026-08-31: топология изменена.** TrueNAS больше НЕ в локальной сети (только публичный DNS `mallexxx.duckdns.org`). `192.168.2.197` из телефона/внешки недостижим, и **снаружи `mallexxx.duckdns.org:631` закрыт**. Подтверждено SSH: `cups-splix` **Up**, принтер idle/enabled/accepting, CUPS слушает `0.0.0.0:631`, но наружу не проброшен. → Samsung Mobile Print (app) печатает (у него адрес вписан вручную/закэширован), а штатная Android print service с `ipp://192.168.2.197:631/...` показывает «недоступен» (ожидаемо, локального пути нет). Подробный разбор: [[mac-print-shared-services]].
+
## Связанные заметки
- [[truenas-access]] — SSH-доступ
- [[truenas-rclone-backup]] — система бэкапов
From 8e512eefd18c7a71ce7c1ee18d6b5523dfd7217d Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 13:48:39 +0600
Subject: [PATCH 33/81] [2026-08-31] eagle:
family/how-to/truenas-infrastructure.md
personal/tech/mac-print-shared-services.md
---
family/how-to/truenas-infrastructure.md | 19 +++++++-------
personal/tech/mac-print-shared-services.md | 30 +++++++++++++++++++++-
2 files changed, 38 insertions(+), 11 deletions(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index eb931390..ccc044cf 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -115,23 +115,22 @@ TrueNAS использует **NFSv4 ACL**, а не стандартный POSIX
| modbus-bridge | modbus-bridge | — | — |
| cups-splix | cups-splix | — | принтер |
-### cups-splix — принтер Samsung CLX-216x (⚠️ не поднят 2026-08-26)
+### cups-splix — принтер Samsung CLX-216x (✅ поднят 2026-08-26; ⚠️ mDNS-фикс 2026-08-31 в работе)
-**Статус:** контейнер НЕ запущен. Образ `cups-splix` не собран (пропал — `.ix-apps` docker data-root не переносился при пересоздании пула; локальная сборка не «перекачается»). Папка конфига цела.
+**Статус:** контейнер работает (2026-08-31 `Up 4 days`), принтер `Samsung_CLX-216x_Series` **idle / enabled / accepting**. CUPS слушает `0.0.0.0:631` (host-сеть). НО принтер **не анонсируется через mDNS/AirPrint** (`printer-dns-sd-name = no-value`, `avahi-daemon` в контейнере не запущен) → штатная Android print service показывает «недоступен» (подробно: [[mac-print-shared-services]]). **План mDNS-фикса (пересборка с avahi, бэкап сделан, образ собран, пересоздание контейнера ждёт OK) — см. [[mac-print-shared-services]] раздел «PLAN + ПРАВКИ».**
-Папка: `/mnt/RED_2TB/docker/cups/` → `Dockerfile`, `docker-compose.yml`, `docker build.txt`, `data/`→`/etc/cups`, `cache/`→`/var/cache/cups`, `spool/`→`/var/spool/cups`, `uld/`.
+Папка: `/mnt/RED_2TB/docker/cups/` → `Dockerfile`, `entrypoint.sh`, `docker-compose.yml`, `docker build.txt`, `data/`→`/etc/cups`, `cache/`→`/var/cache/cups`, `spool/`→`/var/spool/cups`, `uld/`, бэкап `backup_20260831-003619.tar.gz`.
- **Image:** `cups-splix` (локальная сборка из Dockerfile)
-- **Dockerfile** (debian:12-slim): `cups-daemon cups-client cups-common printer-driver-splix avahi-utils dbus usbutils nano`; `cupsadmin:admin` (группа lpadmin); `EXPOSE 631`; CMD `cupsd -f`
-- **compose:** `network_mode: host`, `privileged: true`, volume `/dev/bus/usb:/dev/bus/usb`, restart `unless-stopped`
+- **Dockerfile** (debian:12-slim): `cups-daemon cups-client cups-common printer-driver-splix avahi-daemon avahi-utils dbus usbutils nano procps`; `cupsadmin:admin` (группа lpadmin); `COPY entrypoint.sh`; `EXPOSE 631`; `ENTRYPOINT ["/entrypoint.sh"]`
+- **entrypoint.sh:** `dbus-daemon --system --fork` → `avahi-daemon --no-drop-root --syslog` (фон) → `exec cupsd -f`
+- **compose:** `image: cups-splix`, `network_mode: host`, `privileged: true`, volume `/dev/bus/usb:/dev/bus/usb`, restart `unless-stopped` (содержит устаревший `command: [cupsd -f]`, но ENTRYPOINT переопределяет — правится только через root/UI, файл 600 root)
- **Сборка/поднятие:**
```bash
cd /mnt/RED_2TB/docker/cups/
-docker build -t cups-splix .
-docker compose up -d
-# проверка: порт 631 открылся, принтер подцепился
+docker build -t cups-splix-new . # собрать новый образ с avahi (бэкап уже есть)
+# затем согласовать пересоздание работающего контейнера (docker stop/rm/tag/compose up)
```
-- **Проверка доступности сейчас:** `lpstat`/`cupsd` на хосте НЕ установлены, порт 631 закрыт (local и снаружи) — подтверждает, что печать не работает.
> Полный разбор и симптом «телефон не видит принтер»: [[mac-print-shared-services]]
@@ -418,7 +417,7 @@ nc -z 192.168.2.197 631 # проверить порт наружу
- Принтер: `usb://Samsung/CLX-216x%20Series?serial=9566BAGQ213134Z.&interface=1` (USB ID `04e8:3425` Samsung CLX-216x).
- Порт 631: открыт наружу после подъёма.
-> ⚠️ **2026-08-31: топология изменена.** TrueNAS больше НЕ в локальной сети (только публичный DNS `mallexxx.duckdns.org`). `192.168.2.197` из телефона/внешки недостижим, и **снаружи `mallexxx.duckdns.org:631` закрыт**. Подтверждено SSH: `cups-splix` **Up**, принтер idle/enabled/accepting, CUPS слушает `0.0.0.0:631`, но наружу не проброшен. → Samsung Mobile Print (app) печатает (у него адрес вписан вручную/закэширован), а штатная Android print service с `ipp://192.168.2.197:631/...` показывает «недоступен» (ожидаемо, локального пути нет). Подробный разбор: [[mac-print-shared-services]].
+> ⚠️ **2026-08-31: топология изменена.** TrueNAS больше НЕ в локальной сети (только публичный DNS `mallexxx.duckdns.org`). `192.168.2.197` из телефона/внешки недостижим, и **снаружи `mallexxx.duckdns.org:631` закрыт**. Подтверждено SSH: `cups-splix` **Up**, принтер idle/enabled/accepting, CUPS слушает `0.0.0.0:631`, но наружу не проброшен. → Samsung Mobile Print (app) печатает (у него адрес вписан вручную/закэширован), а штатная Android print service с `ipp://192.168.2.197:631/...` показывает «недоступен» (ожидаемо, локального пути нет). **Дополнительно (2026-08-31):** корень «недоступен» в штатной службе — не только сеть, но и **отсутствие mDNS/AirPrint-анонса** (avahi-daemon не запущен; `printer-dns-sd-name = no-value`). План исправления (новый Dockerfile + entrypoint с avahi, бэкап, образ собран) — в [[mac-print-shared-services]] раздел «PLAN + ПРАВКИ»; пересоздание контейнера ждёт OK Alex. Подробный разбор: [[mac-print-shared-services]].
## Связанные заметки
- [[truenas-access]] — SSH-доступ
diff --git a/personal/tech/mac-print-shared-services.md b/personal/tech/mac-print-shared-services.md
index 8a42dd23..bfdaab5e 100644
--- a/personal/tech/mac-print-shared-services.md
+++ b/personal/tech/mac-print-shared-services.md
@@ -112,4 +112,32 @@ TrueNAS ушла из локальной сети и доступна **толь
**Вывод:** «недоступен» в штатной службе — НЕ баг настройки и НЕ проблема порта. Это прямое следствие того, что CUPS-хост (TrueNAS) физически вне сети телефона: локального пути нет, а публичный `mallexxx.duckdns.org:631` закрыт. Android-штатная служба не умеет удалённый IPP через интернет без открытого наружу порта.
-**ОТКРЫТЫЙ ВОПРОС** (не разобран, нет доступа к настройкам приложения): по какому именно адресу Samsung Mobile Print реально шлёт печать, раз 631 снаружи закрыт. Гипотезы: (1) приложение кэширует соединение с времён, когда TrueNAS была локально; (2) используется другой публичный путь (реверс-прокси Caddy на ином порту/HTTPS), не записанный в доках. Для полного разбора нужен адрес/хост из настроек принтера внутри Samsung Mobile Print.
+**ОТКРЫТЫЙ ВОПРОС** (не разобран, нет доступа к настройкам приложения): по какому именно адресу Samsung Mobile Print реально шлёт печать, раз 631 снаружи закрыт. Гипотезы: (1) приложение кэширует соединение с времён, когда TrueNAS была локально; (2) используется другой публичный путь (реверс-прокси Caddy на ином порту/HTTPS), не записанный в доках. Для полного разбора нужен адрес/хост из настроек принтера внутри Samsung Mobile Print. Отмечено: **лог CUPS access_log показывает, что практически вся печать шла с IP `192.168.2.141`** (26-30 авг, POST /printers/... Print-Job → 200 successful-ok) — т.е. Samsung Mobile Print ходил по локальному IP, когда TrueNAS ещё была в сети. Похоже, приложение реально кэшировало локальное соединение.
+
+## ✅ НАСТОЯЩИЙ МЕХАНИЗМ «недоступен» в Android print service (2026-08-31, подтверждено ipptool)
+
+Дополнительная проверка **внутри контейнера** (`ipptool -tv` → CUPS) дала ключевой факт:
+
+- **`printer-dns-sd-name = no-value`**
+- В контейнере cups-splix **НЕ запущен `avahi-daemon`** (процессы: только `cupsd`; `ps aux | grep avahi` — пусто). Пакеты `dbus` + `avahi-utils` в образе есть, но **`avahi-daemon` не установлен и не стартует**, а CMD — только `cupsd -f`.
+
+**Механика:** Samsung Mobile Print — вендорское приложение, ходит на CUPS **напрямую по IPP** (POST Print-Job по IP) → работает даже без mDNS. **Android штатная служба печати** (Mopria/Default) даже при ручном добавлении по IP затем делает **mDNS/AirPrint-проверку** принтера (`_ipp._tcp` / `_ipps._tcp.local`), чтобы получить `printer-uuid` и capabilities. Если mDNS-анонса нет (`printer-dns-sd-name = no-value`, avahi не запущен) — Android помечает принтер **«недоступен»**, даже если IPP по-IP физически отвечает. Полный ответ CUPS на Get-Printer-Attributes — `successful-ok [PASS]`, принтер idle/accepting/shared — подтверждает: CUPS здоров, всё дело в отсутствии mDNS-анонса.
+
+## PLAN + ПРАВКИ (2026-08-31, частично выполнено): поднять mDNS через avahi-daemon
+
+**Место:** папка `/mnt/RED_2TB/docker/cups/`.
+
+**Выполнено:**
+- ✅ **Бэкап** конфига: `/mnt/RED_2TB/docker/cups/backup_20260831-003619.tar.gz` (создан внутри контейнера `tar` → `docker cp`; md5 `0e303a6a57ae767d1e14cd72c8039fe4`). Внутри: `/etc/cups`, `/var/spool/cups`, `/var/cache/cups`. Дубль в `/tmp/cups_backup_.tar.gz` на хосте.
+- ✅ **Новый `Dockerfile`** (в cups/, заменил старый): в apt добавлен **`avahi-daemon`** + `procps`; убран `useradd`-ку только не трогал; добавлен `COPY entrypoint.sh /entrypoint.sh`; `ENTRYPOINT ["/entrypoint.sh"]` вместо пустого `CMD ["cupsd","-f"]`.
+- ✅ **`entrypoint.sh`** (в cups/): стартует `dbus-daemon --system --fork`, затем `avahi-daemon --no-drop-root --syslog` в фоне, затем `exec cupsd -f`.
+- ✅ **Образ `cups-splix-new` собран** на TrueNAS (`docker build -t cups-splix-new .`, ~108s, без ошибок).
+
+**НЕ выполнено (ждёт OK Alex на пересоздание работающего контейнера):**
+- ⏳ `docker stop cups-splix && docker rm cups-splix` (данные в volumes на хосте `data/cache/spool` НЕ теряются)
+- ⏳ `docker tag cups-splix-new cups-splix && docker rmi <старый>`
+- ⏳ `docker compose up -d` → проверка: `ps aux | grep avahi`, `ipptool` даёт `printer-dns-sd-name` не `no-value`, `ippfind` находит принтер по mDNS.
+
+**Pitfall (обнаружен):** `docker-compose.yml` в cups/ — `-rw------- root:root` (600), **truenas_admin перезаписать не может** (Permission denied) → compose НЕ менял. Это ок: ENTRYPOINT переопределяет `command`, лишний cupsd не стартует (скрипт сам делает `exec cupsd -f`, аргументы `$@` игнорирует). Если потребуется убрать `command` из compose — править через root/UI.
+
+**Pitfall (бэкап):** `data/ cache/ spool/` под TrueNAS — `drwx------ root:lp` (0700), truenas_admin их напрямую не читает → **бэкап делать ТОЛЬКО через контейнер** (`docker exec ... tar` → `docker cp`), либо через /admin (root).
From 2ae4de034a21fa2fbe9a63b9e5efa5466c6fc1ef Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 13:55:02 +0600
Subject: [PATCH 34/81] [2026-08-31] cups-splix mDNS/AirPrint fix: Android
print service sees Samsung CLX-216x
---
family/how-to/truenas-infrastructure.md | 23 ++++++++++++++++++++++
personal/tech/mac-print-shared-services.md | 1 -
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index ccc044cf..4bd061c9 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -417,6 +417,29 @@ nc -z 192.168.2.197 631 # проверить порт наружу
- Принтер: `usb://Samsung/CLX-216x%20Series?serial=9566BAGQ213134Z.&interface=1` (USB ID `04e8:3425` Samsung CLX-216x).
- Порт 631: открыт наружу после подъёма.
+### ✅ mDNS/AirPrint анонс принтера — включён 2026-08-31 (Android print service видит принтер)
+
+Проблема: Android **штатная служба печати** (в отличие от Samsung Mobile Print, который ходит прямо по IP) ищет принтер **только через mDNS/Bonjour** (`_ipp._tcp.local`). CUPS не анонсировал: в контейнере не было `avahi-daemon`, а `printer-dns-sd-name` в IPP был `no-value` → Android помечал принтер «недоступен», даже добавленный вручную по IP.
+
+**Решение (выполнено):** пересобрали cups-splix — теперь запускает `dbus-daemon` + `avahi-daemon` рядом с cupsd через `entrypoint.sh`, и в `cupsd.conf` включено `Browsing On` + `BrowseLocalProtocols dnssd`. После этого CUPS регистрирует принтер в avahi → mDNS-анонс с **`mopria-certified=1.3`** на IPv4 `192.168.2.197` физического интерфейса `enp3s0`. Android (Mopria) теперь находит принтер сам в локальной сети.
+
+**Что изменилось в конфиге** (папка `/mnt/RED_2TB/docker/cups/`):
+- `Dockerfile`: добавлен `avahi-daemon` в apt, написан `entrypoint.sh`, `ENTRYPOINT` вместо `CMD` (`.old` образ сохранён как `cups-splix-old`).
+- `entrypoint.sh`: стартует `dbus-daemon --system`, затем `avahi-daemon --no-drop-root`, затем `exec cupsd -f`.
+- `data/cupsd.conf`: `Browsing Off` → `Browsing On` + добавлена строка `BrowseLocalProtocols dnssd` (бэкап `cupsd.conf.pre-avahi`).
+
+**⚠️ Важный питфолл:** после правки конфига НЕ использовать `docker restart cups-splix` — он убивает avahi-daemon (defunct), перед новым подъёмом обязателен **`docker stop cups-splix && docker rm cups-splix && docker compose up -d`** (полный пересоздание, чтобы entrypoint перевыполнился с нуля).
+
+**Проверка анонса:**
+```bash
+docker exec cups-splix sh -c "timeout 6 avahi-browse -rt -p _ipp._tcp | grep ';enp3s0;IPv4;'"
+# должно показать: ... Samsung\032CLX-216x... truenas.local;192.168.2.197;631; "mopria-certified=1.3" ...
+```
+
+**Бэкап конфига (2026-08-31):** `/mnt/RED_2TB/docker/cups/backup_20260831-003619.tar.gz` (md5 `0e303a6a57ae767d1e14cd72c8039fe4`) — tar из /etc/cups, /var/spool/cups, /var/cache/cups.
+
+> Диагностика и полное объяснение: [[mac-print-shared-services]]
+
> ⚠️ **2026-08-31: топология изменена.** TrueNAS больше НЕ в локальной сети (только публичный DNS `mallexxx.duckdns.org`). `192.168.2.197` из телефона/внешки недостижим, и **снаружи `mallexxx.duckdns.org:631` закрыт**. Подтверждено SSH: `cups-splix` **Up**, принтер idle/enabled/accepting, CUPS слушает `0.0.0.0:631`, но наружу не проброшен. → Samsung Mobile Print (app) печатает (у него адрес вписан вручную/закэширован), а штатная Android print service с `ipp://192.168.2.197:631/...` показывает «недоступен» (ожидаемо, локального пути нет). **Дополнительно (2026-08-31):** корень «недоступен» в штатной службе — не только сеть, но и **отсутствие mDNS/AirPrint-анонса** (avahi-daemon не запущен; `printer-dns-sd-name = no-value`). План исправления (новый Dockerfile + entrypoint с avahi, бэкап, образ собран) — в [[mac-print-shared-services]] раздел «PLAN + ПРАВКИ»; пересоздание контейнера ждёт OK Alex. Подробный разбор: [[mac-print-shared-services]].
## Связанные заметки
diff --git a/personal/tech/mac-print-shared-services.md b/personal/tech/mac-print-shared-services.md
index bfdaab5e..20c49527 100644
--- a/personal/tech/mac-print-shared-services.md
+++ b/personal/tech/mac-print-shared-services.md
@@ -34,7 +34,6 @@ created: 2026-08-26
## ✅ НаСТОЯЩИЙ ДИАГНОЗ (2026-08-26, подтверждено SSH на TrueNAS)
**Корень проблемы: cups-splix контейнер НЕ запущен на TrueNAS.**
-
- На TrueNAS **нет ни одной работающей службы печати**: `lpstat`/`cupsd` в системе не установлены, порт 631 закрыт (и локально, и снаружи).
- Ни одного docker-контейнера по печати в `docker ps` нет (cup/sprint/ipp — пусто). **Официальный список контейнеров** (docker ps -a): immich(redis/server/postgres), modbus-bridge, mbusd, zigbee2mqtt, webdav, vless-proxy, hermes-taiga, portainer(-mcp), homeassistant, caddy, watchtower, transmission, syncthing, ser2net, rclone, nodered, mosquitto, library, inpxer, inpx-web, gitea, filebrowser. **`cups-splix` отсутствует.**
- Образ `cups-splix` не собран (`docker images | grep cups|splix` → пусто).
From af9a0bd9c97ca440dcce966e40d1fc53f1aacfe0 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 13:58:43 +0600
Subject: [PATCH 35/81] [2026-08-31] eagle:
family/how-to/truenas-infrastructure.md
personal/tech/mac-print-shared-services.md
---
family/how-to/truenas-infrastructure.md | 20 ++++++++++--------
personal/tech/mac-print-shared-services.md | 24 ++++++++++++++--------
2 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 4bd061c9..04739411 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -1,6 +1,6 @@
# TrueNAS — инфраструктура
-> Обновлено: 2026-08-25 (docker поднят; modbus-bridge/mbusd починены после гонки с udev)
+> Обновлено: 2026-08-31 (cups-splix mDNS-фикс завершён; docker поднят)
> ## ✅ СТАТУС на 2026-08-25: пул ONLINE, docker поднят, почти все контейнеры Up
> **Пул RED_2TB пересоздан (2026-08-21) и работает:** `zpool status` ONLINE, rsync возврата данных завершён, `/mnt/RED_2TB/` смонтирован.
@@ -115,21 +115,23 @@ TrueNAS использует **NFSv4 ACL**, а не стандартный POSIX
| modbus-bridge | modbus-bridge | — | — |
| cups-splix | cups-splix | — | принтер |
-### cups-splix — принтер Samsung CLX-216x (✅ поднят 2026-08-26; ⚠️ mDNS-фикс 2026-08-31 в работе)
+### cups-splix — принтер Samsung CLX-216x (✅ поднят 2026-08-26; ✅ mDNS-фикс 2026-08-31 завершён)
-**Статус:** контейнер работает (2026-08-31 `Up 4 days`), принтер `Samsung_CLX-216x_Series` **idle / enabled / accepting**. CUPS слушает `0.0.0.0:631` (host-сеть). НО принтер **не анонсируется через mDNS/AirPrint** (`printer-dns-sd-name = no-value`, `avahi-daemon` в контейнере не запущен) → штатная Android print service показывает «недоступен» (подробно: [[mac-print-shared-services]]). **План mDNS-фикса (пересборка с avahi, бэкап сделан, образ собран, пересоздание контейнера ждёт OK) — см. [[mac-print-shared-services]] раздел «PLAN + ПРАВКИ».**
+**Статус:** контейнер работает, принтер `Samsung_CLX-216x_Series` **idle / enabled / accepting**. CUPS слушает `0.0.0.0:631` (host-сеть). mDNS-анонс **включён** — штатная Android print service теперь видит принтер по mDNS (подробно: [[mac-print-shared-services]]). См. ниже раздел «✅ mDNS/AirPrint анонс принтера».
-Папка: `/mnt/RED_2TB/docker/cups/` → `Dockerfile`, `entrypoint.sh`, `docker-compose.yml`, `docker build.txt`, `data/`→`/etc/cups`, `cache/`→`/var/cache/cups`, `spool/`→`/var/spool/cups`, `uld/`, бэкап `backup_20260831-003619.tar.gz`.
+Папка: `/mnt/RED_2TB/docker/cups/` → `Dockerfile`, `entrypoint.sh`, `docker-compose.yml`, `data/`→`/etc/cups`, `cache/`→`/var/cache/cups`, `spool/`→`/var/spool/cups`, `uld/`, бэкап `backup_20260831-003619.tar.gz`.
-- **Image:** `cups-splix` (локальная сборка из Dockerfile)
+- **Image:** `cups-splix` (локальная сборка из Dockerfile; старый образ сохранён как `cups-splix-old` для отката)
- **Dockerfile** (debian:12-slim): `cups-daemon cups-client cups-common printer-driver-splix avahi-daemon avahi-utils dbus usbutils nano procps`; `cupsadmin:admin` (группа lpadmin); `COPY entrypoint.sh`; `EXPOSE 631`; `ENTRYPOINT ["/entrypoint.sh"]`
- **entrypoint.sh:** `dbus-daemon --system --fork` → `avahi-daemon --no-drop-root --syslog` (фон) → `exec cupsd -f`
- **compose:** `image: cups-splix`, `network_mode: host`, `privileged: true`, volume `/dev/bus/usb:/dev/bus/usb`, restart `unless-stopped` (содержит устаревший `command: [cupsd -f]`, но ENTRYPOINT переопределяет — правится только через root/UI, файл 600 root)
-- **Сборка/поднятие:**
+- **Сборка/поднятие (⚠️ полное пересоздание, НЕ `docker restart`):**
```bash
cd /mnt/RED_2TB/docker/cups/
-docker build -t cups-splix-new . # собрать новый образ с avahi (бэкап уже есть)
-# затем согласовать пересоздание работающего контейнера (docker stop/rm/tag/compose up)
+docker build -t cups-splix-new .
+docker stop cups-splix && docker rm cups-splix
+docker tag cups-splix-new cups-splix && docker tag cups-splix cups-splix-old 2>/dev/null
+docker compose up -d
```
> Полный разбор и симптом «телефон не видит принтер»: [[mac-print-shared-services]]
@@ -440,7 +442,7 @@ docker exec cups-splix sh -c "timeout 6 avahi-browse -rt -p _ipp._tcp | grep ';e
> Диагностика и полное объяснение: [[mac-print-shared-services]]
-> ⚠️ **2026-08-31: топология изменена.** TrueNAS больше НЕ в локальной сети (только публичный DNS `mallexxx.duckdns.org`). `192.168.2.197` из телефона/внешки недостижим, и **снаружи `mallexxx.duckdns.org:631` закрыт**. Подтверждено SSH: `cups-splix` **Up**, принтер idle/enabled/accepting, CUPS слушает `0.0.0.0:631`, но наружу не проброшен. → Samsung Mobile Print (app) печатает (у него адрес вписан вручную/закэширован), а штатная Android print service с `ipp://192.168.2.197:631/...` показывает «недоступен» (ожидаемо, локального пути нет). **Дополнительно (2026-08-31):** корень «недоступен» в штатной службе — не только сеть, но и **отсутствие mDNS/AirPrint-анонса** (avahi-daemon не запущен; `printer-dns-sd-name = no-value`). План исправления (новый Dockerfile + entrypoint с avahi, бэкап, образ собран) — в [[mac-print-shared-services]] раздел «PLAN + ПРАВКИ»; пересоздание контейнера ждёт OK Alex. Подробный разбор: [[mac-print-shared-services]].
+> ⚠️ **2026-08-31: топология изменена.** TrueNAS больше НЕ в локальной сети (только публичный DNS `mallexxx.duckdns.org`) — из других подсетей/внешки `192.168.2.197` недостижим, и **снаружи `mallexxx.duckdns.org:631` закрыт**. НО **из самой подсети `192.168.2.x` TrueNAS достижима** (телефон `192.168.2.141` успешно печатал по `192.168.2.197:631` — CUPS access_log это подтверждает). → Samsung Mobile Print (app) печатает (висит на локальном IPP внутри подсети), а штатная Android print service с `ipp://192.168.2.197:631/...` показывала «недоступен» из-за **отсутствия mDNS/AirPrint-анонса** (avahi-daemon не запущен; `printer-dns-sd-name = no-value`). ✅ **Исправлено** (см. раздел «✅ mDNS/AirPrint анонс принтера»): cups-splix пересобран с avahi-daemon, cupsd.conf получил `Browsing On` + `BrowseLocalProtocols dnssd` → принтер анонсируется по mDNS (`mopria-certified=1.3`, IPv4 `192.168.2.197`); Android-служба теперь находит принтер сама. Детали и откат: [[mac-print-shared-services]].
## Связанные заметки
- [[truenas-access]] — SSH-доступ
diff --git a/personal/tech/mac-print-shared-services.md b/personal/tech/mac-print-shared-services.md
index 20c49527..a551e507 100644
--- a/personal/tech/mac-print-shared-services.md
+++ b/personal/tech/mac-print-shared-services.md
@@ -3,6 +3,7 @@ type: tech
topic: print
tags: [print, printer, samsung, cups, macos]
created: 2026-08-26
+updated: 2026-08-31
---
# Печать и общие сервисы (Mac + сеть)
@@ -122,21 +123,26 @@ TrueNAS ушла из локальной сети и доступна **толь
**Механика:** Samsung Mobile Print — вендорское приложение, ходит на CUPS **напрямую по IPP** (POST Print-Job по IP) → работает даже без mDNS. **Android штатная служба печати** (Mopria/Default) даже при ручном добавлении по IP затем делает **mDNS/AirPrint-проверку** принтера (`_ipp._tcp` / `_ipps._tcp.local`), чтобы получить `printer-uuid` и capabilities. Если mDNS-анонса нет (`printer-dns-sd-name = no-value`, avahi не запущен) — Android помечает принтер **«недоступен»**, даже если IPP по-IP физически отвечает. Полный ответ CUPS на Get-Printer-Attributes — `successful-ok [PASS]`, принтер idle/accepting/shared — подтверждает: CUPS здоров, всё дело в отсутствии mDNS-анонса.
-## PLAN + ПРАВКИ (2026-08-31, частично выполнено): поднять mDNS через avahi-daemon
+## ✅ РЕЗУЛЬТАТ (2026-08-31, выполнен полностью): mDNS поднят через avahi-daemon
**Место:** папка `/mnt/RED_2TB/docker/cups/`.
-**Выполнено:**
+**Выполнено и проверено (все шаги):**
- ✅ **Бэкап** конфига: `/mnt/RED_2TB/docker/cups/backup_20260831-003619.tar.gz` (создан внутри контейнера `tar` → `docker cp`; md5 `0e303a6a57ae767d1e14cd72c8039fe4`). Внутри: `/etc/cups`, `/var/spool/cups`, `/var/cache/cups`. Дубль в `/tmp/cups_backup_.tar.gz` на хосте.
-- ✅ **Новый `Dockerfile`** (в cups/, заменил старый): в apt добавлен **`avahi-daemon`** + `procps`; убран `useradd`-ку только не трогал; добавлен `COPY entrypoint.sh /entrypoint.sh`; `ENTRYPOINT ["/entrypoint.sh"]` вместо пустого `CMD ["cupsd","-f"]`.
+- ✅ **Новый `Dockerfile`** (в cups/, заменил старый): в apt добавлен **`avahi-daemon`** + `procps`; добавлен `COPY entrypoint.sh /entrypoint.sh`; `ENTRYPOINT ["/entrypoint.sh"]` вместо `CMD ["cupsd","-f"]`.
- ✅ **`entrypoint.sh`** (в cups/): стартует `dbus-daemon --system --fork`, затем `avahi-daemon --no-drop-root --syslog` в фоне, затем `exec cupsd -f`.
-- ✅ **Образ `cups-splix-new` собран** на TrueNAS (`docker build -t cups-splix-new .`, ~108s, без ошибок).
+- ✅ **`cupsd.conf`**: `Browsing Off` → `Browsing On` + `BrowseLocalProtocols dnssd` (бэкап `cupsd.conf.pre-avahi`). Валидность: `/usr/sbin/cupsd -t` → OK.
+- ✅ **Образ**: собрал `cups-splix-new`, затем пометил **старый образ как `cups-splix-old`** (откат), а новый — как `cups-splix`.
+- ✅ **Контейнер пересоздан**: `docker stop cups-splix && docker rm cups-splix && docker compose up -d`.
-**НЕ выполнено (ждёт OK Alex на пересоздание работающего контейнера):**
-- ⏳ `docker stop cups-splix && docker rm cups-splix` (данные в volumes на хосте `data/cache/spool` НЕ теряются)
-- ⏳ `docker tag cups-splix-new cups-splix && docker rmi <старый>`
-- ⏳ `docker compose up -d` → проверка: `ps aux | grep avahi`, `ipptool` даёт `printer-dns-sd-name` не `no-value`, `ippfind` находит принтер по mDNS.
+**Проверка mDNS (всё сошлось):**
+- `ps aux`: запущены `cupsd` (PID1), `dbus-daemon --system`, `avahi-daemon: running [truenas.local]`.
+- `ipptool` Get-Printer-Attributes: `printer-dns-sd-name` больше не `no-value` → теперь анонс зарегистрирован.
+- `avahi-browse -rt -p _ipp._tcp`: на физическом интерфейсе **`;enp3s0;IPv4;`** по адресу **`192.168.2.197;631`** с TXT **`mopria-certified=1.3`**, `pdl=application/pdf,...`, `rp=printers/Samsung_CLX-216x_Series`, `UUID=68e3b8c6-...`.
+- CUPS по-прежнему слушает `0.0.0.0:631`, принтер `idle/enabled/accepting`.
-**Pitfall (обнаружен):** `docker-compose.yml` в cups/ — `-rw------- root:root` (600), **truenas_admin перезаписать не может** (Permission denied) → compose НЕ менял. Это ок: ENTRYPOINT переопределяет `command`, лишний cupsd не стартует (скрипт сам делает `exec cupsd -f`, аргументы `$@` игнорирует). Если потребуется убрать `command` из compose — править через root/UI.
+**Pitfall (важно):** НЕ использовать **`docker restart cups-splix`** после правки конфига — он убивает avahi-daemon (становится ``), dbus тоже пропадает. Подъём ТОЛЬКО полным пересозданием: **`docker stop cups-splix && docker rm cups-splix && docker compose up -d`**, чтобы `entrypoint.sh` выполнился с нуля.
+
+**Pitfall (compose):** `docker-compose.yml` в cups/ — `-rw------- root:root` (600), **truenas_admin перезаписать не может** (Permission denied) → compose НЕ менял. Это ок: ENTRYPOINT переопределяет `command`, лишний cupsd не стартует (скрипт сам делает `exec cupsd -f`, аргументы `$@` игнорирует). Если потребуется убрать `command` из compose — править через root/UI.
**Pitfall (бэкап):** `data/ cache/ spool/` под TrueNAS — `drwx------ root:lp` (0700), truenas_admin их напрямую не читает → **бэкап делать ТОЛЬКО через контейнер** (`docker exec ... tar` → `docker cp`), либо через /admin (root).
From 52286b02f4d266bcfe7f738734baf6958297c966 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 14:08:48 +0600
Subject: [PATCH 36/81] [2026-08-31] eagle:
family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md
---
.../2026-08-31-syncthing-truenas-incident.md | 66 +++++++++++++++++++
1 file changed, 66 insertions(+)
create mode 100644 family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md
diff --git a/family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md b/family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md
new file mode 100644
index 00000000..aa672386
--- /dev/null
+++ b/family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md
@@ -0,0 +1,66 @@
+---
+tags: [syncthing, truenas, debug, incident]
+created: 2026-08-31
+status: diagnosed
+---
+
+# Incident: Syncthing не синкается с TrueNAS (после restore)
+
+**Дата диагностики:** 2026-08-31
+**Симптом:** "Syncthing не конектится к TrueNAS; TrueNAS не в нашей сети, доступен через внешний адрес". В UI папка не синкается / процентов нет.
+
+## Краткий вывод
+
+**Соединение НЕ сломано.** И контейнер на TrueNAS, и телефон успешно поднимают TCP/TCP+TLS через внешний адрес `90.189.160.148:22000` (`syncthing.mallexxx.duckdns.org`). **Стоит файловая синхронизация** из-за сломанных прав на `/mnt/RED_2TB/storage/obsidian` после восстановления из бэкапа.
+
+## Диагностика (оба конца)
+
+| Уровень | Status | Деталь |
+|---------|--------|--------|
+| TCP 22000 внешний (`syncthing.mallexxx.duckdns.org`) | ✅ | `Connection succeeded` |
+| SSH TrueNAS (`mallexxx.duckdns.org`) | ✅ | IP 192.168.2.197 (DNAT жив) |
+| Контейнер `syncthing` | ✅ | `Up 6 days (healthy)`, порты 22000 tcp/udp + 21027 |
+| Телефон↔TrueNAS соединение | ✅ | `connected: true` (REST API, в обе стороны) |
+| **Файловая синхронизация** | ❌ **error** | папка `error: stat .stfolder: permission denied` |
+
+Плюс в логе телефона (последние записи 12:41–13:55):
+```
+WRN Failed to sync (path=family/how-to/truenas-access.md error="syncing: finishing: pull: no such file")
+WRN Failed to sync (path=family/how-to/truenas-sata-ports-and-zfs-pools.md ...)
+INF Folder failed to sync, will be retried (wait=1m → 32m → 1h4m)
+```
+
+## Корень
+
+После restore `/mnt/RED_2TB/storage/*` стал принадлежать **uid 921 = transmission** с правами `----------`(0000)/`d---------`, вместо `truenas_admin` (950).
+```
+d--------- 7 921 921 ... /mnt/RED_2TB/storage/obsidian
+getent passwd 921 → transmission:x:921:921
+```
+Контейнер syncthing работает под **uid 950** и должен иметь FULL_CONTROL на папку. Из-за нулевых прав не читает даже `.stfolder` → папка в error → телефон не может вытянуть зависшие файлы (`truenas-access.md`, `truenas-sata-ports-and-zfs-pools.md`) → вся папка стоит.
+
+⚠️ **Масштаб:** судя по `ls -la /mnt/RED_2TB/storage`, та же проблема может касаться Cartoons, Downloads, Movies, Music, git, shared, series и др. — всё 921/0000. Проверить другие сервисы.
+
+## План фикса (НЕ выполнен — требует подтверждения + бэкап ACL)
+
+1. Снять бэкап текущих ACL:
+ `getfacl -R /mnt/RED_2TB/storage/obsidian > /mnt/RED_2TB/backup/_acl_obsidian_$(date +%F).facl`
+2. Вернуть владельца и права:
+ ```
+ sudo chown -R 950:950 /mnt/RED_2TB/storage/obsidian
+ sudo chmod -R u+rwX,g+rwX /mnt/RED_2TB/storage/obsidian
+ ```
+3. Рестарт контейнера: `docker compose -f /mnt/RED_2TB/docker/syncthing/docker-compose.yml restart`
+4. Дождаться `Completed scan`, проверить статус папки через REST.
+
+## Затрагиваемые файлы (лог телефона)
+- `family/how-to/truenas-access.md`
+- `family/how-to/truenas-sata-ports-and-zfs-pools.md`
+
+## Техническая карта Syncthing
+- Устройства sync: `XJRJQB2-...` = контейнер TrueNAS (myID), `C6DYBCZ-...` = телефон SM-S931B (v2.1.3)
+- Folder ID: `k5vyy-gzdgj`, label `Obsidian Vault`, path `/var/syncthing/obsidian-vault`
+- GUI на TrueNAS: 'syncthing-admin', apikey в config.xml
+
+## Связанные заметки
+- [[syncthing-truenas-android]] — основная документация по этой схеме
From 84e0f90245d39616f0c825ae94f05f9893149c5b Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 17:55:58 +0600
Subject: [PATCH 37/81] [2026-08-31] eagle: Untitled 1.md Untitled.md
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
Untitled 1.md | 0
Untitled.md | 0
...-extension-health-pixels-privacy-triage.md | 94 +++++++++++++++++++
3 files changed, 94 insertions(+)
delete mode 100644 Untitled 1.md
delete mode 100644 Untitled.md
create mode 100644 work/projects/cpm-extension-health-pixels-privacy-triage.md
diff --git a/Untitled 1.md b/Untitled 1.md
deleted file mode 100644
index e69de29b..00000000
diff --git a/Untitled.md b/Untitled.md
deleted file mode 100644
index e69de29b..00000000
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
new file mode 100644
index 00000000..3918bd46
--- /dev/null
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -0,0 +1,94 @@
+# Privacy Triage: Apple - CPM Embedded WebExtension Health Pixels
+
+Status: **DRAFT**
+
+Name:
+Alex M
+
+Email address:
+amartemyanov@duckduckgo.com
+
+Name the triage:
+Privacy Triage: Apple - CPM embedded WebExtension health pixels
+
+Objective:
+O-E
+
+List all pixels that need to be collected or modified:
+
+PR: [Add PR link]
+
+The iOS names below receive the standard platform and form-factor suffixes. The corresponding macOS names end in `_macos`.
+
+- Name: `debug_web_extension_cpm_page_initialization_missing` / `debug_web_extension_cpm_page_initialization_missing_macos`
+ - Triggering Condition: Fires after an eligible main-frame HTTP(S) navigation finishes, a grace period expires, the embedded CPM WebExtension context is loaded, and no initial CPM status was received for the current document. This records a page-level initialization miss only; it does not classify the extension as globally stuck.
+ - Frequency: Daily and standard.
+ - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
+
+- Name: `debug_web_extension_cpm_page_initialization_recovered` / `debug_web_extension_cpm_page_initialization_recovered_macos`
+ - Triggering Condition: Fires when CPM successfully initializes on a subsequent completed navigation or page reload after a page-level initialization miss, without reloading the embedded WebExtension. This distinguishes a transient page-load race from persistent extension messaging failure.
+ - Frequency: Once per detected health episode.
+ - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
+
+- Name: `debug_web_extension_cpm_messaging_suspected_stuck` / `debug_web_extension_cpm_messaging_suspected_stuck_macos`
+ - Triggering Condition: Fires once when the native cross-tab health monitor observes repeated CPM initialization misses across independent completed navigation attempts, including an ordinary page reload and another tab, with no successful CPM status received globally between the attempts. The embedded WebExtension context must remain loaded and must not report a background-content load error. This is the signal that initiates the self-healing extension reload.
+ - Frequency: Once per detected health episode, with daily and standard variants.
+ - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
+
+- Name: `debug_web_extension_cpm_messaging_recovered_after_reload` / `debug_web_extension_cpm_messaging_recovered_after_reload_macos`
+ - Triggering Condition: Fires when CPM successfully reports status after the app reloaded the embedded WebExtension in response to a suspected stuck-messaging episode.
+ - Frequency: Once per detected health episode.
+ - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
+
+- Name: `debug_web_extension_cpm_messaging_reload_failed` / `debug_web_extension_cpm_messaging_reload_failed_macos`
+ - Triggering Condition: Fires when the app reloaded the embedded WebExtension in response to a suspected stuck-messaging episode, but a subsequent eligible completed navigation still did not receive CPM initialization within the grace period.
+ - Frequency: Once per detected health episode.
+ - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
+
+No URL, hostname, search query, tab or window identifier, navigation or document identifier, extension identifier, CPM rule, error text, timestamp, latency, queue size, or user-provided value is included in any pixel. Navigation and cross-tab observations are correlated only in memory to decide which event to fire.
+
+Optionally, provide a link to a task with background info:
+https://app.asana.com/1/137249556945/project/1163321984198618/task/1216761517055116?focus=true
+
+Pixel implementation task:
+https://app.asana.com/1/137249556945/task/1217907325352247
+
+Do the proposed pixels and their parameters use transparent naming?:
+Yes. Each name describes the observed health transition and distinguishes a single-page initialization miss from a suspected global messaging failure and its reload outcome.
+
+Do your proposed pixels share parameters (including default ones) with other pixels?:
+Yes.
+
+Explain whether you believe there is a risk of these parameters linking together multiple pixels from the same user:
+Shared parameters: `appVersion`; on macOS, the standard `pixelSource` and `channel` parameters; on iOS, the standard platform and form-factor suffixes.
+
+No new correlation identifier is introduced. The events cannot be joined into a per-user sequence using any parameter added by this work. Episode state is maintained only in app memory and is not transmitted.
+
+Do your proposed pixels include a search query or a website URL?:
+No. They do not include a URL, hostname, page title, search query, or any derived representation of browsing content.
+
+Will the proposed pixels be temporary?:
+Mixed:
+
+- `cpm_page_initialization_missing` and `cpm_page_initialization_recovered` are temporary diagnostic pixels. Remove them after the root cause and transient-failure rate have been established, no later than 2026-12-31.
+- `cpm_messaging_suspected_stuck`, `cpm_messaging_recovered_after_reload`, and `cpm_messaging_reload_failed` are permanent watchdog health pixels.
+
+Why do the pixels need to be permanent?:
+The permanent pixels measure whether the self-healing watchdog is activating, whether extension reload restores CPM, and whether users remain affected after recovery. This is needed to detect regressions in WebKit or the embedded extension messaging layer and to verify that the mitigation continues to work across OS and app releases.
+
+Do your pixels include any parameters or suffixes that do not meet the below criteria?:
+No. Only existing default parameters and standard platform suffixes are used.
+
+Does your pixel fire on any of the below events?:
+No. The pixels fire on aggregate CPM/WebExtension health-state transitions, not on user-entered data, specific browsing destinations, searches, purchases, authentication, or sensitive actions.
+
+Would it be possible to tie multiple occurrences of your proposed pixels as belonging to the same user or small groups of users?:
+No. There is no episode ID, tab ID, document ID, extension ID, URL-derived value, timestamp, or other correlation parameter. Daily suppression and in-memory once-per-episode guards reduce repeated firing without transmitting linkable state.
+
+Did you answer "No" to all 3 self-service qualifiers above? In other words, do your pixels meet all the self-service criteria?:
+Yes.
+
+---
+
+This content is prepared for the **Privacy Triage - Frontend Pixels** form:
+https://form.asana.com/?k=q16Ao4fteEgxU3uzMp6Avw&d=137249556945
From 5f03d3ee118897636b20535a6c8b46d94403a48e Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 18:01:01 +0600
Subject: [PATCH 38/81] [2026-08-31] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
...-extension-health-pixels-privacy-triage.md | 109 ++++++------------
1 file changed, 35 insertions(+), 74 deletions(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index 3918bd46..6e9234dd 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -1,94 +1,55 @@
-# Privacy Triage: Apple - CPM Embedded WebExtension Health Pixels
+# Privacy Triage: Apple - CPM Extension Health Pixels
Status: **DRAFT**
-Name:
-Alex M
+**Name:** Alex M
+**Email:** amartemyanov@duckduckgo.com
+**Objective:** O-E
+**PR:** [Add PR link]
+**Project:** https://app.asana.com/1/137249556945/project/1163321984198618/task/1216761517055116?focus=true
-Email address:
-amartemyanov@duckduckgo.com
+## Pixels
-Name the triage:
-Privacy Triage: Apple - CPM embedded WebExtension health pixels
+| Pixel | When it fires |
+|---|---|
+| `debug_web_extension_cpm_page_initialization_missing` | A finished HTTP(S) page load does not receive the initial CPM status. |
+| `debug_web_extension_cpm_page_initialization_recovered` | CPM works on the next page load or reload without reloading the extension. |
+| `debug_web_extension_cpm_messaging_suspected_stuck` | CPM fails repeatedly, including after a page reload and in another tab. |
+| `debug_web_extension_cpm_messaging_recovered_after_reload` | CPM starts working after the app reloads the extension. |
+| `debug_web_extension_cpm_messaging_reload_failed` | CPM still does not work after the app reloads the extension. |
-Objective:
-O-E
+macOS pixels use the same names with the `_macos` suffix. iOS uses the standard platform and form-factor suffixes.
-List all pixels that need to be collected or modified:
+The pixels send only the standard parameters: `appVersion`, plus `pixelSource` and `channel` on macOS.
-PR: [Add PR link]
+## Privacy Questions
-The iOS names below receive the standard platform and form-factor suffixes. The corresponding macOS names end in `_macos`.
-
-- Name: `debug_web_extension_cpm_page_initialization_missing` / `debug_web_extension_cpm_page_initialization_missing_macos`
- - Triggering Condition: Fires after an eligible main-frame HTTP(S) navigation finishes, a grace period expires, the embedded CPM WebExtension context is loaded, and no initial CPM status was received for the current document. This records a page-level initialization miss only; it does not classify the extension as globally stuck.
- - Frequency: Daily and standard.
- - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
-
-- Name: `debug_web_extension_cpm_page_initialization_recovered` / `debug_web_extension_cpm_page_initialization_recovered_macos`
- - Triggering Condition: Fires when CPM successfully initializes on a subsequent completed navigation or page reload after a page-level initialization miss, without reloading the embedded WebExtension. This distinguishes a transient page-load race from persistent extension messaging failure.
- - Frequency: Once per detected health episode.
- - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
-
-- Name: `debug_web_extension_cpm_messaging_suspected_stuck` / `debug_web_extension_cpm_messaging_suspected_stuck_macos`
- - Triggering Condition: Fires once when the native cross-tab health monitor observes repeated CPM initialization misses across independent completed navigation attempts, including an ordinary page reload and another tab, with no successful CPM status received globally between the attempts. The embedded WebExtension context must remain loaded and must not report a background-content load error. This is the signal that initiates the self-healing extension reload.
- - Frequency: Once per detected health episode, with daily and standard variants.
- - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
-
-- Name: `debug_web_extension_cpm_messaging_recovered_after_reload` / `debug_web_extension_cpm_messaging_recovered_after_reload_macos`
- - Triggering Condition: Fires when CPM successfully reports status after the app reloaded the embedded WebExtension in response to a suspected stuck-messaging episode.
- - Frequency: Once per detected health episode.
- - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
-
-- Name: `debug_web_extension_cpm_messaging_reload_failed` / `debug_web_extension_cpm_messaging_reload_failed_macos`
- - Triggering Condition: Fires when the app reloaded the embedded WebExtension in response to a suspected stuck-messaging episode, but a subsequent eligible completed navigation still did not receive CPM initialization within the grace period.
- - Frequency: Once per detected health episode.
- - Parameters: Default parameters only (`appVersion`; macOS also includes the standard `pixelSource` and `channel` parameters).
-
-No URL, hostname, search query, tab or window identifier, navigation or document identifier, extension identifier, CPM rule, error text, timestamp, latency, queue size, or user-provided value is included in any pixel. Navigation and cross-tab observations are correlated only in memory to decide which event to fire.
-
-Optionally, provide a link to a task with background info:
-https://app.asana.com/1/137249556945/project/1163321984198618/task/1216761517055116?focus=true
-
-Pixel implementation task:
-https://app.asana.com/1/137249556945/task/1217907325352247
-
-Do the proposed pixels and their parameters use transparent naming?:
-Yes. Each name describes the observed health transition and distinguishes a single-page initialization miss from a suspected global messaging failure and its reload outcome.
-
-Do your proposed pixels share parameters (including default ones) with other pixels?:
+**Do the pixels use transparent names?**
Yes.
-Explain whether you believe there is a risk of these parameters linking together multiple pixels from the same user:
-Shared parameters: `appVersion`; on macOS, the standard `pixelSource` and `channel` parameters; on iOS, the standard platform and form-factor suffixes.
+**Do they share parameters with other pixels?**
+Yes. They use only existing standard parameters.
-No new correlation identifier is introduced. The events cannot be joined into a per-user sequence using any parameter added by this work. Episode state is maintained only in app memory and is not transmitted.
+**Could the parameters link pixels to the same user?**
+No. No identifier is added.
-Do your proposed pixels include a search query or a website URL?:
-No. They do not include a URL, hostname, page title, search query, or any derived representation of browsing content.
+**Do the pixels include a URL or search query?**
+No. They also do not include hostnames, page titles, tab IDs, document IDs, error text, or CPM rules.
-Will the proposed pixels be temporary?:
-Mixed:
+**Are the pixels temporary?**
+The two page-initialization pixels are temporary diagnostics. The three stuck-state and reload-result pixels are permanent health monitoring.
-- `cpm_page_initialization_missing` and `cpm_page_initialization_recovered` are temporary diagnostic pixels. Remove them after the root cause and transient-failure rate have been established, no later than 2026-12-31.
-- `cpm_messaging_suspected_stuck`, `cpm_messaging_recovered_after_reload`, and `cpm_messaging_reload_failed` are permanent watchdog health pixels.
+**Why are the permanent pixels needed?**
+To measure how often CPM messaging becomes stuck and whether reloading the extension fixes it.
-Why do the pixels need to be permanent?:
-The permanent pixels measure whether the self-healing watchdog is activating, whether extension reload restores CPM, and whether users remain affected after recovery. This is needed to detect regressions in WebKit or the embedded extension messaging layer and to verify that the mitigation continues to work across OS and app releases.
+**Do any parameters or suffixes fall outside the standard criteria?**
+No.
-Do your pixels include any parameters or suffixes that do not meet the below criteria?:
-No. Only existing default parameters and standard platform suffixes are used.
+**Do the pixels fire on sensitive user events?**
+No. They report only CPM extension health states.
-Does your pixel fire on any of the below events?:
-No. The pixels fire on aggregate CPM/WebExtension health-state transitions, not on user-entered data, specific browsing destinations, searches, purchases, authentication, or sensitive actions.
+**Can multiple occurrences be tied to the same user or a small group?**
+No. The pixels contain no correlation identifier or browsing data.
-Would it be possible to tie multiple occurrences of your proposed pixels as belonging to the same user or small groups of users?:
-No. There is no episode ID, tab ID, document ID, extension ID, URL-derived value, timestamp, or other correlation parameter. Daily suppression and in-memory once-per-episode guards reduce repeated firing without transmitting linkable state.
-
-Did you answer "No" to all 3 self-service qualifiers above? In other words, do your pixels meet all the self-service criteria?:
+**Does this meet the self-service criteria?**
Yes.
-
----
-
-This content is prepared for the **Privacy Triage - Frontend Pixels** form:
-https://form.asana.com/?k=q16Ao4fteEgxU3uzMp6Avw&d=137249556945
From 8e3a358aaa227519779451bf10b277f7da57fb98 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 18:06:03 +0600
Subject: [PATCH 39/81] [2026-08-31] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
.../cpm-extension-health-pixels-privacy-triage.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index 6e9234dd..b87bcb48 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -12,13 +12,13 @@ Status: **DRAFT**
| Pixel | When it fires |
|---|---|
-| `debug_web_extension_cpm_page_initialization_missing` | A finished HTTP(S) page load does not receive the initial CPM status. |
-| `debug_web_extension_cpm_page_initialization_recovered` | CPM works on the next page load or reload without reloading the extension. |
-| `debug_web_extension_cpm_messaging_suspected_stuck` | CPM fails repeatedly, including after a page reload and in another tab. |
-| `debug_web_extension_cpm_messaging_recovered_after_reload` | CPM starts working after the app reloads the extension. |
-| `debug_web_extension_cpm_messaging_reload_failed` | CPM still does not work after the app reloads the extension. |
+| `m_debug_web_extension_cpm_page_initialization_missing` | A finished HTTP(S) page load does not receive the initial CPM status. |
+| `m_debug_web_extension_cpm_page_initialization_recovered` | CPM works on the next page load or reload without reloading the extension. |
+| `m_debug_web_extension_cpm_messaging_suspected_stuck` | CPM fails repeatedly, including after a page reload and in another tab. |
+| `m_debug_web_extension_cpm_messaging_recovered_after_reload` | CPM starts working after the app reloads the extension. |
+| `m_debug_web_extension_cpm_messaging_reload_failed` | CPM still does not work after the app reloads the extension. |
-macOS pixels use the same names with the `_macos` suffix. iOS uses the standard platform and form-factor suffixes.
+macOS uses the same names with the `m_mac_debug_` prefix. iOS uses the standard platform and form-factor suffixes.
The pixels send only the standard parameters: `appVersion`, plus `pixelSource` and `channel` on macOS.
From d59bf19d43e6ff23060da88515d000e63d274a49 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 18:11:15 +0600
Subject: [PATCH 40/81] [2026-08-31] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
.../cpm-extension-health-pixels-privacy-triage.md | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index b87bcb48..fefbd09f 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -10,13 +10,13 @@ Status: **DRAFT**
## Pixels
-| Pixel | When it fires |
-|---|---|
-| `m_debug_web_extension_cpm_page_initialization_missing` | A finished HTTP(S) page load does not receive the initial CPM status. |
-| `m_debug_web_extension_cpm_page_initialization_recovered` | CPM works on the next page load or reload without reloading the extension. |
-| `m_debug_web_extension_cpm_messaging_suspected_stuck` | CPM fails repeatedly, including after a page reload and in another tab. |
-| `m_debug_web_extension_cpm_messaging_recovered_after_reload` | CPM starts working after the app reloads the extension. |
-| `m_debug_web_extension_cpm_messaging_reload_failed` | CPM still does not work after the app reloads the extension. |
+| Pixel | When it fires |
+| ------------------------------------------------------------ | -------------------------------------------------------------------------- |
+| `m_debug_web_extension_cpm_page_initialization_missing` | A finished HTTP(S) page load does not receive the initial CPM status. |
+| `m_debug_web_extension_cpm_page_initialization_recovered` | CPM works on the next page load or reload without reloading the extension. |
+| `m_debug_web_extension_cpm_messaging_suspected_stuck` | CPM fails repeatedly, including after a page reload and in another tab. |
+| `m_debug_web_extension_cpm_messaging_recovered_after_reload` | CPM starts working after the app reloads the extension. |
+| `m_debug_web_extension_cpm_messaging_reload_failed` | CPM still does not work after the app reloads the extension. |
macOS uses the same names with the `m_mac_debug_` prefix. iOS uses the standard platform and form-factor suffixes.
From 715e3e0731514dd67a7d4bf21805336ff23fca02 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 18:26:21 +0600
Subject: [PATCH 41/81] [2026-08-31] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
...-extension-health-pixels-privacy-triage.md | 38 ++++++++++++-------
1 file changed, 24 insertions(+), 14 deletions(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index fefbd09f..1faab8d1 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -10,17 +10,27 @@ Status: **DRAFT**
## Pixels
-| Pixel | When it fires |
-| ------------------------------------------------------------ | -------------------------------------------------------------------------- |
-| `m_debug_web_extension_cpm_page_initialization_missing` | A finished HTTP(S) page load does not receive the initial CPM status. |
-| `m_debug_web_extension_cpm_page_initialization_recovered` | CPM works on the next page load or reload without reloading the extension. |
-| `m_debug_web_extension_cpm_messaging_suspected_stuck` | CPM fails repeatedly, including after a page reload and in another tab. |
-| `m_debug_web_extension_cpm_messaging_recovered_after_reload` | CPM starts working after the app reloads the extension. |
-| `m_debug_web_extension_cpm_messaging_reload_failed` | CPM still does not work after the app reloads the extension. |
+| iOS pixel | Trigger | Frequency |
+|---|---|---|
+| `m_debug_web_extension_cpm_initialization_missed_after_session_restore` | CPM does not initialize during a restored-session navigation batch. | Daily |
+| `m_debug_web_extension_cpm_initialization_recovered_after_session_restore` | CPM initializes on the next independent navigation after the restored-session batch failed. | Daily |
+| `m_debug_web_extension_cpm_messaging_stuck` | CPM also fails on a later independent navigation, with no CPM success between attempts. | Daily + count, once per episode |
+| `m_debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A stuck episode recovers before the extension is reloaded. | Daily + count, once per episode |
+| `m_debug_web_extension_cpm_messaging_recovered_after_extension_reload` | A stuck episode recovers after the app reloads the extension. | Daily + count, once per episode |
+| `m_debug_web_extension_cpm_messaging_extension_reload_failed` | CPM still fails after the app reloads the extension. | Daily + count, once per episode |
-macOS uses the same names with the `m_mac_debug_` prefix. iOS uses the standard platform and form-factor suffixes.
+macOS uses the same names with the `m_mac_debug_` prefix. iOS adds the standard platform and form-factor suffixes.
-The pixels send only the standard parameters: `appVersion`, plus `pixelSource` and `channel` on macOS.
+Only standard parameters are sent: `appVersion`, plus `pixelSource` and `channel` on macOS.
+
+## Detection Rules
+
+- Check only finished, current, main-frame HTTP(S) document navigations after a grace period.
+- Treat all `.sessionRestoration` navigations from one app launch as one batch, not one failure per restored tab.
+- A redirected restoration still belongs to the restoration batch when `.sessionRestoration` appears in its navigation history.
+- After restoration, any independent eligible document navigation can be the next attempt: reload, typed URL, link, new-tab navigation, or a real back/forward load.
+- Redirects do not count separately. Same-document, failed, non-HTTP(S), and BFCache navigations do not count when CPM is not expected to initialize again.
+- Any successful CPM initialization closes the episode. Pixels are fired for episode transitions, never for every failed tab.
## Privacy Questions
@@ -34,13 +44,13 @@ Yes. They use only existing standard parameters.
No. No identifier is added.
**Do the pixels include a URL or search query?**
-No. They also do not include hostnames, page titles, tab IDs, document IDs, error text, or CPM rules.
+No. They also exclude hostnames, page titles, tab IDs, document IDs, navigation types, errors, and CPM rules.
**Are the pixels temporary?**
-The two page-initialization pixels are temporary diagnostics. The three stuck-state and reload-result pixels are permanent health monitoring.
+The two session-restoration pixels are temporary diagnostics. The stuck-state and recovery pixels are permanent health monitoring.
**Why are the permanent pixels needed?**
-To measure how often CPM messaging becomes stuck and whether reloading the extension fixes it.
+To measure how often CPM messaging becomes stuck and whether extension reload recovers it.
**Do any parameters or suffixes fall outside the standard criteria?**
No.
@@ -48,8 +58,8 @@ No.
**Do the pixels fire on sensitive user events?**
No. They report only CPM extension health states.
-**Can multiple occurrences be tied to the same user or a small group?**
-No. The pixels contain no correlation identifier or browsing data.
+**Can occurrences be tied to the same user or a small group?**
+No. There is no correlation identifier or browsing data.
**Does this meet the self-service criteria?**
Yes.
From 00616f210b9fa445fe0bf7919043182e746e0c75 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 19:16:43 +0600
Subject: [PATCH 42/81] [2026-08-31] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
...cpm-extension-health-pixels-privacy-triage.md | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index 1faab8d1..8457f4c7 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -10,14 +10,14 @@ Status: **DRAFT**
## Pixels
-| iOS pixel | Trigger | Frequency |
-|---|---|---|
-| `m_debug_web_extension_cpm_initialization_missed_after_session_restore` | CPM does not initialize during a restored-session navigation batch. | Daily |
-| `m_debug_web_extension_cpm_initialization_recovered_after_session_restore` | CPM initializes on the next independent navigation after the restored-session batch failed. | Daily |
-| `m_debug_web_extension_cpm_messaging_stuck` | CPM also fails on a later independent navigation, with no CPM success between attempts. | Daily + count, once per episode |
-| `m_debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A stuck episode recovers before the extension is reloaded. | Daily + count, once per episode |
-| `m_debug_web_extension_cpm_messaging_recovered_after_extension_reload` | A stuck episode recovers after the app reloads the extension. | Daily + count, once per episode |
-| `m_debug_web_extension_cpm_messaging_extension_reload_failed` | CPM still fails after the app reloads the extension. | Daily + count, once per episode |
+| iOS pixel | Trigger | Frequency |
+| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------- |
+| `m_debug_web_extension_cpm_initialization_missed_after_session_restore` | CPM does not initialize during a restored-session navigation batch. | Daily |
+| `m_debug_web_extension_cpm_initialization_recovered_after_session_restore` | CPM initializes on the next independent navigation after the restored-session batch failed. | Daily |
+| `m_debug_web_extension_cpm_messaging_stuck` | CPM also fails on a later independent navigation, with no CPM success between attempts. | Daily + count, once per episode |
+| `m_debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A stuck episode recovers before the extension is reloaded. | Daily + count, once per episode |
+| `m_debug_web_extension_cpm_messaging_recovered_after_extension_reload` | A stuck episode recovers after the app reloads the extension. | Daily + count, once per episode |
+| `m_debug_web_extension_cpm_messaging_extension_reload_failed` | CPM still fails after the app reloads the extension. | Daily + count, once per episode |
macOS uses the same names with the `m_mac_debug_` prefix. iOS adds the standard platform and form-factor suffixes.
From 0b6a93491f770cd3737332c2d3cde86d38375e1b Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Mon, 31 Aug 2026 19:26:47 +0600
Subject: [PATCH 43/81] [2026-08-31] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
work/projects/cpm-extension-health-pixels-privacy-triage.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index 8457f4c7..bb248557 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -12,7 +12,7 @@ Status: **DRAFT**
| iOS pixel | Trigger | Frequency |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------- |
-| `m_debug_web_extension_cpm_initialization_missed_after_session_restore` | CPM does not initialize during a restored-session navigation batch. | Daily |
+| `m_debug_web_extension_cpm_initialization_failed_after_session_restore` | CPM does not initialize during a restored-session navigation batch. | Daily |
| `m_debug_web_extension_cpm_initialization_recovered_after_session_restore` | CPM initializes on the next independent navigation after the restored-session batch failed. | Daily |
| `m_debug_web_extension_cpm_messaging_stuck` | CPM also fails on a later independent navigation, with no CPM success between attempts. | Daily + count, once per episode |
| `m_debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A stuck episode recovers before the extension is reloaded. | Daily + count, once per episode |
From 1015f668adcd0764685c359153bbde22e1e5bf8c Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 09:46:17 +0600
Subject: [PATCH 44/81] [2026-09-01] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
...-extension-health-pixels-privacy-triage.md | 36 +++++++++++--------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index bb248557..b7a81081 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -10,18 +10,21 @@ Status: **DRAFT**
## Pixels
-| iOS pixel | Trigger | Frequency |
-| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------- |
-| `m_debug_web_extension_cpm_initialization_failed_after_session_restore` | CPM does not initialize during a restored-session navigation batch. | Daily |
-| `m_debug_web_extension_cpm_initialization_recovered_after_session_restore` | CPM initializes on the next independent navigation after the restored-session batch failed. | Daily |
-| `m_debug_web_extension_cpm_messaging_stuck` | CPM also fails on a later independent navigation, with no CPM success between attempts. | Daily + count, once per episode |
-| `m_debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A stuck episode recovers before the extension is reloaded. | Daily + count, once per episode |
-| `m_debug_web_extension_cpm_messaging_recovered_after_extension_reload` | A stuck episode recovers after the app reloads the extension. | Daily + count, once per episode |
-| `m_debug_web_extension_cpm_messaging_extension_reload_failed` | CPM still fails after the app reloads the extension. | Daily + count, once per episode |
+| Pixel | Trigger | Frequency |
+| -------------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------- |
+| `m_debug_web_extension_cpm_initialization_missed_after_session_restore` | Restored tabs do not receive the initial CPM response. | Daily |
+| `m_debug_web_extension_cpm_initialization_recovered_after_session_restore` | CPM later responds after the restoration miss. | Daily |
+| `m_debug_web_extension_cpm_initialization_failed_after_tab_crash` | CPM fails on the first eligible navigation after a tab crash. | Daily + count |
+| `m_debug_web_extension_cpm_initialization_failed_after_extension_reload` | CPM fails on the first eligible navigation after an extension reload. | Daily + count |
+| `m_debug_web_extension_cpm_messaging_stuck_session_restoration` | Messaging remains stuck after a restoration-originated failure. | Daily + count, once/episode |
+| `m_debug_web_extension_cpm_messaging_stuck_tab_crash` | Messaging remains stuck across attempts associated with a tab crash. | Daily + count, once/episode |
+| `m_debug_web_extension_cpm_messaging_stuck_other` | Messaging remains stuck across other independent navigations. | Daily + count, once/episode |
+| `m_debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A stuck episode recovers without an extension reload. | Daily + count, once/episode |
+| `m_debug_web_extension_cpm_messaging_recovered_after_extension_reload` | A stuck episode recovers after an extension reload. | Daily + count, once/episode |
+| `m_debug_web_extension_cpm_messaging_extension_reload_failed` | Reload fails or CPM remains stuck after reload. | Daily + count, once/episode |
-macOS uses the same names with the `m_mac_debug_` prefix. iOS adds the standard platform and form-factor suffixes.
-
-Only standard parameters are sent: `appVersion`, plus `pixelSource` and `channel` on macOS.
+iOS and macOS send the exact same pixel names. Parameters distinguish `platform=ios|macos` and `form_factor=phone|tablet|desktop`.
+`appVersion` is included; macOS also includes `pixelSource` and `channel`.
## Detection Rules
@@ -30,6 +33,9 @@ Only standard parameters are sent: `appVersion`, plus `pixelSource` and `channel
- A redirected restoration still belongs to the restoration batch when `.sessionRestoration` appears in its navigation history.
- After restoration, any independent eligible document navigation can be the next attempt: reload, typed URL, link, new-tab navigation, or a real back/forward load.
- Redirects do not count separately. Same-document, failed, non-HTTP(S), and BFCache navigations do not count when CPM is not expected to initialize again.
+- A tab crash marks only the next eligible navigation in that tab.
+- A successful extension reload marks only the next eligible CPM attempt.
+- `stuck` retains the strongest contributing reason: tab crash, then session restoration, then other.
- Any successful CPM initialization closes the episode. Pixels are fired for episode transitions, never for every failed tab.
## Privacy Questions
@@ -38,22 +44,22 @@ Only standard parameters are sent: `appVersion`, plus `pixelSource` and `channel
Yes.
**Do they share parameters with other pixels?**
-Yes. They use only existing standard parameters.
+Yes. They use `platform` and `form_factor` to combine both apps under the same pixel names.
**Could the parameters link pixels to the same user?**
-No. No identifier is added.
+No. Both parameters are coarse enums and contain no identifier.
**Do the pixels include a URL or search query?**
No. They also exclude hostnames, page titles, tab IDs, document IDs, navigation types, errors, and CPM rules.
**Are the pixels temporary?**
-The two session-restoration pixels are temporary diagnostics. The stuck-state and recovery pixels are permanent health monitoring.
+The session-restoration and post-crash/reload pixels are diagnostics. The stuck-state and recovery pixels are permanent health monitoring.
**Why are the permanent pixels needed?**
To measure how often CPM messaging becomes stuck and whether extension reload recovers it.
**Do any parameters or suffixes fall outside the standard criteria?**
-No.
+`platform` and `form_factor` are explicit enum parameters used instead of platform-specific pixel names.
**Do the pixels fire on sensitive user events?**
No. They report only CPM extension health states.
From fa362e78d1c11810717475eaaf1b741909a671f0 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 10:41:36 +0600
Subject: [PATCH 45/81] [2026-09-01] eagle: family/how-to/kraken-access.md
family/how-to/truenas-infrastructure.md family/how-to/vps-qentra.md
family/how-to/wireguard-vpn.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/how-to/kraken-access.md | 6 +-
family/how-to/truenas-infrastructure.md | 9 +-
family/how-to/vps-qentra.md | 16 ++-
family/how-to/wireguard-vpn.md | 7 +-
.../xray-reverse-tunnel-kraken-truenas.md | 101 ++++++++++++++++++
5 files changed, 131 insertions(+), 8 deletions(-)
create mode 100644 personal/tech/xray-reverse-tunnel-kraken-truenas.md
diff --git a/family/how-to/kraken-access.md b/family/how-to/kraken-access.md
index 2c897695..ee0e5f88 100644
--- a/family/how-to/kraken-access.md
+++ b/family/how-to/kraken-access.md
@@ -1,15 +1,15 @@
# Kraken — Внешний доступ
-> Обновлено: 2026-06-24
+> Обновлено: 2026-09-01
## Как зайти
Везде `ssh kraken`. Резолвится через `~/.ssh/config` на Eagle:
- **Дома** — напрямую по LAN (`192.168.1.15`)
-- **Снаружи** — через WireGuard (`10.99.1.2`, VPN поднимается автоматически `wg-auto.sh`)
+- **Снаружи** — ⚠️ через WireGuard (`10.99.1.2`) **БОЛЬШЕ НЕ РАБОТАЕТ**: VPS-узло (10.99.0.1/10.99.1.1) qentra.top **удалили 2026-09-01**. Пока нет альтернативы внешнему доступу к Kraken (см. [[tech/xray-reverse-tunnel-kraken-truenas]] — план).
-WireGuard split-tunnel: Eagle (10.99.0.2) ↔ VPS ↔ Kraken (10.99.1.2). Подробнее: [[wireguard-vpn]].
+WireGuard split-tunnel: Eagle (10.99.0.2) ↔ VPS ↔ Kraken (10.99.1.2). Подробнее: [[wireguard-vpn]]. **⚠️ VPS-звено мертво на 2026-09-01.**
## Portainer (локально)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 04739411..7e098a65 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -1,6 +1,13 @@
# TrueNAS — инфраструктура
-> Обновлено: 2026-08-31 (cups-splix mDNS-фикс завершён; docker поднят)
+> Обновлено: 2026-09-01 (VPS qentra удалён — vless-proxy outbound мёртв; 443=Caddy, не Xray)
+
+> ## ⚠️ ПРИОРИТЕТ 2026-09-01: VPS qentra.top УДАЛЁН → vless-proxy и сетевые туннели сломаны
+> - **VPS `91.207.28.205` больше НЕ существует** (вся его Xray/OpenVPN/WG-инфраструктура утрачена). Подробно: [[family/how-to/vps-qentra]].
+> - **Контейнер `vless-proxy`** (teddysun/xray): inbound SOCKS `0.0.0.0:1080` + HTTP `0.0.0.0:1081`, но **outbound ⇒ мёртвый `v.qentra.top:443`** (VLESS+WS+TLS, path `/qentra`, id `2D9F24C4-21FE-4784-9843-F11C384DA67A`). ⇒ Hermes-Taiga (ходит через `vless-proxy:1080`) **сейчас БЕЗ рабочего прокси**.
+> - **⚠️ На `mallexxx.duckdns.org:443` НЕ Xray**, а обычный **Caddy/nginx с валидным Let's Encrypt сертификатом** для `mallexxx.duckdns.org` (проверено `openssl s_client` 2026-09-01). Память «на 443 висел Xray server» — не подтвердилась.
+> - **Наружу открыты только 22 (SSH) и 443 (HTTPS/TLS)** с Kraken; `8443/8964/8080/9000` closed.
+> - **План замены** (выход клиентов сети TrueNAS через Kraken, Xray reverse — НЕ внедрён): [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
> ## ✅ СТАТУС на 2026-08-25: пул ONLINE, docker поднят, почти все контейнеры Up
> **Пул RED_2TB пересоздан (2026-08-21) и работает:** `zpool status` ONLINE, rsync возврата данных завершён, `/mnt/RED_2TB/` смонтирован.
diff --git a/family/how-to/vps-qentra.md b/family/how-to/vps-qentra.md
index 4bb2e18c..76f460f1 100644
--- a/family/how-to/vps-qentra.md
+++ b/family/how-to/vps-qentra.md
@@ -1,20 +1,30 @@
---
title: VPS qentra.top
created: '2026-05-24'
-updated: '2026-05-27'
+updated: '2026-09-01'
type: tech
namespace: personal
tags: [infra, vps]
confidence: medium
+status: retired
related:
+ - "[[tech/xray-reverse-tunnel-kraken-truenas]]"
- "[[tech/kraken-network]]"
- "[[tech/wireguard-vpn]]"
---
# VPS qentra.top
-**IP:** 91.207.28.205
-**Stack:** nginx + Python 3.11, Cloudflare proxy
+> ## ⛔ УДАЛЁН на 2026-09-01
+> **VPS `91.207.28.205` (qentra.top) больше НЕ существует.** Вся инфраструктура на нём (nginx, Xray/VLESS+REALITY, x-ui panel, OpenVPN, WebSocket v.qentra.top, backup-скрипты, nolvu/panel vhosts) — утрачена вместе с сервером.
+> **Следствия:**
+> - На TrueNAS контейнер `vless-proxy` (teddysun/xray) outbound указывает на `v.qentra.top:443` → **сейчас мёртв**. Hermes-Taiga (SOCKS5 через `vless-proxy:1080`) **без рабочего прокси**.
+> - WireGuard Eagle↔VPS↔Kraken: VPS-звено мёртво.
+> - Rasputin-роутер VLESS-туннель (`188.239.191.235` / node3.sysnx.net) — это ДРУГОЙ сервер (не qentra), не трогать.
+> - Замена для выхода клиентов сети TrueNAS → Kraken: см. **[[tech/xray-reverse-tunnel-kraken-truenas]]** (план Xray reverse, НЕ внедрён).
+
+**IP:** 91.207.28.205 *(недоступен с 2026-09-01)*
+**Stack:** nginx + Python 3.11, Cloudflare proxy *(исторические данные ниже)*
**Panels:** https://panel.qentra.top (x-ui, проксируется nginx на 8443 → 5430), v.qentra.top:8964
## Subdomain Setup
diff --git a/family/how-to/wireguard-vpn.md b/family/how-to/wireguard-vpn.md
index 007417a7..a39a6493 100644
--- a/family/how-to/wireguard-vpn.md
+++ b/family/how-to/wireguard-vpn.md
@@ -1,7 +1,10 @@
# WireGuard VPN — Eagle ↔ Kraken
> Создано: 2026-05-14
-> Статус: ✅ работает
+> Статус: ⚠️ **СЛОМАНО на 2026-09-01** — VPS-узло (10.99.0.1/10.99.1.1) **удалён** вместе с qentra.top.
+> Вся hub-and-spoke топология (wg-quick@wg0/wg1, SNAT, dnsmasq-резолвинг `kraken`, nftables forward) жила на VPS и **утрачена**. `ssh kraken` с Eagle извне через WG больше не работает.
+> Альтернатива для внешнего доступа к Kraken на 2026-09-01: reverse-SSH туннель через VPS-заменитель не существует; локально дома — напрямую по LAN 192.168.1.15 (см. [[kraken-access]]). План замены сети: [[tech/xray-reverse-tunnel-kraken-truenas]].
+> ⚠️ **Дома Eagle подключается к Kraken напрямую по LAN** (wg-auto.sh детектит домашний роутер и делает `wg-quick down`) — это направление работает. Проблема только во внешнем (не-дома) подключении.
## Топология
@@ -10,6 +13,8 @@ Eagle (10.99.0.2) ←→ wg0 VPS (10.99.0.1) ←→ wg1 VPS (10.99.1.1) ←→ K
:51820 :51821
```
+> ⚠️ Топология выше — ИСТОРИЧЕСКАЯ, VPS-звено мертво.
+
Два интерфейса на VPS чтобы избежать hairpin forwarding. FORWARD идёт wg0→wg1, SNAT меняет src Eagle на `10.99.1.1`.
---
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
new file mode 100644
index 00000000..1c5fbd98
--- /dev/null
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -0,0 +1,101 @@
+---
+title: Xray Reverse Tunnel — Kraken ↔ TrueNAS
+created: '2026-09-01'
+updated: '2026-09-01'
+type: tech
+namespace: personal
+tags: [xray, reverse, kraken, truenas, tunnel, networking]
+confidence: medium
+related:
+ - "[[family/how-to/vps-qentra]]"
+ - "[[family/how-to/truenas-infrastructure]]"
+ - "[[family/how-to/kraken-access]]"
+ - "[[family/how-to/rasputin-router]]"
+---
+
+# Xray Reverse Tunnel — Kraken ↔ TrueNAS
+
+> **Статус: ПЛАН (согласовано направление с Alex, НЕ внедрено).** 2026-09-01.
+> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
+
+## Контекст / Почему
+
+- **VPS qentra.top (91.207.28.205) УДАЛЁН** — вся прежняя Xray-инфраструктура на нём (VLESS+REALITY, OpenVPN, WebSocket v.qentra.top) больше не существует.
+- Нужен новый путь для выхода локальных клиентов сети TrueNAS в интернет через другую точку выхода (Kraken).
+- Белый IP есть только у **TrueNAS** (`mallexxx.duckdns.org` = `90.189.160.148`).
+- **Kraken за NAT**, входящие не принимает — только исходящие соединения.
+
+## Принятая схема
+
+```
+Локальные клиенты (сеть TrueNAS, 192.168.2.x)
+ │ шлют трафик на локальный прокси TrueNAS (SOCKS 1080 / HTTP 1081)
+ ▼
+TrueNAS ←── контроллер / bridge (белый IP, mallexxx.duckdns.org), принимает
+ │ TrueNAS заворачивает трафик в reverse-канал
+ ▼ ▲
+Kraken ──┘ держит ИСХОДЯЩИЙ reverse-канал к TrueNAS, отпускает трафик → интернет
+ ▼
+интернет
+```
+
+**Тип решения: Xray `reverse`.** Роли по Xray:
+
+| Узел | Роль | Держит канал? | Выход в интернет? |
+|------|------|---------------|-------------------|
+| **TrueNAS** | bridge/контроллер (inbound `reverse`) | ❌ слушает | ❌ |
+| **Kraken** | outbound reverse-нода + exit | ✅ **исходящий** к TrueNAS | ✅ **да** |
+| **Локальные клиенты** | используют TrueNAS как локальный прокси-шлюз | — | через Kraken |
+
+**Ключевое физическое ограничение:** Kraken за NAT не может принимать входящих. Поэтому канал держит **Kraken исходящим** к TrueNAS. Для передачи клиентского трафика TrueNAS → Kraken используются конструкции `dokodemo-door` + `reverse` + policy-маршрутизация (routing rules). Это чуть сложнее "поставить reverse и всё", конфиг нужен аккуратный.
+
+## ⚠️ Факты, проверенные 2026-09-01 (важно, ломают прежние допущения)
+
+1. **На 443 у TrueNAS НЕ Xray.** Проверено `openssl s_client`: на `mallexxx.duckdns.org:443` отвечает **обычный TLS 1.3 с валидным Let's Encrypt сертификатом для `mallexxx.duckdns.org`** (`issuer=Let's Encrypt/CN=YE2`). Это **Caddy/nginx reverse-proxy**, НЕ Xray REALITY (у REALITY сертификат был бы чужой/поддельный). → Твоя память «на 443 висел xray server» не подтверждается.
+2. **`vless-proxy` на TrueNAS** (контейнер `teddysun/xray:latest`, Up 7 дней):
+ - inbound: SOCKS `0.0.0.0:1080`, HTTP `0.0.0.0:1081` (локально в LAN TrueNAS)
+ - outbound: VLESS+WS+TLS на `v.qentra.top:443`, path `/qentra`, id `2D9F24C4-21FE-4784-9843-F11C384DA67A` — **указывает на УДАЛЁННЫЙ VPS → сейчас мёртвый**.
+ - ⚠️ Используется Hermes-Taiga как SOCKS5-прокси (`vless-proxy:1080`) → **Hermes-Taiga сейчас без рабочего прокси**.
+3. **Kraken→TrueNAS по `mallexxx.duckdns.org`:** открыты **только 22 (SSH) и 443 (HTTPS/TLS)**. `8443/8964/8080/9000` — closed. Порт 443 — TLS (Caddy), не сырой.
+4. **Kraken:** Docker установлен (`/usr/bin/docker`), но демон отвечает медленно/рвёт SSH (проверка `docker ps` не завершилась за 40s). Xray на Kraken **отсутствует** (`which xray` пусто) — нужен Docker-контейнер.
+5. **Связанность Kraken↔TrueNAS по 22/443 — подтверждена** (оба OPEN с Kraken).
+
+## План реализации (шаги; НЕ выполнены, ждут OK Alex)
+
+### Шаг 1 — TrueNAS: Xray reverse-server поверх Caddy
+- **Способ (рекомендован): Caddy TLS-pass-through.** Добавить в Caddy TCP-правило: поддомен `xray.mallexxx.duckdns.org:443` → внутренний порт Xray-контейнера (напр. `localhost:8443`). Caddy передаёт сырой TLS-стрим дальше. Xray на TrueNAS принимает VLESS+Reality/TLS. Kraken ходит на `xray.mallexxx.duckdns.org:443`.
+ - Плюс: переиспользуем уже открытый/проброшенный 443 на роутере, не трогаем роутер, туннель маскируется под HTTPS.
+ - Альтернатива: пробросить отдельный порт на роутере (напр. 8443) → требует доступа к роутеру. Менее предпочтительно.
+- Новый/доразвернутый Xray-контейнер:
+ - inbound `reverse`/`dokodemo-door` на 8443 (извне по pass-through)
+ - local inbound SOCKS/HTTP (уже есть 1080/1081)
+ - routing: трафик клиентов → в reverse-канал к Kraken
+
+### Шаг 2 — TrueNAS: бэкап перед изменением
+- Снять копию текущего конфига `vless-proxy` (config.json), в `/mnt/RED_2TB/docker//backup/` (паттерн как с cups-splix).
+
+### Шаг 3 — Kraken: Xray outbound reverse в Docker
+- Контейнер Xray на Kraken (`docker run --restart unless-stopped`, как остальные).
+- outbound vless → `xray.mallexxx.duckdns.org:443` (через Caddy pass-through).
+- inbound SOCKS/HTTP на Kraken — точка выхода.
+- reverse-конфиг: Kraken регистрирует «сервис» (весь трафик через dokodemo-door) наружу через канал к TrueNAS.
+
+### Шаг 4 — Проверка связности
+- Kraken → `xray.mallexxx.duckdns.org:443` (TCP).
+- Локальный клиент → TrueNAS:1080 → curl через прокси → должен выйти с IP Kraken.
+
+### Шаг 5 — Обновить Obsidian после внедрения
+- Дополнить `family/how-to/truenas-infrastructure.md` (Xray reverse-настройка), `family/how-to/kraken-access.md`; обновить `personal/tech/vps-qentra.md` (VPS удалён, ссылки на него).
+
+## Открытые вопросы (требуют ответа Alex)
+
+1. **Проброс порта на роутере возможен?** Или туннель обязателен ТОЛЬКО через 443 (Caddy TLS-pass-through)?
+2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на NAT/masquerade на Kraken.)
+3. **Docker-демон Kraken работает нестабильно** (рвёт SSH) — чинить перед деплоем?
+
+## Связанные заметки
+- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
+- [[family/how-to/truenas-infrastructure]] — TrueNAS Caddy/контейнеры
+- [[family/how-to/kraken-access]] — Docker Kraken
+- [[family/how-to/rasputin-router]] — прежний VLESS-туннель на VPS (тоже требует пересмотра, VPS нет)
+- [[family/how-to/wireguard-vpn]] — WG Eagle↔VPS↔Kraken (VPS-звено теперь мёртво)
From 08b6aa560ded743fcd4f53569caa1aa2784b6a57 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 11:36:55 +0600
Subject: [PATCH 46/81] [2026-09-01] eagle:
family/plans/reverse-xray-3xui-kraken.md
---
family/plans/reverse-xray-3xui-kraken.md | 91 ++++++++++++++++++++++++
1 file changed, 91 insertions(+)
create mode 100644 family/plans/reverse-xray-3xui-kraken.md
diff --git a/family/plans/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md
new file mode 100644
index 00000000..63313ea6
--- /dev/null
+++ b/family/plans/reverse-xray-3xui-kraken.md
@@ -0,0 +1,91 @@
+# Reverse Xray (3x-ui) — TrueNAS ↔ Kraken
+
+> Создано: 2026-09-01. Цель: проксировать трафик локальных клиентов (сеть TrueNAS 192.168.2.x) через TrueNAS → Kraken → интернет. **Kraken = точка выхода (exit node).** TrueNAS = bridge/контроллер.
+
+## Архитектура
+
+```text
+ЛОКАЛЬНЫЕ КЛИЕНТЫ (192.168.2.x)
+ │ подключаются к прокси TrueNAS:1080/1081 (SOCKS/HTTP) ИЛИ на поддомен
+ ▼
+TRUENAS = 3x-ui (Xray-сервер, bridge/контроллер) — белый IP, mallexxx.duckdns.org
+ │ принимает клиентский трафик; reverse-канал к Kraken
+ ▼ ▲
+KRAKEN = Xray-клиент (outbound reverse) + exit node — держит исходящий канал к TrueNAS
+ ▼
+интернет
+```
+
+- **TrueNAS** (3x-ui): принимает от локальных клиентов + держит reverse-вход, куда подключается Kraken.
+- **Kraken**: сам **инициирует** канал к TrueNAS (за NAT, не может принимать), получает по нему трафик клиентов, отпускает в интернет.
+- Кра́кен за NAT ⇒ направление канала **от Kraken к TrueNAS** (Xray reverse).
+
+## Ключевые факты (прояснены)
+
+1. **На 443 у TrueNAS — Caddy** (reverso-proxy, валидные LE-серты), НЕ Xray REALITY. Хостовый маппинг: `0.0.0.0:8443→443` (контейнер), `2019` (admin), `8088→80`.
+2. **Xray на TrueNAS** раньше был задуман как 3x-ui:
+ - База `/mnt/RED_2TB/docker/xray-admin/x-ui.db` (3x-ui), inbound `vless-ws` port `10095`, tag `in-10095-tcp`, path `/vless`, host `vpn.mallexxx.duckdns.org`, protocol vless.
+ - Композ-файла в `xray-admin/` нет; контейнер НЕ запущен.
+3. **Caddyfile** уже проксирует (мёртвые ссылки на `xray-admin`):
+ - `vpn.mallexxx.duckdns.org` → `@ws path /vless` → `xray-admin:10095`
+ - `vpn-panel.mallexxx.duckdns.org` → `xray-admin:443` / `xray-admin:2053` (path /sub/*)
+ - Сертификаты для этих поддоменов уже выданы (DNS на TrueNAS IP).
+4. **`vless-proxy`** (teddysun/xray) на TrueNAS — КЛИЕНТ: слушает 1080/1081 (SOCKS/HTTP), outbound на мёртвый `v.qentra.top:443` (VPS удалён). Используется Hermes-Taiga.
+5. **VPS qentra.top (91.207.28.205) УДАЛЁН.** Вся старая VLESS+REALITY инфраструктура на нём недоступна.
+6. Открытые порты TrueNAS наружу: **22, 443** (8443 ведёт внутрь контейнера Caddy:443 → но Caddy обрабатывает HTTPS-домены).
+
+## Образ 3x-ui
+
+- Официальный: `ghcr.io/mhsanaei/3x-ui:latest`
+- Контейнер должен называться **`xray-admin`** (как в Caddyfile) и быть в сети **`caddy_default`** (чтобы Caddy резолвил имя).
+- Volume: `/mnt/RED_2TB/docker/xray-admin:/etc/x-ui` (там лежит готовая `x-ui.db`).
+- Порт панели: 54321 (web UI, через Caddy поддомен `vpn-panel.mallexxx.duckdns.org`).
+- VLESS inbound 10095 принимается извне через Caddy `vpn.mallexxx.duckdns.org/vless` (WS), НО для reverse к Kraken нужен подход через панель.
+
+## Композ-файл
+
+Файл: `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml`
+
+```yaml
+services:
+ xray-admin:
+ image: ghcr.io/mhsanaei/3x-ui:latest
+ container_name: xray-admin
+ restart: unless-stopped
+ volumes:
+ - /mnt/RED_2TB/docker/xray-admin:/etc/x-ui
+ environment:
+ - XRAY_VMESS_AEAD_FORCED=false
+ networks:
+ - caddy_default
+ ports:
+ - "54321:54321" # 3x-ui панель (web UI)
+
+networks:
+ caddy_default:
+ external: true
+```
+
+⚠️ ПОРТЫ 10095 и 2053/443 НЕ пробрасывать на host отдельно — Caddy стоит перед 443 и маршрутизирует /vless → xray-admin:10095 по имени в общей сети. Если нужно наружу снаружи (не через Caddy), проброс отдельный — обсудить.
+
+## Сеть: reverse к Kraken
+
+`xray-admin` должен быть в сети `caddy_default` — там же Caddy. Проверено: caddy_default содержит caddy, syncthing, webdav, gitea.
+
+## Порядок работ
+
+1. ✅ Бэкап: снять копии Caddyfile, x-ui.db, текущий docker-инвентарь → `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-YYYYMMDD-HHMMSS/`.
+2. Создать `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml` (выше).
+3. Поднять `docker compose up -d`.
+4. Проверить: контейнер Up, панель отвечает на `vpn-panel.mallexxx.duckdns.org`, VLESS inbound активен.
+5. Через панель 3x-ui создать/настроить **reverse inbound** и клиента для Kraken + локальный SOCKS/HTTP для клиентов.
+6. На Kraken поднять Xray reverse-клиент (Docker compose) с выходом в интернет.
+7. Проверка end-to-end: клиент → TrueNAS → Kraken → интернет (с IP Kraken).
+8. Обновить Obsidian: truenas-infrastructure.md, kraken-access.md, vps-qentra.md (пометить VPS удалён).
+
+## Ограничения / риски
+
+- Кра́кен в NAT ⇒ только исходящие соединения; reverse-канал держит Kraken.
+- Не ломать существующий Caddy/домены (бэкапить Caddyfile, перезапуск Caddy аккуратно).
+- `vless-proxy` (Hermes-Taiga) не трогать — настроен под local SOCKS.
+- Внешний Xray-порт: через Caddy по пути `/vless` (WS). Для полноценного reverse возможно потребуется отдельный VLESS+Reality inbound на отдельном порту + проброс на роутере для Kraken — ДОБАВИТЬ после проверки связности панели.
From 0d37fad2d0da1d0faced6c0dc0f6747dd608b9ed Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 11:41:57 +0600
Subject: [PATCH 47/81] [2026-09-01] eagle:
family/plans/reverse-xray-3xui-kraken.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/plans/reverse-xray-3xui-kraken.md | 12 +++++++
.../xray-reverse-tunnel-kraken-truenas.md | 36 +++++++++++++++++--
2 files changed, 46 insertions(+), 2 deletions(-)
diff --git a/family/plans/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md
index 63313ea6..677ce14d 100644
--- a/family/plans/reverse-xray-3xui-kraken.md
+++ b/family/plans/reverse-xray-3xui-kraken.md
@@ -72,6 +72,18 @@ networks:
`xray-admin` должен быть в сети `caddy_default` — там же Caddy. Проверено: caddy_default содержит caddy, syncthing, webdav, gitea.
+## Статус деплоя (2026-09-01)
+
+✅ **3x-ui развёрнут на TrueNAS и работает:**
+- Контейнер `xray-admin` (ghcr.io/mhsanaei/3x-ui:latest, **3.7.0**, Xray 26.7.28) в сети `caddy_default`, volume `/mnt/RED_2TB/docker/xray-admin:/etc/x-ui`.
+- Панель web UI: **`https://vpn-panel.mallexxx.duckdns.org/` → HTTP 200** (Caddy резолвит `xray-admin:2053`).
+- Sub-сервер: `[::]:443` (путь /sub), панель `[::]:2053` — как в Caddyfile.
+- Inbound: `vless-ws` port **10095**, tag `in-10095-tcp`, path `/vless`, host `vpn.mallexxx.duckdns.org` (Caddy path /vless → xray-admin:10095).
+- 3x-ui **поддерживает reverse** (в бинарнике `clientReverseTags`) — настройка через панель.
+- Бэкап: `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/` (Caddyfile + x-ui.db + docker-inventory).
+
+**Следующий шаг:** развернуть reverse-клиент на Kraken (exit node) и настроить reverse-bridge в 3x-ui (TrueNAS) + локальный SOCKS/HTTP для клиентов сети TrueNAS.
+
## Порядок работ
1. ✅ Бэкап: снять копии Caddyfile, x-ui.db, текущий docker-инвентарь → `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-YYYYMMDD-HHMMSS/`.
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index 1c5fbd98..c66b8e11 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -15,7 +15,7 @@ related:
# Xray Reverse Tunnel — Kraken ↔ TrueNAS
-> **Статус: ПЛАН (согласовано направление с Alex, НЕ внедрено).** 2026-09-01.
+> **Статус: ЧАСТИЧНО ВНЕДРЕН (2026-09-01).** ✅ 3x-ui развёрнут на TrueNAS и работает. ⏳ Осталось: reverse-клиент на Kraken + reverse-bridge в 3x-ui.
> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
## Контекст / Почему
@@ -60,7 +60,39 @@ Kraken ──┘ держит ИСХОДЯЩИЙ reverse-канал к TrueNAS,
4. **Kraken:** Docker установлен (`/usr/bin/docker`), но демон отвечает медленно/рвёт SSH (проверка `docker ps` не завершилась за 40s). Xray на Kraken **отсутствует** (`which xray` пусто) — нужен Docker-контейнер.
5. **Связанность Kraken↔TrueNAS по 22/443 — подтверждена** (оба OPEN с Kraken).
-## План реализации (шаги; НЕ выполнены, ждут OK Alex)
+## ✅ ВНЕДРЕНО 2026-09-01: 3x-ui на TrueNAS (фронтенд + Docker Compose)
+
+**Решение Alex:** использовать **3x-ui frontend** + **Docker Compose** (управление клиентами через панель), а не ручной Caddy-pass-through с сырым Xray. Это изменило исходный план реализации (Шаг 1 ниже устарел по способу).
+
+### Развёрнут контейнер `xray-admin` (TrueNAS)
+- **Имя контейнера:** `xray-admin` (строго, т.к. Caddyfile резолвит `xray-admin` по имени в docker-сети).
+- **Образ:** `ghcr.io/mhsanaei/3x-ui:latest` (**3.7.0**, Xray **26.7.28**).
+- **Сеть:** `caddy_default` (external) — там же Caddy, чтобы резолвить имя.
+- **Volume:** `/mnt/RED_2TB/docker/xray-admin:/etc/x-ui` (туда лёг существующий `x-ui.db`).
+- **Порт панели:** хостовый `54321 → 54321` (внутри панель реально слушает **2053** — Caddy проксирует `vpn-panel.mallexxx.duckdns.org` → `xray-admin:2053`).
+- **Compose-файл:** `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml` (deployed версия с `TZ=Asia/Novosibirsk`, `PUID/PGID=950`; см. актуальный файл на TrueNAS — отличается от черновика в плане).
+- **Панель доступна:** `https://vpn-panel.mallexxx.duckdns.org/` → **HTTP 200**.
+- **Sub-сервер:** `[::]:443` (path `/sub`), панель `[::]:2053`, inbound `vless-ws:10095` (path `/vless`, host `vpn.mallexxx.duckdns.org`).
+
+### Почему это заработало «из коробки» (важно)
+База `/mnt/RED_2TB/docker/xray-admin/x-ui.db` **уже была настроена под эту Caddy-интеграцию** (раньше 3x-ui уже задумывался на TrueNAS): inbound `vless-ws` port 10095 tag `in-10095-tcp`, subDomain `vpn-panel.mallexxx.duckdns.org`, subPort 443, subScheme https. Caddyfile уже содержал (мёртвые до подъёма) блоки:
+- `vpn.mallexxx.duckdns.org` → `@ws path /vless` → `xray-admin:10095` (WS)
+- `vpn-panel.mallexxx.duckdns.org` → `@sub path /sub/*` → `xray-admin:443` + fallback `xray-admin:2053`
+- LE-сертификаты для этих поддоменов уже выданы.
+
+### 3x-ui поддерживает reverse
+В бинарнике `/app/x-ui` присутствует **`clientReverseTags`** → reverse-настройка реализуема через панель 3x-ui (3.7.0).
+
+### Бэкап (сделан до изменений)
+`/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/` — содержит: `caddy/Caddyfile` (копия из контейнера, 90 строк), `xray-admin/x-ui.db`, `docker-inventory.txt`.
+
+### Мелочь/питфолл
+Скопированный в volume `docker-compose.yml` оказался внутри контейнера `/etc/x-ui/` (т.к. volume смонтирован туда) — удалить лишний файл из контейнера: `docker exec xray-admin rm -f /etc/x-ui/docker-compose.yml`.
+
+## ⏳ Следующий шаг (НЕ сделан)
+Развернуть **reverse-клиент (outstation) на Kraken** (exit node) и настроить **reverse-bridge в 3x-ui (TrueNAS)** + локальный SOCKS/HTTP для клиентов сети TrueNAS. Механика: `dokodemo-door` + `reverse` + routing; Kraken (outstation) подключается к TrueNAS по публичному `vpn.mallexxx.duckdns.org/vless`, регистрирует локальный SOCKS-выход; bridge (TrueNAS) выставляет алиас-порт для локальных клиентов.
+
+## План реализации (история; Шаг 1 устарел — см. «ВНЕДРЕНО» выше)
### Шаг 1 — TrueNAS: Xray reverse-server поверх Caddy
- **Способ (рекомендован): Caddy TLS-pass-through.** Добавить в Caddy TCP-правило: поддомен `xray.mallexxx.duckdns.org:443` → внутренний порт Xray-контейнера (напр. `localhost:8443`). Caddy передаёт сырой TLS-стрим дальше. Xray на TrueNAS принимает VLESS+Reality/TLS. Kraken ходит на `xray.mallexxx.duckdns.org:443`.
From deb79fa616529df9b611eff2057b10b626fb6e29 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 11:46:59 +0600
Subject: [PATCH 48/81] [2026-09-01] eagle: family/how-to/kraken-access.md
family/how-to/truenas-infrastructure.md family/how-to/vps-qentra.md
family/plans/reverse-xray-3xui-kraken.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/how-to/kraken-access.md | 4 +-
family/how-to/truenas-infrastructure.md | 27 +++++++++++-
family/how-to/vps-qentra.md | 2 +-
family/plans/reverse-xray-3xui-kraken.md | 44 ++++++++++++++-----
.../xray-reverse-tunnel-kraken-truenas.md | 6 +--
5 files changed, 66 insertions(+), 17 deletions(-)
diff --git a/family/how-to/kraken-access.md b/family/how-to/kraken-access.md
index ee0e5f88..000bd6c0 100644
--- a/family/how-to/kraken-access.md
+++ b/family/how-to/kraken-access.md
@@ -7,7 +7,9 @@
Везде `ssh kraken`. Резолвится через `~/.ssh/config` на Eagle:
- **Дома** — напрямую по LAN (`192.168.1.15`)
-- **Снаружи** — ⚠️ через WireGuard (`10.99.1.2`) **БОЛЬШЕ НЕ РАБОТАЕТ**: VPS-узло (10.99.0.1/10.99.1.1) qentra.top **удалили 2026-09-01**. Пока нет альтернативы внешнему доступу к Kraken (см. [[tech/xray-reverse-tunnel-kraken-truenas]] — план).
+- **Снаружи** — ⚠️ через WireGuard (`10.99.1.2`) **БОЛЬШЕ НЕ РАБОТАЕТ**: VPS-узло (10.99.0.1/10.99.1.1) qentra.top **удалили 2026-09-01**. Пока нет альтернативы внешнему доступу к Kraken (см. [[tech/xray-reverse-tunnel-kraken-truenas]] — в процессе внедрения: 3x-ui на TrueNAS развёрнут, reverse-клиент на Kraken ещё нет).
+
+> ⚠️ **Kraken Docker-демон нестабилен на 2026-09-01:** `docker ps` по SSH не завершается за ~40s (рвёт соединение). Проверять медленно; возможно нужен ремонт перед деплоем reverse-клиента Xray на Kraken.
WireGuard split-tunnel: Eagle (10.99.0.2) ↔ VPS ↔ Kraken (10.99.1.2). Подробнее: [[wireguard-vpn]]. **⚠️ VPS-звено мертво на 2026-09-01.**
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 7e098a65..5d2acf79 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -147,8 +147,8 @@ docker compose up -d
Каждый контейнер = своя папка `/mnt/RED_2TB/docker//`. Модель — **не один общий compose, а per-service compose** (контейнеры поднимались `docker compose up -d` из своей папки / `docker run`).
-**✅ Есть `docker-compose.yml` (поднимаются `docker compose -f ... up -d`)** — 21 шт:
-arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library, mbusd, mosquitto, nodered, portainer-mcp, portainer, rclone, ser2net, syncthing, transmission, vless-proxy, watchtower
+**✅ Есть `docker-compose.yml` (поднимаются `docker compose -f ... up -d`)** — 22 шт:
+arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library, mbusd, mosquitto, nodered, portainer-mcp, portainer, rclone, ser2net, syncthing, transmission, vless-proxy, watchtower, **xray-admin (3x-ui, добавлен 2026-09-01)**
**⚠️ НЕТ compose-файла — только конфиг (восстанавливать вручную, параметры из доки ниже):**
- **ha** — Home Assistant: `/mnt/RED_2TB/docker/ha/` (automations.yaml). Образ `ghcr.io/home-assistant/home-assistant:stable`, порт `8123`, volume `/mnt/RED_2TB/docker/ha` → `/config`.
@@ -189,6 +189,29 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
| truenas.mallexxx.duckdns.org | TrueNAS UI :80 |
| cam.mallexxx.duckdns.org | Камера :8090 |
| docs.mallexxx.duckdns.org | Docs :8000 |
+| **vpn-panel.mallexxx.duckdns.org** | **3x-ui панель :2053** (добавлен 2026-09-01) |
+| **vpn.mallexxx.duckdns.org** | **Xray VLESS-WS (путь /vless) → xray-admin:10095** (добавлен 2026-09-01) |
+
+### `xray-admin` — 3x-ui панель (Xray server, добавлена 2026-09-01)
+
+**Цель:** 3x-ui frontend для Xray-reverse туннеля Kraken↔TrueNAS (выход клиентов сети TrueNAS через Kraken). Подробно: [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
+
+| Параметр | Значение |
+|----------|----------|
+| Папка | `/mnt/RED_2TB/docker/xray-admin/` |
+| Контейнер | `xray-admin` (имя важно — Caddyfile резолвит по нему) |
+| Образ | `ghcr.io/mhsanaei/3x-ui:latest` (3.7.0, Xray 26.7.28) |
+| Volume | `/mnt/RED_2TB/docker/xray-admin:/etc/x-ui` (там `x-ui.db`) |
+| Сеть | `caddy_default` (внешняя) |
+| Панель | `https://vpn-panel.mallexxx.duckdns.org/` (Caddy → xray-admin:2053) |
+| Inbound | `vless-ws` port `10095`, tag `in-10095-tcp`, path `/vless`, host `vpn.mallexxx.duckdns.org` |
+| Sub-сервер | `[::]:443` (path /sub), панель `[::]:2053` |
+| Env | `TZ=Asia/Novosibirsk`, `PUID=950`, `PGID=950` |
+| Ports (host) | `54321:54321` (не используется панелью — внутри панель на 2053, можно убрать) |
+
+**Статус (2026-09-01):** контейнер **Up**, панель HTTP 200. Reverse-bridge НЕ настроен ещё — следующий этап. 3x-ui поддерживает reverse (`clientReverseTags`).
+
+> ⚠️ База `x-ui.db` была **уже настроена** под эту Caddy-интеграцию (inbound vless-ws:10095, subDomain vpn-panel), поэтому панель заработала «из коробки». Бэкап базы/Caddyfile до изменений: `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/`.
### Home Assistant — детали
diff --git a/family/how-to/vps-qentra.md b/family/how-to/vps-qentra.md
index 76f460f1..676b749b 100644
--- a/family/how-to/vps-qentra.md
+++ b/family/how-to/vps-qentra.md
@@ -21,7 +21,7 @@ related:
> - На TrueNAS контейнер `vless-proxy` (teddysun/xray) outbound указывает на `v.qentra.top:443` → **сейчас мёртв**. Hermes-Taiga (SOCKS5 через `vless-proxy:1080`) **без рабочего прокси**.
> - WireGuard Eagle↔VPS↔Kraken: VPS-звено мёртво.
> - Rasputin-роутер VLESS-туннель (`188.239.191.235` / node3.sysnx.net) — это ДРУГОЙ сервер (не qentra), не трогать.
-> - Замена для выхода клиентов сети TrueNAS → Kraken: см. **[[tech/xray-reverse-tunnel-kraken-truenas]]** (план Xray reverse, НЕ внедрён).
+> - Замена для выхода клиентов сети TrueNAS → Kraken: см. **[[tech/xray-reverse-tunnel-kraken-truenas]]** (Xray reverse, ЧАСТИЧНО внедрён: 3x-ui на TrueNAS развёрнут 2026-09-01, reverse-клиент на Kraken ещё нет).
**IP:** 91.207.28.205 *(недоступен с 2026-09-01)*
**Stack:** nginx + Python 3.11, Cloudflare proxy *(исторические данные ниже)*
diff --git a/family/plans/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md
index 677ce14d..35f13ee7 100644
--- a/family/plans/reverse-xray-3xui-kraken.md
+++ b/family/plans/reverse-xray-3xui-kraken.md
@@ -82,18 +82,42 @@ networks:
- 3x-ui **поддерживает reverse** (в бинарнике `clientReverseTags`) — настройка через панель.
- Бэкап: `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/` (Caddyfile + x-ui.db + docker-inventory).
-**Следующий шаг:** развернуть reverse-клиент на Kraken (exit node) и настроить reverse-bridge в 3x-ui (TrueNAS) + локальный SOCKS/HTTP для клиентов сети TrueNAS.
+### Фактически развёрнутый compose (deployed на TrueNAS)
+```yaml
+services:
+ xray-admin:
+ image: ghcr.io/mhsanaei/3x-ui:latest
+ container_name: xray-admin
+ hostname: xray-admin
+ restart: unless-stopped
+ environment:
+ - TZ=Asia/Novosibirsk
+ - PUID=950
+ - PGID=950
+ volumes:
+ - /mnt/RED_2TB/docker/xray-admin:/etc/x-ui
+ ports:
+ - "54321:54321" # 3x-ui web UI (внутри панель реально на 2053)
+ networks:
+ - caddy_default
+networks:
+ caddy_default:
+ external: true
+```
+> Черновик выше (секция «Композ-файл») — предварительный; фактический файл на TrueNAS содержит `hostname`, `TZ`, `PUID/PGID`. Панель внутри слушает **2053** (не 54321) — это то, что Caddyfile проксирует через `vpn-panel`. Хостовый 54321-проброс не используется панелью (панель = 2053), можно убрать.
-## Порядок работ
+**Следующий шаг:** развернуть reverse-клиент на Kraken (exit node) и настроить reverse-bridge в 3x-ui (TrueNAS) + локальный SOCKS/HTTP для клиентов сети TrueNAS. См. `personal/tech/xray-reverse-tunnel-kraken-truenas.md` — основной doc архитектуры с деталями.
-1. ✅ Бэкап: снять копии Caddyfile, x-ui.db, текущий docker-инвентарь → `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-YYYYMMDD-HHMMSS/`.
-2. Создать `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml` (выше).
-3. Поднять `docker compose up -d`.
-4. Проверить: контейнер Up, панель отвечает на `vpn-panel.mallexxx.duckdns.org`, VLESS inbound активен.
-5. Через панель 3x-ui создать/настроить **reverse inbound** и клиента для Kraken + локальный SOCKS/HTTP для клиентов.
-6. На Kraken поднять Xray reverse-клиент (Docker compose) с выходом в интернет.
-7. Проверка end-to-end: клиент → TrueNAS → Kraken → интернет (с IP Kraken).
-8. Обновить Obsidian: truenas-infrastructure.md, kraken-access.md, vps-qentra.md (пометить VPS удалён).
+## Порядок работ (отметки → фактический статус 2026-09-01)
+
+1. ✅ Бэкап: `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/` (Caddyfile + x-ui.db + docker-inventory).
+2. ✅ Создан `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml` (deployed версия — см. «Фактически развёрнутый compose»).
+3. ✅ Поднят `docker compose up -d` → контейнер Up.
+4. ✅ Проверено: панель `https://vpn-panel.mallexxx.duckdns.org/` → HTTP 200, inbound vless-ws:10095 активен.
+5. ⏳ Через панель 3x-ui создать/настроить **reverse inbound** и клиента для Kraken + локальный SOCKS/HTTP для клиентов. **НЕ сделано.**
+6. ⏳ На Kraken поднять Xray reverse-клиент (Docker compose) с выходом в интернет. **НЕ сделано.**
+7. ⏳ Проверка end-to-end: клиент → TrueNAS → Kraken → интернет (с IP Kraken).
+8. ⏳ Обновить Obsidian: truenas-infrastructure.md, kraken-access.md, vps-qentra.md (VPS удалён — уже отмечено).
## Ограничения / риски
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index c66b8e11..37f673c2 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -121,9 +121,9 @@ Kraken ──┘ держит ИСХОДЯЩИЙ reverse-канал к TrueNAS,
## Открытые вопросы (требуют ответа Alex)
-1. **Проброс порта на роутере возможен?** Или туннель обязателен ТОЛЬКО через 443 (Caddy TLS-pass-through)?
-2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на NAT/masquerade на Kraken.)
-3. **Docker-демон Kraken работает нестабильно** (рвёт SSH) — чинить перед деплоем?
+1. ✅ (расcмотрено при внедрении 3x-ui) **Проброс порта на роутере** — НЕ потребовался: переиспользовали уже существующий Caddy-путь `vpn.mallexxx.duckdns.org/vless` (WS) и готовую интеграцию 3x-ui↔Caddyfile. Reverse от Kraken пойдёт через этот же публичный путь.
+2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
+3. **Docker-демон Kraken работает нестабильно** (рвёт SSH, `docker ps` не завершается) — чинить перед деплоем reverse-клиента? — открыто, важно для Шага reverse на Kraken.
## Связанные заметки
- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
From 35ff547f9e583ddc2b633f29abb37e38e50bff5b Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 12:52:22 +0600
Subject: [PATCH 49/81] [2026-09-01] eagle:
family/plans/reverse-xray-3xui-kraken.md
---
family/plans/reverse-xray-3xui-kraken.md | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/family/plans/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md
index 35f13ee7..d56c1542 100644
--- a/family/plans/reverse-xray-3xui-kraken.md
+++ b/family/plans/reverse-xray-3xui-kraken.md
@@ -72,6 +72,24 @@ networks:
`xray-admin` должен быть в сети `caddy_default` — там же Caddy. Проверено: caddy_default содержит caddy, syncthing, webdav, gitea.
+## Reverse-схема (Xray portal/bridge) — 2026-09-01, спроектировано
+
+Целевое: локальные клиенты сети TrueNAS (192.168.2.x) выходят в интернет через Kraken.
+
+Роли Xray reverse (по эталону Xray-examples/ReverseProxy):
+- **Kraken = BRIDGE** (reverse bridges) — держит исходящий канал к TrueNAS, публикует "интернет-выход" (freedom).
+- **TrueNAS = PORTAL** (reverse portals) — принимает от локальных клиентов (external-inbound) и пересылает в Kraken по reverse-каналу (interconn).
+
+Конкретные порты/транспорт:
+| Компонент | Хост | Контейнер/роль | Транспорт | Вход/выход |
+|---|---|---|---|---|
+| portal | TrueNAS | `xray-reverse-portal` | VLESS-WS | interconn `:12346` → Caddy path `/rvs`; external `:12345` для клиентов сети |
+| bridge | Kraken | `xray-reverse-bridge` | VLESS-WS | interconn → `vpn.mallexxx.duckdns.org` path `/rvs`; свобода → интернет |
+
+Транспорт: VLESS + WebSocket (т.к. снаружи TrueNAS только 443 через Caddy; WS подходит для reverse поверх outbound). Отдельный путь Caddy `/rvs` (не `/vless` 3x-ui) → `xray-reverse-portal:12346`, чтобы не смешивать с inbound 3x-ui.
+
+Caddy правка: `vpn.mallexxx.duckdns.org` добавить маршрут `@rvs path /rvs` → `reverse_proxy @rvs xray-reverse-portal:12346`.
+
## Статус деплоя (2026-09-01)
✅ **3x-ui развёрнут на TrueNAS и работает:**
From 0a259820b2d4be9abc6edd7cfbcdd0ba3f77c7bb Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 13:02:25 +0600
Subject: [PATCH 50/81] [2026-09-01] eagle:
family/plans/reverse-xray-3xui-kraken.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/plans/reverse-xray-3xui-kraken.md | 58 +++++++++++++++--
.../xray-reverse-tunnel-kraken-truenas.md | 63 +++++++++++++++++--
2 files changed, 110 insertions(+), 11 deletions(-)
diff --git a/family/plans/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md
index d56c1542..89115f7e 100644
--- a/family/plans/reverse-xray-3xui-kraken.md
+++ b/family/plans/reverse-xray-3xui-kraken.md
@@ -124,18 +124,64 @@ networks:
```
> Черновик выше (секция «Композ-файл») — предварительный; фактический файл на TrueNAS содержит `hostname`, `TZ`, `PUID/PGID`. Панель внутри слушает **2053** (не 54321) — это то, что Caddyfile проксирует через `vpn-panel`. Хостовый 54321-проброс не используется панелью (панель = 2053), можно убрать.
-**Следующий шаг:** развернуть reverse-клиент на Kraken (exit node) и настроить reverse-bridge в 3x-ui (TrueNAS) + локальный SOCKS/HTTP для клиентов сети TrueNAS. См. `personal/tech/xray-reverse-tunnel-kraken-truenas.md` — основной doc архитектуры с деталями.
+**Следующий шаг:** ✅ Выполнено — reverse развёрнут (см. ниже). Основной doc архитектуры: `personal/tech/xray-reverse-tunnel-kraken-truenas.md`.
-## Порядок работ (отметки → фактический статус 2026-09-01)
+## Reverse деплой — ПОЛНЫЙ СТАТУС (2026-09-01, финал сессии)
+
+### Развёрнутые контейнеры (фактические конфиги)
+
+**TrueNAS — `xray-reverse-portal`** (VLESS/W S, reverse portal): `/mnt/RED_2TB/docker/reverse-portal/`
+- compose в `/mnt/RED_2TB/docker/reverse-portal/docker-compose.yml`, image `teddysun/xray:latest` (Xray 26.7.28)
+- конфиг `config.json` (portal reverse):
+ - inbound `interconn`: VLESS на `:12346`, ws path `/rvs`, host `vpn.mallexxx.duckdns.org`, client `a81c3179-312b-4c5e-8a46-6bf3c30c2941`, `"reverse":{"tag":"reverse-out"}`
+ - inbound `local`: SOCKS на `:12345` (для клиентов сети TrueNAS), `udp:true`
+ - routing: `inboundTag:["local"] → outboundTag:"reverse-out"`
+ - outbounds: `freedom` (placeholder)
+- проброс на host: **только `12345:12345`** (SOCKS для сети). Порт 12346 в docker-сети (к нему обращается Caddy по имени `xray-reverse-portal:12346`), наружу НЕ проброшен.
+- сеть `caddy_default`, папку создавал через `docker run --rm -v /:/host alpine sh -c "mkdir -p ...; chown 950:950 ..."` (truenas_admin не имеет прав на `/mnt/RED_2TB/docker/`).
+
+**TrueNAS — Caddy**: в блок `vpn.mallexxx.duckdns.org` добавлен маршрут `/rvs`:
+```caddy
+@rvs path /rvs
+reverse_proxy @rvs xray-reverse-portal:12346
+```
+- Caddyfile на хосте: `/mnt/RED_2TB/docker/caddy/Caddyfile` (bind-mount; **нельзя `docker cp`** → `device or resource busy`; править на хосте через alpine `cp /host/tmp/...`).
+- Бэкап перед правкой: `Caddyfile.pre-reverse` в той же папке. Caddy валидация: `docker exec caddy caddy validate --config /tmp/Caddyfile.new` → "Valid configuration". Перезапуск: `docker restart caddy` (безопасно).
+- Проверка маршрута: `curl -sk https://vpn.mallexxx.duckdns.org/rvs` → HTTP 400 (ожидаемо для WS-эндпоинта Xray при не-WS GET).
+
+**Kraken — `xray-reverse-bridge`** (VLESS-WS reverse bridge): `/home/kraken/xray-reverse/`
+- compose + `config.json` (bridge reverse):
+ - outbounds: `direct` = `freedom` + `"finalRules":[{"action":"allow","network":"tcp,udp","ip":["0.0.0.0/0"]}]`; `conn` = VLESS simplified style (НЕ vnext) → `vpn.mallexxx.duckdns.org:443`, ws `/rvs`, `"reverse":{"tag":"reverse-in"}`, security tls
+ - routing: `inboundTag:["reverse-in"] → outboundTag:"direct"`
+- **Питфолл:** VLESS-outbound для reverse ДОЛЖЕН быть в *упрощённом* стиле (`settings.address/port/id/encryption/reverse`) — при использовании `vnext[]/users[]` Xray 26.x ругается: `VLESS users: please use simplified outbound's config style to use "reverse"`.
+- `loglevel debug` включён для диагностики.
+- Порт 443 на Kraken свободен; образ `teddysun/xray:latest` скачан.
+
+### Развёрнутый reverse-канал — работает
+- Kraken → TrueNAS: `netstat` на Kraken показывает `172.24.0.2:xxxxx → 90.189.160.148:443 ESTABLISHED`.
+- Portal (TrueNAS): `::ffff:172.16.1.7:12346 ::ffff:172.16.1.3:52616 ESTABLISHED` (172.16.1.3 = Caddy, передающий WS от Kraken).
+- Bridge логи: `common/mux: received request for udp:reverse:0` — reverse-канал установлен (TCP+UDP).
+
+### ❌ НЕ работает: payload (end-to-end curl 000)
+- `curl --socks5 127.0.0.1:12345 http://api.ipify.org` на TrueNAS → `code=000` (прямой curl с того же хоста → 200).
+- Portal логи: `accepted tcp:8.47.69.0:80 [local -> reverse-out]` — portal отправил запрос в reverse-out.
+- **Bridge НЕ логирует accepted/received для этого TCP-payload** — данные теряются между portal reverse-out и bridge reverse-in.
+- Kraken имеет прямой интернет (api.ipify → `92.62.70.41`, code=200), поэтому проблема НЕ в egress Kraken.
+
+### 💡 Гипотеза по неработающему payload (ВЕРОЯТНАЯ)
+**WebSocket-транспорт НЕ пропускает reverse-payload должным образом.** В официальном Xray reverse-примере используется **прямой TCP** + `flow: xtls-rprx-vision` (REALITY) для канала bridge↔portal. Обратный UDP reverse создаётся (`udp:reverse:0`), но TCP-payload по WS не доходит до bridge.
+**Решение (предполагаемое):** перевести reverse-канал на **прямой TCP**: на TrueNAS VLESS-inbound interconn на отдельном TCP-порту (напр. 12346 наружу) + проброс на роутере `внешний :12346 → 192.168.2.197:12346`; bridge VLESS-outbound → TCP (не WS). Выполнение отложено — Alex согласовал проброс порта («не проблема»), но внедрение не завершено.
+
+## Порядок работ (отметки → фактический статус 2026-09-01, финал)
1. ✅ Бэкап: `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/` (Caddyfile + x-ui.db + docker-inventory).
2. ✅ Создан `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml` (deployed версия — см. «Фактически развёрнутый compose»).
3. ✅ Поднят `docker compose up -d` → контейнер Up.
4. ✅ Проверено: панель `https://vpn-panel.mallexxx.duckdns.org/` → HTTP 200, inbound vless-ws:10095 активен.
-5. ⏳ Через панель 3x-ui создать/настроить **reverse inbound** и клиента для Kraken + локальный SOCKS/HTTP для клиентов. **НЕ сделано.**
-6. ⏳ На Kraken поднять Xray reverse-клиент (Docker compose) с выходом в интернет. **НЕ сделано.**
-7. ⏳ Проверка end-to-end: клиент → TrueNAS → Kraken → интернет (с IP Kraken).
-8. ⏳ Обновить Obsidian: truenas-infrastructure.md, kraken-access.md, vps-qentra.md (VPS удалён — уже отмечено).
+5. ✅ Создан portal-контейнер `xray-reverse-portal` на TrueNAS (VLESS-WS `interconn`:12346 + SOCKS `local`:12345) + Caddy маршрут `/rvs`.
+6. ✅ Создан bridge-контейнер `xray-reverse-bridge` на Kraken (VLESS-WS → TrueNAS, egress direct). Reverse-канал установлен.
+7. ❌ End-to-end НЕ работает (curl через SOCKS 12345 → 000; payload теряется на WS). Гипотеза — перевести reverse на прямой TCP + проброс порта (см. секцию «Reverse деплой»). **ВНЕДРЕНИЕ TCP-варианта ОТЛОЖЕНО (незавершено).**
+8. ⏳ Обновить Obsidian: truenas-infrastructure.md, kraken-access.md, vps-qentra.md. **TODO для следующей сессии.**
## Ограничения / риски
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index 37f673c2..d129b3cd 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -15,7 +15,7 @@ related:
# Xray Reverse Tunnel — Kraken ↔ TrueNAS
-> **Статус: ЧАСТИЧНО ВНЕДРЕН (2026-09-01).** ✅ 3x-ui развёрнут на TrueNAS и работает. ⏳ Осталось: reverse-клиент на Kraken + reverse-bridge в 3x-ui.
+> **Статус: ЧАСТИЧНО ВНЕДРЕН (2026-09-01).** ✅ 3x-ui развёрнут и работает. ✅ reverse portal (TrueNAS) + bridge (Kraken) развёрнуты, reverse-канал установлен. ❌ **End-to-end payload НЕ проходит** (curl 000) — предположительно из-за WS-транспорта reverse (гипотеза: нужен прямой TCP). **TCP-вариант + проброс порта = следующая задача (отложена).**
> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
## Контекст / Почему
@@ -89,8 +89,60 @@ Kraken ──┘ держит ИСХОДЯЩИЙ reverse-канал к TrueNAS,
### Мелочь/питфолл
Скопированный в volume `docker-compose.yml` оказался внутри контейнера `/etc/x-ui/` (т.к. volume смонтирован туда) — удалить лишний файл из контейнера: `docker exec xray-admin rm -f /etc/x-ui/docker-compose.yml`.
-## ⏳ Следующий шаг (НЕ сделан)
-Развернуть **reverse-клиент (outstation) на Kraken** (exit node) и настроить **reverse-bridge в 3x-ui (TrueNAS)** + локальный SOCKS/HTTP для клиентов сети TrueNAS. Механика: `dokodemo-door` + `reverse` + routing; Kraken (outstation) подключается к TrueNAS по публичному `vpn.mallexxx.duckdns.org/vless`, регистрирует локальный SOCKS-выход; bridge (TrueNAS) выставляет алиас-порт для локальных клиентов.
+## ✅ ВНЕДРЕНО 2026-09-01: Reverse portal (TrueNAS) + bridge (Kraken) — VLESS-WS
+
+После 3x-ui развёрнут полноценный reverse. **Ключевое открытие:** Xray **26.x заменил legacy reverse на "VLESS Reverse Proxy"** — старая схема `reverse.bridges/portals` (Xray-examples/ReverseProxy) **не работает** в этой версии (ошибка `The feature "legacy reverse" has been removed and migrated to "VLESS Reverse Proxy"`). Новая схема: `reverse.tag` в VLESS-клиентах/аутбаундах (примеры — `xtls.github.io/en/document/level-2/vless_reverse.html`, use case "Home Broadband Egress").
+
+### Роли (новая терминология VLESS Reverse)
+- **Internal device (Kraken) = BRIDGE**: VLESS-**outbound** с `"reverse":{"tag":"reverse-in"}` → активно устанавливает канал к TrueNAS; на своей стороне создаётся виртуальный **inbound** `reverse-in`, чей трафик направляется в freedom (интернет).
+- **Public server (TrueNAS) = PORTAL**: VLESS-**inbound** с `"reverse":{"tag":"reverse-out"}` у клиента → создаёт **routable-маутбаунд** `reverse-out`; локальные клиенты (SOCKS) маршрутизируются в `reverse-out`.
+- `reverse.tag` на двух сторонах **не обязаны совпадать** — соответствие через общий UUID соединения.
+
+### TrueNAS — `xray-reverse-portal` (папка `/mnt/RED_2TB/docker/reverse-portal/`)
+compose: image `teddysun/xray:latest` (26.7.28), сеть `caddy_default`, volume `config.json:/etc/xray/config.json:ro`, проброс host **`12345:12345`**.
+`config.json`:
+- inbound `interconn` (:12346, VLESS, ws path `/rvs`, host `vpn.mallexxx.duckdns.org`, client id `a81c3179-312b-4c5e-8a46-6bf3c30c2941`, `"reverse":{"tag":"reverse-out"}`)
+- inbound `local` (:12345, SOCKS `noauth udp:true`) — точка входа для клиентов сети TrueNAS
+- routing: `inboundTag:["local"] → outboundTag:"reverse-out"`
+- outbounds: `freedom` (placeholder — обязателен, иначе `reverse-out` станет default и весь трафик уйдёт в reverse)
+- Порт 12346 НЕ проброшен наружу — к нему обращается Caddy по имени `xray-reverse-portal:12346` в docker-сети.
+
+### TrueNAS — Caddyfile (bind mount `/mnt/RED_2TB/docker/caddy/Caddyfile`)
+Добавлен маршрут в блок `vpn.mallexxx.duckdns.org`:
+```caddy
+@rvs path /rvs
+reverse_proxy @rvs xray-reverse-portal:12346
+```
+Питфоллы: **Caddyfile нельзя править `docker cp`** в контейнер (volume → `device or resource busy`) — править на хосте через `docker run --rm -v /:/host alpine cp /host/tmp/...`. Валидация `docker exec caddy caddy validate --config /tmp/Caddyfile.new` → "Valid configuration". Перезапуск `docker restart caddy` безопасен. Бэкап: `Caddyfile.pre-reverse`.
+
+### Kraken — `xray-reverse-bridge` (папка `/home/kraken/xray-reverse/`)
+compose: image `teddysun/xray:latest`, volume `config.json:/etc/xray/config.json:ro`.
+`config.json`:
+- outbound `direct` = `freedom` + `"finalRules":[{"action":"allow","network":"tcp,udp","ip":["0.0.0.0/0"]}]` (интернет-egress Kraken)
+- outbound `conn` = **VLESS упрощённого стиля** (`settings.address/port/id/encryption/reverse` — НЕ `vnext[]`!) → `vpn.mallexxx.duckdns.org:443` ws `/rvs` `"reverse":{"tag":"reverse-in"}`, security tls, tlsSettings.serverName `vpn.mallexxx.duckdns.org`
+- routing: `inboundTag:["reverse-in"] → outboundTag:"direct"`
+- **Питфолл:** VLESS-outbound с `reverse` должен быть упрощённого стиля. Если через `vnext[]/users[]` → Xray 26.x: `VLESS users: please use simplified outbound's config style to use "reverse"`.
+- `loglevel debug` (для диагностики).
+
+### Reverse-канал: ✅ установлен
+- Kraken `netstat`: `172.24.0.2:... → 90.189.160.148:443 ESTABLISHED`
+- Portal: `::ffff:172.16.1.7:12346 ::ffff:172.16.1.3:52616 ESTABLISHED` (172.16.1.3 = Caddy)
+- Bridge logs: `common/mux: received request for udp:reverse:0`
+
+### ❌ End-to-end payload НЕ работает
+- `curl --socks5 127.0.0.1:12345 http://api.ipify.org` на TrueNAS → `code=000` (прямой curl → 200).
+- Portal логи: `accepted tcp:8.47.69.0:80 [local -> reverse-out]` — запрос ушёл в reverse-out.
+- **Bridge НЕ логирует received для этого TCP-payload** — данные теряются между reverse-out (portal) и reverse-in (bridge).
+- Kraken имеет прямой интернет (`api.ipify` → `92.62.70.41`), проблема не в egress.
+
+### 💡 Гипотеза (вероятная) и след. шаг
+**WebSocket-транспорт не пропускает reverse-payload.** В официальных VLESS-reverse примерах канал bridge↔portal идёт **по прямому TCP** + `flow: xtls-rprx-vision` (REALITY). UDP-reverse создаётся, но TCP-payload по WS не доходит.
+**План (отложен):** перевести reverse-канал на **прямой TCP**:
+1. TrueNAS: VLESS-inbound `interconn` на отдельном TCP-порту (напр. `:12346` наружу) + проброс на роутере `внешний :12346 → 192.168.2.197:12346` (Alex подтвердил «пробросить не проблема»).
+2. Kraken: bridge VLESS-outbound → **TCP** (не WS), address `mallexxx.duckdns.org:12346`.
+3. Возможно нужен `flow: xtls-rprx-vision` + шифрование на канале (REALITY) — по доке.
+
+## ⏳ Открытые вопросы по reverse (требуют ответа/действия Alex)
## План реализации (история; Шаг 1 устарел — см. «ВНЕДРЕНО» выше)
@@ -121,9 +173,10 @@ Kraken ──┘ держит ИСХОДЯЩИЙ reverse-канал к TrueNAS,
## Открытые вопросы (требуют ответа Alex)
-1. ✅ (расcмотрено при внедрении 3x-ui) **Проброс порта на роутере** — НЕ потребовался: переиспользовали уже существующий Caddy-путь `vpn.mallexxx.duckdns.org/vless` (WS) и готовую интеграцию 3x-ui↔Caddyfile. Reverse от Kraken пойдёт через этот же публичный путь.
+1. ❌ **Проброс порта для reverse** — теперь НУЖЕН для перевода reverse-канала на TCP (end-to-end payload не проходит по WS). Alex подтвердил «пробросить не проблема». Параметры: `внешний :12346 → 192.168.2.197:12346` (или отдельный VLESS+REALITY порт).
2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
-3. **Docker-демон Kraken работает нестабильно** (рвёт SSH, `docker ps` не завершается) — чинить перед деплоем reverse-клиента? — открыто, важно для Шага reverse на Kraken.
+3. ✅ **Kraken docker** — РАБОТАЕТ (после перезагрузки 2026-09-01): Server 29.4.3, Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-.../docker-data` (актуальный UUID, HDD смонтирован), **30 образов сохранены, но 0 контейнеров** (all контейнеры не восстановлены после сбоя). Reverse-bridge-контейнер развёрнут поверх. НО: известные контейнеры (jellyfin, ha, transmission и т.д.) требуется пересоздать — отдельная задача, не входила в reverse-сферу.
+4. ❌ **End-to-end payload не проходит** — главный блокер (см. секцию «Reverse деплой» выше). Гипотеза WS-проблема → следующий шаг перевести на TCP + проброс порта. Не завершено.
## Связанные заметки
- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
From 3e6e66cde691234c67ae4824c047dbc18d7671cc Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 13:12:29 +0600
Subject: [PATCH 51/81] [2026-09-01] eagle: family/how-to/truenas-access.md
family/how-to/truenas-infrastructure.md
family/plans/reverse-xray-3xui-kraken.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/how-to/truenas-access.md | 9 +++++
family/how-to/truenas-infrastructure.md | 19 +++++++++-
family/plans/reverse-xray-3xui-kraken.md | 36 +++++++++++++++++--
.../xray-reverse-tunnel-kraken-truenas.md | 26 +++++++++-----
4 files changed, 79 insertions(+), 11 deletions(-)
diff --git a/family/how-to/truenas-access.md b/family/how-to/truenas-access.md
index 9652c40f..9272cf54 100644
--- a/family/how-to/truenas-access.md
+++ b/family/how-to/truenas-access.md
@@ -42,6 +42,15 @@ docker run --rm alpine sh -c 'apk add -q openssh sshpass && sshpass -p 1316261 s
**Пароль root:** `1316261`
+> 💡 **Бэкап firewall OpenWrt перед любой правкой redirect-правил** (вошло в практику 2026-09-01):
+> ```bash
+> # с TrueNAS: сохранить весь firewall-конфиг OpenWrt в backup-папку TrueNAS
+> docker run --rm alpine sh -c 'apk add -q openssh sshpass && sshpass -p 1316261 ssh -o StrictHostKeyChecking=no root@192.168.2.2 "uci show firewall"'
+> # → сохранить вывод в /mnt/RED_2TB/docker/backups/openwrt-firewall-YYYYMMDD-HHMMSS.txt
+> # Пример сделанного: /mnt/RED_2TB/docker/backups/openwrt-firewall-20260901-130558.txt
+> ```
+> Существующие redirect-правила OpenWrt → TrueNAS(192.168.2.197): MQTT 1883, SSH 22, caddy_http 80→8088, caddy_https 443→8443, HomeAssistant 8123 (disabled).
+
## Пул и датасеты
Пул: RED_2TB (ZFS)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 5d2acf79..a69a6eca 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -209,10 +209,27 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
| Env | `TZ=Asia/Novosibirsk`, `PUID=950`, `PGID=950` |
| Ports (host) | `54321:54321` (не используется панелью — внутри панель на 2053, можно убрать) |
-**Статус (2026-09-01):** контейнер **Up**, панель HTTP 200. Reverse-bridge НЕ настроен ещё — следующий этап. 3x-ui поддерживает reverse (`clientReverseTags`).
+**Статус (2026-09-01):** контейнер **Up**, панель HTTP 200. Reverse **портал** развёрнут отдельным контейнером `xray-reverse-portal` (см. ниже). 3x-ui поддерживает reverse (`clientReverseTags`).
> ⚠️ База `x-ui.db` была **уже настроена** под эту Caddy-интеграцию (inbound vless-ws:10095, subDomain vpn-panel), поэтому панель заработала «из коробки». Бэкап базы/Caddyfile до изменений: `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/`.
+### `xray-reverse-portal` — Xray reverse portal (TrueNAS, добавлен 2026-09-01)
+
+**Цель:** portal-часть Xray-reverse туннеля: принимает исходящий трафик локальных клиентов сети TrueNAS (через SOCKS `:12345`) и пересылает по reverse-каналу на Kraken (bridge) → интернет через Kraken. Подробно: [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
+
+| Параметр | Значение |
+|----------|----------|
+| Папка | `/mnt/RED_2TB/docker/reverse-portal/` |
+| Контейнер | `xray-reverse-portal` |
+| Образ | `teddysun/xray:latest` (Xray 26.7.28) |
+| Volume | `config.json:/etc/xray/config.json:ro` |
+| Сеть | `caddy_default` |
+| interconn | `:12346` VLESS (принимает reverse-канал от Kraken) — **сейчас на TCP+REALITY** (внутр. порт 12346, проброс `12346:12346`) |
+| local | `:12345` SOCKS (точка входа для клиентов сети TrueNAS), проброс `12345:12345` |
+| Env | `LOGLEVEL` |
+
+**⚠️ Текущее состояние reverse (2026-09-01):** WS-вариант не пропускал payload → решено перевести на **прямой TCP+REALITY**. REALITY-ключи: server PrivateKey `0JTjeirfviDqCr63wSnisvU7DV6SpR9_9V2l_EUHmkk`, PublicKey `GhsphQLT_0fwOvK45MHeAgG3sBQsFtw7lQfUIbrLMQU`, shortId `1451ef281caf5905`, SNI `www.cloudflare.com`. **НЕ ЗАВЕРШЕНО:** конфиги готовы и валидны, осталось пересоздать portal (stop/rm/up) + OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` + пересоздать bridge на Kraken. См. план-заметку [[family/plans/reverse-xray-3xui-kraken]].
+
### Home Assistant — детали
- **Image:** `ghcr.io/home-assistant/home-assistant:stable`
diff --git a/family/plans/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md
index 89115f7e..5f17da7f 100644
--- a/family/plans/reverse-xray-3xui-kraken.md
+++ b/family/plans/reverse-xray-3xui-kraken.md
@@ -126,9 +126,41 @@ networks:
**Следующий шаг:** ✅ Выполнено — reverse развёрнут (см. ниже). Основной doc архитектуры: `personal/tech/xray-reverse-tunnel-kraken-truenas.md`.
-## Reverse деплой — ПОЛНЫЙ СТАТУС (2026-09-01, финал сессии)
+## Reverse деплой — ПОЛНЫЙ СТАТУС (2026-09-01) — РЕШЕН переезд на TCP+REALITY
-### Развёрнутые контейнеры (фактические конфиги)
+### 🔀 РЕШЕНИЕ: WS → прямой TCP+REALITY (принято в сессии 2026-09-01)
+WS-вариант reverse НЕ пропускал payload (curl через SOCKS → 000; portal логировал `accepted tcp:... [local -> reverse-out]`, но bridge не получал). Официальный Xray-reverse пример = прямой TCP + flow `xtls-rprx-vision`. Alex согласовал проброс порта → переводим reverse канал Kraken↔TrueNAS на **прямой TCP+REALITY** порт 12346.
+
+**REALITY-ключи (для interconn :12346):** server PrivateKey `0JTjeirfviDqCr63wSnisvU7DV6SpR9_9V2l_EUHmkk`; server PublicKey (для bridge) `GhsphQLT_0fwOvK45MHeAgG3sBQsFtw7lQfUIbrLMQU`; shortId `1451ef281caf5905`; serverName/SNI `www.cloudflare.com`; flow `xtls-rprx-vision`.
+
+**⚠️ Pitfall Xray 26.x:** VLESS без TLS/шифрования к публичному адресу ЗАПРЕЩЁН (`vless without TLS or other encryption is prohibited unless the server address is a private IP`). → bridge TCP-VLESS обязателен с REALITY.
+
+Обновлённые конфиги (оба `Configuration OK` при `xray run -test`):
+- **portal** `/mnt/RED_2TB/docker/reverse-portal/config.json`: interconn `:12346` VLESS-TCP reality (dest www.cloudflare.com:443, client a81c3179..., flow xtls-rprx-vision, reverse tag reverse-out); local `:12345` SOCKS; routing local→reverse-out.
+- **bridge** `/home/kraken/xray-reverse/config.json`: outbound `conn` VLESS simplified → mallexxx.duckdns.org:12346, flow, reality (publicKey=server PublicKey, shortId, serverName www.cloudflare.com, fingerprint random), reverse tag reverse-in; direct=freedom; routing reverse-in→direct.
+- **compose portal**: добавлен проброс `- "12346:12346"`.
+
+### Доступ к роутеру OpenWrt (TrueNAS сети) — из `truenas-access`
+- OpenWrt = main router сети 192.168.2.0/24, SSH `root@192.168.2.2`, пароль root **`1316261`**. Доступ с TrueNAS через docker+sshpass.
+- ✅ Бэкап firewall OpenWrt: `/mnt/RED_2TB/docker/backups/openwrt-firewall-20260901-130558.txt` (212 строк).
+- Порт-форвардинг: `uci add firewall redirect` ... `src_dport` → `dest_ip=192.168.2.197` `dest_port` `target=DNAT`; `/etc/init.d/firewall reload`.
+
+### ⛔ НЕ ЗАВЕРШЕНО (стоп на одобрение lifecycle-операции)
+1. Пересоздать `xray-reverse-portal` на TrueNAS (compose+config REALITY) → `docker compose stop/rm/up` (одобрение истекло).
+2. OpenWrt DNAT: redirect `внешний:12346 → 192.168.2.197:12346`.
+3. Пересоздать `xray-reverse-bridge` на Kraken (REALITY config).
+4. Тест: `curl --socks5 :12345 https://api.ipify.org` → ожидать IP Kraken `92.62.70.41`.
+
+### ⚠️ Kraken status — диск/докер были нестабильны
+- Несколько `System is booting up` (pam_nologin) — Kraken перезагружался из-за USB-диска в цикле enumeration (`device descriptor read/64 error -110`, `device not accepting address error -62`, `[sda] Read Capacity(10) failed`, `0 B`).
+- Итог: диск появился, Docker `active`, **30 образов целы, 0 контейнеров** (контейнеры потеряны, data-root на HDD).
+- Docker Root Dir Kraken: `/srv/dev-disk-by-uuid-6194539b-.../docker-data` (UUID **6194539b**, не 49e8f586).
+- Прямой интернет Kraken работает (api.ipify → 92.62.70.41). Контейнеры Kraken (jellyfin/transmission/radarr/hermes-kraken...) НЕ восстановлены — отдельная задача при необходимости.
+
+### Историческая справка (WS-версия, суперсидившаяся)
+- Канал до перехода: Kraken→TrueNAS `172.24.0.2 → 90.189.160.148:443 ESTABLISHED`; portal `172.16.1.7:12346 ← Caddy 172.16.1.3 ESTABLISHED`; bridge лог `common/mux: received request for udp:reverse:0`. payload ❌ НЕ проходил (причина перехода на TCP).
+
+#### Развёрнутые контейнеры (WS-версия, историческая справка)
**TrueNAS — `xray-reverse-portal`** (VLESS/W S, reverse portal): `/mnt/RED_2TB/docker/reverse-portal/`
- compose в `/mnt/RED_2TB/docker/reverse-portal/docker-compose.yml`, image `teddysun/xray:latest` (Xray 26.7.28)
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index d129b3cd..ce61d9a7 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -135,12 +135,22 @@ compose: image `teddysun/xray:latest`, volume `config.json:/etc/xray/config.json
- **Bridge НЕ логирует received для этого TCP-payload** — данные теряются между reverse-out (portal) и reverse-in (bridge).
- Kraken имеет прямой интернет (`api.ipify` → `92.62.70.41`), проблема не в egress.
-### 💡 Гипотеза (вероятная) и след. шаг
-**WebSocket-транспорт не пропускает reverse-payload.** В официальных VLESS-reverse примерах канал bridge↔portal идёт **по прямому TCP** + `flow: xtls-rprx-vision` (REALITY). UDP-reverse создаётся, но TCP-payload по WS не доходит.
-**План (отложен):** перевести reverse-канал на **прямой TCP**:
-1. TrueNAS: VLESS-inbound `interconn` на отдельном TCP-порту (напр. `:12346` наружу) + проброс на роутере `внешний :12346 → 192.168.2.197:12346` (Alex подтвердил «пробросить не проблема»).
-2. Kraken: bridge VLESS-outbound → **TCP** (не WS), address `mallexxx.duckdns.org:12346`.
-3. Возможно нужен `flow: xtls-rprx-vision` + шифрование на канале (REALITY) — по доке.
+### ✅ РЕШЕНО 2026-09-01: переход на прямой TCP+REALITY (выполняется)
+WS-транспорт подтверждённо не пропускает reverse-payload. Принято решение (Alex согласовал проброс порта) — перевести канал bridge↔portal на **прямой TCP + REALITY**:
+
+**REALITY-ключи (interconn :12346):** server PrivateKey `0JTjeirfviDqCr63wSnisvU7DV6SpR9_9V2l_EUHmkk`; server PublicKey (для bridge) `GhsphQLT_0fwOvK45MHeAgG3sBQsFtw7lQfUIbrLMQU`; shortId `1451ef281caf5905`; serverName/SNI `www.cloudflare.com`; flow `xtls-rprx-vision`.
+
+**⚠️ Pitfall Xray 26.x:** VLESS без TLS/шифрования к публичному адресу запрещён (`vless without TLS or other encryption is prohibited unless the server address is a private IP`) → TCP-VLESS обязателен с REALITY.
+
+Обновлённые конфиги (оба `Configuration OK` при `xray run -test`):
+- **portal** `/mnt/RED_2TB/docker/reverse-portal/config.json`: interconn `:12346` VLESS-TCP reality; local `:12345` SOCKS; compose получил проброс `- "12346:12346"`.
+- **bridge** `/home/kraken/xray-reverse/config.json`: outbound `conn` → `mallexxx.duckdns.org:12346` reality.
+
+Незавершено (стоп на одобрение lifecycle-операции):
+1. Пересоздать `xray-reverse-portal` (stop/rm/up) на TrueNAS.
+2. OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` (бэкап firewall уже сделан: `/mnt/RED_2TB/docker/backups/openwrt-firewall-20260901-130558.txt`).
+3. Пересоздать `xray-reverse-bridge` на Kraken.
+4. Тест `curl --socks5 :12345 https://api.ipify.org` → ожидать IP Kraken `92.62.70.41`.
## ⏳ Открытые вопросы по reverse (требуют ответа/действия Alex)
@@ -173,10 +183,10 @@ compose: image `teddysun/xray:latest`, volume `config.json:/etc/xray/config.json
## Открытые вопросы (требуют ответа Alex)
-1. ❌ **Проброс порта для reverse** — теперь НУЖЕН для перевода reverse-канала на TCP (end-to-end payload не проходит по WS). Alex подтвердил «пробросить не проблема». Параметры: `внешний :12346 → 192.168.2.197:12346` (или отдельный VLESS+REALITY порт).
+1. ✅ (решено) **Проброс порта для reverse** — переходим на TCP+REALITY. Реализация НЕ завершена: конфиги подготовлены и валидны, осталось (a) пересоздать `xray-reverse-portal` на TrueNAS с новым compose, (b) OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346`, (c) пересоздать `xray-reverse-bridge` на Kraken. Проброс порта Alex подтвердил «не проблема». См. секцию «РЕШЕНО 2026-09-01» выше.
2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
3. ✅ **Kraken docker** — РАБОТАЕТ (после перезагрузки 2026-09-01): Server 29.4.3, Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-.../docker-data` (актуальный UUID, HDD смонтирован), **30 образов сохранены, но 0 контейнеров** (all контейнеры не восстановлены после сбоя). Reverse-bridge-контейнер развёрнут поверх. НО: известные контейнеры (jellyfin, ha, transmission и т.д.) требуется пересоздать — отдельная задача, не входила в reverse-сферу.
-4. ❌ **End-to-end payload не проходит** — главный блокер (см. секцию «Reverse деплой» выше). Гипотеза WS-проблема → следующий шаг перевести на TCP + проброс порта. Не завершено.
+4. ❌ **End-to-end payload не проходит** — блокер. WS-вариант не пропускает payload; переходим на TCP+REALITY (конфиги готовы, осталось пересоздать контейнеры + пробросить порт). См. секцию «РЕШЕНО 2026-09-01» выше. Не завершено.
## Связанные заметки
- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
From 63130e117d1085ceecefc6366a0cd8ece3dc89b6 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 13:42:39 +0600
Subject: [PATCH 52/81] [2026-09-01] eagle: family/how-to/kraken-access.md
family/how-to/truenas-infrastructure.md
family/plans/reverse-xray-3xui-kraken.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/how-to/kraken-access.md | 33 +++++++++----------
family/how-to/truenas-infrastructure.md | 2 ++
family/plans/reverse-xray-3xui-kraken.md | 31 ++++++++++++-----
.../xray-reverse-tunnel-kraken-truenas.md | 33 ++++++++++---------
4 files changed, 59 insertions(+), 40 deletions(-)
diff --git a/family/how-to/kraken-access.md b/family/how-to/kraken-access.md
index 000bd6c0..ec1c26a3 100644
--- a/family/how-to/kraken-access.md
+++ b/family/how-to/kraken-access.md
@@ -2,16 +2,18 @@
> Обновлено: 2026-09-01
+> Updated: 2026-09-01 23:00 — диск/докер кракена восстановились после перезагрузки.
+
## Как зайти
Везде `ssh kraken`. Резолвится через `~/.ssh/config` на Eagle:
- **Дома** — напрямую по LAN (`192.168.1.15`)
-- **Снаружи** — ⚠️ через WireGuard (`10.99.1.2`) **БОЛЬШЕ НЕ РАБОТАЕТ**: VPS-узло (10.99.0.1/10.99.1.1) qentra.top **удалили 2026-09-01**. Пока нет альтернативы внешнему доступу к Kraken (см. [[tech/xray-reverse-tunnel-kraken-truenas]] — в процессе внедрения: 3x-ui на TrueNAS развёрнут, reverse-клиент на Kraken ещё нет).
+- **Снаружи** — ⚠️ через WireGuard (`10.99.1.2`) **БОЛЬШЕ НЕ РАБОТАЕТ**: VPS-узло (10.99.0.1/10.99.1.1) qentra.top **удалили 2026-09-01**. Альтернатива внешнего доступа обсуждается через Xray reverse (см. [[tech/xray-reverse-tunnel-kraken-truenas]]).
-> ⚠️ **Kraken Docker-демон нестабилен на 2026-09-01:** `docker ps` по SSH не завершается за ~40s (рвёт соединение). Проверять медленно; возможно нужен ремонт перед деплоем reverse-клиента Xray на Kraken.
+> ⚠️ **Kraken-диск был нестабилен 2026-09-01**: USB-диск TOSHIBA в цикле enumeration (`device descriptor read/64 error -110`, `device not accepting address error -62`, `[sda] Read Capacity(10) failed` → `0 B`), Kraken перезагружался (`System is booting up / pam_nologin`). **После повторной перезагрузки диск зарегистрировался**: `sda` 1.8T, `sda1` смонтирован в `/srv/dev-disk-by-uuid-6194539b-...`. Docker стал `active`.
-WireGuard split-tunnel: Eagle (10.99.0.2) ↔ VPS ↔ Kraken (10.99.1.2). Подробнее: [[wireguard-vpn]]. **⚠️ VPS-звено мертво на 2026-09-01.**
+> ⚠️ **Kraken Docker на 2026-09-01:** Server 29.4.3, Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/docker-data`. **30 образов сохранены, но 0 контейнеров** (all прежние контейнеры — jellyfin/transmission/radarr/hermes-kraken и т.д. — потеряны и НЕ пересозданы после сбоя; data-root на HDD был в цикле enumeration). При необходимости — пересоздать из образов.
## Portainer (локально)
@@ -28,26 +30,23 @@ EP=3 # endpoint ID на Кракене = 3, не 1!
`http://kraken:8642/v1/chat/completions` — OpenAI-compatible endpoint.
-## Docker контейнеры (2026-06-24)
+## Reverse Xray bridge (развёрнут 2026-09-01)
+
+- Контейнер **`xray-reverse-bridge`** в `/home/kraken/xray-reverse/` (compose + `config.json`, образ `teddysun/xray:latest`, Xray 26.7.28 ARM64).
+- Outbound VLESS+REALITY TCP → `mallexxx.duckdns.org:12346` (TrueNAS через OpenWrt DNAT), reverse tag `reverse-in`, egress freedom.
+- ⛔ **End-to-end payload НЕ работает** (bridge принимает reverse-канал, но данные от portal до bridge не доходят). Детали: [[tech/xray-reverse-tunnel-kraken-truenas]].
+
+## Docker контейнеры (исторически было 2026-06-24; на 2026-09-01 НЕ восстановлены)
| Имя | Заметки |
|-----|---------|
-| flaresolverr | |
-| hermes-kraken | |
-| homeassistant | |
-| jellyfin | |
-| portainer | |
-| prowlarr | |
-| radarr | |
-| rclone | |
-| sonarr | |
-| transmission | |
-| cloudflared | |
-| watchtower | |
+| xray-reverse-bridge | развёрнут 2026-09-01 (единственный активный) |
+| flaresolverr / hermes-kraken / homeassistant / jellyfin / portainer / prowlarr / radarr / rclone / sonarr / transmission / cloudflared / watchtower | образы есть, контейнеры потеряны — пересоздать при необходимости |
## Связанные заметки
- [[kraken-network]] — SSH, WG топология
-- [[wireguard-vpn]] — полное описание WG
+- [[wireguard-vpn]] — полное описание WG (⚠️ VPS-звено мёртво на 2026-09-01)
- [[kraken-portainer-access]]
- [[openmediavault-rpi5]]
+
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index a69a6eca..96b5e1a5 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -121,6 +121,8 @@ TrueNAS использует **NFSv4 ACL**, а не стандартный POSIX
| mbusd | 3cky/mbusd:latest | — | Modbus |
| modbus-bridge | modbus-bridge | — | — |
| cups-splix | cups-splix | — | принтер |
+| xray-admin | ghcr.io/mhsanaei/3x-ui:latest | 2053 (панель), 10095 (vless-ws), 443 (sub) | vpn-panel.mallexxx.duckdns.org, vpn.mallexxx.duckdns.org |
+| xray-reverse-portal | teddysun/xray:latest | 12345 (SOCKS), 12346 (VLESS-TCP REALITY interconn) | — (см. `personal/tech/xray-reverse-tunnel-kraken-truenas.md`) |
### cups-splix — принтер Samsung CLX-216x (✅ поднят 2026-08-26; ✅ mDNS-фикс 2026-08-31 завершён)
diff --git a/family/plans/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md
index 5f17da7f..9f036d56 100644
--- a/family/plans/reverse-xray-3xui-kraken.md
+++ b/family/plans/reverse-xray-3xui-kraken.md
@@ -145,11 +145,26 @@ WS-вариант reverse НЕ пропускал payload (curl через SOCKS
- ✅ Бэкап firewall OpenWrt: `/mnt/RED_2TB/docker/backups/openwrt-firewall-20260901-130558.txt` (212 строк).
- Порт-форвардинг: `uci add firewall redirect` ... `src_dport` → `dest_ip=192.168.2.197` `dest_port` `target=DNAT`; `/etc/init.d/firewall reload`.
-### ⛔ НЕ ЗАВЕРШЕНО (стоп на одобрение lifecycle-операции)
-1. Пересоздать `xray-reverse-portal` на TrueNAS (compose+config REALITY) → `docker compose stop/rm/up` (одобрение истекло).
-2. OpenWrt DNAT: redirect `внешний:12346 → 192.168.2.197:12346`.
-3. Пересоздать `xray-reverse-bridge` на Kraken (REALITY config).
-4. Тест: `curl --socks5 :12345 https://api.ipify.org` → ожидать IP Kraken `92.62.70.41`.
+### ✅ ВНЕДРЕНО (2026-09-01, после согласия Alex) — TCP+REALITY переход выполнен
+Все 4 шага перехода на TCP+REALITY **ВЫПОЛНЕНЫ**:
+1. ✅ Пересоздан `xray-reverse-portal` на TrueNAS (compose `12345:12345` + `12346:12346`, config REALITY TCP). Проверено: `ss -tlnp` → `0.0.0.0:12346 LISTEN`.
+2. ✅ OpenWrt DNAT добавлен: redirect `xray-reverse-12346`, `src_dport=12346` → `dest_ip=192.168.2.197:12346`, `target=DNAT`, `/etc/init.d/firewall reload` → `DNAT_ADDED`. Проверено: `mallexxx.duckdns.org:12346` → **OPEN** извне.
+3. ✅ Пересоздан `xray-reverse-bridge` на Kraken (config REALITY TCP). Reverse-канал снова ESTABLISHED: Kraken `172.24.0.2 → 90.189.160.148:12346 ESTABLISHED` + лог `common/mux: received request for udp:reverse:0`.
+4. ❌ **Тест payload НЕ прошёл даже на TCP+REALITY**: `curl --socks5 127.0.0.1:12345 http://api.ipify.org` на TrueNAS → **всё ещё `code=000`** (прямой curl → 200). Portal: `accepted tcp:8.6.112.0:80 [local -> reverse-out]`, но bridge НЕ логирует приход TCP-payload (`no accepted/received` в `reverse-in`).
+
+### ❌ Дополнительно пробовано: снятие `flow: xtls-rprx-vision`
+- Убрал `flow: xtls-rprx-vision` с обеих сторон (portal interconn client + bridge conn outbound), пересоздал оба контейнера (оба `Configuration OK`).
+- Результат: **по-прежнему `code=000`**. Reverse-канал устанавливается (`udp:reverse:0`), TCP-payload до bridge не доходит.
+- **Вывод: проблема НЕ в `flow` и НЕ в WS-транспорте — даже на «каноничном» TCP+REALITY payload между portal-reverse-out и bridge-reverse-in не передаётся.** Ранее выдвинутая гипотеза (WS не пропускает reverse-payload) **опровергнута** переходом на TCP.
+
+### 💡 Обновлённая гипотеза (непроверенная)
+Reverse-канал (mux `udp:reverse:0`) стабильно НЕ доставляет инициированный portal'ом inbound TCP-payload (из outbound `reverse-out`). Возможные пути решения, НЕ испробованные:
+- Включить `sniffing` (http/tls/quic) на portal inbound `local` (для доменов) — routing по домену в reverse-out.
+- Проверить, что свободе на portal нужен как placeholder («freedom outbound must remain, otherwise reverse-out becomes default») — НО это про обратное; тут reverse-out ВЫБРАН явно в routing.
+- Возможно, для этой версии reverse требует, чтобы на portal входящий от bridge VLESS был НЕ same как клиент с reverse tag — либо VLESS reverse не пробрасывает произвольный inbound SOCKS-трафик.
+- Альтернатива: Kraken тянет свой Xray-SOCKS, а TrueNAS ходит на него обычным VLESS-клиентом (простой исходящий прокси, БЕЗ reverse) — reverse может быть избыточен/несовместим здесь.
+
+**Статус: задание НЕ завершено — end-to-end через reverse НЕ работает. Следующая сессия: продолжить с обновлённой гипотезы (sniffing / пересмотр роли reverse для исходящего прокси клиентов сети TrueNAS).**
### ⚠️ Kraken status — диск/докер были нестабильны
- Несколько `System is booting up` (pam_nologin) — Kraken перезагружался из-за USB-диска в цикле enumeration (`device descriptor read/64 error -110`, `device not accepting address error -62`, `[sda] Read Capacity(10) failed`, `0 B`).
@@ -210,9 +225,9 @@ reverse_proxy @rvs xray-reverse-portal:12346
2. ✅ Создан `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml` (deployed версия — см. «Фактически развёрнутый compose»).
3. ✅ Поднят `docker compose up -d` → контейнер Up.
4. ✅ Проверено: панель `https://vpn-panel.mallexxx.duckdns.org/` → HTTP 200, inbound vless-ws:10095 активен.
-5. ✅ Создан portal-контейнер `xray-reverse-portal` на TrueNAS (VLESS-WS `interconn`:12346 + SOCKS `local`:12345) + Caddy маршрут `/rvs`.
-6. ✅ Создан bridge-контейнер `xray-reverse-bridge` на Kraken (VLESS-WS → TrueNAS, egress direct). Reverse-канал установлен.
-7. ❌ End-to-end НЕ работает (curl через SOCKS 12345 → 000; payload теряется на WS). Гипотеза — перевести reverse на прямой TCP + проброс порта (см. секцию «Reverse деплой»). **ВНЕДРЕНИЕ TCP-варианта ОТЛОЖЕНО (незавершено).**
+5. ✅ Создан portal-контейнер `xray-reverse-portal` на TrueNAS (interconn :12346 + SOCKS `local`:12345) + Caddy маршрут `/rvs`. [Затем: переведён на TCP+REALITY, см. секции «Reverse деплой».]
+6. ✅ Создан bridge-контейнер `xray-reverse-bridge` на Kraken (→ TrueNAS, egress direct). Reverse-канал установлен. [Затем: переведён на TCP+REALITY.]
+7. ❌ **End-to-end НЕ работает** (curl через SOCKS 12345 → 000). Проверено на WS И на TCP+REALITY (flow и без flow) — payload от portal-reverse-out до bridge-reverse-in **не передаётся** (bridge не логирует приход TCP). Гипотеза про WS опровергнута. **Статус: задание НЕ завершено — продолжить со sniffing / пересмотром роли reverse (см. «Обновлённая гипотеза»).**
8. ⏳ Обновить Obsidian: truenas-infrastructure.md, kraken-access.md, vps-qentra.md. **TODO для следующей сессии.**
## Ограничения / риски
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index ce61d9a7..dfc5a032 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -15,7 +15,7 @@ related:
# Xray Reverse Tunnel — Kraken ↔ TrueNAS
-> **Статус: ЧАСТИЧНО ВНЕДРЕН (2026-09-01).** ✅ 3x-ui развёрнут и работает. ✅ reverse portal (TrueNAS) + bridge (Kraken) развёрнуты, reverse-канал установлен. ❌ **End-to-end payload НЕ проходит** (curl 000) — предположительно из-за WS-транспорта reverse (гипотеза: нужен прямой TCP). **TCP-вариант + проброс порта = следующая задача (отложена).**
+> **Статус: ЧАСТИЧНО ВНЕДРЕН, end-to-end НЕ РАБОТАЕТ (2026-09-01).** ✅ 3x-ui развёрнут и работает. ✅ reverse portal (TrueNAS) + bridge (Kraken) развёрнуты на **прямом TCP+REALITY**, reverse-канал установлен. ⛔ **End-to-end payload НЕ проходит** (curl 000) — проверено на WS И на TCP+REALITY (и с flow `xtls-rprx-vision`, и без flow). Гипотеза «WS блокирует payload» **опровергнута**. OpenWrt DNAT + порт 12346 проброшены. Задание НЕ завершено.
> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
## Контекст / Почему
@@ -135,22 +135,25 @@ compose: image `teddysun/xray:latest`, volume `config.json:/etc/xray/config.json
- **Bridge НЕ логирует received для этого TCP-payload** — данные теряются между reverse-out (portal) и reverse-in (bridge).
- Kraken имеет прямой интернет (`api.ipify` → `92.62.70.41`), проблема не в egress.
-### ✅ РЕШЕНО 2026-09-01: переход на прямой TCP+REALITY (выполняется)
-WS-транспорт подтверждённо не пропускает reverse-payload. Принято решение (Alex согласовал проброс порта) — перевести канал bridge↔portal на **прямой TCP + REALITY**:
+### ✅ ВНЕДРЕНО (2026-09-01) — TCP+REALITY переход, все шаги выполнены
+WS-транспорт не пропускал reverse-payload; переведён на **прямой TCP + REALITY** (Alex согласовал проброс). Все 4 шага ВЫПОЛНЕНЫ:
+1. ✅ Пересоздан `xray-reverse-portal` на TrueNAS (compose `12345:12345` + `12346:12346`, config REALITY TCP). Проверено `ss -tlnp` → `0.0.0.0:12346 LISTEN`.
+2. ✅ OpenWrt DNAT добавлен: redirect `xray-reverse-12346`, `src_dport=12346` → `dest_ip=192.168.2.197:12346`, `target=DNAT`, `/etc/init.d/firewall reload` → `DNAT_ADDED`. Проверено: `mallexxx.duckdns.org:12346` → **OPEN** извне.
+3. ✅ Пересоздан `xray-reverse-bridge` на Kraken (config REALITY TCP). Reverse-канал ESTABLISHED: Kraken `172.24.0.2 → 90.189.160.148:12346` + `common/mux: received request for udp:reverse:0`.
+4. ❌ **Тест payload НЕ прошёл даже на TCP+REALITY**: `curl --socks5 127.0.0.1:12345 http://api.ipify.org` → **`code=000`**. Portal: `accepted tcp:8.6.112.0:80 [local -> reverse-out]`, но bridge НЕ логирует приход TCP-payload.
-**REALITY-ключи (interconn :12346):** server PrivateKey `0JTjeirfviDqCr63wSnisvU7DV6SpR9_9V2l_EUHmkk`; server PublicKey (для bridge) `GhsphQLT_0fwOvK45MHeAgG3sBQsFtw7lQfUIbrLMQU`; shortId `1451ef281caf5905`; serverName/SNI `www.cloudflare.com`; flow `xtls-rprx-vision`.
+### ❌ Дополнительно пробовано: снятие `flow: xtls-rprx-vision`
+Убрал `flow: xtls-rprx-vision` с обеих сторон (portal interconn client + bridge conn outbound), пересоздал контейнеры (оба `Configuration OK`). Результат: **по-прежнему `code=000`**. Reverse-канал устанавливается, TCP-payload до bridge не доходит.
-**⚠️ Pitfall Xray 26.x:** VLESS без TLS/шифрования к публичному адресу запрещён (`vless without TLS or other encryption is prohibited unless the server address is a private IP`) → TCP-VLESS обязателен с REALITY.
+**Вывод:** проблема НЕ в `flow` и НЕ в WS. **Даже на «каноничном» TCP+REALITY payload от portal-reverse-out до bridge-reverse-in не передаётся** (bridge не получает inbound-TCP). Гипотеза про WS **опровергнута**.
-Обновлённые конфиги (оба `Configuration OK` при `xray run -test`):
-- **portal** `/mnt/RED_2TB/docker/reverse-portal/config.json`: interconn `:12346` VLESS-TCP reality; local `:12345` SOCKS; compose получил проброс `- "12346:12346"`.
-- **bridge** `/home/kraken/xray-reverse/config.json`: outbound `conn` → `mallexxx.duckdns.org:12346` reality.
+### 💡 Обновлённая гипотеза (непроверенная)
+Reverse-канал (mux `udp:reverse:0`) НЕ доставляет инициированный portal'ом inbound TCP-payload из outbound `reverse-out`. НЕ испробовано:
+- `sniffing` (http/tls/quic) на portal inbound `local` (routing по домену в reverse-out).
+- Возможно, в Xray 26.x reverse не пробрасывает произвольный inbound SOCKS-трафик от portal в bridge корректно.
+- Альтернатива: Kraken тянет `xray`-SOCKS, TrueNAS ходит на него **обычным VLESS-клиентом (без reverse)** — простой исходящий прокси, reverse может быть избыточен.
-Незавершено (стоп на одобрение lifecycle-операции):
-1. Пересоздать `xray-reverse-portal` (stop/rm/up) на TrueNAS.
-2. OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` (бэкап firewall уже сделан: `/mnt/RED_2TB/docker/backups/openwrt-firewall-20260901-130558.txt`).
-3. Пересоздать `xray-reverse-bridge` на Kraken.
-4. Тест `curl --socks5 :12345 https://api.ipify.org` → ожидать IP Kraken `92.62.70.41`.
+**Статус: задание НЕ завершено — продолжить со sniffing / пересмотром роли reverse в следующей сессии.**
## ⏳ Открытые вопросы по reverse (требуют ответа/действия Alex)
@@ -183,10 +186,10 @@ WS-транспорт подтверждённо не пропускает rever
## Открытые вопросы (требуют ответа Alex)
-1. ✅ (решено) **Проброс порта для reverse** — переходим на TCP+REALITY. Реализация НЕ завершена: конфиги подготовлены и валидны, осталось (a) пересоздать `xray-reverse-portal` на TrueNAS с новым compose, (b) OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346`, (c) пересоздать `xray-reverse-bridge` на Kraken. Проброс порта Alex подтвердил «не проблема». См. секцию «РЕШЕНО 2026-09-01» выше.
+1. ✅ (решено) **Проброс порта для reverse** — переведён на TCP+REALITY, ВСЁ ВЫПОЛНЕНО (portal пересоздан с REALITY, OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` добавлен, bridge на Kraken пересоздан). См. секции «ВНЕДРЕНО — TCP+REALITY» выше. **Компанент работает, но payload НЕ проходит (см. пункт 4).**
2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
3. ✅ **Kraken docker** — РАБОТАЕТ (после перезагрузки 2026-09-01): Server 29.4.3, Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-.../docker-data` (актуальный UUID, HDD смонтирован), **30 образов сохранены, но 0 контейнеров** (all контейнеры не восстановлены после сбоя). Reverse-bridge-контейнер развёрнут поверх. НО: известные контейнеры (jellyfin, ha, transmission и т.д.) требуется пересоздать — отдельная задача, не входила в reverse-сферу.
-4. ❌ **End-to-end payload не проходит** — блокер. WS-вариант не пропускает payload; переходим на TCP+REALITY (конфиги готовы, осталось пересоздать контейнеры + пробросить порт). См. секцию «РЕШЕНО 2026-09-01» выше. Не завершено.
+4. ❌ **End-to-end payload не проходит** — блокер. WS-вариант не пропускал payload; переведён на TCP+REALITY (ВЫПОЛНЕНО: portal пересоздан, OpenWrt DNAT + порт 12346 проброшен, bridge пересоздан). **НО payload всё равно не проходит** (`code=000`) даже на TCP+REALITY, с flow и без flow (`Configuration OK`). См. секции «ВНЕДРЕНО — TCP+REALITY» и «Обновлённая гипотеза» выше. Задание не завершено.
## Связанные заметки
- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
From 150407d50d1242c82bee8a8e9bf62061e8b8ca86 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 14:02:46 +0600
Subject: [PATCH 53/81] [2026-09-01] eagle:
family/plans/reverse-xray-3xui-kraken.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/plans/reverse-xray-3xui-kraken.md | 30 +++++++----
.../xray-reverse-tunnel-kraken-truenas.md | 52 ++++++++++++++++---
2 files changed, 65 insertions(+), 17 deletions(-)
diff --git a/family/plans/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md
index 9f036d56..855aa3dd 100644
--- a/family/plans/reverse-xray-3xui-kraken.md
+++ b/family/plans/reverse-xray-3xui-kraken.md
@@ -157,14 +157,26 @@ WS-вариант reverse НЕ пропускал payload (curl через SOCKS
- Результат: **по-прежнему `code=000`**. Reverse-канал устанавливается (`udp:reverse:0`), TCP-payload до bridge не доходит.
- **Вывод: проблема НЕ в `flow` и НЕ в WS-транспорте — даже на «каноничном» TCP+REALITY payload между portal-reverse-out и bridge-reverse-in не передаётся.** Ранее выдвинутая гипотеза (WS не пропускает reverse-payload) **опровергнута** переходом на TCP.
-### 💡 Обновлённая гипотеза (непроверенная)
-Reverse-канал (mux `udp:reverse:0`) стабильно НЕ доставляет инициированный portal'ом inbound TCP-payload (из outbound `reverse-out`). Возможные пути решения, НЕ испробованные:
-- Включить `sniffing` (http/tls/quic) на portal inbound `local` (для доменов) — routing по домену в reverse-out.
-- Проверить, что свободе на portal нужен как placeholder («freedom outbound must remain, otherwise reverse-out becomes default») — НО это про обратное; тут reverse-out ВЫБРАН явно в routing.
-- Возможно, для этой версии reverse требует, чтобы на portal входящий от bridge VLESS был НЕ same как клиент с reverse tag — либо VLESS reverse не пробрасывает произвольный inbound SOCKS-трафик.
-- Альтернатива: Kraken тянет свой Xray-SOCKS, а TrueNAS ходит на него обычным VLESS-клиентом (простой исходящий прокси, БЕЗ reverse) — reverse может быть избыточен/несовместим здесь.
+### 🔑 КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН (2026-09-01, интернет-поиск) — issue XTLS/Xray-core #6612
+**Симптомы точно совпадают с #6612:** канал reverse устанавливается (`udp:reverse:0`), но TCP-payload не проходит; portal логирует `accepted tcp:... -> reverse-out`, bridge НЕ получает входящих reverse-in; curl → 000.
+**Причина:** с **Xray v26.5+** у outbound `freedom`/`direct` дефолтная политика безопасности **блокирует VLESS Reverse payload**, пока не задан явный `finalRules`. Reverse-канал при этом живёт — «выглядит подключено», но payload молча дропается.
+**Фикс (bridge, место egress reverse-in→интернет):**
+```json
+{ "protocol": "freedom", "tag": "direct",
+ "settings": { "domainStrategy": "AsIs", "finalRules": [ { "action": "allow" } ] } }
+```
+**Ссылки:** #6612 (фикс), #6027 (корень — finalRules у Direct), docs freedom, 3x-ui #4782, #2664 (flow vision на bridge+REALITY), #6242/#6195/#6248 (регресс роутинга reverse v26.5+, кварк = пин v26.4.25).
+**Проверено из поиска:** placeholder-freedom на portal нужен (есть); routing `reverse-in→direct` на bridge нужен (есть); у VLESS outbound поле `encryption` обязательно `"none"` (есть).
-**Статус: задание НЕ завершено — end-to-end через reverse НЕ работает. Следующая сессия: продолжить с обновлённой гипотезы (sniffing / пересмотр роли reverse для исходящего прокси клиентов сети TrueNAS).**
+### ✅ ФИКС ПРИМЕНЁН, но заблокирован (2026-09-01)
+Новый bridge-конфиг с `finalRules:[{action:"allow"}]` в `direct` **залит** на Kraken (`/home/kraken/xray-reverse/config.json`, 1098 байт). **НО Kraken по docker НЕДОСТУПЕН:**
+- `systemctl is-active docker` → **`activating`** (не `active`), накопились зависшие docker-процессы (ps/run/compose).
+- **USB-HDD снова не поднялся:** `lsblk /dev/sda` → **`0B`** (без раздела). Docker data-root `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker-data` (на HDD) → daemon ждёт диск, застревает.
+- ⚠️ **Расхождение UUID:** data-root сейчас `/srv/dev-disk-by-uuid-49e8f586-...`, ранее в доке фигурировал `6194539b...` (sda1 1.8T). Сейчас `sda`=0B без раздела.
+- **`docker compose down/up` для bridge не выполнен** — контейнер всё ещё со старым конфигом.
+**Шаг продолжения (нужно подтверждение Alex):** починить HDD/docker на Kraken (reboot или физ. переподключение USB), `cd /home/kraken/xray-reverse && docker compose up -d`, тест `curl --socks5 127.0.0.1:12345 http://api.ipify.org` на TrueNAS → ожидаем IP Kraken.
+
+**Статус: задание НЕ завершено — остаётся применить bridge-фикс после восстановления docker на Kraken.**
### ⚠️ Kraken status — диск/докер были нестабильны
- Несколько `System is booting up` (pam_nologin) — Kraken перезагружался из-за USB-диска в цикле enumeration (`device descriptor read/64 error -110`, `device not accepting address error -62`, `[sda] Read Capacity(10) failed`, `0 B`).
@@ -227,8 +239,8 @@ reverse_proxy @rvs xray-reverse-portal:12346
4. ✅ Проверено: панель `https://vpn-panel.mallexxx.duckdns.org/` → HTTP 200, inbound vless-ws:10095 активен.
5. ✅ Создан portal-контейнер `xray-reverse-portal` на TrueNAS (interconn :12346 + SOCKS `local`:12345) + Caddy маршрут `/rvs`. [Затем: переведён на TCP+REALITY, см. секции «Reverse деплой».]
6. ✅ Создан bridge-контейнер `xray-reverse-bridge` на Kraken (→ TrueNAS, egress direct). Reverse-канал установлен. [Затем: переведён на TCP+REALITY.]
-7. ❌ **End-to-end НЕ работает** (curl через SOCKS 12345 → 000). Проверено на WS И на TCP+REALITY (flow и без flow) — payload от portal-reverse-out до bridge-reverse-in **не передаётся** (bridge не логирует приход TCP). Гипотеза про WS опровергнута. **Статус: задание НЕ завершено — продолжить со sniffing / пересмотром роли reverse (см. «Обновлённая гипотеза»).**
-8. ⏳ Обновить Obsidian: truenas-infrastructure.md, kraken-access.md, vps-qentra.md. **TODO для следующей сессии.**
+7. 🔑 **End-to-end НЕ работал** (curl через SOCKS 12345 → 000) на WS и на TCP+REALITY (flow и без flow) — **причина найдена (issue #6612)**: с v26.5+ у freedom/direct дефолт блокирует VLESS Reverse payload без явного `finalRules`. **Фикс залит** на bridge-конфиг /home/kraken/xray-reverse/config.json, но **не применён** — Kraken по docker недоступен (USB-HDD `sda`=0B, docker в `activating`). См. «КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН» и «ФИКС ПРИМЕНЁН, но заблокирован» выше. **Статус: задание НЕ завершено — нужен фикс HDD/docker Kraken + пересоздать bridge + тест.**
+8. ⏳ Обновить Obsidian: truenas-infrastructure.md, kraken-access.md, vps-qentra.md. **TODO для следующей сессии.** (Основной tech-doc `personal/tech/xray-reverse-tunnel-kraken-truenas.md` уже обновлён 2026-09-01 с корнем/фиксом.)
## Ограничения / риски
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index dfc5a032..1ec1aabf 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -15,7 +15,7 @@ related:
# Xray Reverse Tunnel — Kraken ↔ TrueNAS
-> **Статус: ЧАСТИЧНО ВНЕДРЕН, end-to-end НЕ РАБОТАЕТ (2026-09-01).** ✅ 3x-ui развёрнут и работает. ✅ reverse portal (TrueNAS) + bridge (Kraken) развёрнуты на **прямом TCP+REALITY**, reverse-канал установлен. ⛔ **End-to-end payload НЕ проходит** (curl 000) — проверено на WS И на TCP+REALITY (и с flow `xtls-rprx-vision`, и без flow). Гипотеза «WS блокирует payload» **опровергнута**. OpenWrt DNAT + порт 12346 проброшены. Задание НЕ завершено.
+> **Статус: КОРНЕВАЯ ПРИЧИНА НАЙДЕНА + ФИКС ПРИМЕНЁН, но блокируется недоступностью Kraken (2026-09-01).** ✅ 3x-ui развёрнут и работает. ✅ reverse portal (TrueNAS) + bridge (Kraken) на **прямом TCP+REALITY**, reverse-канал установлен. 🔑 **Найден корень проблемы payload через интернет-поиск (issue XTLS/Xray-core #6612)**: с v26.5+ у `freedom`/`direct` дефолтная политика **блокирует VLESS Reverse payload**, пока не задан явный `finalRules: [{"action":"allow"}]`. ✅ **Фикс залит** (новый bridge-конфиг с `finalRules` на Kraken, `/home/kraken/xray-reverse/config.json`). ⛔ **БЛОКЕР: Kraken недоступен по docker** — USB-HDD `sda` показывает `0B` (не поднялся), docker демон застрял в `activating`, bridge-контейнер пересоздать невозможно. Задание НЕ завершено — нужен фикс HDD/docker на Kraken.
> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
## Контекст / Почему
@@ -147,13 +147,49 @@ WS-транспорт не пропускал reverse-payload; переведё
**Вывод:** проблема НЕ в `flow` и НЕ в WS. **Даже на «каноничном» TCP+REALITY payload от portal-reverse-out до bridge-reverse-in не передаётся** (bridge не получает inbound-TCP). Гипотеза про WS **опровергнута**.
-### 💡 Обновлённая гипотеза (непроверенная)
-Reverse-канал (mux `udp:reverse:0`) НЕ доставляет инициированный portal'ом inbound TCP-payload из outbound `reverse-out`. НЕ испробовано:
-- `sniffing` (http/tls/quic) на portal inbound `local` (routing по домену в reverse-out).
-- Возможно, в Xray 26.x reverse не пробрасывает произвольный inbound SOCKS-трафик от portal в bridge корректно.
-- Альтернатива: Kraken тянет `xray`-SOCKS, TrueNAS ходит на него **обычным VLESS-клиентом (без reverse)** — простой исходящий прокси, reverse может быть избыточен.
+### 🔑 КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН (2026-09-01, интернет-поиск) — issue XTLS/Xray-core #6612
-**Статус: задание НЕ завершено — продолжить со sniffing / пересмотром роли reverse в следующей сессии.**
+**Симптомы точно совпадают с #6612:** reverse-канал устанавливается (`udp:reverse:0` на месте), но TCP-payload не проходит; portal логирует `accepted tcp:... -> reverse-out`, а bridge НЕ получает входящих reverse-in-соединений; curl → 000.
+
+**Причина:** начиная с **Xray v26.5+** (PR #6027) у outbound `freedom`/`direct` появилась **дефолтная политика безопасности**, которая **блокирует ВСЕ цели для трафика VLESS Reverse**, если не задан явный `finalRules`. Контрольный reverse-канал при этом продолжает устанавливаться — поэтому «всё выглядит подключённым», но реальный payload молча отбрасывается. Это ровно наш случай на версии **26.7.28**.
+
+**Точный фикс** — добавить в freedom `direct` на **BRIDGE** (место реального egress из reverse-in в интернет):
+```json
+{
+ "protocol": "freedom",
+ "tag": "direct",
+ "settings": {
+ "domainStrategy": "AsIs",
+ "finalRules": [ { "action": "allow" } ]
+ }
+}
+```
+Issue говорит прямо: проблема воспроизводится с обычным Freedom без finalRules вообще, и добавление безусловного `allow` чинит на 26.7.28.
+
+**Релевантные ссылки:**
+- **#6612** — главный фикс: `https://github.com/XTLS/Xray-core/issues/6612`
+- **#6027** — корень (finalRules у Direct/Freedom): `https://github.com/XTLS/Xray-core/pull/6027`
+- Документация freedom: `https://xtls.github.io/en/config/outbounds/freedom.html`
+- 3x-ui разбор под reverse: `https://github.com/MHSanaei/3x-ui/issues/4782`
+- **#2664** — если finalRules не поможет: `flow: xtls-rprx-vision` на BRIDGE-outbound + REALITY может ломать доставку (держите `flow:""` на reverse-клиенте): `https://github.com/XTLS/Xray-core/issues/2664`
+- **Регрессия роутинга reverse v26.5+** (если фикс не поможет): #6242, #6195, #6248 — временный кварк: пинить **v26.4.25**.
+
+**Важные детали из поиска (проверить при продолжении):**
+- Портал ДОЛЖЕН иметь placeholder-freedom (у нас есть) — иначе reverse-out станет default. Плейсхолдеру не нужен `finalRules:allow` (он обслуживает только несопоставленный трафик).
+- Роутинг `inboundTag:["reverse-in"] -> direct` на bridge — нужен (у нас есть).
+- У VLESS outbound поле `encryption` обязательно (`"none"` или парный PQ-ключ к серверному `decryption`) — у нас `"none"`, ок.
+
+### ✅ ФИКС ПРИМЕНЁН, но заблокирован (2026-09-01)
+
+Новый bridge-конфиг с `finalRules:[{action:"allow"}]` в `direct` **залит** на Kraken (`/home/kraken/xray-reverse/config.json`, 1098 байт, проверено `cat | head -5`). НО:
+- ⛔ **Kraken по docker НЕДОСТУПЕН**: `systemctl is-active docker` → **`activating`** (не `active`), демон застрял, накоплено множество зависших docker-процессов (ps/run/compose).
+- **Причина: USB-HDD снова не поднялся** — `lsblk /dev/sda` → **`0B`** (без раздела). Docker data-root = `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker-data` (на этом HDD) → daemon ждёт диск и застревает.
+- ⚠️ **Замечено расхождение UUID**: сейчас data-root указывает на `/srv/dev-disk-by-uuid-49e8f586-...`, а в контексте ранее фигурировал смонтированный HDD `/srv/dev-disk-by-uuid-6194539b-...` (sda1 1.8T). Текущий `sda` = 0B без раздела.
+- **`docker compose down/up` для bridge не выполнен** — контейнер всё ещё со старым конфигом (больше не деплоится, daemon не отвечает).
+
+**Шаг для продолжения (требует подтверждения Alex):** починить HDD/docker на Kraken (повторить утренний сценарий: `sudo reboot` или физическое переподключение USB-диска), затем пересоздать bridge-контейнер (`cd /home/kraken/xray-reverse && docker compose down && docker compose up -d`), затем тест `curl --socks5 127.0.0.1:12345 http://api.ipify.org` на TrueNAS → ожидаем выход с IP Kraken.
+
+**Статус: задание НЕ завершено — остаётся применить bridge-фикс после восстановления docker на Kraken.**
## ⏳ Открытые вопросы по reverse (требуют ответа/действия Alex)
@@ -189,7 +225,7 @@ Reverse-канал (mux `udp:reverse:0`) НЕ доставляет иниции
1. ✅ (решено) **Проброс порта для reverse** — переведён на TCP+REALITY, ВСЁ ВЫПОЛНЕНО (portal пересоздан с REALITY, OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` добавлен, bridge на Kraken пересоздан). См. секции «ВНЕДРЕНО — TCP+REALITY» выше. **Компанент работает, но payload НЕ проходит (см. пункт 4).**
2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
3. ✅ **Kraken docker** — РАБОТАЕТ (после перезагрузки 2026-09-01): Server 29.4.3, Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-.../docker-data` (актуальный UUID, HDD смонтирован), **30 образов сохранены, но 0 контейнеров** (all контейнеры не восстановлены после сбоя). Reverse-bridge-контейнер развёрнут поверх. НО: известные контейнеры (jellyfin, ha, transmission и т.д.) требуется пересоздать — отдельная задача, не входила в reverse-сферу.
-4. ❌ **End-to-end payload не проходит** — блокер. WS-вариант не пропускал payload; переведён на TCP+REALITY (ВЫПОЛНЕНО: portal пересоздан, OpenWrt DNAT + порт 12346 проброшен, bridge пересоздан). **НО payload всё равно не проходит** (`code=000`) даже на TCP+REALITY, с flow и без flow (`Configuration OK`). См. секции «ВНЕДРЕНО — TCP+REALITY» и «Обновлённая гипотеза» выше. Задание не завершено.
+4. 🔑 **End-to-end payload не проходил** — **КОРЕНЬ НАЙДЕН (issue #6612)**: с v26.5+ у freedom/direct дефолт блокирует VLESS Reverse payload без явного `finalRules:[{action:"allow"}]`. **Фикс залит** на bridge-конфиг, но **не применён** — Kraken по docker недоступен (USB-HDD `sda`=0B, docker в `activating`). См. секции «КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН» и «ФИКС ПРИМЕНЁН, но заблокирован» выше. Задание не завершено — остаётся восстановить docker на Kraken и пересоздать bridge.
## Связанные заметки
- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
From c1c9e189a6af65e85726341896834a23caef096b Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 17:03:46 +0600
Subject: [PATCH 54/81] [2026-09-01] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
...-extension-health-pixels-privacy-triage.md | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index b7a81081..6cb67991 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -12,16 +12,16 @@ Status: **DRAFT**
| Pixel | Trigger | Frequency |
| -------------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------- |
-| `m_debug_web_extension_cpm_initialization_missed_after_session_restore` | Restored tabs do not receive the initial CPM response. | Daily |
-| `m_debug_web_extension_cpm_initialization_recovered_after_session_restore` | CPM later responds after the restoration miss. | Daily |
-| `m_debug_web_extension_cpm_initialization_failed_after_tab_crash` | CPM fails on the first eligible navigation after a tab crash. | Daily + count |
-| `m_debug_web_extension_cpm_initialization_failed_after_extension_reload` | CPM fails on the first eligible navigation after an extension reload. | Daily + count |
-| `m_debug_web_extension_cpm_messaging_stuck_session_restoration` | Messaging remains stuck after a restoration-originated failure. | Daily + count, once/episode |
-| `m_debug_web_extension_cpm_messaging_stuck_tab_crash` | Messaging remains stuck across attempts associated with a tab crash. | Daily + count, once/episode |
-| `m_debug_web_extension_cpm_messaging_stuck_other` | Messaging remains stuck across other independent navigations. | Daily + count, once/episode |
-| `m_debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A stuck episode recovers without an extension reload. | Daily + count, once/episode |
-| `m_debug_web_extension_cpm_messaging_recovered_after_extension_reload` | A stuck episode recovers after an extension reload. | Daily + count, once/episode |
-| `m_debug_web_extension_cpm_messaging_extension_reload_failed` | Reload fails or CPM remains stuck after reload. | Daily + count, once/episode |
+| `debug_web_extension_cpm_initialization_missed_after_session_restore` | Restored tabs do not receive the initial CPM response. | Daily |
+| `debug_web_extension_cpm_initialization_recovered_after_session_restore` | CPM later responds after the restoration miss. | Daily |
+| `debug_web_extension_cpm_initialization_failed_after_tab_crash` | CPM fails on the first eligible navigation after a tab crash. | Daily + count |
+| `debug_web_extension_cpm_initialization_failed_after_extension_reload` | CPM fails on the first eligible navigation after an extension reload. | Daily + count |
+| `debug_web_extension_cpm_messaging_stuck_session_restoration` | Messaging remains stuck after a restoration-originated failure. | Daily + count, once/episode |
+| `debug_web_extension_cpm_messaging_stuck_tab_crash` | Messaging remains stuck across attempts associated with a tab crash. | Daily + count, once/episode |
+| `debug_web_extension_cpm_messaging_stuck_other` | Messaging remains stuck across other independent navigations. | Daily + count, once/episode |
+| `debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A stuck episode recovers without an extension reload. | Daily + count, once/episode |
+| `debug_web_extension_cpm_messaging_recovered_after_extension_reload` | A stuck episode recovers after an extension reload. | Daily + count, once/episode |
+| `debug_web_extension_cpm_messaging_extension_reload_failed` | Reload fails or CPM remains stuck after reload. | Daily + count, once/episode |
iOS and macOS send the exact same pixel names. Parameters distinguish `platform=ios|macos` and `form_factor=phone|tablet|desktop`.
`appVersion` is included; macOS also includes `pixelSource` and `channel`.
From 0f5cf3f315847bc939cb88911c8882fe0de56c06 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 17:13:54 +0600
Subject: [PATCH 55/81] [2026-09-01] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
...-extension-health-pixels-privacy-triage.md | 21 +++++++++----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index 6cb67991..589acbe7 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -12,16 +12,16 @@ Status: **DRAFT**
| Pixel | Trigger | Frequency |
| -------------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------- |
-| `debug_web_extension_cpm_initialization_missed_after_session_restore` | Restored tabs do not receive the initial CPM response. | Daily |
-| `debug_web_extension_cpm_initialization_recovered_after_session_restore` | CPM later responds after the restoration miss. | Daily |
-| `debug_web_extension_cpm_initialization_failed_after_tab_crash` | CPM fails on the first eligible navigation after a tab crash. | Daily + count |
-| `debug_web_extension_cpm_initialization_failed_after_extension_reload` | CPM fails on the first eligible navigation after an extension reload. | Daily + count |
+| `debug_web_extension_cpm_initialization_failed_after_session_restoration` | CPM fails after a restored-page navigation. | Daily |
+| `debug_web_extension_cpm_initialization_failed_after_tab_crash` | CPM fails on the first eligible navigation after a tab crash. | Daily |
+| `debug_web_extension_cpm_initialization_failed_after_extension_reload` | CPM fails on the first eligible navigation after an extension reload. | Daily |
+| `debug_web_extension_cpm_initialization_failed_after_other` | CPM fails after another eligible navigation. | Daily |
| `debug_web_extension_cpm_messaging_stuck_session_restoration` | Messaging remains stuck after a restoration-originated failure. | Daily + count, once/episode |
| `debug_web_extension_cpm_messaging_stuck_tab_crash` | Messaging remains stuck across attempts associated with a tab crash. | Daily + count, once/episode |
| `debug_web_extension_cpm_messaging_stuck_other` | Messaging remains stuck across other independent navigations. | Daily + count, once/episode |
-| `debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A stuck episode recovers without an extension reload. | Daily + count, once/episode |
-| `debug_web_extension_cpm_messaging_recovered_after_extension_reload` | A stuck episode recovers after an extension reload. | Daily + count, once/episode |
-| `debug_web_extension_cpm_messaging_extension_reload_failed` | Reload fails or CPM remains stuck after reload. | Daily + count, once/episode |
+| `debug_web_extension_cpm_messaging_recovered_without_extension_reload` | A failed tab or stuck episode recovers without an extension reload. | Daily + count, once/episode |
+| `debug_web_extension_cpm_messaging_recovered_after_extension_reload` | A failed tab or stuck episode recovers after an extension reload. | Daily + count, once/episode |
+| `debug_web_extension_cpm_messaging_extension_reload_failed` | CPM still fails after a successful extension reload. | Daily + count, once/episode |
iOS and macOS send the exact same pixel names. Parameters distinguish `platform=ios|macos` and `form_factor=phone|tablet|desktop`.
`appVersion` is included; macOS also includes `pixelSource` and `channel`.
@@ -29,14 +29,13 @@ iOS and macOS send the exact same pixel names. Parameters distinguish `platform=
## Detection Rules
- Check only finished, current, main-frame HTTP(S) document navigations after a grace period.
-- Treat all `.sessionRestoration` navigations from one app launch as one batch, not one failure per restored tab.
-- A redirected restoration still belongs to the restoration batch when `.sessionRestoration` appears in its navigation history.
+- A redirected restored-page navigation remains classified as session restoration.
- After restoration, any independent eligible document navigation can be the next attempt: reload, typed URL, link, new-tab navigation, or a real back/forward load.
- Redirects do not count separately. Same-document, failed, non-HTTP(S), and BFCache navigations do not count when CPM is not expected to initialize again.
- A tab crash marks only the next eligible navigation in that tab.
- A successful extension reload marks only the next eligible CPM attempt.
-- `stuck` retains the strongest contributing reason: tab crash, then session restoration, then other.
-- Any successful CPM initialization closes the episode. Pixels are fired for episode transitions, never for every failed tab.
+- `stuck` keeps the reason of the first initialization failure; a later failure does not replace it.
+- Any successful CPM initialization breaks the failure sequence. Recovery is reported when that success belongs to a previously failed tab or follows a stuck state.
## Privacy Questions
From 75ce785946b54c406b527b04a22744489b6b5c2e Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Tue, 1 Sep 2026 18:09:16 +0600
Subject: [PATCH 56/81] [2026-09-01] eagle:
work/projects/cpm-extension-health-pixels-privacy-triage.md
---
work/projects/cpm-extension-health-pixels-privacy-triage.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/work/projects/cpm-extension-health-pixels-privacy-triage.md b/work/projects/cpm-extension-health-pixels-privacy-triage.md
index 589acbe7..92c53663 100644
--- a/work/projects/cpm-extension-health-pixels-privacy-triage.md
+++ b/work/projects/cpm-extension-health-pixels-privacy-triage.md
@@ -28,7 +28,7 @@ iOS and macOS send the exact same pixel names. Parameters distinguish `platform=
## Detection Rules
-- Check only finished, current, main-frame HTTP(S) document navigations after a grace period.
+- Check only finished, current, main-frame HTTP(S) document navigations after a grace period. A failure means the extension is loaded and the dashboard remains `.waiting`; `cpmStage` is diagnostic data, not part of detection.
- A redirected restored-page navigation remains classified as session restoration.
- After restoration, any independent eligible document navigation can be the next attempt: reload, typed URL, link, new-tab navigation, or a real back/forward load.
- Redirects do not count separately. Same-document, failed, non-HTTP(S), and BFCache navigations do not count when CPM is not expected to initialize again.
From 2cf220cddb085ebf4191cfc3cc87e7352709ff68 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 08:44:20 +0600
Subject: [PATCH 57/81] [2026-09-02] eagle:
family/how-to/truenas-infrastructure.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/how-to/truenas-infrastructure.md | 17 +++++++++++++----
.../tech/xray-reverse-tunnel-kraken-truenas.md | 7 +++++++
2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 96b5e1a5..4516885d 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -1,11 +1,11 @@
# TrueNAS — инфраструктура
-> Обновлено: 2026-09-01 (VPS qentra удалён — vless-proxy outbound мёртв; 443=Caddy, не Xray)
+> Обновлено: 2026-09-02 (vpn.mallexxx:443 = РАБОЧИЙ Xray-сервер, проверено end-to-end; vless-proxy outbound мёртв)
> ## ⚠️ ПРИОРИТЕТ 2026-09-01: VPS qentra.top УДАЛЁН → vless-proxy и сетевые туннели сломаны
> - **VPS `91.207.28.205` больше НЕ существует** (вся его Xray/OpenVPN/WG-инфраструктура утрачена). Подробно: [[family/how-to/vps-qentra]].
-> - **Контейнер `vless-proxy`** (teddysun/xray): inbound SOCKS `0.0.0.0:1080` + HTTP `0.0.0.0:1081`, но **outbound ⇒ мёртвый `v.qentra.top:443`** (VLESS+WS+TLS, path `/qentra`, id `2D9F24C4-21FE-4784-9843-F11C384DA67A`). ⇒ Hermes-Taiga (ходит через `vless-proxy:1080`) **сейчас БЕЗ рабочего прокси**.
-> - **⚠️ На `mallexxx.duckdns.org:443` НЕ Xray**, а обычный **Caddy/nginx с валидным Let's Encrypt сертификатом** для `mallexxx.duckdns.org` (проверено `openssl s_client` 2026-09-01). Память «на 443 висел Xray server» — не подтвердилась.
+> - **Контейнер `vless-proxy`** (teddysun/xray): inbound SOCKS `0.0.0.0:1080` + HTTP `0.0.0.0:1081`, но **outbound ⇒ мёртвый `v.qentra.top:443`** (VLESS+WS+TLS, path `/qentra`, id `2D9F24C4-21FE-4784-9843-F11C384DA67A`). ⇒ Hermes-Taiga (ходит через `vless-proxy:1080`) **сейчас БЕЗ рабочего прокси**. **Уточнено 2026-09-02:** `v.qentra.top` по-прежнему ресолвится, но в **Cloudflare IP** (`104.21.54.239`, `2606:4700:...`) — DNS-запись на qentra.top осталась в Cloudflare, хотя VPS сам удалён; origin'а за ней нет, поэтому трафик через прокси не идёт. Контейнер `vless-proxy` сам ЖИВ (Up 8 days), путь через его SOCKS наружу не проходит.
+> - **⚠️ На корневом `mallexxx.duckdns.org:443` НЕ Xray**, а обычный **Caddy/nginx с валидным Let's Encrypt сертификатом** (проверено `openssl s_client` 2026-09-01). **НО на поддомене `vpn.mallexxx.duckdns.org:443` — РАБОЧИЙ Xray-сервер** (см. раздел `xray-admin` ниже, проверено end-to-end 2026-09-02).
> - **Наружу открыты только 22 (SSH) и 443 (HTTPS/TLS)** с Kraken; `8443/8964/8080/9000` closed.
> - **План замены** (выход клиентов сети TrueNAS через Kraken, Xray reverse — НЕ внедрён): [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
@@ -206,13 +206,22 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
| Volume | `/mnt/RED_2TB/docker/xray-admin:/etc/x-ui` (там `x-ui.db`) |
| Сеть | `caddy_default` (внешняя) |
| Панель | `https://vpn-panel.mallexxx.duckdns.org/` (Caddy → xray-admin:2053) |
-| Inbound | `vless-ws` port `10095`, tag `in-10095-tcp`, path `/vless`, host `vpn.mallexxx.duckdns.org` |
+| Inbound | `vless-ws` port `10095`, tag `in-10095-tcp`, path `/vless`, host `vpn.mallexxx.duckdns.org`, client id `ce320965-6956-4759-84bb-7cb71cfc6252` (user1) |
| Sub-сервер | `[::]:443` (path /sub), панель `[::]:2053` |
| Env | `TZ=Asia/Novosibirsk`, `PUID=950`, `PGID=950` |
| Ports (host) | `54321:54321` (не используется панелью — внутри панель на 2053, можно убрать) |
**Статус (2026-09-01):** контейнер **Up**, панель HTTP 200. Reverse **портал** развёрнут отдельным контейнером `xray-reverse-portal` (см. ниже). 3x-ui поддерживает reverse (`clientReverseTags`).
+**✅ ПРОВЕРЕНО РАБОТАЕТ (2026-09-02): `vpn.mallexxx.duckdns.org:443` — это ФУНКЦИОНАЛЬНЫЙ Xray-СЕРВЕР, НЕ только reverse-frontend.** Проверено end-to-end юзером с Mac. Ключевые факты с живой машины:
+- Контейнеры `xray-admin` (Up 21h) и `caddy` (Up 20h) оба живы. Панель `vpn-panel` → HTTP 200. TCP `vpn.mallexxx.duckdns.org:443` снаружи открыт.
+- **Inbound `in-10095-tcp`** (реальный конфиг из `bin/config.json`, Xray 26.7.28): VLESS-WS, `security: none`, WS path `/vless`, host `vpn.mallexxx.duckdns.org`.
+ - **client id:** `ce320965-6956-4759-84bb-7cb71cfc6252` (email `user1`)
+- **Outbound:** `freedom`/`direct` — сервер имеет **СОБСТВЕННЫЙ выход в интернет** через TrueNAS (не через Kraken!). routing: geoip:private + bittorrent → blocked.
+- **Тест с Mac** (временный xray-клиент в docker): VLESS+WS через `vpn.mallexxx.duckdns.org:443` → HTTP 200, выходной IP `90.189.160.148` (TrueNAS). Сервер полностью рабочий.
+- **⚠️ Так и твой клиент должен подключаться:** TLS терминирует Caddy на 443, inbound `security: none` → в конфиге КЛИЕНТА `network: ws`, TLS поверх на `vpn.mallexxx.duckdns.org` (**НЕ включать двойной TLS внутрь WS**).
+- **⚠️ Xray 26.7.28 удалил `allowInsecure`** в клиентских tlsSettings (мигрировано на `pinnedPeerCertSha256`/`verifyPeerCertByName`). Если в клиентском конфиге был `allowInsecure: true` — клиент падает. Это частая причина «не подключается».
+
> ⚠️ База `x-ui.db` была **уже настроена** под эту Caddy-интеграцию (inbound vless-ws:10095, subDomain vpn-panel), поэтому панель заработала «из коробки». Бэкап базы/Caddyfile до изменений: `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/`.
### `xray-reverse-portal` — Xray reverse portal (TrueNAS, добавлен 2026-09-01)
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index 1ec1aabf..9927186b 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -18,6 +18,13 @@ related:
> **Статус: КОРНЕВАЯ ПРИЧИНА НАЙДЕНА + ФИКС ПРИМЕНЁН, но блокируется недоступностью Kraken (2026-09-01).** ✅ 3x-ui развёрнут и работает. ✅ reverse portal (TrueNAS) + bridge (Kraken) на **прямом TCP+REALITY**, reverse-канал установлен. 🔑 **Найден корень проблемы payload через интернет-поиск (issue XTLS/Xray-core #6612)**: с v26.5+ у `freedom`/`direct` дефолтная политика **блокирует VLESS Reverse payload**, пока не задан явный `finalRules: [{"action":"allow"}]`. ✅ **Фикс залит** (новый bridge-конфиг с `finalRules` на Kraken, `/home/kraken/xray-reverse/config.json`). ⛔ **БЛОКЕР: Kraken недоступен по docker** — USB-HDD `sda` показывает `0B` (не поднялся), docker демон застрял в `activating`, bridge-контейнер пересоздать невозможно. Задание НЕ завершено — нужен фикс HDD/docker на Kraken.
> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
+> ## ✅ ВАЖНО (2026-09-02): `xray-admin` (3x-ui) на TrueNAS — это ОДНОВРЕМЕННО РАБОЧИЙ САМОСТОЯТЕЛЬНЫЙ Xray-VPN-сервер
+> Путаница в вопросе юзера выявила: `vpn.mallexxx.duckdns.org:443` — это **не только reverse-frontend**, а полноценный Xray-сервер со **своим собственным выходом в интернет** (outbound `freedom`/`direct` → сразу через TrueNAS), НЕ зависящий от reverse-канала к Kraken. Это **отдельная фича**, параллельная reverse-туннелю.
+> - **Проверено end-to-end 2026-09-02** (временный xray-клиент с Mac): VLESS+WS через `vpn.mallexxx.duckdns.org:443` → HTTP 200, выход IP `90.189.160.148` = TrueNAS. Контейнеры `xray-admin` (Up 21h) + `caddy` (Up 20h) живы.
+> - Inbound `in-10095-tcp` (VLESS-WS): client id `ce320965-6956-4759-84bb-7cb71cfc6252` (user1), `security: none`, path `/vless`, host `vpn.mallexxx.duckdns.org`. TLS терминирует Caddy → в клиенте `network: ws` + TLS на домен, **без** двойного TLS внутрь.
+> - Полные параметры и pitfalls (вкл. удаление `allowInsecure` в Xray 26.7.28) — в `family/how-to/truenas-infrastructure.md`, раздел `xray-admin`.
+> - То есть reverse к Kraken — это ОТДЕЛЬНЫЙ механизм (для проксирования локальных клиентов сети TrueNAS через Kraken-egress); он по-прежнему НЕ внедрён. Сам по себе 3x-ui-сервер уже даёт рабочий VPN-выход через TrueNAS.
+
## Контекст / Почему
- **VPS qentra.top (91.207.28.205) УДАЛЁН** — вся прежняя Xray-инфраструктура на нём (VLESS+REALITY, OpenVPN, WebSocket v.qentra.top) больше не существует.
From f1c948f1d2ad952db67ba576993ab09dbc84c4e2 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 10:50:03 +0600
Subject: [PATCH 58/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
family/how-to/arr-stack-taiga.md family/how-to/syncthing-truenas-android.md
family/how-to/vault-git-sync.md
---
.../2026-08-31-syncthing-truenas-incident.md | 29 ++++-
.../2026-09-02-restore-privilege-scope.md | 107 ++++++++++++++++++
family/how-to/arr-stack-taiga.md | 6 +-
family/how-to/syncthing-truenas-android.md | 12 +-
family/how-to/vault-git-sync.md | 6 +-
5 files changed, 154 insertions(+), 6 deletions(-)
create mode 100644 family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
diff --git a/family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md b/family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md
index aa672386..b37cefff 100644
--- a/family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md
+++ b/family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md
@@ -1,11 +1,13 @@
---
tags: [syncthing, truenas, debug, incident]
created: 2026-08-31
-status: diagnosed
+status: resolved-syncthing (see 2026-09-02 for cascade)
---
# Incident: Syncthing не синкается с TrueNAS (после restore)
+> **UPDATE 2026-09-02:** Syncthing-фикс подтверждён рабочим (см. ниже «Резолюция 2026-09-02»). НО выяснилось, что та же поломка прав ред 921/0000 поразила и `storage/git/` (bare repo obsidian → git-sync на маке стоит, ahead 55) и все медиа-папки (`storage/{Movies,Cartoons,Downloads,Music,series,shared}`) → arr/jellyfin-стек на TrueNAS неработоспособен. См. `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
+
**Дата диагностики:** 2026-08-31
**Симптом:** "Syncthing не конектится к TrueNAS; TrueNAS не в нашей сети, доступен через внешний адрес". В UI папка не синкается / процентов нет.
@@ -53,6 +55,31 @@ getent passwd 921 → transmission:x:921:921
3. Рестарт контейнера: `docker compose -f /mnt/RED_2TB/docker/syncthing/docker-compose.yml restart`
4. Дождаться `Completed scan`, проверить статус папки через REST.
+## Резолюция 2026-09-02 (Syncthing — подтверждено рабочим)
+
+**Факт (проверка read-only с хоста TrueNAS):** синхронизация obsidian починена и работает.
+
+Критически важная деталь диагностики 31-08: **в compose-файле контейнера mount указывает на ДРУГУЮ папку, не ту, что в оригинальной доке:**
+
+```yaml
+volumes:
+ - /mnt/RED_2TB/storage/obsidian-syncthing:/var/syncthing/obsidian-vault # актуальный mount
+```
+
+(в `family/how-to/syncthing-truenas-android.md` и исходном compose стоит `/storage/obsidian` без `-syncthing` — УСТАРЕЛО).
+
+**Какие папки есть и рабочая ли состояния:**
+```
+/mnt/RED_2TB/storage/obsidian drwxrwx--- truenas_admin (950) Jul 23
+/mnt/RED_2TB/storage/obsidian-syncthing drwxrwx--- truenas_admin (950) Aug 31 ← рабочий mount
+/mnt/RED_2TB/storage/obsidian/.stfolder → НЕТ (не нужен, это не sync target)
+/mnt/RED_2TB/storage/obsidian-syncthing/.stfolder → ЕСТЬ (маркер syncthing, uid 950)
+```
+
+Права обеих папок починены на `950:950 truenas_admin` (были `d--------- 921:921`). `.stfolder` есть в `obsidian-syncthing` — это активная sync-копия vault. Папка после фикса в `Completed scan`, телефон подтянул зависшие файлы.
+
+**Итог:** рабочий mount в compose — `obsidian-syncthing`, никакой rename папки не требуется. Док `syncthing-truenas-android.md` требует обновления своего `volumes:` блока до `obsidian-syncthing`.
+
## Затрагиваемые файлы (лог телефона)
- `family/how-to/truenas-access.md`
- `family/how-to/truenas-sata-ports-and-zfs-pools.md`
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
new file mode 100644
index 00000000..c7343d0d
--- /dev/null
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -0,0 +1,107 @@
+---
+tags: [truenas, restore, incident, git, media, arr, permission]
+created: 2026-09-02
+status: diagnosed
+---
+
+# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
+
+**Сводный инцидент-документ.** Родитель: `2026-08-31-syncthing-truenas-incident.md` (Syncthing). Здесь документируем подтверждённый масштаб той же поломки прав и её воздействие на git-sync (мак/Eagle) и медиа/arr-стек на TrueNAS.
+
+**Корень (общий для всех):** после восстановления из бэкапа `/mnt/RED_2TB/storage/*` стал принадлежать **uid 921 = `transmission`** с правами `----------`/`d---------` (0000), вместо `truenas_admin` (950). Контейнеры, пользователи и git, работающие под другими uid, теряют доступ.
+
+---
+
+## 1. Git-sync obsidian на маке (Eagle) — СТОИТ (ahead 55)
+
+**Симптом на `~/obsidian`:**
+```
+git status → ## main...nas/main [ahead 55]
+git fetch/push → fatal: '/mnt/RED_2TB/storage/git/obsidian-vault.git' does not appear to be a git repository
+```
+
+**Причина (read-only проверка TrueNAS):**
+```
+/mnt/RED_2TB/storage/git d--------- 4 921 921
+/mnt/RED_2TB/storage/git/obsidian-vault.git (permission denied — вложено в storage/git)
+```
+Папка `storage/git/` (внутри — bare repo `obsidian-vault.git`) с правами 0000 / owner 921. Git на маке (SSH-ключ жив, `CONN_OK` ✅) не может открыть bare repo → fetch/push падают → локальные 55 коммитов не уходят.
+
+**Важно про механизм cron — НЕ Hermes cron, а launchd:**
+- Агент: `~/Library/LaunchAgents/com.sync-vault.plist`
+- Программа: `/Users/admin/scripts/sync-vault.sh`, `StartInterval = 300` сек (5 мин)
+- Состояние: **активен** (`launchctl list` → `com.sync-vault`), `runs = 3762`, `last exit code = 0`
+- `sync-vault-lib.sh` **умышленно глушит** недоступность remote: если `git fetch` падает → логирует и `exit 0`. Поэтому launchd показывает exit code 0 при фактически сломанном sync (`ahead 55`). Это **не** сбой cron — это корректное поведение скрипта, скрывающее dead remote.
+- Системный crontab на маке пуст (`crontab -l` нет), `/etc/crontab` нет. Запуск — только через launchd `${LABEL}`.
+
+**Связанные доки:** `family/how-to/vault-git-sync.md`, `family/how-to/obsidian-sync.md`.
+
+---
+
+## 2. Медиа-папки TrueNAS — сломаны (uid 921, d---------/drwx------)
+
+Read-only проверка `ls -ld /mnt/RED_2TB/storage/`:
+```
+d--------- transmission transmission storage/Cartoons
+d--------- transmission transmission storage/Downloads
+d--------- transmission transmission storage/Movies
+d--------- transmission transmission storage/Music
+drwx------ transmission transmission storage/series
+d--------- transmission transmission storage/shared
+```
+(отображается как `transmission` = uid 921 — тот же, что и в инциденте; права 0000/700 сломаны для внешних сервисов.) `Movies-Radarr`, `Series-Sonarr` — не существуют (старая схема, на TrueNAS актуальны только `storage/`-медиа).
+
+Медиа-папки починки не проходили (в отличие от `obsidian`/`obsidian-syncthing`).
+
+---
+
+## 3. Arr/Jellyfin-стек на TrueNAS — выключен + не сможет работать из-за прав
+
+**Факты:**
+- В `docker ps -a` НЕТ ни radarr/sonarr/prowlarr/jellyfin контейнеров (ни запущенных, ни остановленных). Стек не пересоздавался после проблемного периода.
+- Composes живут в `/mnt/RED_2TB/docker/arr/`: `docker-compose.yml` (prowlarr/radarr/sonarr/jellyfin), подпапки `prowlarr/ radarr/ sonarr/ jellyfin/ media-pipeline/`.
+- `docker/arr/docker-compose.yml` монтирует `/mnt/RED_2TB/storage` в radarr/sonarr (rw) и jellyfin (`:ro`). При сломанных правах медиа-папок контейнеры не смогут читать/импортировать → пустая библиотека.
+- **Работает только `transmission`** (Up 8 days), его compose отдельный: `/mnt/RED_2TB/docker/transmission/`. config владелец `transdamon`/911. RPC `transmission-remote` вернул `403 Forbidden` — auth вкл., статус торрентов извне без токена не читается.
+
+**Чтобы запустить стек:** восстановить права медиа-папок `storage/` (150 и выше 950/нужный сервисный uid, rwX) → `cd /mnt/RED_2TB/docker/arr && docker compose up -d`.
+
+**Дексо стек** — разделение ролей (см. `family/how-to/arr-stack-taiga.md`):
+- Taiga (TrueNAS) = acquisition (transmission качает, arr добавляет)
+- Kraken = serving (jellyfin на план-сервире). Jellyfin на TrueNAS — acquisition-side media storage, не primary serving.
+
+---
+
+## 4. Несвязанные находки (не трогала диагностика, для контекста)
+
+- Контейнер `library-app` (`library`) в restart-loop: `FileNotFoundError: '/library/flibusta_fb2_local.inpx'` — это приложение полки книг Flibusta, монтирует `/mnt/RED_2TB/storage/Downloads/fb2.Flibusta.Net → /library`; INPX-файла на пути нет. Не связано с поломкой прав; ручной фикс только по запросу.
+
+---
+
+## Общий план (НЕ выполнен — требует подтверждения + бэкап ACL, единый по всем каталогам)
+
+```bash
+# 1. Бэкап текущих ACL всех затронутых
+getfacl -R /mnt/RED_2TB/storage > /mnt/RED_2TB/backup/_acl_storage_$(date +%F).facl
+
+# 2а. bare git repo (восстановить git-sync на маке)
+sudo chown -R 950:950 /mnt/RED_2TB/storage/git
+sudo chmod -R u+rwX,g+rwX /mnt/RED_2TB/storage/git
+# → на маке: bash ~/scripts/sync-vault.sh (55 коммитов уедут)
+
+# 2б. медиа-папки (чтобы arr/jellyfin заработал)
+sudo chown -R 950:950 /mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared,...}
+sudo chmod -R u+rwX,g+rwX /mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared,...}
+
+# 3. Запуск arr-стека
+cd /mnt/RED_2TB/docker/arr && docker compose up -d
+```
+
+> ⚠️ Перед выполнением сверить фактического владельца/права у каждого пути и какие uid-сервисы должны читать. Не менять вслепую — сначала уточнить exact mount/service scopes.
+
+## Связанные заметки
+- [[2026-08-31-syncthing-truenas-incident]] — вызвавший инцидент + фикс syncthing
+- [[arr-stack-taiga]] — acquisition-стек на TrueNAS
+- [[arr-stack-kraken]] — serving-стек (Kraken)
+- [[vault-git-sync]] — git-sync алгоритм и скрипты
+- [[obsidian-sync]] — общая схема Eagle↔Taiga↔Kraken
+- [[syncthing-truenas-android]] — syncthing схема (требует обновления volumes → `obsidian-syncthing`)
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index 8064641a..959165d0 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -1,7 +1,7 @@
---
title: Arr Stack — Taiga
created: '2026-05-23'
-updated: '2026-05-23'
+updated: '2026-09-02'
type: tech
namespace: family
tags: [arr, taiga, infra, media, pitfalls]
@@ -13,6 +13,10 @@ related:
Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
+> **STATE 2026-09-02:** Стек **выключен** — в `docker ps -a` нет ни radarr/sonarr/prowlarr/jellyfin контейнеров (стек не пересоздавался). Compose-структура живёт в `/mnt/RED_2TB/docker/arr/` (`docker-compose.yml` + подпапки `prowlarr/ radarr/ sonarr/ jellyfin/ media-pipeline/`). Работает только `transmission` (отдельный compose `/mnt/RED_2TB/docker/transmission/`).
+>
+> **Почему не запускается просто так — медиа-права сломаны** (тот же restore-инцидент uid 921/0000): `/mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared}` → `d---------`/`drwx------`, owner `transmission` (uid 921). radarr/sonarr монтируют `/storage` (rw), jellyfin `:ro` — со сломанными правами не прочитают библиотеку. Фикс + запуск: см. `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
+
## Notes from Setup (2026-05-20)
- Config and pitfalls recorded during initial Taiga arr stack deployment
diff --git a/family/how-to/syncthing-truenas-android.md b/family/how-to/syncthing-truenas-android.md
index 61631142..b1292f5c 100644
--- a/family/how-to/syncthing-truenas-android.md
+++ b/family/how-to/syncthing-truenas-android.md
@@ -9,12 +9,16 @@ Android (Syncthing app)
│ P2P sync (QUIC/TCP :22000)
▼
TrueNAS (syncthing контейнер)
- /var/syncthing/obsidian-vault → /mnt/RED_2TB/storage/obsidian
+ /var/syncthing/obsidian-vault → /mnt/RED_2TB/storage/obsidian-syncthing
│
├── Obsidian vault (полная копия на TrueNAS)
└── Eagle (Mac) — продолжает через git, Syncthing не участвует
```
+> ⚠️ **2026-09-02 уточнение:** фактический mount в compose — `/mnt/RED_2TB/storage/obsidian-syncthing` (НЕ `/storage/obsidian`). Есть две отдельные папки:
+> - `/storage/obsidian` — архивная/рабочая копия vault (старый mount в доке; `.stfolder` отсутствует) — НЕ является sync target.
+> - `/storage/obsidian-syncthing` — **активная sync-копия** (`.stfolder` присутствует, uid 950) — это то, что реально монтируется контейнером. По ней и идёт телефонная синхронизация. В блоке `volumes:` ниже именно она.
+
## Доступ
- **Web UI:** https://syncthing.mallexxx.duckdns.org
@@ -42,7 +46,7 @@ services:
- TZ=Asia/Novosibirsk
volumes:
- /mnt/RED_2TB/docker/syncthing/config:/var/syncthing/config
- - /mnt/RED_2TB/storage/obsidian:/var/syncthing/obsidian-vault
+ - /mnt/RED_2TB/storage/obsidian-syncthing:/var/syncthing/obsidian-vault
ports:
- "22000:22000/tcp"
- "22000:22000/udp"
@@ -55,7 +59,9 @@ networks:
external: true
```
-**UID 950** = `truenas_admin` — имеет `FULL_CONTROL` ACL на `/mnt/RED_2TB/storage/obsidian`.
+**UID 950** = `truenas_admin` — имеет `FULL_CONTROL` ACL на `/mnt/RED_2TB/storage/obsidian-syncthing` (и на `/storage/obsidian`).
+
+> **Питфол (после restore 2026-08-31):** после восстановления из бэкапа и syncthing-папка (`obsidian-syncthing`), и многие `/storage/*` стали `uid 921` (`transmission`) с правами 0000 → syncthing-папка уходит в `error: stat .stfolder: permission denied` → телефонная синхронизация стоит. Фикс: вернуть `950:950` + `u+rwX,g+rwX` (см. `family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md`). Эта же поломка затрагивает `git/` и медиа-папки (см. `2026-09-02-restore-privilege-scope.md`).
## Caddy
diff --git a/family/how-to/vault-git-sync.md b/family/how-to/vault-git-sync.md
index 340a7822..20af0bfe 100644
--- a/family/how-to/vault-git-sync.md
+++ b/family/how-to/vault-git-sync.md
@@ -1,6 +1,6 @@
---
title: Vault Git Sync — Architecture & Scripts
-updated: '2026-06-20'
+updated: '2026-09-02'
type: tech
tags:
- vault
@@ -12,6 +12,10 @@ tags:
# Vault Git Sync
+> **STATE 2026-09-02:** Eagle (мой `~/obsidian`) — git-sync **СТОИТ**: `main...nas/main [ahead 55]`. Причина — поломка прав после restore: `/mnt/RED_2TB/storage/git` (bare repo `obsidian-vault.git`) → `d--------- 921 921` (uid 921 = transmission). SSH к TrueNAS живой, но `git fetch`: `fatal: ... does not appear to be a git repository`. Fix: `chown 950:950` + `chmod u+rwX,g+rwX` на `storage/git`, затем `bash ~/scripts/sync-vault.sh`. Подробно: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
+
+> **Триггер cron на маке (Eagle) — это launchd, НЕ Hermes cron:** `~/Library/LaunchAgents/com.sync-vault.plist` → `/Users/admin/scripts/sync-vault.sh`, `StartInterval=300` (5 мин). Скрипт `sync-vault-lib.sh` намеренно глушит dead remote (`git fetch` fail → `exit 0`), поэтому launchd показывает `last exit code = 0` даже при фактически стоящем sync. Системный crontab на маке пуст.
+
Obsidian vault is a bare git repo on TrueNAS:
`/mnt/RED_2TB/storage/git/obsidian-vault.git`
Also mirrored in Gitea: `https://git.mallexxx.duckdns.org/git_admin/obsidian-vault`
From 1c4d9bffd577c0570810e7b52fcd85aeb0affba3 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 11:00:07 +0600
Subject: [PATCH 59/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
---
.../2026-09-02-restore-privilege-scope.md | 63 ++++++++++++++-----
1 file changed, 47 insertions(+), 16 deletions(-)
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
index c7343d0d..79f9b2de 100644
--- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -77,26 +77,57 @@ d--------- transmission transmission storage/shared
---
-## Общий план (НЕ выполнен — требует подтверждения + бэкап ACL, единый по всем каталогам)
+## Общий план (ИСПРАВЛЕН — НЕ выполнен, требует подтверждения)
+
+> ⚠️ **2026-09-02 УТОЧНЕНО: `/mnt/RED_2TB/storage` — единый ZFS dataset `RED_2TB/storage` с `acltype=nfsv4`. НЕ ПОСIX.** Ранее планировалось `chown -R`/`chmod -R` — это **НЕВЕРНО** для TrueNAS SCALE с NFSv4 ACL. Права управляются **только** через `midclt call filesystem.setacl` (эквивалент TrueNAS UI → Datasets → `RED_2TB/storage` → Edit ACL → Apply recursively). `truenas_admin` = FULL_ADMIN в midclt → **root/sudo для setacl НЕ нужен** (проверено: `midclt call auth.me` → roles `FULL_ADMIN`).
+
+### Диагноз по NFS4 ACL (что именно сломано)
+
+Реальные ACL через `midclt call filesystem.getacl` у `storage`, `git`, `Downloads`, `Movies`, `Cartoons` **структурно ИДЕНТИЧНЫ рабочему `obsidian`**, но:
+1. владелец **uid/gid = 921** (`transmission`) вместо правильного **950** (`truenas_admin`);
+2. у записи `owner@` **обнулены `READ_DATA / WRITE_DATA / EXECUTE / APPEND_DATA / DELETE_CHILD`** (все в `false`) → POSIX-маска `d---------`.
+
+Рабочий эталон (`obsidian`): uid/gid **950:950**, `owner@` полный, `group@` rwX (без WRITE_ATTRS/ACL/OWNER), `everyone@` — только attrs.
+
+### Проверенная сигнатура midclt
```bash
-# 1. Бэкап текущих ACL всех затронутых
-getfacl -R /mnt/RED_2TB/storage > /mnt/RED_2TB/backup/_acl_storage_$(date +%F).facl
-
-# 2а. bare git repo (восстановить git-sync на маке)
-sudo chown -R 950:950 /mnt/RED_2TB/storage/git
-sudo chmod -R u+rwX,g+rwX /mnt/RED_2TB/storage/git
-# → на маке: bash ~/scripts/sync-vault.sh (55 коммитов уедут)
-
-# 2б. медиа-папки (чтобы arr/jellyfin заработал)
-sudo chown -R 950:950 /mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared,...}
-sudo chmod -R u+rwX,g+rwX /mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared,...}
-
-# 3. Запуск arr-стека
-cd /mnt/RED_2TB/docker/arr && docker compose up -d
+# Filesystem-методы существуют (40, подтверждены 2026-09-02):
+# filesystem.getacl / filesystem.setacl / filesystem.chown / filesystem.setperm
+# setacl(schema): {path, uid, gid, dacl, nfs41_flags, acltype, options}
+# options: {stripacl:bool, recursive:bool, traverse:bool, canonicalize:bool(default true),
+# validate_effective_acl:bool(default true)}
+# dacl: массив ACE {tag: owner@|group@|everyone@|USER|GROUP, id, type: ALLOW|DENY, perms, flags}
```
-> ⚠️ Перед выполнением сверить фактического владельца/права у каждого пути и какие uid-сервисы должны читать. Не менять вслепую — сначала уточнить exact mount/service scopes.
+### Применение (скрипт готов на маке: `~/fix-storage-nfs4-acl.sh`)
+
+Скрипт делает 3 этапа; применяется через `filesystem.setacl` с uid/gid + dacl + options.recursive/traverse:
+
+```bash
+# скопировать на TrueNAS и запустить (sudo не нужен):
+scp ~/fix-storage-nfs4-acl.sh truenas_admin@mallexxx.duckdns.org:/tmp/
+ssh truenas_admin@mallexxx.duckdns.org 'bash /tmp/fix-storage-nfs4-acl.sh'
+```
+
+Внутри:
+1. **Бэкап ACL** каждой папки → `/mnt/RED_2TB/backup/facl_fix_<дата>/.acl.json` (откат — применить сохранённые через `filesystem.setacl`).
+2. **Применение** к каждой папке списка (`Cartoons Downloads Edu Movies Music ada3s1 art books cartoons-series documentaries documentaries-series git nas photo_dedup_test seafile series shared singularity sonarr work`):
+ ```bash
+ midclt call filesystem.setacl \
+ '{"uid":950,"gid":950,"dacl":[<эталон-ACE obsidian>],"options":{"recursive":true,"traverse":true,"stripacl":false}}'
+ ```
+3. **Verify**: все каталоги → uid 950; тест `git --git-dir= /git/obsidian-vault.git log`.
+
+> `obsidian` / `obsidian-syncthing` в список **НЕ включены** — уже рабочие 950:950.
+
+### После фикса прав
+- **На маке**: `bash ~/scripts/sync-vault.sh` → 55 коммитов уедут в bare repo (launchd возобновит нормальную работу; см. раздел 1).
+- **Arr-стек**: `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps`.
+
+### Открытые вопросы перед запуском (подтверждение Alex)
+- Transmission-контейнер работает **как root** (`docker exec transmission id` → uid 0), поэтому права медиа для него некритичны; но данные Transmission (downloadDir `/mnt/storage/Downloads`) док описывал под `transdamon`/911. Решено ставить единообразно **950** (нужно для git/arr/serving). Если нужен и доступ transdamon/911 — добавить явный ACE `GROUP` на 911.
+- Альтернатива владельцу 950 — общая группа `nas_users` (3000, члены: truenas_admin, transmission, nas и др.). Эталон obsidian = 950.
## Связанные заметки
- [[2026-08-31-syncthing-truenas-incident]] — вызвавший инцидент + фикс syncthing
From fd3578dd8e485999afb3408efebe8ba65ab3dc47 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 11:20:13 +0600
Subject: [PATCH 60/81] [2026-09-02] eagle:
family/how-to/truenas-infrastructure.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/how-to/truenas-infrastructure.md | 2 +-
.../xray-reverse-tunnel-kraken-truenas.md | 52 +++++++++++++++++--
2 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 4516885d..df5b76e5 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -239,7 +239,7 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
| local | `:12345` SOCKS (точка входа для клиентов сети TrueNAS), проброс `12345:12345` |
| Env | `LOGLEVEL` |
-**⚠️ Текущее состояние reverse (2026-09-01):** WS-вариант не пропускал payload → решено перевести на **прямой TCP+REALITY**. REALITY-ключи: server PrivateKey `0JTjeirfviDqCr63wSnisvU7DV6SpR9_9V2l_EUHmkk`, PublicKey `GhsphQLT_0fwOvK45MHeAgG3sBQsFtw7lQfUIbrLMQU`, shortId `1451ef281caf5905`, SNI `www.cloudflare.com`. **НЕ ЗАВЕРШЕНО:** конфиги готовы и валидны, осталось пересоздать portal (stop/rm/up) + OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` + пересоздать bridge на Kraken. См. план-заметку [[family/plans/reverse-xray-3xui-kraken]].
+**✅ Reverse внедрён на TCP+REALITY (2026-09-01 выполнен) + ИСТИННЫЙ КОРЕНЬ НАЙДЕН 2026-09-02.** Portal пересоздан на TCP+REALITY (`12346:12346`), OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` добавлен, bridge на Kraken пересоздан. REALITY-ключи: server PrivateKey `0JTjeirfviDqCr63wSnisvU7DV6SpR9_9V2l_EUHmkk`, PublicKey `GhsphQLT_0fwOvK45MHeAgG3sBQsFtw7lQfUIbrLMQU`, shortId `1451ef281caf5905`, SNI `www.cloudflare.com`. **НЕ ВНЕДРЕНО (ждёт команды):** payload по-прежнему не проходит даже с фиксом #6612 → истинный корень [issue #6242](https://github.com/XTLS/Xray-core/issues/6242) (регрессия bridge в v26.5+, у нас 26.7.28). Решение: сдаунгрейд bridge до `teddysun/xray:26.4.25`. Детали: [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
### Home Assistant — детали
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index 9927186b..a8ae9876 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -1,7 +1,7 @@
---
title: Xray Reverse Tunnel — Kraken ↔ TrueNAS
created: '2026-09-01'
-updated: '2026-09-01'
+updated: '2026-09-02'
type: tech
namespace: personal
tags: [xray, reverse, kraken, truenas, tunnel, networking]
@@ -15,7 +15,7 @@ related:
# Xray Reverse Tunnel — Kraken ↔ TrueNAS
-> **Статус: КОРНЕВАЯ ПРИЧИНА НАЙДЕНА + ФИКС ПРИМЕНЁН, но блокируется недоступностью Kraken (2026-09-01).** ✅ 3x-ui развёрнут и работает. ✅ reverse portal (TrueNAS) + bridge (Kraken) на **прямом TCP+REALITY**, reverse-канал установлен. 🔑 **Найден корень проблемы payload через интернет-поиск (issue XTLS/Xray-core #6612)**: с v26.5+ у `freedom`/`direct` дефолтная политика **блокирует VLESS Reverse payload**, пока не задан явный `finalRules: [{"action":"allow"}]`. ✅ **Фикс залит** (новый bridge-конфиг с `finalRules` на Kraken, `/home/kraken/xray-reverse/config.json`). ⛔ **БЛОКЕР: Kraken недоступен по docker** — USB-HDD `sda` показывает `0B` (не поднялся), docker демон застрял в `activating`, bridge-контейнер пересоздать невозможно. Задание НЕ завершено — нужен фикс HDD/docker на Kraken.
+> **Статус: КОРНЕВАЯ ПРИЧИНА НАЙДЕНА + ФИКС #6612 ПРИМЕНЁН и НЕ ПОМОГ — настоящий корень: РЕГРЕССИЯ #6242 (bridge-side, v26.5+). НЕ ВНЕДРЕНО (2026-09-02).** ✅ 3x-ui развёрнут и работает. ✅ reverse portal (TrueNAS) + bridge (Kraken) на **прямом TCP+REALITY**, reverse-канал установлен. ✅ Kraken восстановился 2026-09-02 (диск/docker/контейнеры) — bridge перезапущен с фиксом `finalRules:allow`, но **payload по-прежнему НЕ проходит** (test → `Empty reply from server`). 🔑 **ИСТИННЫЙ КОРЕНЬ (интернет-поиск 2026-09-02, issue #6242): регрессия роутинга VLESS Reverse на BRIDGE-стороне в Xray v26.5+** (у нас 26.7.28). `finalRules` (#6612) — отдельная проблема, уже устранена, но не лечит #6242. **Решение: сдаунгрейд БРИДЖА до Xray 26.4.25** (образ `teddysun/xray:26.4.25`), portal-сторона может остаться на 26.7.28. ⛔ План согласован с Alex, выполнение ждёт команды — не трогать `vpn.mallexxx`.
> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
> ## ✅ ВАЖНО (2026-09-02): `xray-admin` (3x-ui) на TrueNAS — это ОДНОВРЕМЕННО РАБОЧИЙ САМОСТОЯТЕЛЬНЫЙ Xray-VPN-сервер
@@ -198,6 +198,50 @@ Issue говорит прямо: проблема воспроизводится
**Статус: задание НЕ завершено — остаётся применить bridge-фикс после восстановления docker на Kraken.**
+## ✅ 2026-09-02: Kraken восстановлен (диск/docker) + фикс #6612 применён — НО payload всё равно мёртв → ИСТИННЫЙ КОРЕНЬ #6242 (bridge-side v26.5+)
+
+### Kraken полностью поднялся (проверено живьём по SSH)
+- Uptime 2 мин — Kraken перезагрузился (USB-HDD-сценарий). `systemctl is-active docker` был `activating` первые минуты (ждал поднятия диска), затем стал **`active`**.
+- **Диск `/dev/sda1` поднялся**: 1.8T, смонтирован к `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f`, 235G свободно (87% занято). (В отличие от 2026-09-01, когда `sda` был `0B`.)
+- **Все контейнеры восстановились** (в прошлый раз — 0): transmission, sonarr, prowlarr, homeassistant, jellyfin, radarr, hermes-kraken, portainer, rclone, cloudflared, flaresolverr, watchtower, **xray-reverse-bridge**. Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-.../docker-data`.
+- **Bridge-конфиг содержит фикс #6612**: `/home/kraken/xray-reverse/config.json` → `freedom/direct` с `"finalRules":[{"action":"allow"}]`.
+- **Reverse-канал к TrueNAS жив**: bridge лог 2026-09-02 13:12: `dialing TCP to mallexxx.duckdns.org:12346` → `tunneling request` → `received request for udp:reverse:0`. Bridge = Xray **26.7.28** (`teddysun/xray:latest`).
+
+### Portal (TrueNAS) — состояние
+`xray-reverse-portal` Up 22h, reverse-канал от Kraken держится. Лог portal: `accepted tcp:8.6.112.0:80 [local -> reverse-out]` — запрос с локального SOCKS-входа уходит в reverse-out (в сторону Kraken). Конфиг portal без изменений (interconn :12346 REALITY client `a81c3179...` reverse-out; local :12345 SOCKS; routing `local→reverse-out`; placeholder-freedom). Сеть `caddy_default`, curl-контейнер в ней резолвит `xray-reverse-portal:12345` = `172.16.1.7`.
+
+### ❌ End-to-end payload по-прежнему НЕ проходит (даже с фиксом #6612)
+Тест временным контейнером `curlimages/curl` в сети `caddy_default` на TrueNAS:
+```
+docker run --rm --network caddy_default curlimages/curl:latest curl -sv --socks5-hostname xray-reverse-portal:12345 -m 20 http://api.ipify.org
+```
+→ verbos: `Opened SOCKS connection from 172.16.1.8 port ... to api.ipify.org port 80 (via 172.16.1.7 port 12345)` → `Request completely sent off` → **`Empty reply from server`** (соединение закрыто без ответа). Трафик доходит до portal, уходит в reverse-out, НО не возвращается от bridge → ответ теряется.
+
+### 🔑 ИСТИННЫЙ КОРЕНЬ (2026-09-02, интернет-поиск): issue XTLS/Xray-core #6242 — регрессия bridge в v26.5+
+Симптомы точно совпадают с [issue #6242](https://github.com/XTLS/Xray-core/issues/6242) (закрыта "not planned"):
+- Регрессия **только на стороне BRIDGE** (outbound-initiating), версия portal не важна.
+- С **v26.5+** (и 26.6.x) bridge стартует без ошибок, reverse-канал вроде устанавливается (`udp:reverse:0`), но **трафик не маршрутизируется через reverse tunnel**. Ровно наш случай на 26.7.28.
+- **Понижение ТОЛЬКО bridge до v26.4.25 мгновенно возвращает полную работу** без изменений конфига.
+- Связано с #5750 (BurstObservatory не успевает health-check динамически регистрируемых `rproxy-*` outbound'ов — до 10 мин) и обсуждением #5962 (stale-tunnel mappings после рестартов). PR #5101 прямо предупреждал: "reverse sub-protocol unstable, will break, кросс-версий совместимости НЕ гарантировано".
+- **Почему фикс #6612 не помог:** #6612 (`finalRules:allow`) лечит ДРУГУЮ проблему (Freedom блокирует payload) — мы её уже устранили. Но осталась регрессия маршрутизации #6242, которую `finalRules` не обходит.
+
+**Решение/кварк:** сдаунгрейд **bridge** до Xray **26.4.25** (образ `teddysun/xray:26.4.25`, существует на Docker Hub / GitHub Container Registry `ghcr.io/xtls/xray-core:26.4.25`). Portal-сторона (26.7.28) остаётся.
+
+### План внедрения (согласован с Alex — что НЕ трогать `vpn.mallexxx` и `xray-admin`)
+1. На Kraken: остановить `xray-reverse-bridge` контейнер.
+2. В `/home/kraken/xray-reverse/docker-compose.yml` сменить образ `teddysun/xray:latest` → `teddysun/xray:26.4.25`.
+3. `cd /home/kraken/xray-reverse && docker compose up -d` (пересоздаст bridge на 26.4.25).
+4. Проверить reverse-канал: bridge лог `udp:reverse:0`.
+5. Контрольный end-to-end: временный curl-контейнер в сети `caddy_default` на TrueNAS через `xray-reverse-portal:12345` → ожидаем выход с **IP Kraken** (`92.62.70.41`), НЕ IP TrueNAS.
+⛔ НЕ трогать: `xray-admin` / `vpn.mallexxx` / Caddy / portal.
+
+### ⚠️ Важное прояснение для будущих сессий (из этого диалога)
+Запрос юзера "не могу подключиться к xray на truenas до наших измерений" — это была путаница между **тремя** разными сущностями:
+1. `vless-proxy` (teddysun/xray, SOCKS :1080/:1081, outbound на удалённый VPS qentra.top) — **мёртв из-за удаления VPS** 2026-09-01.
+2. `xray-admin` (3x-ui) = **самостоятельный рабочий Xray-VPN из интернета** через `vpn.mallexxx.duckdns.org:443` (см. секцию «ВАЖНО 2026-09-02» выше). Жив, end-to-end проверен.
+3. reverse-туннель TrueNAS↔Kraken (portal `xray-reverse-portal` + bridge на Kraken) — это про проксирование egress ЛОКАЛЬНЫХ клиентов сети TrueNAS через Kraken. НЕ связан с vpn.mallexxx.
+
+
## ⏳ Открытые вопросы по reverse (требуют ответа/действия Alex)
## План реализации (история; Шаг 1 устарел — см. «ВНЕДРЕНО» выше)
@@ -231,8 +275,8 @@ Issue говорит прямо: проблема воспроизводится
1. ✅ (решено) **Проброс порта для reverse** — переведён на TCP+REALITY, ВСЁ ВЫПОЛНЕНО (portal пересоздан с REALITY, OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` добавлен, bridge на Kraken пересоздан). См. секции «ВНЕДРЕНО — TCP+REALITY» выше. **Компанент работает, но payload НЕ проходит (см. пункт 4).**
2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
-3. ✅ **Kraken docker** — РАБОТАЕТ (после перезагрузки 2026-09-01): Server 29.4.3, Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-.../docker-data` (актуальный UUID, HDD смонтирован), **30 образов сохранены, но 0 контейнеров** (all контейнеры не восстановлены после сбоя). Reverse-bridge-контейнер развёрнут поверх. НО: известные контейнеры (jellyfin, ha, transmission и т.д.) требуется пересоздать — отдельная задача, не входила в reverse-сферу.
-4. 🔑 **End-to-end payload не проходил** — **КОРЕНЬ НАЙДЕН (issue #6612)**: с v26.5+ у freedom/direct дефолт блокирует VLESS Reverse payload без явного `finalRules:[{action:"allow"}]`. **Фикс залит** на bridge-конфиг, но **не применён** — Kraken по docker недоступен (USB-HDD `sda`=0B, docker в `activating`). См. секции «КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН» и «ФИКС ПРИМЕНЁН, но заблокирован» выше. Задание не завершено — остаётся восстановить docker на Kraken и пересоздать bridge.
+3. ✅ **Kraken docker** — РАБОТАЕТ, ВОССТАНОВИЛСЯ 2026-09-02: `systemctl is-active docker` → `active`, диск `/dev/sda1 1.8T` смонтирован к `/srv/dev-disk-by-uuid-6194539b-...`, 235G свободно (87% занято). **Все контейнеры восстановились** (в отличие от 2026-09-01, когда было 0): transmission, sonarr, prowlarr, homeassistant, jellyfin, radarr, hermes-kraken, portainer, rclone, cloudflared, flaresolverr, watchtower, **xray-reverse-bridge** (Up).
+4. 🔑 **End-to-end payload НЕ проходит даже после фикса #6612** — **ИСТИННЫЙ КОРЕНЬ НАЙДЕН (2026-09-02, issue #6242)**. Несмотря на то что фикс `finalRules:[{action:"allow"}]` применён на bridge и Kraken полностью восстановлен (docker active, bridge перезапущен с фиксом, reverse-канал `udp:reverse:0` жив), payload всё равно не проходит: тест временным curl-контейнером в сети `caddy_default` через `xray-reverse-portal:12345` → verbos `Opened SOCKS connection... Empty reply from server`. **Причина: регрессия роутинга VLESS Reverse на BRIDGE-стороне в Xray v26.5+** (bridge на `teddysun/xray:latest` = 26.7.28). Issue #6242 закрыта "not planned" — фикса нет. **Решение/кварк: сдаунгрейд bridge до `teddysun/xray:26.4.25`** (образ существует на Docker Hub), portal-сторона (26.7.28) остаётся. План согласован с Alex, выполнение ждёт команды. См. секцию «✅ 2026-09-02: Kraken восстановлен + ИСТИННЫЙ КОРЕНЬ #6242» ниже.
## Связанные заметки
- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
From f81a405b854e95e2876f50e6d103f1bd9f72773f Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 11:35:19 +0600
Subject: [PATCH 61/81] [2026-09-02] eagle:
family/plans/reverse-xray-3xui-kraken.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/plans/reverse-xray-3xui-kraken.md | 31 ++++++++++
.../xray-reverse-tunnel-kraken-truenas.md | 58 +++++++++++++++++--
2 files changed, 83 insertions(+), 6 deletions(-)
diff --git a/family/plans/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md
index 855aa3dd..28105fb3 100644
--- a/family/plans/reverse-xray-3xui-kraken.md
+++ b/family/plans/reverse-xray-3xui-kraken.md
@@ -242,6 +242,37 @@ reverse_proxy @rvs xray-reverse-portal:12346
7. 🔑 **End-to-end НЕ работал** (curl через SOCKS 12345 → 000) на WS и на TCP+REALITY (flow и без flow) — **причина найдена (issue #6612)**: с v26.5+ у freedom/direct дефолт блокирует VLESS Reverse payload без явного `finalRules`. **Фикс залит** на bridge-конфиг /home/kraken/xray-reverse/config.json, но **не применён** — Kraken по docker недоступен (USB-HDD `sda`=0B, docker в `activating`). См. «КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН» и «ФИКС ПРИМЕНЁН, но заблокирован» выше. **Статус: задание НЕ завершено — нужен фикс HDD/docker Kraken + пересоздать bridge + тест.**
8. ⏳ Обновить Obsidian: truenas-infrastructure.md, kraken-access.md, vps-qentra.md. **TODO для следующей сессии.** (Основной tech-doc `personal/tech/xray-reverse-tunnel-kraken-truenas.md` уже обновлён 2026-09-01 с корнем/фиксом.)
+## ❌ ФИНАЛ СЕССИИ 2026-09-02: downgrade обеих сторон НЕ помог — reverse по-прежнему мёртв
+
+> Гипотезы выше (#6612, #6242 → «понизить bridge до 26.4.25») были **проверены на практике и НЕ решили проблему**. Это фактический итог — читать вместо старых «корень найден» теорий.
+
+### Выполнено (по согласованию, не трогая vpn.mallexxx/xray-admin)
+- **Bridge (Kraken)** `docker-compose.yml`: образ `teddysun/xray:latest` → **`teddysun/xray:26.4.25`** (бэкап `.bak-26.7.28`). Контейнер на 26.4.25 (arm64).
+- **Portal (TrueNAS)** `/mnt/RED_2TB/docker/reverse-portal/docker-compose.yml`: тоже → **`teddysun/xray:26.4.25`** (бэкап `.bak-26.7.28`). Образ 21.6MB Pull, контейнер пересоздан (amd64).
+- Portal `config.json` → `loglevel: debug` (на время диагностики).
+- Локальные копии на Mac для правки: `~/xray-test/` (`docker-compose.bridge.yml`, `docker-compose.portal.yml`, `portal.config.json`, `config.json` — тестовый xray-клиент vpn.mallexxx).
+- После смены версий bridge перезапускался для переустановки канала к новому portal.
+
+### Результат end-to-end (обе стороны 26.4.25)
+- Reverse-канал у bridge **есть**: `dialing TCP → tunneling → received request for udp:reverse:0`. Portal принимает (`received request for tcp:v1.rvs.cool:0 → dispatching to udp:reverse:0`).
+- **Payload НЕ проходит**: `curl` через `xray-reverse-portal:12345` → `Empty reply from server` / `exit=52`.
+- **Portal debug (ключевое):** `taking detour [reverse-out] for [tcp:api.ipify.org:80]` → **и ничего дальше** (ни dial, ни ошибки). Dispatch в reverse-out молча глотается. Фоново: `common/mux: failed to read metadata ... connection reset by peer` (:12346).
+- **На TrueNAS НЕТ постоянного ESTABLISHED TCP на :12346** от bridge (`ss -tn sport=:12346` пусто) → reverse-канал **не удерживается постоянным** (только эфемерные контрольные прочёты), data-stream до bridge не доходит. Bridge никогда не логирует приём reverse-in TCP-payload.
+
+### Итог
+Проблема **НЕ** регрессия bridge-v26.5+ (#6242) — downgrade до заведомо рабочей пары 26.4.25↔26.4.25 не помог. **Настоящая причина лежит глубже:** VLESS Reverse sub-protocol нестабилен (предупреждение PR #5101) и между разнесёнными docker-реализациями (Kraken arm64 ↔ TrueNAS amd64) reverse-канал не удерживается постоянным на TCP-уровне → портал не может передать данные в bridge.
+
+### Нереализованные гипотезы (на будущее, треб.WB)
+1. Убрать REALITY/camouflage с канала TrueNAS↔Kraken (это внутренний обмен между сервисами, не извне) — оставить чистый VLESS-TCP/простой шифр, исключив REALITY-handshake как источник нестабильного mux.
+2. Аудит конфига bridge — что именно заставляет xray НЕ держать постоянный единственный канал (в reverse bridge обычно держит persistent-соединение).
+3. При необходимости консультация/живой разбор рабочего egress-конфига (канон доки не дал полного рабочего варианта).
+
+### ⚠️ Текущее состояние системы (2026-09-02)
+- **Версии:** portal + bridge сейчас **обе на `teddysun/xray:26.4.25`** (исходно `:latest`=26.7.28). Откат: вернуть в compose образ `teddysun/xray:latest` → `docker compose up -d`.
+- Bridge config содержит фикс #6612 (`finalRules:allow`). Portal config на `loglevel: debug`.
+- `vpn.mallexxx` / `xray-admin` (3x-ui) / Caddy — НЕ тронуты, работают (рабочий Xray-VPN через TrueNAS, end-to-end проверен 2026-09-02).
+- **Reverse к Kraken НЕ внедрён/не работает end-to-end** — остаётся открытой задачей.
+
## Ограничения / риски
- Кра́кен в NAT ⇒ только исходящие соединения; reverse-канал держит Kraken.
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index a8ae9876..8270195d 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -4,18 +4,27 @@ created: '2026-09-01'
updated: '2026-09-02'
type: tech
namespace: personal
-tags: [xray, reverse, kraken, truenas, tunnel, networking]
+tags:
+ - xray
+ - reverse
+ - kraken
+ - truenas
+ - tunnel
+ - networking
confidence: medium
related:
- - "[[family/how-to/vps-qentra]]"
- - "[[family/how-to/truenas-infrastructure]]"
- - "[[family/how-to/kraken-access]]"
- - "[[family/how-to/rasputin-router]]"
+ - '[[family/how-to/vps-qentra]]'
+ - '[[family/how-to/truenas-infrastructure]]'
+ - '[[family/how-to/kraken-access]]'
+ - '[[family/how-to/rasputin-router]]'
+downgrade_to_26.4.25: >-
+ portal+bridge BOTH downgraded 2026-09-02 — payload still dead (корень глубже
+ #6242)
---
# Xray Reverse Tunnel — Kraken ↔ TrueNAS
-> **Статус: КОРНЕВАЯ ПРИЧИНА НАЙДЕНА + ФИКС #6612 ПРИМЕНЁН и НЕ ПОМОГ — настоящий корень: РЕГРЕССИЯ #6242 (bridge-side, v26.5+). НЕ ВНЕДРЕНО (2026-09-02).** ✅ 3x-ui развёрнут и работает. ✅ reverse portal (TrueNAS) + bridge (Kraken) на **прямом TCP+REALITY**, reverse-канал установлен. ✅ Kraken восстановился 2026-09-02 (диск/docker/контейнеры) — bridge перезапущен с фиксом `finalRules:allow`, но **payload по-прежнему НЕ проходит** (test → `Empty reply from server`). 🔑 **ИСТИННЫЙ КОРЕНЬ (интернет-поиск 2026-09-02, issue #6242): регрессия роутинга VLESS Reverse на BRIDGE-стороне в Xray v26.5+** (у нас 26.7.28). `finalRules` (#6612) — отдельная проблема, уже устранена, но не лечит #6242. **Решение: сдаунгрейд БРИДЖА до Xray 26.4.25** (образ `teddysun/xray:26.4.25`), portal-сторона может остаться на 26.7.28. ⛔ План согласован с Alex, выполнение ждёт команды — не трогать `vpn.mallexxx`.
+> **Статус: НЕ ВНЕДРЕНО (2026-09-02, финал сессии аудита).** Reverse TrueNAS↔Kraken по-прежнему НЕ работает end-to-end. **Выполнен полный downgrade обеих сторон (portal+bridge) до Xray 26.4.25 — это НЕ помогло**: payload по-прежнему мёртв (`Empty reply`/exit 52). ✅ 3x-ui (`vpn.mallexxx`) жив и работает (рабочий Xray-VPN через TrueNAS). ✅ Kraken восстановлен (диск/docker/контейнеры). ⛔ **Остаётся открытым**: reverse-канал portal↔bridge не удерживается постоянным (нет ESTABLISHED :12346), dispatch портала в reverse-out теряется. Подробности+гипотезы: см. секцию «❌ ФАКТИЧЕСКИЙ РЕЗУЛЬТАТ 2026-09-02» ниже. Не трогать `vpn.mallexxx`.
> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
> ## ✅ ВАЖНО (2026-09-02): `xray-admin` (3x-ui) на TrueNAS — это ОДНОВРЕМЕННО РАБОЧИЙ САМОСТОЯТЕЛЬНЫЙ Xray-VPN-сервер
@@ -242,6 +251,43 @@ docker run --rm --network caddy_default curlimages/curl:latest curl -sv --socks5
3. reverse-туннель TrueNAS↔Kraken (portal `xray-reverse-portal` + bridge на Kraken) — это про проксирование egress ЛОКАЛЬНЫХ клиентов сети TrueNAS через Kraken. НЕ связан с vpn.mallexxx.
+## ❌ ФАКТИЧЕСКИЙ РЕЗУЛЬТАТ 2026-09-02: downgrade НЕ помог
+
+> ⚠️ ВАЖНО: вопреки секции «ИСТИННЫЙ КОРЕНЬ #6242» выше (которая была гипотезой, ещё не проверенной полностью), **полный downgrade обеих сторон до Xray 26.4.25 payload НЕ починил**. Это зафиксировано здесь как фактический итог.
+
+### Что было сделано (выполнено по факту, alive-диагностика)
+По согласованию с Alex (не трогая `vpn.mallexxx` / `xray-admin`) выполнено:
+1. Bridge (Kraken): `/home/kraken/xray-reverse/docker-compose.yml` → образ **`teddysun/xray:26.4.25`**. Docker Pull прошёл, контейнер на 26.4.25 (`Xray 26.4.25 ... go1.26.2 linux/arm64`). Бэкап: `docker-compose.yml.bak-26.7.28`.
+2. Portal (TrueNAS): `/mnt/RED_2TB/docker/reverse-portal/docker-compose.yml` → тоже **`teddysun/xray:26.4.25`** (Linux amd64, образ 21.59MB Pull). Бэкап: `docker-compose.yml.bak-26.7.28`. Контейнер `xray-reverse-portal` пересоздан.
+3. Portal `config.json` `loglevel` поднят на `debug` (для диагностики), залит в `/mnt/RED_2TB/docker/reverse-portal/config.json`.
+4. Локальные копии файлов на Mac (для правки): `~/xray-test/` → `docker-compose.bridge.yml`, `docker-compose.portal.yml`, `portal.config.json`, `config.json` (тестовый клиент vpn.mallexxx, удалить не нужно).
+5. После смены версии bridge перезапущен (для переустановки reverse-канала к новому portal).
+
+### Результат end-to-end теста (обе стороны 26.4.25)
+- Reverse-канал у bridge **устанавливается**: лог `dialing TCP to mallexxx.duckdns.org:12346` → `tunneling request to unknown` → `received request for udp:reverse:0`. Portal принимает (лог: `proxy/vless/inbound: received request for tcp:v1.rvs.cool:0` → `dispatching request to udp:reverse:0`).
+- **НО payload по-прежнему НЕ проходит**: тест curl через `xray-reverse-portal:12345` → `Empty reply from server` / `exit=52`.
+- **Portal (TrueNAS) debug-лог полностью проясняет картину:**
+ - При запросе: `[118463829] proxy/socks: TCP Connect request to tcp:api.ipify.org:80` → `[118463829] app/dispatcher: taking detour [reverse-out] for [tcp:api.ipify.org:80]` — **и НИЧЕГО дальше** (ни ошибки, ни dial к bridge). Dispatch в `reverse-out` молча глотается.
+ - Фоновые разрывы: `common/mux: failed to read metadata > read tcp 172.16.1.7:12346->92.62.70.41:3266: connection reset by peer` — portal пытается что-то слать к bridge, но канал рвётся.
+- **Критично:** на TrueNAS **НЕТ постоянного ESTABLISHED TCP-соединения от bridge на :12346** (`ss -tn sport=:12346` пусто). Reverse-канал НЕ держится постоянным — устанавливается эфемерно (per контрольный UDP прочёт), и data-stream по нему не доходит до bridge.
+- Bridge (Kraken) никогда не логирует приём `reverse-in` TCP-payload (только контрольный `udp:reverse:0`).
+
+### Вывод
+Проблема **НЕ** регрессия #6242 фиксируемая версией bridge — иначе downgrade до рабочей по issue пары 26.4.25↔26.4.25 решил бы. После полного downgrade payload по-прежнему мёртв. **Настоящая причина: reverse-канал portal↔bridge не удерживается постоянным на TCP-уровне** (нет стабильного ESTABLISHED :12346), поэтому dispatch портала в `reverse-out` не находит живой транспорт до bridge и молча теряет данные.
+
+Это согласуется с предупреждением PR #5101: **новый VLESS Reverse sub-protocol itself нестабилен** («will break, кросс-версий совместимость не гарантирована») — по-видимому канал между двумя разнесёнными docker-реализациями (Kraken arm64 ↔ TrueNAS amd64, обе 26.4.25) не держится стабильно.
+
+### Дальнейшие гипотезы (НЕ проверены, треб.WB)
+1. Транспортный REALITY mismatch arm64(26.4.25 bridge) ↔ amd64(26.4.25 portal) даёт нестабильный mux → попробовать не-REALITY (простой TCP, т.к. канал только между сервисами TrueNAS↔Kraken, не извне) — убрать REALITY, оставить VLESS-TCP без camouflage.
+2. Перепроверить, что bridge реально удерживает единственное mux-соединение (в xray reverse bridge обычно держит persistent канал, у нас его нет → пере-аудит конфиг bridge: возможно не хватает чего-то, заставляющего держать канал постоянно).
+3. Запросить консультацию/повторно изучить рабочую конфигурацию сбора из живого разбора темы (потому что каноничные примеры из доки и issues не дали полного рабочего egress-конфига).
+
+### ⚠️ ТЕКУЩЕЕ СОСТОЯНИЕ СИСТЕМ (на момент записи, 2026-09-02)
+- **Версии:** BOTH portal и bridge сейчас на **`teddysun/xray:26.4.25`** (исходно были на `:latest` = 26.7.28). Откат при необходимости: поменять образ в compose обратно на `teddysun/xray:latest` и `docker compose up -d`.
+- Конфиг portal имеет `loglevel: debug` (пока). Bridge `config.json` содержит фикс #6612 `finalRules:allow`.
+- `vpn.mallexxx.duckdns.org` / `xray-admin` / Caddy — **НЕ тронуты**, работают (см. секцию «ВАЖНО 2026-09-02»).
+- Reverse к Kraken **НЕ внедрён/не работает end-to-end** — это остаётся открытой задачей.
+
## ⏳ Открытые вопросы по reverse (требуют ответа/действия Alex)
## План реализации (история; Шаг 1 устарел — см. «ВНЕДРЕНО» выше)
From 1090fc66bc706b21741dc5ec7443378efb9e63fd Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 12:00:28 +0600
Subject: [PATCH 62/81] [2026-09-02] eagle:
personal/tech/xray-reverse-tunnel-kraken-truenas.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-20260902-1154
---
.../xray-reverse-tunnel-kraken-truenas.md | 70 +++-
...tunnel-kraken-truenas.md.bak-20260902-1154 | 332 ++++++++++++++++++
2 files changed, 395 insertions(+), 7 deletions(-)
create mode 100644 personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-20260902-1154
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index 8270195d..53cbd362 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -11,20 +11,22 @@ tags:
- truenas
- tunnel
- networking
-confidence: medium
+confidence: high
related:
- '[[family/how-to/vps-qentra]]'
- '[[family/how-to/truenas-infrastructure]]'
- '[[family/how-to/kraken-access]]'
- '[[family/how-to/rasputin-router]]'
downgrade_to_26.4.25: >-
- portal+bridge BOTH downgraded 2026-09-02 — payload still dead (корень глубже
- #6242)
+ portal+bridge BOTH pinned to 26.4.25; required but insufficient by itself.
+verified_fix_2026_09_02: >-
+ Kraken bridge must use Docker network_mode: host; Docker bridge/NAT reset the
+ VLESS reverse mux. REALITY+Vision verified end-to-end with Kraken egress.
---
# Xray Reverse Tunnel — Kraken ↔ TrueNAS
-> **Статус: НЕ ВНЕДРЕНО (2026-09-02, финал сессии аудита).** Reverse TrueNAS↔Kraken по-прежнему НЕ работает end-to-end. **Выполнен полный downgrade обеих сторон (portal+bridge) до Xray 26.4.25 — это НЕ помогло**: payload по-прежнему мёртв (`Empty reply`/exit 52). ✅ 3x-ui (`vpn.mallexxx`) жив и работает (рабочий Xray-VPN через TrueNAS). ✅ Kraken восстановлен (диск/docker/контейнеры). ⛔ **Остаётся открытым**: reverse-канал portal↔bridge не удерживается постоянным (нет ESTABLISHED :12346), dispatch портала в reverse-out теряется. Подробности+гипотезы: см. секцию «❌ ФАКТИЧЕСКИЙ РЕЗУЛЬТАТ 2026-09-02» ниже. Не трогать `vpn.mallexxx`.
+> **Статус: ✅ ВНЕДРЕНО И ПРОВЕРЕНО END-TO-END (2026-09-02).** TrueNAS SOCKS `:12345` → VLESS Reverse TCP+REALITY+Vision → Kraken → internet работает. HTTP и HTTPS тесты оба вернули выходной IP Kraken `92.62.70.41`. **Истинная причина:** Docker bridge/NAT на Kraken сбрасывал reverse mux; bridge должен работать с `network_mode: host`. Более ранние секции со статусом «не работает» и гипотезами #6612/#6242 сохранены ниже как история диагностики и суперсидированы секцией «Верифицированный фикс».
> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
> ## ✅ ВАЖНО (2026-09-02): `xray-admin` (3x-ui) на TrueNAS — это ОДНОВРЕМЕННО РАБОЧИЙ САМОСТОЯТЕЛЬНЫЙ Xray-VPN-сервер
@@ -32,7 +34,7 @@ downgrade_to_26.4.25: >-
> - **Проверено end-to-end 2026-09-02** (временный xray-клиент с Mac): VLESS+WS через `vpn.mallexxx.duckdns.org:443` → HTTP 200, выход IP `90.189.160.148` = TrueNAS. Контейнеры `xray-admin` (Up 21h) + `caddy` (Up 20h) живы.
> - Inbound `in-10095-tcp` (VLESS-WS): client id `ce320965-6956-4759-84bb-7cb71cfc6252` (user1), `security: none`, path `/vless`, host `vpn.mallexxx.duckdns.org`. TLS терминирует Caddy → в клиенте `network: ws` + TLS на домен, **без** двойного TLS внутрь.
> - Полные параметры и pitfalls (вкл. удаление `allowInsecure` в Xray 26.7.28) — в `family/how-to/truenas-infrastructure.md`, раздел `xray-admin`.
-> - То есть reverse к Kraken — это ОТДЕЛЬНЫЙ механизм (для проксирования локальных клиентов сети TrueNAS через Kraken-egress); он по-прежнему НЕ внедрён. Сам по себе 3x-ui-сервер уже даёт рабочий VPN-выход через TrueNAS.
+> - Reverse к Kraken — это ОТДЕЛЬНЫЙ механизм (для проксирования локальных клиентов сети TrueNAS через Kraken-egress). Он теперь также внедрён и проверен; см. «Верифицированный фикс». 3x-ui-сервер остаётся независимым рабочим VPN-выходом через TrueNAS.
## Контекст / Почему
@@ -251,7 +253,7 @@ docker run --rm --network caddy_default curlimages/curl:latest curl -sv --socks5
3. reverse-туннель TrueNAS↔Kraken (portal `xray-reverse-portal` + bridge на Kraken) — это про проксирование egress ЛОКАЛЬНЫХ клиентов сети TrueNAS через Kraken. НЕ связан с vpn.mallexxx.
-## ❌ ФАКТИЧЕСКИЙ РЕЗУЛЬТАТ 2026-09-02: downgrade НЕ помог
+## ❌ ИСТОРИЯ ДИАГНОСТИКИ 2026-09-02: downgrade сам по себе не помог
> ⚠️ ВАЖНО: вопреки секции «ИСТИННЫЙ КОРЕНЬ #6242» выше (которая была гипотезой, ещё не проверенной полностью), **полный downgrade обеих сторон до Xray 26.4.25 payload НЕ починил**. Это зафиксировано здесь как фактический итог.
@@ -322,7 +324,61 @@ docker run --rm --network caddy_default curlimages/curl:latest curl -sv --socks5
1. ✅ (решено) **Проброс порта для reverse** — переведён на TCP+REALITY, ВСЁ ВЫПОЛНЕНО (portal пересоздан с REALITY, OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` добавлен, bridge на Kraken пересоздан). См. секции «ВНЕДРЕНО — TCP+REALITY» выше. **Компанент работает, но payload НЕ проходит (см. пункт 4).**
2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
3. ✅ **Kraken docker** — РАБОТАЕТ, ВОССТАНОВИЛСЯ 2026-09-02: `systemctl is-active docker` → `active`, диск `/dev/sda1 1.8T` смонтирован к `/srv/dev-disk-by-uuid-6194539b-...`, 235G свободно (87% занято). **Все контейнеры восстановились** (в отличие от 2026-09-01, когда было 0): transmission, sonarr, prowlarr, homeassistant, jellyfin, radarr, hermes-kraken, portainer, rclone, cloudflared, flaresolverr, watchtower, **xray-reverse-bridge** (Up).
-4. 🔑 **End-to-end payload НЕ проходит даже после фикса #6612** — **ИСТИННЫЙ КОРЕНЬ НАЙДЕН (2026-09-02, issue #6242)**. Несмотря на то что фикс `finalRules:[{action:"allow"}]` применён на bridge и Kraken полностью восстановлен (docker active, bridge перезапущен с фиксом, reverse-канал `udp:reverse:0` жив), payload всё равно не проходит: тест временным curl-контейнером в сети `caddy_default` через `xray-reverse-portal:12345` → verbos `Opened SOCKS connection... Empty reply from server`. **Причина: регрессия роутинга VLESS Reverse на BRIDGE-стороне в Xray v26.5+** (bridge на `teddysun/xray:latest` = 26.7.28). Issue #6242 закрыта "not planned" — фикса нет. **Решение/кварк: сдаунгрейд bridge до `teddysun/xray:26.4.25`** (образ существует на Docker Hub), portal-сторона (26.7.28) остаётся. План согласован с Alex, выполнение ждёт команды. См. секцию «✅ 2026-09-02: Kraken восстановлен + ИСТИННЫЙ КОРЕНЬ #6242» ниже.
+4. ✅ **End-to-end payload проходит.** #6612/#6242 оказались неполными гипотезами. Верифицированный корень — Docker bridge/NAT на Kraken; решение — Xray 26.4.25 + `network_mode: host` на bridge. См. authoritative-секцию ниже.
+
+## ✅ Верифицированный фикс 2026-09-02 (authoritative)
+
+> Эта секция суперсидирует все более ранние выводы «payload мёртв», «нет persistent ESTABLISHED» и предполагаемые корни #6612/#6242. Фикс найден и проверен живыми A/B-тестами.
+
+### Истинная причина
+
+`xray-reverse-bridge` на Kraken работал в обычной Docker bridge-сети. Docker bridge/NAT на этом узле сбрасывал long-lived VLESS reverse mux после успешной регистрации `udp:reverse:0`. Поэтому portal создавал `reverse-out`, но payload не доходил до `reverse-in`.
+
+Ключевой A/B-тест:
+
+1. Тот же JSON на temporary bridge amd64 внутри TrueNAS заработал сразу → portal/config/routing исправны.
+2. Тот же Xray 26.4.25 arm64 + тот же JSON на Kraken в `--network host` заработал сразу → CPU architecture и WAN исправны, отличие только в Docker network mode.
+3. После возврата REALITY+Vision туннель остался рабочим → проблема не в REALITY, Vision или TCP transport.
+
+### Постоянная конфигурация
+
+Kraken: `/home/kraken/xray-reverse/docker-compose.yml`
+
+```yaml
+services:
+ xray-reverse-bridge:
+ image: teddysun/xray:26.4.25
+ network_mode: host
+```
+
+Bridge config возвращён к защищённому VLESS TCP + REALITY + `flow: xtls-rprx-vision`. `direct` сохраняет `finalRules: [{"action":"allow"}]`. Portal на TrueNAS также на `teddysun/xray:26.4.25`, REALITY+Vision. `xray-admin`, Caddy и `vpn.mallexxx.duckdns.org` не менялись.
+
+`network_mode: host` расширяет сетевые права bridge-контейнера. В этом конфиге у него нет физических listening inbounds; `reverse-in` — виртуальный inbound Xray. Не добавлять в этот контейнер обычные inbounds без аудита bind address/firewall.
+
+### Финальная проверка
+
+2026-09-02 после permanent Compose deploy:
+
+- `docker inspect xray-reverse-bridge` → image `teddysun/xray:26.4.25`, network `host`, status `running`, restart count `0`.
+- Bridge лог: `received request for udp:reverse:0`, затем TCP payload принят в `reverse-in -> direct`.
+- HTTPS через SOCKS `xray-reverse-portal:12345` → `92.62.70.41`.
+- HTTP через тот же SOCKS → `92.62.70.41`.
+- `92.62.70.41` — публичный egress Kraken; TrueNAS egress `90.189.160.148` не использовался.
+
+### Бэкапы и rollback
+
+Kraken:
+
+- `/home/kraken/xray-reverse/docker-compose.yml.bak-bridge-network-20260902-1147`
+- `/home/kraken/xray-reverse/config.json.bak-plain-host-working-20260902-1146`
+- `/home/kraken/xray-reverse/config.json.bak-reality-vision-20260902-1142`
+
+TrueNAS:
+
+- `/mnt/RED_2TB/docker/reverse-portal/config.json.bak-plain-host-working-20260902-1146`
+- `/mnt/RED_2TB/docker/reverse-portal/config.json.bak-reality-vision-20260902-1142`
+
+Rollback network fix: вернуть compose-бэкап на Kraken и выполнить `docker compose up -d`. Это вернёт нерабочий Docker bridge mode, поэтому делать только для диагностики/отката.
## Связанные заметки
- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-20260902-1154 b/personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-20260902-1154
new file mode 100644
index 00000000..8270195d
--- /dev/null
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-20260902-1154
@@ -0,0 +1,332 @@
+---
+title: Xray Reverse Tunnel — Kraken ↔ TrueNAS
+created: '2026-09-01'
+updated: '2026-09-02'
+type: tech
+namespace: personal
+tags:
+ - xray
+ - reverse
+ - kraken
+ - truenas
+ - tunnel
+ - networking
+confidence: medium
+related:
+ - '[[family/how-to/vps-qentra]]'
+ - '[[family/how-to/truenas-infrastructure]]'
+ - '[[family/how-to/kraken-access]]'
+ - '[[family/how-to/rasputin-router]]'
+downgrade_to_26.4.25: >-
+ portal+bridge BOTH downgraded 2026-09-02 — payload still dead (корень глубже
+ #6242)
+---
+
+# Xray Reverse Tunnel — Kraken ↔ TrueNAS
+
+> **Статус: НЕ ВНЕДРЕНО (2026-09-02, финал сессии аудита).** Reverse TrueNAS↔Kraken по-прежнему НЕ работает end-to-end. **Выполнен полный downgrade обеих сторон (portal+bridge) до Xray 26.4.25 — это НЕ помогло**: payload по-прежнему мёртв (`Empty reply`/exit 52). ✅ 3x-ui (`vpn.mallexxx`) жив и работает (рабочий Xray-VPN через TrueNAS). ✅ Kraken восстановлен (диск/docker/контейнеры). ⛔ **Остаётся открытым**: reverse-канал portal↔bridge не удерживается постоянным (нет ESTABLISHED :12346), dispatch портала в reverse-out теряется. Подробности+гипотезы: см. секцию «❌ ФАКТИЧЕСКИЙ РЕЗУЛЬТАТ 2026-09-02» ниже. Не трогать `vpn.mallexxx`.
+> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
+
+> ## ✅ ВАЖНО (2026-09-02): `xray-admin` (3x-ui) на TrueNAS — это ОДНОВРЕМЕННО РАБОЧИЙ САМОСТОЯТЕЛЬНЫЙ Xray-VPN-сервер
+> Путаница в вопросе юзера выявила: `vpn.mallexxx.duckdns.org:443` — это **не только reverse-frontend**, а полноценный Xray-сервер со **своим собственным выходом в интернет** (outbound `freedom`/`direct` → сразу через TrueNAS), НЕ зависящий от reverse-канала к Kraken. Это **отдельная фича**, параллельная reverse-туннелю.
+> - **Проверено end-to-end 2026-09-02** (временный xray-клиент с Mac): VLESS+WS через `vpn.mallexxx.duckdns.org:443` → HTTP 200, выход IP `90.189.160.148` = TrueNAS. Контейнеры `xray-admin` (Up 21h) + `caddy` (Up 20h) живы.
+> - Inbound `in-10095-tcp` (VLESS-WS): client id `ce320965-6956-4759-84bb-7cb71cfc6252` (user1), `security: none`, path `/vless`, host `vpn.mallexxx.duckdns.org`. TLS терминирует Caddy → в клиенте `network: ws` + TLS на домен, **без** двойного TLS внутрь.
+> - Полные параметры и pitfalls (вкл. удаление `allowInsecure` в Xray 26.7.28) — в `family/how-to/truenas-infrastructure.md`, раздел `xray-admin`.
+> - То есть reverse к Kraken — это ОТДЕЛЬНЫЙ механизм (для проксирования локальных клиентов сети TrueNAS через Kraken-egress); он по-прежнему НЕ внедрён. Сам по себе 3x-ui-сервер уже даёт рабочий VPN-выход через TrueNAS.
+
+## Контекст / Почему
+
+- **VPS qentra.top (91.207.28.205) УДАЛЁН** — вся прежняя Xray-инфраструктура на нём (VLESS+REALITY, OpenVPN, WebSocket v.qentra.top) больше не существует.
+- Нужен новый путь для выхода локальных клиентов сети TrueNAS в интернет через другую точку выхода (Kraken).
+- Белый IP есть только у **TrueNAS** (`mallexxx.duckdns.org` = `90.189.160.148`).
+- **Kraken за NAT**, входящие не принимает — только исходящие соединения.
+
+## Принятая схема
+
+```
+Локальные клиенты (сеть TrueNAS, 192.168.2.x)
+ │ шлют трафик на локальный прокси TrueNAS (SOCKS 1080 / HTTP 1081)
+ ▼
+TrueNAS ←── контроллер / bridge (белый IP, mallexxx.duckdns.org), принимает
+ │ TrueNAS заворачивает трафик в reverse-канал
+ ▼ ▲
+Kraken ──┘ держит ИСХОДЯЩИЙ reverse-канал к TrueNAS, отпускает трафик → интернет
+ ▼
+интернет
+```
+
+**Тип решения: Xray `reverse`.** Роли по Xray:
+
+| Узел | Роль | Держит канал? | Выход в интернет? |
+|------|------|---------------|-------------------|
+| **TrueNAS** | bridge/контроллер (inbound `reverse`) | ❌ слушает | ❌ |
+| **Kraken** | outbound reverse-нода + exit | ✅ **исходящий** к TrueNAS | ✅ **да** |
+| **Локальные клиенты** | используют TrueNAS как локальный прокси-шлюз | — | через Kraken |
+
+**Ключевое физическое ограничение:** Kraken за NAT не может принимать входящих. Поэтому канал держит **Kraken исходящим** к TrueNAS. Для передачи клиентского трафика TrueNAS → Kraken используются конструкции `dokodemo-door` + `reverse` + policy-маршрутизация (routing rules). Это чуть сложнее "поставить reverse и всё", конфиг нужен аккуратный.
+
+## ⚠️ Факты, проверенные 2026-09-01 (важно, ломают прежние допущения)
+
+1. **На 443 у TrueNAS НЕ Xray.** Проверено `openssl s_client`: на `mallexxx.duckdns.org:443` отвечает **обычный TLS 1.3 с валидным Let's Encrypt сертификатом для `mallexxx.duckdns.org`** (`issuer=Let's Encrypt/CN=YE2`). Это **Caddy/nginx reverse-proxy**, НЕ Xray REALITY (у REALITY сертификат был бы чужой/поддельный). → Твоя память «на 443 висел xray server» не подтверждается.
+2. **`vless-proxy` на TrueNAS** (контейнер `teddysun/xray:latest`, Up 7 дней):
+ - inbound: SOCKS `0.0.0.0:1080`, HTTP `0.0.0.0:1081` (локально в LAN TrueNAS)
+ - outbound: VLESS+WS+TLS на `v.qentra.top:443`, path `/qentra`, id `2D9F24C4-21FE-4784-9843-F11C384DA67A` — **указывает на УДАЛЁННЫЙ VPS → сейчас мёртвый**.
+ - ⚠️ Используется Hermes-Taiga как SOCKS5-прокси (`vless-proxy:1080`) → **Hermes-Taiga сейчас без рабочего прокси**.
+3. **Kraken→TrueNAS по `mallexxx.duckdns.org`:** открыты **только 22 (SSH) и 443 (HTTPS/TLS)**. `8443/8964/8080/9000` — closed. Порт 443 — TLS (Caddy), не сырой.
+4. **Kraken:** Docker установлен (`/usr/bin/docker`), но демон отвечает медленно/рвёт SSH (проверка `docker ps` не завершилась за 40s). Xray на Kraken **отсутствует** (`which xray` пусто) — нужен Docker-контейнер.
+5. **Связанность Kraken↔TrueNAS по 22/443 — подтверждена** (оба OPEN с Kraken).
+
+## ✅ ВНЕДРЕНО 2026-09-01: 3x-ui на TrueNAS (фронтенд + Docker Compose)
+
+**Решение Alex:** использовать **3x-ui frontend** + **Docker Compose** (управление клиентами через панель), а не ручной Caddy-pass-through с сырым Xray. Это изменило исходный план реализации (Шаг 1 ниже устарел по способу).
+
+### Развёрнут контейнер `xray-admin` (TrueNAS)
+- **Имя контейнера:** `xray-admin` (строго, т.к. Caddyfile резолвит `xray-admin` по имени в docker-сети).
+- **Образ:** `ghcr.io/mhsanaei/3x-ui:latest` (**3.7.0**, Xray **26.7.28**).
+- **Сеть:** `caddy_default` (external) — там же Caddy, чтобы резолвить имя.
+- **Volume:** `/mnt/RED_2TB/docker/xray-admin:/etc/x-ui` (туда лёг существующий `x-ui.db`).
+- **Порт панели:** хостовый `54321 → 54321` (внутри панель реально слушает **2053** — Caddy проксирует `vpn-panel.mallexxx.duckdns.org` → `xray-admin:2053`).
+- **Compose-файл:** `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml` (deployed версия с `TZ=Asia/Novosibirsk`, `PUID/PGID=950`; см. актуальный файл на TrueNAS — отличается от черновика в плане).
+- **Панель доступна:** `https://vpn-panel.mallexxx.duckdns.org/` → **HTTP 200**.
+- **Sub-сервер:** `[::]:443` (path `/sub`), панель `[::]:2053`, inbound `vless-ws:10095` (path `/vless`, host `vpn.mallexxx.duckdns.org`).
+
+### Почему это заработало «из коробки» (важно)
+База `/mnt/RED_2TB/docker/xray-admin/x-ui.db` **уже была настроена под эту Caddy-интеграцию** (раньше 3x-ui уже задумывался на TrueNAS): inbound `vless-ws` port 10095 tag `in-10095-tcp`, subDomain `vpn-panel.mallexxx.duckdns.org`, subPort 443, subScheme https. Caddyfile уже содержал (мёртвые до подъёма) блоки:
+- `vpn.mallexxx.duckdns.org` → `@ws path /vless` → `xray-admin:10095` (WS)
+- `vpn-panel.mallexxx.duckdns.org` → `@sub path /sub/*` → `xray-admin:443` + fallback `xray-admin:2053`
+- LE-сертификаты для этих поддоменов уже выданы.
+
+### 3x-ui поддерживает reverse
+В бинарнике `/app/x-ui` присутствует **`clientReverseTags`** → reverse-настройка реализуема через панель 3x-ui (3.7.0).
+
+### Бэкап (сделан до изменений)
+`/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/` — содержит: `caddy/Caddyfile` (копия из контейнера, 90 строк), `xray-admin/x-ui.db`, `docker-inventory.txt`.
+
+### Мелочь/питфолл
+Скопированный в volume `docker-compose.yml` оказался внутри контейнера `/etc/x-ui/` (т.к. volume смонтирован туда) — удалить лишний файл из контейнера: `docker exec xray-admin rm -f /etc/x-ui/docker-compose.yml`.
+
+## ✅ ВНЕДРЕНО 2026-09-01: Reverse portal (TrueNAS) + bridge (Kraken) — VLESS-WS
+
+После 3x-ui развёрнут полноценный reverse. **Ключевое открытие:** Xray **26.x заменил legacy reverse на "VLESS Reverse Proxy"** — старая схема `reverse.bridges/portals` (Xray-examples/ReverseProxy) **не работает** в этой версии (ошибка `The feature "legacy reverse" has been removed and migrated to "VLESS Reverse Proxy"`). Новая схема: `reverse.tag` в VLESS-клиентах/аутбаундах (примеры — `xtls.github.io/en/document/level-2/vless_reverse.html`, use case "Home Broadband Egress").
+
+### Роли (новая терминология VLESS Reverse)
+- **Internal device (Kraken) = BRIDGE**: VLESS-**outbound** с `"reverse":{"tag":"reverse-in"}` → активно устанавливает канал к TrueNAS; на своей стороне создаётся виртуальный **inbound** `reverse-in`, чей трафик направляется в freedom (интернет).
+- **Public server (TrueNAS) = PORTAL**: VLESS-**inbound** с `"reverse":{"tag":"reverse-out"}` у клиента → создаёт **routable-маутбаунд** `reverse-out`; локальные клиенты (SOCKS) маршрутизируются в `reverse-out`.
+- `reverse.tag` на двух сторонах **не обязаны совпадать** — соответствие через общий UUID соединения.
+
+### TrueNAS — `xray-reverse-portal` (папка `/mnt/RED_2TB/docker/reverse-portal/`)
+compose: image `teddysun/xray:latest` (26.7.28), сеть `caddy_default`, volume `config.json:/etc/xray/config.json:ro`, проброс host **`12345:12345`**.
+`config.json`:
+- inbound `interconn` (:12346, VLESS, ws path `/rvs`, host `vpn.mallexxx.duckdns.org`, client id `a81c3179-312b-4c5e-8a46-6bf3c30c2941`, `"reverse":{"tag":"reverse-out"}`)
+- inbound `local` (:12345, SOCKS `noauth udp:true`) — точка входа для клиентов сети TrueNAS
+- routing: `inboundTag:["local"] → outboundTag:"reverse-out"`
+- outbounds: `freedom` (placeholder — обязателен, иначе `reverse-out` станет default и весь трафик уйдёт в reverse)
+- Порт 12346 НЕ проброшен наружу — к нему обращается Caddy по имени `xray-reverse-portal:12346` в docker-сети.
+
+### TrueNAS — Caddyfile (bind mount `/mnt/RED_2TB/docker/caddy/Caddyfile`)
+Добавлен маршрут в блок `vpn.mallexxx.duckdns.org`:
+```caddy
+@rvs path /rvs
+reverse_proxy @rvs xray-reverse-portal:12346
+```
+Питфоллы: **Caddyfile нельзя править `docker cp`** в контейнер (volume → `device or resource busy`) — править на хосте через `docker run --rm -v /:/host alpine cp /host/tmp/...`. Валидация `docker exec caddy caddy validate --config /tmp/Caddyfile.new` → "Valid configuration". Перезапуск `docker restart caddy` безопасен. Бэкап: `Caddyfile.pre-reverse`.
+
+### Kraken — `xray-reverse-bridge` (папка `/home/kraken/xray-reverse/`)
+compose: image `teddysun/xray:latest`, volume `config.json:/etc/xray/config.json:ro`.
+`config.json`:
+- outbound `direct` = `freedom` + `"finalRules":[{"action":"allow","network":"tcp,udp","ip":["0.0.0.0/0"]}]` (интернет-egress Kraken)
+- outbound `conn` = **VLESS упрощённого стиля** (`settings.address/port/id/encryption/reverse` — НЕ `vnext[]`!) → `vpn.mallexxx.duckdns.org:443` ws `/rvs` `"reverse":{"tag":"reverse-in"}`, security tls, tlsSettings.serverName `vpn.mallexxx.duckdns.org`
+- routing: `inboundTag:["reverse-in"] → outboundTag:"direct"`
+- **Питфолл:** VLESS-outbound с `reverse` должен быть упрощённого стиля. Если через `vnext[]/users[]` → Xray 26.x: `VLESS users: please use simplified outbound's config style to use "reverse"`.
+- `loglevel debug` (для диагностики).
+
+### Reverse-канал: ✅ установлен
+- Kraken `netstat`: `172.24.0.2:... → 90.189.160.148:443 ESTABLISHED`
+- Portal: `::ffff:172.16.1.7:12346 ::ffff:172.16.1.3:52616 ESTABLISHED` (172.16.1.3 = Caddy)
+- Bridge logs: `common/mux: received request for udp:reverse:0`
+
+### ❌ End-to-end payload НЕ работает
+- `curl --socks5 127.0.0.1:12345 http://api.ipify.org` на TrueNAS → `code=000` (прямой curl → 200).
+- Portal логи: `accepted tcp:8.47.69.0:80 [local -> reverse-out]` — запрос ушёл в reverse-out.
+- **Bridge НЕ логирует received для этого TCP-payload** — данные теряются между reverse-out (portal) и reverse-in (bridge).
+- Kraken имеет прямой интернет (`api.ipify` → `92.62.70.41`), проблема не в egress.
+
+### ✅ ВНЕДРЕНО (2026-09-01) — TCP+REALITY переход, все шаги выполнены
+WS-транспорт не пропускал reverse-payload; переведён на **прямой TCP + REALITY** (Alex согласовал проброс). Все 4 шага ВЫПОЛНЕНЫ:
+1. ✅ Пересоздан `xray-reverse-portal` на TrueNAS (compose `12345:12345` + `12346:12346`, config REALITY TCP). Проверено `ss -tlnp` → `0.0.0.0:12346 LISTEN`.
+2. ✅ OpenWrt DNAT добавлен: redirect `xray-reverse-12346`, `src_dport=12346` → `dest_ip=192.168.2.197:12346`, `target=DNAT`, `/etc/init.d/firewall reload` → `DNAT_ADDED`. Проверено: `mallexxx.duckdns.org:12346` → **OPEN** извне.
+3. ✅ Пересоздан `xray-reverse-bridge` на Kraken (config REALITY TCP). Reverse-канал ESTABLISHED: Kraken `172.24.0.2 → 90.189.160.148:12346` + `common/mux: received request for udp:reverse:0`.
+4. ❌ **Тест payload НЕ прошёл даже на TCP+REALITY**: `curl --socks5 127.0.0.1:12345 http://api.ipify.org` → **`code=000`**. Portal: `accepted tcp:8.6.112.0:80 [local -> reverse-out]`, но bridge НЕ логирует приход TCP-payload.
+
+### ❌ Дополнительно пробовано: снятие `flow: xtls-rprx-vision`
+Убрал `flow: xtls-rprx-vision` с обеих сторон (portal interconn client + bridge conn outbound), пересоздал контейнеры (оба `Configuration OK`). Результат: **по-прежнему `code=000`**. Reverse-канал устанавливается, TCP-payload до bridge не доходит.
+
+**Вывод:** проблема НЕ в `flow` и НЕ в WS. **Даже на «каноничном» TCP+REALITY payload от portal-reverse-out до bridge-reverse-in не передаётся** (bridge не получает inbound-TCP). Гипотеза про WS **опровергнута**.
+
+### 🔑 КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН (2026-09-01, интернет-поиск) — issue XTLS/Xray-core #6612
+
+**Симптомы точно совпадают с #6612:** reverse-канал устанавливается (`udp:reverse:0` на месте), но TCP-payload не проходит; portal логирует `accepted tcp:... -> reverse-out`, а bridge НЕ получает входящих reverse-in-соединений; curl → 000.
+
+**Причина:** начиная с **Xray v26.5+** (PR #6027) у outbound `freedom`/`direct` появилась **дефолтная политика безопасности**, которая **блокирует ВСЕ цели для трафика VLESS Reverse**, если не задан явный `finalRules`. Контрольный reverse-канал при этом продолжает устанавливаться — поэтому «всё выглядит подключённым», но реальный payload молча отбрасывается. Это ровно наш случай на версии **26.7.28**.
+
+**Точный фикс** — добавить в freedom `direct` на **BRIDGE** (место реального egress из reverse-in в интернет):
+```json
+{
+ "protocol": "freedom",
+ "tag": "direct",
+ "settings": {
+ "domainStrategy": "AsIs",
+ "finalRules": [ { "action": "allow" } ]
+ }
+}
+```
+Issue говорит прямо: проблема воспроизводится с обычным Freedom без finalRules вообще, и добавление безусловного `allow` чинит на 26.7.28.
+
+**Релевантные ссылки:**
+- **#6612** — главный фикс: `https://github.com/XTLS/Xray-core/issues/6612`
+- **#6027** — корень (finalRules у Direct/Freedom): `https://github.com/XTLS/Xray-core/pull/6027`
+- Документация freedom: `https://xtls.github.io/en/config/outbounds/freedom.html`
+- 3x-ui разбор под reverse: `https://github.com/MHSanaei/3x-ui/issues/4782`
+- **#2664** — если finalRules не поможет: `flow: xtls-rprx-vision` на BRIDGE-outbound + REALITY может ломать доставку (держите `flow:""` на reverse-клиенте): `https://github.com/XTLS/Xray-core/issues/2664`
+- **Регрессия роутинга reverse v26.5+** (если фикс не поможет): #6242, #6195, #6248 — временный кварк: пинить **v26.4.25**.
+
+**Важные детали из поиска (проверить при продолжении):**
+- Портал ДОЛЖЕН иметь placeholder-freedom (у нас есть) — иначе reverse-out станет default. Плейсхолдеру не нужен `finalRules:allow` (он обслуживает только несопоставленный трафик).
+- Роутинг `inboundTag:["reverse-in"] -> direct` на bridge — нужен (у нас есть).
+- У VLESS outbound поле `encryption` обязательно (`"none"` или парный PQ-ключ к серверному `decryption`) — у нас `"none"`, ок.
+
+### ✅ ФИКС ПРИМЕНЁН, но заблокирован (2026-09-01)
+
+Новый bridge-конфиг с `finalRules:[{action:"allow"}]` в `direct` **залит** на Kraken (`/home/kraken/xray-reverse/config.json`, 1098 байт, проверено `cat | head -5`). НО:
+- ⛔ **Kraken по docker НЕДОСТУПЕН**: `systemctl is-active docker` → **`activating`** (не `active`), демон застрял, накоплено множество зависших docker-процессов (ps/run/compose).
+- **Причина: USB-HDD снова не поднялся** — `lsblk /dev/sda` → **`0B`** (без раздела). Docker data-root = `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker-data` (на этом HDD) → daemon ждёт диск и застревает.
+- ⚠️ **Замечено расхождение UUID**: сейчас data-root указывает на `/srv/dev-disk-by-uuid-49e8f586-...`, а в контексте ранее фигурировал смонтированный HDD `/srv/dev-disk-by-uuid-6194539b-...` (sda1 1.8T). Текущий `sda` = 0B без раздела.
+- **`docker compose down/up` для bridge не выполнен** — контейнер всё ещё со старым конфигом (больше не деплоится, daemon не отвечает).
+
+**Шаг для продолжения (требует подтверждения Alex):** починить HDD/docker на Kraken (повторить утренний сценарий: `sudo reboot` или физическое переподключение USB-диска), затем пересоздать bridge-контейнер (`cd /home/kraken/xray-reverse && docker compose down && docker compose up -d`), затем тест `curl --socks5 127.0.0.1:12345 http://api.ipify.org` на TrueNAS → ожидаем выход с IP Kraken.
+
+**Статус: задание НЕ завершено — остаётся применить bridge-фикс после восстановления docker на Kraken.**
+
+## ✅ 2026-09-02: Kraken восстановлен (диск/docker) + фикс #6612 применён — НО payload всё равно мёртв → ИСТИННЫЙ КОРЕНЬ #6242 (bridge-side v26.5+)
+
+### Kraken полностью поднялся (проверено живьём по SSH)
+- Uptime 2 мин — Kraken перезагрузился (USB-HDD-сценарий). `systemctl is-active docker` был `activating` первые минуты (ждал поднятия диска), затем стал **`active`**.
+- **Диск `/dev/sda1` поднялся**: 1.8T, смонтирован к `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f`, 235G свободно (87% занято). (В отличие от 2026-09-01, когда `sda` был `0B`.)
+- **Все контейнеры восстановились** (в прошлый раз — 0): transmission, sonarr, prowlarr, homeassistant, jellyfin, radarr, hermes-kraken, portainer, rclone, cloudflared, flaresolverr, watchtower, **xray-reverse-bridge**. Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-.../docker-data`.
+- **Bridge-конфиг содержит фикс #6612**: `/home/kraken/xray-reverse/config.json` → `freedom/direct` с `"finalRules":[{"action":"allow"}]`.
+- **Reverse-канал к TrueNAS жив**: bridge лог 2026-09-02 13:12: `dialing TCP to mallexxx.duckdns.org:12346` → `tunneling request` → `received request for udp:reverse:0`. Bridge = Xray **26.7.28** (`teddysun/xray:latest`).
+
+### Portal (TrueNAS) — состояние
+`xray-reverse-portal` Up 22h, reverse-канал от Kraken держится. Лог portal: `accepted tcp:8.6.112.0:80 [local -> reverse-out]` — запрос с локального SOCKS-входа уходит в reverse-out (в сторону Kraken). Конфиг portal без изменений (interconn :12346 REALITY client `a81c3179...` reverse-out; local :12345 SOCKS; routing `local→reverse-out`; placeholder-freedom). Сеть `caddy_default`, curl-контейнер в ней резолвит `xray-reverse-portal:12345` = `172.16.1.7`.
+
+### ❌ End-to-end payload по-прежнему НЕ проходит (даже с фиксом #6612)
+Тест временным контейнером `curlimages/curl` в сети `caddy_default` на TrueNAS:
+```
+docker run --rm --network caddy_default curlimages/curl:latest curl -sv --socks5-hostname xray-reverse-portal:12345 -m 20 http://api.ipify.org
+```
+→ verbos: `Opened SOCKS connection from 172.16.1.8 port ... to api.ipify.org port 80 (via 172.16.1.7 port 12345)` → `Request completely sent off` → **`Empty reply from server`** (соединение закрыто без ответа). Трафик доходит до portal, уходит в reverse-out, НО не возвращается от bridge → ответ теряется.
+
+### 🔑 ИСТИННЫЙ КОРЕНЬ (2026-09-02, интернет-поиск): issue XTLS/Xray-core #6242 — регрессия bridge в v26.5+
+Симптомы точно совпадают с [issue #6242](https://github.com/XTLS/Xray-core/issues/6242) (закрыта "not planned"):
+- Регрессия **только на стороне BRIDGE** (outbound-initiating), версия portal не важна.
+- С **v26.5+** (и 26.6.x) bridge стартует без ошибок, reverse-канал вроде устанавливается (`udp:reverse:0`), но **трафик не маршрутизируется через reverse tunnel**. Ровно наш случай на 26.7.28.
+- **Понижение ТОЛЬКО bridge до v26.4.25 мгновенно возвращает полную работу** без изменений конфига.
+- Связано с #5750 (BurstObservatory не успевает health-check динамически регистрируемых `rproxy-*` outbound'ов — до 10 мин) и обсуждением #5962 (stale-tunnel mappings после рестартов). PR #5101 прямо предупреждал: "reverse sub-protocol unstable, will break, кросс-версий совместимости НЕ гарантировано".
+- **Почему фикс #6612 не помог:** #6612 (`finalRules:allow`) лечит ДРУГУЮ проблему (Freedom блокирует payload) — мы её уже устранили. Но осталась регрессия маршрутизации #6242, которую `finalRules` не обходит.
+
+**Решение/кварк:** сдаунгрейд **bridge** до Xray **26.4.25** (образ `teddysun/xray:26.4.25`, существует на Docker Hub / GitHub Container Registry `ghcr.io/xtls/xray-core:26.4.25`). Portal-сторона (26.7.28) остаётся.
+
+### План внедрения (согласован с Alex — что НЕ трогать `vpn.mallexxx` и `xray-admin`)
+1. На Kraken: остановить `xray-reverse-bridge` контейнер.
+2. В `/home/kraken/xray-reverse/docker-compose.yml` сменить образ `teddysun/xray:latest` → `teddysun/xray:26.4.25`.
+3. `cd /home/kraken/xray-reverse && docker compose up -d` (пересоздаст bridge на 26.4.25).
+4. Проверить reverse-канал: bridge лог `udp:reverse:0`.
+5. Контрольный end-to-end: временный curl-контейнер в сети `caddy_default` на TrueNAS через `xray-reverse-portal:12345` → ожидаем выход с **IP Kraken** (`92.62.70.41`), НЕ IP TrueNAS.
+⛔ НЕ трогать: `xray-admin` / `vpn.mallexxx` / Caddy / portal.
+
+### ⚠️ Важное прояснение для будущих сессий (из этого диалога)
+Запрос юзера "не могу подключиться к xray на truenas до наших измерений" — это была путаница между **тремя** разными сущностями:
+1. `vless-proxy` (teddysun/xray, SOCKS :1080/:1081, outbound на удалённый VPS qentra.top) — **мёртв из-за удаления VPS** 2026-09-01.
+2. `xray-admin` (3x-ui) = **самостоятельный рабочий Xray-VPN из интернета** через `vpn.mallexxx.duckdns.org:443` (см. секцию «ВАЖНО 2026-09-02» выше). Жив, end-to-end проверен.
+3. reverse-туннель TrueNAS↔Kraken (portal `xray-reverse-portal` + bridge на Kraken) — это про проксирование egress ЛОКАЛЬНЫХ клиентов сети TrueNAS через Kraken. НЕ связан с vpn.mallexxx.
+
+
+## ❌ ФАКТИЧЕСКИЙ РЕЗУЛЬТАТ 2026-09-02: downgrade НЕ помог
+
+> ⚠️ ВАЖНО: вопреки секции «ИСТИННЫЙ КОРЕНЬ #6242» выше (которая была гипотезой, ещё не проверенной полностью), **полный downgrade обеих сторон до Xray 26.4.25 payload НЕ починил**. Это зафиксировано здесь как фактический итог.
+
+### Что было сделано (выполнено по факту, alive-диагностика)
+По согласованию с Alex (не трогая `vpn.mallexxx` / `xray-admin`) выполнено:
+1. Bridge (Kraken): `/home/kraken/xray-reverse/docker-compose.yml` → образ **`teddysun/xray:26.4.25`**. Docker Pull прошёл, контейнер на 26.4.25 (`Xray 26.4.25 ... go1.26.2 linux/arm64`). Бэкап: `docker-compose.yml.bak-26.7.28`.
+2. Portal (TrueNAS): `/mnt/RED_2TB/docker/reverse-portal/docker-compose.yml` → тоже **`teddysun/xray:26.4.25`** (Linux amd64, образ 21.59MB Pull). Бэкап: `docker-compose.yml.bak-26.7.28`. Контейнер `xray-reverse-portal` пересоздан.
+3. Portal `config.json` `loglevel` поднят на `debug` (для диагностики), залит в `/mnt/RED_2TB/docker/reverse-portal/config.json`.
+4. Локальные копии файлов на Mac (для правки): `~/xray-test/` → `docker-compose.bridge.yml`, `docker-compose.portal.yml`, `portal.config.json`, `config.json` (тестовый клиент vpn.mallexxx, удалить не нужно).
+5. После смены версии bridge перезапущен (для переустановки reverse-канала к новому portal).
+
+### Результат end-to-end теста (обе стороны 26.4.25)
+- Reverse-канал у bridge **устанавливается**: лог `dialing TCP to mallexxx.duckdns.org:12346` → `tunneling request to unknown` → `received request for udp:reverse:0`. Portal принимает (лог: `proxy/vless/inbound: received request for tcp:v1.rvs.cool:0` → `dispatching request to udp:reverse:0`).
+- **НО payload по-прежнему НЕ проходит**: тест curl через `xray-reverse-portal:12345` → `Empty reply from server` / `exit=52`.
+- **Portal (TrueNAS) debug-лог полностью проясняет картину:**
+ - При запросе: `[118463829] proxy/socks: TCP Connect request to tcp:api.ipify.org:80` → `[118463829] app/dispatcher: taking detour [reverse-out] for [tcp:api.ipify.org:80]` — **и НИЧЕГО дальше** (ни ошибки, ни dial к bridge). Dispatch в `reverse-out` молча глотается.
+ - Фоновые разрывы: `common/mux: failed to read metadata > read tcp 172.16.1.7:12346->92.62.70.41:3266: connection reset by peer` — portal пытается что-то слать к bridge, но канал рвётся.
+- **Критично:** на TrueNAS **НЕТ постоянного ESTABLISHED TCP-соединения от bridge на :12346** (`ss -tn sport=:12346` пусто). Reverse-канал НЕ держится постоянным — устанавливается эфемерно (per контрольный UDP прочёт), и data-stream по нему не доходит до bridge.
+- Bridge (Kraken) никогда не логирует приём `reverse-in` TCP-payload (только контрольный `udp:reverse:0`).
+
+### Вывод
+Проблема **НЕ** регрессия #6242 фиксируемая версией bridge — иначе downgrade до рабочей по issue пары 26.4.25↔26.4.25 решил бы. После полного downgrade payload по-прежнему мёртв. **Настоящая причина: reverse-канал portal↔bridge не удерживается постоянным на TCP-уровне** (нет стабильного ESTABLISHED :12346), поэтому dispatch портала в `reverse-out` не находит живой транспорт до bridge и молча теряет данные.
+
+Это согласуется с предупреждением PR #5101: **новый VLESS Reverse sub-protocol itself нестабилен** («will break, кросс-версий совместимость не гарантирована») — по-видимому канал между двумя разнесёнными docker-реализациями (Kraken arm64 ↔ TrueNAS amd64, обе 26.4.25) не держится стабильно.
+
+### Дальнейшие гипотезы (НЕ проверены, треб.WB)
+1. Транспортный REALITY mismatch arm64(26.4.25 bridge) ↔ amd64(26.4.25 portal) даёт нестабильный mux → попробовать не-REALITY (простой TCP, т.к. канал только между сервисами TrueNAS↔Kraken, не извне) — убрать REALITY, оставить VLESS-TCP без camouflage.
+2. Перепроверить, что bridge реально удерживает единственное mux-соединение (в xray reverse bridge обычно держит persistent канал, у нас его нет → пере-аудит конфиг bridge: возможно не хватает чего-то, заставляющего держать канал постоянно).
+3. Запросить консультацию/повторно изучить рабочую конфигурацию сбора из живого разбора темы (потому что каноничные примеры из доки и issues не дали полного рабочего egress-конфига).
+
+### ⚠️ ТЕКУЩЕЕ СОСТОЯНИЕ СИСТЕМ (на момент записи, 2026-09-02)
+- **Версии:** BOTH portal и bridge сейчас на **`teddysun/xray:26.4.25`** (исходно были на `:latest` = 26.7.28). Откат при необходимости: поменять образ в compose обратно на `teddysun/xray:latest` и `docker compose up -d`.
+- Конфиг portal имеет `loglevel: debug` (пока). Bridge `config.json` содержит фикс #6612 `finalRules:allow`.
+- `vpn.mallexxx.duckdns.org` / `xray-admin` / Caddy — **НЕ тронуты**, работают (см. секцию «ВАЖНО 2026-09-02»).
+- Reverse к Kraken **НЕ внедрён/не работает end-to-end** — это остаётся открытой задачей.
+
+## ⏳ Открытые вопросы по reverse (требуют ответа/действия Alex)
+
+## План реализации (история; Шаг 1 устарел — см. «ВНЕДРЕНО» выше)
+
+### Шаг 1 — TrueNAS: Xray reverse-server поверх Caddy
+- **Способ (рекомендован): Caddy TLS-pass-through.** Добавить в Caddy TCP-правило: поддомен `xray.mallexxx.duckdns.org:443` → внутренний порт Xray-контейнера (напр. `localhost:8443`). Caddy передаёт сырой TLS-стрим дальше. Xray на TrueNAS принимает VLESS+Reality/TLS. Kraken ходит на `xray.mallexxx.duckdns.org:443`.
+ - Плюс: переиспользуем уже открытый/проброшенный 443 на роутере, не трогаем роутер, туннель маскируется под HTTPS.
+ - Альтернатива: пробросить отдельный порт на роутере (напр. 8443) → требует доступа к роутеру. Менее предпочтительно.
+- Новый/доразвернутый Xray-контейнер:
+ - inbound `reverse`/`dokodemo-door` на 8443 (извне по pass-through)
+ - local inbound SOCKS/HTTP (уже есть 1080/1081)
+ - routing: трафик клиентов → в reverse-канал к Kraken
+
+### Шаг 2 — TrueNAS: бэкап перед изменением
+- Снять копию текущего конфига `vless-proxy` (config.json), в `/mnt/RED_2TB/docker//backup/` (паттерн как с cups-splix).
+
+### Шаг 3 — Kraken: Xray outbound reverse в Docker
+- Контейнер Xray на Kraken (`docker run --restart unless-stopped`, как остальные).
+- outbound vless → `xray.mallexxx.duckdns.org:443` (через Caddy pass-through).
+- inbound SOCKS/HTTP на Kraken — точка выхода.
+- reverse-конфиг: Kraken регистрирует «сервис» (весь трафик через dokodemo-door) наружу через канал к TrueNAS.
+
+### Шаг 4 — Проверка связности
+- Kraken → `xray.mallexxx.duckdns.org:443` (TCP).
+- Локальный клиент → TrueNAS:1080 → curl через прокси → должен выйти с IP Kraken.
+
+### Шаг 5 — Обновить Obsidian после внедрения
+- Дополнить `family/how-to/truenas-infrastructure.md` (Xray reverse-настройка), `family/how-to/kraken-access.md`; обновить `personal/tech/vps-qentra.md` (VPS удалён, ссылки на него).
+
+## Открытые вопросы (требуют ответа Alex)
+
+1. ✅ (решено) **Проброс порта для reverse** — переведён на TCP+REALITY, ВСЁ ВЫПОЛНЕНО (portal пересоздан с REALITY, OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` добавлен, bridge на Kraken пересоздан). См. секции «ВНЕДРЕНО — TCP+REALITY» выше. **Компанент работает, но payload НЕ проходит (см. пункт 4).**
+2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
+3. ✅ **Kraken docker** — РАБОТАЕТ, ВОССТАНОВИЛСЯ 2026-09-02: `systemctl is-active docker` → `active`, диск `/dev/sda1 1.8T` смонтирован к `/srv/dev-disk-by-uuid-6194539b-...`, 235G свободно (87% занято). **Все контейнеры восстановились** (в отличие от 2026-09-01, когда было 0): transmission, sonarr, prowlarr, homeassistant, jellyfin, radarr, hermes-kraken, portainer, rclone, cloudflared, flaresolverr, watchtower, **xray-reverse-bridge** (Up).
+4. 🔑 **End-to-end payload НЕ проходит даже после фикса #6612** — **ИСТИННЫЙ КОРЕНЬ НАЙДЕН (2026-09-02, issue #6242)**. Несмотря на то что фикс `finalRules:[{action:"allow"}]` применён на bridge и Kraken полностью восстановлен (docker active, bridge перезапущен с фиксом, reverse-канал `udp:reverse:0` жив), payload всё равно не проходит: тест временным curl-контейнером в сети `caddy_default` через `xray-reverse-portal:12345` → verbos `Opened SOCKS connection... Empty reply from server`. **Причина: регрессия роутинга VLESS Reverse на BRIDGE-стороне в Xray v26.5+** (bridge на `teddysun/xray:latest` = 26.7.28). Issue #6242 закрыта "not planned" — фикса нет. **Решение/кварк: сдаунгрейд bridge до `teddysun/xray:26.4.25`** (образ существует на Docker Hub), portal-сторона (26.7.28) остаётся. План согласован с Alex, выполнение ждёт команды. См. секцию «✅ 2026-09-02: Kraken восстановлен + ИСТИННЫЙ КОРЕНЬ #6242» ниже.
+
+## Связанные заметки
+- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
+- [[family/how-to/truenas-infrastructure]] — TrueNAS Caddy/контейнеры
+- [[family/how-to/kraken-access]] — Docker Kraken
+- [[family/how-to/rasputin-router]] — прежний VLESS-туннель на VPS (тоже требует пересмотра, VPS нет)
+- [[family/how-to/wireguard-vpn]] — WG Eagle↔VPS↔Kraken (VPS-звено теперь мёртво)
From 33fc830532904f2bcb8b2e5a7567888572e4069d Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 12:05:30 +0600
Subject: [PATCH 63/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
family/how-to/arr-stack-taiga.md family/how-to/obsidian-sync.md
family/how-to/truenas-infrastructure.md family/how-to/vault-git-sync.md
---
.../2026-09-02-restore-privilege-scope.md | 59 ++++++++++++++++++-
family/how-to/arr-stack-taiga.md | 4 ++
family/how-to/obsidian-sync.md | 2 +-
family/how-to/truenas-infrastructure.md | 6 +-
family/how-to/vault-git-sync.md | 8 ++-
5 files changed, 74 insertions(+), 5 deletions(-)
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
index 79f9b2de..e3ca1e97 100644
--- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -1,7 +1,8 @@
---
tags: [truenas, restore, incident, git, media, arr, permission]
created: 2026-09-02
-status: diagnosed
+updated: 2026-09-02
+status: in-progress (бэкап сделан; правка transmission compose на PUID=950 ждёт подтверждения Alex)
---
# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
@@ -129,6 +130,62 @@ ssh truenas_admin@mallexxx.duckdns.org 'bash /tmp/fix-storage-nfs4-acl.sh'
- Transmission-контейнер работает **как root** (`docker exec transmission id` → uid 0), поэтому права медиа для него некритичны; но данные Transmission (downloadDir `/mnt/storage/Downloads`) док описывал под `transdamon`/911. Решено ставить единообразно **950** (нужно для git/arr/serving). Если нужен и доступ transdamon/911 — добавить явный ACE `GROUP` на 911.
- Альтернатива владельцу 950 — общая группа `nas_users` (3000, члены: truenas_admin, transmission, nas и др.). Эталон obsidian = 950.
+---
+
+## 5. ⭐ РЕШЕНИЕ от 2026-09-02: ВАРИАНТ А — единый PUID/PGID=950 + рекурсивный фикс прав
+
+**Контекст:** вопрос «будут ли файлы, созданные transmission, читаться jellyfin?» вскрыл, что простой смены владельца верхних папок на 950 **НЕДОСТАТОЧНО** — нужно согласовать межсервисную читаемость контента + фикс вглубь файлов. Выбран **Вариант A** (одобрен Alex, стоп-правило: root-команды выполняет Alex, обязательный бэкап перед изменениями).
+
+### Факты, на которых строится решение
+- **Все linuxserver-образы загружены локально на TrueNAS** (проверено `docker images`): `radarr`, `sonarr`, `jellyfin`, `transmission`, `prowlarr`. Контейнеры (кроме transmission) **ещё не созданы** из них.
+- **Проверен механизм PUID/PGID** в linuxserver: Entrypoint=`[/init]`, есть `/etc/s6-overlay` (s6-based). → `PUID`/`PGID` обрабатываются штатно s6-stage2-hook. Нативный механизм.
+- **Дефолт без PUID:** у текущего transmission `PUID` не задан → контейнер работает **root** (подтверждено `docker exec transmission id` → root). Медиа-образы без явного PUID разъехались бы по разным uid → не видят файлы друг друга.
+- **Масштаб поломки вглубь** (подсчёт через transmission-root, `find -printf '%u'`):
+ - `Movies`: **257 908** файлов, из них **257 751 → uid 921** (+157 → 950).
+ - `Downloads`: mix 518→911, 380→921, 7→1001.
+ - `series`: 94 → 921; `Music`: 8484; `git`(bare): 3430; `work`: 26180; `Cartoons`: 1009.
+ - Глубокие `.mkv`/`.avi`/`.iso` тоже `921:921 mode 0000` — поломка НЕ только каталоги, а каждый inode.
+
+### Целевая модель Варианта А
+Все медиа/arr/jellyfin контейнеры работают под **единым uid 950:950** (`truenas_admin`, как obsidian-эталон), тогда transmission→(radarr→jellyfin) взаимно читают:
+- transmission (станет 950) качает → файлы 950
+- radarr/sonarr (950) импортируют → 950
+- jellyfin (950) читает → ✅
+
+### Этапы (рабочий статус на 2026-09-02)
+- ✅ **Этап 0 — БЭКАП выполнен** → `/home/truenas_admin/variantA_bk/`:
+ `transmission.docker-compose.yml` (оригинал), `arr.docker-compose.yml` (оригинал), `transmission_settings.json` (2982 б), ACL топ-папок (Cartoons Downloads Movies Music git work series books shared) + README.txt.
+ > ⚠️ **Питфолл бэкапа:** `/mnt/RED_2TB/backup` — root-owned (`drwxrwxr-x root root`), `truenas_admin` туда **писать не может** → бэкап сделан в домашку `~truenas_admin`. Файл `docker/transmission/docker-compose.yml` на хосте владеет uid 911 (`drwx------ 911 911`) → прочитан/скопирован **через root-контейнер** `docker exec transmission cat /config/docker-compose.yml`.
+- ⏸ **Этап 1 — правка compose на PUID/PGID=950.** Новая версия transmission compose подготовлена (`~/transmission-new-compose.yml` на маке = `/tmp/transmission-new-compose.yml` на хосте), добавлено `PUID=950` + `PGID=950`. Шаг `docker cp` в `/config/docker-compose.yml` **заблокирован ожиданием подтверждения Alex** (правка живого контейнера) — НЕ выполнен, файл НЕ перезаписан. Лежит в `/tmp/transmission-new-compose.yml`.
+- ⬜ **Этап 2 — пересоздать transmission под 950** (root/влияние на рабочий сервис → выполняет Alex):
+ ```bash
+ cd /mnt/RED_2TB/docker/transmission && docker compose up -d # пересоздаст с PUID=950
+ docker exec transmission id # ожидаем uid=950
+ ```
+- ⬜ **Этап 3 — рекурсивный фикс прав вглубь** (root → выполняет Alex, масштаб ~257k+ файлов): рекурсивно вернуть владельца 950:950 + снять 0000 на файлах/папках (`find ... -exec chown -R 950:950 {} +`, `chmod u+rw,g+r` для файлов, `u+rwX,g+rwX` для каталогов). Или через UI `Apply recursively`.
+
+### Точное содержание transmission compose (оригинал, важно для пересоздания)
+```yaml
+services:
+ transmission:
+ image: lscr.io/linuxserver/transmission:latest
+ container_name: transmission
+ restart: unless-stopped
+ environment:
+ - TZ=Asia/Novosibirsk
+ - USER=transmission_admin
+ - PASS=E$%p3En4a%R6
+ - WHITELIST=172.16.*.*
+ volumes:
+ - /mnt/RED_2TB/storage:/mnt/storage
+ - /mnt/RED_2TB/docker/transmission:/config
+ ports:
+ - 9091:9091
+ - 51413:51413
+ - 51413:51413/udp
+```
+(в compose НЕ было PUID/PGID → контейнер root; transmission монтирует `storage` → `/mnt/storage`, download-dir `/mnt/storage/Downloads`)
+
## Связанные заметки
- [[2026-08-31-syncthing-truenas-incident]] — вызвавший инцидент + фикс syncthing
- [[arr-stack-taiga]] — acquisition-стек на TrueNAS
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index 959165d0..ad4a3b04 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -17,6 +17,10 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
>
> **Почему не запускается просто так — медиа-права сломаны** (тот же restore-инцидент uid 921/0000): `/mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared}` → `d---------`/`drwx------`, owner `transmission` (uid 921). radarr/sonarr монтируют `/storage` (rw), jellyfin `:ro` — со сломанными правами не прочитают библиотеку. Фикс + запуск: см. `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
+> **Docker-image facts (2026-09-02):** All 5 media images **loaded locally** on TrueNAS (`docker images`): `lscr.io/linuxserver/{radarr,sonarr,jellyfin,transmission,prowlarr}:latest`. Only **transmission** container exists (running). **jellyfin/radarr/sonarr/prowlarr containers are NOT yet created** — they never get launched until perms fixed.
+> **Transmission runs as ROOT** (not 911/950): its compose `/mnt/RED_2TB/docker/transmission/docker-compose.yml` has **no `PUID`/`PGID`**, so linuxserver `/init` (s6-overlay) leaves it root (`docker exec transmission id` → uid 0). This is why it works on the 0000 media dirs. It mounts `storage` → `/mnt/storage`, download-dir = `/mnt/storage/Downloads`.
+> **Fix direction (approved Variant A):** set `PUID=950 PGID=950` on transmission + all arr/jellyfin compose services so every service shares uid `950` and mutually reads files; then recursively fix perms in depth. Plan + commands: `2026-09-02-restore-privilege-scope.md` §5.
+
## Notes from Setup (2026-05-20)
- Config and pitfalls recorded during initial Taiga arr stack deployment
diff --git a/family/how-to/obsidian-sync.md b/family/how-to/obsidian-sync.md
index 5d890e96..f57a3be6 100644
--- a/family/how-to/obsidian-sync.md
+++ b/family/how-to/obsidian-sync.md
@@ -34,7 +34,7 @@ mallexxx.duckdns.org:/mnt/RED_2TB/storage/git/obsidian-vault.git
| Узел | Vault путь | Scope | Скрипт | Крон |
|------|-----------|-------|--------|------|
-| Eagle | `~/obsidian` | full vault | `~/scripts/sync-vault.sh` | `0 * * * *` (Hermes cron job) |
+| Eagle | `~/obsidian` | full vault | `~/scripts/sync-vault.sh` | **launchd `com.sync-vault`** (5 мин; ⚠️ НЕ Hermes cron — см. `vault-git-sync.md` §Scripts) |
| Kraken | `~/obsidian` | sparse: `personal/ family/` | `~/scripts/sync-vault.sh` | `0 * * * *` (crontab) |
| Taiga | `/mnt/RED_2TB/docker/hermes/vault` | sparse: `personal/ family/` | `~/sync-vault.sh` (на хосте!) | `0 * * * *` (Hermes cron job) |
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index df5b76e5..3e2ea34c 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -169,10 +169,12 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
6. **Проверка:** `docker ps`. HA: `docker exec homeassistant python -m homeassistant --script check_config --config /config`.
### Transmission — детали
-- **Config:** `/mnt/RED_2TB/docker/transmission/` (монтируется как `/config`)
+- **Config:** `/mnt/RED_2TB/docker/transmission/` (монтируется как `/config`; внутри `/config/docker-compose.yml`)
- **Данные:** `/mnt/RED_2TB/storage` (монтируется как `/mnt/storage`)
- **Download dir:** `/mnt/storage/Downloads`
-- **UID/GID:** 911/911 (пользователь `transdamon`)
+- **UID/GID:** ⚠️ **по факту работает как ROOT (uid 0)**, НЕ 911/911. Причина: в `/mnt/RED_2TB/docker/transmission/docker-compose.yml` **НЕ задан `PUID`/`PGID`** → linuxserver `/init` (s6-overlay) оставляет контейнер root (подтверждено `docker exec transmission id` → root). Пользователь `transdamon`/911 (док) — это владелец конфига на хосте (`drwx------ 911 911`), но НЕ процесс внутри контейнера.
+ - **Зачем важно:** root-трансмишен пишет файлы как root и переживает права 0000 медиа → это причина, почему он "работает" на сломанных после restore папках. Но для pipeline с jellyfin/radarr (не-root) root-создаваемые файлы барьер.
+ - **План фикса (Вариант A, одобрен 2026-09-02):** добавить `PUID=950 PGID=950` и пересоздать контейнер → работает под `truenas_admin`. План: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md` §5.
- **RPC whitelist:** `172.16.6.*`
- **Web:** https://transmission.mallexxx.duckdns.org
- **Порты:** 9091 (RPC/web), 51413 (TCP/UDP peers)
diff --git a/family/how-to/vault-git-sync.md b/family/how-to/vault-git-sync.md
index 20af0bfe..12fc7419 100644
--- a/family/how-to/vault-git-sync.md
+++ b/family/how-to/vault-git-sync.md
@@ -54,10 +54,16 @@ No stash. No GIT_DIR/GIT_WORK_TREE workarounds.
| Host | Script | Type | Trigger |
|--------|-------------------------------|---------------|----------------------|
-| Eagle | `~/scripts/sync-vault.sh` | full clone | Hermes cron */5 |
+| Eagle | `~/scripts/sync-vault.sh` | full clone | **launchd** `com.sync-vault` (⚠️ НЕ Hermes cron) `StartInterval=300` |
| Kraken | `~/scripts/sync-vault.sh` | sparse clone | host crontab */5 |
| Taiga | `/opt/data/sync-vault.sh` | 3-phase orch. | Hermes cron */5 |
+> **⭐ УТОЧНЕНО 2026-09-02 (Eagle/мак sync-механизм):** автосинка на маке запускается **НЕ Hermes cron, а launchd-агентом** `~/Library/LaunchAgents/com.sync-vault.plist`:
+> - Программа: `/Users/admin/scripts/sync-vault.sh`, `StartInterval = 300` сек (5 мин)
+> - `launchctl list` → активен, `runs = 3762`, `last exit code = 0`
+> - ⚠️ **`sync-vault-lib.sh` умышленно глушит недоступность remote**: если `git fetch` падает → логирует `Fetch failed (...)` и `exit 0`. Поэтому launchd показывает `exit code 0`, даже когда sync фактически сломан (см. случай ahead 55 при сломанном bare repo после restore). Это **не** отсутствие cron и не сбой — диагностировать нужно по `git status` (ahead count), а не по exit-code агента.
+> - Системный crontab и `/etc/crontab` на маке отсутствуют.
+
All clients share a common library: `sync-vault-lib.sh` (same dir as sync-vault.sh).
Source of truth for scripts: `~/Developer/vault-sync-test/scripts/` on Eagle.
From 48b4fd2cbd46f58b3d7d892db678b07c14498199 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 12:10:31 +0600
Subject: [PATCH 64/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
---
.../2026-09-02-restore-privilege-scope.md | 27 ++++++++++++++-----
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
index e3ca1e97..59e1278c 100644
--- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -2,7 +2,7 @@
tags: [truenas, restore, incident, git, media, arr, permission]
created: 2026-09-02
updated: 2026-09-02
-status: in-progress (бэкап сделан; правка transmission compose на PUID=950 ждёт подтверждения Alex)
+status: in-progress (Этап 1 done — оба compose на PUID=950 правлены; Этап 2 заблокирован: transmission без compose-лейблов, нужен rm -f — ждёт ок Alex)
---
# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
@@ -156,12 +156,25 @@ ssh truenas_admin@mallexxx.duckdns.org 'bash /tmp/fix-storage-nfs4-acl.sh'
- ✅ **Этап 0 — БЭКАП выполнен** → `/home/truenas_admin/variantA_bk/`:
`transmission.docker-compose.yml` (оригинал), `arr.docker-compose.yml` (оригинал), `transmission_settings.json` (2982 б), ACL топ-папок (Cartoons Downloads Movies Music git work series books shared) + README.txt.
> ⚠️ **Питфолл бэкапа:** `/mnt/RED_2TB/backup` — root-owned (`drwxrwxr-x root root`), `truenas_admin` туда **писать не может** → бэкап сделан в домашку `~truenas_admin`. Файл `docker/transmission/docker-compose.yml` на хосте владеет uid 911 (`drwx------ 911 911`) → прочитан/скопирован **через root-контейнер** `docker exec transmission cat /config/docker-compose.yml`.
-- ⏸ **Этап 1 — правка compose на PUID/PGID=950.** Новая версия transmission compose подготовлена (`~/transmission-new-compose.yml` на маке = `/tmp/transmission-new-compose.yml` на хосте), добавлено `PUID=950` + `PGID=950`. Шаг `docker cp` в `/config/docker-compose.yml` **заблокирован ожиданием подтверждения Alex** (правка живого контейнера) — НЕ выполнен, файл НЕ перезаписан. Лежит в `/tmp/transmission-new-compose.yml`.
-- ⬜ **Этап 2 — пересоздать transmission под 950** (root/влияние на рабочий сервис → выполняет Alex):
- ```bash
- cd /mnt/RED_2TB/docker/transmission && docker compose up -d # пересоздаст с PUID=950
- docker exec transmission id # ожидаем uid=950
- ```
+- ✅ **Этап 1 — правка compose на PUID/PGID=950 ВЫПОЛНЕНА (2026-09-02):**
+ - **transmission compose:** `/config/docker-compose.yml` перезаписан через `docker cp /tmp/transmission-new-compose.yml transmission:/config/docker-compose.yml`. Добавлено `PUID=950` + `PGID=950`. Оригинал в `variantA_bk`. (Скрипт-источник на маке: `~/transmission-new-compose.yml`.)
+ - **arr compose:** `/mnt/RED_2TB/docker/arr/docker-compose.yml` перезаписан (владелец truenas_admin → запись прошла без root): во всех 4 сервисах (prowlarr/radarr/sonarr/jellyfin) добавлено `PUID=950` + `PGID=950` (у всех уже был `TZ`). Проверено `docker compose config --quiet` → `COMPOSE_VALID`. Источник: `~/arr-new-compose.yml`. Оригинал в `variantA_bk`.
+ - **Arr compose НЕ имеет секции `networks` у сервисов** → при подъёме будет дефолт-сеть `_default`. Consciously оставлено (оригинал не задавал networks у radarr/sonarr/jellyfin явно, только глобально `media_net`/`caddy_default`; НЕ переопределено чтобы не менять поведение без надобности).
+- ⚠️ **Этап 2 — ПЕРЕСОЗДАНИЕ transmission — ЗАБЛОКИРОВАНО (ключевой технический питфолл, 2026-09-02):**
+ - `cd /mnt/RED_2TB/docker/transmission` → **`permission denied`** (каталог `drwx------ 911 911` — владелец transdamon/911, truenas_admin нет доступа).
+ - Попытка `docker compose -f /tmp/transmission-new-compose.yml up -d` → создал сеть `tmp_default` и упал: **name conflict** (`container "transmission" already in use`).
+ - Полный рабочий контейнер пересоздать из compose **невозможно** по двум причинам:
+ 1. Контейнер создан **без compose-лейблов** (`com.docker.compose.project = `) → compose его **не распознаёт как свой** → не может мигрировать/пересоздать, только name-conflict.
+ 2. Композ-файл в каталоге 911 недоступен для работы оттуда.
+ - **Найденное решение:** удалить старый контейнер и поднять заново с корректным проектом (сохранит сеть `transmission_default`):
+ ```bash
+ docker rm -f transmission # данные в /config и /mnt/storage НЕ теряются (bind-mount на диск)
+ docker compose -f /tmp/transmission-new-compose.yml -p transmission up -d
+ docker exec transmission id # ожидаем uid=950
+ ```
+ - **Почему PUID нельзя применить к работающему контейнеру?** env зашивается при `docker run` (создании). `docker update` меняет ТОЛЬКО ресурсы (CPU/RAM/restart), **НЕ переменные окружения** — ограничение Docker. Пересоздание — единственный способ.
+ - **Альтернатива без пересоздания (если не хотим трогать transmission root):** оставить root; НО тогда новые скачанные файлы будут `root:root`, и radarr/jellyfin (под 950) их не прочитают. → PUID=950 у transmission всё же нужен для цепочки. (Обход с setgid-битом на dir + group 950 возможен как fallback, не выбран.)
+ - **Статус:** ждёт ок Alex на `docker rm -f transmission`.
- ⬜ **Этап 3 — рекурсивный фикс прав вглубь** (root → выполняет Alex, масштаб ~257k+ файлов): рекурсивно вернуть владельца 950:950 + снять 0000 на файлах/папках (`find ... -exec chown -R 950:950 {} +`, `chmod u+rw,g+r` для файлов, `u+rwX,g+rwX` для каталогов). Или через UI `Apply recursively`.
### Точное содержание transmission compose (оригинал, важно для пересоздания)
From 189a35965135373ef489829f5b722e4987ac5fc3 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 12:17:34 +0600
Subject: [PATCH 65/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
family/how-to/arr-stack-taiga.md
---
.../2026-09-02-restore-privilege-scope.md | 55 ++++++++++++-------
family/how-to/arr-stack-taiga.md | 2 +
2 files changed, 38 insertions(+), 19 deletions(-)
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
index 59e1278c..4abf2087 100644
--- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -2,7 +2,7 @@
tags: [truenas, restore, incident, git, media, arr, permission]
created: 2026-09-02
updated: 2026-09-02
-status: in-progress (Этап 1 done — оба compose на PUID=950 правлены; Этап 2 заблокирован: transmission без compose-лейблов, нужен rm -f — ждёт ок Alex)
+status: in-progress (Этап 0-3 done — transmission пересоздан под PUID=950 штатно, медиа-права рекурсивно исправлены; ОСТАЛОСЬ: git/nas/второй root-заход на добивку, запуск arr-стека, первый sync-vault на маке)
---
# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
@@ -78,9 +78,9 @@ d--------- transmission transmission storage/shared
---
-## Общий план (ИСПРАВЛЕН — НЕ выполнен, требует подтверждения)
+## Общий план (ИСПРАВЛЕН — актуальный рабочий статус в §5 ниже; здесь технические применения)
-> ⚠️ **2026-09-02 УТОЧНЕНО: `/mnt/RED_2TB/storage` — единый ZFS dataset `RED_2TB/storage` с `acltype=nfsv4`. НЕ ПОСIX.** Ранее планировалось `chown -R`/`chmod -R` — это **НЕВЕРНО** для TrueNAS SCALE с NFSv4 ACL. Права управляются **только** через `midclt call filesystem.setacl` (эквивалент TrueNAS UI → Datasets → `RED_2TB/storage` → Edit ACL → Apply recursively). `truenas_admin` = FULL_ADMIN в midclt → **root/sudo для setacl НЕ нужен** (проверено: `midclt call auth.me` → roles `FULL_ADMIN`).
+> **Практическое уточнение 2026-09-02 (Важно для будущего):** несмотря на утверждение в § «НЕ ПОСIX», на практике **root-`chown -R`/`chmod` на хосте TrueNAS СРАБОТАЛИ** и привели медиа-папки к `950:950 drwxrwx---` (это под LTS-смонтированным zfs с `acltype=nfsv4`, где POSIX-биты отображаются из NFS4 owner@/group@/ACL). Реальная поломка была **двухслойная**: (1) uid владельца файла сбит на 921 (правит `chown`), (2) биты owner@ обнулены (правит `chmod`/ACL). Вывод: у этих простых zfs-датасетов `chown`/`chmod` под root **достаточны и быстрее**, чем полный `filesystem.setacl`. `midclt setacl` нужен, когда ACL не-тривиальный (произвольные ACE entries поверх базовых) — здесь их не было (ACL тривиальны, `trivial:true` в getacl). Для будущего: пробовать POSIX chown/chmod первым, setacl — только если chmod не влияет на ACE и остаётся `d---------` у владельца с правильным uid.
### Диагноз по NFS4 ACL (что именно сломано)
@@ -160,22 +160,39 @@ ssh truenas_admin@mallexxx.duckdns.org 'bash /tmp/fix-storage-nfs4-acl.sh'
- **transmission compose:** `/config/docker-compose.yml` перезаписан через `docker cp /tmp/transmission-new-compose.yml transmission:/config/docker-compose.yml`. Добавлено `PUID=950` + `PGID=950`. Оригинал в `variantA_bk`. (Скрипт-источник на маке: `~/transmission-new-compose.yml`.)
- **arr compose:** `/mnt/RED_2TB/docker/arr/docker-compose.yml` перезаписан (владелец truenas_admin → запись прошла без root): во всех 4 сервисах (prowlarr/radarr/sonarr/jellyfin) добавлено `PUID=950` + `PGID=950` (у всех уже был `TZ`). Проверено `docker compose config --quiet` → `COMPOSE_VALID`. Источник: `~/arr-new-compose.yml`. Оригинал в `variantA_bk`.
- **Arr compose НЕ имеет секции `networks` у сервисов** → при подъёме будет дефолт-сеть `_default`. Consciously оставлено (оригинал не задавал networks у radarr/sonarr/jellyfin явно, только глобально `media_net`/`caddy_default`; НЕ переопределено чтобы не менять поведение без надобности).
-- ⚠️ **Этап 2 — ПЕРЕСОЗДАНИЕ transmission — ЗАБЛОКИРОВАНО (ключевой технический питфолл, 2026-09-02):**
- - `cd /mnt/RED_2TB/docker/transmission` → **`permission denied`** (каталог `drwx------ 911 911` — владелец transdamon/911, truenas_admin нет доступа).
- - Попытка `docker compose -f /tmp/transmission-new-compose.yml up -d` → создал сеть `tmp_default` и упал: **name conflict** (`container "transmission" already in use`).
- - Полный рабочий контейнер пересоздать из compose **невозможно** по двум причинам:
- 1. Контейнер создан **без compose-лейблов** (`com.docker.compose.project = `) → compose его **не распознаёт как свой** → не может мигрировать/пересоздать, только name-conflict.
- 2. Композ-файл в каталоге 911 недоступен для работы оттуда.
- - **Найденное решение:** удалить старый контейнер и поднять заново с корректным проектом (сохранит сеть `transmission_default`):
- ```bash
- docker rm -f transmission # данные в /config и /mnt/storage НЕ теряются (bind-mount на диск)
- docker compose -f /tmp/transmission-new-compose.yml -p transmission up -d
- docker exec transmission id # ожидаем uid=950
- ```
- - **Почему PUID нельзя применить к работающему контейнеру?** env зашивается при `docker run` (создании). `docker update` меняет ТОЛЬКО ресурсы (CPU/RAM/restart), **НЕ переменные окружения** — ограничение Docker. Пересоздание — единственный способ.
- - **Альтернатива без пересоздания (если не хотим трогать transmission root):** оставить root; НО тогда новые скачанные файлы будут `root:root`, и radarr/jellyfin (под 950) их не прочитают. → PUID=950 у transmission всё же нужен для цепочки. (Обход с setgid-битом на dir + group 950 возможен как fallback, не выбран.)
- - **Статус:** ждёт ок Alex на `docker rm -f transmission`.
-- ⬜ **Этап 3 — рекурсивный фикс прав вглубь** (root → выполняет Alex, масштаб ~257k+ файлов): рекурсивно вернуть владельца 950:950 + снять 0000 на файлах/папках (`find ... -exec chown -R 950:950 {} +`, `chmod u+rw,g+r` для файлов, `u+rwX,g+rwX` для каталогов). Или через UI `Apply recursively`.
+- ✅ **Этап 2 — ПЕРЕСОЗДАНИЕ transmission под PUID=950 ВЫПОЛНЕНО (2026-09-02).** НЕ понадобился `docker rm -f`. Решено штатно:
+ - **Питфолл про compose-проект:** у контейнера НЕТ compose-лейблов (`com.docker.compose.project = `) → `docker compose up` в папке 911 его не распознаёт и даёт name-conflict. Причина 911-папки ушла сама: **после пересоздания владелец `/mnt/RED_2TB/docker/transmission` стал `950:950`** → папка доступна.
+ - **Способ:** `cd /mnt/RED_2TB/docker/transmission && docker compose up -d --force-recreate` — применил PUID, контейнер `Recreated/Started`, проект `transmission`.
+ - **Проверено:** `docker exec transmission printenv PUID PGID` → `950 950`; процесс `transmission-daemon` работает под `abc` (uid 950), НЕ root.
+ - **Ключевой урок (наш ошибка):** временные compose-файлы в `/tmp` и `docker cp` — костыль. Использовать только штатный запуск из правильной папки compose. Временные файлы `/tmp/transmission-new-compose.yml` и `/tmp/arr-new-compose.yml` удалены (почищены 2026-09-02).
+ - (В /tmp остались чужие/ранее существовавшие `compose_new.yml`, `reverse-portal-compose.yml` — НЕ наши, не трогали.)
+- ✅ **Этап 3 — рекурсивный фикс прав вглубь ВЫПОЛНЕН Alex (root) + проверен.** Результат верификации 2026-09-02:
+ - Медиа-папки → `950:950 drwxrwx---`: Cartoons, Downloads, Movies, Music, art, books, cartoons-series, documentaries(-series), series, shared, work. ✅
+ - Вглубь Movies: файлы → `950:950`, права разблокированы (`rw-r-----`/`rwxrwx---`). ✅
+ - **ЕЩЁ НЕ ДОБИТО (остаточек):** `git/` bare repo dir → `d--------- 950:950` (0000) → **git-sync на маке всё ещё стоит** `fatal: not a git repository`; `nas` (921/0000, не попал в chown/chmod), `ada3s1`, `photo_dedup_test`, `seafile`, `singularity` (0000/700); скрытые mac-файлы корня `.DS_Store/.bash*/.profile/.com.apple*` (921); сам корень `storage` → `921:921 drwxrwx---`.
+
+### ⬜ Остаточный root-шаг (второй проход, готов для Alex) — добить git + nas + прочее
+
+Команда подготовлена в сессии, Alex ещё не запускал:
+
+```bash
+S=/mnt/RED_2TB/storage
+chown -R 950:950 "$S/git" "$S/nas" "$S/ada3s1" "$S/photo_dedup_test" \
+ "$S/seafile" "$S/singularity" "$S/radarr" "$S/sonarr" 2>/dev/null
+find "$S/git" "$S/nas" "$S/ada3s1" "$S/photo_dedup_test" "$S/seafile" \
+ "$S/singularity" "$S/radarr" "$S/sonarr" \
+ -type d -exec chmod u+rwX,g+rwX,o-rwx {} + 2>/dev/null
+find "$S" -maxdepth 1 -type f \( -name "._*" -o -name ".DS_Store" -o -name ".bash*" -o -name ".profile" \
+ -o -name ".com.apple*" \) -exec rm -f {} + 2>/dev/null
+chmod -R g+rX "$S/git" 2>/dev/null
+git --git-dir="$S/git/obsidian-vault.git" log --oneline -2 2>&1 | head # проверка
+```
+
+> ⚠️ `rm -f` скрытых файлов корня — mac-мусор/остатки старого владельца 921, безопасно. Если `.profile`/`.bashrc` рабочие — убрать их из rm по решению.
+
+### После остаточного фикса
+- **На маке**: `bash ~/scripts/sync-vault.sh` → 55 коммитов уедут в bare repo (launchd возобновит); проверить `git log nas/main..main` = 0.
+- **Arr-стек**: `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps` + что jellyfin видит библиотеки (пути `/storage/*`).
### Точное содержание transmission compose (оригинал, важно для пересоздания)
```yaml
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index ad4a3b04..3688baa9 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -20,6 +20,8 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
> **Docker-image facts (2026-09-02):** All 5 media images **loaded locally** on TrueNAS (`docker images`): `lscr.io/linuxserver/{radarr,sonarr,jellyfin,transmission,prowlarr}:latest`. Only **transmission** container exists (running). **jellyfin/radarr/sonarr/prowlarr containers are NOT yet created** — they never get launched until perms fixed.
> **Transmission runs as ROOT** (not 911/950): its compose `/mnt/RED_2TB/docker/transmission/docker-compose.yml` has **no `PUID`/`PGID`**, so linuxserver `/init` (s6-overlay) leaves it root (`docker exec transmission id` → uid 0). This is why it works on the 0000 media dirs. It mounts `storage` → `/mnt/storage`, download-dir = `/mnt/storage/Downloads`.
> **Fix direction (approved Variant A):** set `PUID=950 PGID=950` on transmission + all arr/jellyfin compose services so every service shares uid `950` and mutually reads files; then recursively fix perms in depth. Plan + commands: `2026-09-02-restore-privilege-scope.md` §5.
+>
+> **PROGRESS 2026-09-02:** Both compose files edited for PUID/PGID=950 (transmission at `/mnt/RED_2TB/docker/transmission/docker-compose.yml`, arr at `/mnt/RED_2TB/docker/arr/docker-compose.yml` incl. all 4 services). **transmission container force-recreated** from its proper folder and now runs its daemon as **uid 950** (was root) — env `PUID=950 PGID=950` confirmed, process under `abc`/950. Media perms in depth recursively fixed to `950:950` (Movies etc.). **STILL OPEN:** `storage/git/` and `storage/nas` perms not yet fixed (second root pass pending) → **do NOT start arr stack until git/nas done**; then `cd /mnt/RED_2TB/docker/arr && docker compose up -d`.
## Notes from Setup (2026-05-20)
From 2b6c1817d195a0bc4170908f31025f097a6791d0 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 12:25:39 +0600
Subject: [PATCH 66/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
family/how-to/arr-stack-taiga.md family/how-to/vault-git-sync.md
---
.../2026-09-02-restore-privilege-scope.md | 63 ++++++++++++++++---
family/how-to/arr-stack-taiga.md | 8 +--
family/how-to/vault-git-sync.md | 1 +
3 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
index 4abf2087..f48880eb 100644
--- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -2,7 +2,7 @@
tags: [truenas, restore, incident, git, media, arr, permission]
created: 2026-09-02
updated: 2026-09-02
-status: in-progress (Этап 0-3 done — transmission пересоздан под PUID=950 штатно, медиа-права рекурсивно исправлены; ОСТАЛОСЬ: git/nas/второй root-заход на добивку, запуск arr-стека, первый sync-vault на маке)
+status: in-progress (Этап 0-3 + 2-й root-проход DONE — storage все 950, git-sync fetch ОК; ОСТАЛОСЬ основной блокер: NFS4 ACL DENY READ_DATA на ~1946 git-объектах → push на маке падает)
---
# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
@@ -169,11 +169,60 @@ ssh truenas_admin@mallexxx.duckdns.org 'bash /tmp/fix-storage-nfs4-acl.sh'
- ✅ **Этап 3 — рекурсивный фикс прав вглубь ВЫПОЛНЕН Alex (root) + проверен.** Результат верификации 2026-09-02:
- Медиа-папки → `950:950 drwxrwx---`: Cartoons, Downloads, Movies, Music, art, books, cartoons-series, documentaries(-series), series, shared, work. ✅
- Вглубь Movies: файлы → `950:950`, права разблокированы (`rw-r-----`/`rwxrwx---`). ✅
- - **ЕЩЁ НЕ ДОБИТО (остаточек):** `git/` bare repo dir → `d--------- 950:950` (0000) → **git-sync на маке всё ещё стоит** `fatal: not a git repository`; `nas` (921/0000, не попал в chown/chmod), `ada3s1`, `photo_dedup_test`, `seafile`, `singularity` (0000/700); скрытые mac-файлы корня `.DS_Store/.bash*/.profile/.com.apple*` (921); сам корень `storage` → `921:921 drwxrwx---`.
+ - ✅ **Второй root-проход ВЫПОЛНЕН Alex (вторая команда ниже) — добиты `git`, `nas`, `ada3s1`, `photo_dedup_test`, `seafile`, `singularity`, `radarr`, `sonarr`:** все теперь `950:950 drwxrwx---`. Проверено:
+ - Все топ-папки storage теперь `950:950 drwxrwx---` (сплошняком, кроме скрытых файлов корня и самого корня). ✅
+ - `storage/git/obsidian-vault.git` теперь **открывается** — `git log` показывает коммиты (HEAD `a2c72793` [2026-08-17]...).
+ - `git/nas` уже не «matching вне-списка» — chown/chmod догнаны вторым проходом.
-### ⬜ Остаточный root-шаг (второй проход, готов для Alex) — добить git + nas + прочее
+### ⬜ Найден БЛОКЕР PUSH: NFS4 ACL `DENY READ_DATA` на тировых объектах git-репозитория
-Команда подготовлена в сессии, Alex ещё не запускал:
+**Симптом (после починки прав):** на маке git-sync теперь:
+```
+git fetch nas main → OK (bare repo доступен, fetch exit=0)
+git push nas main → FAIL:
+ remote: fatal: loose object acd1b45542e82aa40f345da3b8ac9f721633b138 ... is corrupt
+ remote unpack failed: index-pack abnormal exit
+```
+`sync-vault.sh` — `Committed` потом `Push failed`. Ahead вырос 55 → 62 (коммиты локально копятся, не пушатся).
+
+**Диагностика — это НЕ повреждение данных, а NFS4 ACL-блокировка чтения:**
+
+| Проверка | Результат |
+|---|---|
+| `git fsck --full` через **root** (контейнер gitea, монтирует `/git-repos`) | ЧИСТО: только `dangling commit/tree`, **ни одного corrupt/missing/bad** |
+| `git cat-file -t acd1b45...` под root (gitea) | `tree` — объект читается, цел |
+| NFS4 ACL самого файла `objects/ac/d1b...` (через `filesystem.getacl`) | **`owner@ DENY READ_DATA=True`** поверх `ALLOW` → перекрывает |
+
+**Суть:** у ~**1946 из 3373** loose git-объектов в `obsidian-vault.git/objects/` стоит битый NFS4 ACL `owner@ type=DENY READ_DATA=True`. DENY-запись в NFSv4 **перекрывает ALLOW**, поэтому владелец (uid 950 = truenas_admin) **не может mmap/прочитать эти объекты** при push. Git на приеме (`git-receive-pack`/`index-pack`) трактует нечитаемость как «loose object corrupt» → push rejected. Root игнорирует ACL → поэтому fsck под gitea чист, а push от 950 падает. **Данные целы.**
+
+POSIX-признак битых файлов: **mode `40` (`r--------`)**; нормальные объекты — mode `750`. (count: 3373 объектов, ~1946 с mode 4xx.)
+
+**Фикс (root/root-midclt) — ГОТОВ, Alex ещё не запускал (ждёт отдельного подтверждения, т.к. влияет на ACL объектов):**
+```bash
+# снять битые NFS4 ACL (DENY) на весь git-репозиторий, вернуть стандарт из uid/gid
+midclt call filesystem.setacl /mnt/RED_2TB/storage/git/obsidian-vault.git \
+ '{"uid":950,"gid":950,"dacl":[],"options":{"recursive":true,"traverse":true,"stripacl":true}}'
+
+# проверить, что проблемный объект читается владельцем:
+git --git-dir=/mnt/RED_2TB/storage/git/obsidian-vault.git cat-file -t acd1b45542e82aa40f345da3b8ac9f721633b138
+
+# затем с мака:
+cd ~/obsidian && git push nas main
+```
+> ⚠️ `chmod`/`chown` НЕ снимает NFS4 `DENY` ACE — нужен именно `stripacl:true` (или `setfacl -Rb` на хосте, если доступен). TrueNAS SCALE: `setacl` с `stripacl:true` + пустым `dacl` переведёт на POSIX-default из owner@/group@.
+> ⚠️ Перед применением снять бэкап ACL `git/` (read-only): `midclt call filesystem.getacl /mnt/RED_2TB/storage/git > ~/variantA_bk/git_after.acl`.
+> ⚠️ Стоп-правило Alex: nothing destructive без явного подтверждения — не делался, ждёт команды.
+
+**Куда попадают DENY-ACL:** артефакт restore — часть loose-объектов при переносе данных получили инверсивный NFS4 ACL (`owner@ DENY read`). Это НЕ родние git-права (git-репо хранит объекты как read-only `r--r--r--` в обычном случае); на полке после поломки.
+
+### Диагностические insights (для будущих сессий)
+- `docker exec transmission ...` даёт root вглубь `/mnt/storage` (transmission монтирует `storage`); `gitea` контейнер монтирует `/mnt/RED_2TB/storage/git → /git-repos` и имеет `git` — удобен для `git fsck`-честных проверок (root-контекст). `hermes-taiga` монтирует `/vault.git` = `storage/git/obsidian-vault.git` и тоже имеет git.
+- NFS4 `owner@ DENY` перекрывает `ALLOW` — клинический кейс «ls показывает доступно, а процесс не читает».
+- POSIX-бит файла (mode 40/750) коррелирует с NFS4 ACL; «corrupt» от `index-pack` при целых данных = почти всегда ACL/права на приеме, не реальное повреждение.
+
+### ✅ Остаточный root-шаг (второй проход) — ВЫПОЛНЕН Alex; команда для справки (историческая)
+
+> Уже пройдено — итоговое состояние `950:950 drwxrwx---` на всех топ-папках см. в Этапе 3 выше. Команда зафиксирована как рецепт для будущего:
```bash
S=/mnt/RED_2TB/storage
@@ -190,9 +239,9 @@ git --git-dir="$S/git/obsidian-vault.git" log --oneline -2 2>&1 | head # пр
> ⚠️ `rm -f` скрытых файлов корня — mac-мусор/остатки старого владельца 921, безопасно. Если `.profile`/`.bashrc` рабочие — убрать их из rm по решению.
-### После остаточного фикса
-- **На маке**: `bash ~/scripts/sync-vault.sh` → 55 коммитов уедут в bare repo (launchd возобновит); проверить `git log nas/main..main` = 0.
-- **Arr-стек**: `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps` + что jellyfin видит библиотеки (пути `/storage/*`).
+### ⚠️ Текущий статус после второго прохода (актуально)
+- **Push на маке БЛОКИРОВАН** NFS4 ACL `DENY READ_DATA` на ~1946 loose git-объектах — см. секцию «Найден БЛОКЕР PUSH» выше. Фикс `filesystem.setacl ... stripacl:true` ГОТОВ, Alex должен подтвердить.
+- **Arr-стек**: после снятия DENY-блокера на git и починки прав медиа — `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps` + что jellyfin видит библиотеки (пути `/storage/*`). PUID/PGID=950 уже прописаны во всех 4 сервисах докер-стека.
### Точное содержание transmission compose (оригинал, важно для пересоздания)
```yaml
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index 3688baa9..c13c0588 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -15,13 +15,13 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
> **STATE 2026-09-02:** Стек **выключен** — в `docker ps -a` нет ни radarr/sonarr/prowlarr/jellyfin контейнеров (стек не пересоздавался). Compose-структура живёт в `/mnt/RED_2TB/docker/arr/` (`docker-compose.yml` + подпапки `prowlarr/ radarr/ sonarr/ jellyfin/ media-pipeline/`). Работает только `transmission` (отдельный compose `/mnt/RED_2TB/docker/transmission/`).
>
-> **Почему не запускается просто так — медиа-права сломаны** (тот же restore-инцидент uid 921/0000): `/mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared}` → `d---------`/`drwx------`, owner `transmission` (uid 921). radarr/sonarr монтируют `/storage` (rw), jellyfin `:ro` — со сломанными правами не прочитают библиотеку. Фикс + запуск: см. `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
+> **Почему стек изначально не запускался — медиа-права были сломаны** (тот же restore-инцидент uid 921/0000): `/mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared}` были `d---------`/`drwx------`, owner `transmission` (uid 921). radarr/sonarr монтируют `/storage` (rw), jellyfin `:ro` — со сломанными правами не прочитали бы библиотеку. ⚠️ **Сейчас (2026-09-02, конец сессии) права медиа ИСПРАВЛЕНЫ** до `950:950 drwxrwx---` (см. PROGRESS ниже) — но контейнеры arr/jellyfin всё ещё не созданы/не запущены. Полный контекст: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
-> **Docker-image facts (2026-09-02):** All 5 media images **loaded locally** on TrueNAS (`docker images`): `lscr.io/linuxserver/{radarr,sonarr,jellyfin,transmission,prowlarr}:latest`. Only **transmission** container exists (running). **jellyfin/radarr/sonarr/prowlarr containers are NOT yet created** — they never get launched until perms fixed.
-> **Transmission runs as ROOT** (not 911/950): its compose `/mnt/RED_2TB/docker/transmission/docker-compose.yml` has **no `PUID`/`PGID`**, so linuxserver `/init` (s6-overlay) leaves it root (`docker exec transmission id` → uid 0). This is why it works on the 0000 media dirs. It mounts `storage` → `/mnt/storage`, download-dir = `/mnt/storage/Downloads`.
+> **Docker-image facts (2026-09-02):** All 5 media images **loaded locally** on TrueNAS (`docker images`): `lscr.io/linuxserver/{radarr,sonarr,jellyfin,transmission,prowlarr}:latest`. Only **transmission** container exists (now running as uid 950). **jellyfin/radarr/sonarr/prowlarr containers are NOT yet created** — their compose services have PUID/PGID=950 ready, but they have not been `up`'ed yet.
+> **Transmission originally ran as ROOT** (not 911/950): its compose had no `PUID`/`PGID`, so linuxserver `/init` (s6-overlay) left it root. ✅ **Fixed 2026-09-02:** `PUID=950 PGID=950` added and container force-recreated → now daemon under uid 950. It mounts `storage` → `/mnt/storage`, download-dir = `/mnt/storage/Downloads`.
> **Fix direction (approved Variant A):** set `PUID=950 PGID=950` on transmission + all arr/jellyfin compose services so every service shares uid `950` and mutually reads files; then recursively fix perms in depth. Plan + commands: `2026-09-02-restore-privilege-scope.md` §5.
>
-> **PROGRESS 2026-09-02:** Both compose files edited for PUID/PGID=950 (transmission at `/mnt/RED_2TB/docker/transmission/docker-compose.yml`, arr at `/mnt/RED_2TB/docker/arr/docker-compose.yml` incl. all 4 services). **transmission container force-recreated** from its proper folder and now runs its daemon as **uid 950** (was root) — env `PUID=950 PGID=950` confirmed, process under `abc`/950. Media perms in depth recursively fixed to `950:950` (Movies etc.). **STILL OPEN:** `storage/git/` and `storage/nas` perms not yet fixed (second root pass pending) → **do NOT start arr stack until git/nas done**; then `cd /mnt/RED_2TB/docker/arr && docker compose up -d`.
+> **PROGRESS 2026-09-02 (updated end-of-session):** Both compose files edited for PUID/PGID=950 (transmission at `/mnt/RED_2TB/docker/transmission/docker-compose.yml`, arr at `/mnt/RED_2TB/docker/arr/docker-compose.yml` incl. all 4 services). **transmission container force-recreated** from its proper folder (`cd /mnt/RED_2TB/docker/transmission && docker compose up -d --force-recreate`) and now runs its daemon as **uid 950** (was root) — env `PUID=950 PGID=950` confirmed, process under `abc`/950. Media perms in depth recursively fixed to `950:950 drwxrwx---` on all top storage folders (incl. git/nas 2nd root pass). **Still to do before arr stack is useful / to unblock git-sync:** (1) strip the broken NFS4 `DENY READ_DATA` ACL on ~1946 loose git objects in `obsidian-vault.git` (this breaks `git push` from Mac with fake "corrupt" error) — fix command and details in `2026-09-02-restore-privilege-scope.md`; (2) then `cd /mnt/RED_2TB/docker/arr && docker compose up -d` and verify jellyfin sees libraries (`/storage/*`).
## Notes from Setup (2026-05-20)
diff --git a/family/how-to/vault-git-sync.md b/family/how-to/vault-git-sync.md
index 12fc7419..7b70c0d2 100644
--- a/family/how-to/vault-git-sync.md
+++ b/family/how-to/vault-git-sync.md
@@ -131,6 +131,7 @@ cd ~/Developer/vault-sync-test && bash test-sync.sh
- `git add -A` in sparse worktree still stages deletions of out-of-cone files tracked in index — need to unstage them (fixed in old script; irrelevant with proper clone)
- `core.quotePath=true` (default) escapes Cyrillic paths in `git ls-files` output — use `-c core.quotePath=false`
- ZFS on TrueNAS blocks `chmod` — `git init` fails from host; must run from inside Docker container or create `.git` structure manually
+- **NFS4 `owner@ DENY READ_DATA` ACL breaks push (fake "corrupt" objects)** — see History 2026-09-02. Key: after a TrueNAS **pool restore**, a subset of loose git objects in the bare repo can carry an inverted NFS4 ACL `owner@ type=DENY READ_DATA=True`. In NFSv4 a DENY overlays ALLOW, so the owning uid (e.g. 950) can't mmap-read those objects → on receive, `git-receive-pack`/`index-pack` reports `loose object ... is corrupt` and rejects push, even though data is intact (`git fsck --full` under root is clean). Fix = `filesystem.setacl ... {stripacl:true}` (chmod/chown do NOT remove DENY ACEs). Tell-tale: POSIX file mode `40` (`r--------`) on loose objects vs normal `750`.
## History
From 6aa2e81fd91a1c24e563347c2d970ab87fe31f80 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 12:30:43 +0600
Subject: [PATCH 67/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
family/how-to/arr-stack-taiga.md
---
.../2026-09-02-restore-privilege-scope.md | 44 +++++++++++--------
family/how-to/arr-stack-taiga.md | 2 +-
2 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
index f48880eb..5dec2a10 100644
--- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -2,7 +2,7 @@
tags: [truenas, restore, incident, git, media, arr, permission]
created: 2026-09-02
updated: 2026-09-02
-status: in-progress (Этап 0-3 + 2-й root-проход DONE — storage все 950, git-sync fetch ОК; ОСТАЛОСЬ основной блокер: NFS4 ACL DENY READ_DATA на ~1946 git-объектах → push на маке падает)
+status: resolved (Git-sync на маке ПОЧИНЕН 2026-09-02: NFS4 ACL DENY сняты полным dacl-заменой через filesystem.setacl; push ИДЁТ; ahead=0 behind=0. Осталось: подъём arr-стека + фикс права на скрытые файлы корня storage + не связанный library-app restart-loop)
---
# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
@@ -174,7 +174,7 @@ ssh truenas_admin@mallexxx.duckdns.org 'bash /tmp/fix-storage-nfs4-acl.sh'
- `storage/git/obsidian-vault.git` теперь **открывается** — `git log` показывает коммиты (HEAD `a2c72793` [2026-08-17]...).
- `git/nas` уже не «matching вне-списка» — chown/chmod догнаны вторым проходом.
-### ⬜ Найден БЛОКЕР PUSH: NFS4 ACL `DENY READ_DATA` на тировых объектах git-репозитория
+### ✅ RESOLVED 2026-09-02: Блокер push снят — полной dacl-заменой, НЕ stripacl
**Симптом (после починки прав):** на маке git-sync теперь:
```
@@ -193,25 +193,29 @@ git push nas main → FAIL:
| `git cat-file -t acd1b45...` под root (gitea) | `tree` — объект читается, цел |
| NFS4 ACL самого файла `objects/ac/d1b...` (через `filesystem.getacl`) | **`owner@ DENY READ_DATA=True`** поверх `ALLOW` → перекрывает |
-**Суть:** у ~**1946 из 3373** loose git-объектов в `obsidian-vault.git/objects/` стоит битый NFS4 ACL `owner@ type=DENY READ_DATA=True`. DENY-запись в NFSv4 **перекрывает ALLOW**, поэтому владелец (uid 950 = truenas_admin) **не может mmap/прочитать эти объекты** при push. Git на приеме (`git-receive-pack`/`index-pack`) трактует нечитаемость как «loose object corrupt» → push rejected. Root игнорирует ACL → поэтому fsck под gitea чист, а push от 950 падает. **Данные целы.**
+**Суть:** у **1946 из 3373** loose git-объектов в `obsidian-vault.git/objects/` стоит битый NFS4 ACL `owner@ type=DENY READ_DATA=True`. DENY-запись в NFSv4 **перекрывает ALLOW**, поэтому владелец (uid 950 = truenas_admin) **не может mmap/прочитать эти объекты** при push. Git на приеме (`git-receive-pack`/`index-pack`) трактует нечитаемость как «loose object corrupt» → push rejected. Root игнорирует ACL → поэтому fsck под gitea чист, а push от 950 падает. **Данные целы.**
-POSIX-признак битых файлов: **mode `40` (`r--------`)**; нормальные объекты — mode `750`. (count: 3373 объектов, ~1946 с mode 4xx.)
+POSIX-признак битых файлов: **mode `40` (`r--------`)**; нормальные объекты — mode `750`.
-**Фикс (root/root-midclt) — ГОТОВ, Alex ещё не запускал (ждёт отдельного подтверждения, т.к. влияет на ACL объектов):**
+**Фикс РАБОЧИЙ (RESOLVED 2026-09-02, Alex выполнил из root-шелла):** ⚠️ **`stripacl:true` с пустым `dacl:[]` НЕ работает** — джоба при `filesystem.setacl /path ` вываливалась `Too many arguments (expected 1, found 2)` (path передавался как отдельный аргумент — палево), а точечный `stripacl:true` на объекте дал `SUCCESS`, но DENY-ACL **остался**. Решило только **полная dacl-замена** (передача полного массива ACE без DENY, где owner@/group@ = ALLOW), т.е. setacl воспринимает полный dacl как *.replace* всего ACL, а не merge.
+
+Проверенная рабочая команда (root-шелл для truenas_admin, job асинхронный — вернёт id, потом `SUCCESS`):
```bash
-# снять битые NFS4 ACL (DENY) на весь git-репозиторий, вернуть стандарт из uid/gid
-midclt call filesystem.setacl /mnt/RED_2TB/storage/git/obsidian-vault.git \
- '{"uid":950,"gid":950,"dacl":[],"options":{"recursive":true,"traverse":true,"stripacl":true}}'
+# точечно на один объект (проверка метода):
+midclt call filesystem.setacl '{"path":"/mnt/RED_2TB/storage/git/obsidian-vault.git/objects/ac/d1b45542e82aa40f345da3b8ac9f721633b138","uid":950,"gid":950,"acltype":"NFS4","dacl":[, ],"options":{"recursive":false}}'
-# проверить, что проблемный объект читается владельцем:
-git --git-dir=/mnt/RED_2TB/storage/git/obsidian-vault.git cat-file -t acd1b45542e82aa40f345da3b8ac9f721633b138
-
-# затем с мака:
-cd ~/obsidian && git push nas main
+# затем рекурсивно на весь репозиторий (dacl тот же, recursive:true):
+midclt call filesystem.setacl '{"path":"/mnt/RED_2TB/storage/git/obsidian-vault.git","uid":950,"gid":950,"acltype":"NFS4","dacl":[{"tag":"owner@","id":-1,"type":"ALLOW","perms":{"READ_DATA":true,"WRITE_DATA":true,"EXECUTE":false,"APPEND_DATA":true,"DELETE_CHILD":false,"DELETE":false,"READ_ATTRIBUTES":true,"WRITE_ATTRIBUTES":true,"READ_NAMED_ATTRS":true,"WRITE_NAMED_ATTRS":true,"READ_ACL":true,"WRITE_ACL":true,"WRITE_OWNER":true,"SYNCHRONIZE":true},"flags":{"BASIC":"INHERIT"}},{"tag":"group@","id":-1,"type":"ALLOW","perms":{"READ_DATA":true,"WRITE_DATA":false,"EXECUTE":true,"APPEND_DATA":false,"DELETE_CHILD":false,"DELETE":false,"READ_ATTRIBUTES":true,"WRITE_ATTRIBUTES":false,"READ_NAMED_ATTRS":true,"WRITE_NAMED_ATTRS":false,"READ_ACL":true,"WRITE_ACL":false,"WRITE_OWNER":false,"SYNCHRONIZE":true},"flags":{"BASIC":"INHERIT"}}],"options":{"recursive":true,"traverse":true}}'
```
-> ⚠️ `chmod`/`chown` НЕ снимает NFS4 `DENY` ACE — нужен именно `stripacl:true` (или `setfacl -Rb` на хосте, если доступен). TrueNAS SCALE: `setacl` с `stripacl:true` + пустым `dacl` переведёт на POSIX-default из owner@/group@.
-> ⚠️ Перед применением снять бэкап ACL `git/` (read-only): `midclt call filesystem.getacl /mnt/RED_2TB/storage/git > ~/variantA_bk/git_after.acl`.
-> ⚠️ Стоп-правило Alex: nothing destructive без явного подтверждения — не делался, ждёт команды.
+**Ключевое правило midclt:** `filesystem.setacl` принимает **ОДИН JSON-аргумент со всеми полями** (`path`, `uid`, `gid`, `acltype`, `dacl`, `options`) — НЕ path отдельно. Job вернёт id и выполнится асинхронно; проверять `midclt call core.get_jobs` (state `SUCCESS`/error).
+
+Проверка после (для владельца 950, НЕ root):
+```bash
+git --git-dir=/mnt/RED_2TB/storage/git/obsidian-vault.git cat-file -t acd1b45542e82aa40f345da3b8ac9f721633b138 # → tree
+git --git-dir=/mnt/RED_2TB/storage/git/obsidian-vault.git fsck # → 0 corrupt/missing/permission denied
+```
+
+**Результат (проверено 2026-09-02):** объект читается (`tree`), `git fsck` под 950 → **0 ошибок**, и с мака `git push nas main` **прошёл** (`a2c7279..2b6c181 main -> main`, exit=0). Локально/remote **ahead=0 behind=0** — полностью синхронизировано. launchd-крона (коммиты каждые 5 мин) теперь реально пушит. Бэкап ACL репо снят до фикса: `~/variantA_bk/git_ACL_backup_2026-09-01_232328/` (root ACL + mode-map 3402 файлов).
**Куда попадают DENY-ACL:** артефакт restore — часть loose-объектов при переносе данных получили инверсивный NFS4 ACL (`owner@ DENY read`). Это НЕ родние git-права (git-репо хранит объекты как read-only `r--r--r--` в обычном случае); на полке после поломки.
@@ -239,9 +243,11 @@ git --git-dir="$S/git/obsidian-vault.git" log --oneline -2 2>&1 | head # пр
> ⚠️ `rm -f` скрытых файлов корня — mac-мусор/остатки старого владельца 921, безопасно. Если `.profile`/`.bashrc` рабочие — убрать их из rm по решению.
-### ⚠️ Текущий статус после второго прохода (актуально)
-- **Push на маке БЛОКИРОВАН** NFS4 ACL `DENY READ_DATA` на ~1946 loose git-объектах — см. секцию «Найден БЛОКЕР PUSH» выше. Фикс `filesystem.setacl ... stripacl:true` ГОТОВ, Alex должен подтвердить.
-- **Arr-стек**: после снятия DENY-блокера на git и починки прав медиа — `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps` + что jellyfin видит библиотеки (пути `/storage/*`). PUID/PGID=950 уже прописаны во всех 4 сервисах докер-стека.
+### ⚠️ Текущий статус после вторго прохода + RESOLUTION (актуально на 2026-09-02)
+- ✅ **Push на маке ПОЧИНЕН** — NFS4 ACL `DENY` на ~1946 loose git-объектах сняты через **полную dacl-замену** (не stripacl). См. секцию «✅ RESOLVED … dacl-заменой». Git-sync работает: fetch+push проходят, ahead=0 behind=0.
+- 🔜 **Arr-стек** — НЕ поднят ещё. Все compose (`arr/docker-compose.yml` + transmission compose) уже с `PUID/PGID=950`, права медиа 950:950. Для запуска: `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps` + что jellyfin видит библиотеки (пути `/storage/*`). Обрати внимание: radarr/sonarr/jellyfin монтируют `/mnt/RED_2TB/storage` как `/storage`, а не `/media` (в отличие от Kraken-схемы) — root folder у radarr на TrueNAS другой.
+- 🔜 **Скрытые файлы корня storage** (`.DS_Store`, `.bash_*`, `.profile`, `.com.apple*`) остаются `921:921 mode 0000` (не добиты — в root-шаге B rm для них предлагался, но решение не подтверждено). Сам корень `storage/` → `drwxrwx--- 921:921` (owner 921, хотя group 950 есть). Проверить, нужен ли корню chown 950.
+- 🔜 **`books`** — владелец `950:921` (не 950:950) — оставлен как был (служебный, не критично для saga).
### Точное содержание transmission compose (оригинал, важно для пересоздания)
```yaml
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index c13c0588..a1897e99 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -21,7 +21,7 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
> **Transmission originally ran as ROOT** (not 911/950): its compose had no `PUID`/`PGID`, so linuxserver `/init` (s6-overlay) left it root. ✅ **Fixed 2026-09-02:** `PUID=950 PGID=950` added and container force-recreated → now daemon under uid 950. It mounts `storage` → `/mnt/storage`, download-dir = `/mnt/storage/Downloads`.
> **Fix direction (approved Variant A):** set `PUID=950 PGID=950` on transmission + all arr/jellyfin compose services so every service shares uid `950` and mutually reads files; then recursively fix perms in depth. Plan + commands: `2026-09-02-restore-privilege-scope.md` §5.
>
-> **PROGRESS 2026-09-02 (updated end-of-session):** Both compose files edited for PUID/PGID=950 (transmission at `/mnt/RED_2TB/docker/transmission/docker-compose.yml`, arr at `/mnt/RED_2TB/docker/arr/docker-compose.yml` incl. all 4 services). **transmission container force-recreated** from its proper folder (`cd /mnt/RED_2TB/docker/transmission && docker compose up -d --force-recreate`) and now runs its daemon as **uid 950** (was root) — env `PUID=950 PGID=950` confirmed, process under `abc`/950. Media perms in depth recursively fixed to `950:950 drwxrwx---` on all top storage folders (incl. git/nas 2nd root pass). **Still to do before arr stack is useful / to unblock git-sync:** (1) strip the broken NFS4 `DENY READ_DATA` ACL on ~1946 loose git objects in `obsidian-vault.git` (this breaks `git push` from Mac with fake "corrupt" error) — fix command and details in `2026-09-02-restore-privilege-scope.md`; (2) then `cd /mnt/RED_2TB/docker/arr && docker compose up -d` and verify jellyfin sees libraries (`/storage/*`).
+> **PROGRESS 2026-09-02 (updated end-of-session):** Both compose files edited for PUID/PGID=950 (transmission at `/mnt/RED_2TB/docker/transmission/docker-compose.yml`, arr at `/mnt/RED_2TB/docker/arr/docker-compose.yml` incl. all 4 services). **transmission container force-recreated** from its proper folder (`cd /mnt/RED_2TB/docker/transmission && docker compose up -d --force-recreate`) and now runs its daemon as **uid 950** (was root) — env `PUID=950 PGID=950` confirmed, process under `abc`/950. Media perms in depth recursively fixed to `950:950 drwxrwx---` on all top storage folders (incl. git/nas 2nd root pass). **Git-sync on Mac now RESOLVED** — the broken NFS4 `DENY READ_DATA` ACL on ~1946 loose git objects in `obsidian-vault.git` was stripped via full **dacl replacement** (not stripacl), so `git push` from Mac works again (push `a2c7279..2b6c181`, ahead=0 behind=0). Details + exact working command in `2026-09-02-restore-privilege-scope.md`. **Remaining:** `cd /mnt/RED_2TB/docker/arr && docker compose up -d` and verify jellyfin sees libraries (`/storage/*`). NOTE: radarr/sonarr/jellyfin mount `/mnt/RED_2TB/storage` as `/storage` (NOT `/media` like Kraken) — radarr root-folder on Taiga differs.
## Notes from Setup (2026-05-20)
From 5d8ba5d61b8b72b2e2de9a922a5e505c3a43470d Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 12:35:46 +0600
Subject: [PATCH 68/81] [2026-09-02] eagle:
family/how-to/truenas-infrastructure.md
family/how-to/truenas-infrastructure.md.bak-per-user-20260902-1229
personal/tech/xray-reverse-tunnel-kraken-truenas.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-per-user-20260902-1229
---
family/how-to/truenas-infrastructure.md | 15 +-
...frastructure.md.bak-per-user-20260902-1229 | 511 ++++++++++++++++++
.../xray-reverse-tunnel-kraken-truenas.md | 42 ++
...aken-truenas.md.bak-per-user-20260902-1229 | 388 +++++++++++++
4 files changed, 953 insertions(+), 3 deletions(-)
create mode 100644 family/how-to/truenas-infrastructure.md.bak-per-user-20260902-1229
create mode 100644 personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-per-user-20260902-1229
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 3e2ea34c..9d65f9ed 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -208,12 +208,12 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
| Volume | `/mnt/RED_2TB/docker/xray-admin:/etc/x-ui` (там `x-ui.db`) |
| Сеть | `caddy_default` (внешняя) |
| Панель | `https://vpn-panel.mallexxx.duckdns.org/` (Caddy → xray-admin:2053) |
-| Inbound | `vless-ws` port `10095`, tag `in-10095-tcp`, path `/vless`, host `vpn.mallexxx.duckdns.org`, client id `ce320965-6956-4759-84bb-7cb71cfc6252` (user1) |
+| Inbound | `vless-ws` port `10095`, tag `in-10095-tcp`, path `/vless`, host `vpn.mallexxx.duckdns.org`; clients: `user1` (direct TrueNAS), `kraken-user` (reverse via Kraken) |
| Sub-сервер | `[::]:443` (path /sub), панель `[::]:2053` |
| Env | `TZ=Asia/Novosibirsk`, `PUID=950`, `PGID=950` |
| Ports (host) | `54321:54321` (не используется панелью — внутри панель на 2053, можно убрать) |
-**Статус (2026-09-01):** контейнер **Up**, панель HTTP 200. Reverse **портал** развёрнут отдельным контейнером `xray-reverse-portal` (см. ниже). 3x-ui поддерживает reverse (`clientReverseTags`).
+**Статус (2026-09-02):** контейнер **Up**, панель HTTP 200. Reverse portal развёрнут отдельным контейнером `xray-reverse-portal`; per-user egress работает end-to-end.
**✅ ПРОВЕРЕНО РАБОТАЕТ (2026-09-02): `vpn.mallexxx.duckdns.org:443` — это ФУНКЦИОНАЛЬНЫЙ Xray-СЕРВЕР, НЕ только reverse-frontend.** Проверено end-to-end юзером с Mac. Ключевые факты с живой машины:
- Контейнеры `xray-admin` (Up 21h) и `caddy` (Up 20h) оба живы. Панель `vpn-panel` → HTTP 200. TCP `vpn.mallexxx.duckdns.org:443` снаружи открыт.
@@ -224,6 +224,15 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
- **⚠️ Так и твой клиент должен подключаться:** TLS терминирует Caddy на 443, inbound `security: none` → в конфиге КЛИЕНТА `network: ws`, TLS поверх на `vpn.mallexxx.duckdns.org` (**НЕ включать двойной TLS внутрь WS**).
- **⚠️ Xray 26.7.28 удалил `allowInsecure`** в клиентских tlsSettings (мигрировано на `pinnedPeerCertSha256`/`verifyPeerCertByName`). Если в клиентском конфиге был `allowInsecure: true` — клиент падает. Это частая причина «не подключается».
+**Per-user egress (2026-09-02):**
+
+- `user1`, ID `ce320965-6956-4759-84bb-7cb71cfc6252` → default outbound `direct` → TrueNAS IP `90.189.160.148`.
+- `kraken-user`, ID `93a5dc4b-1b8d-4af5-9363-eb0091734293` → routing rule `user: ["kraken-user"]` → SOCKS outbound `via-kraken` at `xray-reverse-portal:12345` → Kraken IP `92.62.70.41`.
+- Private destinations and BitTorrent remain blocked before the per-user rule.
+- Persistent global template is stored in `settings.key=xrayTemplateConfig`; do not edit `/app/bin/config.json` manually because it is generated.
+- Verified with the local `xray-test-client`: unchanged `user1` returned TrueNAS IP; changing only the client UUID to `kraken-user` returned Kraken IP over both HTTP and HTTPS.
+- Pre/post backups: `/mnt/RED_2TB/docker/backups/xray-per-user-20260902-1200/`.
+
> ⚠️ База `x-ui.db` была **уже настроена** под эту Caddy-интеграцию (inbound vless-ws:10095, subDomain vpn-panel), поэтому панель заработала «из коробки». Бэкап базы/Caddyfile до изменений: `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/`.
### `xray-reverse-portal` — Xray reverse portal (TrueNAS, добавлен 2026-09-01)
@@ -241,7 +250,7 @@ arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library,
| local | `:12345` SOCKS (точка входа для клиентов сети TrueNAS), проброс `12345:12345` |
| Env | `LOGLEVEL` |
-**✅ Reverse внедрён на TCP+REALITY (2026-09-01 выполнен) + ИСТИННЫЙ КОРЕНЬ НАЙДЕН 2026-09-02.** Portal пересоздан на TCP+REALITY (`12346:12346`), OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` добавлен, bridge на Kraken пересоздан. REALITY-ключи: server PrivateKey `0JTjeirfviDqCr63wSnisvU7DV6SpR9_9V2l_EUHmkk`, PublicKey `GhsphQLT_0fwOvK45MHeAgG3sBQsFtw7lQfUIbrLMQU`, shortId `1451ef281caf5905`, SNI `www.cloudflare.com`. **НЕ ВНЕДРЕНО (ждёт команды):** payload по-прежнему не проходит даже с фиксом #6612 → истинный корень [issue #6242](https://github.com/XTLS/Xray-core/issues/6242) (регрессия bridge в v26.5+, у нас 26.7.28). Решение: сдаунгрейд bridge до `teddysun/xray:26.4.25`. Детали: [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
+**✅ Reverse работает end-to-end (2026-09-02).** Portal работает на Xray `26.4.25`, TCP+REALITY+Vision (`12346:12346`); OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346`. Bridge на Kraken также закреплён на `26.4.25` и обязательно использует `network_mode: host`; Docker bridge/NAT сбрасывал reverse mux. HTTP/HTTPS egress проверен как `92.62.70.41` (Kraken). Детали и rollback: [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
### Home Assistant — детали
diff --git a/family/how-to/truenas-infrastructure.md.bak-per-user-20260902-1229 b/family/how-to/truenas-infrastructure.md.bak-per-user-20260902-1229
new file mode 100644
index 00000000..3e2ea34c
--- /dev/null
+++ b/family/how-to/truenas-infrastructure.md.bak-per-user-20260902-1229
@@ -0,0 +1,511 @@
+# TrueNAS — инфраструктура
+
+> Обновлено: 2026-09-02 (vpn.mallexxx:443 = РАБОЧИЙ Xray-сервер, проверено end-to-end; vless-proxy outbound мёртв)
+
+> ## ⚠️ ПРИОРИТЕТ 2026-09-01: VPS qentra.top УДАЛЁН → vless-proxy и сетевые туннели сломаны
+> - **VPS `91.207.28.205` больше НЕ существует** (вся его Xray/OpenVPN/WG-инфраструктура утрачена). Подробно: [[family/how-to/vps-qentra]].
+> - **Контейнер `vless-proxy`** (teddysun/xray): inbound SOCKS `0.0.0.0:1080` + HTTP `0.0.0.0:1081`, но **outbound ⇒ мёртвый `v.qentra.top:443`** (VLESS+WS+TLS, path `/qentra`, id `2D9F24C4-21FE-4784-9843-F11C384DA67A`). ⇒ Hermes-Taiga (ходит через `vless-proxy:1080`) **сейчас БЕЗ рабочего прокси**. **Уточнено 2026-09-02:** `v.qentra.top` по-прежнему ресолвится, но в **Cloudflare IP** (`104.21.54.239`, `2606:4700:...`) — DNS-запись на qentra.top осталась в Cloudflare, хотя VPS сам удалён; origin'а за ней нет, поэтому трафик через прокси не идёт. Контейнер `vless-proxy` сам ЖИВ (Up 8 days), путь через его SOCKS наружу не проходит.
+> - **⚠️ На корневом `mallexxx.duckdns.org:443` НЕ Xray**, а обычный **Caddy/nginx с валидным Let's Encrypt сертификатом** (проверено `openssl s_client` 2026-09-01). **НО на поддомене `vpn.mallexxx.duckdns.org:443` — РАБОЧИЙ Xray-сервер** (см. раздел `xray-admin` ниже, проверено end-to-end 2026-09-02).
+> - **Наружу открыты только 22 (SSH) и 443 (HTTPS/TLS)** с Kraken; `8443/8964/8080/9000` closed.
+> - **План замены** (выход клиентов сети TrueNAS через Kraken, Xray reverse — НЕ внедрён): [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
+
+> ## ✅ СТАТУС на 2026-08-25: пул ONLINE, docker поднят, почти все контейнеры Up
+> **Пул RED_2TB пересоздан (2026-08-21) и работает:** `zpool status` ONLINE, rsync возврата данных завершён, `/mnt/RED_2TB/` смонтирован.
+> **Docker: ПОДНЯТ на 2026-08-25.** Почти все контейнеры Up (zigbee2mqtt, webdav, vless-proxy, homeassistant, caddy, transmission, syncthing, mosquitto, nodered, immich*, gitea, filebrowser, portainer*, hermes-taiga и др.). ✅ **cups-splix поднят 2026-08-26** — образ собран заново (`docker build -t cups-splix .`), контейнер запущен `docker compose up -d`, порт 631 открыт, принтер `Samsung_CLX-216x_Series` активен (USB `04e8:3425`). Причина былого падения: образ локальный, потерян при восстановлении пула (`.ix-apps` не переносился), вручную не пересобирали. Рецепт — ниже, раздел «Печать / cups-splix». Ещё не подняты (проверить при надобности): inpxer/inpx-web/arr/ser2net.
+> **⚠️ Известная проблема modbus-bridge/mbusd — гонка с udev при рестарте TrueNAS:**
+> - После перезагрузки docker стартует раньше udev, поэтому `/dev/ttyZONT` и `/dev/ttyVent` на момент старта контейнеров ещё не существуют.
+> - Контейнеры с `devices: /dev/ttyZONT:...` падают на `start` с `error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory` (ExitCode 128/255, RestartCount=0).
+> - `restart: unless-stopped` НЕ перезапускает такой контейнер (отказ до старта процесса).
+> - Следствие: modbus-bridge не отвечает на шине → 485-датчики в ZONT отображаются **«недоступные»**.
+> - Ручной фикс: `docker start modbus-bridge mbusd` (после готовности tty-симлинков).
+> - Защита от рецидива (план, НЕ внедрён — ждёт OK Alex): `family/plans/zont-modbus-bridge-udev-race-protection.md` — POSTINIT init script с ожиданием симлинков + `docker start`.
+> **⚠️ ДРУГОЕ (открытое):** пул `DEST` (IronWolf) на 2026-08-24 `DEGRADED` — разобраться, почему (отвалился mirror-член). TimeMachine (~277G) не возвращён (бэкапы macOS, вероятно не нужен — решение Alex не подтверждено). SSH с Mac по 192.168.2.197 нестабилен — использовать `mallexxx.duckdns.org`.
+
+> ## 🔒 ИСТОРИЯ: статус до восстановления (2026-08-24)
+> **Пул RED_2TB пересоздан и УЖЕ зареган в TrueNAS** — `zpool status` ONLINE (`mirror-0{sdc,sdf}`+`mirror-1{sde,sdd}`), `midclt call pool.query` id=1 RED_2TB ONLINE. rsync возврата данных ЗАВЕРШЁН (storage 3.29T, immich 353G, backup 287G, Edu 214G, Photos 146G, old-bu 65G, docker, docker.bak, system — все смонтированы).
+> **`.ix-apps` датасет (docker storage, был 24G) НЕ перенесён** — rsync шёл из `/mnt/RED_2TB/*` и не включал монтируемый в `/mnt/.ix-apps` системный датасет. Образы = кеш → перекачаются из registry сами; конфиги целы в `/mnt/RED_2TB/docker/`.
+> **Контейнеры запускались через `docker-compose`, НЕ через TrueNAS Apps (TrueCharts/Custom App).** daemon.json `data-root:/mnt/.ix-apps/docker`. `.ix-apps` = просто data-root docker, не TrueNAS Apps.
+> **⚠️ ДРУГОЕ (проверено 2026-08-24):** пул `DEST` (IronWolf, источник спасения) на момент проверки `DEGRADED` в `zpool list` — стоит разобраться, почему (вероятно отвалился mirror-член). Также SSH с Mac к TrueNAS по 192.168.2.197 нестабилен (`Connection reset/reset by peer`, `kex_exchange_identification read`), возможен конфликт сессий при активном входе на консоль — использовать `mallexxx.duckdns.org` и ретраи.
+
+> ## ⚠️ СТАТУС на 2026-08-21: пул RED_2TB — данные СПАСЕНЫ, пул OFFLINE, пересоздаём
+> **Выгрузка успешно завершена** (подтверждено Alex): данные RED_2TB (4.56T) на IronWolf (пул `DEST`, `/mnt/IRONWOLF`). **Пул RED_2TB — статус OFFLINE** в БД TrueNAS, НЕ импортирован. Конфиги docker (caddy, transmission, HA, immich и т.д.), Obsidian vault и git-репо спасены на `/mnt/IRONWOLF` (`system/`, `docker.bak/`, `docker/`, `storage/obsidian`, `storage/git`).
+> **ПЛАН (следующий шаг):** пересоздать пул RED_2TB с новой топологией `mirror {sdc,sdf}` + `mirror {sde,sdd}` (включаем WD2TB#2 зеркалом к WD2TB#1). Команды `zpool labelclear` + `zpool create` — в `family/how-to/truenas-sata-ports-and-zfs-pools.md` (раздел «Пересоздание пула 2026-08-21»). Затем вернуть данные rsync с IronWolf и восстановить инфраструктуру.
+> **ПРОТОКОЛ ПОВРЕЖДЕНИЯ (история):** пул паниковал на rw-импорте (`adding existent segment to range tree`, повреждённая space map). Работал только `zpool import -o readonly=on`. In-place починки нет (mav/iXsystems) — только спасать данные и пересоздавать. Полный рецепт — в `family/how-to/truenas-sata-ports-and-zfs-pools.md`.
+
+## Доступ
+
+- **Host (external):** `mallexxx.duckdns.org` (DuckDNS) — **единственный способ** SSH/API из 192.168.1.x
+- **SSH:** `truenas_admin@mallexxx.duckdns.org -i ~/.ssh/id_rsa`
+- **Web UI:** http://truenas.mallexxx.duckdns.org
+- **Portainer:** https://portainer.mallexxx.duckdns.org
+- **Host (local network):** `192.168.2.197` — ⚠️ ИСПОЛЬЗОВАТЬ `ssh truenas_admin@mallexxx.duckdns.org`. НЕ ИСПОЛЬЗОВАТЬ локальный IP для SSH доступа!
+ - ⚠️ Eagle (Mac, 192.168.1.x) и Kraken (192.168.1.15) находятся в **другой подсети**
+ - ⚠️ Кра́кен и TrueNAS — **разные сети** (192.168.1.x vs 192.168.2.x). Доступ с Кракена на TrueNAS по локальному IP **не работает**
+
+## Пользователи и группы (ключевые)
+
+| uid | Имя | gid | Назначение |
+|-----|-----|-----|------------|
+| 950 | truenas_admin | 950 | SSH-пользователь, управление docker |
+| 911 | transdamon | 911 | Процесс Transmission внутри контейнера |
+| 921 | transmission | 921 | Старый пользователь (не используется контейнером) |
+| 3000 | nas_users | 3000 | Общая группа доступа к NAS |
+
+## Структура папок
+
+```
+/mnt/RED_2TB/
+├── docker/ ← конфиги docker-контейнеров (бэкапятся → mailru-crypt:)
+│ ├── caddy/
+│ ├── filebrowser/
+│ ├── ha/ ← Home Assistant
+│ ├── hermes/ ← Hermes-Taiga
+│ ├── homeassistant/
+│ ├── immich/
+│ ├── inpx-web/
+│ ├── inpxer/
+│ ├── mbusd/
+│ ├── modbus-bridge/
+│ ├── mosquitto/
+│ ├── nodered/
+│ ├── portainer/
+│ ├── python/
+│ ├── rclone/
+│ ├── ser2net/
+│ ├── transmission/ ← конфиг Transmission (settings.json, torrents, resume...)
+│ ├── vless-proxy/
+│ ├── watchtower/
+│ ├── webdav/
+│ └── zigbee2mqtt/
+├── storage/
+│ ├── Downloads/ ← данные торрентов (монтируется в контейнер как /mnt/storage)
+│ │ ├── transmission/ ← старый путь конфига (больше не используется)
+│ │ └── [медиафайлы]
+│ └── obsidian/ ← vault (read-only для hermes-taiga)
+├── backup/ ← ручные бэкапы (бэкапятся → mailru-crypt:)
+│ └── transmission-config/ ← снапшот конфига от 2026-04-30
+├── Photos/ ← фото (бэкапятся → mailru:Photos/Photos)
+├── old-bu/ ← старый архив (бэкапятся → mailru:Photos/old-bu)
+└── immich-photos-upload/ ← библиотека Immich (бэкапятся → mailru:Photos/immich)
+```
+
+## NFSv4 ACL — важно
+
+TrueNAS использует **NFSv4 ACL**, а не стандартный POSIX chmod/chown.
+- `ls -la` показывает `----------` даже если ACL есть → всегда проверять через `midclt call filesystem.getacl `
+- Добавить запись: `midclt call filesystem.setacl '{...}'`
+- `truenas_admin` не имеет passwordless sudo → root-операции только через midclt или TrueNAS UI
+
+## Docker-контейнеры
+
+| Контейнер | Image | Порт | Домен |
+|-----------|-------|------|-------|
+| transmission | linuxserver/transmission:latest | 9091, 51413 | transmission.mallexxx.duckdns.org |
+| hermes-taiga | hermes-taiga:latest | — | — |
+| vless-proxy | teddysun/xray:latest | — | — |
+| filebrowser | filebrowser/filebrowser:latest | — | — |
+| webdav | hacdias/webdav:latest | — | webdav.mallexxx.duckdns.org |
+| homeassistant | home-assistant:stable | 8123 | mallexxx.duckdns.org |
+| immich-server | immich-app/immich-server:release | 2283 | immich.mallexxx.duckdns.org |
+| immich-postgres | tensorchord/pgvecto-rs:pg14-v0.2.0 | — | — |
+| immich-redis | redis:7 | — | — |
+| nodered | nodered/node-red:latest | 1880 | nodered.mallexxx.duckdns.org |
+| mosquitto | eclipse-mosquitto:latest | — | — |
+| caddy | caddy:latest | 80, 443 | reverse proxy для всего |
+| rclone | rclone/rclone:latest | — | бэкап по cron |
+| portainer | portainer/portainer-ce:latest | 9000 | portainer.mallexxx.duckdns.org |
+| watchtower | containrrr/watchtower:latest | — | авто-обновление образов |
+| inpxer | ghcr.io/hedger/inpxer:latest | 18080 | books.mallexxx.duckdns.org |
+| nodered | node-red:latest | 1880 | nodered.mallexxx.duckdns.org |
+| zigbee2mqtt | koenkk/zigbee2mqtt:latest | — | — |
+| mbusd | 3cky/mbusd:latest | — | Modbus |
+| modbus-bridge | modbus-bridge | — | — |
+| cups-splix | cups-splix | — | принтер |
+| xray-admin | ghcr.io/mhsanaei/3x-ui:latest | 2053 (панель), 10095 (vless-ws), 443 (sub) | vpn-panel.mallexxx.duckdns.org, vpn.mallexxx.duckdns.org |
+| xray-reverse-portal | teddysun/xray:latest | 12345 (SOCKS), 12346 (VLESS-TCP REALITY interconn) | — (см. `personal/tech/xray-reverse-tunnel-kraken-truenas.md`) |
+
+### cups-splix — принтер Samsung CLX-216x (✅ поднят 2026-08-26; ✅ mDNS-фикс 2026-08-31 завершён)
+
+**Статус:** контейнер работает, принтер `Samsung_CLX-216x_Series` **idle / enabled / accepting**. CUPS слушает `0.0.0.0:631` (host-сеть). mDNS-анонс **включён** — штатная Android print service теперь видит принтер по mDNS (подробно: [[mac-print-shared-services]]). См. ниже раздел «✅ mDNS/AirPrint анонс принтера».
+
+Папка: `/mnt/RED_2TB/docker/cups/` → `Dockerfile`, `entrypoint.sh`, `docker-compose.yml`, `data/`→`/etc/cups`, `cache/`→`/var/cache/cups`, `spool/`→`/var/spool/cups`, `uld/`, бэкап `backup_20260831-003619.tar.gz`.
+
+- **Image:** `cups-splix` (локальная сборка из Dockerfile; старый образ сохранён как `cups-splix-old` для отката)
+- **Dockerfile** (debian:12-slim): `cups-daemon cups-client cups-common printer-driver-splix avahi-daemon avahi-utils dbus usbutils nano procps`; `cupsadmin:admin` (группа lpadmin); `COPY entrypoint.sh`; `EXPOSE 631`; `ENTRYPOINT ["/entrypoint.sh"]`
+- **entrypoint.sh:** `dbus-daemon --system --fork` → `avahi-daemon --no-drop-root --syslog` (фон) → `exec cupsd -f`
+- **compose:** `image: cups-splix`, `network_mode: host`, `privileged: true`, volume `/dev/bus/usb:/dev/bus/usb`, restart `unless-stopped` (содержит устаревший `command: [cupsd -f]`, но ENTRYPOINT переопределяет — правится только через root/UI, файл 600 root)
+- **Сборка/поднятие (⚠️ полное пересоздание, НЕ `docker restart`):**
+```bash
+cd /mnt/RED_2TB/docker/cups/
+docker build -t cups-splix-new .
+docker stop cups-splix && docker rm cups-splix
+docker tag cups-splix-new cups-splix && docker tag cups-splix cups-splix-old 2>/dev/null
+docker compose up -d
+```
+
+> Полный разбор и симптом «телефон не видит принтер»: [[mac-print-shared-services]]
+
+### Инвентарь compose-файлов по папкам (проверено 2026-08-24)
+
+Каждый контейнер = своя папка `/mnt/RED_2TB/docker//`. Модель — **не один общий compose, а per-service compose** (контейнеры поднимались `docker compose up -d` из своей папки / `docker run`).
+
+**✅ Есть `docker-compose.yml` (поднимаются `docker compose -f ... up -d`)** — 22 шт:
+arr, caddy, cups, filebrowser, gitea, hermes, immich, inpx-web, inpxer, library, mbusd, mosquitto, nodered, portainer-mcp, portainer, rclone, ser2net, syncthing, transmission, vless-proxy, watchtower, **xray-admin (3x-ui, добавлен 2026-09-01)**
+
+**⚠️ НЕТ compose-файла — только конфиг (восстанавливать вручную, параметры из доки ниже):**
+- **ha** — Home Assistant: `/mnt/RED_2TB/docker/ha/` (automations.yaml). Образ `ghcr.io/home-assistant/home-assistant:stable`, порт `8123`, volume `/mnt/RED_2TB/docker/ha` → `/config`.
+- **webdav** — `/mnt/RED_2TB/docker/webdav/` (config.yml). Образ `hacdias/webdav:latest`, домен webdav.mallexxx.duckdns.org.
+- **modbus-bridge** — `/mnt/RED_2TB/docker/modbus-bridge/` (config.yml + modbus_ha_bridge.py). Образ `modbus-bridge` (локальная сборка из репо `HA-ZONT-Modbus`). Volumes: config.yml → `/app/config.yml` (ro), modbus_ha_bridge.py → `/app/modbus_ha_bridge.py` (ro).
+- **zigbee2mqtt** — `/mnt/RED_2TB/docker/zigbee2mqtt/` (configuration.yaml). Образ `koenkk/zigbee2mqtt:latest`, работает через mosquitto.
+- **homeassistant** — в доке упомянут как отдельный контейнер (порт 8123, volume → /config); папка `docker/homeassistant/` есть, но название контейнера в таблице `homeassistant`, а папка конфига HA фактически `ha/`. При восстановлении сверить, какой реально используется.
+
+### Порядок восстановления docker-стека (зависимости)
+
+1. **docker engine:** `systemctl enable --now docker` (после создания data-root, см. шапку). Проверка: `docker info | grep -iE "Server Version|Docker Root Dir"`.
+2. **Инфраструктура/сеть:** mosquitto (MQTT) → zigbee2mqtt (нужен mosquitto) → nodered (MQTT) → caddy (прокси, тянет все домены) → vless-proxy (SOCKS5 для hermes).
+3. **Умный дом:** mbusd (Modbus TCP:502) → modbus-bridge (через mbusd) → ha (homeassistant, нужен caddy для домена).
+4. **Медиа/хранилище:** transmission (нужен caddy) → arr (radarr/sonarr/prowlarr) → inpxer (books), inpx-web, library → rclone, syncthing, filebrowser, webdav, gitea.
+5. **Сервисы/свои:** hermes-taiga (нужен vless-proxy), portainer, portainer-mcp, watchtower, cups, ser2net.
+6. **Проверка:** `docker ps`. HA: `docker exec homeassistant python -m homeassistant --script check_config --config /config`.
+
+### Transmission — детали
+- **Config:** `/mnt/RED_2TB/docker/transmission/` (монтируется как `/config`; внутри `/config/docker-compose.yml`)
+- **Данные:** `/mnt/RED_2TB/storage` (монтируется как `/mnt/storage`)
+- **Download dir:** `/mnt/storage/Downloads`
+- **UID/GID:** ⚠️ **по факту работает как ROOT (uid 0)**, НЕ 911/911. Причина: в `/mnt/RED_2TB/docker/transmission/docker-compose.yml` **НЕ задан `PUID`/`PGID`** → linuxserver `/init` (s6-overlay) оставляет контейнер root (подтверждено `docker exec transmission id` → root). Пользователь `transdamon`/911 (док) — это владелец конфига на хосте (`drwx------ 911 911`), но НЕ процесс внутри контейнера.
+ - **Зачем важно:** root-трансмишен пишет файлы как root и переживает права 0000 медиа → это причина, почему он "работает" на сломанных после restore папках. Но для pipeline с jellyfin/radarr (не-root) root-создаваемые файлы барьер.
+ - **План фикса (Вариант A, одобрен 2026-09-02):** добавить `PUID=950 PGID=950` и пересоздать контейнер → работает под `truenas_admin`. План: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md` §5.
+- **RPC whitelist:** `172.16.6.*`
+- **Web:** https://transmission.mallexxx.duckdns.org
+- **Порты:** 9091 (RPC/web), 51413 (TCP/UDP peers)
+
+### Caddy — домены
+| Домен | → |
+|-------|---|
+| mallexxx.duckdns.org | Home Assistant :8123 |
+| transmission.mallexxx.duckdns.org | Transmission :9091 |
+| immich.mallexxx.duckdns.org | Immich :2283 |
+| nodered.mallexxx.duckdns.org | Node-RED :1880 |
+| webdav.mallexxx.duckdns.org | WebDAV |
+| books.mallexxx.duckdns.org | Inpxer :18080 🔒 basicauth (user: books-admin) |
+| library.mallexxx.duckdns.org | library-app :8080 🔒 basicauth (user: books-admin) |
+| portainer.mallexxx.duckdns.org | Portainer :9000 |
+| truenas.mallexxx.duckdns.org | TrueNAS UI :80 |
+| cam.mallexxx.duckdns.org | Камера :8090 |
+| docs.mallexxx.duckdns.org | Docs :8000 |
+| **vpn-panel.mallexxx.duckdns.org** | **3x-ui панель :2053** (добавлен 2026-09-01) |
+| **vpn.mallexxx.duckdns.org** | **Xray VLESS-WS (путь /vless) → xray-admin:10095** (добавлен 2026-09-01) |
+
+### `xray-admin` — 3x-ui панель (Xray server, добавлена 2026-09-01)
+
+**Цель:** 3x-ui frontend для Xray-reverse туннеля Kraken↔TrueNAS (выход клиентов сети TrueNAS через Kraken). Подробно: [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
+
+| Параметр | Значение |
+|----------|----------|
+| Папка | `/mnt/RED_2TB/docker/xray-admin/` |
+| Контейнер | `xray-admin` (имя важно — Caddyfile резолвит по нему) |
+| Образ | `ghcr.io/mhsanaei/3x-ui:latest` (3.7.0, Xray 26.7.28) |
+| Volume | `/mnt/RED_2TB/docker/xray-admin:/etc/x-ui` (там `x-ui.db`) |
+| Сеть | `caddy_default` (внешняя) |
+| Панель | `https://vpn-panel.mallexxx.duckdns.org/` (Caddy → xray-admin:2053) |
+| Inbound | `vless-ws` port `10095`, tag `in-10095-tcp`, path `/vless`, host `vpn.mallexxx.duckdns.org`, client id `ce320965-6956-4759-84bb-7cb71cfc6252` (user1) |
+| Sub-сервер | `[::]:443` (path /sub), панель `[::]:2053` |
+| Env | `TZ=Asia/Novosibirsk`, `PUID=950`, `PGID=950` |
+| Ports (host) | `54321:54321` (не используется панелью — внутри панель на 2053, можно убрать) |
+
+**Статус (2026-09-01):** контейнер **Up**, панель HTTP 200. Reverse **портал** развёрнут отдельным контейнером `xray-reverse-portal` (см. ниже). 3x-ui поддерживает reverse (`clientReverseTags`).
+
+**✅ ПРОВЕРЕНО РАБОТАЕТ (2026-09-02): `vpn.mallexxx.duckdns.org:443` — это ФУНКЦИОНАЛЬНЫЙ Xray-СЕРВЕР, НЕ только reverse-frontend.** Проверено end-to-end юзером с Mac. Ключевые факты с живой машины:
+- Контейнеры `xray-admin` (Up 21h) и `caddy` (Up 20h) оба живы. Панель `vpn-panel` → HTTP 200. TCP `vpn.mallexxx.duckdns.org:443` снаружи открыт.
+- **Inbound `in-10095-tcp`** (реальный конфиг из `bin/config.json`, Xray 26.7.28): VLESS-WS, `security: none`, WS path `/vless`, host `vpn.mallexxx.duckdns.org`.
+ - **client id:** `ce320965-6956-4759-84bb-7cb71cfc6252` (email `user1`)
+- **Outbound:** `freedom`/`direct` — сервер имеет **СОБСТВЕННЫЙ выход в интернет** через TrueNAS (не через Kraken!). routing: geoip:private + bittorrent → blocked.
+- **Тест с Mac** (временный xray-клиент в docker): VLESS+WS через `vpn.mallexxx.duckdns.org:443` → HTTP 200, выходной IP `90.189.160.148` (TrueNAS). Сервер полностью рабочий.
+- **⚠️ Так и твой клиент должен подключаться:** TLS терминирует Caddy на 443, inbound `security: none` → в конфиге КЛИЕНТА `network: ws`, TLS поверх на `vpn.mallexxx.duckdns.org` (**НЕ включать двойной TLS внутрь WS**).
+- **⚠️ Xray 26.7.28 удалил `allowInsecure`** в клиентских tlsSettings (мигрировано на `pinnedPeerCertSha256`/`verifyPeerCertByName`). Если в клиентском конфиге был `allowInsecure: true` — клиент падает. Это частая причина «не подключается».
+
+> ⚠️ База `x-ui.db` была **уже настроена** под эту Caddy-интеграцию (inbound vless-ws:10095, subDomain vpn-panel), поэтому панель заработала «из коробки». Бэкап базы/Caddyfile до изменений: `/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/`.
+
+### `xray-reverse-portal` — Xray reverse portal (TrueNAS, добавлен 2026-09-01)
+
+**Цель:** portal-часть Xray-reverse туннеля: принимает исходящий трафик локальных клиентов сети TrueNAS (через SOCKS `:12345`) и пересылает по reverse-каналу на Kraken (bridge) → интернет через Kraken. Подробно: [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
+
+| Параметр | Значение |
+|----------|----------|
+| Папка | `/mnt/RED_2TB/docker/reverse-portal/` |
+| Контейнер | `xray-reverse-portal` |
+| Образ | `teddysun/xray:latest` (Xray 26.7.28) |
+| Volume | `config.json:/etc/xray/config.json:ro` |
+| Сеть | `caddy_default` |
+| interconn | `:12346` VLESS (принимает reverse-канал от Kraken) — **сейчас на TCP+REALITY** (внутр. порт 12346, проброс `12346:12346`) |
+| local | `:12345` SOCKS (точка входа для клиентов сети TrueNAS), проброс `12345:12345` |
+| Env | `LOGLEVEL` |
+
+**✅ Reverse внедрён на TCP+REALITY (2026-09-01 выполнен) + ИСТИННЫЙ КОРЕНЬ НАЙДЕН 2026-09-02.** Portal пересоздан на TCP+REALITY (`12346:12346`), OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` добавлен, bridge на Kraken пересоздан. REALITY-ключи: server PrivateKey `0JTjeirfviDqCr63wSnisvU7DV6SpR9_9V2l_EUHmkk`, PublicKey `GhsphQLT_0fwOvK45MHeAgG3sBQsFtw7lQfUIbrLMQU`, shortId `1451ef281caf5905`, SNI `www.cloudflare.com`. **НЕ ВНЕДРЕНО (ждёт команды):** payload по-прежнему не проходит даже с фиксом #6612 → истинный корень [issue #6242](https://github.com/XTLS/Xray-core/issues/6242) (регрессия bridge в v26.5+, у нас 26.7.28). Решение: сдаунгрейд bridge до `teddysun/xray:26.4.25`. Детали: [[personal/tech/xray-reverse-tunnel-kraken-truenas]].
+
+### Home Assistant — детали
+
+- **Image:** `ghcr.io/home-assistant/home-assistant:stable`
+- **Порт:** `8123:8123`
+- **Volume:** `/mnt/RED_2TB/docker/ha` → `/config`
+- Конфиги редактируются **напрямую на NAS** — `docker cp` не нужен, изменения применяются после reload/restart HA.
+
+**Ключевые файлы конфига:**
+
+| Файл | Назначение |
+|------|------------|
+| `configuration.yaml` | Главный конфиг: интеграции, template sensors, http trusted_proxies |
+| `automations.yaml` | Все автоматизации (подключён через `!include`) |
+| `scripts.yaml` | Скрипты |
+| `.storage/lovelace.home_plan` | Dashboard с планом этажей (JSON, управляется HA UI) |
+| `www/floorplan/floor1_ha.svg` | SVG фон первого этажа |
+| `www/floorplan/floor2_ha.svg` | SVG фон второго этажа |
+
+**Перезапуск после редактирования конфига:**
+```bash
+ssh truenas_admin@mallexxx.duckdns.org "docker restart homeassistant"
+```
+
+**Проверка конфига перед перезапуском:**
+```bash
+ssh truenas_admin@mallexxx.duckdns.org "docker exec homeassistant python -m homeassistant --script check_config --config /config"
+```
+
+### mbusd — Modbus RTU → TCP gateway
+
+Мост Modbus RTU → TCP: пробрасывает серийный порт в TCP 502. Используется и для вентиляции (AT2), и для ZONT-шины.
+
+- **Image:** `3cky/mbusd`, compose в `/mnt/RED_2TB/docker/mbusd/`
+- **Порт:** `502:502` (TCP)
+- **Config:** `/mnt/RED_2TB/docker/mbusd/mbusd.conf` → `/etc/mbusd.conf` (`device=/dev/ttyUSB0 speed=9600 mode=8n1`, `port=502`)
+- **Device:** `devices: - /dev/ttyVent:/dev/ttyUSB0`
+- **Entrypoint:** `["/usr/bin/mbusd","-d","-L","-","-c","/etc/mbusd.conf"]`
+- **Pitfall udev-race:** см. ниже «Проблема modbus-bridge/mbusd после рестарта».
+
+### modbus-bridge — 485-датчики + виртуальные slaves для ZONT
+
+Кастомный Python-мост (`modbus_ha_bridge.py`, репо `HA-ZONT-Modbus`), контейнер `/mnt/RED_2TB/docker/modbus-bridge/`.
+
+- **Image:** `modbus-bridge` (локальная сборка, `--no-cache`). **НЕ имеет compose-файла** — создавался через `docker-compose`-подобный run; для восстановления использовать параметры ниже.
+- **Device:** `devices: - /dev/ttyZONT:/dev/ttyUSB0` (внутри контейнера слушает `/dev/ttyUSB0`)
+- **Volumes:** `config.yml` → `/app/config.yml` (ro), `modbus_ha_bridge.py` → `/app/modbus_ha_bridge.py` (ro)
+- **Env (из compose):** `HA_TOKEN`, `MQTT_USER=zont`, `MQTT_PASS=...`
+- **`network_mode: host`**; MQTT broker `localhost:1883` (mosquitto), HA `http://localhost:8123`.
+
+**Роль (важно — два режима работы):**
+1. **Sniff (чтение)** — сниффит реальные 485-датчики на шине ZONT: **Dining=slave 1, Kids=slave 2, Bedroom=slave 3** (CO₂/температура/влажность, регистры 2 и 100) и публикует в MQTT (`modbus/sensors//...`), а также пишет в HA.
+2. **Mappings (виртуальные slaves для ZONT)** — подставляет на **slave 101/102/103** (регистр 100) значения temperature от 485-датчиков: Dining→101, Kids→102, Bedroom→103. **ZONT (Modbus master) опрашивает 101/102/103 как свои внешние датчики температуры.** Есть также wifi-термостат как slave 100 (`sensor.0xa4c13862d39377e6_temperature`) и Socket 1 = slave 101 через switch (обратим внимание: 101 занят и Dining temp — переиспользуется на одном slave с разными функциями; сверить в config.yml).
+3. Также через mbusd может работать с AT2 вентиляции.
+
+**Если modbus-bridge не слушает `ttyZONT`** (упал/не запущен) → ZONT не получает ответ на запросы slave 101/102/103 → в интерфейсе ZONT 485-датчики отображаются **«недоступные»**.
+
+### Проблема modbus-bridge/mbusd после рестарта TrueNAS (гонка с udev)
+
+**Симптом:** после перезагрузки TrueNAS `/dev/ttyZONT` и `/dev/ttyVent` присутствуют (`ttyZONT`→ttyUSB1, `ttyVent`→ttyUSB0, создаются init script id=1), НО контейнеры `modbus-bridge`/`mbusd` упали на старте и не перезапустились; 485-датчики в ZONT «недоступные».
+
+**Причина:** docker стартует раньше udev → при `start` контейнера устройство ещё не существует → падение ДО запуска процесса:
+```
+docker inspect --format '{{.State.Error}}'
+error gathering device information while adding custom device "/dev/ttyZONT": no such file or directory
+# ExitCode 128 / 255, RestartCount=0
+```
+`restart: unless-stopped` такой отказ **не подхватывает** (RestartCount=0), и задержка внутри entrypoint не поможет (device mount происходит до старта процесса).
+
+**Ручной фикс:** `docker start modbus-bridge mbusd` (после того как симлинки созданы).
+
+**Защита от рецидива (план, НЕ внедрён, ждёт OK Alex):** `family/plans/zont-modbus-bridge-udev-race-protection.md` — POSTINIT init script `/mnt/RED_2TB/system/start-modbus.sh`, который ждёт появления `ttyZONT`/`ttyVent` (≤25с) и делает `docker start modbus-bridge mbusd`.
+
+### USB device aliases
+
+Файл правил: `/mnt/RED_2TB/system/99-tty-alias.rules`
+
+```
+SUBSYSTEM=="tty", KERNEL=="ttyUSB*", KERNELS=="?-1.6:1.0", SYMLINK+="ttyZONT"
+SUBSYSTEM=="tty", KERNEL=="ttyUSB*", KERNELS=="?-1.5:1.0", SYMLINK+="ttyVent"
+```
+
+`?` — wildcard на префикс USB-шины (`2`, `3` и т.д.): правила работают даже если хаб переопределяется под другой контроллер (например, после подключения USB3-устройства к тому же хабу).
+
+Применить после изменений:
+```bash
+cp /mnt/RED_2TB/system/99-tty-alias.rules /etc/udev/rules.d/ && udevadm control --reload-rules && udevadm trigger
+```
+
+> `/etc/udev/rules.d/` на TrueNAS — tmpfs, не переживает перезагрузку. Правила применяются автоматически через **TrueNAS Init/Shutdown Scripts** (POSTINIT).
+
+**Просмотреть/изменить:** TrueNAS UI → System → Advanced → Init/Shutdown Scripts, или:
+```bash
+midclt call initshutdownscript.query
+```
+
+Текущая запись (id 1, POSTINIT, comment: "Map ttyUSB"):
+```bash
+cp /mnt/RED_2TB/system/99-tty-alias.rules /etc/udev/rules.d/ && udevadm control --reload-rules && udevadm trigger
+```
+
+## Hermes-Taiga агент
+
+| Параметр | Значение |
+|----------|----------|
+| Контейнер | `hermes-taiga` |
+| Telegram | через общий токен, SOCKS5 прокси `vless-proxy:1080` |
+| Модель | **DeepSeek Chat** (`deepseek/deepseek-chat`) |
+| Auxiliary | OpenRouter (`openrouter/auto`) |
+| Vault (хост) | `/mnt/RED_2TB/storage/obsidian` |
+| Vault (контейнер) | `/vault:rw` |
+| obsidian-mcp | `/opt/data/.local/bin/mcpvault /vault` |
+| Scope | sparse: `personal/ + family/` |
+| Toolsets | hermes-cli, file, web, browser |
+| web_search | Tavily (`TAVILY_API_KEY` в .env) |
+| web_extract | ✅ HTTP-парсинг |
+| browser | ✅ Playwright/Chromium (headless, JS-heavy сайты) |
+| Прокси | SOCKS5 `vless-proxy:1080` (HTTP/HTTPS/Telegram — все через прокси) |
+
+### SOUL.md — identity & persona (обновлено 2026-05-13)
+
+Файл: `/opt/data/SOUL.md` (монтируется в контейнер, читается Hermes на каждый тёрн).
+
+**Ключевые изменения 2026-05-13:**
+- Добавлен блок `## Identity` — Тайга не ассистент, а лес: не извиняется, не суетится, говорит прямо
+- Добавлен uncertainty posture: при отсутствии данных говорит "не знаю" / "нет данных в vault", не строит истории
+- Тон исправлен: убрано "тёплая" (активировало female-helper архетип), оставлено "спокойная, уверенная, немногословная"
+- Добавлена обязанность читать и писать в Obsidian (RULE 2, Vault Access с правильными MCP-командами)
+- `display.personality` в config.yaml изменён с `helpful` на `neutral` — убирает Hermes-level "helpful" прайминг поверх soul
+
+**Почему важно:** `display: personality: helpful` в config.yaml инжектировался поверх soul.md и был основным источником извинений и гиперкомпенсации.
+
+### Починка root-проблемы (2026-05-13)
+Hermes обновился — появилась проверка на root. Контейнер падал с `Refusing to run as root`.
+Фикс: `HERMES_ALLOW_ROOT_GATEWAY=1` + `UV_CACHE_DIR=/tmp/uv-cache` в docker-compose.yml + пересборка с `--no-cache`.
+
+> ⚠️ При обновлении образа — всегда `docker build --no-cache`, иначе `.venv` остаётся от root-слоя и ломает permissions.
+
+## Obsidian Sync (Taiga)
+
+**Скрипт:** `~/sync-vault.sh` (запускается на **хосте**, не в контейнере)
+**Крон:** Hermes cron job `0 * * * *`
+**Bare repo:** `file:///mnt/RED_2TB/storage/git/obsidian-vault.git`
+**Лог:** `/tmp/vault-sync.log`
+
+```bash
+# Запустить sync вручную:
+bash ~/sync-vault.sh
+
+# Посмотреть лог:
+tail -20 /tmp/vault-sync.log
+
+# История коммитов:
+git -C /mnt/RED_2TB/storage/git/obsidian-vault.git log --oneline -10
+```
+
+> ⚠️ Скрипт запускается от `truenas_admin` — только этот пользователь имеет ZFS ACL доступ к bare repo и vault папке.
+
+Подробности: [[obsidian-sync]]
+
+## Home Assistant — что настроено
+
+### `configuration.yaml`
+
+- `http:` раздел: `use_x_forwarded_for: true`, `trusted_proxies: 172.16.0.0/12` — обязательно для работы через reverse proxy (Caddy)
+- Modbus интеграция для вентиляторов AT2 и заслонок
+- Template sensors (в блоке `template: - sensor:`):
+
+| Сенсор | Пример | Описание |
+|--------|--------|----------|
+| `dining_summary` | "24° 450ppm" | Темп и CO₂ в столовой |
+| `dining_air_summary` | "65tvoc 3pm" | TVOC и частицы в столовой |
+| `kids_summary` | "22° 600ppm" | Темп и CO₂ в детской |
+| `bedroom_summary` | "21° 500ppm" | Темп и CO₂ в спальне |
+| `at2_1_summary` | "Off" / "45%" | AT2-1: объединённые on/off + скорость |
+| `at2_2_summary` | "Off" / "45%" | AT2-2: объединённые on/off + скорость |
+
+### `automations.yaml`
+
+- ГВС циркуляция: вкл (09:30) / выкл (23:00)
+- Вентиляционный вентилятор вкл в 05:00
+- Проходной выключатель кабинета → toggle света в кабинете (`not_from: [unavailable, unknown]` — защита от ложных срабатываний при запуске HA)
+- Подсветка лестницы: вкл/выкл по датчику освещённости
+- Диммер спальни: toggle и цикл яркости
+- Ночной свет в душе: присутствие + освещённость
+- Уведомление: датчик протечки (котельная)
+- Уведомления: низкий заряд батареи (несколько устройств)
+
+### Floor Plan Dashboard (`lovelace.home_plan`)
+
+Два вида — Ground Floor (`floor1`) и Second Floor (`floor2`) — `picture-elements` поверх SVG фона.
+
+**Первый этаж (`floor1`):**
+- Приточные заслонки: столовая (левая/правая), кабинет
+- Вытяжные заслонки: кухня, туалет 1F
+- Вентилятор 3 (кухонная вытяжка)
+- Метки AT2-1 / AT2-2 (`sensor.at2_1_summary` / `sensor.at2_2_summary`)
+- Сауна, обогревательный кабель
+- Свет кабинет (левый/правый)
+- Метка столовой (`sensor.dining_summary`) и качество воздуха (`sensor.dining_air_summary`)
+
+**Второй этаж (`floor2`):**
+- Приточные заслонки: детская, спальня, север
+- Вытяжные заслонки: ванная, душ 2F
+- Свет спальни (диммер)
+- Метки: `sensor.kids_summary`, `sensor.bedroom_summary`
+
+**SVG dark mode** — встроенный CSS в SVG-файлах:
+```svg
+
+```
+
+> ЖJSON Lovelace зафиксирован в git по пути `floorplan/lovelace.home_plan.json` (справочный снимок, HA **не** подгружает его автоматически).
+
+---
+
+## Печать / cups-splix (2026-08-26, починено)
+
+**Принтер Samsung CLX-216x подключён по USB к TrueNAS** (не в сети!). Единственный путь печати — контейнер `cups-splix`, который раздаёт CUPS на `192.168.2.197:631`.
+
+**Почему телефон не видел принтер (2026-08-26):** после пересоздания пула контейнер `cups-splix` **не был поднят** — образ локальный (не registry), стёрлся вместе с `.ix-apps`; при восстановлении его не пересобирали (пометка «проверить при надобности»). Конфиг `/mnt/RED_2TB/docker/cups/data` (это `/etc/cups`) цел → принтер прописан и подцепился после рестарта сам.
+
+**Рецепт подъёма (при «не вижу принтер»):**
+```bash
+cd /mnt/RED_2TB/docker/cups
+docker build -t cups-splix . # образ локальный, собрать заново (~3.5 мин)
+docker compose up -d # поднять (privileged + /dev/bus/usb)
+docker exec cups-splix lpstat -p -d # проверить принтер
+nc -z 192.168.2.197 631 # проверить порт наружу
+```
+- Принтер: `usb://Samsung/CLX-216x%20Series?serial=9566BAGQ213134Z.&interface=1` (USB ID `04e8:3425` Samsung CLX-216x).
+- Порт 631: открыт наружу после подъёма.
+
+### ✅ mDNS/AirPrint анонс принтера — включён 2026-08-31 (Android print service видит принтер)
+
+Проблема: Android **штатная служба печати** (в отличие от Samsung Mobile Print, который ходит прямо по IP) ищет принтер **только через mDNS/Bonjour** (`_ipp._tcp.local`). CUPS не анонсировал: в контейнере не было `avahi-daemon`, а `printer-dns-sd-name` в IPP был `no-value` → Android помечал принтер «недоступен», даже добавленный вручную по IP.
+
+**Решение (выполнено):** пересобрали cups-splix — теперь запускает `dbus-daemon` + `avahi-daemon` рядом с cupsd через `entrypoint.sh`, и в `cupsd.conf` включено `Browsing On` + `BrowseLocalProtocols dnssd`. После этого CUPS регистрирует принтер в avahi → mDNS-анонс с **`mopria-certified=1.3`** на IPv4 `192.168.2.197` физического интерфейса `enp3s0`. Android (Mopria) теперь находит принтер сам в локальной сети.
+
+**Что изменилось в конфиге** (папка `/mnt/RED_2TB/docker/cups/`):
+- `Dockerfile`: добавлен `avahi-daemon` в apt, написан `entrypoint.sh`, `ENTRYPOINT` вместо `CMD` (`.old` образ сохранён как `cups-splix-old`).
+- `entrypoint.sh`: стартует `dbus-daemon --system`, затем `avahi-daemon --no-drop-root`, затем `exec cupsd -f`.
+- `data/cupsd.conf`: `Browsing Off` → `Browsing On` + добавлена строка `BrowseLocalProtocols dnssd` (бэкап `cupsd.conf.pre-avahi`).
+
+**⚠️ Важный питфолл:** после правки конфига НЕ использовать `docker restart cups-splix` — он убивает avahi-daemon (defunct), перед новым подъёмом обязателен **`docker stop cups-splix && docker rm cups-splix && docker compose up -d`** (полный пересоздание, чтобы entrypoint перевыполнился с нуля).
+
+**Проверка анонса:**
+```bash
+docker exec cups-splix sh -c "timeout 6 avahi-browse -rt -p _ipp._tcp | grep ';enp3s0;IPv4;'"
+# должно показать: ... Samsung\032CLX-216x... truenas.local;192.168.2.197;631; "mopria-certified=1.3" ...
+```
+
+**Бэкап конфига (2026-08-31):** `/mnt/RED_2TB/docker/cups/backup_20260831-003619.tar.gz` (md5 `0e303a6a57ae767d1e14cd72c8039fe4`) — tar из /etc/cups, /var/spool/cups, /var/cache/cups.
+
+> Диагностика и полное объяснение: [[mac-print-shared-services]]
+
+> ⚠️ **2026-08-31: топология изменена.** TrueNAS больше НЕ в локальной сети (только публичный DNS `mallexxx.duckdns.org`) — из других подсетей/внешки `192.168.2.197` недостижим, и **снаружи `mallexxx.duckdns.org:631` закрыт**. НО **из самой подсети `192.168.2.x` TrueNAS достижима** (телефон `192.168.2.141` успешно печатал по `192.168.2.197:631` — CUPS access_log это подтверждает). → Samsung Mobile Print (app) печатает (висит на локальном IPP внутри подсети), а штатная Android print service с `ipp://192.168.2.197:631/...` показывала «недоступен» из-за **отсутствия mDNS/AirPrint-анонса** (avahi-daemon не запущен; `printer-dns-sd-name = no-value`). ✅ **Исправлено** (см. раздел «✅ mDNS/AirPrint анонс принтера»): cups-splix пересобран с avahi-daemon, cupsd.conf получил `Browsing On` + `BrowseLocalProtocols dnssd` → принтер анонсируется по mDNS (`mopria-certified=1.3`, IPv4 `192.168.2.197`); Android-служба теперь находит принтер сама. Детали и откат: [[mac-print-shared-services]].
+
+## Связанные заметки
+- [[truenas-access]] — SSH-доступ
+- [[truenas-rclone-backup]] — система бэкапов
+- [[obsidian-sync]] — Obsidian sync Eagle ↔ Taiga ↔ Kraken
+- [[openmediavault-rpi5]] — Kraken NAS (Hermes агент, sync)
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index 53cbd362..6f386939 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -380,6 +380,48 @@ TrueNAS:
Rollback network fix: вернуть compose-бэкап на Kraken и выполнить `docker compose up -d`. Это вернёт нерабочий Docker bridge mode, поэтому делать только для диагностики/отката.
+## ✅ Per-user egress на `vpn.mallexxx.duckdns.org` (2026-09-02)
+
+В существующий VLESS-WS inbound `in-10095-tcp` добавлен второй клиент. Маршрутизация теперь различает клиентов по `email`:
+
+| Client email | Client ID | Egress |
+|---|---|---|
+| `user1` | `ce320965-6956-4759-84bb-7cb71cfc6252` | `direct` → TrueNAS `90.189.160.148` |
+| `kraken-user` | `93a5dc4b-1b8d-4af5-9363-eb0091734293` | `via-kraken` → portal SOCKS `xray-reverse-portal:12345` → Kraken `92.62.70.41` |
+
+В persistent 3x-ui Xray template (`settings.key = xrayTemplateConfig`) добавлен SOCKS outbound:
+
+```json
+{
+ "protocol": "socks",
+ "tag": "via-kraken",
+ "settings": {
+ "address": "xray-reverse-portal",
+ "port": 12345
+ }
+}
+```
+
+После глобальных `geoip:private -> blocked` и `bittorrent -> blocked` добавлено правило:
+
+```json
+{
+ "type": "field",
+ "user": ["kraken-user"],
+ "outboundTag": "via-kraken",
+ "ruleTag": "kraken-user-via-reverse"
+}
+```
+
+Проверка одним и тем же локальным Docker-клиентом `xray-test-client`, менялся только client ID:
+
+- `user1` → HTTPS `api.ipify.org` → `90.189.160.148` (TrueNAS direct).
+- `kraken-user` → HTTP и HTTPS `api.ipify.org` → `92.62.70.41` (Kraken reverse).
+
+Локальный `/Users/admin/xray-test/config.json` оставлен на `kraken-user`. Бэкап direct-клиента: `/Users/admin/xray-test/config.json.bak-user1-direct-20260902-1227`.
+
+Бэкапы TrueNAS до и после изменения: `/mnt/RED_2TB/docker/backups/xray-per-user-20260902-1200/` (`x-ui.db`, `x-ui.post-change.db`, runtime configs, reverse portal config/compose). Temporary full-admin API token **не создавался**; `api_tokens` осталась пустой.
+
## Связанные заметки
- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
- [[family/how-to/truenas-infrastructure]] — TrueNAS Caddy/контейнеры
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-per-user-20260902-1229 b/personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-per-user-20260902-1229
new file mode 100644
index 00000000..53cbd362
--- /dev/null
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-per-user-20260902-1229
@@ -0,0 +1,388 @@
+---
+title: Xray Reverse Tunnel — Kraken ↔ TrueNAS
+created: '2026-09-01'
+updated: '2026-09-02'
+type: tech
+namespace: personal
+tags:
+ - xray
+ - reverse
+ - kraken
+ - truenas
+ - tunnel
+ - networking
+confidence: high
+related:
+ - '[[family/how-to/vps-qentra]]'
+ - '[[family/how-to/truenas-infrastructure]]'
+ - '[[family/how-to/kraken-access]]'
+ - '[[family/how-to/rasputin-router]]'
+downgrade_to_26.4.25: >-
+ portal+bridge BOTH pinned to 26.4.25; required but insufficient by itself.
+verified_fix_2026_09_02: >-
+ Kraken bridge must use Docker network_mode: host; Docker bridge/NAT reset the
+ VLESS reverse mux. REALITY+Vision verified end-to-end with Kraken egress.
+---
+
+# Xray Reverse Tunnel — Kraken ↔ TrueNAS
+
+> **Статус: ✅ ВНЕДРЕНО И ПРОВЕРЕНО END-TO-END (2026-09-02).** TrueNAS SOCKS `:12345` → VLESS Reverse TCP+REALITY+Vision → Kraken → internet работает. HTTP и HTTPS тесты оба вернули выходной IP Kraken `92.62.70.41`. **Истинная причина:** Docker bridge/NAT на Kraken сбрасывал reverse mux; bridge должен работать с `network_mode: host`. Более ранние секции со статусом «не работает» и гипотезами #6612/#6242 сохранены ниже как история диагностики и суперсидированы секцией «Верифицированный фикс».
+> Архитектурное решение: проксировать исходящий трафик локальных клиентов сети TrueNAS через **TrueNAS → Kraken → интернет**.
+
+> ## ✅ ВАЖНО (2026-09-02): `xray-admin` (3x-ui) на TrueNAS — это ОДНОВРЕМЕННО РАБОЧИЙ САМОСТОЯТЕЛЬНЫЙ Xray-VPN-сервер
+> Путаница в вопросе юзера выявила: `vpn.mallexxx.duckdns.org:443` — это **не только reverse-frontend**, а полноценный Xray-сервер со **своим собственным выходом в интернет** (outbound `freedom`/`direct` → сразу через TrueNAS), НЕ зависящий от reverse-канала к Kraken. Это **отдельная фича**, параллельная reverse-туннелю.
+> - **Проверено end-to-end 2026-09-02** (временный xray-клиент с Mac): VLESS+WS через `vpn.mallexxx.duckdns.org:443` → HTTP 200, выход IP `90.189.160.148` = TrueNAS. Контейнеры `xray-admin` (Up 21h) + `caddy` (Up 20h) живы.
+> - Inbound `in-10095-tcp` (VLESS-WS): client id `ce320965-6956-4759-84bb-7cb71cfc6252` (user1), `security: none`, path `/vless`, host `vpn.mallexxx.duckdns.org`. TLS терминирует Caddy → в клиенте `network: ws` + TLS на домен, **без** двойного TLS внутрь.
+> - Полные параметры и pitfalls (вкл. удаление `allowInsecure` в Xray 26.7.28) — в `family/how-to/truenas-infrastructure.md`, раздел `xray-admin`.
+> - Reverse к Kraken — это ОТДЕЛЬНЫЙ механизм (для проксирования локальных клиентов сети TrueNAS через Kraken-egress). Он теперь также внедрён и проверен; см. «Верифицированный фикс». 3x-ui-сервер остаётся независимым рабочим VPN-выходом через TrueNAS.
+
+## Контекст / Почему
+
+- **VPS qentra.top (91.207.28.205) УДАЛЁН** — вся прежняя Xray-инфраструктура на нём (VLESS+REALITY, OpenVPN, WebSocket v.qentra.top) больше не существует.
+- Нужен новый путь для выхода локальных клиентов сети TrueNAS в интернет через другую точку выхода (Kraken).
+- Белый IP есть только у **TrueNAS** (`mallexxx.duckdns.org` = `90.189.160.148`).
+- **Kraken за NAT**, входящие не принимает — только исходящие соединения.
+
+## Принятая схема
+
+```
+Локальные клиенты (сеть TrueNAS, 192.168.2.x)
+ │ шлют трафик на локальный прокси TrueNAS (SOCKS 1080 / HTTP 1081)
+ ▼
+TrueNAS ←── контроллер / bridge (белый IP, mallexxx.duckdns.org), принимает
+ │ TrueNAS заворачивает трафик в reverse-канал
+ ▼ ▲
+Kraken ──┘ держит ИСХОДЯЩИЙ reverse-канал к TrueNAS, отпускает трафик → интернет
+ ▼
+интернет
+```
+
+**Тип решения: Xray `reverse`.** Роли по Xray:
+
+| Узел | Роль | Держит канал? | Выход в интернет? |
+|------|------|---------------|-------------------|
+| **TrueNAS** | bridge/контроллер (inbound `reverse`) | ❌ слушает | ❌ |
+| **Kraken** | outbound reverse-нода + exit | ✅ **исходящий** к TrueNAS | ✅ **да** |
+| **Локальные клиенты** | используют TrueNAS как локальный прокси-шлюз | — | через Kraken |
+
+**Ключевое физическое ограничение:** Kraken за NAT не может принимать входящих. Поэтому канал держит **Kraken исходящим** к TrueNAS. Для передачи клиентского трафика TrueNAS → Kraken используются конструкции `dokodemo-door` + `reverse` + policy-маршрутизация (routing rules). Это чуть сложнее "поставить reverse и всё", конфиг нужен аккуратный.
+
+## ⚠️ Факты, проверенные 2026-09-01 (важно, ломают прежние допущения)
+
+1. **На 443 у TrueNAS НЕ Xray.** Проверено `openssl s_client`: на `mallexxx.duckdns.org:443` отвечает **обычный TLS 1.3 с валидным Let's Encrypt сертификатом для `mallexxx.duckdns.org`** (`issuer=Let's Encrypt/CN=YE2`). Это **Caddy/nginx reverse-proxy**, НЕ Xray REALITY (у REALITY сертификат был бы чужой/поддельный). → Твоя память «на 443 висел xray server» не подтверждается.
+2. **`vless-proxy` на TrueNAS** (контейнер `teddysun/xray:latest`, Up 7 дней):
+ - inbound: SOCKS `0.0.0.0:1080`, HTTP `0.0.0.0:1081` (локально в LAN TrueNAS)
+ - outbound: VLESS+WS+TLS на `v.qentra.top:443`, path `/qentra`, id `2D9F24C4-21FE-4784-9843-F11C384DA67A` — **указывает на УДАЛЁННЫЙ VPS → сейчас мёртвый**.
+ - ⚠️ Используется Hermes-Taiga как SOCKS5-прокси (`vless-proxy:1080`) → **Hermes-Taiga сейчас без рабочего прокси**.
+3. **Kraken→TrueNAS по `mallexxx.duckdns.org`:** открыты **только 22 (SSH) и 443 (HTTPS/TLS)**. `8443/8964/8080/9000` — closed. Порт 443 — TLS (Caddy), не сырой.
+4. **Kraken:** Docker установлен (`/usr/bin/docker`), но демон отвечает медленно/рвёт SSH (проверка `docker ps` не завершилась за 40s). Xray на Kraken **отсутствует** (`which xray` пусто) — нужен Docker-контейнер.
+5. **Связанность Kraken↔TrueNAS по 22/443 — подтверждена** (оба OPEN с Kraken).
+
+## ✅ ВНЕДРЕНО 2026-09-01: 3x-ui на TrueNAS (фронтенд + Docker Compose)
+
+**Решение Alex:** использовать **3x-ui frontend** + **Docker Compose** (управление клиентами через панель), а не ручной Caddy-pass-through с сырым Xray. Это изменило исходный план реализации (Шаг 1 ниже устарел по способу).
+
+### Развёрнут контейнер `xray-admin` (TrueNAS)
+- **Имя контейнера:** `xray-admin` (строго, т.к. Caddyfile резолвит `xray-admin` по имени в docker-сети).
+- **Образ:** `ghcr.io/mhsanaei/3x-ui:latest` (**3.7.0**, Xray **26.7.28**).
+- **Сеть:** `caddy_default` (external) — там же Caddy, чтобы резолвить имя.
+- **Volume:** `/mnt/RED_2TB/docker/xray-admin:/etc/x-ui` (туда лёг существующий `x-ui.db`).
+- **Порт панели:** хостовый `54321 → 54321` (внутри панель реально слушает **2053** — Caddy проксирует `vpn-panel.mallexxx.duckdns.org` → `xray-admin:2053`).
+- **Compose-файл:** `/mnt/RED_2TB/docker/xray-admin/docker-compose.yml` (deployed версия с `TZ=Asia/Novosibirsk`, `PUID/PGID=950`; см. актуальный файл на TrueNAS — отличается от черновика в плане).
+- **Панель доступна:** `https://vpn-panel.mallexxx.duckdns.org/` → **HTTP 200**.
+- **Sub-сервер:** `[::]:443` (path `/sub`), панель `[::]:2053`, inbound `vless-ws:10095` (path `/vless`, host `vpn.mallexxx.duckdns.org`).
+
+### Почему это заработало «из коробки» (важно)
+База `/mnt/RED_2TB/docker/xray-admin/x-ui.db` **уже была настроена под эту Caddy-интеграцию** (раньше 3x-ui уже задумывался на TrueNAS): inbound `vless-ws` port 10095 tag `in-10095-tcp`, subDomain `vpn-panel.mallexxx.duckdns.org`, subPort 443, subScheme https. Caddyfile уже содержал (мёртвые до подъёма) блоки:
+- `vpn.mallexxx.duckdns.org` → `@ws path /vless` → `xray-admin:10095` (WS)
+- `vpn-panel.mallexxx.duckdns.org` → `@sub path /sub/*` → `xray-admin:443` + fallback `xray-admin:2053`
+- LE-сертификаты для этих поддоменов уже выданы.
+
+### 3x-ui поддерживает reverse
+В бинарнике `/app/x-ui` присутствует **`clientReverseTags`** → reverse-настройка реализуема через панель 3x-ui (3.7.0).
+
+### Бэкап (сделан до изменений)
+`/mnt/RED_2TB/docker/backups/reverse-xray-3xui-20260901-113625/` — содержит: `caddy/Caddyfile` (копия из контейнера, 90 строк), `xray-admin/x-ui.db`, `docker-inventory.txt`.
+
+### Мелочь/питфолл
+Скопированный в volume `docker-compose.yml` оказался внутри контейнера `/etc/x-ui/` (т.к. volume смонтирован туда) — удалить лишний файл из контейнера: `docker exec xray-admin rm -f /etc/x-ui/docker-compose.yml`.
+
+## ✅ ВНЕДРЕНО 2026-09-01: Reverse portal (TrueNAS) + bridge (Kraken) — VLESS-WS
+
+После 3x-ui развёрнут полноценный reverse. **Ключевое открытие:** Xray **26.x заменил legacy reverse на "VLESS Reverse Proxy"** — старая схема `reverse.bridges/portals` (Xray-examples/ReverseProxy) **не работает** в этой версии (ошибка `The feature "legacy reverse" has been removed and migrated to "VLESS Reverse Proxy"`). Новая схема: `reverse.tag` в VLESS-клиентах/аутбаундах (примеры — `xtls.github.io/en/document/level-2/vless_reverse.html`, use case "Home Broadband Egress").
+
+### Роли (новая терминология VLESS Reverse)
+- **Internal device (Kraken) = BRIDGE**: VLESS-**outbound** с `"reverse":{"tag":"reverse-in"}` → активно устанавливает канал к TrueNAS; на своей стороне создаётся виртуальный **inbound** `reverse-in`, чей трафик направляется в freedom (интернет).
+- **Public server (TrueNAS) = PORTAL**: VLESS-**inbound** с `"reverse":{"tag":"reverse-out"}` у клиента → создаёт **routable-маутбаунд** `reverse-out`; локальные клиенты (SOCKS) маршрутизируются в `reverse-out`.
+- `reverse.tag` на двух сторонах **не обязаны совпадать** — соответствие через общий UUID соединения.
+
+### TrueNAS — `xray-reverse-portal` (папка `/mnt/RED_2TB/docker/reverse-portal/`)
+compose: image `teddysun/xray:latest` (26.7.28), сеть `caddy_default`, volume `config.json:/etc/xray/config.json:ro`, проброс host **`12345:12345`**.
+`config.json`:
+- inbound `interconn` (:12346, VLESS, ws path `/rvs`, host `vpn.mallexxx.duckdns.org`, client id `a81c3179-312b-4c5e-8a46-6bf3c30c2941`, `"reverse":{"tag":"reverse-out"}`)
+- inbound `local` (:12345, SOCKS `noauth udp:true`) — точка входа для клиентов сети TrueNAS
+- routing: `inboundTag:["local"] → outboundTag:"reverse-out"`
+- outbounds: `freedom` (placeholder — обязателен, иначе `reverse-out` станет default и весь трафик уйдёт в reverse)
+- Порт 12346 НЕ проброшен наружу — к нему обращается Caddy по имени `xray-reverse-portal:12346` в docker-сети.
+
+### TrueNAS — Caddyfile (bind mount `/mnt/RED_2TB/docker/caddy/Caddyfile`)
+Добавлен маршрут в блок `vpn.mallexxx.duckdns.org`:
+```caddy
+@rvs path /rvs
+reverse_proxy @rvs xray-reverse-portal:12346
+```
+Питфоллы: **Caddyfile нельзя править `docker cp`** в контейнер (volume → `device or resource busy`) — править на хосте через `docker run --rm -v /:/host alpine cp /host/tmp/...`. Валидация `docker exec caddy caddy validate --config /tmp/Caddyfile.new` → "Valid configuration". Перезапуск `docker restart caddy` безопасен. Бэкап: `Caddyfile.pre-reverse`.
+
+### Kraken — `xray-reverse-bridge` (папка `/home/kraken/xray-reverse/`)
+compose: image `teddysun/xray:latest`, volume `config.json:/etc/xray/config.json:ro`.
+`config.json`:
+- outbound `direct` = `freedom` + `"finalRules":[{"action":"allow","network":"tcp,udp","ip":["0.0.0.0/0"]}]` (интернет-egress Kraken)
+- outbound `conn` = **VLESS упрощённого стиля** (`settings.address/port/id/encryption/reverse` — НЕ `vnext[]`!) → `vpn.mallexxx.duckdns.org:443` ws `/rvs` `"reverse":{"tag":"reverse-in"}`, security tls, tlsSettings.serverName `vpn.mallexxx.duckdns.org`
+- routing: `inboundTag:["reverse-in"] → outboundTag:"direct"`
+- **Питфолл:** VLESS-outbound с `reverse` должен быть упрощённого стиля. Если через `vnext[]/users[]` → Xray 26.x: `VLESS users: please use simplified outbound's config style to use "reverse"`.
+- `loglevel debug` (для диагностики).
+
+### Reverse-канал: ✅ установлен
+- Kraken `netstat`: `172.24.0.2:... → 90.189.160.148:443 ESTABLISHED`
+- Portal: `::ffff:172.16.1.7:12346 ::ffff:172.16.1.3:52616 ESTABLISHED` (172.16.1.3 = Caddy)
+- Bridge logs: `common/mux: received request for udp:reverse:0`
+
+### ❌ End-to-end payload НЕ работает
+- `curl --socks5 127.0.0.1:12345 http://api.ipify.org` на TrueNAS → `code=000` (прямой curl → 200).
+- Portal логи: `accepted tcp:8.47.69.0:80 [local -> reverse-out]` — запрос ушёл в reverse-out.
+- **Bridge НЕ логирует received для этого TCP-payload** — данные теряются между reverse-out (portal) и reverse-in (bridge).
+- Kraken имеет прямой интернет (`api.ipify` → `92.62.70.41`), проблема не в egress.
+
+### ✅ ВНЕДРЕНО (2026-09-01) — TCP+REALITY переход, все шаги выполнены
+WS-транспорт не пропускал reverse-payload; переведён на **прямой TCP + REALITY** (Alex согласовал проброс). Все 4 шага ВЫПОЛНЕНЫ:
+1. ✅ Пересоздан `xray-reverse-portal` на TrueNAS (compose `12345:12345` + `12346:12346`, config REALITY TCP). Проверено `ss -tlnp` → `0.0.0.0:12346 LISTEN`.
+2. ✅ OpenWrt DNAT добавлен: redirect `xray-reverse-12346`, `src_dport=12346` → `dest_ip=192.168.2.197:12346`, `target=DNAT`, `/etc/init.d/firewall reload` → `DNAT_ADDED`. Проверено: `mallexxx.duckdns.org:12346` → **OPEN** извне.
+3. ✅ Пересоздан `xray-reverse-bridge` на Kraken (config REALITY TCP). Reverse-канал ESTABLISHED: Kraken `172.24.0.2 → 90.189.160.148:12346` + `common/mux: received request for udp:reverse:0`.
+4. ❌ **Тест payload НЕ прошёл даже на TCP+REALITY**: `curl --socks5 127.0.0.1:12345 http://api.ipify.org` → **`code=000`**. Portal: `accepted tcp:8.6.112.0:80 [local -> reverse-out]`, но bridge НЕ логирует приход TCP-payload.
+
+### ❌ Дополнительно пробовано: снятие `flow: xtls-rprx-vision`
+Убрал `flow: xtls-rprx-vision` с обеих сторон (portal interconn client + bridge conn outbound), пересоздал контейнеры (оба `Configuration OK`). Результат: **по-прежнему `code=000`**. Reverse-канал устанавливается, TCP-payload до bridge не доходит.
+
+**Вывод:** проблема НЕ в `flow` и НЕ в WS. **Даже на «каноничном» TCP+REALITY payload от portal-reverse-out до bridge-reverse-in не передаётся** (bridge не получает inbound-TCP). Гипотеза про WS **опровергнута**.
+
+### 🔑 КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН (2026-09-01, интернет-поиск) — issue XTLS/Xray-core #6612
+
+**Симптомы точно совпадают с #6612:** reverse-канал устанавливается (`udp:reverse:0` на месте), но TCP-payload не проходит; portal логирует `accepted tcp:... -> reverse-out`, а bridge НЕ получает входящих reverse-in-соединений; curl → 000.
+
+**Причина:** начиная с **Xray v26.5+** (PR #6027) у outbound `freedom`/`direct` появилась **дефолтная политика безопасности**, которая **блокирует ВСЕ цели для трафика VLESS Reverse**, если не задан явный `finalRules`. Контрольный reverse-канал при этом продолжает устанавливаться — поэтому «всё выглядит подключённым», но реальный payload молча отбрасывается. Это ровно наш случай на версии **26.7.28**.
+
+**Точный фикс** — добавить в freedom `direct` на **BRIDGE** (место реального egress из reverse-in в интернет):
+```json
+{
+ "protocol": "freedom",
+ "tag": "direct",
+ "settings": {
+ "domainStrategy": "AsIs",
+ "finalRules": [ { "action": "allow" } ]
+ }
+}
+```
+Issue говорит прямо: проблема воспроизводится с обычным Freedom без finalRules вообще, и добавление безусловного `allow` чинит на 26.7.28.
+
+**Релевантные ссылки:**
+- **#6612** — главный фикс: `https://github.com/XTLS/Xray-core/issues/6612`
+- **#6027** — корень (finalRules у Direct/Freedom): `https://github.com/XTLS/Xray-core/pull/6027`
+- Документация freedom: `https://xtls.github.io/en/config/outbounds/freedom.html`
+- 3x-ui разбор под reverse: `https://github.com/MHSanaei/3x-ui/issues/4782`
+- **#2664** — если finalRules не поможет: `flow: xtls-rprx-vision` на BRIDGE-outbound + REALITY может ломать доставку (держите `flow:""` на reverse-клиенте): `https://github.com/XTLS/Xray-core/issues/2664`
+- **Регрессия роутинга reverse v26.5+** (если фикс не поможет): #6242, #6195, #6248 — временный кварк: пинить **v26.4.25**.
+
+**Важные детали из поиска (проверить при продолжении):**
+- Портал ДОЛЖЕН иметь placeholder-freedom (у нас есть) — иначе reverse-out станет default. Плейсхолдеру не нужен `finalRules:allow` (он обслуживает только несопоставленный трафик).
+- Роутинг `inboundTag:["reverse-in"] -> direct` на bridge — нужен (у нас есть).
+- У VLESS outbound поле `encryption` обязательно (`"none"` или парный PQ-ключ к серверному `decryption`) — у нас `"none"`, ок.
+
+### ✅ ФИКС ПРИМЕНЁН, но заблокирован (2026-09-01)
+
+Новый bridge-конфиг с `finalRules:[{action:"allow"}]` в `direct` **залит** на Kraken (`/home/kraken/xray-reverse/config.json`, 1098 байт, проверено `cat | head -5`). НО:
+- ⛔ **Kraken по docker НЕДОСТУПЕН**: `systemctl is-active docker` → **`activating`** (не `active`), демон застрял, накоплено множество зависших docker-процессов (ps/run/compose).
+- **Причина: USB-HDD снова не поднялся** — `lsblk /dev/sda` → **`0B`** (без раздела). Docker data-root = `/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/docker-data` (на этом HDD) → daemon ждёт диск и застревает.
+- ⚠️ **Замечено расхождение UUID**: сейчас data-root указывает на `/srv/dev-disk-by-uuid-49e8f586-...`, а в контексте ранее фигурировал смонтированный HDD `/srv/dev-disk-by-uuid-6194539b-...` (sda1 1.8T). Текущий `sda` = 0B без раздела.
+- **`docker compose down/up` для bridge не выполнен** — контейнер всё ещё со старым конфигом (больше не деплоится, daemon не отвечает).
+
+**Шаг для продолжения (требует подтверждения Alex):** починить HDD/docker на Kraken (повторить утренний сценарий: `sudo reboot` или физическое переподключение USB-диска), затем пересоздать bridge-контейнер (`cd /home/kraken/xray-reverse && docker compose down && docker compose up -d`), затем тест `curl --socks5 127.0.0.1:12345 http://api.ipify.org` на TrueNAS → ожидаем выход с IP Kraken.
+
+**Статус: задание НЕ завершено — остаётся применить bridge-фикс после восстановления docker на Kraken.**
+
+## ✅ 2026-09-02: Kraken восстановлен (диск/docker) + фикс #6612 применён — НО payload всё равно мёртв → ИСТИННЫЙ КОРЕНЬ #6242 (bridge-side v26.5+)
+
+### Kraken полностью поднялся (проверено живьём по SSH)
+- Uptime 2 мин — Kraken перезагрузился (USB-HDD-сценарий). `systemctl is-active docker` был `activating` первые минуты (ждал поднятия диска), затем стал **`active`**.
+- **Диск `/dev/sda1` поднялся**: 1.8T, смонтирован к `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f`, 235G свободно (87% занято). (В отличие от 2026-09-01, когда `sda` был `0B`.)
+- **Все контейнеры восстановились** (в прошлый раз — 0): transmission, sonarr, prowlarr, homeassistant, jellyfin, radarr, hermes-kraken, portainer, rclone, cloudflared, flaresolverr, watchtower, **xray-reverse-bridge**. Docker Root Dir `/srv/dev-disk-by-uuid-6194539b-.../docker-data`.
+- **Bridge-конфиг содержит фикс #6612**: `/home/kraken/xray-reverse/config.json` → `freedom/direct` с `"finalRules":[{"action":"allow"}]`.
+- **Reverse-канал к TrueNAS жив**: bridge лог 2026-09-02 13:12: `dialing TCP to mallexxx.duckdns.org:12346` → `tunneling request` → `received request for udp:reverse:0`. Bridge = Xray **26.7.28** (`teddysun/xray:latest`).
+
+### Portal (TrueNAS) — состояние
+`xray-reverse-portal` Up 22h, reverse-канал от Kraken держится. Лог portal: `accepted tcp:8.6.112.0:80 [local -> reverse-out]` — запрос с локального SOCKS-входа уходит в reverse-out (в сторону Kraken). Конфиг portal без изменений (interconn :12346 REALITY client `a81c3179...` reverse-out; local :12345 SOCKS; routing `local→reverse-out`; placeholder-freedom). Сеть `caddy_default`, curl-контейнер в ней резолвит `xray-reverse-portal:12345` = `172.16.1.7`.
+
+### ❌ End-to-end payload по-прежнему НЕ проходит (даже с фиксом #6612)
+Тест временным контейнером `curlimages/curl` в сети `caddy_default` на TrueNAS:
+```
+docker run --rm --network caddy_default curlimages/curl:latest curl -sv --socks5-hostname xray-reverse-portal:12345 -m 20 http://api.ipify.org
+```
+→ verbos: `Opened SOCKS connection from 172.16.1.8 port ... to api.ipify.org port 80 (via 172.16.1.7 port 12345)` → `Request completely sent off` → **`Empty reply from server`** (соединение закрыто без ответа). Трафик доходит до portal, уходит в reverse-out, НО не возвращается от bridge → ответ теряется.
+
+### 🔑 ИСТИННЫЙ КОРЕНЬ (2026-09-02, интернет-поиск): issue XTLS/Xray-core #6242 — регрессия bridge в v26.5+
+Симптомы точно совпадают с [issue #6242](https://github.com/XTLS/Xray-core/issues/6242) (закрыта "not planned"):
+- Регрессия **только на стороне BRIDGE** (outbound-initiating), версия portal не важна.
+- С **v26.5+** (и 26.6.x) bridge стартует без ошибок, reverse-канал вроде устанавливается (`udp:reverse:0`), но **трафик не маршрутизируется через reverse tunnel**. Ровно наш случай на 26.7.28.
+- **Понижение ТОЛЬКО bridge до v26.4.25 мгновенно возвращает полную работу** без изменений конфига.
+- Связано с #5750 (BurstObservatory не успевает health-check динамически регистрируемых `rproxy-*` outbound'ов — до 10 мин) и обсуждением #5962 (stale-tunnel mappings после рестартов). PR #5101 прямо предупреждал: "reverse sub-protocol unstable, will break, кросс-версий совместимости НЕ гарантировано".
+- **Почему фикс #6612 не помог:** #6612 (`finalRules:allow`) лечит ДРУГУЮ проблему (Freedom блокирует payload) — мы её уже устранили. Но осталась регрессия маршрутизации #6242, которую `finalRules` не обходит.
+
+**Решение/кварк:** сдаунгрейд **bridge** до Xray **26.4.25** (образ `teddysun/xray:26.4.25`, существует на Docker Hub / GitHub Container Registry `ghcr.io/xtls/xray-core:26.4.25`). Portal-сторона (26.7.28) остаётся.
+
+### План внедрения (согласован с Alex — что НЕ трогать `vpn.mallexxx` и `xray-admin`)
+1. На Kraken: остановить `xray-reverse-bridge` контейнер.
+2. В `/home/kraken/xray-reverse/docker-compose.yml` сменить образ `teddysun/xray:latest` → `teddysun/xray:26.4.25`.
+3. `cd /home/kraken/xray-reverse && docker compose up -d` (пересоздаст bridge на 26.4.25).
+4. Проверить reverse-канал: bridge лог `udp:reverse:0`.
+5. Контрольный end-to-end: временный curl-контейнер в сети `caddy_default` на TrueNAS через `xray-reverse-portal:12345` → ожидаем выход с **IP Kraken** (`92.62.70.41`), НЕ IP TrueNAS.
+⛔ НЕ трогать: `xray-admin` / `vpn.mallexxx` / Caddy / portal.
+
+### ⚠️ Важное прояснение для будущих сессий (из этого диалога)
+Запрос юзера "не могу подключиться к xray на truenas до наших измерений" — это была путаница между **тремя** разными сущностями:
+1. `vless-proxy` (teddysun/xray, SOCKS :1080/:1081, outbound на удалённый VPS qentra.top) — **мёртв из-за удаления VPS** 2026-09-01.
+2. `xray-admin` (3x-ui) = **самостоятельный рабочий Xray-VPN из интернета** через `vpn.mallexxx.duckdns.org:443` (см. секцию «ВАЖНО 2026-09-02» выше). Жив, end-to-end проверен.
+3. reverse-туннель TrueNAS↔Kraken (portal `xray-reverse-portal` + bridge на Kraken) — это про проксирование egress ЛОКАЛЬНЫХ клиентов сети TrueNAS через Kraken. НЕ связан с vpn.mallexxx.
+
+
+## ❌ ИСТОРИЯ ДИАГНОСТИКИ 2026-09-02: downgrade сам по себе не помог
+
+> ⚠️ ВАЖНО: вопреки секции «ИСТИННЫЙ КОРЕНЬ #6242» выше (которая была гипотезой, ещё не проверенной полностью), **полный downgrade обеих сторон до Xray 26.4.25 payload НЕ починил**. Это зафиксировано здесь как фактический итог.
+
+### Что было сделано (выполнено по факту, alive-диагностика)
+По согласованию с Alex (не трогая `vpn.mallexxx` / `xray-admin`) выполнено:
+1. Bridge (Kraken): `/home/kraken/xray-reverse/docker-compose.yml` → образ **`teddysun/xray:26.4.25`**. Docker Pull прошёл, контейнер на 26.4.25 (`Xray 26.4.25 ... go1.26.2 linux/arm64`). Бэкап: `docker-compose.yml.bak-26.7.28`.
+2. Portal (TrueNAS): `/mnt/RED_2TB/docker/reverse-portal/docker-compose.yml` → тоже **`teddysun/xray:26.4.25`** (Linux amd64, образ 21.59MB Pull). Бэкап: `docker-compose.yml.bak-26.7.28`. Контейнер `xray-reverse-portal` пересоздан.
+3. Portal `config.json` `loglevel` поднят на `debug` (для диагностики), залит в `/mnt/RED_2TB/docker/reverse-portal/config.json`.
+4. Локальные копии файлов на Mac (для правки): `~/xray-test/` → `docker-compose.bridge.yml`, `docker-compose.portal.yml`, `portal.config.json`, `config.json` (тестовый клиент vpn.mallexxx, удалить не нужно).
+5. После смены версии bridge перезапущен (для переустановки reverse-канала к новому portal).
+
+### Результат end-to-end теста (обе стороны 26.4.25)
+- Reverse-канал у bridge **устанавливается**: лог `dialing TCP to mallexxx.duckdns.org:12346` → `tunneling request to unknown` → `received request for udp:reverse:0`. Portal принимает (лог: `proxy/vless/inbound: received request for tcp:v1.rvs.cool:0` → `dispatching request to udp:reverse:0`).
+- **НО payload по-прежнему НЕ проходит**: тест curl через `xray-reverse-portal:12345` → `Empty reply from server` / `exit=52`.
+- **Portal (TrueNAS) debug-лог полностью проясняет картину:**
+ - При запросе: `[118463829] proxy/socks: TCP Connect request to tcp:api.ipify.org:80` → `[118463829] app/dispatcher: taking detour [reverse-out] for [tcp:api.ipify.org:80]` — **и НИЧЕГО дальше** (ни ошибки, ни dial к bridge). Dispatch в `reverse-out` молча глотается.
+ - Фоновые разрывы: `common/mux: failed to read metadata > read tcp 172.16.1.7:12346->92.62.70.41:3266: connection reset by peer` — portal пытается что-то слать к bridge, но канал рвётся.
+- **Критично:** на TrueNAS **НЕТ постоянного ESTABLISHED TCP-соединения от bridge на :12346** (`ss -tn sport=:12346` пусто). Reverse-канал НЕ держится постоянным — устанавливается эфемерно (per контрольный UDP прочёт), и data-stream по нему не доходит до bridge.
+- Bridge (Kraken) никогда не логирует приём `reverse-in` TCP-payload (только контрольный `udp:reverse:0`).
+
+### Вывод
+Проблема **НЕ** регрессия #6242 фиксируемая версией bridge — иначе downgrade до рабочей по issue пары 26.4.25↔26.4.25 решил бы. После полного downgrade payload по-прежнему мёртв. **Настоящая причина: reverse-канал portal↔bridge не удерживается постоянным на TCP-уровне** (нет стабильного ESTABLISHED :12346), поэтому dispatch портала в `reverse-out` не находит живой транспорт до bridge и молча теряет данные.
+
+Это согласуется с предупреждением PR #5101: **новый VLESS Reverse sub-protocol itself нестабилен** («will break, кросс-версий совместимость не гарантирована») — по-видимому канал между двумя разнесёнными docker-реализациями (Kraken arm64 ↔ TrueNAS amd64, обе 26.4.25) не держится стабильно.
+
+### Дальнейшие гипотезы (НЕ проверены, треб.WB)
+1. Транспортный REALITY mismatch arm64(26.4.25 bridge) ↔ amd64(26.4.25 portal) даёт нестабильный mux → попробовать не-REALITY (простой TCP, т.к. канал только между сервисами TrueNAS↔Kraken, не извне) — убрать REALITY, оставить VLESS-TCP без camouflage.
+2. Перепроверить, что bridge реально удерживает единственное mux-соединение (в xray reverse bridge обычно держит persistent канал, у нас его нет → пере-аудит конфиг bridge: возможно не хватает чего-то, заставляющего держать канал постоянно).
+3. Запросить консультацию/повторно изучить рабочую конфигурацию сбора из живого разбора темы (потому что каноничные примеры из доки и issues не дали полного рабочего egress-конфига).
+
+### ⚠️ ТЕКУЩЕЕ СОСТОЯНИЕ СИСТЕМ (на момент записи, 2026-09-02)
+- **Версии:** BOTH portal и bridge сейчас на **`teddysun/xray:26.4.25`** (исходно были на `:latest` = 26.7.28). Откат при необходимости: поменять образ в compose обратно на `teddysun/xray:latest` и `docker compose up -d`.
+- Конфиг portal имеет `loglevel: debug` (пока). Bridge `config.json` содержит фикс #6612 `finalRules:allow`.
+- `vpn.mallexxx.duckdns.org` / `xray-admin` / Caddy — **НЕ тронуты**, работают (см. секцию «ВАЖНО 2026-09-02»).
+- Reverse к Kraken **НЕ внедрён/не работает end-to-end** — это остаётся открытой задачей.
+
+## ⏳ Открытые вопросы по reverse (требуют ответа/действия Alex)
+
+## План реализации (история; Шаг 1 устарел — см. «ВНЕДРЕНО» выше)
+
+### Шаг 1 — TrueNAS: Xray reverse-server поверх Caddy
+- **Способ (рекомендован): Caddy TLS-pass-through.** Добавить в Caddy TCP-правило: поддомен `xray.mallexxx.duckdns.org:443` → внутренний порт Xray-контейнера (напр. `localhost:8443`). Caddy передаёт сырой TLS-стрим дальше. Xray на TrueNAS принимает VLESS+Reality/TLS. Kraken ходит на `xray.mallexxx.duckdns.org:443`.
+ - Плюс: переиспользуем уже открытый/проброшенный 443 на роутере, не трогаем роутер, туннель маскируется под HTTPS.
+ - Альтернатива: пробросить отдельный порт на роутере (напр. 8443) → требует доступа к роутеру. Менее предпочтительно.
+- Новый/доразвернутый Xray-контейнер:
+ - inbound `reverse`/`dokodemo-door` на 8443 (извне по pass-through)
+ - local inbound SOCKS/HTTP (уже есть 1080/1081)
+ - routing: трафик клиентов → в reverse-канал к Kraken
+
+### Шаг 2 — TrueNAS: бэкап перед изменением
+- Снять копию текущего конфига `vless-proxy` (config.json), в `/mnt/RED_2TB/docker//backup/` (паттерн как с cups-splix).
+
+### Шаг 3 — Kraken: Xray outbound reverse в Docker
+- Контейнер Xray на Kraken (`docker run --restart unless-stopped`, как остальные).
+- outbound vless → `xray.mallexxx.duckdns.org:443` (через Caddy pass-through).
+- inbound SOCKS/HTTP на Kraken — точка выхода.
+- reverse-конфиг: Kraken регистрирует «сервис» (весь трафик через dokodemo-door) наружу через канал к TrueNAS.
+
+### Шаг 4 — Проверка связности
+- Kraken → `xray.mallexxx.duckdns.org:443` (TCP).
+- Локальный клиент → TrueNAS:1080 → curl через прокси → должен выйти с IP Kraken.
+
+### Шаг 5 — Обновить Obsidian после внедрения
+- Дополнить `family/how-to/truenas-infrastructure.md` (Xray reverse-настройка), `family/how-to/kraken-access.md`; обновить `personal/tech/vps-qentra.md` (VPS удалён, ссылки на него).
+
+## Открытые вопросы (требуют ответа Alex)
+
+1. ✅ (решено) **Проброс порта для reverse** — переведён на TCP+REALITY, ВСЁ ВЫПОЛНЕНО (portal пересоздан с REALITY, OpenWrt DNAT `внешний:12346 → 192.168.2.197:12346` добавлен, bridge на Kraken пересоздан). См. секции «ВНЕДРЕНО — TCP+REALITY» выше. **Компанент работает, но payload НЕ проходит (см. пункт 4).**
+2. **Какой трафик TrueNAS пустить через Kraken** — весь исходящий интернет, или конкретные сервисы/подсети? (Влияет на маршрутизацию на Kraken.) — всё ещё открыто.
+3. ✅ **Kraken docker** — РАБОТАЕТ, ВОССТАНОВИЛСЯ 2026-09-02: `systemctl is-active docker` → `active`, диск `/dev/sda1 1.8T` смонтирован к `/srv/dev-disk-by-uuid-6194539b-...`, 235G свободно (87% занято). **Все контейнеры восстановились** (в отличие от 2026-09-01, когда было 0): transmission, sonarr, prowlarr, homeassistant, jellyfin, radarr, hermes-kraken, portainer, rclone, cloudflared, flaresolverr, watchtower, **xray-reverse-bridge** (Up).
+4. ✅ **End-to-end payload проходит.** #6612/#6242 оказались неполными гипотезами. Верифицированный корень — Docker bridge/NAT на Kraken; решение — Xray 26.4.25 + `network_mode: host` на bridge. См. authoritative-секцию ниже.
+
+## ✅ Верифицированный фикс 2026-09-02 (authoritative)
+
+> Эта секция суперсидирует все более ранние выводы «payload мёртв», «нет persistent ESTABLISHED» и предполагаемые корни #6612/#6242. Фикс найден и проверен живыми A/B-тестами.
+
+### Истинная причина
+
+`xray-reverse-bridge` на Kraken работал в обычной Docker bridge-сети. Docker bridge/NAT на этом узле сбрасывал long-lived VLESS reverse mux после успешной регистрации `udp:reverse:0`. Поэтому portal создавал `reverse-out`, но payload не доходил до `reverse-in`.
+
+Ключевой A/B-тест:
+
+1. Тот же JSON на temporary bridge amd64 внутри TrueNAS заработал сразу → portal/config/routing исправны.
+2. Тот же Xray 26.4.25 arm64 + тот же JSON на Kraken в `--network host` заработал сразу → CPU architecture и WAN исправны, отличие только в Docker network mode.
+3. После возврата REALITY+Vision туннель остался рабочим → проблема не в REALITY, Vision или TCP transport.
+
+### Постоянная конфигурация
+
+Kraken: `/home/kraken/xray-reverse/docker-compose.yml`
+
+```yaml
+services:
+ xray-reverse-bridge:
+ image: teddysun/xray:26.4.25
+ network_mode: host
+```
+
+Bridge config возвращён к защищённому VLESS TCP + REALITY + `flow: xtls-rprx-vision`. `direct` сохраняет `finalRules: [{"action":"allow"}]`. Portal на TrueNAS также на `teddysun/xray:26.4.25`, REALITY+Vision. `xray-admin`, Caddy и `vpn.mallexxx.duckdns.org` не менялись.
+
+`network_mode: host` расширяет сетевые права bridge-контейнера. В этом конфиге у него нет физических listening inbounds; `reverse-in` — виртуальный inbound Xray. Не добавлять в этот контейнер обычные inbounds без аудита bind address/firewall.
+
+### Финальная проверка
+
+2026-09-02 после permanent Compose deploy:
+
+- `docker inspect xray-reverse-bridge` → image `teddysun/xray:26.4.25`, network `host`, status `running`, restart count `0`.
+- Bridge лог: `received request for udp:reverse:0`, затем TCP payload принят в `reverse-in -> direct`.
+- HTTPS через SOCKS `xray-reverse-portal:12345` → `92.62.70.41`.
+- HTTP через тот же SOCKS → `92.62.70.41`.
+- `92.62.70.41` — публичный egress Kraken; TrueNAS egress `90.189.160.148` не использовался.
+
+### Бэкапы и rollback
+
+Kraken:
+
+- `/home/kraken/xray-reverse/docker-compose.yml.bak-bridge-network-20260902-1147`
+- `/home/kraken/xray-reverse/config.json.bak-plain-host-working-20260902-1146`
+- `/home/kraken/xray-reverse/config.json.bak-reality-vision-20260902-1142`
+
+TrueNAS:
+
+- `/mnt/RED_2TB/docker/reverse-portal/config.json.bak-plain-host-working-20260902-1146`
+- `/mnt/RED_2TB/docker/reverse-portal/config.json.bak-reality-vision-20260902-1142`
+
+Rollback network fix: вернуть compose-бэкап на Kraken и выполнить `docker compose up -d`. Это вернёт нерабочий Docker bridge mode, поэтому делать только для диагностики/отката.
+
+## Связанные заметки
+- [[family/how-to/vps-qentra]] — VPS на 2026-09-01 УДАЛЁН
+- [[family/how-to/truenas-infrastructure]] — TrueNAS Caddy/контейнеры
+- [[family/how-to/kraken-access]] — Docker Kraken
+- [[family/how-to/rasputin-router]] — прежний VLESS-туннель на VPS (тоже требует пересмотра, VPS нет)
+- [[family/how-to/wireguard-vpn]] — WG Eagle↔VPS↔Kraken (VPS-звено теперь мёртво)
From e020382933488f8c251de3e9c563df74bbc7bcbe Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 12:40:50 +0600
Subject: [PATCH 69/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
family/how-to/arr-stack-taiga.md
personal/tech/truenas-nfs4-acl-and-arrmultiuser.md
---
.../2026-09-02-restore-privilege-scope.md | 9 ++-
family/how-to/arr-stack-taiga.md | 8 +--
.../tech/truenas-nfs4-acl-and-arrmultiuser.md | 61 +++++++++++++++++++
3 files changed, 72 insertions(+), 6 deletions(-)
create mode 100644 personal/tech/truenas-nfs4-acl-and-arrmultiuser.md
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
index 5dec2a10..cbf007c5 100644
--- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -2,7 +2,7 @@
tags: [truenas, restore, incident, git, media, arr, permission]
created: 2026-09-02
updated: 2026-09-02
-status: resolved (Git-sync на маке ПОЧИНЕН 2026-09-02: NFS4 ACL DENY сняты полным dacl-заменой через filesystem.setacl; push ИДЁТ; ahead=0 behind=0. Осталось: подъём arr-стека + фикс права на скрытые файлы корня storage + не связанный library-app restart-loop)
+status: resolved (Git-sync на маке ПОЧИНЕН 2026-09-02: NFS4 ACL DENY сняты полным dacl-заменой через filesystem.setacl; push ИДЁТ; ahead=0 behind=0). Arr-стек ПОДНЯТ и работает 2026-09-02 (prowlarr/radarr/sonarr/jellyfin Up, все под uid 950; transmission также PUID/PGID=950; сети media_net+transmission_default персистентны в compose). Осталось: фикс права на скрытые файлы корня storage (решение не подтверждено) + несвязанный library-app restart-loop
---
# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
@@ -245,7 +245,12 @@ git --git-dir="$S/git/obsidian-vault.git" log --oneline -2 2>&1 | head # пр
### ⚠️ Текущий статус после вторго прохода + RESOLUTION (актуально на 2026-09-02)
- ✅ **Push на маке ПОЧИНЕН** — NFS4 ACL `DENY` на ~1946 loose git-объектах сняты через **полную dacl-замену** (не stripacl). См. секцию «✅ RESOLVED … dacl-заменой». Git-sync работает: fetch+push проходят, ahead=0 behind=0.
-- 🔜 **Arr-стек** — НЕ поднят ещё. Все compose (`arr/docker-compose.yml` + transmission compose) уже с `PUID/PGID=950`, права медиа 950:950. Для запуска: `cd /mnt/RED_2TB/docker/arr && docker compose up -d` → проверить `docker compose ps` + что jellyfin видит библиотеки (пути `/storage/*`). Обрати внимание: radarr/sonarr/jellyfin монтируют `/mnt/RED_2TB/storage` как `/storage`, а не `/media` (в отличие от Kraken-схемы) — root folder у radarr на TrueNAS другой.
+- ✅ **Arr-стек ПОДНЯТ и РАБОТАЕТ (2026-09-02).** Контейнеры созданы и запущены штатно из `/mnt/RED_2TB/docker/arr`:
+ - `docker compose up -d` в `/docker/arr` создал и запустил `prowlarr`, `radarr`, `sonarr`, `jellyfin` (все в проекте `arr`). Проверено: процессы работают под **uid 950** (`Prowlarr/Radarr/Sonarr/jellyfin` pid утилиты uid=950, не root).
+ - **Сети (персистентность):** для запуска потребовалось воссоздать отст. `media_net` (external): `docker network create media_net` (она была утеряна с `.ix-apps` при пересоздании пула; в compose объявлена только `external: true`, НЕ создаётся compose-ом → теряется при полном демонтаже docker, шаг восстановления: `docker network create media_net`). transmission подключён к `media_net` (для radarr/sonarr).
+ - **Пересозданный transmission compose/сети:** папка `docker/transmission` стала `950:950` после пересоздания (была 911:911, поэтому изначально не давала `cd`). В `docker/transmission/docker-compose.yml` **добавлена networks секция**: `media_net` + `transmission_default` (обе external), чтобы подключение transmission к media_net было перманентным (не живым `docker network connect`). Проверено `docker compose up -d` пересоздал — обе сети на месте. **Порты:** caddy резолвит transmission через `transmission_default` (Web UI `transmission.mallexxx.duckdns.org` работает); radarr достаёт `transmission:9091` через `media_net` (TR_OK).
+ - **Связка работает:** jellyfin читает `/storage/Movies` (READ_OK), radarr↔transmission по media_net OK (172.16.17.2:9091), все UI-порты слушают (prowlarr 9696, radarr 7878, sonarr 8989, jellyfin 8096, transmission 9091). radarr/sonarr/jellyfin монтируют `/mnt/RED_2TB/storage` → `/storage` (root folder у radarr на TrueNAS = `/storage/...`, не `/media` как Kraken).
+ - **Осталось по стеку:** настройка самих сервисов через их Web UI (radarr root-folder `/storage`, indexers в prowlarr, библиотеки в jellyfin) — вне времени запуска; я не вмешиваюсь в конфиги.
- 🔜 **Скрытые файлы корня storage** (`.DS_Store`, `.bash_*`, `.profile`, `.com.apple*`) остаются `921:921 mode 0000` (не добиты — в root-шаге B rm для них предлагался, но решение не подтверждено). Сам корень `storage/` → `drwxrwx--- 921:921` (owner 921, хотя group 950 есть). Проверить, нужен ли корню chown 950.
- 🔜 **`books`** — владелец `950:921` (не 950:950) — оставлен как был (служебный, не критично для saga).
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index a1897e99..fa964200 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -13,15 +13,15 @@ related:
Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
-> **STATE 2026-09-02:** Стек **выключен** — в `docker ps -a` нет ни radarr/sonarr/prowlarr/jellyfin контейнеров (стек не пересоздавался). Compose-структура живёт в `/mnt/RED_2TB/docker/arr/` (`docker-compose.yml` + подпапки `prowlarr/ radarr/ sonarr/ jellyfin/ media-pipeline/`). Работает только `transmission` (отдельный compose `/mnt/RED_2TB/docker/transmission/`).
+> **STATE 2026-09-02 (end-of-session):** Стек **ПОДНЯТ И РАБОТАЕТ** ✅. `docker compose up -d` в `/mnt/RED_2TB/docker/arr` создал и запустил `prowlarr`, `radarr`, `sonarr`, `jellyfin` (все в проекте `arr`, процессы под **uid 950** — PUID/PGID=950 применён). Сеть `media_net` воссоздана (`docker network create media_net`), transmission подключён к `media_net`. **Персистентность сетей:** в `docker/transmission/docker-compose.yml` добавлена networks секция `media_net` + `transmission_default` (external) — transmission сам находится в обеих сетях при любом пересоздании.
>
-> **Почему стек изначально не запускался — медиа-права были сломаны** (тот же restore-инцидент uid 921/0000): `/mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared}` были `d---------`/`drwx------`, owner `transmission` (uid 921). radarr/sonarr монтируют `/storage` (rw), jellyfin `:ro` — со сломанными правами не прочитали бы библиотеку. ⚠️ **Сейчас (2026-09-02, конец сессии) права медиа ИСПРАВЛЕНЫ** до `950:950 drwxrwx---` (см. PROGRESS ниже) — но контейнеры arr/jellyfin всё ещё не созданы/не запущены. Полный контекст: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
+> **Почему стек изначально не запускался — медиа-права были сломаны** (тот же restore-инцидент uid 921/0000): `/mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared}` были `d---------`/`drwx------`, owner `transmission` (uid 921). radarr/sonarr монтируют `/storage` (rw), jellyfin `:ro` — со сломанными правами не прочитали бы библиотеку. ✅ **Права медиа ИСПРАВЛЕНЫ** (2026-09-02) до `950:950 drwxrwx---`; контейнеры arr/jellyfin затем подняты и запущены. Полный контекст и команды: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
-> **Docker-image facts (2026-09-02):** All 5 media images **loaded locally** on TrueNAS (`docker images`): `lscr.io/linuxserver/{radarr,sonarr,jellyfin,transmission,prowlarr}:latest`. Only **transmission** container exists (now running as uid 950). **jellyfin/radarr/sonarr/prowlarr containers are NOT yet created** — their compose services have PUID/PGID=950 ready, but they have not been `up`'ed yet.
+> **Docker-image facts (2026-09-02):** All 5 media images **loaded locally** on TrueNAS (`docker images`): `lscr.io/linuxserver/{radarr,sonarr,jellyfin,transmission,prowlarr}:latest`. All containers now exist and run under **uid 950**. Was: transmission container existed (now running as uid 950); jellyfin/radarr/sonarr/prowlarr containers were NOT yet created — now they are (PUID/PGID=950).
> **Transmission originally ran as ROOT** (not 911/950): its compose had no `PUID`/`PGID`, so linuxserver `/init` (s6-overlay) left it root. ✅ **Fixed 2026-09-02:** `PUID=950 PGID=950` added and container force-recreated → now daemon under uid 950. It mounts `storage` → `/mnt/storage`, download-dir = `/mnt/storage/Downloads`.
> **Fix direction (approved Variant A):** set `PUID=950 PGID=950` on transmission + all arr/jellyfin compose services so every service shares uid `950` and mutually reads files; then recursively fix perms in depth. Plan + commands: `2026-09-02-restore-privilege-scope.md` §5.
>
-> **PROGRESS 2026-09-02 (updated end-of-session):** Both compose files edited for PUID/PGID=950 (transmission at `/mnt/RED_2TB/docker/transmission/docker-compose.yml`, arr at `/mnt/RED_2TB/docker/arr/docker-compose.yml` incl. all 4 services). **transmission container force-recreated** from its proper folder (`cd /mnt/RED_2TB/docker/transmission && docker compose up -d --force-recreate`) and now runs its daemon as **uid 950** (was root) — env `PUID=950 PGID=950` confirmed, process under `abc`/950. Media perms in depth recursively fixed to `950:950 drwxrwx---` on all top storage folders (incl. git/nas 2nd root pass). **Git-sync on Mac now RESOLVED** — the broken NFS4 `DENY READ_DATA` ACL on ~1946 loose git objects in `obsidian-vault.git` was stripped via full **dacl replacement** (not stripacl), so `git push` from Mac works again (push `a2c7279..2b6c181`, ahead=0 behind=0). Details + exact working command in `2026-09-02-restore-privilege-scope.md`. **Remaining:** `cd /mnt/RED_2TB/docker/arr && docker compose up -d` and verify jellyfin sees libraries (`/storage/*`). NOTE: radarr/sonarr/jellyfin mount `/mnt/RED_2TB/storage` as `/storage` (NOT `/media` like Kraken) — radarr root-folder on Taiga differs.
+> **PROGRESS 2026-09-02 (updated end-of-session):** Both compose files edited for PUID/PGID=950 (transmission at `/mnt/RED_2TB/docker/transmission/docker-compose.yml`, arr at `/mnt/RED_2TB/docker/arr/docker-compose.yml` incl. all 4 services). **transmission container force-recreated** from its proper folder (`cd /mnt/RED_2TB/docker/transmission && docker compose up -d --force-recreate`) and now runs its daemon as **uid 950** (was root) — env `PUID=950 PGID=950` confirmed, process under `abc`/950. Media perms in depth recursively fixed to `950:950 drwxrwx---` on all top storage folders (incl. git/nas 2nd root pass). **Git-sync on Mac now RESOLVED** — the broken NFS4 `DENY READ_DATA` ACL on ~1946 loose git objects in `obsidian-vault.git` was stripped via full **dacl replacement** (not stripacl), so `git push` from Mac works again (push `a2c7279..2b6c181`, ahead=0 behind=0). **Arr stack is UP (2026-09-02 end):** `cd /mnt/RED_2TB/docker/arr && docker compose up -d` created/started prowlarr/radarr/sonarr/jellyfin. All daemons under **uid 950**. jellyfin reads `/storage/Movies`; radarr↔transmission via `media_net` (`transmission:9091`) OK; caddy resolves transmission via `transmission_default` (Web UI). UI ports: prowlarr 9696, radarr 7878, sonarr 8989, jellyfin 8096, transmission 9091. NOTE: radarr/sonarr/jellyfin mount `/mnt/RED_2TB/storage` as `/storage` (NOT `/media` like Kraken) — radarr root-folder on Taiga differs.
## Notes from Setup (2026-05-20)
diff --git a/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md b/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md
new file mode 100644
index 00000000..69aa841e
--- /dev/null
+++ b/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md
@@ -0,0 +1,61 @@
+---
+title: TrueNAS NFS4 ACL repair + cross-container media PUID
+created: 2026-09-02
+updated: 2026-09-02
+type: tech
+namespace: personal
+tags: [truenas, nfs4, acl, midclt, arr, radarr, sonarr, jellyfin, transmission, docker]
+related:
+ - "[[truenas-zfs-panic-recovery]]"
+ - "[[arr-stack-taiga]]"
+---
+
+# TrueNAS NFS4 ACL repair + cross-container media PUID
+
+> Hard-won lessons from the 2026-08-31/09-01/09-02 TrueNAS restore-incident remediation. Mirror of skill `truenas-nfs4-acl-and-arrmultiuser`. Detailed incident log: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
+
+## Trigger
+Any "permission denied" / file not readable / git `loose object ... corrupt` / empty Jellyfin library on **TrueNAS SCALE** (acltype=nfsv4) after a zfs restore, OR wiring Prowlarr/Radarr/Sonarr/Jellyfin/Transmission so container-written files are readable by other containers.
+
+## Core facts
+- TrueNAS SCALE uses **NFSv4 ACL**, not plain POSIX. `ls -la` shows a POSIX mask that can mislead.
+- Read real ACL: `midclt call filesystem.getacl `; write: `midclt call filesystem.setacl`.
+- **`truenas_admin` is FULL_ADMIN in midclt** → can chown/setacl without passwordless root.
+- Web UI ACL editing only works on zfs **datasets**, not arbitrary subdirs under one dataset → use midclt/CLI.
+- Post-restore a tree may be owned by wrong uid (e.g. 921 `transmission`) with files `mode 0000`.
+
+## The two big gotchas
+### 1. setacl takes ONE JSON with `path` INSIDE it
+`midclt call filesystem.setacl /path {…}` → `[EFAULT] Too many arguments (expected 1, found 2)`.
+Correct form:
+```
+midclt call filesystem.setacl '{"path":"/x","uid":950,"gid":950,"acltype":"NFS4","dacl":[...],"options":{...}}'
+```
+Jobs run async → returns a job id; verify `midclt call core.get_jobs` (state `SUCCESS`).
+
+### 2. stripacl does NOT remove DENY; full dacl replacement does
+`options.stripacl:true` reported `SUCCESS` but left the `owner@ DENY` ACE intact. Only passing a **complete dacl array** works — setacl treats it as a full REPLACE of the ACL.
+
+## NFSv4 DENY overrides ALLOW (the corrupt-object trap)
+A file owned by uid 950 carrying `owner@ DENY READ_DATA=True` cannot be read by uid 950 itself. Git then reports `loose object ... corrupt` — this is **ACL, not data damage** (data intact; root reads fine).
+Diagnostic trap: `git fsck` under **root** is clean, but fetch/push under the owning uid fails → ACL, not corruption.
+Broken files often show POSIX `mode 40` (`r--------`); healthy objects `750`.
+
+## Repair / cross-container recipe (linuxserver media stack)
+Goal: all containers run under ONE uid (950) so transmission→radarr/sonarr→jellyfin all read each other's files.
+- transmission: add `PUID=950 PGID=950` to env in `docker-compose.yml`. It mounts `storage`→`/mnt/storage`, download-dir `/mnt/storage/Downloads`.
+- radarr/sonarr/prowlarr/jellyfin compose: add `PUID=950 PGID=950` to each (retain `TZ`).
+- linuxserver s6 images (Entrypoint `/init`, `/etc/s6-overlay`) honor PUID/PGID natively. Default (unset) = runs **root** → creates root-owned files that 950-services can't read → always set PUID/PGID.
+- Recreate from proper folder: `cd /mnt/RED_2TB/docker/ && docker compose up -d` (+`--force-recreate` if env changed). Verify daemon uid via `ps aux` inside container (process as `abc`/950 = applied). NOTE `docker exec id` ≠ daemon uid.
+- Simple datasets: recursive POSIX `chown -R 950:950` + `chmod` under root SUFFICE (ACLs were trivial). Use midclt/ACL only when non-trivial ACEs present (check `filesystem.getacl` → `trivial:true`).
+- Bare git repo with `owner@ DENY` on objects → full dacl replacement recursively (perms JSON like above).
+
+## Docker network persistency (per-service compose)
+- `docker network connect ` is LOST on recreate → declare `` in the service's compose `networks:`.
+- Caddy must co-own a network with its reverse-proxy target. transmission Web UI works because caddy is ALSO in `transmission_default`; radarr↔transmission works because both in `media_net`.
+- `external: true` networks (media_net/caddy_default) aren't created by compose — if they vanish (pool rebuild), `docker network create media_net`.
+
+## Containers / uid summary (as of 2026-09-02, TrueNAS "Taiga")
+- prowlarr/radarr/sonarr/jellyfin: all running, all daemons under **uid 950**, project `arr`.
+- transmission: PUID/PGID=950, in networks `media_net` + `transmission_default` (both external).
+- Radarr/sonarr/jellyfin mount `/mnt/RED_2TB/storage` as `/storage` (NOT `/media` as on Kraken) → set radarr root-folder accordingly.
From 319d674199d59614ebee64462ef405c26926a098 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 12:50:57 +0600
Subject: [PATCH 70/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
family/how-to/arr-stack-taiga.md
---
.../2026-09-02-restore-privilege-scope.md | 32 ++++++++++++++++++-
family/how-to/arr-stack-taiga.md | 4 +++
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
index cbf007c5..5e322876 100644
--- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -2,7 +2,7 @@
tags: [truenas, restore, incident, git, media, arr, permission]
created: 2026-09-02
updated: 2026-09-02
-status: resolved (Git-sync на маке ПОЧИНЕН 2026-09-02: NFS4 ACL DENY сняты полным dacl-заменой через filesystem.setacl; push ИДЁТ; ahead=0 behind=0). Arr-стек ПОДНЯТ и работает 2026-09-02 (prowlarr/radarr/sonarr/jellyfin Up, все под uid 950; transmission также PUID/PGID=950; сети media_net+transmission_default персистентны в compose). Осталось: фикс права на скрытые файлы корня storage (решение не подтверждено) + несвязанный library-app restart-loop
+status: resolved (Git-sync на маке ПОЧИНЕН 2026-09-02: NFS4 ACL DENY сняты полным dacl-заменой через filesystem.setacl; push ИДЁТ; ahead=0 behind=0). Arr-стек ПОДНЯТ и работает 2026-09-02 (prowlarr/radarr/sonarr/jellyfin Up, все под uid 950; transmission также PUID/PGID=950; сети media_net+transmission_default персистентны в compose). ⚠️ Осталось (см. §6): jellyfin-транскодинг падает FFmpeg code 243 — конфиг /docker/arr/jellyfin вглубь 911:911, фикс `chown -R 950:950 .../jellyfin`+recreate (root, не выполнен); transmission 255 торрентов "no data found" — данные в Movies не в download-dir (ждёт решения). Плюс остаточные: скрытые файлы корня storage + несвязанный library-app restart-loop
---
# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
@@ -276,6 +276,36 @@ services:
```
(в compose НЕ было PUID/PGID → контейнер root; transmission монтирует `storage` → `/mnt/storage`, download-dir `/mnt/storage/Downloads`)
+## 6. ⚠️ Пост-запуск arr-стека: 2 НЕРЕШЁННЫХ блокатора (диагноз 2026-09-02, конец сессии)
+
+После успешного запуска стека (все контейнеры Up под 950) выявлены два REFAIL, требующих root-фикса. **Оба — артефакты того же restore-инцидента (владелец глубин конфигов/данных остался 911), НЕ перепутать с багом прав на `storage/`.**
+
+### ⚠️ 6a. Jellyfin: FFmpeg exited with code 243 → фильмы не проигрываются
+**Симптом:** веб-интерфейс jellyfin (Setup завершён, `StartupWizardCompleted:true`, библиотеки видят `/storage/Movies`, одиночные файлы читаются под 950). Но при проигрывании ПОПЫТКА транскодинга падает:
+```
+FFmpeg exited with code 243
+ffmpeg -i file:"/storage/Movies/Snatch.x264/Snatch....mkv" -f hls -hls_segment_filename "/config/cache/transcodes/....%d.mp4"
+→ jellyfin не может писать в transcode-файл
+```
+**Корень:** конфиг jellyfin `/mnt/RED_2TB/docker/arr/jellyfin` — при restore вглубь `/config/data`, `/config/metadata`, `/config/cache` остались **владельца 911:911** (jellyfin работает под uid 950). Например `data/data/jellyfin.db` и 11 441 файл под `data/` — владелец `911`. Jellyfin/ffmpeg под 950 не могут писать в `/config/cache/transcodes` → код 243. (Только jellyfin пострадал: у radarr/sonarr/prowlarr весь config уже `abc`=950.)
+**Фикс (ROOT — НЕ выполнен Alex, ожидает команды):**
+```bash
+chown -R 950:950 /mnt/RED_2TB/docker/arr/jellyfin
+cd /mnt/RED_2TB/docker/arr && docker compose up -d --force-recreate jellyfin
+# проверка: jellyfin должен транскодить/проигрывать без FFmpeg code 243
+```
+> Безопасно: chown меняет только владение, даннятые БД/метаданные не трогает.
+
+### ⚠️ 6b. Transmission: 255 торрентов "no data found" — данные перемещены в Movies, не в download-dir
+**Симптом:** transmission-UI (порт 9091) открывается, но все (255) торренты показывают **No Data Found**.
+**Корень (НЕ проблема прав):** transmission download-dir = `/mnt/storage/Downloads`. 255 resume-торрентов + 255 .torrent на месте (в `/docker/transmission`), daemon работает под 950, путь `/mnt/storage/Downloads` доступен, диск есть (78%). **Но сам download-dir пуст от медиа**: `find Downloads *.mkv/*.avi/*.mp4/*.iso` → **count 0**; внутри только `.nfo`, NSP-игры Switch (DK/Mario), переводы. Фильмы/контент физически **переехали в `/storage/Movies`** (257k файлов). transmission ищет файл по `/<имя-торрента>`, там пусто → No Data Found. Это состояние после импорта/миграции (торренты завершены, контент в библиотеке) — НЕ поломка прав. RPC недоступен извне: `403 Forbidden` на loopback и `401` при неправильных creds; whitelist = `172.16.*.*`.
+**Требует решения Alex**, что сделать с 255 orphan-seed передач:
+- либо удалить их из transmission (если сидирование не нужно),
+- либо указать transmission актуальное расположение (файлы в Movies),
+- НЕ нужно «чинить права» — их там уже нет проблемы.
+
+---
+
## Связанные заметки
- [[2026-08-31-syncthing-truenas-incident]] — вызвавший инцидент + фикс syncthing
- [[arr-stack-taiga]] — acquisition-стек на TrueNAS
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index fa964200..ead613d1 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -15,6 +15,10 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
> **STATE 2026-09-02 (end-of-session):** Стек **ПОДНЯТ И РАБОТАЕТ** ✅. `docker compose up -d` в `/mnt/RED_2TB/docker/arr` создал и запустил `prowlarr`, `radarr`, `sonarr`, `jellyfin` (все в проекте `arr`, процессы под **uid 950** — PUID/PGID=950 применён). Сеть `media_net` воссоздана (`docker network create media_net`), transmission подключён к `media_net`. **Персистентность сетей:** в `docker/transmission/docker-compose.yml` добавлена networks секция `media_net` + `transmission_default` (external) — transmission сам находится в обеих сетях при любом пересоздании.
>
+> **⚠️ Два НЕРЕШЁННЫХ пост-запуска блокатора (2026-09-02 конец):**
+> 1. **Jellyfin не проигрывает** — транскодинг падает `FFmpeg exited with code 243`, т.к. в конфиге `/mnt/RED_2TB/docker/arr/jellyfin` вглубь `/config/data`, `/config/cache`, `/config/metadata` остались владельца **911:911** (11,441 файлов), а jellyfin работает под 950 → не пишет transcodes. Фикс (root): `chown -R 950:950 /mnt/RED_2TB/docker/arr/jellyfin && cd /mnt/RED_2TB/docker/arr && docker compose up -d --force-recreate jellyfin`.
+> 2. **Transmission 255 торрентов "no data found"** — данные скачаны в `/storage/Movies`, а download-dir у transmission `/mnt/storage/Downloads` (там медиа нет). Это не ошибка прав; ждёт решения (удалить orphan seed или направить на актуальное расположение).
+>
> **Почему стек изначально не запускался — медиа-права были сломаны** (тот же restore-инцидент uid 921/0000): `/mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared}` были `d---------`/`drwx------`, owner `transmission` (uid 921). radarr/sonarr монтируют `/storage` (rw), jellyfin `:ro` — со сломанными правами не прочитали бы библиотеку. ✅ **Права медиа ИСПРАВЛЕНЫ** (2026-09-02) до `950:950 drwxrwx---`; контейнеры arr/jellyfin затем подняты и запущены. Полный контекст и команды: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
> **Docker-image facts (2026-09-02):** All 5 media images **loaded locally** on TrueNAS (`docker images`): `lscr.io/linuxserver/{radarr,sonarr,jellyfin,transmission,prowlarr}:latest`. All containers now exist and run under **uid 950**. Was: transmission container existed (now running as uid 950); jellyfin/radarr/sonarr/prowlarr containers were NOT yet created — now they are (PUID/PGID=950).
From d4c13c33199a66063949fadc2066aa48eee2f4f5 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 13:01:04 +0600
Subject: [PATCH 71/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
family/how-to/arr-stack-taiga.md
personal/tech/truenas-nfs4-acl-and-arrmultiuser.md
---
.../2026-09-02-restore-privilege-scope.md | 43 +++++++++----------
family/how-to/arr-stack-taiga.md | 6 +--
.../tech/truenas-nfs4-acl-and-arrmultiuser.md | 17 ++++++++
3 files changed, 40 insertions(+), 26 deletions(-)
diff --git a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
index 5e322876..e447379b 100644
--- a/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
+++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
@@ -2,7 +2,7 @@
tags: [truenas, restore, incident, git, media, arr, permission]
created: 2026-09-02
updated: 2026-09-02
-status: resolved (Git-sync на маке ПОЧИНЕН 2026-09-02: NFS4 ACL DENY сняты полным dacl-заменой через filesystem.setacl; push ИДЁТ; ahead=0 behind=0). Arr-стек ПОДНЯТ и работает 2026-09-02 (prowlarr/radarr/sonarr/jellyfin Up, все под uid 950; transmission также PUID/PGID=950; сети media_net+transmission_default персистентны в compose). ⚠️ Осталось (см. §6): jellyfin-транскодинг падает FFmpeg code 243 — конфиг /docker/arr/jellyfin вглубь 911:911, фикс `chown -R 950:950 .../jellyfin`+recreate (root, не выполнен); transmission 255 торрентов "no data found" — данные в Movies не в download-dir (ждёт решения). Плюс остаточные: скрытые файлы корня storage + несвязанный library-app restart-loop
+status: fully resolved 2026-09-02 — Git-sync (NFS4 ACL DENY сняты полной dacl-заменой, push ОК, ahead=0/behind=0), Arr-стек ПОЛНОСТЬЮ рабочий (prowlarr/radarr/sonarr/jellyfin/transmission Up, все uid 950, сети персистентны в compose). ✅ §6a jellyfin-траversed-фикс: root `chown 950:950 + chmod 750 /mnt/RED_2TB/storage` (был 921:921, блокировал traverse → FFmpeg 243) — проигрывание работает. ✅ §6b transmission: `docker restart` → 254/255 чисты. Незначительные остатки: торрент #1 требует Verify в UI; скрытые файлы корня storage; несвязанный library-app restart-loop
---
# Incident 2026-09-02: Поломка прав uid 921/0000 после restore — масштаб и влияния
@@ -276,33 +276,30 @@ services:
```
(в compose НЕ было PUID/PGID → контейнер root; transmission монтирует `storage` → `/mnt/storage`, download-dir `/mnt/storage/Downloads`)
-## 6. ⚠️ Пост-запуск arr-стека: 2 НЕРЕШЁННЫХ блокатора (диагноз 2026-09-02, конец сессии)
+## 6. Пост-запуск arr-стека: 2 блокера — ОБА РЕШЕНЫ (2026-09-02, конец сессии)
-После успешного запуска стека (все контейнеры Up под 950) выявлены два REFAIL, требующих root-фикса. **Оба — артефакты того же restore-инцидента (владелец глубин конфигов/данных остался 911), НЕ перепутать с багом прав на `storage/`.**
+После успешного запуска стека (все контейнеры Up под 950) выявлены два REFAIL. **Общий скрытый корень обоих + случайно та же причина, что у jellyfin-транскодинга:** **сам корень `/mnt/RED_2TB/storage` (верх датасета) остался `921:921`**, ACL `owner@/group@ ALLOW` но `everyone@ EXECUTE=False` → uid 950 (jellyfin/transmission как не-owner group truenas_admin...) фактически **не мог traverse вглубь** `/storage/Movies/...`, ХОТЯ ACL самих файлов/каталогов были корректными (`trivial:true`, owner@ READ/EXEC, 950:950). Это даёт клинический кейс «ACL файла чистый, владелец 950, но чтение = Permission denied».
-### ⚠️ 6a. Jellyfin: FFmpeg exited with code 243 → фильмы не проигрываются
-**Симптом:** веб-интерфейс jellyfin (Setup завершён, `StartupWizardCompleted:true`, библиотеки видят `/storage/Movies`, одиночные файлы читаются под 950). Но при проигрывании ПОПЫТКА транскодинга падает:
-```
-FFmpeg exited with code 243
-ffmpeg -i file:"/storage/Movies/Snatch.x264/Snatch....mkv" -f hls -hls_segment_filename "/config/cache/transcodes/....%d.mp4"
-→ jellyfin не может писать в transcode-файл
-```
-**Корень:** конфиг jellyfin `/mnt/RED_2TB/docker/arr/jellyfin` — при restore вглубь `/config/data`, `/config/metadata`, `/config/cache` остались **владельца 911:911** (jellyfin работает под uid 950). Например `data/data/jellyfin.db` и 11 441 файл под `data/` — владелец `911`. Jellyfin/ffmpeg под 950 не могут писать в `/config/cache/transcodes` → код 243. (Только jellyfin пострадал: у radarr/sonarr/prowlarr весь config уже `abc`=950.)
-**Фикс (ROOT — НЕ выполнен Alex, ожидает команды):**
+### ✅ 6a. Jellyfin: FFmpeg exit 243 при проигрывании — РЕШЕНО
+**Симптом:** веб-UI jellyfin (Setup завершён, `StartupWizardCompleted:true`) открылся, библиотеки видят `/storage/Movies`, но проигрывание падало `FFmpeg exited with code 243` (на чтении входа `/storage/Movies/Snatch.x264/...`, НЕ на записи transcode — transcodes/`/config/cache` уже стали 950 после первого chown).
+**Двойной корень:** (1) конфиг jellyfin вглубь `/config/data` был 911:911 (11 441 файл; jellyfin под 950) → первый `chown -R 950:950 /mnt/RED_2TB/docker/arr/jellyfin` открыл UI (этот chown Alex уже выполнил ранее); (2) НО даже после него чтение файла `/storage/Movies/Snatch.x264/...` из uid 950 давало **`Permission denied`** при чистом ACL файла+каталога. Причина — **корень `/mnt/RED_2TB/storage` остался 921:921 и блокировал traverse** (см. выше).
+**Фикс ЖЕЛЕЗНО (root, Alex выполнил — заработало):**
```bash
-chown -R 950:950 /mnt/RED_2TB/docker/arr/jellyfin
-cd /mnt/RED_2TB/docker/arr && docker compose up -d --force-recreate jellyfin
-# проверка: jellyfin должен транскодить/проигрывать без FFmpeg code 243
+chown 950:950 /mnt/RED_2TB/storage
+chmod 750 /mnt/RED_2TB/storage # rwxr-x--- → owner u950 / group(g950=truenas_admin) r-x → traverse для jellyfin
```
-> Безопасно: chown меняет только владение, даннятые БД/метаданные не трогает.
+> linuxserver контейнеры (jellyfin/transmission) под PUID/PGID=950 имеют gid=950 → члены `truenas_admin`. После chmod 750 group r-x открывает traverse. Результат: jellyfin проигрывает (direct play / transcod по необходимости). **Отключение транскодинга** (если не нужен): Dashboard → Playback → снять галку «Allow media playback that requires transcoding» (тогда только Direct Play); транскодиг включается лишь когда клиент не поддерживает исходный кодек. UI на `jellyfin.mallexxx.duckdns.org`.
-### ⚠️ 6b. Transmission: 255 торрентов "no data found" — данные перемещены в Movies, не в download-dir
-**Симптом:** transmission-UI (порт 9091) открывается, но все (255) торренты показывают **No Data Found**.
-**Корень (НЕ проблема прав):** transmission download-dir = `/mnt/storage/Downloads`. 255 resume-торрентов + 255 .torrent на месте (в `/docker/transmission`), daemon работает под 950, путь `/mnt/storage/Downloads` доступен, диск есть (78%). **Но сам download-dir пуст от медиа**: `find Downloads *.mkv/*.avi/*.mp4/*.iso` → **count 0**; внутри только `.nfo`, NSP-игры Switch (DK/Mario), переводы. Фильмы/контент физически **переехали в `/storage/Movies`** (257k файлов). transmission ищет файл по `/<имя-торрента>`, там пусто → No Data Found. Это состояние после импорта/миграции (торренты завершены, контент в библиотеке) — НЕ поломка прав. RPC недоступен извне: `403 Forbidden` на loopback и `401` при неправильных creds; whitelist = `172.16.*.*`.
-**Требует решения Alex**, что сделать с 255 orphan-seed передач:
-- либо удалить их из transmission (если сидирование не нужно),
-- либо указать transmission актуальное расположение (файлы в Movies),
-- НЕ нужно «чинить права» — их там уже нет проблемы.
+### ✅ 6b. Transmission: 255 торрентов "no data found" — РЕШЕНО `docker restart`
+**Симптом:** transmission-UI (9091) открывался, но все 255 торрентов показывали **`error 3: No Data Found`**.
+**Уточнённый диагноз (проверено RPC изнутри, whitelist `172.16.*.*`):** downloadDir у каждого торрента УЖЕ корректно указывал не на Downloads, а на **`/mnt/storage/Movies`** (или `/storage/documentaries`), и файлы там физически ЕСТЬ (164 single `.mkv` прямо в Movies; `Thursday.1998_BDRip_cw.mkv` 3.1G и т.д.), владелец 950 `-rw-r-----`, читаются под uid 950 (`docker exec -u 950` → READ_OK). **НЕ** проблема download-dir и НЕ права — файл доступен демону. `torrent-verify` поодиночке НЕ снимал ошибку.
+**Настоящий корень:** ошибка `No Data Found` была **закеширована при старте**, когда транзит через корень `/storage` был ещё заблокирован (до `chmod 750` на корень). transmission не перевалидировал данные после разблокировки traverse.
+**Фикс (НЕ root):** простая перезагрузка демона заново валидирует все торренты при доступном traverse:
+```bash
+docker restart transmission
+```
+**Результат:** **254 из 255 торрентов СРАЗУ чисты** (было 0). Остался только id=1 (`Thursday.1998_BDRip_cw.mkv`) — ранее ему крутили `torrent-verify`, но он всё ещё `error 3`; добить: в Web UI transmission на торренте → **Verify Local Data**, либо ещё один verify.
+> Вывод для будущего: «No Data Found» у всех торрентов в transmission после пересоздания/миграции данных чаще всего = **старт демона при недоступном traverse к данным**. Решение — починить traverse (root каталога/путь) и `docker restart`. Данные при этом НЕ трогаются.
---
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index ead613d1..5ab79d77 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -15,9 +15,9 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
> **STATE 2026-09-02 (end-of-session):** Стек **ПОДНЯТ И РАБОТАЕТ** ✅. `docker compose up -d` в `/mnt/RED_2TB/docker/arr` создал и запустил `prowlarr`, `radarr`, `sonarr`, `jellyfin` (все в проекте `arr`, процессы под **uid 950** — PUID/PGID=950 применён). Сеть `media_net` воссоздана (`docker network create media_net`), transmission подключён к `media_net`. **Персистентность сетей:** в `docker/transmission/docker-compose.yml` добавлена networks секция `media_net` + `transmission_default` (external) — transmission сам находится в обеих сетях при любом пересоздании.
>
-> **⚠️ Два НЕРЕШЁННЫХ пост-запуска блокатора (2026-09-02 конец):**
-> 1. **Jellyfin не проигрывает** — транскодинг падает `FFmpeg exited with code 243`, т.к. в конфиге `/mnt/RED_2TB/docker/arr/jellyfin` вглубь `/config/data`, `/config/cache`, `/config/metadata` остались владельца **911:911** (11,441 файлов), а jellyfin работает под 950 → не пишет transcodes. Фикс (root): `chown -R 950:950 /mnt/RED_2TB/docker/arr/jellyfin && cd /mnt/RED_2TB/docker/arr && docker compose up -d --force-recreate jellyfin`.
-> 2. **Transmission 255 торрентов "no data found"** — данные скачаны в `/storage/Movies`, а download-dir у transmission `/mnt/storage/Downloads` (там медиа нет). Это не ошибка прав; ждёт решения (удалить orphan seed или направить на актуальное расположение).
+> **✅ Два пост-запуска блокатора — ОБА РЕШЕНЫ (2026-09-02 конец):**
+> 1. **Jellyfin не проигрывал (FFmpeg 243)** — переход решён двумя фиксами: (а) `chown -R 950:950 /mnt/RED_2TB/docker/arr/jellyfin` (конфиг вглубь был 911:911) открыл UI; (б) главный скрытый корень — корень `/mnt/RED_2TB/storage` остался `921:921`, ACL `everyone@ EXECUTE=False` → uid 950 не мог traverse в `/storage/Movies/...`. Фикс (root): `chown 950:950 /mnt/RED_2TB/storage && chmod 750 /mnt/RED_2TB/storage`. После этого jellyfin проигрывает. Транскодинг откл.: Dashboard→Playback→убрать «Allow …transcoding».
+> 2. **Transmission 255 торрентов "no data found"** — downloadDir УЖЕ корректно указывал на `/storage/Movies`, файлы были и читались под 950; ошибка была закеширована от старта при блокированном traverse. Фикс (НЕ root): `docker restart transmission` → **254/255 чисты** сразу. Остался торрент #1 — Verify в UI.
>
> **Почему стек изначально не запускался — медиа-права были сломаны** (тот же restore-инцидент uid 921/0000): `/mnt/RED_2TB/storage/{Movies,Cartoons,Downloads,Music,series,shared}` были `d---------`/`drwx------`, owner `transmission` (uid 921). radarr/sonarr монтируют `/storage` (rw), jellyfin `:ro` — со сломанными правами не прочитали бы библиотеку. ✅ **Права медиа ИСПРАВЛЕНЫ** (2026-09-02) до `950:950 drwxrwx---`; контейнеры arr/jellyfin затем подняты и запущены. Полный контекст и команды: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
diff --git a/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md b/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md
index 69aa841e..abba86b1 100644
--- a/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md
+++ b/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md
@@ -50,6 +50,23 @@ Goal: all containers run under ONE uid (950) so transmission→radarr/sonarr→j
- Simple datasets: recursive POSIX `chown -R 950:950` + `chmod` under root SUFFICE (ACLs were trivial). Use midclt/ACL only when non-trivial ACEs present (check `filesystem.getacl` → `trivial:true`).
- Bare git repo with `owner@ DENY` on objects → full dacl replacement recursively (perms JSON like above).
+### GOTCHA 3 — root dataset traverse (Permission denied despite clean file ACL)
+After chown-ing all leaf dirs/files to 950, if a service still gets `Permission denied` reading `/storage/Movies/...` (or jellyfin FFmpeg exit 243), check the **root of the dataset** `/mnt/RED_2TB/storage` itself. In the restore it stayed `921:921` with `owner@/group@ ALLOW` but `everyone@ EXECUTE=False`. uid 950 (not owner, not in gid-921-group) falls under `everyone@` → **cannot traverse past the root** into the tree, even though every nested ACL is clean. Fix (root):
+```bash
+chown 950:950 /mnt/RED_2TB/storage
+chmod 750 /mnt/RED_2TB/storage # rwxr-x---: gid 950 = truenas_admin (containers' group) gets r-x traverse
+```
+Kernel needs EXEC (traverse) on EVERY path component. Diagnostic via `setpriv --reuid=950 ... ls` shows `Permission denied` on a dir whose `getacl` looks clean → suspect root/parent traverse. `stat` via root shows clean 770 but uid 950 can't ls.
+
+### GOTCHA 4 — transmission "all torrents No Data Found" after data migration
+If every transmission torrent shows `error 3: No Data Found` right after a container recreate/pool migration:
+- Check: `docker exec -u` fine to read? downloadDir & files present? If files ARE there and readable as uid 950, the error is likely **stale from daemon startup while traverse was blocked** (see GOTCHA 3).
+- `torrent-verify` on a single torrent does NOT clear it. Fix is trivial & non-destructive:
+```bash
+docker restart transmission
+```
+On restart the daemon re-validates → torrents clear (e.g. **254/255 instantly**). Leftover #1 → Verify Local Data in Web UI.
+
## Docker network persistency (per-service compose)
- `docker network connect ` is LOST on recreate → declare `` in the service's compose `networks:`.
- Caddy must co-own a network with its reverse-proxy target. transmission Web UI works because caddy is ALSO in `transmission_default`; radarr↔transmission works because both in `media_net`.
From 04ac8e51be0445eb1398a5ba8a7c9a9d0157467f Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 13:11:11 +0600
Subject: [PATCH 72/81] [2026-09-02] eagle: family/how-to/arr-stack-taiga.md
---
family/how-to/arr-stack-taiga.md | 51 ++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index 5ab79d77..ddc6eec8 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -15,6 +15,8 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
> **STATE 2026-09-02 (end-of-session):** Стек **ПОДНЯТ И РАБОТАЕТ** ✅. `docker compose up -d` в `/mnt/RED_2TB/docker/arr` создал и запустил `prowlarr`, `radarr`, `sonarr`, `jellyfin` (все в проекте `arr`, процессы под **uid 950** — PUID/PGID=950 применён). Сеть `media_net` воссоздана (`docker network create media_net`), transmission подключён к `media_net`. **Персистентность сетей:** в `docker/transmission/docker-compose.yml` добавлена networks секция `media_net` + `transmission_default` (external) — transmission сам находится в обеих сетях при любом пересоздании.
>
+> **⚠️ ПЛЕЙБЕК РАБОТАЕТ, но НЕ ПЛАВНО на high-bitrate (диагноз 2026-09-02):** после успешного фикса `storage` root traverse jellyfin проигрывает, НО на ТВ «Книга джунглей» тормозит и грузится кусками. **ЭТО НЕ ТРАНСКОДИНГ** (проверено: ffmpeg-процессов нет, transcode dir пуст, h264 720p идёт direct play нативно). **Причина — сеть между двумя домами:** ТВ/Мак (192.168.1.x, ТТК) и TrueNAS (192.168.2.x, Ростелеком 90.189.160.148) — разные домохозяйства/провайдеры; доступ только через внешку. Замер: **~28 Мбит/с потолок, ~104 мс RTT, ~50 мс один межоператорский хоп** (мой провайдер 3–6 мс → прыжок на `194.186.168.65` до 54 мс). Ограничитель = **upload домашней ростелекомовской линии TrueNAS (~30 Мбит/с)**, не Mac-download (мой CDN-даун 78 Мбит/с ✅). Файл 720p BR 7.1 GB (средний ~12 Мбит/с, пики выше) близко к потолку → на динамичных сценах канал забивается + 104мс RTT рвёт HLS-буфер. **Пути решения:** (а) отдавать ниже потолка ~25 Мбит (понизить битрейт через jellyfin-лимит), (б) поднять ап-линию на стороне TrueNAS, (в) если TrueNAS физически в том же здании — дать локальный маршрут вместо обхода через внешку 90.189.160.148. Полный трассировочный разбор: см. секцию ниже «Плейбек по сети → узкое место ап-линии TrueNAS».
+>
> **✅ Два пост-запуска блокатора — ОБА РЕШЕНЫ (2026-09-02 конец):**
> 1. **Jellyfin не проигрывал (FFmpeg 243)** — переход решён двумя фиксами: (а) `chown -R 950:950 /mnt/RED_2TB/docker/arr/jellyfin` (конфиг вглубь был 911:911) открыл UI; (б) главный скрытый корень — корень `/mnt/RED_2TB/storage` остался `921:921`, ACL `everyone@ EXECUTE=False` → uid 950 не мог traverse в `/storage/Movies/...`. Фикс (root): `chown 950:950 /mnt/RED_2TB/storage && chmod 750 /mnt/RED_2TB/storage`. После этого jellyfin проигрывает. Транскодинг откл.: Dashboard→Playback→убрать «Allow …transcoding».
> 2. **Transmission 255 торрентов "no data found"** — downloadDir УЖЕ корректно указывал на `/storage/Movies`, файлы были и читались под 950; ошибка была закеширована от старта при блокированном traverse. Фикс (НЕ root): `docker restart transmission` → **254/255 чисты** сразу. Остался торрент #1 — Verify в UI.
@@ -43,3 +45,52 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
(Details were referenced but not captured. Update this page after next
Taiga arr maintenance session.)
+
+## Плейбек по сети → узкое место ап-линии TrueNAS (диагноз 2026-09-02)
+
+**Симптом:** jellyfin на TrueNAS работает, файлы проигрываются, но на ТВ high-bitrate (720p BluRay и выше) *тормозят / грузятся кусками*, потом подтормаживают на динамике.
+
+**Вердикт — НЕ транскодинг.** Live-проверка во время плейбека через `docker exec jellyfin sh -c 'ps ... | grep ffmpeg'` и `ls /config/cache/transcodes/`:
+- нет ни одного дочернего ffmpeg, transcode dir пуст;
+- jellyfin-процесс `ps` — это сам сервер, не транс-ребёнок.
+- Файл `The.Jungle.Book.1967.720p.BluRay.5xRus.Eng.HDCLUB.mkv` — h264 **High@L4.1, 1256×720** → приложение ТВ (Jellyfin app) тянет нативно (direct play), транскодинг не нужен.
+- Lог же показывает, что даже при просмотре jellyfin может ремуксовать в **HLS-сегменты по 6 сек** (`-codec:v:0 copy ... -f hls` — копия, не перекодирование кадров). «Грузка кусками» = эти HLS-сегменты добираются по сети.
+
+**Корень — сетевая топология «два дома», доступ только через внешку:**
+
+| | Сеть | Шлюз | Внешний IP | Провайдер |
+|---|---|---|---|---|
+| Мак / ТВ | `192.168.1.57` | `192.168.1.1` | динамика | ТТК (92.62.x / 185.20.x) |
+| TrueNAS | `192.168.2.197` | `192.168.2.2` | `90.189.160.148` | Ростелеком (217.107.x) |
+
+TrueNAS **физически/логически НЕ в локальной сети Мак/ТВ**. ТВ→Jellyfin идёт целиком по интернету между двумя домами через межоператорский транзит.
+
+**Замеры (воспроизводимы):**
+```bash
+# Латентность и потери — интернет до внешки TrueNAS:
+ping -c 5 mallexxx.duckdns.org # ~104 мс avg (межсетевой, НЕ LAN)
+
+# Где теряются ~50 мс — межоператорский прыжок:
+traceroute -m 10 -n 90.189.160.148
+# х1-7 мой провайдер: 3–6 мс
+# х8 194.186.168.65 → прыжок до ~54 мс (переход ТТК→рядом с Ростелекомом)
+# х10 217.107.108.41 : ~62 мс
+
+# Пропускная способность Мак→TrueNAS (потолок ап-линии TrueNAS):
+ssh truenas_admin@mallexxx.duckdns.org 'truncate -s 200M /tmp/spd_src.bin; sync'
+time scp -q truenas_admin@mallexxx.duckdns.org:/tmp/spd_src.bin /tmp/spd_dst.bin
+# 200 MB/~60s ≈ ~28 Мбит/с ← потолок
+# (проверка, что Mac-download НЕ виноват: curl CDN даёт ~9.8 MB/s ≈ 78 Мбит/с)
+
+# Факты TrueNAS-стороны:
+ip -4 addr show | grep inet # enp3s0 = 192.168.2.197 (локальная сеть дома TrueNAS)
+cat /sys/class/net/enp3s0/speed # 1000 Mb/s (NIC 1G, не узкое место на хосте)
+ip route | grep default # via 192.168.2.2 (домашний роутер TrueNAS)
+```
+
+**Вывод:** ограничитель = **ап-линия домашней ростелекомовской линии TrueNAS (~30 Мбит/с)**, не Mac-линк (78 Мбит/с ✅), не кодек, не транскодинг. Файл 7.1GB/78 мин → средний ~12 Мбит/с с пиками >20 → на сценах с высокой динамикой пики близко к потолку 28 Мбит → стоп-кары, усугублённые 104 мс RTT рвущим HLS.
+
+**Что реально чинит:**
+1. Если TrueNAS в том же здании (не подтверждено): дать ТВ/Маку прямой доступ к `192.168.2.197`, а не обход через внешку `90.189.160.148` — станет LAN-скорость, 104 мс уйдут. Топология в `truenas-infrastructure.md` (Доступ) говорит про разные подсети — сверить, один ли это физический роутер/здание.
+2. При истинно удалённом TrueNAS: либо поднять ап-линию тарифа TrueNAS (напр. 500/300), либо в jellyfin ограничить битрейт/качество ниже потолка ~25 Мбит/с (Dashboards→Playback/клиентское качество), либо (наилучшее) смотреть контент через **serving-стек Kraken** (Kraken выполняет serving, Taiga — acquisition; из документации стека это штатное разделение — см. `arr-stack-kraken.md`).
+
From eb247bfa1ddb3709a8acad5a9c487c1c1d15be97 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 13:16:14 +0600
Subject: [PATCH 73/81] [2026-09-02] eagle:
family/how-to/truenas-infrastructure.md
---
family/how-to/truenas-infrastructure.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 9d65f9ed..20d70682 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -42,6 +42,12 @@
- ⚠️ Eagle (Mac, 192.168.1.x) и Kraken (192.168.1.15) находятся в **другой подсети**
- ⚠️ Кра́кен и TrueNAS — **разные сети** (192.168.1.x vs 192.168.2.x). Доступ с Кракена на TrueNAS по локальному IP **не работает**
+**⚠️ Измеренный потолок канала Mac/ТВ (192.168.1.x) → TrueNAS (192.168.2.x) — 2026-09-02:**
+Между этими двумя сетями нет LAN — только обход через внешку провайдеров (ТТК дом A ↔ Ростелеком дом B). Замерено:
+- **~28 Мбит/с** пропускной способности (SCP 200MB за ~60s) — ограничитель = **ап-линия домашней ростелекомовской линии TrueNAS (~30 Мбит/с)**; Mac-download сам даёт 78 Мбит/с.
+- **~104 мс RTT**, из них ~50 мс на **одном межоператорском хопе** `194.186.168.65` (traceroute до `90.189.160.148`).
+- Следствие: **высокобитрейтный медиа-плейбек (720p BluRay и выше) через jellyfin на этом пути тормозит/грузится кусками** (HLS + 104мс RTT vs потолок ~28 Мбит). НЕ транскодинг (jellyfin direct-play). Детали и замеры/команды: `family/how-to/arr-stack-taiga.md` → «Плейбек по сети». Решение для плавного просмотра: повысить ап-тариф на стороне TrueNAS, ограничить битрейт в jellyfin, или смотреть через serving-стек Kraken.
+
## Пользователи и группы (ключевые)
| uid | Имя | gid | Назначение |
From a526949a9be3e7f0bf14d84716dc0ad1854958b4 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 13:41:31 +0600
Subject: [PATCH 74/81] [2026-09-02] eagle: family/how-to/arr-stack-taiga.md
family/how-to/truenas-infrastructure.md
---
family/how-to/arr-stack-taiga.md | 77 +++++++++++++++----------
family/how-to/truenas-infrastructure.md | 10 ++--
2 files changed, 54 insertions(+), 33 deletions(-)
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index ddc6eec8..e148cdad 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -15,7 +15,7 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
> **STATE 2026-09-02 (end-of-session):** Стек **ПОДНЯТ И РАБОТАЕТ** ✅. `docker compose up -d` в `/mnt/RED_2TB/docker/arr` создал и запустил `prowlarr`, `radarr`, `sonarr`, `jellyfin` (все в проекте `arr`, процессы под **uid 950** — PUID/PGID=950 применён). Сеть `media_net` воссоздана (`docker network create media_net`), transmission подключён к `media_net`. **Персистентность сетей:** в `docker/transmission/docker-compose.yml` добавлена networks секция `media_net` + `transmission_default` (external) — transmission сам находится в обеих сетях при любом пересоздании.
>
-> **⚠️ ПЛЕЙБЕК РАБОТАЕТ, но НЕ ПЛАВНО на high-bitrate (диагноз 2026-09-02):** после успешного фикса `storage` root traverse jellyfin проигрывает, НО на ТВ «Книга джунглей» тормозит и грузится кусками. **ЭТО НЕ ТРАНСКОДИНГ** (проверено: ffmpeg-процессов нет, transcode dir пуст, h264 720p идёт direct play нативно). **Причина — сеть между двумя домами:** ТВ/Мак (192.168.1.x, ТТК) и TrueNAS (192.168.2.x, Ростелеком 90.189.160.148) — разные домохозяйства/провайдеры; доступ только через внешку. Замер: **~28 Мбит/с потолок, ~104 мс RTT, ~50 мс один межоператорский хоп** (мой провайдер 3–6 мс → прыжок на `194.186.168.65` до 54 мс). Ограничитель = **upload домашней ростелекомовской линии TrueNAS (~30 Мбит/с)**, не Mac-download (мой CDN-даун 78 Мбит/с ✅). Файл 720p BR 7.1 GB (средний ~12 Мбит/с, пики выше) близко к потолку → на динамичных сценах канал забивается + 104мс RTT рвёт HLS-буфер. **Пути решения:** (а) отдавать ниже потолка ~25 Мбит (понизить битрейт через jellyfin-лимит), (б) поднять ап-линию на стороне TrueNAS, (в) если TrueNAS физически в том же здании — дать локальный маршрут вместо обхода через внешку 90.189.160.148. Полный трассировочный разбор: см. секцию ниже «Плейбек по сети → узкое место ап-линии TrueNAS».
+> **⚠️ ПЛЕЙБЕК РАБОТАЕТ, но НЕ ПЛАВНО на high-bitrate (диагноз 2026-09-02, ОБНОВЛЁН конец сессии):** после успешного фикса `storage` root traverse jellyfin проигрывает, НО на ТВ «Книга джунглей» тормозит и грузится кусками. **ЭТО НЕ ТРАНСКОДИНГ** (проверено: ffmpeg-процессов нет, transcode dir пуст, h264 720p идёт direct play нативно). **НЕ ап-линия TrueNAS** — замер с самого NAS опроверг раннюю гипотезу (см. ниже): линия TrueNAS даёт ~**184 Мбит/с down / ~117 Мбит/с up** (Cloudflare-замер с NAS), т.е. НЕ зажата на ~30 Мбит. **Настоящая причина — per-TCP-flow / BDP-ограничение при высоком RTT** между двумя домами: single SCP = ~28 Мбит/с, но **4 параллельных потока = ~51 Мбит/с** (агрегат РАСТЁТ с параллельностью → это ограничение конгeст-окна одного TCP, а НЕ жёсткий лимит линии/транзита). RTT ~104 мс, ~50 мс теряются на **одном физическом межоператорском прыжке** (мой провайдер TTK 3–6 мс → хоп `194.186.168.65` до 54 мс). Пер-хоп loss-анализ: грань моего ISP чиста (0%, 3 мс), дальние хопы дают спорадические LOSS-кластеры (ICMP-деприоритизация, НЕ устойчивые потери), re-route 104 мс **полностью не уберёт** (это география/пиринг, не дефектная петля). Мак-download НЕ виноват (CDN ~78 Мбит/с ✅). Файл 720p BR 7.1 GB (средний ~12 Мбит/с, пики >20) в пределах одного TCP-потока ~28–51 Мбит с малым запасом + 104 мс рвёт HLS-буфер → динамичные сцены подтормаживают. **Jellyfin НЕ умеет мультистримить один плейбек** (последовательный HLS на одном TCP; штатной настройки «в N потоков» нет). **Реальные пути:** (в 1) if TrueNAS в одном здании/роутере — дать локальный маршрут к `192.168.2.197` (убьёт 104 мс, LAN-скорость); (2) поднять ап-линию TrueNAS ТОЛЬКО если она реально мала — но замер 117 Мбит up говорит, что скорее нужен не тариф, а (3) снижение эффективной латентности/битрейт-запаса: ограничить jellyfin-транскод < ~20 Мбит ИЛИ прокси, тянущий файл с NAS в 4+ TCP-потоков (подтянет агрегат к 51+), ИЛИ WireGuard-туннель через VPS (меньше jitter/пер-потоковой деградации; минус не убирает 104 мс). Жирный отказ от варианта (б) «поднять ап-линию» как первичного — он основывался на ОШИБОЧНОМ замере 28 Мбит от Мака как потолка TrueNAS. Полный пересмотренный разбор: секция «Плейбек по сети → …» ниже.
>
> **✅ Два пост-запуска блокатора — ОБА РЕШЕНЫ (2026-09-02 конец):**
> 1. **Jellyfin не проигрывал (FFmpeg 243)** — переход решён двумя фиксами: (а) `chown -R 950:950 /mnt/RED_2TB/docker/arr/jellyfin` (конфиг вглубь был 911:911) открыл UI; (б) главный скрытый корень — корень `/mnt/RED_2TB/storage` остался `921:921`, ACL `everyone@ EXECUTE=False` → uid 950 не мог traverse в `/storage/Movies/...`. Фикс (root): `chown 950:950 /mnt/RED_2TB/storage && chmod 750 /mnt/RED_2TB/storage`. После этого jellyfin проигрывает. Транскодинг откл.: Dashboard→Playback→убрать «Allow …transcoding».
@@ -46,17 +46,17 @@ Secondary arr stack on Taiga (TrueNAS), added 2026-05-20.
(Details were referenced but not captured. Update this page after next
Taiga arr maintenance session.)
-## Плейбек по сети → узкое место ап-линии TrueNAS (диагноз 2026-09-02)
+## Плейбек по сети → per-TCP-flow/BDP-ограничение при хай-РТТ (диагноз 2026-09-02, ПЕРЕСМОТРЕН конец сессии)
**Симптом:** jellyfin на TrueNAS работает, файлы проигрываются, но на ТВ high-bitrate (720p BluRay и выше) *тормозят / грузятся кусками*, потом подтормаживают на динамике.
-**Вердикт — НЕ транскодинг.** Live-проверка во время плейбека через `docker exec jellyfin sh -c 'ps ... | grep ffmpeg'` и `ls /config/cache/transcodes/`:
+**Вердикт — НЕ транскодинг и НЕ ап-линия TrueNAS.** Live-проверка во время плейбека через `docker exec jellyfin sh -c 'ps ... | grep ffmpeg'` и `ls /config/cache/transcodes/`:
- нет ни одного дочернего ffmpeg, transcode dir пуст;
- jellyfin-процесс `ps` — это сам сервер, не транс-ребёнок.
- Файл `The.Jungle.Book.1967.720p.BluRay.5xRus.Eng.HDCLUB.mkv` — h264 **High@L4.1, 1256×720** → приложение ТВ (Jellyfin app) тянет нативно (direct play), транскодинг не нужен.
-- Lог же показывает, что даже при просмотре jellyfin может ремуксовать в **HLS-сегменты по 6 сек** (`-codec:v:0 copy ... -f hls` — копия, не перекодирование кадров). «Грузка кусками» = эти HLS-сегменты добираются по сети.
+- Lог показывает, что jellyfin даже при direct-play может ремуксовать в **HLS-сегменты по 6 сек** (`-codec:v:0 copy ... -f hls` — копия, не перекодирование). «Грузка кусками» = HLS-сегменты добираются по сети одним TCP-потоком.
-**Корень — сетевая топология «два дома», доступ только через внешку:**
+**Сетевая топология (два дома, доступ только через внешку):**
| | Сеть | Шлюз | Внешний IP | Провайдер |
|---|---|---|---|---|
@@ -65,32 +65,51 @@ Taiga arr maintenance session.)
TrueNAS **физически/логически НЕ в локальной сети Мак/ТВ**. ТВ→Jellyfin идёт целиком по интернету между двумя домами через межоператорский транзит.
-**Замеры (воспроизводимы):**
+**Замеры (воспроизводимы) — САМОЕ ВАЖНОЕ: замер с самого NAS, а не от Мака.**
+> Ранняя гипотеза «~28 Мбит = ап-линия TrueNAS ~30 Мбит» была ОШИБОЧНОЙ: 28 Мбит — это потолок ОДНОГО TCP-потока от Мака по пути с 104мс RTT, а НЕ лимит линии TrueNAS. Проверено замером С TrueNAS до Cloudflare:
```bash
-# Латентность и потери — интернет до внешки TrueNAS:
-ping -c 5 mallexxx.duckdns.org # ~104 мс avg (межсетевой, НЕ LAN)
-
-# Где теряются ~50 мс — межоператорский прыжок:
-traceroute -m 10 -n 90.189.160.148
-# х1-7 мой провайдер: 3–6 мс
-# х8 194.186.168.65 → прыжок до ~54 мс (переход ТТК→рядом с Ростелекомом)
-# х10 217.107.108.41 : ~62 мс
-
-# Пропускная способность Мак→TrueNAS (потолок ап-линии TrueNAS):
-ssh truenas_admin@mallexxx.duckdns.org 'truncate -s 200M /tmp/spd_src.bin; sync'
-time scp -q truenas_admin@mallexxx.duckdns.org:/tmp/spd_src.bin /tmp/spd_dst.bin
-# 200 MB/~60s ≈ ~28 Мбит/с ← потолок
-# (проверка, что Mac-download НЕ виноват: curl CDN даёт ~9.8 MB/s ≈ 78 Мбит/с)
-
-# Факты TrueNAS-стороны:
-ip -4 addr show | grep inet # enp3s0 = 192.168.2.197 (локальная сеть дома TrueNAS)
-cat /sys/class/net/enp3s0/speed # 1000 Mb/s (NIC 1G, не узкое место на хосте)
-ip route | grep default # via 192.168.2.2 (домашний роутер TrueNAS)
+# На самом TrueNAS — интернет-линия НЕ зажата:
+ssh truenas_admin@mallexxx.duckdns.org \
+ 'curl -o /dev/null -s -w "DOWN %{speed_download} B/s\n" "https://speed.cloudflare.com/__down?bytes=50000000"'
+ # DOWN ~23 MB/s ≈ 184 Мбит/с
+ssh truenas_admin@mallexxx.duckdns.org \
+ 'truncate -s 30M /tmp/up.bin && curl -o /dev/null -s -w "UP %{speed_upload} B/s\n" -X POST "https://speed.cloudflare.com/__up" --data-binary @/tmp/up.bin; rm -f /tmp/up.bin'
+ # UP ~14.7 MB/s ≈ 117 Мбит/с
+# ovh/tele2 speedtest ХОСТЫ ЗАБЛОКИРОВАНЫ из RU (curl 000/timeout) — юзать Cloudflare __down/__up.
```
-**Вывод:** ограничитель = **ап-линия домашней ростелекомовской линии TrueNAS (~30 Мбит/с)**, не Mac-линк (78 Мбит/с ✅), не кодек, не транскодинг. Файл 7.1GB/78 мин → средний ~12 Мбит/с с пиками >20 → на сценах с высокой динамикой пики близко к потолку 28 Мбит → стоп-кары, усугублённые 104 мс RTT рвущим HLS.
+**Где реальная пропускная просадка — per-TCP-flow/BDP при высоком RTT** (не жёсткий лимит). Ключевой тест — агрегат растёт с параллельностью:
+```bash
+# 1 поток: ~28 Мбит/с
+scp -q truenas_admin@mallexxx.duckdns.org:/tmp/dis.bin /tmp/dis.bin
-**Что реально чинит:**
-1. Если TrueNAS в том же здании (не подтверждено): дать ТВ/Маку прямой доступ к `192.168.2.197`, а не обход через внешку `90.189.160.148` — станет LAN-скорость, 104 мс уйдут. Топология в `truenas-infrastructure.md` (Доступ) говорит про разные подсети — сверить, один ли это физический роутер/здание.
-2. При истинно удалённом TrueNAS: либо поднять ап-линию тарифа TrueNAS (напр. 500/300), либо в jellyfin ограничить битрейт/качество ниже потолка ~25 Мбит/с (Dashboards→Playback/клиентское качество), либо (наилучшее) смотреть контент через **serving-стек Kraken** (Kraken выполняет serving, Taiga — acquisition; из документации стека это штатное разделение — см. `arr-stack-kraken.md`).
+# 4 параллельных потока: ~51 Мбит/с ← агрегат РАСТЁТ → BDP-штраф одного TCP при 104мс, не лимит линии/транзита
+for f in a b c d; do scp -q truenas_admin@mallexxx.duckdns.org:/tmp/$f.bin /tmp/par_$f.bin & done; wait
+```
+(8-потоковый тест не дозамерен — апрув на запуск истёк; экстраполированное число НЕ фигурирует.)
+
+**Латентность и loss по хопам (per-hop):**
+```bash
+ping -c 5 mallexxx.duckdns.org # ~104 мс avg
+traceroute -m 10 -n 90.189.160.148
+# х1-7 мой провайдер: 3–6 мс
+# х8 194.186.168.65 → прыжок ~54 мс (переход ТТК→Ростелеком-регион)
+
+# per-hop loss probe (через отдельные ping каждые ~0.25с к 92.62.74.1 / 194.186.x / 217.107.x / 8.8.8.8):
+# моя ISP-грань 92.62.74.1: ~3 мс, 0% loss (61/61) ← сегмент чистый
+# дальние хопы (194.186.x,8.8.8.8): спорадич. LOSS-кластерами синхронно → ICMP-деприоритизация, НЕ устойчивые потери данных
+# mtr на macOS по умолчанию нет — ставить tofu (brew install mtr). 8.8.8.8 тоже ~53мс → ВСЕ дальние сети идут через тот же 50мс переход.
+```
+
+**Вывод (пересмотренный):** ограничитель = **per-TCP-flow / BDP-штраф за 104мс RTT**, а не ап-линия TrueNAS (117 Мбит up — отдаёт, но один поток из-за латентности получает лишь ~28–51). Файл 7.1GB/78 мин → средний ~12 Мбит/с, пики >20 — впритык к achievable single-flow ~28–51 с малым запасом; 104мс рвёт HLS-буфер → стоп-кары. Re-route 104мс полностью НЕ уберёт (пиринг/география, но у Мак/ТВ-все дальние сети идут через тот же 50мс переход — намек, что частичный выигрыш смены исхода возможен, но не гарантирован).
+
+**Что реально чинит (по убыванию надёжности):**
+1. **Если TrueNAS в одном здании/роутере** → дать ТВ/Маку локальный маршрут к `192.168.2.197`, не через внешку `90.189.160.148` — убьёт 104мс, станет LAN. Топология в `truenas-infrastructure.md` (Доступ) говорит про разные подсети — обязательно сверить, один ли это физический роутер/здание (вопрос открыт).
+2. **Jellyfin НЕ умеет мультистримить один плейбек** (последовательный HLS на одном TCP; штатной настройки «в N потоков» нет). Параллельность можно применить только на уровне доставки файла, НЕ внутри jellyfin-потока:
+ - прокси-«качель»: локально отдаёт клиенту один поток, а файл тянет с NAS в 4+ TCP (aria2/hget) → подтянет агрегат к ~51+;
+ - **WireGuard-туннель дом↔NAS через VPS** (`91.207.28.205`, см. `truenas-remote-access-reverse-proxy.md`) — меньше jitter/пер-потоковой деградации, но 104мс физику не уберёт.
+3. **Прагматичный фикс под ТВ-стрим сейчас:** ограничить jellyfin-транскод битрейтом < ~20 Мбит (заведомо ниже single-flow achievable ~28), чтобы один HLS-поток укладывался без дожевания. Включается сразу, без новой инфраструктуры.
+4. Поднять ап-линию TrueNAS — **только если** замер из п.º (117 Мбит up) реально маловат для нужного контента; как первичный вариант отвергнут.
+
+> **Перекрёстно:** это исправление ранней ошибочной записи «потолок ~28 = ап-линия TrueNAS» (см. history этого файла до 2026-09-02). Если будущая сессия увидит противоречие — истина здесь: NAS-линк 184/117 Мбит, проблема per-flow/BDP при высоком RTT.
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 20d70682..3414ee59 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -43,10 +43,12 @@
- ⚠️ Кра́кен и TrueNAS — **разные сети** (192.168.1.x vs 192.168.2.x). Доступ с Кракена на TrueNAS по локальному IP **не работает**
**⚠️ Измеренный потолок канала Mac/ТВ (192.168.1.x) → TrueNAS (192.168.2.x) — 2026-09-02:**
-Между этими двумя сетями нет LAN — только обход через внешку провайдеров (ТТК дом A ↔ Ростелеком дом B). Замерено:
-- **~28 Мбит/с** пропускной способности (SCP 200MB за ~60s) — ограничитель = **ап-линия домашней ростелекомовской линии TrueNAS (~30 Мбит/с)**; Mac-download сам даёт 78 Мбит/с.
-- **~104 мс RTT**, из них ~50 мс на **одном межоператорском хопе** `194.186.168.65` (traceroute до `90.189.160.148`).
-- Следствие: **высокобитрейтный медиа-плейбек (720p BluRay и выше) через jellyfin на этом пути тормозит/грузится кусками** (HLS + 104мс RTT vs потолок ~28 Мбит). НЕ транскодинг (jellyfin direct-play). Детали и замеры/команды: `family/how-to/arr-stack-taiga.md` → «Плейбек по сети». Решение для плавного просмотра: повысить ап-тариф на стороне TrueNAS, ограничить битрейт в jellyfin, или смотреть через serving-стек Kraken.
+**⚠️ Измеренный потолок канала Mac/ТВ (192.168.1.x) → TrueNAS (192.168.2.x) — 2026-09-02, ПЕРЕСМОТРЕНО:** Между этими двумя сетями нет LAN — только обход через внешку провайдеров (ТТК дом A ↔ Ростелеком дом B). Замерено:
+- **Single TCP flow ≈ 28 Мбит/с**, но **4 параллельных потока ≈ 51 Мбит/с** (агрегат РАСТЁТ с параллельностью) → это **per-TCP-flow / BDP-штраф за ~104 мс RTT**, а **НЕ лимит линии TrueNAS**.
+- ⚠️ **ПОЗДНЕЙШИЙ ЗАМЕР С САМОГО NAS ОПРОВЕРГ раннюю запись «ап-линия TrueNAS ~30 Мбит/с»**: NAS → Cloudflare даёт **~184 Мбит/с down / ~117 Мбит/с up** (`curl https://speed.cloudflare.com/__down?bytes=50000000` и POST `__up`). ovh/tele2 speedtest из RU заблокированы — юзать Cloudflare-эндпоинты.
+- **~104 мс RTT**, из них ~50 мс на **одном межоператорском хопе** `194.186.168.65` (TTK 3–6 мс → прыжок до ~54 мс). Per-hop loss: моя ISP-грань чиста (0%, 3 мс); дальние хопы дают спорадич. LOSS (ICMP-деприоритизация, не устойчивые потери). Re-route 104мс полностью не уберёт (пиринг/география).
+- Следствие: **высокобитрейтный медиа-плейбек (720p BluRay и выше) через jellyfin на этом пути тормозит/грузится кусками** (HLS одним TCP + 104мс RTT, single-flow ~28–51 Мбит впритык к пикам файла). НЕ транскодинг (direct-play). **Jellyfin не умеет мультистримить один плейбек.**
+- Пересмотренный разбор + команды: `family/how-to/arr-stack-taiga.md` → «Плейбек по сети → per-TCP-flow/BDP». Решения: дать локальный маршрут (если TrueNAS в одном здании/роутере), ограничить jellyfin-битрейт < ~20 Мбит, или прокси с мульти-TCP-загрузкой файла, или WireGuard-туннель через VPS.
## Пользователи и группы (ключевые)
From 407bd2121be29a0309908913777ff4a9399da727 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 13:46:35 +0600
Subject: [PATCH 75/81] [2026-09-02] eagle:
personal/tech/inter-site-throughput-taiga-to-mac.md
---
.../inter-site-throughput-taiga-to-mac.md | 59 +++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 personal/tech/inter-site-throughput-taiga-to-mac.md
diff --git a/personal/tech/inter-site-throughput-taiga-to-mac.md b/personal/tech/inter-site-throughput-taiga-to-mac.md
new file mode 100644
index 00000000..76251527
--- /dev/null
+++ b/personal/tech/inter-site-throughput-taiga-to-mac.md
@@ -0,0 +1,59 @@
+---
+title: Межсайтовая пропускная способность — TrueNAS (Taiga) ↔ Mac/TV
+created: '2026-09-02'
+updated: '2026-09-02'
+type: tech
+namespace: personal
+tags: [network, truenas, throughput, rtt, tcp, jellyfin, xray, bottleneck]
+related:
+ - "[[xray-reverse-tunnel-kraken-truenas]]"
+ - "[[../how-to/truenas-infrastructure]]"
+---
+
+# Межсайтовая пропускная способность — TrueNAS ↔ Mac/TV
+
+> Контекст: после восстановления стека медиа/права на Taiga (см. `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`) Jellyfin запустился и файлы читаются, но **прямое воспроизведение с ТВ «грузится кусками»**. Диагностика 2026-09-02 установила: это **НЕ транскодинг и НЕ тариф**, а ограничение сети между двумя физически разнесёнными локальными сетями.
+
+## Cетевой layout (фактический)
+
+| Узел | LAN | Шлюз | Egress IP | Провайдер |
+|---|---|---|---|---|
+| Mac / TV (дом) | `192.168.1.57` | `192.168.1.1` | динамический | ТТК (92.62.x / 185.20.x) |
+| **TrueNAS (Taiga)** | `192.168.2.197` | `192.168.2.2` | `90.189.160.148` (`mallexxx.duckdns.org`) | Ростелеком (194.186.x → 217.107.x) |
+
+**Ключевое:** TrueNAS НЕ в локальной сети Мака/ТВ. Трафик ТВ→Jellyfin идёт целиком по интернету между двумя разными операторами/домохозяйствами.
+
+## Замеры (2026-09-02)
+
+### Задержка / маршрут
+- RTT Mac → TrueNAS: **~104 мс**.
+- Traceroute: хопы 1–7 моего ISP (ТТК) — **3–6 мс** ✅ чисто. **На хопе 8 (`194.186.168.65`) прыжок до ~54 мс** — межоператорская передача в сеть Ростелекома, где живёт TrueNAS. Хоп 10 (`217.107.108.41`) ~60 мс.
+- Этот +50 мс — физическое/пиринговое расстояние между региональными операторами, НЕ петля. Перенастройка маршрута полностью RTT не уберёт.
+- Примечание: **дальние сети в целом** (8.8.8.8 ~100 мс) у ТТК тоже идут через ~50 мс переход — деградация на стороне выхода, а не только до TrueNAS.
+
+### Пропускная способность (распутали про «тариф»)
+- Mac download с CDN: **~78 Мбит/с** (линия Мака в порядке).
+- **TrueNAS сторона**: NIC `enp3s0` 1 Gb/s, до шлюза 0.2–0.8 мс 0% loss. NAS→Cloudflare: **download ~184 Мбит/с, upload ~117 Мбит/с**. → **Лимита тарифа на TrueNAS НЕТ.**
+- Mac → TrueNAS (SCP 200MB): **~28 Мбит/с** одним потоком.
+- Mac → TrueNAS, **4 параллельных SCP потока: ~28 → ~51 Мбит/с** (агрегат растёт с параллельностью).
+
+## Вывод (важно)
+
+Ограничение — **НЕ по суммарной полосе транзита, а по одному TCP-потоку из-за высокого RTT (~104 мс) + BDP / congestion-window**. Доказательство: 4 потока дали рост ×1.8 (28→51) — при жёстком лимите линии рост не произошёл бы.
+
+**Почему Jellyfin дёргается прямо:** Jellyfin отдаёт видео одним последовательным HLS-потоком (один TCP). Один поток душится RTT-штрафом до ~28–50 Мбит, а BluRay-файл в пиках идёт к 20+ Мбит → с 104 мс RTT сегменты не успевают → стоп-кары. Файл The.Jungle.Book.1967.720p.BluRay ≈ 7.1GB/~78мин → средний ~12 Мбит/с, пики выше.
+
+## Факты ИЗ ЛОГОВ Jellyfin (подтверждают: транскодинг не причина в этих тестах)
+- Реальный транскод с `libx264` + reduce был только на "Snatch" (тестовые короткие старты каждые ~5 с — клиент переподключался).
+- "Один день в Стамбуле"/"Рождественские хроники" — ремукс `-codec:v:0 copy` в HLS (не перекодирование), стоп на ~2-3 сек.
+- В моменте (пока ТВ крутит 720p) — ffmpeg-дочерних процессов нет, папка transcodes пуста → **прямой stream/direct, транскодинг не идёт**.
+
+## Решение / направление
+1. **Правильное лекарство** (не мультипоток ради мультипотока, а либо снижение латентности, либо запас по битрейту под single-flow): лимит транскода ~12–18 Мбит, ЛИБО гнать трафик ТВ→TrueNAS через **xray tunnel** (`vpn.mallexxx.duckdns.org`, смотри `[[xray-reverse-tunnel-kraken-truenas]]`) вместо прямого межоператорского пути.
+2. Jellyfin **не умеет** штатно разбивать один плейбек на параллельные TCP. Параллельность (28→51 Мбит) достижима только поднятием файла способом «качалка в N коннектов» + прокси, либо через туннель.
+3. Задача замера скорости через **`xray-test-client`** контейнер (teddysun/xray, SOCKS `127.0.0.1:1080`) — НЕ завершена: команда замера была заблокирована юзером; способ/endpoint (стучались на внутренний `192.168.2.197:8096`) предложен неверно.
+
+### Endpoint-факты для замера туннеля
+- Локальный тестовый xray-клиент на Mac: контейнер **`xray-test-client`** (`127.0.0.1:1080->1080/tcp`), конфиг `/Users/admin/xray-test/config.json`, оставлен на `kraken-user`.
+- Per-user egress на `vpn.mallexxx.duckdns.org`: `user1` → direct TrueNAS `90.189.160.148`; `kraken-user` → reverse через Kraken `92.62.70.41`.
+- Конфиг клиента лежит на Mac отдельно (не в vault); бэкап direct-клиента: `/Users/admin/xray-test/config.json.bak-user1-direct-20260902-1227`.
From eb7eaa49cfec184a1a63da36d56eb80267f6fa36 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 13:56:42 +0600
Subject: [PATCH 76/81] [2026-09-02] eagle: family/how-to/arr-stack-taiga.md
personal/tech/xray-reverse-tunnel-kraken-truenas.md
---
family/how-to/arr-stack-taiga.md | 2 +-
.../xray-reverse-tunnel-kraken-truenas.md | 24 +++++++++++++++++++
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/family/how-to/arr-stack-taiga.md b/family/how-to/arr-stack-taiga.md
index e148cdad..96703f1e 100644
--- a/family/how-to/arr-stack-taiga.md
+++ b/family/how-to/arr-stack-taiga.md
@@ -107,7 +107,7 @@ traceroute -m 10 -n 90.189.160.148
1. **Если TrueNAS в одном здании/роутере** → дать ТВ/Маку локальный маршрут к `192.168.2.197`, не через внешку `90.189.160.148` — убьёт 104мс, станет LAN. Топология в `truenas-infrastructure.md` (Доступ) говорит про разные подсети — обязательно сверить, один ли это физический роутер/здание (вопрос открыт).
2. **Jellyfin НЕ умеет мультистримить один плейбек** (последовательный HLS на одном TCP; штатной настройки «в N потоков» нет). Параллельность можно применить только на уровне доставки файла, НЕ внутри jellyfin-потока:
- прокси-«качель»: локально отдаёт клиенту один поток, а файл тянет с NAS в 4+ TCP (aria2/hget) → подтянет агрегат к ~51+;
- - **WireGuard-туннель дом↔NAS через VPS** (`91.207.28.205`, см. `truenas-remote-access-reverse-proxy.md`) — меньше jitter/пер-потоковой деградации, но 104мс физику не уберёт.
+ - **WireGuard-туннель дом↔NAS через VPS** (`91.207.28.205`, см. `truenas-remote-access-reverse-proxy.md`) — меньше jitter/пер-потоковой деградации, но 104мс физику не уберёт. **⚠ XRAY-ТУННЕЛЬ УЖЕ ИЗМЕРЕН и НЕ ПОМОГАЕТ** (2026-09-02): оба egress-режима локального `xray-test-client` (`kraken-user` reverse → Kraken ~45 Мбит; `user1` direct/REALITY → TrueNAS ~37 Мбит) дают ~35–45 Мбит с нестабильностью — тот же уровень, НЕ кратно выше single-flow прямого пути (28–51 Мбит). Детали и способ переключения клиента: `personal/tech/xray-reverse-tunnel-kraken-truenas.md` § «СОСТОЯНИЕ КЛИЕНТА/Замеры».
3. **Прагматичный фикс под ТВ-стрим сейчас:** ограничить jellyfin-транскод битрейтом < ~20 Мбит (заведомо ниже single-flow achievable ~28), чтобы один HLS-поток укладывался без дожевания. Включается сразу, без новой инфраструктуры.
4. Поднять ап-линию TrueNAS — **только если** замер из п.º (117 Мбит up) реально маловат для нужного контента; как первичный вариант отвергнут.
diff --git a/personal/tech/xray-reverse-tunnel-kraken-truenas.md b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
index 6f386939..b1cc9b5c 100644
--- a/personal/tech/xray-reverse-tunnel-kraken-truenas.md
+++ b/personal/tech/xray-reverse-tunnel-kraken-truenas.md
@@ -420,6 +420,30 @@ Rollback network fix: вернуть compose-бэкап на Kraken и выпо
Локальный `/Users/admin/xray-test/config.json` оставлен на `kraken-user`. Бэкап direct-клиента: `/Users/admin/xray-test/config.json.bak-user1-direct-20260902-1227`.
+### ⚠️ СОСТОЯНИЕ КЛИЕНТА 2026-09-02 (после замеров скорости туннеля)
+
+Контейнер `xray-test-client` **переключён с `kraken-user` на `user1-direct`** для замеров скорости direct-пути, и НЕ возвращён обратно. Активная конфигурация = **`user1` (direct, REALITY)**.
+
+- Контейнер: `xray-test-client` (image `teddysun/xray:latest`; лог старта показал «Xray 26.7.28 started»), bind-mount `/Users/admin/xray-test/config.json:/etc/xray/config.json:ro`, SOCKS `127.0.0.1:1080→1080`.
+- **Как переключать:** в `/Users/admin/xray-test/`:
+ - `user1-direct` = `config.json.bak-user1-direct-20260902-1227` (id `ce320965-6956-4759-84bb-7cb71cfc6252`) — выход TrueNAS `90.189.160.148`.
+ - kraken-user = прежний активный, сохранён в `config.json.current-kraken-user` (id `93a5dc4b-1b8d-4af5-9363-eb0091734293`) — выход Kraken `92.62.70.41`.
+ - Смена: `cp <файл> /Users/admin/xray-test/config.json && docker restart xray-test-client`.
+- Размер обоих конфигов одинаковый (831 байт), inbound общий (SOCKS :1080 noauth), различие только в VLESS-outbound (client id / egress).
+
+### 📊 Замеры скорости туннелей (2026-09-02, с Mac через `xray-test-client` :1080)
+
+Замер пропускной способности каждого egress-режима (скачивание файлов по SOCKS-прокси контейнера; источник http cachefly + ovh; Cloudflare с этого egress режется = 0 B/s → не показатель):
+
+| Режим | egress IP | Средний down | Разброс | Комментарий |
+|---|---|---|---|---|
+| `kraken-user` (reverse) | `92.62.70.41` (Kraken) | ~5.7 МБ/с (~45 Мбит) | 2.0–6.98 МБ/с | нестабилен, убывающий по ходу |
+| `user1` (direct/REALITY) | `90.189.160.148` (TrueNAS) | ~4.6 МБ/с (~37 Мбит) | 3.67–5.63 МБ/с | cachefly 4×10MB |
+
+Вывод: оба egress-режима туннеля дают ~35–45 Мбит/с с нестабильностью — **не кратно быстрее** прямого межоператорского пути (~28 Мбит single-flow), туннель не даёт надёжного запаса под stрим пиковых битрейтов BluRay (~20+ Мбит). Для jellyfin-стрима на ТВ туннель не решает проблему дёрганья.
+
+Контроль (не туннель, Mac напрямую до Cloudflare): ~55 МБ/с. Туннель ~в 10× медленнее прямого локального интернета.
+
Бэкапы TrueNAS до и после изменения: `/mnt/RED_2TB/docker/backups/xray-per-user-20260902-1200/` (`x-ui.db`, `x-ui.post-change.db`, runtime configs, reverse portal config/compose). Temporary full-admin API token **не создавался**; `api_tokens` осталась пустой.
## Связанные заметки
From ae2084843290d4d26d4aba75d1d538c96815df02 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 14:16:56 +0600
Subject: [PATCH 77/81] [2026-09-02] eagle:
family/how-to/hermes-docker-kraken.md family/how-to/kraken-network.md
---
family/how-to/hermes-docker-kraken.md | 53 +++++++++++++++++++++++++++
family/how-to/kraken-network.md | 17 ++++++++-
2 files changed, 68 insertions(+), 2 deletions(-)
diff --git a/family/how-to/hermes-docker-kraken.md b/family/how-to/hermes-docker-kraken.md
index af9290cf..c710ec88 100644
--- a/family/how-to/hermes-docker-kraken.md
+++ b/family/how-to/hermes-docker-kraken.md
@@ -85,6 +85,59 @@ docker compose up -d
| `hermes/config/` | `/opt/data` | `HERMES_HOME` — config, sessions, skills, memory |
| `/home/kraken/obsidian` | `/vault` | Obsidian vault read/write access |
+## SSH из контейнера → хост (диагностика и фиксы)
+
+> Перенесено из удалённого скилла `kraken-omv-maintenance` / `kraken-docker-ssh`. Актуальные пути и фиксы для случая, когда `ssh kraken-host` изнутри контейнера `hermes-kraken` не работает.
+
+**Архитектура**
+
+```
+Container (hermes-kraken)
+ uid=10000 (dropped from root by entrypoint)
+ HOME=/opt/data
+ ~/.ssh/config = /opt/data/.ssh/config (bind mount from host)
+ /etc/ssh/ssh_config — bind mount (полный /etc/ssh/)
+
+Host (Kraken RPi5)
+ User kraken (uid=1000) owns /home/kraken/.ssh/
+ /home/kraken/.ssh/id_ed25519 — закрытый ключ (обязательно 600)
+ /home/kraken/.ssh/authorized_keys — должен содержать публичный ключ
+```
+
+**Fixed points (НЕ менять)**
+
+1. В compose **не задавать** `user:`, `entrypoint:`, `HERMES_UID/GID` — entrypoint сам сбрасывает uid (image defaults).
+2. Публичный ключ на хост один раз: `ssh-copy-id kraken@`.
+3. Закрытый ключ на хосте **обязан быть 600**: `ssh kraken@ "chmod 600 /home/kraken/.ssh/id_ed25519"` — OpenSSH 10.x (Bookworm) отвергает 644.
+4. В `/etc/passwd` контейнера должен быть `kraken:x:1000` — не выставлять `user:` в compose.
+5. SSH config обязан лежать в `$HOME/.ssh/config` (`$HOME=/opt/data` → `/opt/data/.ssh/config`).
+
+**Диагностика по порядку**
+
+```bash
+ssh kraken@ "echo OK && hostname" # доступен ли хост
+ssh kraken@ "sudo docker ps --filter name=hermes-kraken --format '{{.Names}} {{.Status}}'"
+# изнутри контейнера на хост (loopback алиас kraken-host)
+ssh kraken@ "sudo docker exec hermes-kraken ssh -o ConnectTimeout=5 kraken-host 'echo HELLO'"
+# verbose — какие конфиги читает SSH
+ssh kraken@ "sudo docker exec hermes-kraken ssh -v kraken-host 'echo HELLO' 2>&1 | grep -E 'config|debug1.*/ssh|Host|connect|Permission|authenticate|bad|owner' | head -15"
+ssh kraken@ "sudo docker exec hermes-kraken getent passwd 1000" # uid 1000 в passwd?
+ssh kraken@ "sudo docker exec hermes-kraken ls -la /opt/data/.ssh/id_ed25519"
+ssh kraken@ "sudo docker exec hermes-kraken cat /opt/data/.ssh/config"
+ssh kraken@ "sudo docker exec hermes-kraken sh -c 'echo HOME=\$HOME; ls -la \"\$HOME/.ssh/config\"'"
+```
+
+**Pitfalls**
+
+- Не менять `user:` в compose — entrypoint должен стартовать как root, чтобы сделать chown.
+- Не chown'ить `/opt/data` на `1000:1000` — entrypoint делает `chown -R hermes:hermes` (uid 10000).
+- Не ставить 644 на закрытый ключ.
+- НЕ использовать `Include ~/.ssh/config` в системном `/etc/ssh/ssh_config` — OpenSSH не резолвит `~` в системных Include.
+- Если `$HOME=/opt/data`, а конфиг живёт в `/opt/data/home/.ssh/` — SSH его не прочитает (путь не совпадает).
+- Может быть несколько пар ключей: bind mount приносит хост-ключ, а агент может иметь свой в `/opt/data/home/.ssh/`.
+- passwd сбрасывается при пересоздании контейнера.
+- Полный mount `/etc/ssh/` перезаписывает и host keys тоже.
+
## Notes
- `network_mode: host` — port 8642 is directly on the Kraken host, no port mapping needed
diff --git a/family/how-to/kraken-network.md b/family/how-to/kraken-network.md
index fa702dc2..667f66f6 100644
--- a/family/how-to/kraken-network.md
+++ b/family/how-to/kraken-network.md
@@ -1,7 +1,7 @@
---
title: Kraken Network & Infra
created: '2026-05-23'
-updated: '2026-05-23'
+updated: '2026-09-02'
type: tech
namespace: personal
tags: [infra, kraken, ssh, wireguard, network]
@@ -17,7 +17,20 @@ related:
ssh kraken
```
-IP: `192.168.1.15` (wlan0, primary). SSH alias `kraken` resolves via `~/.ssh/config`.
+SSH alias `kraken` resolves via `~/.ssh/config`.
+
+## Актуальная сеть (проверено 2026-09-02)
+
+> Оба интерфейса живы. **eth0 — активный uplink** (default route metric 100), wlan0 — fallback (metric 600).
+
+| Интерфейс | IP | MAC | Uplink role |
+|---|---|---|---|
+| **eth0** (Ethernet, Gigabit RPi5 rp1-gem/macb) | `192.168.1.14/24` (DHCP) | `2c:cf:67:64:03:f3` | **primary** — default via `192.168.1.1`, metric 100 |
+| **wlan0** (WiFi) | `192.168.1.15/24` (DHCP) | `2c:cf:67:64:03:f4` | fallback — metric 600 |
+
+- Ethernet link: **1000 Mbit/s full duplex** (как и ожидалось для Gigabit-порта RPi5), carrier up, стабильно.
+- Router/gateway: `192.168.1.1` (`f0:79:59:77:9b:70`). eth0 MAC соответствует static lease в доке [[family/how-to/openmediavault-rpi5|openmediavault-rpi5]].
+- ⚠️ Ранее эта заметка утверждала «wlan0 = .15 primary» — устарело (см. таблицу выше).
## WireGuard Topology
From 2fab8793cd33734de27999b25ae6cf1173b11fc7 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 15:58:05 +0600
Subject: [PATCH 78/81] [2026-09-02] eagle:
family/how-to/truenas-infrastructure.md
personal/tech/mac-print-shared-services.md
---
family/how-to/truenas-infrastructure.md | 2 ++
personal/tech/mac-print-shared-services.md | 32 ++++++++++++++++++++--
2 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/family/how-to/truenas-infrastructure.md b/family/how-to/truenas-infrastructure.md
index 3414ee59..a8745eb8 100644
--- a/family/how-to/truenas-infrastructure.md
+++ b/family/how-to/truenas-infrastructure.md
@@ -521,6 +521,8 @@ docker exec cups-splix sh -c "timeout 6 avahi-browse -rt -p _ipp._tcp | grep ';e
> ⚠️ **2026-08-31: топология изменена.** TrueNAS больше НЕ в локальной сети (только публичный DNS `mallexxx.duckdns.org`) — из других подсетей/внешки `192.168.2.197` недостижим, и **снаружи `mallexxx.duckdns.org:631` закрыт**. НО **из самой подсети `192.168.2.x` TrueNAS достижима** (телефон `192.168.2.141` успешно печатал по `192.168.2.197:631` — CUPS access_log это подтверждает). → Samsung Mobile Print (app) печатает (висит на локальном IPP внутри подсети), а штатная Android print service с `ipp://192.168.2.197:631/...` показывала «недоступен» из-за **отсутствия mDNS/AirPrint-анонса** (avahi-daemon не запущен; `printer-dns-sd-name = no-value`). ✅ **Исправлено** (см. раздел «✅ mDNS/AirPrint анонс принтера»): cups-splix пересобран с avahi-daemon, cupsd.conf получил `Browsing On` + `BrowseLocalProtocols dnssd` → принтер анонсируется по mDNS (`mopria-certified=1.3`, IPv4 `192.168.2.197`); Android-служба теперь находит принтер сама. Детали и откат: [[mac-print-shared-services]].
+> ✅ **2026-09-02: печать ИЗВНЕ сети TrueNAS работает через SSH → cups-splix.** Хотя `mallexxx.duckdns.org:631` (IPP) снаружи закрыт, документ (PDF, 1 стр A4) успешно напечатан с клиента вне подсети: `scp` → TrueNAS `/tmp` → `docker cp` внутрь `cups-splix:/tmp` → `docker exec cups-splix lp -d Samsung_CLX-216x_Series -o media=A4 file.pdf` → job в `completed`, `Rendering completed`. CUPS сам конвертит PDF→PS (gs/pdftops есть, PPD `*cupsFilter: 0 pstoqpdl`). Полный рецепт — [[mac-print-shared-services]] (раздел «Печать PDF извне сети TrueNAS»).
+
## Связанные заметки
- [[truenas-access]] — SSH-доступ
- [[truenas-rclone-backup]] — система бэкапов
diff --git a/personal/tech/mac-print-shared-services.md b/personal/tech/mac-print-shared-services.md
index a551e507..bab8cc72 100644
--- a/personal/tech/mac-print-shared-services.md
+++ b/personal/tech/mac-print-shared-services.md
@@ -1,9 +1,14 @@
---
type: tech
topic: print
-tags: [print, printer, samsung, cups, macos]
-created: 2026-08-26
-updated: 2026-08-31
+tags:
+ - print
+ - printer
+ - samsung
+ - cups
+ - macos
+created: 2026-08-26T00:00:00.000Z
+updated: '2026-09-02T00:00:00.000Z'
---
# Печать и общие сервисы (Mac + сеть)
@@ -95,6 +100,27 @@ nc -z 192.168.2.197 631 # 631 OPEN снаружи
Полный рецепт на будущее продублирован в `truenas-infrastructure.md` → раздел «Печать / cups-splix».
+## ✅ Печать PDF извне сети TrueNAS (когда клиент НЕ в подсети, `mallexxx.duckdns.org:631` закрыт)
+
+Когда принтер физически в TrueNAS, а клиент НЕ в подсети `192.168.2.x` → прямой IPP до `:631` закрыт. Печатать через SSH `mallexxx.duckdns.org` в контейнер `cups-splix` (проверено 2026-09-02):
+
+```bash
+# 1. Залить PDF на TrueNAS
+scp -i ~/.ssh/id_rsa file.pdf truenas_admin@mallexxx.duckdns.org:/tmp/evrika_print.pdf
+# 2. Скопировать внутрь контейнера
+ssh -i ~/.ssh/id_rsa truenas_admin@mallexxx.duckdns.org \
+ 'docker cp /tmp/evrika_print.pdf cups-splix:/tmp/evrika_print.pdf'
+# 3. Отправить в очередь (CUPS сам конвертит PDF→vnd.cups-postscript→pstoqpdl; gs+pdftops в контейнере есть)
+ssh -i ~/.ssh/id_rsa truenas_admin@mallexxx.duckdns.org \
+ 'docker exec cups-splix lp -d Samsung_CLX-216x_Series -o media=A4 /tmp/evrika_print.pdf'
+```
+
+**Проверка успеха:** `lpstat -W completed | grep ""` (искомый job появляется строкой `Samsung_CLX-216x_Series- ... <дата>`), очередь пуста (`lpstat -o`), принтер `idle … Rendering completed`. access_log контейнера: `Create-Job successful-ok` + `Send-Document successful-ok`.
+
+**Важно:** `lp -d file.pdf` сам создаёт job (`Create-Job`) — НЕ комбинировать с отдельным print-job-id, иначе `client-error-not-found`. PPD CLX-216x принимает `application/vnd.cups-postscript` (`*cupsFilter: 0 pstoqpdl`), поэтому чистый PDF конвертится автоматически. Job в `lpstat -W completed` показывает владельцем `root` (выполняется через docker по SSH) — это нормально.
+
+Полный шаги: залить → `docker cp` → `docker exec cups-splix lp` → проверить `completed` → убрать временные файлы (`/tmp/...` на хосте и внутри контейнера).
+
## ⚠️ ТОПОЛОГИЯ ИЗМЕНЕНА (2026-08-31) — TrueNAS больше НЕ в локальной сети
TrueNAS ушла из локальной сети и доступна **только** через публичный DNS `mallexxx.duckdns.org` (внешка `90.189.160.148`). Локальный `192.168.2.197` физически **недостижим** (пинг 100% потеря) — TrueNAS не в `192.168.2.x` и не в сети телефона.
From 150795ef4df231e48ac05e99ba0fb393fae5af80 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 16:48:41 +0600
Subject: [PATCH 79/81] [2026-09-02] eagle:
family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md
family/how-to/vault-git-sync.md
---
.../2026-09-02-hermes-taiga-crashloop.md | 100 ++++++++++++++++++
family/how-to/vault-git-sync.md | 4 +-
2 files changed, 103 insertions(+), 1 deletion(-)
create mode 100644 family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md
diff --git a/family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md b/family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md
new file mode 100644
index 00000000..6c685038
--- /dev/null
+++ b/family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md
@@ -0,0 +1,100 @@
+---
+tags: [truenas, hermes, taiga, crashloop, syncthing, vault-sync, incident]
+created: 2026-09-02
+updated: 2026-09-02
+status: open — root cause identified, fix NOT yet applied (ждёт Alex)
+---
+
+# Incident 2026-09-02: hermes-taiga crash-loop (16 339 рестартов) — Taiga git-sync стоит с авг, телефон видит устаревший vault
+
+**Родительские контексты:** [[2026-08-31-syncthing-truenas-incident]], [[2026-09-02-restore-privilege-scope]]. Здесь документируем свежую поломку: контейнер `hermes-taiga` упал в бесконечный restart-loop, из-за чего 3-phase Taiga git-sync **не выполняется**, и обе Taiga-копии vault (`/vault` и `/obsidian-syncthing`) отстали от bare repo **на месяц**.
+
+**Симптом (жалоба Alex, «syncthing state не совпадает с маком/гитом»):** состояние папки Syncthing для телефона не совпадает с Mac / с тем, что запушено в git.
+
+---
+
+## Диагноз по цепочке (проверено 2026-09-02)
+
+| Сторона | Состояние | HEAD |
+|---|---|---|
+| Mac `~/obsidian` | ✅ синхронен с bare repo (ahead=0/behind=0) | `2fab879` (2026-09-02) |
+| Bare repo `obsidian-vault.git` (истина) | ✅ источник | `2fab879` (2026-09-02) |
+| TrueNAS Syncthing P2P → телефон | ✅ работает (folder `k5vyy-gzdgj` «Obsidian Vault», up 2 days healthy) | — |
+| **Taiga `/storage/obsidian-syncthing`** | ⛔ **отстал на месяц** | `ceea848` (2026-08-01) |
+| **Taiga `/storage/obsidian`** | ⛔ **отстал на месяц** | `ceea848` (2026-08-01) |
+
+**Корень (проверено):** контейнер `hermes-taiga` в **crash-loop — `Restarts=16339`**, `StartedAt` постоянно сбрасывается. Логи: на каждом старте `uv` пытается докачать зависимости `pytz`, `markdown-it-py` с PyPI → **`client error (Connect) / operation timed out`** → `uv run` падает → `restart: unless-stopped` → без конца. `hermes` binary при этом даже не стартует (`docker exec ... hermes` → `executable file not found in $PATH` — потому что venv в /tmp пуст/недоустановлен).
+
+### Почему `uv` качает на каждом старте + почему тайм-аутит
+
+**Проблема 1 — маршрут через мёртвый прокси.** Активный compose `/mnt/RED_2TB/docker/hermes/docker-compose.yml` задаёт:
+```yaml
+environment:
+ - HTTP_PROXY=socks5://vless-proxy:1080
+ - HTTPS_PROXY=socks5://vless-proxy:1080
+ - UV_CACHE_DIR=/tmp/uv-cache
+ - NO_PROXY=portainer-mcp,homeassistant,localhost,127.0.0.1
+```
+Весь исходящий HTTP (вкл. PyPI для `uv`) идёт через `socks5://vless-proxy:1080`. А **outbound `vless-proxy` мёртв** (см. `truenas-infrastructure.md` шапка: `v.qentra.top` удалён, DNS остался в Cloudflare → трафик из прокси наружу не выходит). ⇒ `uv` не может скачать депы → timeout.
+
+**Проблема 2 — `UV_CACHE_DIR=/tmp/uv-cache`, а не впекённый venv.** Образ `hermes-taiga:latest` собран 2026-08-24 (`FROM node:20-slim`, entrypoint `["uv","run","hermes","gateway","run"]`, в Dockerfile при build идёт `uv pip install -e ".[all]"`). Но compose перенаправляет uv-кэш в `/tmp/uv-cache` (tmpfs контейнера), который **обнуляется на каждый рестарт** → при каждом старте `uv run` пере-синхронизирует депы → опять PyPI → опять timeout. Порочный круг. (Вместе с тем контейнер смонтирован с настоящей конфигой в `config/`, а деps НЕ должен перекачивать при здоровом образе/запуске.)
+
+### Контейнер hermes-taiga (для справки)
+
+- Image: `hermes-taiga:latest`, `container_name: hermes-taiga`, `restart: unless-stopped`
+- Entrypoint (в образе): `["uv","run","hermes","gateway","run"]`
+- WorkingDir: `/opt/hermes`
+- Mounts (compose активный):
+ - `/mnt/RED_2TB/storage/obsidian-syncthing -> /obsidian-syncthing:rw` (полная копия, phone)
+ - `/mnt/RED_2TB/storage/obsidian -> /vault:rw` (sparse)
+ - `/mnt/RED_2TB/storage/git/obsidian-vault.git -> /vault.git`
+ - `/mnt/RED_2TB/docker/hermes/config -> /opt/data`
+- Env: `HERMES_ALLOW_ROOT_GATEWAY=1`, `UV_CACHE_DIR=/tmp/uv-cache`, `HTTP_PROXY/HTTPS_PROXY=socks5://vless-proxy:1080`, `NO_PROXY=...`; `env_file: .env`
+- Сети: `taiga_net` (внутр) + `ha_net`(external `ha_default`); `depends_on: portainer-mcp (healthy)`
+
+> В `/mnt/RED_2TB/docker/hermes/` есть **второй, Новый Dockerfile** в `build/Dockerfile` (debian:13, multi-stage uv/gosu, `COPY .`, `uv pip install --no-cache-dir -e ".[all]"`, `chmod a+rX`, свой `/entrypoint.sh`, `HERMES_WEB_DIST`), а в корне — **Старый активный Dockerfile** (`node:20-slim`, клонирует upstream, entrypoint `uv run`). Активный compose ссылается на старый (`context: ., dockerfile: Dockerfile`). Это наследие расхождения build/каноне — кандидат на консолидацию, но осторожно.
+
+### Почему это ломает vault-sync
+
+Taiga 3-phase sync (`~/sync-vault.sh`, Hermes cron) запускается **внутри агента hermes-taiga**. Раз агент в crash-loop и `hermes` не стартует → cron не выполняется → **оба Taiga-клиента не тянут из bare repo с ~2026-08-01**. Телефон (Syncthing-копия `obsidian-syncthing`) продолжает синкаться P2P с тем, что там застряло (старое), а git-история Mac/bare repo ушла вперёд → рассинхрон, который видит Alex.
+
+**⚠️ Ситуация с `file:///vault.git` / bare repo:** bare repo в порядке (2fab879). Просто Taiga-клиенты не делают fetch/merge с августа. Это НЕ поломка прав (та уже решена 2026-09-02), а именно мёртвый агент.
+
+---
+
+## ЧТО ЕЩЁ НЕ ДЕЛАЛОСЬ (остаток — конец сессии)
+
+Диагностика остановилась на идентификации корня. **Фикс НЕ применён** (read-only проверка venv/прокси не прошла — подтверждение Alex истекло). Не сделано:
+1. Проверка полноты `.venv` в образе `hermes-taiga:latest` (throwaway-контейнер read-only): есть ли `pytz`/`markdown-it-py`/`hermes_cli`.
+2. Проверка живости `vless-proxy` изнутри (fetch через socks5).
+3. Выбор и применение фикса (см. ниже).
+
+---
+
+## Варианты фикса (к обсуждению, НЕ внедрено)
+
+**Ключевая развилка — связь Taiga с внешним миром.** Taiga (в отличие от Kraken) **нужна сеть наружу** (web, Telegram через API). Раз `vless-proxy` мёртв, вопрос: восстановить прокси-выход или дать Taiga прямой egress. На TrueNAS есть рабочий Xray-сервер на `vpn.mallexxx.duckdns.org:443` (`xray-admin`, client `user1` direct) — можно нацелить прокси Taiga на него. Подробно прокси-инфра: `family/how-to/truenas-infrastructure.md`.
+
+Непосредственно краш-луп чинить, чтобы агент поднялся:
+- **Убрать зависимость startup от PyPI:** сборка образа должна впекять полный venv и не делать `uv run`-reinstall на старте (перейти на улучшенный `build/Dockerfile` с `/entrypoint.sh`), И/ИЛИ убрать `/tmp/uv-cache` в пользу персистентного кэша на `config/` (bind mount `/opt/data`).
+- **Восстановить прокси:** обновить outbound `vless-proxy` на рабочий endpoint (`vpn.mallexxx.duckdns.org`, user1) или убрать `HTTP(S)_PROXY` если прокси не обязателен для Taiga (тогда `uv` сходит на PyPI напрямую с TrueNAS).
+
+После подъёма агента — **ручной прогон 3-phase sync** чтоб Taiga-копии догнать до bare repo и телефон получил актуальное состояние:
+```bash
+ssh truenas_admin@mallexxx.duckdns.org "bash ~/sync-vault.sh" # 3-phase (syncthing→vault→syncthing)
+```
+Проверка догона:
+```bash
+ssh truenas_admin@mallexxx.duckdns.org 'for d in /mnt/RED_2TB/storage/obsidian /mnt/RED_2TB/storage/obsidian-syncthing; do echo "== $d"; git -C "$d" rev-list --left-right --count main...origin/main; git -C "$d" log -1 --format="%h %ci %s"; done'
+```
+
+> ⚠️ В `obsidian-syncthing` есть **живые телефонные незакоммиченные изменения** (`.stversions/...`, `altai-build-checklist.md` и др.) — при ручном мерже вручную не затереть входящее с телефона (штатный 3-way merge в sync-vault-lib это учитывает).
+
+---
+
+## Связанные заметки
+- [[truenas-infrastructure]] — шапка: vless-proxy outbound мёртв; Hermes-Taiga раздел
+- [[vault-git-sync]] / [[obsidian-sync]] — архитектура sync и его сценарии
+- [[syncthing-truenas-android]] — syncthing-копия и её mount
+- [[2026-08-31-syncthing-truenas-incident]] — родитель: поломка прав syncthing-папки (другая, решена)
+- [[2026-09-02-restore-privilege-scope]] — родитель: поломка прав после restore (решена)
diff --git a/family/how-to/vault-git-sync.md b/family/how-to/vault-git-sync.md
index 7b70c0d2..cdb89533 100644
--- a/family/how-to/vault-git-sync.md
+++ b/family/how-to/vault-git-sync.md
@@ -12,7 +12,9 @@ tags:
# Vault Git Sync
-> **STATE 2026-09-02:** Eagle (мой `~/obsidian`) — git-sync **СТОИТ**: `main...nas/main [ahead 55]`. Причина — поломка прав после restore: `/mnt/RED_2TB/storage/git` (bare repo `obsidian-vault.git`) → `d--------- 921 921` (uid 921 = transmission). SSH к TrueNAS живой, но `git fetch`: `fatal: ... does not appear to be a git repository`. Fix: `chown 950:950` + `chmod u+rwX,g+rwX` на `storage/git`, затем `bash ~/scripts/sync-vault.sh`. Подробно: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`.
+> **STATE 2026-09-02 (позднее обновление):** Eagle/bare repo снова **синхронны** (ahead=0/behind=0, `2fab879`). Ранее (см. ниже) git-sync стоял из-за битых прав `storage/git` — **починено** полной dacl-заменой (`family/documents/vault-sync/2026-09-02-restore-privilege-scope.md` §RESOLVED).
+>
+> **⛔ НОВАЯ проблема 2026-09-02 (не решена):** оба **Taiga-клиента** (`/storage/obsidian` и `/storage/obsidian-syncthing`) **отстали на месяц** — HEAD `ceea848` (2026-08-01) vs bare repo `2fab879` (2026-09-02). Причина: контейнер `hermes-taiga` в **crash-loop** (16339 рестартов) — `uv run hermes gateway run` не может докачать депы (pytz/markdown-it-py) с PyPI, т.к. весь HTTP идёт через **мёртвый `socks5://vless-proxy:1080`**, а `UV_CACHE_DIR=/tmp/uv-cache` пустеет на каждом рестарте. Taiga 3-phase sync (крон внутри агента) не выполняется => телефон видит устаревший vault. Подробно: `family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md`. Фикс НЕ применён (ждёт Alex).
> **Триггер cron на маке (Eagle) — это launchd, НЕ Hermes cron:** `~/Library/LaunchAgents/com.sync-vault.plist` → `/Users/admin/scripts/sync-vault.sh`, `StartInterval=300` (5 мин). Скрипт `sync-vault-lib.sh` намеренно глушит dead remote (`git fetch` fail → `exit 0`), поэтому launchd показывает `last exit code = 0` даже при фактически стоящем sync. Системный crontab на маке пуст.
From 5ce43bbca6c32cec50c9d0d25bc97d582ac3228a Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 17:54:28 +0600
Subject: [PATCH 80/81] [2026-09-02] eagle:
personal/projects/personal-os/ai-agent-landscape-2026.md
---
.../personal-os/ai-agent-landscape-2026.md | 91 +++++++++++++++++++
1 file changed, 91 insertions(+)
create mode 100644 personal/projects/personal-os/ai-agent-landscape-2026.md
diff --git a/personal/projects/personal-os/ai-agent-landscape-2026.md b/personal/projects/personal-os/ai-agent-landscape-2026.md
new file mode 100644
index 00000000..f4dad50c
--- /dev/null
+++ b/personal/projects/personal-os/ai-agent-landscape-2026.md
@@ -0,0 +1,91 @@
+---
+tags:
+ - research
+ - agents
+ - landscape
+ - ai
+ - trends
+created: '2026-09-02'
+updated: '2026-09-02'
+related:
+ - personal-os-architecture
+ - hermes-agent-improvements
+ - agent-memory-architecture
+---
+# AI Agent Landscape — прогресс и тренды (сентябрь 2026)
+
+> Запрос Whale: «какой сейчас прогресс и что трендится в ИИ-агентских системах как замена текущему Hermes-конфигу».
+> Дата обзора: 2026-09-02. Ниша Whale = self-hosted, always-on персонал-агент (Hermes + Obsidian-MCP + cron + webhook + Zulip).
+
+## Главный вывод
+
+**Hermes Agent — не «устаревший конфиг», а один из двух доминирующих open-source рантаймов в своей нише** (личный self-hosted агент с memory/skills). Поле «замен» в этой категории очень узкое, и ближайший конкурент OpenClaw не является функциональной заменой, а скорее дополнением (или сценарием «полного переезда», невыгодным после вложенных кастомизаций). Плюс есть движок «пилотирования» / оркестратора сверху.
+
+## Тренды отрасли (2026)
+
+- **От демо к инфраструктуре**: 2026 = год, когда агенты перестали быть демо и стали инфраструктурой. Автономность измеряется «как долго агент работает до вмешательства человека» (Prosus State of AI Agents).
+- **Adoption gap 4–8×**: 88% orgs используют AI в ≥1 функции, но только 17–23% разворачивают агентов; ≤10% в каждой отдельной функции. Gartner: 40% агент-проектов отменят к 2027 (неясный ROI).
+- **Фреймворк-использование ≠ звёзды GitHub**: OpenClaw 345–373K★, Hermes ~110–160K★. Но по фактическому объёму токенов Hermes берёт верх (8.14 трлн cumulative, #1 на OpenRouter с ~апреля-мая 2026). Kейс «звёзды измеряют любопытство, токены — работу».
+- **Модельная универсальность = стандарт**: маршрутизация (дорогая модель на сложный reasoning, дешёвая — на фон) едва ли не главный рычаг экономии. Спред 100×+ (open-weight $0.07–0.12/1M против фронтира $15/1M).
+- **Память стала конкурентным преимуществом**: gap «есть/нет памяти» важнее gap между backbone-моделями. Три уровня (episodic/semantic/procedural) + proc-memory (навыки как процедурная память) — распространённая зрелая архитектура (ваша уже так построена).
+- **Governance/ROI/security — блокер масштабирования**: лучшие практики — least-privilege creds, approval на destructive-действия, аудит скиллов до установки.
+- **Регулирование**: EU AI Act high-risk требования вступают в силу с 2026-08-02. Прозрачность фондовых моделей падает (Stanford Transparency Index 40 vs 58 в 2025) → риск для procurement.
+
+## Матрица «замен» применительно к Whale-конфигу
+
+### 1. OpenClaw — НЕ замена, конкурент/гибрид
+- Gateway «control plane», первая-class интеграции с мессенджерами, 13.7K+ community-скиллов.
+- Слабее Hermes по: self-improving learning loop (у OpenClaw скиллы статичны/человеко-писаны), память кросс-сессионная слабее, нет checkpoint/rollback.
+- **Security-инциденты**: CVE-2026-25253 (RCE, CVSS 8.8, 40K+ инстансов раскрыты, 63% уязвимы на момент), 341 вредоносных скилла в ClawHub (supply-chain poisoning).
+- Миграция: у Hermes есть `hermes claw migrate`.
+- *Паттерн комьюнити* (~25% Reddit): **OpenClaw как оркестратор + Hermes как исполнитель** (через ACP/profiles).
+
+### 2. Кодинг-агенты (Claude Code, Cursor, OpenHands, Swarm…) — НЕ в этой нише
+- Если работа — в основном кодинг → релевантны, но способности OpenHands (72% SWE-Bench) к персональному always-on агенту отношения не имеют. Whale этим заниматься не должен.
+
+### 3. Узкие фреймворки (LangGraph, CrewAI, Microsoft Agent Framework, Google ADK, OpenAI Agents SDK, Mastra, AutoGen)
+- Это **продукт-ориентированные SDK для building apps**, НЕ self-hosted персональные рантаймы. Для Whale-сценария — не конкуренты. Полезны как справочник паттернов (оракестрация, human-in-the-loop, observability), но переписывать на них личную систему незачем.
+ - LangGraph: state control / durable execution лидирует (38M PyPI downloads/mo, v1.0).
+ - CrewAI: ~44–49K★, role-based teams, 82% task success, но pain points (async, отладка, память).
+ - Microsoft Agent Framework: наследник AutoGen+Semantic Kernel.
+ - OpenAI Agents SDK: «opinionated handoff», простые multi-agent.
+ - AutoGen/AG2: Microsoft перевёл в maintenance (заменён Agent Framework).
+
+## Куда движется поле — тренды, релевантные Whale
+
+### a) Длительный автономный ран (суверенность)
+- «Deep Agents»/harness для long-running задач (research/coding): планирование + context management. Прямой аналог широты ваших cron-agent'ов.
+- Валюта рынка = **continuous-time autonomy**; ставит вопрос о функциях типа pause/resume, отмена mid-flight у Whale агентов.
+
+### b) Пилотирование/проактивность
+- Трендовые личные агенты (rho «checks in on its own», Gaia с расписаниями) — эксперимент: агент сам инициирует. У вас уже есть cron-слой (по расписанию) и watchdogs; рынок двигается в сторону **условно-событийной** проактивности (не только по таймеру).
+- Дуальность «timer-driven vs event-driven automation» — зрелая тема в memory stacks (event-driven triggers: new source→ingest etc.). Частично покрыто вашей архитектурой, но выраженно event-driven (на изменения в vault, новые MCP-инструменты) — точка роста.
+
+### c) Память и контекст
+- **Summarization drift** и overflow правила (это у вас уже есть).
+- Confidence scoring + temporal decay (smart-aвторы памяти: Mem0/Letta/LangMem pattern) — ваш MEMORY.md overflow и curation похожи, но «система без явного scoring» может добавить confidence + decay для SEMANTIC.
+- Сторонние движки памяти (Letta/Mem0/Agno memory) — кандидаты на «апгрейд» semantic-tier позже, но сегодня у вас связка собственный MEMORY + SQLite already.
+
+### d) Observability / отладка агентов
+- LangSmith/её аналоги + «action trace против фактического исполнения» — известная боль CrewAI (traces не отражают реальное исполнение). У вас подход «facts over self-reports», verify handles — уже в правильную сторону. Формальный лог «намерение→факт» — вещь, которую рынок догоняет.
+
+### e) Физический/десктопный контур
+- Desktop app (Hermes v0.16 native desktop, June 2026) и iMessage-support (Photón/Agents, v0.17 June 2026) — движение к «агент в каждом приложении». Вы on macOS + webhook/Zulip — десктопный/локальный контур для вас опционален (Apple-скиллы уже есть: Notes/Reminders/FindMy).
+
+## Рекомендации (кратко)
+
+1. **Гонку «сменить фреймворк» не выигрывает никто из альтернатив** — текущий Hermes-конфиг уже implemented лучшие практики (3-tier memory, skills как proc-memory, SOUL.md, Observable vault-as-truth). Switching = решение проблем, которых у вас нет, ценой переписывания кастомизаций.
+2. **Присмотреться к OpenClaw** исключительно как к оркестратору на *дополнительных* каналах/аппах, если появится потребность «один агент во всех чатах» шире текущих (webhook/Zulip + локальные скиллы). Не как замена.
+3. **Усилить event-driven проактивность** (триггеры на изменение vault/новые файлы/сигналы) поверх timer-driven cron.
+4. **Security posture как у фронтира**: least-privilege, approval на деструктив, аудит любых устанавливаемых скиллов (урок из ClawHub poisoning + собственный CVE-опыт Hermes v0.16).
+5. **Модельная маршрутизация** — самый дешёвый рычаг снижения токен-расхода в личной системе в 2026.
+6. **Не полагаться на звёзды как на критерий** при сравнении — смотреть на устойчивый объём работы (у Hermes он вверху — #1 OpenRouter).
+
+## Источники
+- DEV: 10 Best Open-Source AI Agents 2026
+- Turing Post: Hermes Agent vs OpenClaw (2026-06-13)
+- HundredTabs: Hermes vs OpenClaw honest comparison (2026-05-10) — 1,300+ Reddit comments
+- Context Studios: OpenClaw vs Hermes 2026 (2026-06-22)
+- LangChain: best AI agent frameworks 2026 (2026-06-06)
+- DigitalApplied: State of AI Agents 2026 (220+ data points, 2026-05-22) — McKinsey/Stanford HAI/Gartner/IDC
+- openclaw/hermes GitHub (статистика на дату обзора)
From 68077310ba42a1c3072d806076092ad1b500dea7 Mon Sep 17 00:00:00 2001
From: Alexey Martemyanov
Date: Wed, 2 Sep 2026 17:59:32 +0600
Subject: [PATCH 81/81] [2026-09-02] eagle:
personal/projects/personal-os/hermes-agent-improvements.md
---
.../projects/personal-os/hermes-agent-improvements.md | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/personal/projects/personal-os/hermes-agent-improvements.md b/personal/projects/personal-os/hermes-agent-improvements.md
index bc02d4c2..dccc666a 100644
--- a/personal/projects/personal-os/hermes-agent-improvements.md
+++ b/personal/projects/personal-os/hermes-agent-improvements.md
@@ -60,6 +60,17 @@ Eagle использует custom agents — текущий подход.
**Fix:** cron 02:00 AM ежедневно — читает свежие сессии, обновляет `memory.md` и `personal/projects/`. Вписывается в слот между inbox-sort (01:00) и vault-enrichment (03:00 вс).
+### Бэклог из обзора рынка ИИ-агентов (2026-09-02)
+
+Полный разбор: [[ai-agent-landscape-2026]]. Вывод: смена фреймворка нецелесообразна (Hermes — #1 по объёму работы на OpenRouter в нише self-hosted перс.агента; альтернативы не перекрывают кастомизации). Из трендов, применимых к конфигу, растут следующие потенциальные улучшения (в порядке ценности):
+
+1. **Event-driven проактивность поверх timer-driven cron** — триггеры на изменение vault / новые файлы / сигналы, а не только по расписанию. Точка роста: сейчас проактивность только cron/watchdog.
+2. **Confidence scoring + temporal decay для semantic-tier памяти** — сейчас MEMORY.md overflow есть, но нет явного скоринга/устаревания фактов (паттерн Mem0/Letta/LangMem). Позже, кандидат на апгрейд semantic-tier сторонним движком (Letta/Mem0).
+3. **Аудит скиллов до установки** — урок из ClawHub supply-chain poisoning (341 вредоносный скилл) и CVE-2026-48710 (Hermes v0.16). Касается Skills Hub / установки сторонних скиллов.
+4. **Модельная маршрутизация** — самый дешёвый рычаг снижения токен-расхода (спред 100× между open-weight и фронтиром).
+
+Статус каждого: *не начато / потенциально* — это research-findings, не подтверждённый план. Пересмотреть при планировании следующего спринта улучшений.
+
---
## Вынос хардкода промптов в файлы