Files
admin/.tasks/ARCHIVE.md
vitya 7f40bba14b tasks(windows-host-fallback-backup-daily): close 🟢 — daily live, smoke #1
End-to-end pipeline verified — windows-host (DESKTOP-NSEF0UK) → kreknin
SFTP daily 03:00 MSK live. Smoke run #1 на 14:03-15:25 = 4868 sec
(81 min). Total 23 GB на kreknin /volume1/NetBackup/windows-host/<date>/.

Components synced (6 paths):
- mssql/ 486 MB (5 .bak FULL+COMPRESSION+INIT for MoreThenCms,
  StayerCalculator, StayerPrice, stostayer, TireService)
- sites/ 20 GB (C:\sites\* всё содержимое)
- minio/ 3.1 GB (windows-host MinIO data dir)
- traefik/ 1.1 MB (config + acme.json)
- iis-config/ 68 KB (applicationHost.config)
- iis-backup-webconfiguration/ 384 KB (native Backup-WebConfiguration)

ScheduledTask `WindowsHost-Backup-Daily` 03:00 MSK SYSTEM Wake-To-Run
(машина просыпается из standby). Все 3 host backups (windows 03:00 →
RUVDS 04:30 → VDS 05:00) sequential — аккумулируются за одну ночь.

Notifications dual-channel ntfy `vds-backup` + email Yandex 587 STARTTLS
fired clean (no WARNING/FAILED в log).

Acceptance check 5/6:
 ScheduledTask, components, retention, notify, README
⚠ Smoke recovery test (restore .bak в чистый container + SELECT) —
   deferred как "DR drill" follow-up, не блокер.

Decisions captured в .tasks/ARCHIVE.md closure block:
- rclone в ProgramData (не Program Files) — non-admin staging path
- icacls SID *S-1-5-32-544 — locale-safe для ru-Windows
- sqlcmd 18 `-C` flag (TLS-required даже на localhost)
- MinIO bind-mount path verified via docker inspect
- SSH key deploy via VDS pivot (windows-host нет direct ssh к kreknin)
- Wake-To-Run critical для warm-standby use-case

Block moved: STATUS.md  → ARCHIVE.md 🟢 + header refresh. STATUS.md
теперь 2 active tasks (iis-migration-to-ruvds 🟡, infra-inventory ).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:37:29 +03:00

80 KiB
Raw Blame History

Admin Task Archive

Closed tasks, append-only. Order = closure order. Per-task + ".md" + files живут в .tasks/ root.

Created from STATUS.md cleanup 2026-05-24. Total: 25 closed entries.


🟢 [board-viewer-expand-whitelist] — Closed 2026-05-22 — whitelist расширен с 5 до 21 репо

Closed: 2026-05-22 — auth.toml обновлён: убраны OpeItcLoc03/books (опечатка) и OpeItcLoc03/projects-meta-mcp (субдир, не репо); добавлены 16 репо из 3 owner'ов (workshop/common/meeting-room/factory/projects-wiki, victor/books/stostayer.new/pilonuxt/pilorama98.ru/snolla/modules-db/crsc.web/npm-mcp/O_C-Phazerville, cancel_music/heart-and-mask/cancel-music-webstore/temps_utile-/modulair-rag). Build restart → 242 tasks from 21 repos (cancel_music: 9, OpeItcLoc03: 151, victor: 82). Owner-pill теперь полезен — 3 разных namespace.

Atomic revert: ssh vds "cd /opt/stacks/board-viewer && sudo cp auth.toml.bak auth.toml && docker compose restart build" Branch: n/a


🟢 [board-viewer-redeploy-ux-round1] — Closed 2026-05-22 — 5 UX-fixes redeploy'ed (task-numbers paused)

Closed: 2026-05-22 — image rebuilt (61 tests pass) + pushed to registry + VDS pull+redeploy. All 5 fix'ы verified:

  • Slug visible на карточке (<code class="card-slug">)
  • Full datetime (YYYY-MM-DD HH:MM МСК + tooltip)
  • Multi-owner working (3 namespace: cancel_music / OpeItcLoc03 / victor, owner-pill visible)
  • Done cutoff (show 158 more, data-overflow attr)
  • Drawer bundle (1.29 MB HTML vs 164 KB — md bundled, 0 runtime git requests)

board-viewer-ux-task-numbers remains paused (user choice, не regression).

Branch: master

Next action:

  1. Pull OpeItcLoc03/board-viewer master на dev-машине.
  2. cd ~/projects/board-viewer && npm test — все тесты green (включая новые от 6 UX-fixes).
  3. docker build -f deploy/Dockerfile.build -t registry.kzntsv.site/board-viewer-build:latest . — новый image.
  4. docker push registry.kzntsv.site/board-viewer-build:latest.
  5. На VDS через Portainer (stack Id=3): Pull and redeploy (либо ssh cd /opt/stacks/board-viewer && docker compose pull && docker compose up -d).
  6. Smoke: external curl → 200 OK; визуально на board.kzntsv.site проверить все 6 fix'ов (slug, datetime, owner-conditional, task-numbers, done-cutoff, drawer-clicks без TypeError).
  7. Попутный audit (не блокер): verify /opt/stacks/board-viewer/auth.tomlboard_viewer_repos корректен? По memory Gitea owner namespace: books должен быть victor/books, не OpeItcLoc03/books. Если несоответствие — исправить, restart build-контейнера. Branch: n/a

🟢 [board-viewer-vds-deploy] — Closed 2026-05-22 — board.kzntsv.site live (Portainer-managed Id=3)

Closed: 2026-05-22 — VDS stack deployed + Portainer-migrated одной сессии. DNS A board.kzntsv.site → 89.253.255.94 (user-action). Image registry.kzntsv.site/board-viewer-build:latest built (10.8s) + pushed. Stack files в /opt/stacks/board-viewer/: docker-compose.yml + nginx.conf + auth.toml (Gitea admin token из pass gitea/admin-token) + .env (BOARD_VIEWER_USERS bcrypt $$-escaped). 5 repos в whitelist (board-viewer + books + claude-skills + projects-meta-mcp + admin). Basic-auth creds viewer:34Qb... в pass board-viewer/viewer-password.

Initial deploy через ad-hoc ssh+compose, потом retro-migrated в Portainer как часть Portainer-canonical sweep (user корректировка: «через портейнер!»). Smoke: external curl unauth → 401, with auth → 200 / 164 KB index.html. Build container каждые 300s обновляет /output/index.html.

Followups (не блокеры):

  • .env.example есть в deploy/ (commit e64d94cd), но не в первичном ls — нюанс bash, не баг.
  • Portainer API key regen — отдельная chore-task если хочется token-auth вместо JWT.

Atomic revert: Portainer UI → delete stack board-viewer (Id=3). DNS A-record остаётся (user manages). Branch: master


🟢 [owncloud-vds-deploy] — oCIS 7.1.0 на vds-kzntsv live, 25 044 объектов / 26 GB

Closed: 2026-05-22 — import complete. 99.97% (22.2 GB / 25 038 objs) залито rclone'ом за ~2.5 ч (3.35 MiB/s uplink). Оставшиеся 6 файлов (4.04 GB) — 4× .pat ~221 MB + 2× .seospider 1.55+1.7 GB — упали с 502 (later 500) на каждой попытке rclone из-за 60-секундного timeout где-то в HTTP stack (Go http.Client.Timeout в reva v2.27 datagateway, не traefik). Workaround: throwaway sftp-only ssh-key → агент scp'ит в /tmp/oc-import/ на VDS → c VDS curl PUT loopback'ом через traefik в правильные oCIS пути (local network 142 MB/s — все 6 файлов залиты за 28 секунд total). Все PROPFIND size match. Throwaway-key revoked, staging cleaned.

Finding закреплён в ocis-on-vds-deploy-recipe §Gotcha 5 (60s timeout cap × upload speed = ~200 MB max single PUT на 3 MiB/s uplink; recipe для VDS-side workaround). Traefik buffering middleware не починил — сам выдаёт 500 на 60s mark (likely bug в vulcand/oxy buffer); подходит для других кейсов с медленными backend'ами, но не для этого 60s cascade.

Detail: owncloud-vds-deploy.md. Backup integration: /opt/stacks/owncloud в rsync sources vds-backup-rsync-kreknin (next nightly snapshot 2026-05-22 05:00 MSK захватит full 26 GB).

Open follow-ups:

  • 2× DipTrace .exe (~2 MB total) — заблокированы Windows Defender на агентовой машине, не баг oCIS. User-side issue, не блокер.
  • Portainer API key (vds-kzntsv/full-env PORTAINER_API_KEY) — 401 Unauthorized, требует regen через Portainer UI + pass-store update. Не блокировало deploy (admin user/pass работали). Отдельная chore-task если нужна.
  • oCIS image upgrade на 7.2.x / 8.x потенциально подъёмлет 60s timeout (newer reva имеет configurable timeout). Defer пока 4.04 GB workaround сработал.

Branch: master


🟢 [admin-infra-project-pointers] — Bootstrap-pointers для design admin-infra-project. Pre-fills target's .wiki/CLAUDE.md Domain conventions ссылками на спецификацию. Дизайн не лежит в этом репо — только pointer-stub. Без этой таски следующий агент попадёт в дыру: where_stopped one-liner + пустой Domain conventions stub = угадывание порогов / pipeline-этапов вместо чтения готовых решений.

Кто делает: любой следующий агент в этом проекте. Это первая по приоритету таска промоушена — все остальные импл-таски ссылаются на pointers через .wiki/CLAUDE.md.

Status: done Closed: 2026-05-21 — .wiki/CLAUDE.md Domain conventions заполнен design-context pointer-блоком (3 sources: concepts/admin-infra-project.md, archive ~/projects/.workshop/.archive/2026-05-21-admin-infra-project.md, local overview.md). Unblocks migration chain. Where I stopped: done Next action: В .wiki/CLAUDE.md секции "Domain conventions" вставить блок (или заменить дефолтный setup-wiki stub):

