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/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..b37cefff --- /dev/null +++ b/family/documents/vault-sync/2026-08-31-syncthing-truenas-incident.md @@ -0,0 +1,93 @@ +--- +tags: [syncthing, truenas, debug, incident] +created: 2026-08-31 +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 папка не синкается / процентов нет. + +## Краткий вывод + +**Соединение НЕ сломано.** И контейнер на 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. + +## Резолюция 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` + +## Техническая карта 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]] — основная документация по этой схеме 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/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..e447379b --- /dev/null +++ b/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md @@ -0,0 +1,312 @@ +--- +tags: [truenas, restore, incident, git, media, arr, permission] +created: 2026-09-02 +updated: 2026-09-02 +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 — масштаб и влияния + +**Сводный инцидент-документ.** Родитель: `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-файла на пути нет. Не связано с поломкой прав; ручной фикс только по запросу. + +--- + +## Общий план (ИСПРАВЛЕН — актуальный рабочий статус в §5 ниже; здесь технические применения) + +> **Практическое уточнение 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 (что именно сломано) + +Реальные 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 +# 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} +``` + +### Применение (скрипт готов на маке: `~/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. + +--- + +## 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 ВЫПОЛНЕНА (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 под 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---`). ✅ + - ✅ **Второй 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 догнаны вторым проходом. + +### ✅ RESOLVED 2026-09-02: Блокер push снят — полной dacl-заменой, НЕ stripacl + +**Симптом (после починки прав):** на маке 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`. + +**Фикс РАБОЧИЙ (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 +# точечно на один объект (проверка метода): +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}}' + +# затем рекурсивно на весь репозиторий (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}}' +``` +**Ключевое правило 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--` в обычном случае); на полке после поломки. + +### Диагностические 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 +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 по решению. + +### ⚠️ Текущий статус после вторго прохода + RESOLUTION (актуально на 2026-09-02) +- ✅ **Push на маке ПОЧИНЕН** — NFS4 ACL `DENY` на ~1946 loose git-объектах сняты через **полную dacl-замену** (не stripacl). См. секцию «✅ RESOLVED … dacl-заменой». Git-sync работает: fetch+push проходят, ahead=0 behind=0. +- ✅ **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). + +### Точное содержание 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`) + +## 6. Пост-запуск arr-стека: 2 блокера — ОБА РЕШЕНЫ (2026-09-02, конец сессии) + +После успешного запуска стека (все контейнеры 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 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 950:950 /mnt/RED_2TB/storage +chmod 750 /mnt/RED_2TB/storage # rwxr-x--- → owner u950 / group(g950=truenas_admin) r-x → traverse для jellyfin +``` +> 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" — РЕШЕНО `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`. Данные при этом НЕ трогаются. + +--- + +## Связанные заметки +- [[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..96703f1e 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,22 @@ related: 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 нативно). **НЕ ап-линия 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». +> 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`. + +> **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). **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) - Config and pitfalls recorded during initial Taiga arr stack deployment @@ -29,3 +45,71 @@ 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.) + +## Плейбек по сети → per-TCP-flow/BDP-ограничение при хай-РТТ (диагноз 2026-09-02, ПЕРЕСМОТРЕН конец сессии) + +**Симптом:** jellyfin на TrueNAS работает, файлы проигрываются, но на ТВ high-bitrate (720p BluRay и выше) *тормозят / грузятся кусками*, потом подтормаживают на динамике. + +**Вердикт — НЕ транскодинг и НЕ ап-линия 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 даже при direct-play может ремуксовать в **HLS-сегменты по 6 сек** (`-codec:v:0 copy ... -f hls` — копия, не перекодирование). «Грузка кусками» = HLS-сегменты добираются по сети одним TCP-потоком. + +**Сетевая топология (два дома, доступ только через внешку):** + +| | Сеть | Шлюз | Внешний 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 идёт целиком по интернету между двумя домами через межоператорский транзит. + +**Замеры (воспроизводимы) — САМОЕ ВАЖНОЕ: замер с самого NAS, а не от Мака.** +> Ранняя гипотеза «~28 Мбит = ап-линия TrueNAS ~30 Мбит» была ОШИБОЧНОЙ: 28 Мбит — это потолок ОДНОГО TCP-потока от Мака по пути с 104мс RTT, а НЕ лимит линии TrueNAS. Проверено замером С TrueNAS до Cloudflare: +```bash +# На самом 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. +``` + +**Где реальная пропускная просадка — per-TCP-flow/BDP при высоком RTT** (не жёсткий лимит). Ключевой тест — агрегат растёт с параллельностью: +```bash +# 1 поток: ~28 Мбит/с +scp -q truenas_admin@mallexxx.duckdns.org:/tmp/dis.bin /tmp/dis.bin + +# 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мс физику не уберёт. **⚠ 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) реально маловат для нужного контента; как первичный вариант отвергнут. + +> **Перекрёстно:** это исправление ранней ошибочной записи «потолок ~28 = ап-линия TrueNAS» (см. history этого файла до 2026-09-02). Если будущая сессия увидит противоречие — истина здесь: NAS-линк 184/117 Мбит, проблема per-flow/BDP при высоком RTT. + 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/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/kraken-access.md b/family/how-to/kraken-access.md index 2c897695..ec1c26a3 100644 --- a/family/how-to/kraken-access.md +++ b/family/how-to/kraken-access.md @@ -1,15 +1,19 @@ # Kraken — Внешний доступ -> Обновлено: 2026-06-24 +> Обновлено: 2026-09-01 + +> Updated: 2026-09-01 23:00 — диск/докер кракена восстановились после перезагрузки. ## Как зайти Везде `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**. Альтернатива внешнего доступа обсуждается через Xray reverse (см. [[tech/xray-reverse-tunnel-kraken-truenas]]). -WireGuard split-tunnel: Eagle (10.99.0.2) ↔ VPS ↔ Kraken (10.99.1.2). Подробнее: [[wireguard-vpn]]. +> ⚠️ **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`. + +> ⚠️ **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 (локально) @@ -26,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/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 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/red2tb-dataset-map.md b/family/how-to/red2tb-dataset-map.md new file mode 100644 index 00000000..8d597fcd --- /dev/null +++ b/family/how-to/red2tb-dataset-map.md @@ -0,0 +1,72 @@ +# RED_2TB — карта датасетов и назначений (для решения о пересоздании пула) + +> Собрано: 2026-08-20 (Ель — Кит). Источник: live-данные `zfs list` + `ls` с TrueNAS (readonly-импорт). +> НАЗНАЧЕНИЕ: решить, воссоздавать ли карту датасетов как есть, или что-то поменять/добавить/убрать перед пересозданием пула RED_2TB. +> Ничего не изменялось — только сбор информации. + +## Статус спасения на 2026-08-21 + +- **Выгрузка ЗАВЕРШЕНА УСПЕШНО** (подтверждено 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»). + +## Общая сводка + +- Пул: 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. +- **РЕШЕНО (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`. + +## Ссылки + +- Связанные: [[truenas-sata-ports-and-zfs-pools]] (план спасения/пересоздания), [[truenas-access]] (общие данные доступа). diff --git a/family/how-to/samsung.md b/family/how-to/samsung.md index 24819f98..1a33f809 100644 --- a/family/how-to/samsung.md +++ b/family/how-to/samsung.md @@ -2,15 +2,35 @@ 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) + +Симптом: с телефона не виден принтер. + +### ✅ Корень проблемы (подтверждено SSH на TrueNAS) + +**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 проверить нельзя. ## Отключение Detecting Device 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/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 e05d5a98..a8745eb8 100644 --- a/family/how-to/truenas-infrastructure.md +++ b/family/how-to/truenas-infrastructure.md @@ -1,6 +1,36 @@ # TrueNAS — инфраструктура -> Обновлено: 2026-07-06 +> Обновлено: 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`. ## Доступ @@ -12,6 +42,14 @@ - ⚠️ 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:** +**⚠️ Измеренный потолок канала 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. + ## Пользователи и группы (ключевые) | uid | Имя | gid | Назначение | @@ -91,12 +129,60 @@ 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 завершён) + +**Статус:** контейнер работает, принтер `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:** `/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) @@ -115,6 +201,64 @@ TrueNAS использует **NFSv4 ACL**, а не стандартный POSIX | 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`; 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-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` снаружи открыт. +- **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` — клиент падает. Это частая причина «не подключается». + +**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) + +**Цель:** 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 работает 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 — детали @@ -144,24 +288,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 @@ -310,6 +479,50 @@ 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` (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]]. + +> ✅ **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/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/family/how-to/truenas-sata-ports-and-zfs-pools.md b/family/how-to/truenas-sata-ports-and-zfs-pools.md index 9881b059..afaca945 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,60 @@ # TrueNAS — SATA порты и пулы +<<<<<<< HEAD > Обновлено: 2026-08-17 ## ✅ РЕШЕНО: HGST 12TB завёлся — проблема была в 3.3V PWDIS, НЕ в карте +======= +> Обновлено: 2026-08-21 (данные спасены, пул RED_2TB OFFLINE, готовность к пересозданию). СТАТУС: выгрузка данных на IronWolf **завершена успешно** (4.56T на `/mnt/IRONWOLF`, подтверждено Alex). Пул RED_2TB **OFFLINE** (не импортирован), диски физически на месте. Идёт подготовка к пересозданию пула с новой топологией (добавляем WD2TB#2 зеркалом к WD2TB#1). HGST сломан (см. ниже). + +## ✅ РЕШЕНО (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** (см. блок ниже). + +**ТЕКУЩИЙ БЛОКЕР (конец сессии):** +- `zpool import`/`mount` требуют **root** (`some devices require root privileges`). `truenas_admin` НЕ root (sudo просит пароль). Все операции с пулом — только под root. +- **Нужно добыть root:** SSH → включить root login (`PermitRootLogin`), или выполнять команды через WebUI-админ/консоль. +- **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 УРОНЕН НА ПОЛ — вероятный слом после падения + +**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-диске. + +**Состояние (на 2026-08-17):** **подтверждён аппаратный отказ — диск не ремонтопригоден.** Симптом: ритмичный «click-click, click-click» при работающем моторе = головки не могут выйти на дорожку/парковку (классический click of death после удара). Это механическое повреждение, НЕ лечится программно (SMART/прошивка), чинить может только профи-recovery и это дороже нового диска. **Данных на нём НЕТ** (не был добавлен в пул, только новая разметка sdf1/sdf2) — поэтому списан без recovery. Закрыто. + +> ⚠️ Урок для будущего: enterprise-диск, уроненный на пол работающим — почти всегда аппаратная смерть (мотор/головки). Если на диске критичные данные — только профи-recovery, и шанс не 100%. + +> 🔁 ИСТОРИЯ ВОПРОСА: до падения диск был ПОЛНОСТЬЮ рабочим. Причина исходного «молчания» по SATA — PWDIS (см. ниже). После снятия 3.3V завёлся и линковался. Затем урон на пол — аппаратный отказ (click of death). + +--- + +## ✅ РЕШЕНО (исторически): HGST 12TB заводился — проблема была в 3.3V PWDIS, НЕ в карте +>>>>>>> origin/main **2026-08-17.** HGST **линуется и полностью работает** теперь. Ключевое изменение — **отключена/обезврежена 3.3V линия (PWDIS-контакт №3 на SATA-разъёме питания)**. Диск: - определяется как `/dev/sde`, `HGST HUH721212ALE600`, серийник `8CKD08RE`, **10.9T**, секторов `23437770752` (штатная ёмкость 12TB по SFF-8447) @@ -16,11 +68,16 @@ **ASUS P8H77-V LE**, чипсет Intel H77. 6 SATA портов на Intel контроллере (порт от ASMedia отсутствует). +<<<<<<< HEAD ### Распределение портов (текущее состояние — после перетыкания 2026-08-17) +======= +### Распределение портов (актуально на конец 2026-08-17, после финального перетыкания) +>>>>>>> origin/main | Порт (ata) | Тип | Скорость | Цвет | Диск | by-id | |------------|-----|----------|------|------|-------| | ata1 (SATA6G_1) | SATA 3 | 6 Gb/s | серый | **sda** — WD 2TB (WD20EFAX) | WD-WXH2A31D5HDJ | +<<<<<<< HEAD | 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 | @@ -34,6 +91,24 @@ - Несмотря на перетыкание пулы **здоровы** (см. раздел про пулы ниже) — подтверждено, что ZFS не зависит от порта. > ⚠️ В отличие от изначального плана, диски сейчас не по плану — проверять актуальное распределение по `by-id`/модели, а не по буквам. Типичные буквы при стабильном подключении: sda=WD2TB#1, sdb=WD2TB#2, sdc=WD4TB, sdd=Kingston, sde=Seagate4TB. +======= +| 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 | синий | *(свободен/не иден-тиф.)* | — | +| ata6 (SATA3G_4) | SATA 2 | 3 Gb/s | синий | **sde** — Seagate 4TB (ST4000DM004) | WFN66CM2 | +| *(карта ASM1166)* | — | — | — | **НЕ В СЛОТЕ** (вытащена) | — | + +**⚠️ Фактическое положение на конец сессии (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/модели, не полагаться на буквы.** + +> ⚠️ ⚡ **СРОЧНО:** пул `RED_2TB` после этого перетыкания НЕ заимпортирован (см. блок «КРИТИЧНО» в разделе пулов). Все члены на месте — нужен Import Pool. +>>>>>>> origin/main ## Плата расширения SATA в PCIe (установлена ~2026-07-30) @@ -63,7 +138,11 @@ ## ✅ Решение HGST 12TB (2026-08-17) — корень найден, диск работает +<<<<<<< HEAD > ⚠️ **Актуальный статус на 2026-08-17 вечер:** хоть HGST и завёлся (см. ниже КАК), в финальном перетыкании **карта ASM1166 вытащена из слота, HGST и IronWolf сейчас НЕ подключены** (подробнее в таблице портов выше). Этот блок фиксирует **как была решена причина не-линка** — пригодится, когда диск вернётся в систему. +======= +> ⚠️ **Актуальный статус на 2026-08-17 ночь:** HGST **УРОНЕН НА ПОЛ и сломан** (см. блок КРИТИЧНО в самом верху). Карта ASM1166 вытащена из слота, IronWolf подключён на материнку. Этот блок фиксирует **как изначально решалась причина не-линка** (PWDIS) — полезно для понимания, но теперь диск вышел из строя механически. +>>>>>>> origin/main Проблема не-линка HGST по SATA **решена: это 3.3V PWDIS на SATA-питании.** Разметка не трогалась — sde1=200M, sde2=10.9TB остались как были. Когда диск снова будет подключён — добавить в пул TrueNAS по by-id/GUID (ZFS к порту не привязана). После добавления — удалить этот блок. @@ -102,20 +181,192 @@ lspci -k | grep -i sata ## Важно про ZFS и перетыкание - ZFS не привязана к букве диска или SATA порту. TrueNAS распознаёт пул по GUID на дисках. +<<<<<<< HEAD - Можно перетыкать диски в любые порты — TrueNAS поднимет пул автоматически (подтверждено 2026-08-17). - Если пул не поднялся автоматически: WebUI → Storage → Import Pool. +======= +- **Перетыкание НЕ всегда поднимает пул автоматически** (2026-08-17: RED_2TB не заимпортировался сам после перетыкания, см. «КРИТИЧНО» ниже). Если пул не виден в `zpool list` — нужен ручной Import (WebUI Storage → Import Pool или `zpool import ` под root). Данные при этом целы, пока все члены `ONLINE`. +>>>>>>> origin/main - Проверка пула после перетыкания: `/sbin/zpool status ` — убедиться что все члены `ONLINE`, ошибки `0 0 0`. ## Пулы TrueNAS (проверено 2026-08-17) Имена пулов, топология и члены — проверять статус через `/sbin/zpool` (см. ниже про PATH). +<<<<<<< HEAD | Пул | Размер | Члены (по /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 без зеркала. +======= +| Пул | Размер | Члены (по `/sbin/zpool status` от 2026-08-17) | Топология | Монтирование | +|-----|--------|-----------|-----------|--------------| +| **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** по `zpool status` (readonly-импорт не паникует), scrub «завис» на ~72% (при readonly не доходит — но это НЕ фикс, см. ниже). Все ошибки 0 0 0 — **данные целы**. Один vdev (`WD2TB#1`) simplex — без зеркала; WD2TB#1 сейчас физически отключён. + +### ✅ ПРОГРЕСС (2026-08-17 глубокая ночь): READONLY-импорт УСПЕШЕН, паника преодолена + +**Рабочий рецепт получения доступа к данным подтверждён на практике.** Члены пула физически на месте: `sda2`=WD2TB#1 (ata1), `sdb2`=WD4TB (ata2, буква сменилась), `sde2`=Seagate4TB (ata6). + +**Что сработало (пошагово):** +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 + zpool import -o readonly=on RED_2TB + ``` + → **`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 (после обхода паники). **Точки монтирования не создаются.** + +**СЛЕДУЮЩИЙ ШАГ (не завершён):** смонтировать 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 +``` + +**Критичные ограничения (выяснены по ходу):** +- `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` (идеально байт-в-байт со снимками, но требует пула-приёмника и снимка). + +> ⚠️ После успешного монтирования и выгрузки — обновить таблицу членов пула и закрыть этот блок. + +### 🆕 ПРОГРЕСС (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 `): валидна, но ТОЛЬКО после успешного импорта пула и его стабилизации. Не пытаться до восстановления. + +### ✅ ПЕРЕСОЗДАНИЕ ПУЛА (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. +- `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. + +### ✅ 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) — решено ранее. + +### ⚠️⚠️ АКТИВНАЯ ПРОБЛЕМА №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) безопасен, но не запускает пул. + + + +>>>>>>> origin/main - Идёт регулярный scrub (запускается ночью воскресенья). - **`zpool` не в PATH для zsh truenas_admin** — вызывать через абсолютный путь `/sbin/zpool` или `/usr/sbin/zpool` (иначе `command not found`). diff --git a/family/how-to/vault-git-sync.md b/family/how-to/vault-git-sync.md index 340a7822..cdb89533 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,12 @@ tags: # Vault Git Sync +> **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 на маке пуст. + 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` @@ -50,10 +56,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. @@ -121,6 +133,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 diff --git a/family/how-to/vps-qentra.md b/family/how-to/vps-qentra.md index 4bb2e18c..676b749b 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, ЧАСТИЧНО внедрён: 3x-ui на TrueNAS развёрнут 2026-09-01, reverse-клиент на Kraken ещё нет). + +**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/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/reverse-xray-3xui-kraken.md b/family/plans/reverse-xray-3xui-kraken.md new file mode 100644 index 00000000..28105fb3 --- /dev/null +++ b/family/plans/reverse-xray-3xui-kraken.md @@ -0,0 +1,281 @@ +# 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. + +## 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 и работает:** +- Контейнер `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). + +### Фактически развёрнутый 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 развёрнут (см. ниже). Основной doc архитектуры: `personal/tech/xray-reverse-tunnel-kraken-truenas.md`. + +## 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`. + +### ✅ ВНЕДРЕНО (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. + +### 🔑 КОРЕНЬ ПРОБЛЕМЫ НАЙДЕН (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"` (есть). + +### ✅ ФИКС ПРИМЕНЁН, но заблокирован (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`). +- Итог: диск появился, 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) +- конфиг `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. ✅ Создан 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) — **причина найдена (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. +- Не ломать существующий Caddy/домены (бэкапить Caddyfile, перезапуск Caddy аккуратно). +- `vless-proxy` (Hermes-Taiga) не трогать — настроен под local SOCKS. +- Внешний Xray-порт: через Caddy по пути `/vless` (WS). Для полноценного reverse возможно потребуется отдельный VLESS+Reality inbound на отдельном порту + проброс на роутере для Kraken — ДОБАВИТЬ после проверки связности панели. diff --git a/personal/business/rf-tax-residency.md b/personal/business/rf-tax-residency.md new file mode 100644 index 00000000..ec9983bd --- /dev/null +++ b/personal/business/rf-tax-residency.md @@ -0,0 +1,73 @@ +--- +created: '2026-08-24' +updated: '2026-08-24' +tags: + - tax + - russia + - residency +--- +# Налоговая резиденция РФ — статус и план + +## Контекст + +- ИП в Кыргызстане, доход от иностранных IT-клиентов +- Живу в Бишкеке, гражданин РФ +- Цель: **не становиться налоговым резидентом РФ** (порог — 183 дня в любые 12 последовательных месяцев) +- Пока нерезидент — доход от КР-ИП РФ не касается + +--- + +## Въезды/выезды в РФ + +| Период | Дней | +| ----------------------- | ------- | +| 29.11.2025 → 14.03.2026 | 106 | +| 04.07.2026 → 12.09.2026 | 71 | +| 29.11.2026 → ? | считаем | + +--- + +## Подсчёт скользящего окна + +### На дату приезда 29.11.2026 + +Окно 29.11.2025 → 28.11.2026: +- 29.11.25 → 14.03.26 = **106 дней** +- 04.07.26 → 30.08.26 = **58 дней** +- **Итого: 164 дней** (запас: **18 дней** до 183) +- 06.09.26 → 23.09.26 = 18 + +### Динамика окна при пребывании с 29.11.2026 + +- **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 + +**Выехать не позднее 20.03.2027** + +--- + +## Риски и что отслеживать + +- ФНС планирует автоматическое определение резидентства по загранпаспорту (данные о въездах/выездах) — статус инициативы неясен +- Для стран ЕАЭС (КР, Армения, Казахстан) граница по внутреннему паспорту не фиксируется автоматически — но это может измениться +- При смешанном резидентстве (РФ + КР одновременно по 183+ дней) применяется СИДН РФ–КР (соглашение от 13.01.1999) + +--- + +## СИДН РФ–КР (на случай если всё же стану резидентом РФ) + +1. Получить **сертификат налогового резидентства КР** (ГНС Кыргызстана) +2. Подать **3-НДФЛ** в РФ, указать доход от КР-ИП, сослаться на СИДН (ст. 7 — предпринимательская деятельность) +3. Приложить: сертификат резидентства КР, квитанции об уплате налога в КР, контракты +4. Налог уплаченный в КР засчитывается — доплачивается только разница (КР ~4–6%, РФ 13%) + +--- + +## Вывод / план + +- **Сейчас:** нерезидент РФ, всё чисто +- **Поездка 29.11.2026:** можно сидеть до **20.03.2027** — счётчик нейтрален пока выпадают дни зимы 2025/26 (были в РФ), с 15.03.27 начинает расти +- **Принцип:** следить за датами, особенно после длинного лета в РФ — запас резко сокращается diff --git a/personal/plans/ohotnichy-bilety-interaktiv.md b/personal/plans/ohotnichy-bilety-interaktiv.md new file mode 100644 index 00000000..62b5f691 --- /dev/null +++ b/personal/plans/ohotnichy-bilety-interaktiv.md @@ -0,0 +1,80 @@ +# Интерактивные билеты охотника (a1-tir.ru/bilety) + +Проект: скачать страницу билетов для экзамена на гражданское оружие и превратить в интерактивный тренажёр. + +**Возможности:** номер правильного ответа скрыт · ответы кликабельны · выбранный неправильный → красный, правильный → зелёный. + +**Файлы (исходники в `~/Downloads/`):** +- `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/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`. + +--- + +## Источник и структура (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/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 (статистика на дату обзора) diff --git a/personal/projects/personal-os/eagle-dashboard.md b/personal/projects/personal-os/eagle-dashboard.md index 2102dece..0cb62bbf 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 источников: @@ -299,6 +314,7 @@ When a launchd cron is created and `launchctl print gui//