Files
obsidian-vault/family/documents/vault-sync/2026-09-02-restore-privilege-scope.md
T

313 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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_<дата>/<name>.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 <path> \
'{"uid":950,"gid":950,"dacl":[<эталон-ACE obsidian>],"options":{"recursive":true,"traverse":true,"stripacl":false}}'
```
3. **Verify**: все каталоги → uid 950; тест `git --git-dir=<base>/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` у сервисов** → при подъёме будет дефолт-сеть `<project>_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 = <no value>`) → `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 <json-аргументами>` вываливалась `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":[<owner@ ALLOW полный>, <group@ ALLOW read/exec>],"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`)