[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:
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), глобальный конфиг — нет, но он
|
||||
пересоздаётся при каждом тике самим скриптом.
|
||||
@@ -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). Подробно:
|
||||
|
||||
Reference in new issue
Block a user