Files
obsidian-vault/family/how-to/truenas-infrastructure.md.bak-per-user-20260902-1229

512 lines
46 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<app>`.
> **Контейнеры запускались через `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 <path>`
- Добавить запись: `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/<app>/`. Модель — **не один общий 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/<room>/...`), а также пишет в 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 <c> --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
<style>
@media (prefers-color-scheme: dark) {
path, line, polyline, polygon, rect, circle, ellipse { stroke: white; }
}
</style>
```
> Ж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)