[2026-09-26 09:44:37] taiga-vault: family/documents/vault-sync/2026-09-26-syncthing-clone-origin-url-flip.md family/how-to/vault-git-sync.md

This commit is contained in:
Taiga committed 2026-09-26 09:44:37 +00:00
1 parent 3edbddf512
commit 8efe5c8970
2 files changed
+133 -1

No files matched your search

@@ -0,0 +1,105 @@
---
tags: [vault, git, syncthing, incident]
date: 2026-09-26
---
# 2026-09-26 — Флип `origin` у клона `/obsidian-syncthing` (Syncthing переписал `.git/config`)
## Симптом
Первый за всю историю лога (21646 строк) провал fetch в тике 09:40:31:
```
[2026-09-26 09:40:31] [syncthing] Fetch failed (origin offline?) — skipping merge/push
[2026-09-26 09:40:31] [taiga-vault] Pushed ✓
[2026-09-26 09:40:31] [syncthing-out] Fetch failed (origin offline?) — skipping merge/push
```
Тик завершался `exit 0` (graceful skip в `sync-vault-lib.sh`), поэтому cron-задача
рапортовала «успех», хотя **фазы 1 и 3 не выполнялись вообще**:
- фаза 1 (`syncthing`): правки с телефона коммитились локально, но не доезжали до bare;
- фаза 3 (`syncthing-out`): правки агента/Eagle не доезжали до клона → и дальше по
Syncthing не уходили на телефон.
Состояние на момент обнаружения: bare и `/vault` = `5c939ed8`, `/obsidian-syncthing`
= `66d4d626` (отстал на один коммит Eagle), `git status --porcelain` пуст в обоих,
маркеров конфликта 0. Данные не потеряны — git идемпотентен, отставание догоняется
одним прогоном.
## Причина
`remote.origin.url` клона `/obsidian-syncthing` оказался
```
/mnt/RED_2TB/storage/git/obsidian-vault.git
```
— **хост-путь, которого в контейнере hermes-taiga нет** (`/mnt` пуст, mount-ов на
`/mnt/RED_2TB` нет; bare смонтирован как `/vault.git`). Проба вручную:
```
$ git -C /obsidian-syncthing fetch origin main
fatal: '/mnt/RED_2TB/storage/git/obsidian-vault.git' does not appear to be a git repository
```
До 09:35:33 фаза 1 успешно фетчила и мерджила (`reflog` клона: `merge origin/main:
Fast-forward` в 09:06, 09:12, 09:25, 09:35), а **в 09:36:19 файл `.git/config` был
перезаписан** — `mtime == ctime == 2026-09-26 09:36:19`, владелец uid 950 (наши git-процессы
в контейнере пишут файлы клона как root: `index`, `ORIG_HEAD`, `refs/remotes/origin/main`
— `root`, а сам `config` — `950:950`, `-rw-rw----`). Значит записал **внешний процесс,
а не наш контейнер**.
Это тот же механизм, что и `.sync-conflict`-файлы внутри `.git` (от 2026-05-29:
`config.sync-conflict-…`, `index.sync-conflict-…`, `FETCH_HEAD.sync-conflict-…`):
**каталог `.git` клона `/obsidian-syncthing` сам синкается Syncthing'ом с других
устройств**. Старая конфликт-копия конфига (2026-05-15) содержит совсем другой remote —
телефонный:
```
[remote "origin"] url = https://git.mallexxx.duckdns.org/git_admin/obsidian-vault.git
[user] name = alex phone S25
```
То есть у клона периодически «прилетает» `origin` с чужого устройства, и внутри
контейнера этот адрес не резолвится. Сегодня прилетел хост-путь.
## Что сделано (Taiga, 2026-09-26 09:42–09:43 UTC)
1. Клону вернули контейнерно-корректный remote: `origin = file:///vault.git`
(тот же bare, что и у `/vault`).
2. В `/opt/data/sync-vault.sh` добавлен **идемпотентный guard** (рядом с блоками
`safe.directory` и git identity) — он выполняется в начале каждого тика:
- если `remote.origin.url` клона ≠ `file:///vault.git` → `git remote set-url origin file:///vault.git`;
- в глобальный конфиг (`/root/.gitconfig`) добавляется правило перезаписи
`url."file:///vault.git".insteadOf` для обоих известных чужих адресов
(хост-путь `/mnt/RED_2TB/storage/git/obsidian-vault.git` и Gitea-URL
`https://git.mallexxx.duckdns.org/git_admin/obsidian-vault.git`) — на случай,
если Syncthing перепишет конфиг **посреди** тика, уже после guard'а.
Побочных эффектов нет: других репозиториев, использующих эти два адреса, в
контейнере нет (проверено `grep` по git-конфигам).
## Проверка (после правки)
- `bash -n /opt/data/sync-vault.sh` → syntax OK;
- три прогона, все `exit 0`, `Pushed ✓` ×3, новых `Fetch failed` нет (в логе остались
только 4 исторические строки тика 09:40);
- три SHA равны: `bare` = `/vault` = `/obsidian-syncthing` = `3edbddf5`;
- `git status --porcelain` пуст в обоих клонах, маркеров конфликта 0, `Push failed` 0;
- idempotency guard'а: два прогона подряд → правила `insteadOf` не дублируются (2 шт.);
- функциональный тест перезаписи: временный клон с `origin = /mnt/RED_2TB/storage/git/obsidian-vault.git`
→ `git fetch origin main` проходит (**REWRITE_OK**), т.е. даже чужой адрес теперь
разрешается в локальный bare.
## Осталось за владельцем (Taiga молча не делает)
- **Корень класса** — `.git` клона `/obsidian-syncthing` синкается Syncthing'ом между
устройствами (отсюда и `.sync-conflict-*` внутри `.git`, и флип remote'а). Правильное
лечение — исключить `.git/` из синка этого folder'а на стороне Syncthing
(`.stignore` в корне folder'а или в конфиге демона). Это правка чужой инфраструктуры,
решение Алексея. Пока guard в `sync-vault.sh` держит fetch рабочим независимо от
содержимого `.git/config`.
- Конфиги `/root/.gitconfig` и `/opt/data/sync-vault.sh` живут в контейнере:
скрипт персистентен (`/opt/data` = ZFS), глобальный конфиг — нет, но он
пересоздаётся при каждом тике самим скриптом.
+28 -1
View File
@@ -12,6 +12,19 @@ tags:
# 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`, job `29851c413903`, 1001 тик за 09-22 22:25 → 09-26 09:40 UTC: интервалы **985 × 5 мин**,
> 14 × 4 мин, 1 × 1 мин → **288,6 тика/сутки**, а не 24. Тик «раз в час» дал бы 98 записей за это окно;
> - `/tmp/vault-sync.log` — timestamped `Pushed ✓` каждые 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 — РАСХОЖДЕНИЕ РАСПИСАНИЯ: Taiga-сервер синкается РАЗ В ЧАС, не 5 минут.**
> Job `sync-vault` внутри контейнера `hermes-taiga` (Hermes cron, id `29851c413903`) расписан
> **`0 * * * *`** — то есть раз в час, **хотя таблица ниже (и `family/how-to/obsidian-sync`)
@@ -105,7 +118,7 @@ No stash. No GIT_DIR/GIT_WORK_TREE workarounds.
|--------|-------------------------------|---------------|----------------------|
| 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 `0 * * * *` (РАЗ В ЧАС!)** — job `29851c413903`, см. STATE 2026-09-26 выше. Не `*/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 мин |
> **⭐ УТОЧНЕНО 2026-09-02 (Eagle/мак sync-механизм):** автосинка на маке запускается **НЕ Hermes cron, а launchd-агентом** `~/Library/LaunchAgents/com.sync-vault.plist`:
> - Программа: `/Users/admin/scripts/sync-vault.sh`, `StartInterval = 300` сек (5 мин)
@@ -128,6 +141,17 @@ Source of truth for scripts: `~/Developer/vault-sync-test/scripts/` on Eagle.
`/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:
@@ -202,6 +226,9 @@ Pitfalls: `setacl`/`setperm` are **async** (return a job id) — poll `core.get_
## History
- **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 (`66d4d626` vs bare `5c939ed8`). Фикс: `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 denied` on `objects/XX`. 28 dirs root-owned (created by `mcpvault` of a **second Hermes on TrueNAS** running as root) + 13 dirs with owner `rw-` and no `x`. `chown`/`chmod` as `truenas_admin` → `Operation not permitted` (TrueNAS sudo needs a password, unlike Kraken). Fixed with a **full dacl REPLACE + `FILE_INHERIT`/`DIRECTORY_INHERIT`** via `midclt filesystem.setacl` — **291 dirs, 0 errors**, no root password. Two clean pushes after; Mac = `nas` = `3b923d3`. ⚠️ Direct write-test in `objects/` 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). Подробно: