diff --git a/family/documents/vault-sync/2026-09-21-bare-repo-push-permission-denied.md b/family/documents/vault-sync/2026-09-21-bare-repo-push-permission-denied.md index 1ad63419..52482bc5 100644 --- a/family/documents/vault-sync/2026-09-21-bare-repo-push-permission-denied.md +++ b/family/documents/vault-sync/2026-09-21-bare-repo-push-permission-denied.md @@ -3,7 +3,7 @@ title: "2026-09-21 — bare repo push Permission denied (root-owned objects/XX)" aliases: [2026-09-21-vault-bare-repo-acl, push permission denied objects] tags: [family, vault-sync, incident, truenas, acl, git, midclt] created: 2026-09-21 -updated: 2026-09-21 +updated: 2026-09-22 type: incident namespace: family --- @@ -11,6 +11,11 @@ namespace: family # 2026-09-21 — bare repo push: `Permission denied` на `objects/XX` > **Итог:** push починен, **права поставлены с наследованием** (`FILE_INHERIT`+`DIRECTORY_INHERIT`), чтобы дефект не возвращался. Root-пароль на TrueNAS не потребовался — всё через `midclt` API. Полный рецепт: `personal/tech/truenas-nfs4-acl-and-arrmultiuser.md` §«Durable fix with INHERITANCE». +> +> 🔴 **РЕЦИДИВ 2026-09-22** — дефект вернулся (35 root-каталогов), наследование не защитило. +> Плюс важный урок: **собирать dacl «с нуля» нельзя** — TrueNAS требует поле `id` в +> каждой ACE, работает только read-modify-write. См. раздел «РЕЦИДИВ 2026-09-22» ниже. +> **Статус: ОТКРЫТ** (синк сломан, фикс v2 готов но не применён). ## Симптом @@ -86,9 +91,73 @@ git rev-parse HEAD; git rev-parse nas/main # оба 3b923d3 > `git push` доказал, что *текущее* дерево синхронно, но **не** что свежий root-владельческий объект реально записывается. > Определяющий тест остаётся открытым — см. «Remaining» ниже. +## 🔴 РЕЦИДИВ 2026-09-22: дефект вернулся + моя первая починка упала + +**Симптом тот же** (push 4 коммитов с Mac): +``` +error: unable to write file ./objects/28/fe1cb94d…: Permission denied +error: unable to write file ./objects/d1/cb38b336…: Permission denied + ! [remote rejected] main -> main (unable to migrate objects to permanent storage) +``` + +**Замер:** +``` +root-owned записей в objects/ : 98 (35 каталогов objects/XX + объекты внутри) +root-owned refs/heads/main : тоже root +write-тест: touch objects/28/.wrtest → Permission denied +write-тест: touch objects/d1/.wrtest → Permission denied +``` + +→ **Наследование, поставленное 2026-09-21, НЕ защитило.** Причина та же: root-инстанс +`mcpvault` продолжает писать от root и создавать новые `objects/XX/` от root. Даже +если ACL родителя корректен, свежий root-owned каталог может прийти с режимом +`drwxr-sr-x` (группа без `w`) — запись для uid 950 блокируется. + +### 🔴 Что НЕ сработало (моя ошибка — стоила полного провала запуска) + +Наивный dacl с нуля — собрать ACE заново `{tag, type, perms, flags}` **без поля `id`**: + +```python +{"tag": "owner@", "type": "ALLOW", "perms": {...}, "flags": {...}} # ← нет "id" +``` + +→ TrueNAS отклонил **КАЖДЫЙ** каталог: + +``` +[EINVAL] filesystem_acl.dacl.0: ACL entry is missing required keys(s): {'id'} +[EINVAL] filesystem_acl.dacl.1: ACL entry is missing required keys(s): {'id'} +... (245 каталогов, 0 успешных) +``` + +🔴 **Вывод: `id` — обязательное поле ACE в TrueNAS.** Собирать dacl «с нуля» нельзя. +Работает только **read-modify-write**: взять ACL через `getacl` (там `id` уже +проставлен TrueNAS) и **править в нём только `perms`/`flags`**, сохраняя `id` как есть. + +Сравнение с прошлым успехом (2026-09-21, 291 каталог, 0 ошибок): там +`options.stripacl: false` + **`cur["acl"]` переданный обратно** — то есть именно +read-modify-write. Мой «с нуля» вариант был отклонением от рабочего рецепта. + +**Рабочая схема (v2, скрипт `fix_vault_acl2.py`):** +```python +cur = getacl(path) # голая строка пути +dacl = patch_acl(cur) # правим ТОЛЬКО perms/flags, 'id' не трогаем +setacl({"path": path, "dacl": dacl, + "options": {"recursive": False, "traverse": True, "stripacl": False}}) +# затем поллить core.get_jobs до SUCCESS +``` + +⚠️ Статус: скрипт v2 написан, но **не залит и не запущен** — Alex поставил +задачу «сначала доки, потом чинить синк». Верификация записи (`touch`) не выполнялась. + ## Remaining (открытые вопросы) -1. ~~**Наследование не проверено на практике**~~ — **проверено 2026-09-21 15:05 UTC** (read-only с Taiga), см. раздел «Верификация наследования» ниже. Наследование работает. +0. 🔴 **2026-09-22 РЕЦИДИВ НЕ УСТРАНЁН.** Синк сломан, push с Mac падает. Кол-во root-каталогов + вернулось (35). Фикс v2 (`/tmp/fix_vault_acl2.py` на Mac, read-modify-write) **написан, но не + залит и не запущен** — Alex: «сначала доки, потом чинить синк». + **Порядок возобновления:** `scp` скрипта на TrueNAS → запуск → проверка `touch` в + `objects/XX` → `git push nas main`. Скрипт v1 (`fix_vault_acl_inherit.py`) **нерабочий**, + удалить/не использовать (`[EINVAL] … missing required keys(s): {'id'}`). +1. ~~**Наследование не проверено на практике**~~ — **проверено 2026-09-21 15:05 UTC** (read-only с Taiga), см. раздел «Верификация наследования» ниже. Наследование работает **на тот момент**; 2026-09-22 показал, что от рецидива оно НЕ спасает. 2. **`mcpvault` продолжает работать от root** — пока он root, дефект может вернуться. Профилактика: перезапустить его под uid 950. 3. **`cleanup` не видит `/media/movies`** — на Кра́кене удаляются только Radarr-файлы; просмотренное лежит в `/media/movies`, диск будет расти. Отдельная задача (см. `family/plans/watchlist-disk-overflow-fix.md`). diff --git a/personal/tech/lampac-torrserver.md b/personal/tech/lampac-torrserver.md index 468bd2f9..65a14c53 100644 --- a/personal/tech/lampac-torrserver.md +++ b/personal/tech/lampac-torrserver.md @@ -20,7 +20,17 @@ | Приёмка в UI | ✅ Alex запустил F1 в плеере | | Тестовая раздача `probe` | ✅ убрана сама (сброс при рестарте) | | `min_sid` | ✅ `min_sid: 3` убран → сток `15` | -| Минус год у «Волшебный участок: Фильм» | ⏳ оставлено пусто (см. ниже) | +| Минус год у «Волшебный участок: Фильм» | ✅ ЗАКРЫТО — оставлено пусто (данных нет в TMDB, фильм в производстве) | +| Документация | ✅ саммари → [[family/how-to/lampac-torrents]], план снесён | + +### Итог задачи (2026-09-22) + +Задача про торренты **закрыта полностью**. Единственный дефект был внесён мной +(переключение `redapi` на локальный JacRed) и откачен. Разбор → саммари-док +`family/how-to/lampac-torrents.md`; детальный план `personal/plans/lampac-pidtor-503-fix.md` +снесён по требованию Alex, 8 ссылок на него перебиты. + +**Онлайн НЕ гасим** — Alex запретил ([[personal/plans/lampac-disable-1xbet-ads]] не применён). --- @@ -335,7 +345,7 @@ docker start lampac ## 🔴 «Волшебный участок: Фильм» — `year: null`. НЕ БАГ, данных нет в источнике -**Дата разбора:** 2026-09-22 · **Статус:** ⏳ оставлено пусто (решение Alex ожидается) +**Дата разбора:** 2026-09-22 · **Статус:** ✅ **ЗАКРЫТО. Оставлено пусто (решение Alex).** ### Сырьё @@ -393,6 +403,9 @@ TMDB поиск по названию подтверждает: ровно од **Правило «фильм есть → добавлять» соблюдено:** позиция в каталоге, карточка откроется, торренты найдутся когда фильм выйдет. +**Решение Alex (2026-09-22):** «Ок, закрывай задачу» — оставлено как есть, +косметику `"скоро"` не делаем. Вопрос закрыт. + ### Питфолл: где лежат ключи TMDB/KP ``` diff --git a/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md b/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md index 1a1861cb..44a221a6 100644 --- a/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md +++ b/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md @@ -117,7 +117,12 @@ FOR each dir in [repo] + listdir(f"{repo}/objects"): ``` **⚠️ midclt argument-form pitfalls (cost real time):** -- `filesystem.getacl` and `filesystem.listdir` take a **BARE STRING path**, NOT `{"path": …}`. Passing an object gives `[EINVAL] path: Not a string`. +- 🔴🔴 **Каждая ACE требует поле `id`.** Собирать dacl «с нуля» как `{tag, type, perms, flags}` **НЕЛЬЗЯ** — + TrueNAS отклонит: `[EINVAL] filesystem_acl.dacl.0: ACL entry is missing required keys(s): {'id'}` + (проверено 2026-09-22: 245 каталогов, 0 успешных). Работает только **read-modify-write**: + взять ACL через `getacl` (там `id` уже проставлен), править в нём **только `perms`/`flags`**, + передать `cur["acl"]` обратно в `setacl`. Именно так был сделан успешный прогон 2026-09-21 (291 каталог). +- `filesystem.getacl` и `filesystem.listdir` take a **BARE STRING path**, NOT `{"path": …}`. Passing an object gives `[EINVAL] path: Not a string`. - `filesystem.setacl` takes **ONE JSON object with `path` INSIDE it** (see gotcha #1 above) — the opposite convention from getacl on the same service. Easy to invert. - `filesystem.setacl` / `setperm` are **async**: they return a job id. Polling immediately returns state `WAITING` → looks like failure. Must poll until `SUCCESS`/`FAILED`. With ~120 dirs over SSH the loop outlives the SSH timeout — **run it detached** (`nohup … &`) and read the log later; the first SSH-timed-out run had in fact already applied the ACLs. - Passwordless `sudo` is available on **Kraken** (`sudo -n true` → ok) but **NOT** on TrueNAS `truenas_admin`.