Files
admin/.tasks/STATUS.md
vitya fd8bb13e75 resilience(roadmap): workshop pass 1 — close design-task; file top-3 impl-tasks
- close [resilience-roadmap-design] 🟢 — roadmap expanded via interactive workshop
  with user (per-service RTO/RPO matrix for 10 services, CMS backup 2-phase plan,
  IIS migration track, 4 secondary topics documented as recommendations)
- file 3 impl-tasks  ready (spawned by roadmap-design):
  - cms-stopgap-backup-daily — Phase 1, plug RPO=∞ via daily rsync to kreknin
  - mssql-minio-migration-to-vds — Phase 2 enabler, achieves RPO 1h target
  - windows-hosting-vendor-research — design-task for IIS migration off home host
- unblock [admin-infra-project-review] 🔵 (last blocker cleared)
- expand .wiki/concepts/future-resilient-architecture-goals.md with §Workshop pass 1
  (canonical) above original §Pre-workshop placeholder (historical)

Key user-decisions zafiksirovany in workshop:
- CMS RTO 4h / RPO 1h (not 1h/15min — overkill for one-person infra)
- MSSQL+MinIO migrate to VDS (not Windows-side managed hosting)
- MSSQL Always-On AG rejected — log shipping every 60min sufficient
- IIS long-term dies with snolla-on-node; transitional via managed Windows host
- Cloud off-site = Yandex Object Storage when needed (kreknin already off-site)
- Monitoring = Uptime Kuma on VDS, high-priority among not-filed

Acceptance per spec (user confirmed plan + concrete impl-tasks top-3 + doc committed) — met.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 16:25:04 +03:00

