38 KiB
Executable File
title, updated, type, tags
| title | updated | type | tags | |||||
|---|---|---|---|---|---|---|---|---|
| Vault Git Sync — Architecture & Scripts | 2026-10-15 | tech |
|
Vault Git Sync
✅ STATE 2026-09-26 (поправка Taiga, 09:50 UTC) — «раз в час» было ошибкой чтения: sync идёт каждые 5 минут. Блок ниже («РАСХОЖДЕНИЕ РАСПИСАНИЯ», «задержка до 60 минут») построен на поле
schedule_display: "0 * * * *"в/opt/data/cron/jobs.json— это устаревшее поле, диспетчер его не использует. Факты:
jobs.json→schedule.expr = "*/5 * * * *",display = "*/5 * * * *"(устарело толькоschedule_display);executions.db, job29851c413903, 1001 тик за 09-22 22:25 → 09-26 09:40 UTC: интервалы 985 × 5 мин, 14 × 4 мин, 1 × 1 мин → 288,6 тика/сутки, а не 24. Тик «раз в час» дал бы 98 записей за это окно;/tmp/vault-sync.log— timestampedPushed ✓каждые 5 минут.
hermes cron listпечатает то же устаревшее поле (Schedule: 0 * * * *), поэтому каденцию проверять поexecutions.dbилиjobs.json:schedule.expr, а не по выводу CLI. Задержка «до 60 минут» бумажная; реальная доставка до телефона — в пределах тика. Перезаписьschedule_displayчерезhermes cron edit 29851c413903 --schedule '*/5 * * * *'— на решение Алексея (косметика, на исполнение не влияет).
✅ STATE 2026-09-26 (later, RESOLVED) — ACL с
nas_users+INHERIT применён наobsidian-syncthing, синк работает. Оба дефекта закрыты: (1) Битого remote —origin = /vault.gitпереписан на полный путь (но снова слетел послеsync-vault.sh: тот форситfile:///vault.gitвнутри контейнера — это правильно для/vault, но для хостового клона путь другой. Проверятьgit remote -vперед диагностикой прав). (2) Права — решено перманентно:midclt filesystem.setaclрекурсивно на/mnt/RED_2TB/storage/obsidian-syncthingсgid=3000(nas_users) и флагамиFILE_INHERIT+DIRECTORY_INHERITна все ACE. Былоtrivial_acl: true+ 4051 объект root → сталоtrivial_acl: false,group: 3000, 0 объектов root (все 4073 =truenas_admin). Группаnas_users(gid 3000) содержитroot,truenas_admin,nas,mallexxx,transmission— поэтому и root-процесс Taiga, и Syncthing (uid 950) пишут в одну папку. Наследование проверено ФАКТОМ:touch .inherit_test→truenas_admin:nas_users,mkdir .inherit_dir_test→truenas_admin:nas_users. Артефакты после теста удалены.git fetch origin main→Already up to date; HEAD клона содержал коммит с Mac (f2501dc). Бэкап ACL перед правкой:/home/truenas_admin/variantA_bk/acl-obsidian-syncthing-before-20260926-154827.json. Скрипт:acllib_obsidian_syncthing_fix.py(см. раздел «ACL repair» → рецепт).🔴 ГЛАВНЫЙ ПИТФОЛЛ ЭТОЙ ПРАВКИ:
midclt filesystem.getaclотдаёт КЭШ. Сразу послеsetaclон печатал старые значения (trivial: True,gid: 950,NOINHERIT), и скрипт рапортовал ложный «VERIFY FAILED». Реальное состояние видно только черезnfs4xdr_getfaclиstat. Никогда не веритьmidclt getaclсразу послеsetacl— сверятьnfs4xdr_getfacl(тамtrivial_acl: falseиfd-----= FILE_INHERIT+DIRECTORY_INHERIT) плюсstat -c '%A %U:%G'.Историческая путаница (для будущих сессий): рекурсивный ACL с
nas_usersдействительно ставился 2026-09-23, но на/mnt/RED_2TB/Edu(запрос Alex: «можешь просто группу под acl сделать с inherit чтобы весь edu был доступен группе включающей truenas_admin?»), не наobsidian-syncthing. Отсюда «я это уже чинил» — чинил, но другую папку. Искать прошлые сессии: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;".Разовый
chownлечит только уже существующие объекты; следующий pull от root возвращает дефект. Перманентно лечитACL+INHERIT(наследование новыми объектами) либо запуск процесса не от root. Корень по-прежнему не устранён: контейнерhermes-taigaработает от 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, id29851c413903) расписан0 * * * *— то есть раз в час, хотя таблица ниже (иfamily/how-to/obsidian-sync) заявляет «*/5». Обнаружено при проверке «доехал ли док до телефона»: ноты, залитые с Mac в 14:32, в клонеobsidian-syncthingотсутствовали.Следствие: изменения с Eagle/Kraken доезжают до телефона с задержкой до 60 минут. Цепочка
bare repo → git-клон obsidian-syncthing → Syncthing → телефонрвётся на втором шаге (Syncthing здоров≠док доехал).Ручной триггер (проверено):
docker exec hermes-taiga sh -c 'cd /opt/data && HERMES_HOME=/opt/data \ /opt/hermes/.venv/bin/hermes cron run 29851c413903' # → Triggered job: sync-vault … Ran now: succeeded.После триггера: клон обновлён, Syncthing
needFiles: 0, телефонcompletion: 100.Открытый вопрос к Alex: менять расписание на
*/5 * * * *или оставить раз в час?⚠️ Диагностика синка — только изнутри контейнера Syncthing: порт
8384слушает лишь в контейнере,curlс хоста TrueNAS даёт пустоту.docker exec syncthing sh -c 'K=$(grep -oE "<apikey>[^<]+" /var/syncthing/config/config.xml | sed "s|<apikey>||"); \ curl -s -H "X-API-Key: $K" "http://localhost:8384/rest/db/status?folder=k5vyy-gzdgj"'Папка Syncthing: id
k5vyy-gzdgj«Obsidian Vault», устройстваSM-S931B(телефон) иsyncthing-taiga. Готовый скрипт:~/Developer/personal-os/tools/music-tags/check_syncthing2.sh.
✅ STATE 2026-09-22 — рецидив ACL закрыт перманентно. Дефект вернулся (35 root-каталогов), push с Mac падал. Починено: 236/236 каталогов
INHERIT+WRITEчерезmidclt filesystem.setacl(read-modify-write + ожидание job). Наследование подтверждено ФАКТОМ —mkdir objects/zzz→ новый ACL унаследован (INHERITED:True),touchвнутрь OK. End-to-end push:ba3780c..8b831a8 main -> main✅. Поставлен cron-сторож (каждые 2 ч):~/scripts/vault-acl-watchdog.pyна TrueNAS — SILENT когда всё ок, чинит + докладывает когда сломано. Скрипты:/Users/admin/Developer/vault-tools/. 🔴 Корень НЕ устранён: контейнерhermes-taigaработает от root (HERMES_ALLOW_ROOT_GATEWAY=1,User:пусто) →mcpvault /vaultпишет root-owned объекты. Перманентный фикс —user: "950:950"вtaigaservice/mnt/RED_2TB/docker/hermes/docker-compose.yml(риск: образ может требовать root для/opt/data). Сторож пока закрывает последствия. Подробно:family/documents/vault-sync/2026-09-21-bare-repo-push-permission-denied.md.STATE 2026-09-21 — bare repo push починен (наследуемый ACL).
git push nasпадалPermission deniedнаobjects/XX: 28 каталогов были root-owned (создавалmcpvaultвторого Hermes на TrueNAS, от root), 13 каталогов без owner-x. Фикс: полный dacl-REPLACE сFILE_INHERIT+DIRECTORY_INHERITчерезmidclt filesystem.setacl— 291 каталог, 0 ошибок, без root-пароля. Push прошёл дважды, Mac=nas=3b923d3. ⚠️ Прямой write-тестtouchвobjects/не выполнялся → наследование подтвердит только следующий push root-инстанса. Подробно:family/documents/vault-sync/2026-09-21-bare-repo-push-permission-denied.md.STATE 2026-09-02 (позднее обновление): Eagle/bare repo снова синхронны (ahead=0/behind=0,
2fab879). Ранее (см. ниже) git-sync стоял из-за битых правstorage/git— починено полной dacl-заменой (family/documents/vault-sync/2026-09-02-restore-privilege-scope.md§RESOLVED).STATE 2026-09-04: починена потеря git identity в контейнере Taiga (слетела при рекреации после crashloop 09-02) — коммиты молча не проходили ~1.5 суток, изменения телефона не доезжали до bare. Фикс:
sync-vault.shтеперь идемпотентно ставитuser.name "Taiga"/user.email "taiga@hermes"при каждом запуске. Накопленное закоммичено (2b45264). Подробно:family/documents/vault-sync/2026-09-04-git-identity-loss-silent-commit-failure.md.STATE 2026-09-26 —
pipefail+grep -qмолча отменял коммиты вsync-vault-lib.sh. При большом числе изменений (492 файла ≈ 23 КБ вывода)git status --porcelain | grep -q .падал поSIGPIPE(grep выходил после первого совпадения, git дописывал в закрытый pipe),set -o pipefailобъявлял pipeline упавшим (exit 141) → веткаelse→ коммит не создавался вообще, при этом тик печаталPushed ✓×3 и exit 0. Так застряли 491 mode-flip и правка телефона (1-needs-decision.md, 4 чекбокса[x]). Фикс: условие черезSTATUS="$(git status --porcelain)"+[ -n "$STATUS" ], сообщение коммита — однимawk(первые 25 путей, дальше(... +N more)). ⚠️awk | head -25в подстановке даёт тот же SIGPIPE → тик падает с exit 141, так делать нельзя. Проверено: коммит12a7566e(492 файла), три SHA равны,git statusпуст. Разбор:family/documents/vault-sync/2026-09-26-commit-skip-pipefail.md.⛔ НОВАЯ проблема 2026-09-02 (не решена): оба Taiga-клиента (
/storage/obsidianи/storage/obsidian-syncthing) отстали на месяц — HEADceea848(2026-08-01) vs bare repo2fab879(2026-09-02). Причина: контейнерhermes-taigaв crash-loop (16339 рестартов) —uv run hermes gateway runне может докачать депы (pytz/markdown-it-py) с PyPI, т.к. весь HTTP идёт через мёртвыйsocks5://vless-proxy:1080, аUV_CACHE_DIR=/tmp/uv-cacheпустеет на каждом рестарте. Taiga 3-phase sync (крон внутри агента) не выполняется => телефон видит устаревший vault. Подробно:family/documents/vault-sync/2026-09-02-hermes-taiga-crashloop.md. Фикс НЕ применён (ждёт Alex).
Триггер cron на маке (Eagle) — это launchd, НЕ Hermes cron:
~/Library/LaunchAgents/com.sync-vault.plist→/Users/admin/scripts/sync-vault.sh,StartInterval=300(5 мин). Скриптsync-vault-lib.shнамеренно глушит dead remote (git fetchfail →exit 0), поэтому launchd показываетlast exit code = 0даже при фактически стоящем sync. Системный crontab на маке пуст.
Obsidian vault is a bare git repo on TrueNAS:
/mnt/RED_2TB/storage/git/obsidian-vault.git
Also mirrored in Gitea: https://git.mallexxx.duckdns.org/git_admin/obsidian-vault
Five clients sync to it: Eagle, Kraken, Taiga (vault), Taiga (syncthing/phone), iPad (Edhita Documents).
Architecture
bare repo (obsidian-vault.git) — source of truth
↑↓ ↑↓ ↑↓ ↑↓
Eagle Kraken Taiga /vault Taiga /obsidian-syncthing
full clone sparse clone sparse clone full clone
~/obsidian ~/obsidian personal/family/ phone ↔ Syncthing
personal/family/ .obsidian/
.obsidian/
Taiga runs 3-phase sync (order matters):
- syncthing → phone changes land in bare repo first
- vault → agent (mcpvault) changes land in bare repo
- syncthing → agent changes propagate to phone
Sync Algorithm (same for all clients)
git add -A && git commit # commit local changes
git fetch origin main # get remote state
git merge origin/main --no-edit # 3-way merge (conflict → commit markers)
git push origin main # push back
No stash. No GIT_DIR/GIT_WORK_TREE workarounds.
Scripts
| Host | Script | Type | Trigger |
|---|---|---|---|
| Eagle | ~/scripts/sync-vault.sh |
full clone | launchd com.sync-vault (⚠️ НЕ Hermes cron) StartInterval=300 |
| Kraken | ~/scripts/sync-vault.sh |
sparse clone | host crontab */5 |
| Taiga | /opt/data/sync-vault.sh |
3-phase orch. | Hermes cron */5 * * * * — job 29851c413903. ⚠️ hermes cron list и поле schedule_display в jobs.json показывают устаревшее 0 * * * * (см. поправку в STATE 2026-09-26 выше); фактические тики — каждые 5 мин |
| iPad | /var/mobile/Library/VaultSync/sync-vault.sh |
full working tree, initial shallow clone | launchd local.ipad.obsidian-vault-sync, mobile, StartInterval300 |
⭐ УТОЧНЕНО 2026-09-02 (Eagle/мак sync-механизм): автосинка на маке запускается НЕ Hermes cron, а launchd-агентом
~/Library/LaunchAgents/com.sync-vault.plist:
- Программа:
/Users/admin/scripts/sync-vault.sh,StartInterval = 300сек (5 мин)launchctl list→ активен,runs = 3762,last exit code = 0- ⚠️
sync-vault-lib.shумышленно глушит недоступность remote: еслиgit fetchпадает → логируетFetch failed (...)иexit 0. Поэтому launchd показываетexit code 0, даже когда sync фактически сломан (см. случай ahead 55 при сломанном bare repo после restore). Это не отсутствие cron и не сбой — диагностировать нужно поgit status(ahead count), а не по exit-code агента.- Системный crontab и
/etc/crontabна маке отсутствуют.
All clients share a common library: sync-vault-lib.sh (same dir as sync-vault.sh).
Source of truth for scripts: ~/Developer/vault-sync-test/scripts/ on Eagle.
Taiga Container Mounts
| Container path | Host path | Purpose |
|---|---|---|
/vault |
/mnt/RED_2TB/storage/obsidian |
sparse clone (agent rw) |
/vault.git |
/mnt/RED_2TB/storage/git/obsidian-vault.git |
bare repo (git remote) |
/obsidian-syncthing |
/mnt/RED_2TB/storage/obsidian-syncthing |
full clone (phone sync) |
/opt/data |
/mnt/RED_2TB/docker/hermes/config |
Hermes config + scripts |
/vault is a sparse clone with remote = file:///vault.git.
/vault.git is hidden from the agent (mcpvault points to /vault).
/obsidian-syncthing is also a clone with remote = file:///vault.git, но его .git/ сам синкается
Syncthing'ом с других устройств, и remote.origin.url периодически перезаписывается чужим адресом —
хост-путём /mnt/RED_2TB/storage/git/obsidian-vault.git (2026-09-26 09:36) или Gitea-URL
https://git.mallexxx.duckdns.org/git_admin/obsidian-vault.git (версия с телефона). Внутри контейнера
не резолвится ни один: git fetch origin main → fatal: … does not appear to be a git repository,
sync-vault-lib.sh печатает Fetch failed (origin offline?) — skipping merge/push и выходит с exit 0,
поэтому фазы 1 и 3 молча не выполняются (правки телефона не доезжают до bare, правки агента — до телефона),
а cron рапортует «успех». С 2026-09-26 09:42 sync-vault.sh в начале каждого тика принудительно ставит
origin = file:///vault.git и добавляет url."file:///vault.git".insteadOf для обоих чужих адресов.
Разбор: family/documents/vault-sync/2026-09-26-syncthing-clone-origin-url-flip.md.
Sparse Checkout Paths
Taiga and Kraken check out only:
personal/
family/
.obsidian/
work/ and wiki/ are never checked out on sparse clients — they never touch those paths.
Deploy a Script Fix
# Edit on Eagle:
~/Developer/vault-sync-test/scripts/sync-vault-lib.sh # common algorithm
~/Developer/vault-sync-test/scripts/sync-vault.sh # Taiga 3-phase orchestrator
# Deploy to Taiga:
scp ~/Developer/vault-sync-test/scripts/sync-vault-lib.sh \
~/Developer/vault-sync-test/scripts/sync-vault.sh \
truenas_admin@mallexxx.duckdns.org:/mnt/RED_2TB/docker/hermes/config/
# Deploy to Kraken (via sudo):
cat ~/scripts/sync-vault-lib.sh | ssh kraken "sudo tee ~/scripts/sync-vault-lib.sh > /dev/null"
cat ~/scripts/sync-vault.sh | ssh kraken "sudo tee ~/scripts/sync-vault.sh > /dev/null"
.gitignore
Plugin binaries excluded to avoid cross-device conflicts:
.obsidian/plugins/obsidian-git/main.js
.obsidian/plugins/obsidian-git/styles.css
.obsidian/plugins/obsidian-git/manifest.json
data.json (plugin config) is tracked — shared settings.
Regression Tests
~/Developer/vault-sync-test/test-sync.sh — 16 scenarios (A–P):
covers Cyrillic filenames, moves, deletes, conflicts, work/ phantom-delete, 3-phase sync.
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 начинать с него.- 🔴
midclt filesystem.getaclСРАЗУ ПОСЛЕsetaclотдаёт КЭШ (2026-09-26). Печатает старые значения (trivial: True, старыйgid,NOINHERIT), из-за чего правка выглядит неприменённой. Реальное состояние — толькоnfs4xdr_getfacl <path>(trivial_acl: false, флагиfd-----) +stat -c '%A %U:%G %n'+ фактическийtouchвнутрь папки. Не откатывать и не повторятьsetaclпо показаниямgetacl. chgrp/chownотtruenas_admin→Operation not permitted(2026-09-26). В NFSv4group@— это POSIX-gid файла, а не ACE: без смены gid группа не получает доступа. Менять gid/uid только черезmidclt filesystem.setacl(root-контекст). TrueNASsudoтребует пароль (в отличие от 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(запрос Alex проМаскелиаде). Наobsidian-syncthingACL не было никогда — отсюда ложное «я это уже чинил». Каждый «известный фикс» верифицировать фактом на целевом объекте, а не памятью. Искать прошлые сессии: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 pathspecfails withfatal: pathspec did not match any fileswhen a directory doesn't exist on disk — usegit add -Ainsteadgit add -Ain 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 ingit ls-filesoutput — use-c core.quotePath=false- ZFS on TrueNAS blocks
chmod—git initfails from host; must run from inside Docker container or create.gitstructure manually - NFS4
owner@ DENY READ_DATAACL breaks push (fake "corrupt" objects) — see History 2026-09-02. Key: after a TrueNAS pool restore, a subset of loose git objects in the bare repo can carry an inverted NFS4 ACLowner@ type=DENY READ_DATA=True. In NFSv4 a DENY overlays ALLOW, so the owning uid (e.g. 950) can't mmap-read those objects → on receive,git-receive-pack/index-packreportsloose object ... is corruptand rejects push, even though data is intact (git fsck --fullunder root is clean). Fix = full dacl REPLACE viafilesystem.setacl(chmod/chown do NOT remove DENY ACEs). Tell-tale: POSIX file mode40(r--------) on loose objects vs normal750.⚠️ Don't reach for
stripacl(2026-09-21): it tears the ACL down to POSIX and does not remove a DENY in all cases. Always pass a completedaclarray —setacltreats it as a full replace. Permission deniedwritingobjects/XX/(NOT the corrupt-object case) — see History 2026-09-21. Two defects: (a) dirs with ownerrw-and nox(drw-r-x---), (b)objects/XX/dirs owned byrootwithgroup@:r-x— git runs as uid 950 → cannot write. Root cause ismcpvault(a second Hermes on TrueNAS,/opt/hermes, vault/vault) running as root and creating root-owned objects in the shared bare repo. Requiresmidclt(see below) —chown/chmodastruenas_admingiveOperation not permittedbecause uid 950 isn't the owner, and TrueNASsudoneeds a password (unlike Kraken, wheresudo -nworks). Fix must includeFILE_INHERIT+DIRECTORY_INHERITor the defect returns on the next push by the root instance. Recipe:personal/tech/truenas-nfs4-acl-and-arrmultiuser.md§«Durable fix with INHERITANCE».
ACL repair via midclt (no root password needed)
truenas_admin is FULL_ADMIN in midclt → can read/write ACLs directly.
# READ — path as a BARE STRING
midclt call filesystem.getacl /mnt/RED_2TB/storage/git/obsidian-vault.git
midclt call filesystem.listdir /mnt/RED_2TB/storage/git/obsidian-vault.git/objects
# WRITE — ONE json object with "path" INSIDE it (opposite convention!)
midclt call filesystem.setacl '{"path":"/x","acltype":"NFS4","dacl":[…],
"options":{"recursive":false,"traverse":true,"stripacl":false}}'
Pitfalls: setacl/setperm are async (return a job id) — poll core.get_jobs until SUCCESS/FAILED, or the immediate WAITING reads as a failure. filesystem.setperm refuses when a non-trivial ACL is present ([EINVAL] … Option "stripacl" required).
✅ Рабочий рецепт: группа + INHERIT на папку (проверено 2026-09-26)
Скрипт (acllib_obsidian_syncthing_fix.py, отработал на /mnt/RED_2TB/storage/obsidian-syncthing):
read-modify-write ACL — читает существующий dacl, сохраняет id полей (без них TrueNAS падает
missing required keys: {'id'}), переписывает flags в FILE_INHERIT+DIRECTORY_INHERIT,
ставит gid нужной группы, recursive: true, stripacl: false.
payload = {
"path": TARGET,
"uid": 950, # truenas_admin — оставить владельцем
"gid": 3000, # nas_users — группа, в которой и root, и uid 950
"acltype": "NFS4",
"dacl": new_dacl, # read-modify-write, флаги INHERIT, id сохранены
"options": {"recursive": True, "traverse": True, "stripacl": False},
}
Запуск: scp скрипта на NAS → ssh truenas_admin@mallexxx.duckdns.org "python3 /home/truenas_admin/<script>.py".
Критические детали:
stripacl: false—trueне убирает DENY и ломает наследование (проверено 2026-09-21).gidв payload приrecursive: trueзаодно переписывает владельца всех вложенных объектов (4051 root-объект →truenas_adminодной операцией).group@в NFSv4 — это POSIX-gid, а не ACE.chgrpотtruenas_adminдаётOperation not permitted; менять gid только черезmidclt.- 🔴 VERIFY через
midclt getaclбесполезен — это кэш. Послеsetaclон печатает старые значения. Проверять:nfs4xdr_getfacl <path>(trivial_acl: false,fd-----) +stat -c '%A %U:%G %n'+ фактическийtouchнового файла внутрь папки. - Бэкап перед правкой:
midclt call filesystem.getacl <path> > /home/truenas_admin/variantA_bk/acl-<name>-before-$(date +%Y%m%d-%H%M%S).json. - Приёмка:
find <path> -type f -printf '%u\n' | sort | uniq -c→ 0 объектов от root;git fetch origin main→Already up to date.
History
-
2026-09-26 (ACL на
obsidian-syncthing— закрыто): синк не доезжал до телефона. Два независимых дефекта: (1)originклона =/vault.git(потерян префикс) →fetchпадал; (2) 4051 из 4055 объектов принадлежалиroot— контейнерhermes-taigaработает от root (Config.Userпусто,id→uid=0), он делает pull, git под uid 950 не мог писать (insufficient permission for adding an object). Пользователь потребовал поставить ACL с INHERIT на папку — сделано:midclt filesystem.setaclrecursive,gid=3000(nas_users) +FILE_INHERIT+DIRECTORY_INHERITна все ACE. Былоtrivial_acl: true→ сталоfalse,group: 3000, 0 root-объектов (4073 =truenas_admin). Наследование подтвержденоtouch/mkdirфактом.git fetch→Already up to date. Коммит с Macf2501dcнайден в истории клона. 🔴midclt getaclпослеsetaclотдаёт кэш (ложный «не применилось») — сверятьnfs4xdr_getfacl+stat. Бэкап:/home/truenas_admin/variantA_bk/acl-obsidian-syncthing-before-20260926-154827.json. -
2026-09-26: клон
/obsidian-syncthingпотерял рабочий remote — Syncthing переписал.git/config(mtime 09:36:19, owner uid 950 ≠ наши root-записи) на хост-путь/mnt/RED_2TB/storage/git/obsidian-vault.git. Первый за всю историю логаFetch failed(тик 09:40:31) → фазы 1 и 3 молча скипались приexit 0;/obsidian-syncthingотстал на 1 коммит Eagle (66d4d626vs bare5c939ed8). Фикс:sync-vault.shфорситorigin = file:///vault.gitкаждый тик +insteadOfдля хост-пути и Gitea-URL; after-fix три SHA равны3edbddf5, статусы пусты, новыхFetch failedнет. Корень (.gitклона синкается Syncthing'ом — отсюда же.sync-conflict-*внутри.git) — на решение владельца. Подробно:family/documents/vault-sync/2026-09-26-syncthing-clone-origin-url-flip.md. -
2026-09-26 (поправка каденции): снято ложное «Taiga синкается раз в час».
hermes cron listиjobs.json:schedule_displayпоказывают0 * * * *, ноschedule.expr = "*/5 * * * *"иexecutions.dbдают 985 интервалов по 5 мин из 1001 тика (288,6/сутки). Диагностировать каденцию — только поexecutions.db/schedule.expr. -
2026-09-21: bare repo push
Permission deniedonobjects/XX. 28 dirs root-owned (created bymcpvaultof a second Hermes on TrueNAS running as root) + 13 dirs with ownerrw-and nox.chown/chmodastruenas_admin→Operation not permitted(TrueNAS sudo needs a password, unlike Kraken). Fixed with a full dacl REPLACE +FILE_INHERIT/DIRECTORY_INHERITviamidclt filesystem.setacl— 291 dirs, 0 errors, no root password. Two clean pushes after; Mac =nas=3b923d3. ⚠️ Direct write-test inobjects/was NOT run. Full log:family/documents/vault-sync/2026-09-21-bare-repo-push-permission-denied.md. -
2026-06-20: Kraken ACL fix. mcpvault (Hermes контейнер, uid 10000) не мог писать в существующие файлы vault из-за прав (EACCES). Подробно:
Проблема 1 — права доступа: файлы в vault создаются от uid 1000 (kraken через git на хосте), а mcpvault в контейнере работает от uid 10000.
patch_noteпадал:EACCES: permission denied.delete_note+write_noteсрабатывали, но mcpvault при write перезаписывает файл целиком, переформатируя YAML фронтматер (убирает кавычки, меняет стиль) — шум в git-diff.Проблема 2 — экранирование backtick в patch_note: mcpvault
patch_noteне может найти строку, содержащую тройные обратные кавычки (```). Пришлось якориться на соседнюю строку без backtick — например, на\n\n## Заголовоквместо\``\n\n## Заголовок`.Решение — ACL ext4 (setfacl):
sudo setfacl -R -m u:10000:rwx /srv/.../obsidian # mcpvault — write sudo setfacl -R -d -m u:10000:rwx /srv/.../obsidian # default для новых файлов sudo setfacl -R -m g:kraken:rwx /srv/.../obsidian # kraken (uid 1000) — write sudo setfacl -R -d -m g:kraken:rwx /srv/.../obsidian # default для новых файловИтоговая совместимость: uid 10000 (mcpvault) и uid 1000 (kraken/git) могут читать и писать одни и те же файлы.
patch_noteработает без EACCES.Ограничение (mcpvault patch_note + backtick):
patch_noteне может найти строку, содержащую тройные обратные кавычки (```). Пример: нужно было вставить новый раздел между closing code fence\``и заголовком## Исправления (v0.0.92.... ВариантыoldString`, которые НЕ сработали:"}\n\n\``\n\n## Исправления (v0.0.92..."` — не найдено"\``\n\n\n## Исправления (v0.0.92..."` — не найдено- Любая комбинация пустых строк между
\``` и заголовком — не найдено
Что сработало: якориться на соседнюю строку без обратных кавычек. Вместо
\``\n\n## Исправления...использовать\n\n## Исправления (v0.0.92...— замена пустой строки + заголовка, куда перед заголовком вставляется новый контент, а сам заголовок остаётся.patch_note(oldString="\n\n## Исправления (v0.0.92...")` — найдено и отработало. -
2026-05-23: Taiga deleted 32+ wiki files. Root cause:
git add -Abefore merge staged deletions of files outside sparse cone. Fixed with scopedgit add. -
2026-06-01: "Керамика" lost from gift-ideas. Root cause:
git add $SPARSE_PATHSfailed silently when.obsidian/absent — patch+move not committed. -
2026-06-02: Full architecture migration. Taiga
/vaultconverted fromGIT_DIR=bare GIT_WORK_TREEworkaround to proper sparse clone. All clients unified on samecommit→fetch→merge→pushalgorithm. Sync interval: every 5 min.
iPad iOS9.3.5 — подключён 2026-10-15
Прямой SSH Git клиент того же TrueNAS bare main, Git2.8.1 от Saurik, отдельный restricted Ed25519 key (только этот repo), persisted TrueNAS user settings. Vault в Documents Edhita/ObsidianVault; task работает от mobile каждые300с и при load. Проверены actual iPad→NAS push и NAS→iPad automatic pull на двух последовательных ticks. Native fixture tests повторены 2026-10-15: offline сохранение/retry, commit/push конфликтных маркеров с обеими версиями, получение другим clone, продолжение синка и возобновление прерванного merge. Script/last-success/last-result/error logs находятся вне Git vault; core.fileMode=false, auto gc отключён. При конфликте, как на Mac, client коммитит маркеры с пометкой merge conflict — review needed и пушит; ручное разрешение не блокирует последующие ticks. Скрипты прочих клиентов не менялись. Полные пути, source/evidence и обслуживание: personal/how-to/ipad-vault-sync.