diff --git a/family/documents/vault-sync/2026-09-26-syncthing-clone-root-objects-acl-confusion.md b/family/documents/vault-sync/2026-09-26-syncthing-clone-root-objects-acl-confusion.md new file mode 100644 index 00000000..df6939b6 --- /dev/null +++ b/family/documents/vault-sync/2026-09-26-syncthing-clone-root-objects-acl-confusion.md @@ -0,0 +1,144 @@ +--- +title: obsidian-syncthing — root-owned objects + битый remote + путаница ACL/chown +type: log +namespace: family +tags: [vault, sync, syncthing, git, permissions, acl, nfsv4, root] +date: 2026-09-26 +--- + +# `obsidian-syncthing`: 4031 root-объект, битый remote, ACL/chown не стоит + +**Когда:** 2026-09-26, разбор по запросу Alex «почему документ не доехал до телефона». +**Статус:** диагностика завершена, **фиксы НЕ применены** — ждём решения Alex. + +## Симптом + +Нота `personal/plans/music-tags-review/1-needs-decision.md` закоммичена и запушена с Mac +(`66d4d62..f2501dc main -> main`), но в клон `/obsidian-syncthing` на TrueNAS **не попала**. +`git pull` в клоне падает, а `sync-vault.sh` глушит ошибку (`2>/dev/null`) — синк выглядит +успешным. + +## Две независимые поломки + +### 1. Битый remote в клоне (починено в этой сессии) + +``` +было: origin /vault.git ← потерян префикс пути +стало: origin /mnt/RED_2TB/storage/git/obsidian-vault.git +``` + +`git fetch` при этом падал с `fatal: '/vault.git' does not appear to be a git repository`. +Remote переписан через `git remote set-url`. **Это не связано с правами** — отдельный дефект. + +### 2. Права: новые объекты пишет root (НЕ починено) + +``` +объекты от root: 4051 ← свежие, 2026-09-26 02:42 +объекты от truenas_admin: 4 ← только старые pack с 2026-07-06 +``` + +`git fetch` падает на записи: + +``` +error: insufficient permission for adding an object to repository database .git/objects +fatal: failed to write object +fatal: unpack-objects failed +``` + +**Причина:** контейнер `hermes-taiga` работает **от root** (`docker inspect` → `Config.User` +пусто, `docker exec ... id` → `uid=0(root)`). Именно он делает `git pull` в +`/obsidian-syncthing` — значит каждый новый loose-объект рождается `root:root`, а git под +`truenas_admin` (uid 950) писать в них не может. + +## ACL на `obsidian-syncthing` НЕ СТОИТ — `trivial_acl: true` + +``` +nfs4xdr_getfacl /mnt/RED_2TB/storage/obsidian-syncthing + # owner: 950 # group: 950 # mode: 0o40770 + # trivial_acl: true ← расширенного ACL нет + owner@:rwxpD-aARWcCos:-------:allow + group@:rwxpD-a-R-c--s:-------:allow + everyone@:------a-R-c--s:-------:allow +``` + +Только дефолтный набор TrueNAS (`owner@` / `group@` / `everyone@`), **ни одной именованной +записи**. POSIX-вид (`getfacl`) показывает `user::rwx group::rwx other::---` и **скрывает** +отсутствие расширенного ACL — поэтому диагностировать надо `nfs4xdr_getfacl`, а не `getfacl`. + +## 🔴 Путаница: ACL ставился на `Edu`, не на `obsidian-syncthing` + +Alex помнил, что фикс ACL уже делался («ты вроде ставил рекурсивный acl а не chown»). +Проверка по базе Hermes (`state.db`, таблица `messages`) нашла сессию +**`20260923_124118_9e70782e` (2026-09-23)** — там рекурсивный ACL **действительно ставился**, +но на **`/mnt/RED_2TB/Edu`**, по прямой просьбе Alex: «ты можешь просто группу под acl +сделать с inherit чтобы весь edu был доступен группе включающей truenas_admin?» + +Что тогда было сделано (и сработало — на `Edu`): + +```bash +# 1. бэкап ACL +midclt call filesystem.getacl /mnt/RED_2TB/Edu > .../acl-edu-before-YYYYmmdd-HHMMSS.json +# 2. setacl recursive: gid -> 3000 (nas_users) + INHERIT на всех ACE → job SUCCESS +# 3. chgrp -R nas_users → FAILED (Operation not permitted) +# 4. повтор через midclt setacl с uid/gid=3000 → job 15393 SUCCESS +``` + +Результат на `Edu`: `drwxrwxr-x root:nas_users`, `gid=3000`, все ACE `INHERITED: True`, +`touch` → WRITE_OK. **Группа `nas_users` (gid 3000)** — рабочая: в ней `truenas_admin`, +`mallexxx`, `nas`, `root`, `transmission`. + +> ⚠️ **Урок: «ACL уже ставили» ≠ «ACL стоит на этой папке».** Правка была на `Edu`. +> На `obsidian-syncthing` расширенного ACL **не было никогда** (`trivial_acl: true` доказывает). +> Прежде чем повторять «известный фикс» — проверить, что он на **той самой** папке. + +> 🔴 **`chgrp` от `truenas_admin` даёт `Operation not permitted`.** В NFSv4 `group@` — это +> POSIX-gid файла, а не ACE. Без смены gid группа не получает ничего. Менять gid надо через +> `midclt filesystem.setacl` (root-контекст), а не `chgrp`/`chown` из шелла. + +## Почему однократный `chown` не лечил + +Прошлый разовый `chown` объектов на `truenas_admin` чинил только **уже существующие** объекты. +Следующий `git pull` от root снова рождал root-owned объекты — и `chown` приходилось повторять. +**Это лечение симптома.** Перманентный фикс — либо `ACL + INHERIT` (новые объекты наследуют +права группы), либо запуск синка **не от root** (`user: "950:950"` в taiga service). + +## Что предлагается (решение за Alex) + +| Вариант | Как | Плюс / минус | +|---|---|---| +| **A. ACL + INHERIT** | `setfacl`/`midclt setacl` с `g:truenas_admin:rwx` + `d:` (default) + `chmod g+s` | root и uid 950 пишут одновременно; нужен контроль, что TrueNAS не сбросит ACL при операциях датасета | +| **B. Контейнер не от root** | `user: "950:950"` в taiga service `/mnt/RED_2TB/docker/hermes/docker-compose.yml` | устраняет корень; риск — образ может требовать root для `/opt/data` | +| **C. Повторный `chown`** | перед каждым pull | НЕ рекомендуется — лечит симптом | + +## Сопутствующий дефект: скрипт глушит ошибки + +`sync-vault.sh` / `sync-vault-lib.sh` вызывают `git fetch` и `git merge` с `2>/dev/null`. +При падении синк рапортует **«Pushed ✓» / exit 0**, хотя pull не прошёл. Это и скрывало +поломку. Предложение — убрать `2>/dev/null` с `fetch`/`merge`, чтобы падение было видно в логе +(та же логика, что уже описана для launchd: `exit code 0` ≠ «синк прошёл»). + +## Диагностика (воспроизвести) + +```bash +# владельцы объектов в клоне +ssh truenas_admin@mallexxx.duckdns.org \ + "cd /mnt/RED_2TB/storage/obsidian-syncthing && find .git/objects -type f -printf '%u\n' | sort | uniq -c" + +# реальный ACL (NFSv4, не POSIX!) +nfs4xdr_getfacl /mnt/RED_2TB/storage/obsidian-syncthing # trivial_acl: true? + +# под кем реально работает контейнер +docker inspect hermes-taiga --format '{{.Config.User}}' # пусто => root +docker exec hermes-taiga id # uid=0(root) + +# remote клона +cd /mnt/RED_2TB/storage/obsidian-syncthing && git remote -v +``` + +## Связанные документы + +- `family/how-to/vault-git-sync.md` — архитектура и питфоллы синка +- `family/documents/vault-sync/2026-09-21-bare-repo-push-permission-denied.md` — тот же + root-инстанс Hermes, но на **bare repo** +- `family/documents/vault-sync/2026-09-26-mode-flip-644-755.md` — шум прав от Syncthing +- `personal/tech/truenas-administration` (skill) — NFSv4 ACL через `midclt` diff --git a/family/how-to/vault-git-sync.md b/family/how-to/vault-git-sync.md index 1c8db7da..ec94cfbd 100755 --- a/family/how-to/vault-git-sync.md +++ b/family/how-to/vault-git-sync.md @@ -25,6 +25,21 @@ tags: > реальная доставка до телефона — в пределах тика. Перезапись `schedule_display` через > `hermes cron edit 29851c413903 --schedule '*/5 * * * *'` — на решение Алексея (косметика, на исполнение не влияет). +> **🔴 STATE 2026-09-26 (later) — `obsidian-syncthing` НЕ получает обновления: root-owned objects + битый remote.** +> Нота `1-needs-decision.md` была запушена с Mac (`66d4d62..f2501dc`), но в клон +> `/obsidian-syncthing` **не доехала**. Два независимых дефекта: +> **(1)** `origin` в клоне был **`/vault.git`** (потерян префикс пути) → `git fetch` падал +> `does not appear to be a git repository`. Переписан на полный путь `git remote set-url`. +> **(2)** **4051 из 4055 объектов принадлежат `root`** — контейнер `hermes-taiga` работает от +> root (`Config.User` пусто, `id` → `uid=0`), он и делает pull → новые объекты root-owned, +> git под uid 950 не может писать (`insufficient permission for adding an object`). +> **ACL на `obsidian-syncthing` НЕ СТОИТ** — `nfs4xdr_getfacl` показывает `trivial_acl: true` +> (только `owner@`/`group@`/`everyone@`, ни одной именованной записи). Рекурсивный ACL с группой +> ставился ранее (2026-09-23), но на **`/mnt/RED_2TB/Edu`**, не на эту папку. +> 🔴 **Урок: «ACL уже ставили» ≠ «ACL стоит на этой папке» — проверять фактом.** +> Разовый `chown` лечит только уже существующие объекты; следующий pull от root возвращает дефект. +> Фиксы НЕ применены. Подробно: `family/documents/vault-sync/2026-09-26-syncthing-clone-root-objects-acl-confusion.md`. +> > **🔴 STATE 2026-09-26 — РАСХОЖДЕНИЕ РАСПИСАНИЯ: Taiga-сервер синкается РАЗ В ЧАС, не 5 минут.** > Job `sync-vault` внутри контейнера `hermes-taiga` (Hermes cron, id `29851c413903`) расписан > **`0 * * * *`** — то есть раз в час, **хотя таблица ниже (и `family/how-to/obsidian-sync`) @@ -200,6 +215,12 @@ cd ~/Developer/vault-sync-test && bash test-sync.sh ## Pitfalls +- **`nfs4xdr_getfacl`, а НЕ `getfacl`, если проверяешь наличие расширенного ACL** (2026-09-26). POSIX-вид (`getfacl`, `ls -l`) показывает `user::rwx group::rwx` и **скрывает** отсутствие именованных ACE. Только `nfs4xdr_getfacl` печатает `trivial_acl: true` — что значит «кастомного ACL нет». Все диагностики прав на TrueNAS начинать с него. +- **`chgrp` / `chown` от `truenas_admin` → `Operation not permitted`** (2026-09-26). В NFSv4 `group@` — это POSIX-gid файла, а не ACE: без смены gid группа не получает доступа. Менять gid/uid только через `midclt filesystem.setacl` (root-контекст). TrueNAS `sudo` требует пароль (в отличие от Kraken, где `sudo -n` работает). +- **Разовый `chown` — лечение симптома, не фикс** (2026-09-26). Если root-процесс продолжает писать в папку, root-owned объекты вернутся на следующем же запуске. Перманентно лечит только `ACL`+`INHERIT` (наследование новыми объектами) либо запуск процесса **не от root**. +- **«Этот фикс уже делали» — проверить, что он на ТОЙ САМОЙ папке** (2026-09-26). Рекурсивный ACL с группой `nas_users` ставился 2026-09-23, но на `/mnt/RED_2TB/Edu`. На `obsidian-syncthing` ACL не было никогда. Каждый «известный фикс» верифицировать фактом на целевом объекте, а не памятью. Искать прошлые сессии: `sqlite3 ~/.hermes/hermes-whale/state.db "SELECT session_id, substr(content,1,300) FROM messages WHERE content LIKE '%setfacl%' ORDER BY id DESC LIMIT 40;"`. +- **Клон может иметь битый `origin`** (2026-09-26). `obsidian-syncthing` имел `origin = /vault.git` (потерян префикс пути) → `fatal: does not appear to be a git repository`. Проверять `git remote -v` в клоне до диагностики прав — это независимый дефект, который выглядит как проблема доступа. +- **`sync-vault-lib.sh` глушит ошибки (`2>/dev/null` на fetch/merge)** → «Pushed ✓» / `exit 0` при фактически упавшем pull. Это уже второй раз, когда поломка синка была не видна в логе. Диагностировать по `git status`/`git log` в клоне, не по exit-code. - `git add pathspec` fails with `fatal: pathspec did not match any files` when a directory doesn't exist on disk — use `git add -A` instead - `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` diff --git a/personal/plans/balda-mm-phase4-agents.md b/personal/plans/balda-mm-phase4-agents.md index 64213dd7..d809bc62 100755 --- a/personal/plans/balda-mm-phase4-agents.md +++ b/personal/plans/balda-mm-phase4-agents.md @@ -1,6 +1,8 @@ # Фаза 4 — исполнитель на Mac + `~/agents` + секция «Agents» в Eagle Dashboard -> План. **Работа НЕ начата** (образ не собран, контейнер не запущен). +> План. **Шаг 1 выполнен** (2026-09-26): образ собран, контейнер `balda-executor` Up (healthy), +> `claude 2.1.283` + `codex 0.157.1` внутри, монтирования проверены в обе стороны. +> ⏳ Осталось в шаге 1: логин CLI (нужен апрув на копирование auth-файлов). > Часть [[personal/projects/balda/balda-mm-delegation-plan]] (Фазы 4-5). > Предыдущее: [[personal/projects/balda/balda-kraken-mm-deploy]] (Фаза 3 ✅ — бот отвечает Alex). @@ -15,6 +17,7 @@ | 3 | Управление линками в `~/agents` | Новая секция «Agents» в Eagle Dashboard | | 4 | Auth CLI (claude/codex) | на хосте, `~/cli-auth`, **вне** `~/agents` | | 5 | Сеть Kraken → Mac | Cloudflare Tunnel (существующий cloudflared на Mac) | +| 6 | Провайдер исполнителя (открытый п.1) | `claude` — оба CLI стоят в образе, переключение через `EXECUTOR_PROVIDER` в `.env` | --- @@ -68,6 +71,13 @@ diff + тесты + коммит → Балда → Mattermost | Codex auth | `~/cli-auth/codex` | `/root/.codex` | 0700 | | OAuth-токен Claude | `~/Docker/balda-executor/.env` | env | 0600 | +📌 **Основной путь авторизации — не `claude setup-token`, а копия auth-файлов** из +`~/.claude`/`~/.codex`. Токен в `.env` — запасной вариант для headless без монтирования. +Выбранный вариант: копируются `~/.claude/.credentials.json` + `~/.claude/.claude.json` и +`~/.codex/auth.json` + `~/.codex/config.toml`. Полные каталоги **не** копируются: +`~/.codex` содержит ~640 МБ sqlite-логов/состояний (`logs_2.sqlite`, `state_5.sqlite`, +`memories_1.sqlite`, `queue_1.sqlite`) — исполнителю не нужны, а вес и шум дают. + Пересборка образа и обновление CLI auth не сбрасывают. Токены вне `/agents` → агент их не видит. --- @@ -111,26 +121,77 @@ GET /api/agents/{name}/usage → размер ## 6. Шаги -### Артефакты, уже созданные на диске (2026-09-26) +### ✅ Шаг 1 ВЫПОЛНЕН (2026-09-26) -Созданы **до** подтверждения плана — статус «черновик», при старте сверить и доделать: +Артефакты на диске (все сверены и рабочие; черновики от 15:36 доведены до состояния «собрано и запущено»): -| Путь | Что | -|---|---| -| `~/agents` | рабочая область (drwxr-xr-x, `admin:staff`) | -| `~/cli-auth/{claude,codex}` | auth CLI, 0700, **вне** `~/agents` | -| `~/Docker/balda-executor/Dockerfile` | Node 24 + claude + codex + git, HEALTHCHECK | -| `~/Docker/balda-executor/docker-compose.yml` | 4 тома, сеть `executor-net`, лимит 8G | -| `~/Docker/balda-executor/.env` | ⏳ не создан (токен) | +| Путь | Что | Состояние | +|---|---|---| +| `~/agents` | рабочая область (`admin:staff`) | ✅ монтируется в `/agents`, запись в обе стороны проверена | +| `~/cli-auth/{claude,codex}` | auth CLI, 0700, **вне** `~/agents` | ✅ монтируются в `/root/.claude`, `/root/.codex` | +| `~/Docker/balda-executor/Dockerfile` | `node:24-bookworm` + claude + codex + git + `tini` | ✅ исправлен (см. ниже) | +| `~/Docker/balda-executor/docker-compose.yml` | 3 тома, сеть `executor-net`, `build:` | ✅ исправлен (см. ниже) | +| `~/Docker/balda-executor/agents-mounts.yaml` | манифест внешних проектов (пустой список) | ✅ создан | +| `~/Docker/balda-executor/tools/render-mounts.py` | генератор `compose.override.yaml` | ✅ создан, `--check` работает | +| `~/Docker/balda-executor/compose.override.yaml` | **генерируется**, руками не править | ✅ сгенерирован | +| `~/Docker/balda-executor/.env` | 0600, `EXECUTOR_PROVIDER=claude` | ✅ создан (без секретов) | -`docker-compose.yml` ссылается на `compose.override.yaml`, который генерирует -`tools/render-mounts.py` из `agents-mounts.yaml` — **override вручную не править**. -Ни образ не собран, ни контейнер не запущен. +**Что уже работает (проверено):** +``` +balda-executor Up (healthy) balda-executor:0.1.0 +claude → 2.1.283 (Claude Code) +codex → codex-cli 0.157.1 +``` +`~/agents` ↔ `/agents` пишется в обе стороны; рестарт контейнера состояние сохраняет; +auth-каталоги хоста видны внутри. -**1. `~/agents` + `~/cli-auth` + контейнер (~2 ч)** -`mkdir ~/agents`, `~/cli-auth/{claude,codex}` 0700 → `~/Docker/balda-executor/Dockerfile` -(Node 24 + claude + codex + git) → `docker-compose.yml` с 4 томами → `claude setup-token` в `.env` 0600 → -проверка `claude --version`, `codex --version`, повторный `up -d` остаётся залогинен. +#### 🔴 Три исправленных дефекта черновика (не повторять) + +| Дефект | Симптом | Фикс | +|---|---|---| +| `env_file: .env` без файла | `docker compose up` падает на старте | `.env` создаётся **первым** шагом, 0600 | +| Нет `compose.override.yaml` | `up` падает на монтировании несуществующего пути | `tools/render-mounts.py` запускается **до** `up` | +| `FROM node:24-bookworm` без явных CA | — | `ca-certificates` + `update-ca-certificates` в Dockerfile (условие работы HTTPS у CLI) | + +#### ⚠️ На macOS этой машины — `docker-compose`, НЕ `docker compose` + +Docker CLI 29.4.1 + buildx как плагин (`~/.docker/cli-plugins/`), а compose — **отдельный бинарь** +`/opt/homebrew/bin/docker-compose` (v5.1.3) с симлинком в `/opt/homebrew/lib/docker/cli-plugins/`. +`docker compose` → `docker: unknown command: docker compose`; `docker compose config --quiet` → +`unknown flag: --quiet`. + +📌 **Все команды в этом проекте — через `docker-compose`**, включая `docker-compose config` (без `--quiet`). + +#### Сборка образа (сработавшая команда) + +```bash +cd ~/Docker/balda-executor +python3 tools/render-mounts.py --check # 0 записей, ошибок нет +python3 tools/render-mounts.py # → compose.override.yaml +docker-compose config --services # → executor (валидация) +docker buildx build --builder arm64builder --platform linux/arm64 \ + --provenance=false --load -t balda-executor:0.1.0 . +docker-compose up -d +docker-compose restart # проверка переживания рестарта +``` + +⚠️ Билдер — `arm64builder` (colima, `docker-container` driver). Дефолтный билдер +(`default` → `/var/run/docker.sock`) в статусе `error` — не использовать. +⚠️ `deploy.resources.limits.memory: 8G` из черновика **убран**: в не-swarm compose +эта секция молча игнорируется, лимит создавал ложное ощущение защиты. + +#### ⏳ Осталось в шаге 1: логин CLI (ждёт апрува) + +`~/cli-auth/claude` пуст, в `~/cli-auth/codex` только `tmp/`. Копировать **только auth-файлы**, +не целые каталоги (`~/.codex` ≈ 640 МБ служебных sqlite — агенту не нужны): + +| CLI | Хост | Куда | +|---|---|---| +| claude | `~/.claude/.credentials.json`, `~/.claude/.claude.json` | `~/cli-auth/claude/` | +| codex | `~/.codex/auth.json`, `~/.codex/config.toml` | `~/cli-auth/codex/` | + +📌 В `~/cli-auth` кладётся **копия** (оригиналы на месте, `~/.claude` не переезжает): +у агента-исполнителя свои токены, изолированные от рабочих. Права `~/cli-auth` = 0700. **2. Мост (~3-4 ч)** `~/Developer/agents-bridge/`, FastAPI :8790, `POST /task` (idempotency), `GET /task/{id}`, `GET /healthz`; @@ -167,9 +228,11 @@ MCP-инструмент в Балде → `POST /task`; проба `echo ok > / ## 8. Открыто -1. Провайдер исполнителя — claude или codex -2. Нужен ли `ask`-апрув в будущем -3. Hostname для моста (`agents-bridge.qentra.top`?) +1. ~~Провайдер исполнителя — claude или codex~~ → **claude** (оба в образе, переключение `EXECUTOR_PROVIDER`) +2. ~~Нужен ли `ask`-апрув в будущем~~ → при `allow_all` не нужен; при потребности начинать с варианта B (апрув в мосте, ~0.5 дня) +3. Hostname для моста (`agents-bridge.qentra.top`?) — ждёт шага 3 +4. ⏳ **Апрув на копирование auth-файлов CLI** в `~/cli-auth` — блокирует завершение шага 1 +5. Порт моста `8790` — проверено, свободен (`lsof -nP -i :8790`) ## Связанные заметки - [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5) diff --git a/personal/tech/docker_colima_setup.md b/personal/tech/docker_colima_setup.md index 85d11095..edb68287 100755 --- a/personal/tech/docker_colima_setup.md +++ b/personal/tech/docker_colima_setup.md @@ -111,10 +111,21 @@ cat /tmp/colima.err ## 10. Docker usage +🔴 **Команда — `docker-compose`, а НЕ `docker compose`.** В homebrew-установке compose +приезжает отдельным бинарём, не плагином, поэтому `docker compose …` падает с +`docker: unknown command: docker compose` (выглядит как сломанный Docker — не сломан). +Проверено 2026-09-26: CLI 29.4.1, compose 5.1.3. + +``` bash +docker-compose up -d # ✅ +docker-compose config # валидация (флага --quiet у этой версии НЕТ) +docker compose up -d # ❌ unknown command +``` + Start containers: ``` bash -docker compose up -d +docker-compose up -d ``` Check running containers: @@ -123,6 +134,9 @@ Check running containers: docker ps ``` +Подробнее про границы проброса путей в контейнер (virtiofs) и билдер arm64builder: +[[tech/macos-docker-virtiofs-mount-limits]]. + ------------------------------------------------------------------------ ## Behavior diff --git a/personal/tech/macos-docker-virtiofs-mount-limits.md b/personal/tech/macos-docker-virtiofs-mount-limits.md index 8c5c8e93..497a5ebb 100755 --- a/personal/tech/macos-docker-virtiofs-mount-limits.md +++ b/personal/tech/macos-docker-virtiofs-mount-limits.md @@ -1,15 +1,16 @@ # macOS Docker Desktop — что НЕ проезжает в контейнер (virtiofs) > Создан 2026-09-26. Статус: **проверено живыми тестами** (alpine:3.20, linux/arm64, -> Docker Desktop на macOS 26.6.1, Apple Silicon). +> **Colima** на macOS 26.6.1, Apple Silicon; `colima start --cpu 8 --memory 24 --disk 100`). > Тема: границы проброса путей из мира macOS в контейнер. -> Все выводы получены прогонами, не по памяти. Команды воспроизведения — в §5. +> Все выводы получены прогонами, не по памяти. Команды воспроизведения — в §6. --- ## 0. Главный вывод одной строкой -На macOS Docker Desktop отдаёт контейнеру файлы **не через Linux `mount`**, а через +На macOS рантайм контейнеров (Colima, Docker Desktop) отдаёт контейнеру файлы +**не через Linux `mount`**, а через **virtiofs** — демон, который строит маппинг путей **в момент старта контейнера** и знает только пути **мира macOS**, известные ему на старте. @@ -160,6 +161,55 @@ ln /tmp/a /tmp/b # → Operation not permitt --- +## 8. ⚠️ Реализация на macOS: `docker-compose`, а не `docker compose` + +Проверено 2026-09-26 на этой машине (Docker CLI 29.4.1, macOS 26.6.1, Apple Silicon). +Compose здесь — **отдельный бинарь**, не подкоманда `docker`: + +| Что | Значение | +|---|---| +| CLI | `/opt/homebrew/bin/docker` (v29.4.1) | +| buildx | плагин, симлинк в `~/.docker/cli-plugins/docker-buildx` | +| compose | `/opt/homebrew/bin/docker-compose` (**v5.1.3**), симлинк в `/opt/homebrew/lib/docker/cli-plugins/` | + +```bash +docker compose config # → docker: unknown command: docker compose +docker compose config --quiet # → unknown flag: --quiet +docker-compose config # ✅ работает +docker-compose config --services +``` + +📌 **Всегда `docker-compose` (с дефисом) в этом окружении.** Сообщение об ошибке +выглядит как «docker не знает такой команды», а не «compose не установлен» — легко +принять за сломанный Docker. + +⚠️ Дефолтный билдер (`default` → `/var/run/docker.sock`) в статусе **`error`**. +Для arm64-сборок использовать именованный билдер: +```bash +docker buildx build --builder arm64builder --platform linux/arm64 \ + --provenance=false --load -t . +``` + +### Порядок файлов при генерации override + +`compose.override.yaml` подхватывается compose **автоматически**, но если он ссылается на +несуществующий путь на хосте, `up` падает на монтировании. Поэтому порядок строго такой: + +``` +1. .env ← env_file, иначе up падает сразу +2. agents-mounts.yaml ← манифест (можно пустой: mounts: []) +3. tools/render-mounts.py ← → compose.override.yaml +4. docker-compose up -d +``` + +### Ловушка: `deploy.resources.limits` в не-swarm compose + +Секция `deploy.resources.limits.memory` **молча игнорируется** запуском через +`docker-compose up` (это формат swarm). Лимит создаёт ложное ощущение защиты — +что-то ограничивать нужно иначе (cgroups напрямую или `mem_limit`, зависят от версии). + +--- + ## Связанные заметки - [[personal/plans/balda-mm-phase4-agents]] — применение этих выводов (Фаза 4) - [[personal/projects/balda/balda-mm-delegation-plan]] — общий план проекта diff --git a/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md b/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md index f8c426cf..40d4026e 100755 --- a/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md +++ b/personal/tech/truenas-nfs4-acl-and-arrmultiuser.md @@ -1,7 +1,7 @@ --- title: TrueNAS NFS4 ACL repair + cross-container media PUID created: 2026-09-02 -updated: 2026-09-23 +updated: 2026-09-26 type: tech namespace: personal tags: [truenas, nfs4, acl, midclt, arr, radarr, sonarr, jellyfin, transmission, docker, git] @@ -10,6 +10,7 @@ related: - "[[arr-stack-taiga]]" - "[[watchlist-disk-overflow-fix]]" - "[[2026-09-21-bare-repo-push-permission-denied]]" + - "[[2026-09-26-syncthing-clone-root-objects-acl-confusion]]" --- # TrueNAS NFS4 ACL repair + cross-container media PUID @@ -33,6 +34,18 @@ Any "permission denied" / file not readable / git `loose object ... corrupt` / e ## Core facts - TrueNAS SCALE uses **NFSv4 ACL**, not plain POSIX. `ls -la` shows a POSIX mask that can mislead. - Read real ACL: `midclt call filesystem.getacl `; write: `midclt call filesystem.setacl`. +- 🔴 **`getfacl` (POSIX view) HIDES the absence of a custom ACL — use `nfs4xdr_getfacl`** (2026-09-26). + `getfacl` prints `user::rwx / group::rwx / other::---` whether or not named ACEs exist. + `nfs4xdr_getfacl ` prints `trivial_acl: true` (only `owner@`/`group@`/`everyone@`) — the + definitive "no custom ACL here". Start every permission diagnosis with it, not `getfacl`. +- 🔴 **"We already fixed this with ACL" ≠ "the ACL is set on THIS path"** (2026-09-26). A recursive + group ACL had been applied to `/mnt/RED_2TB/Edu` and was remembered as "done" — but the failing + path was a **different** folder with `trivial_acl: true` (never touched). Verify by fact on the + target object before repeating or ruling out a fix. Find past sessions: + `sqlite3 ~/.hermes/hermes-whale/state.db "SELECT session_id, substr(content,1,300) FROM messages WHERE content LIKE '%setfacl%' ORDER BY id DESC LIMIT 40;"` +- 🔴 **A one-off `chown` is symptom-treatment** (2026-09-26). If a **root** process keeps writing + into the folder, root-owned files/objects reappear on the very next run. Permanent fixes only: + `ACL` + inherit, OR run the writer **not as root** (`user: "950:950"` in compose). - **`truenas_admin` is FULL_ADMIN in midclt** → can chown/setacl without passwordless root. - Web UI ACL editing only works on zfs **datasets**, not arbitrary subdirs under one dataset → use midclt/CLI. - Post-restore a tree may be owned by wrong uid (e.g. 921 `transmission`) with files `mode 0000`.