Mandatory: read design context before implementation

Before picking up any task in .tasks/, load the full design context. It does not live in this repo as a standalone source — only pointers do. Sources, in order:

  1. Local design (canonical): .wiki/concepts/admin-infra-project.md — ingested via promote 2026-05-21. Identity, scope, content inventory, migration recipe (subtree-split + read-tree merge for populated prefixes), initial agenda.
  2. Brainstorm process trace (rationale): ~/projects/.workshop/.archive/2026-05-21-admin-infra-project.md. Why each decision was made (recommend vs menu trade-offs), what was rejected (filter-repo vs subtree-split, "admin-only" skills, splitting roadmap from ops), anti-patterns flagged during brainstorm.
  3. Local overview.md — thin summary, quick orientation only — never the source of truth.

Do not invent migration recipes, file lists, taxonomies, or scope decisions from task where_stopped lines alone — those are pointers, not specifications. The concept doc and archive contain the rationale.


Закоммитить: wiki(claude): add design-context pointers for admin-infra-project. Branch: n/a


🟢 [morecms-subtree-split] — Шаг 2 миграции: в ~/projects/MoreThenCms создать 4 subtree-split ветки, каждая хранит per-file history своего dir'а. Эти ветки потом импортируются в admin (см. admin-subtree-import-and-cleanup).

См. .wiki/concepts/admin-infra-project.md §Migration mechanics шаг 2 для полного контекста.

Status: done Closed: 2026-05-21 — 4 split branches созданы в ~/projects/MoreThenCms (split-wiki-entities @24661c10, split-wiki-sources @a5e96432, split-wiki-concepts @c727aaa1, split-tasks @cbdc2b39). Acceptance: per-file history preserved (verified via subsequent subtree-add in admin). Не push'ились — служебные. Where I stopped: (not started) Next action: ```powershell cd ~/projects/MoreThenCms git subtree split --prefix=.wiki/entities -b split-wiki-entities git subtree split --prefix=.wiki/sources -b split-wiki-sources git subtree split --prefix=.wiki/concepts -b split-wiki-concepts git subtree split --prefix=.tasks -b split-tasks


Acceptance: `git branch | grep split-` показывает 4 ветки. Каждая `git log split-<x>` содержит коммиты только относящиеся к соответствующему dir'у. **Не push'ить** эти ветки — они служебные, нужны только для local subtree-add в admin.

Done — пометить 🟢 + переходить к `admin-subtree-import-and-cleanup`.
**Branch:** n/a
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-21T10:22:50.307Z -->

---

## 🟢 [admin-subtree-import-and-cleanup] — Шаги 3-7 миграции: импорт 4 split-веток из MoreThenCms в admin (clean prefixes через `git subtree add`, populated prefixes — temp-prefix dance с `git subtree add` + `git mv` для history-preservation через merge-commit + rename; read-tree вариант spec'a отвергнут — не сохраняет parent-link к morecms history → `git log --follow` не доходит до morecms-era коммитов), cleanup CMS-следов из mixed splits, STATUS.md merge, index.md regen, push на Gitea.

Объединено в одну таску потому что: тот же agent, та же session, sequential steps без natural review-point между ними. См. `.wiki/concepts/admin-infra-project.md` §Migration mechanics шаги 3-7.

**Status:** done
**Closed:** 2026-05-21 — все 5 шагов отработаны (3a clean prefixes subtree-add для 6 entities + 3 sources; 3b mixed temp-prefix dance для 18 concepts + 8 tasks files с git mv → renames preserve history через merge-commit; 4 cleanup 10 CMS files + 1 stale bootstrap-manifest.md; 5 STATUS.md merge — 7 admin done blocks imported, 4 CMS excluded; 6 index.md regen — 6 entities, 18 concepts, 3 sources; 7 push). **Finding для verify-migration:** `git log --follow .wiki/concepts/<slug>.md` показывает только rename-merge commit, не bridges в morecms-era history (git --follow limitation, не reliably crosses subtree-merge graft даже при rename-detection). Alternative для full history: `git log --all -- <bare-filename>` walks merge graph и находит morecms-era commits. Acceptance criterion 5 verify-migration требует уточнения в close-note этой таски.
**Where I stopped:** (not started)
**Next action:** **3a. Clean prefixes:**
```powershell
cd ~/projects/.admin
git remote add morecms ~/projects/MoreThenCms
git fetch morecms
git rm .wiki/entities/.gitkeep .wiki/sources/.gitkeep
git commit -m "prep: clear gitkeep before subtree import"
git subtree add --prefix=.wiki/entities morecms/split-wiki-entities
git subtree add --prefix=.wiki/sources  morecms/split-wiki-sources

3b. Mixed prefixes (.wiki/concepts/ and .tasks/ already have content):

Preferred — git read-tree merge:

git rm .wiki/concepts/.gitkeep
git commit -m "prep: clear concepts gitkeep before import"
git fetch morecms split-wiki-concepts
git read-tree --prefix=.wiki/concepts/ -u morecms/split-wiki-concepts
git commit -m "import: .wiki/concepts/ from MoreThenCms via subtree-split (history preserved via read-tree)"

git fetch morecms split-tasks
git read-tree --prefix=.tasks-imported/ -u morecms/split-tasks
git commit -m "import: .tasks/ from MoreThenCms via subtree-split (staged at .tasks-imported/)"
# Затем merge content with admin's existing STATUS.md (см. шаг 5).

Альтернатива при конфликтах — temp-prefix dance (.tmp-concepts/, git mv, git rm -r .tmp-concepts).

4. Cleanup CMS-следов:

git rm .wiki/concepts/cms-admin-assets-root-folder-seed.md `
       .wiki/concepts/cms-config-rewrite-pattern.md `
       .wiki/concepts/cms-maljarka-https-mode-crash.md `
       .wiki/concepts/cms-server-port-leak-fix.md `
       .wiki/concepts/webconfig-password-xml-escape.md `
       .tasks-imported/cms-admin-assets-root-folders-seed.md `
       .tasks-imported/cms-admin-assets-root-folders-seed.inserted-rows.txt `
       .tasks-imported/cms-maljarka-https-mode-bug-fix.md `
       .tasks-imported/cms-port-leak-fix.md `
       .tasks-imported/traefik-maljarka-502-bug.md
git commit -m "cleanup: drop CMS files imported via subtree (they stay in MoreThenCms)"

5. STATUS.md merge: скопировать non-CMS таски из .tasks-imported/STATUS.md в admin/.tasks/STATUS.md (сохраняя 🟢 done статусы); удалить .tasks-imported/; rename per-task files в .tasks/. Commit tasks: merge imported MoreThenCms task history with admin live agenda.

6. index.md regen: обновить .wiki/index.md чтобы catalog отражал импортированные entities/concepts/sources. Commit wiki(index): refresh catalog after subtree import.

7. Push: git push origin master.

Acceptance:

  • git log --follow .wiki/concepts/wd40efax-smr-cascade.md показывает MoreThenCms-era коммиты (history preserved)
  • ls .wiki/entities/ содержит все 6 файлов
  • ls .wiki/concepts/ содержит 18 admin-domain + admin-infra-project (новый)
  • cat .tasks/STATUS.md содержит и live agenda (pointers + impl + secrets + roadmap), и imported 🟢 done items
  • Gitea web shows updated tree

Done — 🟢; unblock morecms-cleanup-and-breadcrumb + resilience-roadmap-design. Branch: master


🟢 [morecms-cleanup-and-breadcrumb] — Шаги 8-10 миграции: в ~/projects/MoreThenCms удалить мигрированные файлы (6 entities + 17 concepts + 3 sources + 7 tasks), обновить STATUS.md/index.md, добавить .wiki/concepts/migrated-infra-to-admin.md breadcrumb со списком «что куда уехало», push на Gitea.

Closed: 2026-05-21 — все 3 шага отработаны (8 cleanup commit 1c2b8738: 6 entities + 17 admin concepts + 3 sources + 7 admin tasks + NEXT-SESSION-PROMPT.md removed, STATUS.md trimmed to 4 CMS blocks, index.md catalog regenerated; 9 breadcrumb commit 7c818cef: migrated-infra-to-admin.md created с table-mapping всех 31 файла + "what stays here" section + linked from STATUS.md header & index.md; 10 push: оба коммита pushed на git.kzntsv.site/OpeItcLoc03/MoreThenCms).

См. .wiki/concepts/admin-infra-project.md §Migration mechanics шаги 8-10 + полный шаблон breadcrumb'а в §9.

Очерёдность: ТОЛЬКО после успешного push'а admin'а с импортированным контентом (admin-subtree-import-and-cleanup 🟢). Иначе риск удалить из MoreThenCms то что ещё не зафиксировано в admin.

Status: ready Where I stopped: (not started) Next action: 8. Cleanup commit (drop migrated):

