---
name: mini-launchd-ext-tcc-fda
description: mac mini — launchd-сервисы не видят /Volumes/Ext без Full Disk Access (TCC); homebrew-бинарю FDA выдают вручную через GUI
metadata: 
  node_type: memory
  type: reference
  originSessionId: 883032c2-9dd6-4b50-b864-2fb6bccb7836
  modified: 2026-08-07T21:56:33.275Z
---

На mac mini **launchd-агент (GUI-домен) НЕ может обращаться к внешнему диску
`/Volumes/Ext` без Full Disk Access у конкретного бинаря** — это macOS TCC.

Симптом: процесс **намертво виснет** на `open()`/`read()` файла на Ext (НЕ EPERM,
а блокировка в `__open_nocancel`; стек-сэмпл показывает 100% в этом syscall),
лог пуст, порт не открывается, «never exited». Из интерактивной ssh/Terminal-сессии
тот же доступ работает мгновенно (у Terminal/sshd есть consent) — оттого foreground
проходит, а launchd-служба висит.

**Ключевые факты (проверено 2026-06-29 на Гаечкине):**
- Apple-системные бинари (`/usr/bin/head`, `cat`) читают Ext без FDA — они exempt.
- Сторонние homebrew-бинари (`postgres`, `python3.12`, `mariadbd`) — НЕТ, виснут.
- FDA **per-binary, по реальному пути/cdhash**. Наследования НЕТ:
  - запуск дочерним процессом от FDA-bash НЕ даёт ребёнку доступ;
  - `exec` заменяет образ → ответственным за TCC становится новый бинарь без FDA.
- MariaDB/tbr на Ext работают только потому, что их бинарю FDA выдали **вручную в GUI**.
- Добавить FDA из SSH **нельзя**: TCC.db под SIP; `tccutil` умеет только `reset`.

**Как чинить:** на mini (AnyDesk/RustDesk) System Settings → Privacy & Security →
Full Disk Access → «+» → ⌘⇧G → вставить **реальный путь** бинаря (resolved через
`readlink -f`, напр. `/opt/homebrew/Cellar/postgresql@16/16.14/bin/postgres`) → включить тумблер.
**Грабли:** `brew upgrade` меняет версию в пути → грант ломается, выдавать заново.

Вывод для планирования: «положить данные сервиса на /Volumes/Ext» под launchd —
не бесплатно, требует разового GUI-шага. Если его нельзя — держать данные в `$HOME`
(внутренний, FDA не нужен) и ограничивать рост чисткой + бэкапить в gdrive (rclone).
Связано: [[gaechkin_pricing_constraint]], [[obsidian_documentation_rule]].

**2026-07-13 — повтор на avtero TecDoc MariaDB, подтверждён точный TCC-сервис.**
Мигрировал `com.cebers.mariadb` с 12.2.2→11.4 (`brew install mariadb@11.4`); ОБА
бинаря (старый И новый) зависли под launchd тем же паттерном (0% CPU, `cwd=/`, ни
одного открытого файла). `log show --predicate 'eventMessage CONTAINS "mariadbd"'`
подтвердил точный сервис: **`kTCCServiceSystemPolicyRemovableVolumes`**,
`AUTHREQ_PROMPTING` для нового бинаря — прямое доказательство диагноза без
догадок. `brew install` действительно ломает грант даже для НЕ обновлявшегося
бинаря (12.2.2 тоже перестал работать) — вероятно из-за пересборки общих
зависимостей (openssl@3/pcre2) меняющей code-signature контекст. Юзер выдал FDA
через AnyDesk на оба пути разом → оба сразу заработали. См. [[avtero_oem_provenance]],
Obsidian Projects/Автеро Decisions#D-tecdoc-mariadb-114-lts.

**2026-08-08 — третья форма той же болезни: перезагрузка mini кладёт TecDoc на ~24 минуты.**
При выключении `mariadbd` получил **EPERM** (не зависание!) на запись в свой же datadir
`/Volumes/Ext/tecdoc-mysql/data-114/`: не сдампил буферный пул и **не смог удалить `tc.log`** —
файл координатора двухфазных транзакций. На следующей загрузке протухший `tc.log` даёт
`Bad magic header in tc log` → `Aborting`, а `KeepAlive.Crashed=true` + `ThrottleInterval=30`
превращают это в петлю: **44 попытки, порт 3307 закрыт 00:16:57 → 00:40:52**. Потом старт
прошёл сам, но механизм самоизлечения не установлен — рассчитывать на него нельзя.

Важное для диагностики: `LastExitStatus` у этой метки за час менялся 1 → 256 → −15. **Опираться
на код выхода нельзя вообще** — только на отсутствие PID в первой колонке `launchctl list`,
пустой `ps`, закрытый порт. Проверка каталога — не ручной `python -m app.scrapers.tecdoc_local
selfcheck` (он падает `Access denied ... using password: NO`, потому что в ssh-сессии нет
окружения службы), а `set -a; . ~/Services/avtero/.env; set +a` перед ним, либо код выхода
`local.avtero.tecdoc-keepalive`.

Лечится в `~/Services/tecdoc-mariadb/start-mariadb.sh`: отодвигать `tc.log` в сторону, если
предыдущая попытка упала именно на нём. Удалять его вслепую **нельзя** — у поднятого сервера
это живой рабочий файл.

**Не путать с [[mini_ext_disk_dropout_disksleep]]** — там диск пропадает целиком с USB-шины (не в diskutil, не в system_profiler), а не просто TCC-блокирует уже смонтированный диск для конкретного бинаря.