592 lines
60 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Admin Task Board
_Updated: 2026-05-21 (resilience-roadmap-design workshop pass 1 closed; 3 follow-up impl-tasks filed; review unblocked)_
<!--
Status legend:
🔴 Active — only one at a time
🟡 Paused — in progress, resumable
⚪ Ready — defined, not started
🟢 Done — kept until merged
🔵 Blocked — waiting on external input
-->
## 🟢 [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
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-21T10:21:59.082Z -->
---
## 🟢 [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:
```powershell
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-следов:**
```powershell
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
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-21T10:23:14.254Z -->
---
## 🟢 [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):**
```powershell
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
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-21T10:23:37.967Z -->
---
## 🟢 [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.md``19422352 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:**
```bash
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
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-21T10:23:54.407Z -->
---
## 🟢 [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
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-21T10:24:24.685Z -->
---
## 🟢 [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:**
```bash
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
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-21T10:24:48.748Z -->
---
## 🟢 [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.conf``pinentry-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:**
- MSYS2: `pacman -S pass` (если есть MSYS2)
- Scoop: `scoop install pass`
- Manual: pass is bash script — clone https://git.zx2c4.com/password-store/ и положить на PATH.
**3. Initialize store:**
```bash
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
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-21T10:25:14.759Z -->
---
## ⚪ [admin-infra-project-review] — Code-review checkpoint для брейнсторма admin-infra-project (промоушен 2026-05-21).
**Спецификация:** `.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-common``ls ~/projects/.common/secrets/` точно пуст? Git history `.common/` не содержит leak'нутых secrets в недавних коммитах?
- Для `secrets-manager-adopt``pass 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:** ready
**Where I stopped:** (not started — all blockers cleared 2026-05-21: resilience-roadmap-design 🟢, secrets-out-of-common 🟢, secrets-manager-adopt 🟢)
**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
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-21T10:25:43.430Z -->
---
## ⚪ [cms-stopgap-backup-daily] — Фаза 1 CMS backup. Plug текущую дыру RPO=∞ для CMS прод (sites + MSSQL + MinIO на windows-recovery-host). Ежедневный rsync на kreknin до пока MSSQL/MinIO не переедут на VDS через `mssql-minio-migration-to-vds` (тогда списываем этот pipeline).
Это **временное решение** — не строить ничего изощрённого. Cron через Windows Task Scheduler + rsync + ssh-pipe. Достигаемый RPO = 24ч (не target 1ч, но лучше чем ∞).
**Status:** ready
**Where I stopped:** (not started)
**Next action:** См. `cms-stopgap-backup-daily.md` — acceptance + key files + open questions. Order: (1) verify MinIO data dir location через `docker inspect minio`, (2) write PowerShell backup script с тремя ногами (mssql/sites/minio) + ntfy push, (3) test smoke recovery на MSSQL backup, (4) wire через Windows Task Scheduler daily 03:00 MSK, (5) document в `.wiki/concepts/cms-stopgap-backup-pipeline.md`. Spawn'd by [resilience-roadmap-design] workshop pass 1 2026-05-21.
**Branch:** n/a
---
## ⚪ [mssql-minio-migration-to-vds] — Фаза 2 CMS backup. Перенести MSSQL контейнер + MinIO с windows-recovery-host на vds-kzntsv. Achieves target RPO 1ч через интеграцию с существующим `vds-backup-rsync-kreknin` pipeline (tx log backups каждые 60 мин в существующий rsync→kreknin). Снимает SPOF с домашней машины для критических CMS-данных.
После завершения — списать `cms-stopgap-backup-daily` cron на windows-host.
**Status:** ready
**Where I stopped:** (not started)
**Next action:** См. `mssql-minio-migration-to-vds.md` — full acceptance (8 items) + risks + open questions. Major task (1-2 недели realistic). Разбить на sub-phases: (a) MSSQL container deploy на VDS + restore from backup, (b) connection string change + smoke 8 hosts, (c) MinIO migration через `mc mirror`, (d) backup pipeline integration в `/opt/stacks/backup/scripts/run.sh`. **Pre-cutover benchmark обязателен** (admin assets UI / search / catalogs) — WAN latency 10-30ms vs localhost. Spawn'd by [resilience-roadmap-design] workshop pass 1 2026-05-21.
**Branch:** n/a
---
## ⚪ [windows-hosting-vendor-research] — Design-таска (1 день). Выбрать вендора managed Windows hosting для будущего переезда IIS с windows-recovery-host (домашней машины). Output: 1-pager с recommended vendor + cost estimate + migration recipe outline.
Это **переходный** трек до завершения snolla node-rewrite'а (после которого IIS не нужен вообще). Не файлим impl-таску миграции до этой research'и — vendor выбираем сначала.
**Status:** ready
**Where I stopped:** (not started)
**Next action:** См. `windows-hosting-vendor-research.md` — constraints + candidates + acceptance. Steps: (1) research 4 candidates (Rusonyx / Selectel / FirstVDS / Beget), (2) fill comparison table по criteria (RDP / .NET 4.8 / IIS 10 / public IP / cost / SLA / reviews), (3) write top-1 recommendation с trade-off justification, (4) cost estimate monthly + one-time, (5) document в `.wiki/concepts/windows-hosting-vendor-comparison-2026.md`. **Не file** follow-up impl-task `iis-migration-to-managed-windows-hosting` — пусть future-agent file'нёт когда vendor выбран. Spawn'd by [resilience-roadmap-design] workshop pass 1 2026-05-21.
**Branch:** n/a
---
# Imported from MoreThenCms — 2026-05-21 subtree-split migration
Полное содержимое `.tasks/STATUS.md` из MoreThenCms на момент 2026-05-21, **CMS-domain блоки исключены** (остались в MoreThenCms): `cms-admin-assets-root-folders-seed`, `cms-port-leak-fix`, `traefik-maljarka-502-bug`, `cms-maljarka-https-mode-bug-fix`. История per-file сохранена через subtree-split + temp-prefix merge.
## 🟢 [vds-kzntsv-bootstrap] — VDS поднят, gitea/verdaccio/registry мигрированы (3/3 phases done 2026-05-20)
**Status:** done (2026-05-20 вечер — все 3 заявленные фазы закрыты)
**Where I stopped:** Phase 1 + Phase 2 завершены ✅. VDS `89.253.255.94 / vds.kzntsv.site` Ubuntu 24.04 upgraded через VNC. sudo vitya (NOPASSWD) + ssh-key + hardened sshd (key-only) + ufw 22/80/443 + DB-порты + fail2ban + docker 29.5.1 + buildx + compose. Traefik v2.11 на `traefik.vds.kzntsv.site` (basicAuth vitya:Pryakhin9). Portainer CE 2.21.5 на `portainer.vds.kzntsv.site`**админ-пароль был принудительно изменён на `Pryakhin9-VDS-2026` (18 chars)** из-за hard min-12-char policy в Portainer 2.21+ (regression от 2.20). API key получен и сохранён в `vds-kzntsv.env`. 4 DB-стека (Postgres 16, MariaDB 11.4, Mongo 7, Redis 7) подняты с self-signed TLS, доступны снаружи через traefik raw-TCP passthrough на `<db>.vds.kzntsv.site:<port>` (HostSNI(*) — traefik tls.passthrough+SNI не работает с STARTTLS-протоколами PG/MariaDB; rawTCP forward, DBs терминируют TLS сами). Все DB passwords (PG/Maria/Mongo/Redis) — strong random hex16, сохранены в `vds-kzntsv.env`.
**Open questions:** LE certs для DBs (сейчас self-signed → клиент verify-skip; permanent fix позже через lego sidecar extract из traefik acme.json).
**Next action:** Phase 3 миграция с kreknin **ВСЯ DONE ✅**. 3.1 gitea (132 repos, 4 users, v1.25.5 на git.kzntsv.site). 3.2 verdaccio (8.6G storage, 2063 packages, secret 32 chars). 3.3 registry — **отказались от миграции** старых 35G images (user: «новых наделать могу»), fresh install на registry.kzntsv.site + Joxit UI на registry-ui.vds.kzntsv.site (DELETE_IMAGES=true для GUI cleanup, REGISTRY_STORAGE_DELETE_ENABLED для API delete). Auth vitya:Pryakhin9 (см. vds-kzntsv.env). Follow-up tasks: vds-gc-cron, vds-backup-rsync-kreknin, vds-ntfy-push.
**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](../.wiki/sources/iis-host-migration-2026-05-19.md).
**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](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](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](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
---