cd ~/projects/MoreThenCms
git rm .wiki/entities/* .wiki/sources/* `
       .wiki/concepts/wd40efax-smr-cascade.md `
       .wiki/concepts/hyper-backup-structure-and-recovery.md `
       .wiki/concepts/vbox-windows-stability-tuning.md `
       .wiki/concepts/mssql-container-data-restore.md `
       .wiki/concepts/traefik-on-windows-docker-desktop.md `
       .wiki/concepts/traefik-tcp-passthrough-vs-starttls.md `
       .wiki/concepts/traefik-file-watch-wsl2-broken.md `
       .wiki/concepts/docker-host-loopback-detect.md `
       .wiki/concepts/compose-bcrypt-escape-trap.md `
       .wiki/concepts/portainer-2.21-admin-password-regression.md `
       .wiki/concepts/db-tls-self-signed-via-traefik-raw-tcp.md `
       .wiki/concepts/rusonyx-vps-onboarding-quirks.md `
       .wiki/concepts/registry-gc-mount-and-modify-flag.md `
       .wiki/concepts/verdaccio-prune-semantics.md `
       .wiki/concepts/recovery-architecture-snapshot.md `
       .wiki/concepts/future-resilient-architecture-goals.md `
       .wiki/concepts/iis-migration-2026-05-19-postmortem.md `
       .tasks/vds-kzntsv-bootstrap.md .tasks/vds-gc-cron.md `
       .tasks/vds-backup-rsync-kreknin.md .tasks/vds-ntfy-push.md `
       .tasks/nas-recovery.md `
       .tasks/iis-on-host-migration.md .tasks/iis-traefik-dead-routes-cleanup.md

Manually edit .tasks/STATUS.md — оставить только CMS-domain блоки (cms-*, traefik-maljarka-502-bug, cms-maljarka-https-mode-bug-fix). Manually edit .wiki/index.md — удалить мигрированные entries из catalog.

Commit: migrate: drop infra content moved to OpeItcLoc03/admin (history preserved there via subtree split).

9. Breadcrumb: создать .wiki/concepts/migrated-infra-to-admin.md по шаблону в .wiki/concepts/admin-infra-project.md §9 (frontmatter + Migration table + "What stays here"). Также добавить запись в .wiki/index.md под Concepts.

Commit: wiki(concepts): add migrated-infra-to-admin breadcrumb.

10. Push: git push origin master.

Acceptance:

  • git log --oneline -3 в MoreThenCms показывает 2 cleanup-коммита поверх master
  • ls ~/projects/MoreThenCms/.wiki/entities/ пуст
  • ls ~/projects/MoreThenCms/.wiki/concepts/cms-*.md показывает 4-5 CMS файлов (включая webconfig-password-xml-escape)
  • Gitea web для MoreThenCms показывает обновлённую .wiki/concepts/migrated-infra-to-admin.md

Done — 🟢; unblock verify-migration. Branch: n/a


🟢 [verify-migration] — Шаг 11 миграции: end-to-end sanity check после migration push'ей. Подтвердить что projects-meta cache видит admin's tasks, knowledge_search находит migrated content, history preserved (git log --follow), и admin'ская архитектура целостная.

Closed: 2026-05-21 — validation matrix отработана, миграция структурно целостная. Acceptance summary:

  • 1: admin cache @11:00:45Z — 16 tasks total, 3 active/blocked (verify-migration active, secrets-manager-adopt blocked, admin-infra-project-review blocked). All 7 imported done tasks + admin-infra-project-pointers + impl-чейн присутствуют.
  • 2: MoreThenCms cache — 4 tasks (cms-admin-assets-root-folders-seed, cms-port-leak-fix, traefik-maljarka-502-bug, cms-maljarka-https-mode-bug-fix). 0 active. Никаких infra-таск.
  • ⚠ 3: knowledge_search ищет только shared projects-wiki (15 страниц). Admin's .wiki/ НЕ indexed by knowledge_search. Spec assumption mis-scoped — migration здесь ни при чём. Finding F2.
  • ⚠ 4: same as #3.
  • ⚠ 5: git log --follow .wiki/concepts/wd40efax-smr-cascade.md показывает только rename-merge commit (c4041823). Полная morecms-era history reachable через git log --all -- wd40efax-smr-cascade.md19422352 docs(.wiki): ingest NAS recovery session 2026-05-18/19. Git --follow limitation across subtree-merge graft. Finding F3.
  • 6: MoreThenCms git log -- .wiki/concepts/wd40efax-smr-cascade.md → последний commit = 1c2b8738 migrate: drop infra content... (deletion), предыдущий = original ingest. History preserved with cleanup tail.
  • ⚠ 7: browser-test git.kzntsv.site/OpeItcLoc03/admin — user verification (agent не может).

Findings (not migration-blocking — verification surface issues):

  • F1: mcp__projects-meta__tasks_aggregate project: "OpeItcLoc03/admin" returns empty массив несмотря на 16 tasks в cache. Cache содержимое верное (проверено node -e чтением ~/.cache/projects-mcp/tasks.json). Tool quirk — likely owner whitelist (gitea_owners в auth.toml не включает OpeItcLoc03). Filter logic skips not-whitelisted owners даже при явном qualified name. Follow-up task: добавить OpeItcLoc03 в gitea_owners (related agenda task: migrate-auth-toml-per-machine).
  • F2: knowledge_search не покрывает project-level wikis. Чтобы admin's .wiki/concepts/*.md стали discoverable через knowledge_search, нужны либо отдельные knowledge_ingest циклы в shared projects-wiki, либо gitea-side index integration. Spec verify-migration acceptance criteria 3-4 переадресуется на отдельную follow-up task.
  • F3: git log --follow не bridges subtree-merge graft. Workaround: git log --all -- <bare-filename> для полной cross-repo history. Spec'у admin-infra-project.md §Migration mechanics нужно обновить acceptance criterion 5 на этот альтернативный command.

Migration structural integrity confirmed. Findings F1-F3 → отдельные follow-up tasks при необходимости (см. секцию «Post-verify findings» ниже).

См. .wiki/concepts/admin-infra-project.md §Migration mechanics шаг 11.

Status: blocked Where I stopped: (not started) Next action: Sync cache:

node ~/projects/.common/lib/projects-meta-mcp/dist/sync.js

Validation matrix:

  1. mcp__projects-meta__tasks_aggregate project: "OpeItcLoc03/admin" — должен вернуть все live admin tasks ([admin-infra-project-pointers], impl-таски, [resilience-roadmap-design], [secrets-out-of-common], [secrets-manager-adopt], review-umbrella).
  2. mcp__projects-meta__tasks_aggregate project: "OpeItcLoc03/MoreThenCms"НЕ должна возвращать infra-таски (vds-, nas-recovery, iis-). Должна показывать только CMS-domain ([cms-maljarka-https-mode-bug-fix]).
  3. mcp__projects-meta__knowledge_search query: "WD40EFAX SMR cascade" domain: "all" — должен находить OpeItcLoc03/admin:.wiki/concepts/wd40efax-smr-cascade.md.
  4. mcp__projects-meta__knowledge_search query: "migrated infra admin" domain: "all" — должен находить MoreThenCms breadcrumb.
  5. cd ~/projects/.admin && git log --follow .wiki/concepts/wd40efax-smr-cascade.md — показывает MoreThenCms-era коммиты (proof history preserved).
  6. cd ~/projects/MoreThenCms && git log -p .wiki/concepts/wd40efax-smr-cascade.md — последний коммит = удаление (migrate: prefix).
  7. Открыть https://git.kzntsv.site/OpeItcLoc03/admin в браузере — wiki/tasks visible.

Findings, если есть, — отдельные follow-up tasks через mcp__projects-meta__tasks_create.

Done — 🟢 с close-note типа verified end-to-end: tasks+wiki visible in admin, MoreThenCms clean, history preserved. Branch: n/a


🟢 [resilience-roadmap-design] — Развернуть placeholder .wiki/concepts/future-resilient-architecture-goals.md (мигрирует из MoreThenCms в admin-subtree-import-and-cleanup) в полноценный fault-tolerance roadmap. Естественная отправная точка для глобальной цели пользователя: «создать отказоустойчивое решение, чтобы такие проблемы как 2026-05-18 не повторялись».

Это design task (не impl): продукт — обновлённая concepts/future-resilient-architecture-goals.md + потенциально цепочка impl-тасок-children. См. concepts/admin-infra-project.md §Initial admin agenda E.1.

Status: done Closed: 2026-05-21 — roadmap expanded via workshop pass 1 (interactive session с user). Output:

  • Doc concepts/future-resilient-architecture-goals.md расширен новой секцией §Workshop pass 1 (per-service RTO/RPO matrix для 10 сервисов, CMS backup 2-фазный план, IIS migration track, 4 secondary topics с recommendations без impl-таск). Старый placeholder сохранён как §Pre-workshop placeholder для history.
  • Top-3 impl-tasks filed: cms-stopgap-backup-daily (Фаза 1 — plug RPO=∞), mssql-minio-migration-to-vds (Фаза 2 enabler — achieves RPO 1ч), windows-hosting-vendor-research (design-таска для IIS-переезда).
  • 4 next-phase topics документированы без impl-task (создавать когда руки дойдут): cloud-offsite-backup-yandex-object (defer пока kreknin off-site), monitoring-uptime-kuma-deploy (high-priority, желательно ДО следующего host-incident'а), runbook-coverage-matrix-design (после migrations), network-mwan3-4g-failover (defer пока провайдер стабилен).
  • Key user-decisions зафиксированы: CMS RTO 4ч / RPO 1ч (не 1ч/15мин — overkill); MSSQL+MinIO мигрируют на VDS (не Windows-side); MSSQL Always-On AG отвергнут (log shipping достаточно); IIS долгосрочно умирает с snolla-on-node, переходный — managed Windows hosting; cloud-offsite — Yandex Object Storage когда понадобится.
  • Acceptance per spec (user подтвердил план + concrete impl-tasks top-3 + документ committed) — выполнено.

Unblocks admin-infra-project-review (была последним blocker'ом). Where I stopped: (done) Next action: Прочитать pointers (через admin-infra-project-pointers). Затем:

  1. Inputs read: concepts/recovery-architecture-snapshot.md (текущее состояние request flow + SPOF list), concepts/future-resilient-architecture-goals.md (placeholder с rough goals), entities/* (что есть в стеке физически).

  2. Expand roadmap — заполнить:

    • RTO/RPO targets per service (client websites, gitea, verdaccio, registry, ntfy, traefik, DB park). Recovery Time Objective vs Recovery Point Objective отдельно по каждому. Сейчас effective RTO для NAS-loss был ~15 часов (15h до восстановления через recovery VM), RPO = 1 day (Hyper Backup daily). Какой target ставим?
    • 3-2-1 backup strategy — 3 копии, 2 разных media, 1 off-site. Сейчас: VDS-data → kreknin (1 off-site daily rsync ), но recovery-host data → ?, client sites content → ?, source code → gitea+kreknin-mirror?
    • Multi-host strategy — клиентский прод сейчас на recovery-host (single point of failure). Опции: secondary IIS host (cold/warm standby), full migration на VDS (cms-on-VDS option), bare-metal колокация. Trade-offs стоимости / сложности / RTO.
    • Monitoring stack — сейчас минимально: ntfy push при backup-completion, ручная проверка через ssh. Опции: Uptime Kuma / Prometheus+Grafana / Synology Active Insight / external (Pingdom/UptimeRobot). Цель — early-warning для cascading failures (типа SMR-deg которая привела к 2026-05-18).
    • Runbook coverage — какие incident-сценарии должны иметь runbook'и (NAS-loss, VDS-loss, client-site-down, traefik-crash, cert-expiry, DB-corruption, ransomware-recovery). Какие уже есть фактически (post-mortem chronologies в sources/), какие missing.
  3. Output structure:

    • concepts/future-resilient-architecture-goals.md — обновленный (живой документ, не frozen).
    • Если roadmap большой — child concepts (concepts/resilience-rto-rpo-targets.md, concepts/resilience-monitoring-stack.md, etc.) с link'ами из основного.
    • Impl-tasks для каждого road-map item — отдельные таски через tasks_create (например [setup-uptime-kuma], [secondary-iis-host-cold-standby], etc.).
  4. Constraints: roadmap не review автоматически — это living document. Acceptance: user (vitya) подтвердил план, есть concrete impl-tasks для top-3 priority items, документ committed.

Done — 🟢 с close-note: «roadmap expanded; N impl-tasks created for first wave». Branch: n/a


🟢 [secrets-out-of-common] — Этап 1 secret-management фикса: вынести ~/projects/.common/secrets/ в ~/.config/projects-secrets/ (вне git-tree). Не блокирует migration (независимый workstream).

Why это дыра:

  1. .common/ — git-tracked tree. Один неаккуратный git add . из wrong dir / отключение gitignore rule → leak в историю.
  2. Synology Hyper Backup тащит весь ~/projects/ → секреты сидят в backup'ах на kreknin (и были на dead-DSM).
  3. IDE indexing / LSP / Cursor / Copilot — .env файлы в workspace видны редактору; в зависимости от расширений могут уйти в cloud.
  4. Любой find ~/projects | xargs grep — даёт plain-text secrets.

Этап 1 — быстрый фикс, low-risk, обратимый. Этап 2 (secrets-manager-adopt) — стратегический (encrypted-at-rest через pass).

См. concepts/admin-infra-project.md §Initial admin agenda E.2.

Status: done Closed: 2026-05-21 — миграция отработана.

  • 9 файлов скопированы в ~/.config/projects-secrets/: gitea-token.txt, interns.env, vds-kzntsv.env, kreknin.env, noreply-snolla-smtp.env (5 real secrets) + 3 examples + README. Real-files audit: найдено 6 (не 4 как в spec'е) — kreknin.env + noreply-snolla-smtp.env не были в исходном спецификации листинге.
  • Live code consumers (audit grep): только 2 — ~/projects/.common/lib/interns-mcp/interns_mcp/server.py (PATCH: env-override INTERNS_SECRETS_PATH → fallback ~/.config/projects-secrets/interns.env) + ~/projects/meeting-room/meeting_room/config.py (symmetric). gitea-token.txt / vds-kzntsv.env / kreknin.env / noreply-snolla-smtp.env — manual reference only (никто не читает кодом).
  • Safety filter expanded: interns-mcp/safety.py ALWAYS_ASK_PATTERNS добавил **/projects-secrets/** (canonical glob **/secrets/** не покрывает segment projects-secrets).
  • Skill updates (claude-skills repo): setup-interns 0.3.0 → 0.4.0 (MINOR — write target changed, gitignore-phase dropped), using-interns 0.2.0 → 0.2.1 (PATCH wording), .wiki/concepts/interns-design.md обновлён, dist/ rebuilt.
  • Verification: python -c "from interns_mcp.server import SECRETS_PATH; print(SECRETS_PATH)"C:\Users\vitya\.config\projects-secrets\interns.env, exists=True . Full MCP smoke требует session restart (в текущей сессии MCP bound к старому коду).
  • Cleanup: ~/projects/.common/secrets/ removed entirely (git rm for .gitignore + interns.env.example; real .env-файлы и так были gitignored). ls ~/projects/.common/secrets/ → ENOENT .
  • Commits: OpeItcLoc03/common@0169358 + OpeItcLoc03/claude-skills@ef3d38e.
  • Open follow-ups (not blocking):
    1. ⚠️ ~/projects/meeting-room — pre-existing dirty state (13 staged + UU conflict в .tasks/STATUS.md, не моя работа). Мой edit meeting_room/config.py сидит unstaged. Не коммитил. Нужно решение user: commit-after-unblock, manual stash, или discard. config.py edit critical — без него meeting-room runner read'нёт несуществующий .common/secrets/interns.env, env vars не загрузятся, и ${OLLAMA_CLOUD_API_KEY} placeholder останется unresolved в meeting-room/config/config.yaml.
    2. Doc/wiki references к .common/secrets/ остались в исторических файлах (.workshop/.archive/, vds-ops-mcp/.wiki/, MoreThenCms/.tasks/, books/.wiki/, etc.) — informational, не behavior. Bulk-update — отдельный chore-task если нужно.
    3. Cross-machine: на других машинах (если есть) повторить шаги migrate (mkdir -p ~/.config/projects-secrets/ + scp creds) + pull OpeItcLoc03/common + OpeItcLoc03/claude-skills.

Unblocks secrets-manager-adopt (etap-2). Acceptance per spec — выполнено (см. шаги выше).

Where I stopped: (done) Next action: 1. Audit: ls ~/projects/.common/secrets/ — какие файлы там лежат сейчас? Список ожидаемых из памяти:

  • interns.env — API keys для cheap LLM endpoints
  • gitea-token.txt — admin-scoped Gitea token (admin-scope per memory)
  • vds-kzntsv.env — VDS creds (ssh + DB + portainer + ntfy)
  • возможно другие

2. Audit потребителей:

  • ~/projects/.common/lib/interns-mcp/ — где читается interns.env? Grep по interns.env или dotenv calls.
  • ~/projects/.common/lib/projects-meta-mcp/ — где читается token? Часто это ~/.config/projects-mcp/auth.toml отдельно (см. setup-projects-meta skill); но возможны cross-refs.
  • ~/.claude.json mcpServers.* — где ENV/args ссылаются на .common/secrets/*?
  • ~/projects/.common/secrets/vds-kzntsv.env — кто его читает (scripts? local docs only?).

3. Migration:

mkdir -p ~/.config/projects-secrets
cp -r ~/projects/.common/secrets/* ~/.config/projects-secrets/
chmod 600 ~/.config/projects-secrets/*  # paranoid; Windows ACL отдельно

4. Update consumers: для каждого identifier'а из шага 2 — поменять path на абсолютный ~/.config/projects-secrets/<file> или принять env-override (рекомендуется env-override: легче тестировать на других машинах).

5. Verify: в чистой сессии claude --reload + проверить что mcp__interns__* и mcp__projects-meta__* всё ещё работают.

6. Cleanup .common/secrets/:

  • Удалить (rm -rf ~/projects/.common/secrets/)
  • НЕ оставлять stub-файлы или README — это вектор для regression'а.
  • Git: cd ~/projects/.common && git add -A && git commit -m "secrets: move out of common (now at ~/.config/projects-secrets/)" если .common git-tracked.

7. Cross-machine note: other machines (если есть) — повторить шаги 3-5 при первой возможности (вписать как note в close).

Acceptance: ls ~/projects/.common/secrets/ returns ENOENT, MCP servers still work, no .env references to .common/secrets/ в коде/configs.

Done — 🟢; unblock secrets-manager-adopt. Branch: n/a


🟢 [secrets-manager-adopt] — Этап 2 secret-management: внедрить pass (gpg-based password-store) — encrypted-at-rest secrets + cross-machine sync через private Gitea repo с encrypted blobs.

Альтернатива — Bitwarden CLI (cloud vault, audit log). Recommend pass для one-user setup — нет cloud-dependency, gpg native, encrypted git-syncable.

Why этап 2 нужен: после secrets-out-of-common (этап 1) секреты лежат plaintext в ~/.config/projects-secrets/. Этап 1 убирает риски 1+3 (git leak + IDE indexing), но не риск 4 (любой filesystem read даёт plaintext) и не риск 2 (Hyper Backup на kreknin всё ещё тащит plaintext если ~/.config/ в scope, что зависит от backup config). pass закрывает оба — secrets зашифрованы на диске, расшифровка только в RAM при pass show.

См. concepts/admin-infra-project.md §Initial admin agenda E.3.

Status: done Closed: 2026-05-21 — encrypted-at-rest store live + cross-machine sync ready.

Components installed:

  • gpg key: RSA 4096, no expiration, fingerprint 0CB0B0190149295E8012B438E969C2C24FE3F59E, uid Victor Kuznetsov <vitya.kuznetsov@gmail.com>. User-managed passphrase.
  • pass v1.7.4: manual clone https://git.zx2c4.com/password-store~/.local/src/password-store/, symlinked at ~/.local/bin/pass (PATH'е уже был ~/.local/bin). Deps: gpg , git , getopt . tree отсутствует (cosmetic; pass ls падает — non-critical).
  • gpg-agent config: ~/.gnupg/gpg-agent.confpinentry-program C:/Program Files/Git/usr/bin/pinentry-w32.exe + default-cache-ttl 2592000 (30 days) + max-cache-ttl 2592000. Один passphrase prompt в 30 дней (или после reboot).

Pass-store layout (5 entries):

Pass path Source plaintext Type
interns/ollama-cloud-api-key interns.env OLLAMA_CLOUD_API_KEY single value
gitea/admin-token gitea-token.txt single value
vds-kzntsv/full-env vds-kzntsv.env (whole file) multiline dotenv
kreknin/full-env kreknin.env (whole file) multiline dotenv
snolla-smtp/full-env noreply-snolla-smtp.env (whole file) multiline dotenv

Consumer integration:

  • mcp__interns__*~/.local/bin/interns-mcp-launcher.sh wrapper: export OLLAMA_CLOUD_API_KEY="$(pass show interns/ollama-cloud-api-key)"; exec python -m interns_mcp.server. Wired в ~/.claude.json mcpServers.interns (command: bash.exe, args: [/c/.../launcher.sh], env.INTERNS_PYTHON: /c/.../python.exe). Backup ~/.claude.json.bak-pre-pass-20260521-144943.
  • meeting-room CLI — config.py читает env-override (если установлен) → fallback на ~/.config/projects-secrets/interns.env (теперь нет). User pattern: export OLLAMA_CLOUD_API_KEY=$(pass show interns/ollama-cloud-api-key) перед meeting-room invocation. Documented в ~/.config/projects-secrets/README.md.

Cross-machine sync:

  • Private Gitea repo OpeItcLoc03/password-store-private (private=true) created via API. URL: https://git.kzntsv.site/OpeItcLoc03/password-store-private.git.
  • pass git init + remote add origin + push → 7 commits pushed (1 init + 5 inserts + 1 base).
  • Recovery на новой машине: install pass, gpg --import privkey.asc, git clone <url> ~/.password-store, pass show <any> для cache-warm. Documented в README.

Cleanup plaintext: ~/.config/projects-secrets/ — удалены 5 secret files (interns.env, gitea-token.txt, vds-kzntsv.env, kreknin.env, noreply-snolla-smtp.env). Остались marker README (rewritten под pass-pattern), .gitignore, 2 examples. Acceptance per spec — "пуст или содержит только README".

Verification:

  • pass show interns/ollama-cloud-api-key → 57-char OLLAMA value (matches original interns.env)
  • Wrapper smoke: timeout 3 bash ~/.local/bin/interns-mcp-launcher.sh </dev/null → EXIT=0, no startup error
  • All 5 entries decrypt without re-prompt (gpg-agent cache warm)
  • pass git status → clean, up to date with origin/master
  • ⚠️ Full mcp__interns__bulk_text_read smoke требует Claude Code restart (текущая сессия bound к старому Python процессу — у него env уже cache'нут).

Open follow-ups:

  1. meeting-room WIP unblock: etap-1 edit meeting_room/config.py сидит unstaged рядом с pre-existing dirty state (UU conflict в .tasks/STATUS.md + 13 staged files не моя работа). Required for runtime correctness — без edit'а meeting-room ищет несуществующий ~/projects/.common/secrets/interns.env. Опционально: добавить subprocess-pass fallback в config.py (pass show с timeout) для transparent integration без manual export. Решение user'а.
  2. setup-interns SKILL update (next task candidate): v0.4.0 описывает plaintext-.env write path как canonical. После etap-2 canonical — pass-based wrapper. Нужен SKILL bump 0.4.0 → 0.5.0 (MINOR): добавить gpg+pass prerequisite check, мигрировать Phase 5 write на pass insert, Phase 6 register wrapper-bash command. Большое обновление — отдельная task'а через tasks_create.
  3. New endpoints in interns/config.yaml: добавить новый <NAME>pass insert interns/<name-kebab> + добавить export <NAME>="$(pass show interns/<name-kebab>)" в wrapper-launcher.sh. Pattern documented в README.
  4. VDS secrets read on demand: non-interns secrets (vds/kreknin/smtp) — pass show <path>/full-env для просмотра, eval "$(pass show <path>/full-env | grep -v '^#')" для source в shell. Pattern documented в README.

Acceptance per spec: all 4 criteria met (~/.config/projects-secrets/ clean of secrets, consumers wired (wrapper + meeting-room pattern documented), pass git status clean, encrypted blobs в Gitea private repo).

Commits:

  • ~/.password-store/ (OpeItcLoc03/password-store-private): 7 commits pushed
  • ~/.claude.json: edited locally (config, not git-tracked beyond bak file)
  • New files: ~/.local/bin/interns-mcp-launcher.sh, ~/.gnupg/gpg-agent.conf, README rewritten at ~/.config/projects-secrets/
  • This STATUS.md update committed in OpeItcLoc03/admin

Where I stopped: (done) Next action: 1. Pre-install — gpg setup (Windows):

  • Install Gpg4win (https://www.gpg4win.org/) или через Scoop: scoop install gpg.
  • gpg --full-generate-key — выбрать RSA 4096, без expiration ИЛИ 2-year expiration с rotation reminder.
  • gpg --list-secret-keys — захватить fingerprint.

2. Install pass:

3. Initialize store:

pass init <gpg-fingerprint>
# Creates ~/.password-store/.gpg-id

4. Migrate secrets:

  • Для каждого file/var в ~/.config/projects-secrets/:
    • pass insert <category>/<name> → вводим plaintext, pass сохраняет как ~/.password-store/<category>/<name>.gpg
  • Примеры:
    • pass insert gitea/admin-token
    • pass insert vds-kzntsv/ssh-password
    • pass insert vds-kzntsv/portainer-password
    • pass insert interns/openai-key
    • pass insert interns/anthropic-key
  • Сохранить mapping (какой .env key какой pass-path) — concepts/secrets-pass-mapping.md ingest в admin wiki.

5. Update consumers — заменить direct file reads на shell-out calls:

  • interns-mcp config: вместо OPENAI_API_KEY=<plain> в env-файле → OPENAI_API_KEY=$(pass show interns/openai-key) в wrapper-скрипте startup.
  • ~/.claude.json mcpServers.* env: если поддерживает command-eval — pass show ...; иначе wrapper-script.
  • Альтернатива — pass-aware loader script ~/.config/projects-secrets/load.sh который populate'ит env vars из pass при session start.

6. Cross-machine sync: initialize private Gitea repo OpeItcLoc03/password-store-private (visibility=private), push ~/.password-store/. На каждой машине: clone + gpg --import master key + работает.

7. Cleanup plaintext: удалить ~/.config/projects-secrets/* (содержимое мигрировано в pass). Оставить пустой dir + README указывающий на pass.

8. Verify: в чистой сессии все MCP / scripts работают через pass-resolved secrets.

Acceptance: ls ~/.config/projects-secrets/ пуст или содержит только README, все consumers работают, pass git status clean, encrypted blobs в Gitea private repo.

Done — 🟢; secret-management dyra closed. Blocker: secrets-out-of-common Branch: n/a


🟢 [admin-infra-project-review] — Code-review checkpoint для брейнсторма admin-infra-project (промоушен 2026-05-21).

Closed: 2026-05-21 — review pass executed from .workshop/ cwd по explicit user request (нормально должен бы из ~/projects/.admin/). All 8 impl-tasks verified против spec acceptance criteria через 4 параллельных verification subagent'а: (A) migration completeness — admin 6 entities / 3 sources / 18 concepts; MoreThenCms cleanup чистый; breadcrumb table 28 entries полная; (B) history preservation — git log --all -- <bare> достаёт MoreThenCms-era коммиты (23 для wd40efax-smr-cascade, 51 для vds-kzntsv-bootstrap), --follow ограничен subtree-merge graft (matches F3); git fsck чистый; 4 split-branches в MoreThenCms не push'нуты как и планировалось; (C) secrets etap-1 — ~/projects/.common/secrets/ ENOENT, нет grep-references; secrets etap-2 — 5 entries в ~/.password-store/ декриптятся, wrapper ~/.local/bin/interns-mcp-launcher.sh wired в ~/.claude.json, OpeItcLoc03/password-store-private synced (7 commits), gpg fingerprint 0CB0B019…59E, 30-day gpg-agent cache; (D) commits-vs-spec — все ~30 admin коммитов мапятся на spec steps, archive→spec refinement (read-tree → temp-prefix dance) задокументирован coherently, нет admin-* скилов в claude-skills (anti-pattern избежан), CLAUDE.md 10 lines + follow tdd-criteria отсутствует.

No new findings. F1F3 из [verify-migration] остаются documented там: F1 (tasks_aggregate project: "OpeItcLoc03/admin" empty — likely projects-meta-mcp gitea_owners whitelist bug, не admin defect), F2 (knowledge_search не indexes project-level wikis — spec assumption error в verify-migration criteria 3-4, не impl defect), F3 (git log --follow не bridges subtree-merge graft; workaround git log --all -- <bare-filename> подтверждён working).

Subagent A false-positive caught: breadcrumb-таблица была флагнута как incomplete (8 listed / 11 admin task files), но 3 разницы (cms-stopgap-backup-daily, mssql-minio-migration-to-vds, windows-hosting-vendor-research) — admin-native таски от resilience-roadmap-design workshop pass 1, не migration-targets. Direct read breadcrumb-файла подтвердил полноту (28 entries).

Process note: Workshop CLAUDE.md §«Агент в .workshop/ сам код-ревью не делает» нарушен по user override. Следующий review-цикл лучше гонять из cd ~/projects/.admin/ для clean CLAUDE.md / MCP binding и для соблюдения «не имплементер»-принципа на process-level (хотя current reviewer не был имплементером ни по одной из 8 импл-таск).

User confirmed close by inspection: "Close review as no-new-findings (Recommended)".

Спецификация: .wiki/concepts/admin-infra-project.md (ingested 2026-05-21). Pre-impl bootstrap: admin-infra-project-pointers — заполнил .wiki/CLAUDE.md Domain conventions design-context pointer'ами. Без неё review бы читал stub. Импл-таски (review против их acceptance criteria): morecms-subtree-split, admin-subtree-import-and-cleanup, morecms-cleanup-and-breadcrumb, verify-migration, resilience-roadmap-design, secrets-out-of-common, secrets-manager-adopt.

Кто делает: не имплементер. Следующая сессия в этом проекте (другая модель / другой день / другой агент) поднимает таску с чистым контекстом. «Я только что это написал» bias = главный риск.

Чек-лист ревью:

  • Прочитать спецификацию concepts/admin-infra-project.md (acceptance criteria каждой импл-таски).
  • git log --oneline shipped-коммитов в ~/projects/.admin/ и ~/projects/MoreThenCms/ (по migrate:/cleanup:/wiki:/tasks: префиксам в commit-message).
  • Для migration-цепочки:
    • git log --follow на парах файлов admin VS MoreThenCms — history preserved per-file?
    • Нет ли CMS-файлов в admin (cleanup сработал)?
    • Нет ли admin-файлов в MoreThenCms (cleanup сработал)?
    • cat MoreThenCms/.wiki/concepts/migrated-infra-to-admin.md — breadcrumb корректный, все listings соответствуют реальности?
  • Для verify-migration — повторно прогнать его validation matrix; нет ли регрессий после resilience/secrets работ?
  • Для resilience-roadmap-design — roadmap расширен по существу (RTO/RPO с цифрами, не «TBD»; concrete impl-tasks для top-3) ИЛИ остался stub-<TBD>-placeholder fake-completion'ом?
  • Для secrets-out-of-commonls ~/projects/.common/secrets/ точно пуст? Git history .common/ не содержит leak'нутых secrets в недавних коммитах?
  • Для secrets-manager-adoptpass show actually works для всех migrated entries? ~/.config/projects-secrets/ пуст или README-only?
  • Сверить дизайн-decisions из спецификации со shipped state. Любое отклонение от §Migration mechanics / §Initial admin agenda / §Identity — flag.
  • Findings — отдельные follow-up tasks (<topic>-<gap>-fix или подобное) через tasks_create.

Закрытие: только когда все findings зафайлены ИЛИ ревьюер подтвердил «нет findings» в close-note.

Status: done Where I stopped: (done) Next action: Прочитать pointers (.wiki/CLAUDE.md §Domain conventions) → спецификацию (concepts/admin-infra-project.md). Для каждой импл-таски: git show <commit>, прогнать acceptance criteria из её next_action, сверить с design-decisions в спецификации. Findings → новые follow-up tasks через mcp__projects-meta__tasks_create. См. чек-лист в description выше.

Note: review не обязан проверять acceptance таск-children из roadmap-design (cms-stopgap-backup-daily, mssql-minio-migration-to-vds, windows-hosting-vendor-research) — review скопирует только design-task'у roadmap-design саму, проверка её output'а: roadmap расширен по существу (RTO/RPO с цифрами не «TBD») + top-3 impl-tasks filed. Branch: n/a


🟢 [mssql-minio-migration-to-vds] — декомпозирована 2026-05-22 на две независимые таски

Closed: 2026-05-22 — Discovery prep сессия (windows-side docker ps + sizes + VDS docker ps + stats) выявила что MSSQL и MinIO имеют разные мигратные паттерны (BACKUP/RESTORE vs mc mirror), разные окна простоя, разные rollback, разный risk profile (Express edition limit check vs MinIO 5-year upgrade). User decided: split на две таски.

Children:

  • mssql-vds-migration — MSSQL only, Express edition prod, SHRINKFILE-cleanup → BACKUP/RESTORE → traefik TCP mssql.kzntsv.site:1433.
  • minio-imgproxy-vds-migration — MinIO upgrade 2020-07-13 → latest + imgproxy + imgproxy-nginx, mc mirror, traefik HTTPS minio.vds.kzntsv.site (+ console subdomain).

Original umbrella .tasks/mssql-minio-migration-to-vds.md сохранён для history (acceptance/decisions/risks merged в children). Branch: n/a


🟢 [mssql-vds-migration] — DONE 2026-05-22: MSSQL Express 2022 live на mssql.kzntsv.site:1433, 5 DBs restored + IIS snolla site cutover ✓

Closed: 2026-05-22 — phase 1 (deploy + restore + cutover) complete; 8 prod hosts 200 OK через VDS MSSQL. SHRINKFILE skipped (BACKUP DATABASE сам не включает inactive log space → 26 GB source → 486 MB compressed .bak).

Executed steps:

  1. BACKUP DATABASE WITH COMPRESSION, COPY_ONLY, INIT на source для 5 DBs (3.17 sec на 235 MB MoreThenCms, total 486 MB)
  2. docker cpscp/opt/stacks/databases/mssql/backups/ on VDS (chown 10001:0)
  3. Traefik static config: добавлен mssql entrypoint :1433. Crit gotcha: docker compose restart НЕ применил port mapping change — нужен docker compose up -d (recreate). Симптом: port 1433 не listened.
  4. ufw allow 1433/tcp
  5. docker compose up -d MSSQL stack (Express PID, MEMORY_LIMIT 2048, self-signed TLS auto)
  6. RESTORE DATABASE WITH REPLACE, STATS=50 для 5 DBs — schema auto-upgrade 2019→2022 (versions 953→957)
  7. DBCC CHECKDB ... WITH NO_INFOMSGS, PHYSICAL_ONLY — clean
  8. Crit fix: server-level login snolla НЕ восстановился из .bak — CREATE LOGIN snolla WITH PASSWORD='<orig>', CHECK_POLICY=OFF + ALTER USER snolla WITH LOGIN = snolla в MoreThenCms (stostayer DB не имеет user 'snolla' — defer, stostayer.old site local-only).
  9. Edit C:\sites\snolla\Web.config: Data Source=localhostData Source=mssql.kzntsv.site,1433;...;TrustServerCertificate=True. Backup в .bak-pre-vds-cutover-20260522.
  10. IIS auto-recycle on web.config touch. Smoke 8 hosts 200 OK ✓ (emspb/snolla.com→on.snolla/pilorama98/labtools.ru+pro/tandemmebel/kupimknigi).

Findings закреплены в mssql-on-vds §Gotchas: (1) logins-vs-users orphan dance, (2) traefik recreate-not-restart для port mapping, (3) PowerShell не запускает pass bash-script → NULL pw + misleading "Login failed", (4) password generator excludeать +/= Base64 chars, (5) mssql uid 10001 ownership.

Open follow-ups:

  • Backup pipeline integration (mssql_dump_full daily + mssql_tx_log_backup hourly → RPO 1ч) — отдельная chore-task после первого backup green.
  • stostayer.old site (local-only :8091) — snolla user в stostayer DB отсутствует, conn-string ломан и до и после миграции. Не блокер — фикс через CREATE USER snolla FOR LOGIN snolla; ALTER ROLE db_owner ADD MEMBER snolla в stostayer DB если site реально кому-то нужен.
  • snolla-identity-manager site (Data Source=SRV-1135520-1\SQLEXPRESS) — мёртвая ссылка на чужой хост, defer audit.
  • 48ч soak window: source windows-host MSSQL container оставить running до 2026-05-24 для quick rollback.

Atomic revert: Copy-Item C:\sites\snolla\Web.config.bak-pre-vds-cutover-20260522 C:\sites\snolla\Web.config -Force → IIS auto-recycle. Branch: master


🟢 [minio-imgproxy-vds-migration] — Closed 2026-05-22 — VDS stack live as standby; CMS image pipeline остаётся на windows-host из-за hardcoded URL в closed-source DLL.

Where I stopped: Stack live на VDS (minio.vds.kzntsv.site + minio-console.vds.kzntsv.site + imgproxy.vds.kzntsv.site traefik routes ✓). Все buckets mirrored с format compat 2020→2025 verified (mc mirror through S3 API, не сырое filesystem). CMS image URLs пока рендерятся через windows-host imgproxy (DNS imgproxy.kzntsv.site всё ещё на windows IP).

Executed steps:

  1. pass insert -m minio-vds/full-env (preserve original MinIO creds + IMGPROXY_KEY/SALT — critical for HMAC signed URL compat)
  2. VDS: /opt/stacks/storage/minio-imgproxy/ + scp nginx.conf + write .env from pass + compose (3 services in proxy network)
  3. docker compose up -d → minio + imgproxy + imgproxy-nginx live; traefik labels auto-discovered
  4. External health checks 200 OK (minio/health/live, imgproxy /health, console UI 1309 bytes login page)
  5. mc 2025 setup + bucket prep (mc mb --ignore-existing per bucket — mc 2025 не auto-creates targets, в отличие от старых версий)
  6. mc mirror --preserve --quiet --overwrite old new --insecure 8 buckets (3 GiB / 19324 obj, ~1 час на 600 KiB/s uplink)
  7. Verify per-bucket — 3.0 GiB / 19324 obj match on both sides ✓
  8. Crit finding: один файл Albrecht Dürer ...tif (30 MiB, имя с umlaut) silent-skipped с exit=0 — retry того же mc mirror подтащил. Verify по count+size обязателен, не доверять exit code.

Findings закреплены в minio-imgproxy-on-vds §Gotchas: (1) MinIO upgrade 5y gap works via mc mirror (S3 API), не bind-swap (xl.meta format), (2) mc 2025 НЕ auto-creates buckets, (3) silent skip non-ASCII filename + slow uplink → verify по count, (4) IMGPROXY_KEY/SALT preserve обязательно (HMAC signed URLs), (5) MINIO_ROOT_USER/PASSWORD replace deprecated MINIO_ACCESS_KEY/SECRET — reuse same values.

Resolution 2026-05-22: MoreThenCms.Modules.Imgproxy.dll (closed-source, исходника в репо нет) содержит hardcoded https://imgproxy.kzntsv.site (decoded из DLL strings: /imgproxy;https://imgproxy.kzntsv.site/-ImgproxyHandler_Invoke). Архитектура: browser → traefik → IIS snolla → ImgproxyHandler → server-side GET к imgproxy.kzntsv.site → traefik windows-host → imgproxy-nginx → imgproxy → MinIO localhost:9000.

Sample URL (decoded): https://www.pilorama98.ru/imgproxy/<sig>/.../czM6Ly9waWxvcmFtYTk4L3Byb2R1Y3RzL2wv... → base64 = s3://pilorama98/products/l/9d428089-...jpg (MinIO bucket pilorama98).

Опции рассмотрены: A) hosts+cert trick + LE DNS-01 / B) local nginx-relay с self-signed + CallTrust unknown / C) keep as-is / D) decompile+recompile DLL. User decision: Option C — оставить как есть. Windows-host imgproxy/nginx/MinIO живут indefinitely; VDS stack — standby/backup target. Backup pipeline integration (rsync VDS MinIO → kreknin) — отдельная chore-task если понадобится.

Windows-host MinIO/imgproxy/imgproxy-nginx остаются running (не decommission). Decommission ALLOW только для windows-host MSSQL (см. iis-cutover-to-vds-services). Branch: master

Backup pipeline TODO: minio_mirror daily в /opt/stacks/backup/scripts/run.sh — rsync /opt/stacks/storage/minio-imgproxy/data → kreknin.

Atomic revert: cd /opt/stacks/storage/minio-imgproxy && sudo docker compose down -v (snapshot data via tar archived в acceptance step 3, source containers still running до 48ч decommission window). Branch: master


🟢 [iis-cutover-to-vds-services] — Closed 2026-05-22 — MSSQL cutover live (8 hosts 200 OK ✓); MinIO image-pipeline остаётся на windows-host (user decision Option C — hardcoded URL в closed-source DLL).

MSSQL phase complete:

  • Audit: C:\sites\snolla\Web.config обслуживает 11 CMS hosts (snolla catch-all *:8089). Только 1 conn-string на MoreThenCms DB, login snolla (не sa).
  • stostayer.old\web.config (local-only :8091) — defer (snolla user отсутствует в stostayer DB, conn-string ломан pre-migration уже).
  • stostayer\web.config уже на external www.stostayer.ru,1433 — не наш scope (внешний MSSQL).
  • snolla-identity-manager\web.configData Source=SRV-1135520-1\SQLEXPRESS (мёртвая ссылка на чужой хост, defer audit).
  • Edit Data Source=localhostmssql.kzntsv.site,1433;TrustServerCertificate=True + backup .bak-pre-vds-cutover-20260522
  • IIS auto-recycle на web.config touch (без iisreset).
  • Smoke 8 priority hosts: emspb/snolla.com→on.snolla/pilorama98/labtools.ru/labtools.pro/www.tandemmebel/kupimknigi — все 200 OK, latency 0.3-3.1s (tandemmebel самый медленный — большая страница).

MinIO image-pipeline phase: NOT migrated (user decision Option C):

  • MoreThenCms.Modules.Imgproxy.dll hardcoded https://imgproxy.kzntsv.site (strings analysis 2026-05-22).
  • DLL closed-source, исходника в репо нет; decompile+recompile = ad-hoc-fragile.
  • CMS image pipeline остаётся целиком на windows-host (imgproxy/nginx/MinIO + traefik route imgproxy.kzntsv.site → imgproxy-nginx:80).
  • VDS MinIO+imgproxy stack — standby / future use only. Backup target rsync — optional chore.
  • Decommission windows-host: TOLLY MSSQL container (после 48ч soak). MinIO/imgproxy/imgproxy-nginx — остаются.

48ч soak: monitor через ntfy vds-ops + manual smoke 24h/48h marks. Source windows-host MSSQL container оставить running до 2026-05-24 для rollback.

Decommission window (после 48ч green):

  1. windows-host: docker stop mssql (через неделю — docker rm + docker volume rm mssql_mssql_data)
  2. После DNS swap MinIO: docker stop minio imgproxy imgproxy-nginx
  3. Списать cms-stopgap-backup-daily cron — VDS backup pipeline покрывает после backup integration follow-up.

Atomic revert (full):

Copy-Item C:\sites\snolla\Web.config.bak-pre-vds-cutover-20260522 C:\sites\snolla\Web.config -Force
# IIS auto-recycle (или iisreset /restart)
# Убедиться windows-host MSSQL container running (если decommissioned — docker start mssql)

Status: in_progress (MSSQL ✓, MinIO DNS swap pending) Branch: master


🟢 [windows-hosting-vendor-research] — vendor selected: RUVDS (2026-05-22)

Status: done Selected: RUVDS Windows Server 2025 Core, 2GB RAM, 30GB HDD, 1IP (RDP ready). DC: Королёв. User selected outside research process. Next action: impl-таска iis-migration-to-ruvds (ready). Branch: n/a


🟢 [ruvds-backup-daily-kreknin] — daily rclone SFTP RUVDS → kreknin live (run #1 ✓ 2026-05-24 11:46:51, 20 sec, all 5 components synced)

Closed: 2026-05-24 11:46:51 — full pipeline live, ntfy success fired.

Components synced (5):

  • sites/snolla 8.8 GB / 44725 files (C:\sites\snolla\)
  • iis-config/ (applicationHost.config)
  • iis-backup-webconfiguration/ (Backup-WebConfiguration -Name daily-<date> export)
  • certs/ 14 PFX (LocalMachine\My private-key certs, pass ruvds-backup-pfx)
  • ssh-config/ (sshd_config + administrators_authorized_keys)

Path on kreknin: /volume1/NetBackup/ruvds-iis/<date>/. Retention 7 daily snapshots (pruned by rclone purge step 4).

Schedule: Task RUVDS-Backup-Daily, daily 04:30 MSK as SYSTEM, 2h timeout.

Notifications: ntfy vds-backup topic (shared с VDS backup для phone-side aggregation). Title differentiator: RUVDS backup OK/FAILED (<date>).

Scripts: scripts/ruvds-backup-daily-kreknin/setup.ps1 (one-time install: SSH key + rclone + configs + ScheduledTask) + run.ps1 (live backup logic) + README.

Findings (Decisions log):

  • rclone SFTP без host-key validation (rclone go-sftp library не parsит ssh-keyscan format корректно — key mismatch даже на fresh keys; key-auth = sufficient для нашего threat model)
  • Invoke-Rclone wrapper нужен — $ErrorActionPreference='Stop' + rclone NOTICE на stderr = PS terminating error; wrapper switches к Continue для rclone calls
  • Backup-WebConfiguration -Force параметр отсутствует на этой версии IIS module — используем Remove-WebConfigurationBackup если exists + plain Backup-WebConfiguration

Open follow-ups (не блокер):

  • PFX export pass ruvds-backup-pfx — temporary. TODO: pass-equivalent на Windows (gpg4win + pass-bash, или derived от machine-creds).
  • Retention prune ещё не сработал (1 snapshot today, < 7); проверится в day 8.

Atomic revert: см. scripts/ruvds-backup-daily-kreknin/README.md §Atomic revert.

Closes "Backup strategy для RUVDS IIS" Open question в [iis-migration-to-ruvds]. Branch: master


🟢 [iis-on-host-migration] — attempt 2 close-out: 36h soak passed, 8/8 наших sites зеленые

Status: done (2026-05-21 close-out). Detail: Phase 11 в iis-host-migration-2026-05-19. Close-out evidence: traefik Up 38h (zero restarts), w3wp 8.8h (scheduled pool recycle ~29h default, не crash), 8/8 наших sites вернули Microsoft-IIS/10.0 через full traefik HTTPS chain (emspb/snolla/on.snolla/pilorama98/labtools.ru/labtools.pro/tandemmebel/kupimknigi). Маljarka/sestech/ics-artmaterials оказались off-infra (DNS снят / parked / external nginx) — не наша инфра, не regression. Новая follow-up task iis-traefik-dead-routes-cleanup (низкий приоритет — почистить yml для 3 dead доменов). VM savestate: done 2026-05-21 05:13 MSK (45.6s, VMState=saved). Savestate file 1.73 GB (C:\Users\vitya\VirtualBox VMs\snolla-recovery\Snapshots\2026-05-21T05-12-43-636491200Z.sav). VBoxHeadless процессы освободили ~4 GB private memory. Resume: VBoxManage startvm snolla-recovery --type headless (~30 сек если понадобится). Disk delete (unregistervm --delete, ~95 GB) — позже через неделю uptime. Atomic revert (unchanged): Get-ChildItem C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\*.yml.bak-pre-attempt2-2026-05-19 | %{ Copy-Item $_.FullName -Destination ($_.FullName -replace '\.bak-pre-attempt2-2026-05-19$','') -Force }; Move-Item C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\stostayer.yml.disabled C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\stostayer.yml -Force -EA SilentlyContinue; Move-Item C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\oldstostayer.yml.disabled C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\oldstostayer.yml -Force -EA SilentlyContinue; docker restart traefik Branch: master


🟢 [iis-traefik-dead-routes-cleanup] — sestech + isc-artmaterials disabled, maljarka оказалась live

Status: done (2026-05-21 ~05:42 MSK; ~20 мин работы). Result:

  • sestech.yml.disabled (backup .bak-pre-dead-routes-cleanup-2026-05-21). DNS sestech.ru/www.sestech.ru → 31.31.205.163 = parking provider, наш traefik больше не отвечает (public requests идут на parking host через DNS).
  • isc-artmaterials.yml.disabled (backup created). DNS ics-artmaterials.com/www → 87.236.16.28 = external nginx-reuseport WP-хостинг, public traffic не через нас.
  • Maljarka — НЕ disabled — оказалось yml route'ит maljarka.tandemmebel.ru (subdomain тандеммебели), не голый maljarka.ru. DNS → 94.19.247.14 = наш IP, IIS backend отвечает 200 OK ("Малярка от Тандеммебель", 43KB). Live route, не dead. Изначальная task'а ошиблась с hostname. Smoke regression: 4 live hosts (emspb/on.snolla/www.tandemmebel/kupimknigi) → Microsoft-IIS/10.0 без regression. File-provider auto-reload, traefik restart не понадобился. Side finding: maljarka.tandemmebel.ru через traefik HTTPS → 502 Bad Gateway, хотя backend (host.docker.internal:8089 + Host header) возвращает 200 OK. Конфиг yml identical к работающему tandemmebel.yml. Live bug. Новая follow-up task traefik-maljarka-502-bug (CMS-domain — остаётся в MoreThenCms). Atomic revert: Move-Item C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\sestech.yml.disabled C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\sestech.yml -Force; Move-Item C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\isc-artmaterials.yml.disabled C:\Users\vitya\projects\docker\diskstation\traefik\data\custom\isc-artmaterials.yml -Force Branch: master

🟢 [vds-gc-cron] — weekly GC armed для verdaccio + registry на VDS

Status: done (2026-05-21 00:21 MSK; ~1ч работы). Детали в vds-gc-cron.md. Result: /etc/cron.d/vds-gc armed — Sun 03:00 MSK registry-gc.sh, Sun 03:30 MSK verdaccio-prune.sh. Scripts в /opt/stacks/gc/scripts/ (notify.sh, registry-gc.sh, verdaccio-prune.sh+verdaccio-prune.js). Logs в /var/log/vds-gc/. Notify через ntfy vds-ops. Verdaccio prune real run: 8.5G → 6.8G (freed 1.7G), 7021 packages scanned, 1551 modified, 4524 tgz deleted, 0 errors, 73 locally-published versions защищены (@snollajs/* + similar — detected via _distfiles[tgzName] absent). Atomic JSON rewrite (tmp + rename) + _rev bump. Registry GC smoke: alpine v1+v2 pushed → DELETE manifest → GC freed 61M (whole tree, since both tags pointed одну digest). Empty-storage guard added (first run на свежем registry — /var/lib/registry/docker/registry/v2/repositories отсутствует → skip+min-prio notify, не FAIL). Atomic revert: ssh vitya@89.253.255.94 'sudo rm /etc/cron.d/vds-gc; sudo systemctl restart cron; sudo rm -rf /opt/stacks/gc /var/log/vds-gc' Branch: master


🟢 [vds-backup-rsync-kreknin] — daily VDS→kreknin live, smoke green, cron armed

Status: done (2026-05-20 → 2026-05-21 00:00 first-run завершён успешно). Детали в vds-backup-rsync-kreknin.md. Result: rsync --link-dest pipeline /opt/stacks/backup/scripts/run.sh под cron 0 5 * * * (root) → kreknin /volume1/NetBackup/vds-kzntsv/<DATE>/ + symlink latest. DB dumps (pg/maria/mongo/redis), system config (/etc/ssh, ufw, hosts, docker), ssh keys, /opt/stacks data. Notification dual-channel: email через msmtp+Yandex (/etc/msmtprc) + ntfy publish на vds-backup topic. Retention 7 daily snapshots. Smoke run done — 71,867 files / 11.74G в 21m27s, exit 0. Cron service installed (apt install cron — Ubuntu 24.04 не имел его) + enabled. Next run автоматически 2026-05-21 05:00 MSK. SPOF gap status: closed for VDS — full restore from kreknin возможен (DB dumps + конфиги + ssh keys). Open: phone-side smoke (user verifies ntfy push пришёл из реального backup run). Atomic revert: ssh vitya@89.253.255.94 'sudo systemctl stop cron; sudo rm /etc/cron.d/vds-backup; sudo apt-get remove -y cron msmtp msmtp-mta; sudo rm -rf /etc/msmtprc /opt/stacks/backup /var/log/vds-backup' Branch: master


🟢 [vds-ntfy-push] — self-hosted ntfy.vds.kzntsv.site live, phone-verified

Status: done (2026-05-20 вечер; ~30 min работы). Detail в vds-ntfy-push.md. Result: ntfy v2.11.0 на ntfy.vds.kzntsv.site, LE cert (R12, valid до 2026-08-18), admin user vitya / Pryakhin9 (deny-all дефолт, admin role → rw to all), топики vds-backup + vds-ops. Phone-verified — оба push'а пришли в Android ntfy app (screenshot 22:54). Creds в ~/projects/.common/secrets/vds-kzntsv.env + /opt/stacks/ntfy/.env (chmod 600). Integration ready for: vds-backup-rsync-kreknin (publish status на vds-backup), monitoring scripts (на vds-ops). Atomic revert: ssh vitya@89.253.255.94 'cd /opt/stacks/ntfy && docker compose down -v && cd .. && rm -rf ntfy' + remove ntfy entries из vds-kzntsv.env. Branch: master


🟢 [nas-recovery] — клиентские сайты восстановлены, работают из публичного интернета

Status: done (2026-05-19 ~10:00 MSK — 15 часов работы) Final state: OpenWRT NAT → traefik 2.6.6 (host:8000/4443) → VirtualBox VM snolla-recovery (IIS + CMS) + docker-стек на хосте (MSSQL/MinIO/ES/imgproxy/nginx). 13 client routes, 40 LE certs валидны. Проверено: пользовательский клиент из публичного интернета открывает snolla.com, pilorama98.ru, labtools.ru/pro, tandemmebel.ru, emspb.ru. Backup pull: C:\nas-recovery\vm-sites\ (~11 GB inetpub/wwwroot + stayer/) — на случай если VM снова станет нестабильной. Open issues (минор):

  • X-Forwarded-Proto/Host: CMS делает redirect с портом 4443 в URL — нужно добавить middleware в traefik или включить trust в IIS.
  • MinIO/Azure storage — пользователь упомянул "не так всё", ждём пояснение.
  • docker-сокет проброс в traefik — daemon connection error (некритично, file-provider routing работает).
  • VM в bridged-WiFi была нестабильной → переехали на NAT + port forwards.
  • acme.json renewal через HTTP-01 фейлится для доменов с DNS не на нашем IP — нужно переключить на DNS-01 через REGRU (creds в env уже). Branch: master

🟢 [windows-host-fallback-backup-daily] — Closed 2026-05-24 — daily 03:00 MSK rclone SFTP windows-host → kreknin live; smoke #1 ✓ (4868 sec / 23 GB / 5 DBs)

Closed: 2026-05-24 15:25:09 — end-to-end pipeline verified, ntfy + email fired clean.

Components synced (6 paths) on kreknin /volume1/NetBackup/windows-host/2026-05-24/:

  • mssql/ 486 MB (5 .bak FULL+COMPRESSED+INIT: MoreThenCms 234.7 + StayerCalculator 49.8 + StayerPrice 6.3 + stostayer 193.6 + TireService 0.7)
  • sites/ 20 GB (C:\sites\* — snolla + stostayer.old + stostayer + snolla-identity-manager)
  • minio/ 3.1 GB (windows-host MinIO data dir)
  • traefik/ 1.1 MB (config + acme.json)
  • iis-config/ 68 KB (applicationHost.config)
  • iis-backup-webconfiguration/ 384 KB (Backup-WebConfiguration snapshot daily-<date>)

TOTAL: 23 GB / 4868 sec (~81 min) на home uplink → kreknin (1.5 TB used / 5.6 TB free).

Schedule: Task WindowsHost-Backup-Daily, daily 03:00 MSK as SYSTEM, Wake-To-Run enabled (машина просыпается из standby), 3h timeout. Sequential c RUVDS 04:30 + VDS 05:00 — все 3 host backups аккумулируются на kreknin за одну ночь.

Notifications: dual-channel — ntfy vds-backup topic (shared) + email Yandex SMTP 587 STARTTLS noreply@snolla.comvitya.kuznetsov@gmail.com. Subject windows-host backup <date> -- SUCCESS / FAILED.

Scripts: scripts/windows-host-fallback-backup-daily/setup.ps1 (one-time elevated install: SSH key + rclone + configs + ScheduledTask) + run.ps1 (backup logic, copy на RUVDS pattern с MSSQL backup steps) + README с decisions log.

Findings (Decisions log → scripts README):

  • rclone в C:\ProgramData\backup\rclone.exe (не Program Files) — non-admin staging без admin elevation.
  • icacls SID *S-1-5-32-544 (well-known Administrators) — locale-safe для ru-Windows (BUILTIN\Administrators не парсится на ru-locale).
  • MSSQL backup via sqlcmd 18 с -C — sqlcmd 18 enforces TLS даже на localhost (image 2019-latest имеет sqlcmd 18 в /opt/mssql-tools18/bin/).
  • MinIO data bind-mount path verified via docker inspect minio (host: C:\Users\vitya\projects\docker\diskstation\minio\data → container: /data).
  • MSSQL_SA_PASS в config.env plaintext (windows-host не имеет pass setup) — TODO pass-on-Windows long-term.
  • SSH key deployment to kreknin authorized_keys — via VDS pivot (windows-host не имеет direct SSH к kreknin — было резолвлено созданием dedicated key с deploy через ssh vitya@vds 'ssh -i kreknin-key vitya@kreknin echo ... >> authorized_keys').
  • Wake-To-Run на ScheduledTask критично — windows-host часто в sleep mode, backup в 03:00 MSK поднимает машину для прохода.
  • Pre-staging strategy — без admin запускают ssh-keygen + rclone install + key deploy; elevated setup.ps1 потом только Register-ScheduledTask + lock-down ACL. Минимизирует admin surface.

Open follow-ups (не блокеры):

  • Smoke recovery test (restore .bak в чистый MSSQL container + SELECT TOP 1) — deferred, не блокер для closure (= DR drill task, отдельно).
  • MSSQL_SA_PASS в config.env plaintext → pass-on-Windows long-term.
  • Phase B — periodic mirror active prod (RUVDS sites + VDS MSSQL/MinIO) → windows-host чтобы DR не возвращал stale. Decision after 1-2 недели Phase A.
  • Wake-To-Run verify в первое утро когда машина sleep'нет реально в 03:00 MSK.

Atomic revert: Unregister-ScheduledTask -TaskName 'WindowsHost-Backup-Daily' -Confirm:$false; Remove-Item C:\ProgramData\backup -Recurse -Force. На kreknin (via VDS pivot): rm -rf /volume1/NetBackup/windows-host. Удалить windows-host-backup pubkey из kreknin authorized_keys.

Closes "Backup strategy для windows-host warm-standby" — re-scoped из cms-stopgap-backup-daily (2026-05-21 spec) 2026-05-24 после migration MSSQL/sites + user-decision держать windows-host как DR fallback.

Branch: master