diff --git a/family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md b/family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md new file mode 100644 index 00000000..6c685038 --- /dev/null +++ b/family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md @@ -0,0 +1,100 @@ +--- +tags: [truenas, hermes, taiga, crashloop, syncthing, vault-sync, incident] +created: 2026-09-02 +updated: 2026-09-02 +status: open — root cause identified, fix NOT yet applied (ждёт Alex) +--- + +# Incident 2026-09-02: hermes-taiga crash-loop (16 339 рестартов) — Taiga git-sync стоит с авг, телефон видит устаревший vault + +**Родительские контексты:** [[2026-08-31-syncthing-truenas-incident]], [[2026-09-02-restore-privilege-scope]]. Здесь документируем свежую поломку: контейнер `hermes-taiga` упал в бесконечный restart-loop, из-за чего 3-phase Taiga git-sync **не выполняется**, и обе Taiga-копии vault (`/vault` и `/obsidian-syncthing`) отстали от bare repo **на месяц**. + +**Симптом (жалоба Alex, «syncthing state не совпадает с маком/гитом»):** состояние папки Syncthing для телефона не совпадает с Mac / с тем, что запушено в git. + +--- + +## Диагноз по цепочке (проверено 2026-09-02) + +| Сторона | Состояние | HEAD | +|---|---|---| +| Mac `~/obsidian` | ✅ синхронен с bare repo (ahead=0/behind=0) | `2fab879` (2026-09-02) | +| Bare repo `obsidian-vault.git` (истина) | ✅ источник | `2fab879` (2026-09-02) | +| TrueNAS Syncthing P2P → телефон | ✅ работает (folder `k5vyy-gzdgj` «Obsidian Vault», up 2 days healthy) | — | +| **Taiga `/storage/obsidian-syncthing`** | ⛔ **отстал на месяц** | `ceea848` (2026-08-01) | +| **Taiga `/storage/obsidian`** | ⛔ **отстал на месяц** | `ceea848` (2026-08-01) | + +**Корень (проверено):** контейнер `hermes-taiga` в **crash-loop — `Restarts=16339`**, `StartedAt` постоянно сбрасывается. Логи: на каждом старте `uv` пытается докачать зависимости `pytz`, `markdown-it-py` с PyPI → **`client error (Connect) / operation timed out`** → `uv run` падает → `restart: unless-stopped` → без конца. `hermes` binary при этом даже не стартует (`docker exec ... hermes` → `executable file not found in $PATH` — потому что venv в /tmp пуст/недоустановлен). + +### Почему `uv` качает на каждом старте + почему тайм-аутит + +**Проблема 1 — маршрут через мёртвый прокси.** Активный compose `/mnt/RED_2TB/docker/hermes/docker-compose.yml` задаёт: +```yaml +environment: + - HTTP_PROXY=socks5://vless-proxy:1080 + - HTTPS_PROXY=socks5://vless-proxy:1080 + - UV_CACHE_DIR=/tmp/uv-cache + - NO_PROXY=portainer-mcp,homeassistant,localhost,127.0.0.1 +``` +Весь исходящий HTTP (вкл. PyPI для `uv`) идёт через `socks5://vless-proxy:1080`. А **outbound `vless-proxy` мёртв** (см. `truenas-infrastructure.md` шапка: `v.qentra.top` удалён, DNS остался в Cloudflare → трафик из прокси наружу не выходит). ⇒ `uv` не может скачать депы → timeout. + +**Проблема 2 — `UV_CACHE_DIR=/tmp/uv-cache`, а не впекённый venv.** Образ `hermes-taiga:latest` собран 2026-08-24 (`FROM node:20-slim`, entrypoint `["uv","run","hermes","gateway","run"]`, в Dockerfile при build идёт `uv pip install -e ".[all]"`). Но compose перенаправляет uv-кэш в `/tmp/uv-cache` (tmpfs контейнера), который **обнуляется на каждый рестарт** → при каждом старте `uv run` пере-синхронизирует депы → опять PyPI → опять timeout. Порочный круг. (Вместе с тем контейнер смонтирован с настоящей конфигой в `config/`, а деps НЕ должен перекачивать при здоровом образе/запуске.) + +### Контейнер hermes-taiga (для справки) + +- Image: `hermes-taiga:latest`, `container_name: hermes-taiga`, `restart: unless-stopped` +- Entrypoint (в образе): `["uv","run","hermes","gateway","run"]` +- WorkingDir: `/opt/hermes` +- Mounts (compose активный): + - `/mnt/RED_2TB/storage/obsidian-syncthing -> /obsidian-syncthing:rw` (полная копия, phone) + - `/mnt/RED_2TB/storage/obsidian -> /vault:rw` (sparse) + - `/mnt/RED_2TB/storage/git/obsidian-vault.git -> /vault.git` + - `/mnt/RED_2TB/docker/hermes/config -> /opt/data` +- Env: `HERMES_ALLOW_ROOT_GATEWAY=1`, `UV_CACHE_DIR=/tmp/uv-cache`, `HTTP_PROXY/HTTPS_PROXY=socks5://vless-proxy:1080`, `NO_PROXY=...`; `env_file: .env` +- Сети: `taiga_net` (внутр) + `ha_net`(external `ha_default`); `depends_on: portainer-mcp (healthy)` + +> В `/mnt/RED_2TB/docker/hermes/` есть **второй, Новый Dockerfile** в `build/Dockerfile` (debian:13, multi-stage uv/gosu, `COPY .`, `uv pip install --no-cache-dir -e ".[all]"`, `chmod a+rX`, свой `/entrypoint.sh`, `HERMES_WEB_DIST`), а в корне — **Старый активный Dockerfile** (`node:20-slim`, клонирует upstream, entrypoint `uv run`). Активный compose ссылается на старый (`context: ., dockerfile: Dockerfile`). Это наследие расхождения build/каноне — кандидат на консолидацию, но осторожно. + +### Почему это ломает vault-sync + +Taiga 3-phase sync (`~/sync-vault.sh`, Hermes cron) запускается **внутри агента hermes-taiga**. Раз агент в crash-loop и `hermes` не стартует → cron не выполняется → **оба Taiga-клиента не тянут из bare repo с ~2026-08-01**. Телефон (Syncthing-копия `obsidian-syncthing`) продолжает синкаться P2P с тем, что там застряло (старое), а git-история Mac/bare repo ушла вперёд → рассинхрон, который видит Alex. + +**⚠️ Ситуация с `file:///vault.git` / bare repo:** bare repo в порядке (2fab879). Просто Taiga-клиенты не делают fetch/merge с августа. Это НЕ поломка прав (та уже решена 2026-09-02), а именно мёртвый агент. + +--- + +## ЧТО ЕЩЁ НЕ ДЕЛАЛОСЬ (остаток — конец сессии) + +Диагностика остановилась на идентификации корня. **Фикс НЕ применён** (read-only проверка venv/прокси не прошла — подтверждение Alex истекло). Не сделано: +1. Проверка полноты `.venv` в образе `hermes-taiga:latest` (throwaway-контейнер read-only): есть ли `pytz`/`markdown-it-py`/`hermes_cli`. +2. Проверка живости `vless-proxy` изнутри (fetch через socks5). +3. Выбор и применение фикса (см. ниже). + +--- + +## Варианты фикса (к обсуждению, НЕ внедрено) + +**Ключевая развилка — связь Taiga с внешним миром.** Taiga (в отличие от Kraken) **нужна сеть наружу** (web, Telegram через API). Раз `vless-proxy` мёртв, вопрос: восстановить прокси-выход или дать Taiga прямой egress. На TrueNAS есть рабочий Xray-сервер на `vpn.mallexxx.duckdns.org:443` (`xray-admin`, client `user1` direct) — можно нацелить прокси Taiga на него. Подробно прокси-инфра: `family/how-to/truenas-infrastructure.md`. + +Непосредственно краш-луп чинить, чтобы агент поднялся: +- **Убрать зависимость startup от PyPI:** сборка образа должна впекять полный venv и не делать `uv run`-reinstall на старте (перейти на улучшенный `build/Dockerfile` с `/entrypoint.sh`), И/ИЛИ убрать `/tmp/uv-cache` в пользу персистентного кэша на `config/` (bind mount `/opt/data`). +- **Восстановить прокси:** обновить outbound `vless-proxy` на рабочий endpoint (`vpn.mallexxx.duckdns.org`, user1) или убрать `HTTP(S)_PROXY` если прокси не обязателен для Taiga (тогда `uv` сходит на PyPI напрямую с TrueNAS). + +После подъёма агента — **ручной прогон 3-phase sync** чтоб Taiga-копии догнать до bare repo и телефон получил актуальное состояние: +```bash +ssh truenas_admin@mallexxx.duckdns.org "bash ~/sync-vault.sh" # 3-phase (syncthing→vault→syncthing) +``` +Проверка догона: +```bash +ssh truenas_admin@mallexxx.duckdns.org 'for d in /mnt/RED_2TB/storage/obsidian /mnt/RED_2TB/storage/obsidian-syncthing; do echo "== $d"; git -C "$d" rev-list --left-right --count main...origin/main; git -C "$d" log -1 --format="%h %ci %s"; done' +``` + +> ⚠️ В `obsidian-syncthing` есть **живые телефонные незакоммиченные изменения** (`.stversions/...`, `altai-build-checklist.md` и др.) — при ручном мерже вручную не затереть входящее с телефона (штатный 3-way merge в sync-vault-lib это учитывает). + +--- + +## Связанные заметки +- [[truenas-infrastructure]] — шапка: vless-proxy outbound мёртв; Hermes-Taiga раздел +- [[vault-git-sync]] / [[obsidian-sync]] — архитектура sync и его сценарии +- [[syncthing-truenas-android]] — syncthing-копия и её mount +- [[2026-08-31-syncthing-truenas-incident]] — родитель: поломка прав syncthing-папки (другая, решена) +- [[2026-09-02-restore-privilege-scope]] — родитель: поломка прав после restore (решена) diff --git a/family/how-to/vault-git-sync.md b/family/how-to/vault-git-sync.md index 7b70c0d2..cdb89533 100644 --- a/family/how-to/vault-git-sync.md +++ b/family/how-to/vault-git-sync.md @@ -12,7 +12,9 @@ tags: # Vault Git Sync -> **STATE 2026-09-02:** Eagle (мой `~/obsidian`) — git-sync **СТОИТ**: `main...nas/main [ahead 55]`. Причина — поломка прав после restore: `/mnt/RED_2TB/storage/git` (bare repo `obsidian-vault.git`) → `d--------- 921 921` (uid 921 = transmission). SSH к TrueNAS живой, но `git fetch`: `fatal: ... does not appear to be a git repository`. Fix: `chown 950:950` + `chmod u+rwX,g+rwX` на `storage/git`, затем `bash ~/scripts/sync-vault.sh`. Подробно: `family/documents/vault-sync/2026-09-02-restore-privilege-scope.md`. +> **STATE 2026-09-02 (позднее обновление):** Eagle/bare repo снова **синхронны** (ahead=0/behind=0, `2fab879`). Ранее (см. ниже) git-sync стоял из-за битых прав `storage/git` — **починено** полной dacl-заменой (`family/documents/vault-sync/2026-09-02-restore-privilege-scope.md` §RESOLVED). +> +> **⛔ НОВАЯ проблема 2026-09-02 (не решена):** оба **Taiga-клиента** (`/storage/obsidian` и `/storage/obsidian-syncthing`) **отстали на месяц** — HEAD `ceea848` (2026-08-01) vs bare repo `2fab879` (2026-09-02). Причина: контейнер `hermes-taiga` в **crash-loop** (16339 рестартов) — `uv run hermes gateway run` не может докачать депы (pytz/markdown-it-py) с PyPI, т.к. весь HTTP идёт через **мёртвый `socks5://vless-proxy:1080`**, а `UV_CACHE_DIR=/tmp/uv-cache` пустеет на каждом рестарте. Taiga 3-phase sync (крон внутри агента) не выполняется => телефон видит устаревший vault. Подробно: `family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md`. Фикс НЕ применён (ждёт Alex). > **Триггер cron на маке (Eagle) — это launchd, НЕ Hermes cron:** `~/Library/LaunchAgents/com.sync-vault.plist` → `/Users/admin/scripts/sync-vault.sh`, `StartInterval=300` (5 мин). Скрипт `sync-vault-lib.sh` намеренно глушит dead remote (`git fetch` fail → `exit 0`), поэтому launchd показывает `last exit code = 0` даже при фактически стоящем sync. Системный crontab на маке пуст.