--- tags: [truenas, restore, incident, git, media, arr, permission] created: 2026-09-02 updated: 2026-09-02 status: in-progress (Этап 1 done — оба compose на PUID=950 правлены; Этап 2 заблокирован: transmission без compose-лейблов, нужен rm -f — ждёт ок Alex) --- # 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-файла на пути нет. Не связано с поломкой прав; ручной фикс только по запросу. --- ## Общий план (ИСПРАВЛЕН — НЕ выполнен, требует подтверждения) > ⚠️ **2026-09-02 УТОЧНЕНО: `/mnt/RED_2TB/storage` — единый ZFS dataset `RED_2TB/storage` с `acltype=nfsv4`. НЕ ПОСIX.** Ранее планировалось `chown -R`/`chmod -R` — это **НЕВЕРНО** для TrueNAS SCALE с NFSv4 ACL. Права управляются **только** через `midclt call filesystem.setacl` (эквивалент TrueNAS UI → Datasets → `RED_2TB/storage` → Edit ACL → Apply recursively). `truenas_admin` = FULL_ADMIN в midclt → **root/sudo для setacl НЕ нужен** (проверено: `midclt call auth.me` → roles `FULL_ADMIN`). ### Диагноз по NFS4 ACL (что именно сломано) Реальные ACL через `midclt call filesystem.getacl` у `storage`, `git`, `Downloads`, `Movies`, `Cartoons` **структурно ИДЕНТИЧНЫ рабочему `obsidian`**, но: 1. владелец **uid/gid = 921** (`transmission`) вместо правильного **950** (`truenas_admin`); 2. у записи `owner@` **обнулены `READ_DATA / WRITE_DATA / EXECUTE / APPEND_DATA / DELETE_CHILD`** (все в `false`) → POSIX-маска `d---------`. Рабочий эталон (`obsidian`): uid/gid **950:950**, `owner@` полный, `group@` rwX (без WRITE_ATTRS/ACL/OWNER), `everyone@` — только attrs. ### Проверенная сигнатура midclt ```bash # 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 — ЗАБЛОКИРОВАНО (ключевой технический питфолл, 2026-09-02):** - `cd /mnt/RED_2TB/docker/transmission` → **`permission denied`** (каталог `drwx------ 911 911` — владелец transdamon/911, truenas_admin нет доступа). - Попытка `docker compose -f /tmp/transmission-new-compose.yml up -d` → создал сеть `tmp_default` и упал: **name conflict** (`container "transmission" already in use`). - Полный рабочий контейнер пересоздать из compose **невозможно** по двум причинам: 1. Контейнер создан **без compose-лейблов** (`com.docker.compose.project = `) → compose его **не распознаёт как свой** → не может мигрировать/пересоздать, только name-conflict. 2. Композ-файл в каталоге 911 недоступен для работы оттуда. - **Найденное решение:** удалить старый контейнер и поднять заново с корректным проектом (сохранит сеть `transmission_default`): ```bash docker rm -f transmission # данные в /config и /mnt/storage НЕ теряются (bind-mount на диск) docker compose -f /tmp/transmission-new-compose.yml -p transmission up -d docker exec transmission id # ожидаем uid=950 ``` - **Почему PUID нельзя применить к работающему контейнеру?** env зашивается при `docker run` (создании). `docker update` меняет ТОЛЬКО ресурсы (CPU/RAM/restart), **НЕ переменные окружения** — ограничение Docker. Пересоздание — единственный способ. - **Альтернатива без пересоздания (если не хотим трогать transmission root):** оставить root; НО тогда новые скачанные файлы будут `root:root`, и radarr/jellyfin (под 950) их не прочитают. → PUID=950 у transmission всё же нужен для цепочки. (Обход с setgid-битом на dir + group 950 возможен как fallback, не выбран.) - **Статус:** ждёт ок Alex на `docker rm -f transmission`. - ⬜ **Этап 3 — рекурсивный фикс прав вглубь** (root → выполняет Alex, масштаб ~257k+ файлов): рекурсивно вернуть владельца 950:950 + снять 0000 на файлах/папках (`find ... -exec chown -R 950:950 {} +`, `chmod u+rw,g+r` для файлов, `u+rwX,g+rwX` для каталогов). Или через UI `Apply recursively`. ### Точное содержание transmission compose (оригинал, важно для пересоздания) ```yaml services: transmission: image: lscr.io/linuxserver/transmission:latest container_name: transmission restart: unless-stopped environment: - TZ=Asia/Novosibirsk - USER=transmission_admin - PASS=E$%p3En4a%R6 - WHITELIST=172.16.*.* volumes: - /mnt/RED_2TB/storage:/mnt/storage - /mnt/RED_2TB/docker/transmission:/config ports: - 9091:9091 - 51413:51413 - 51413:51413/udp ``` (в compose НЕ было PUID/PGID → контейнер root; transmission монтирует `storage` → `/mnt/storage`, download-dir `/mnt/storage/Downloads`) ## Связанные заметки - [[2026-08-31-syncthing-truenas-incident]] — вызвавший инцидент + фикс syncthing - [[arr-stack-taiga]] — acquisition-стек на TrueNAS - [[arr-stack-kraken]] — serving-стек (Kraken) - [[vault-git-sync]] — git-sync алгоритм и скрипты - [[obsidian-sync]] — общая схема Eagle↔Taiga↔Kraken - [[syncthing-truenas-android]] — syncthing схема (требует обновления volumes → `obsidian-syncthing`)