Files
admin/.tasks/STATUS.md
vitya 063910e265 incident(books-vds-es): true RCA — ransom-бот через открытый :9200, не оператор
Рецидив вчерашнего ES-инцидента вскрыл истинную причину. Вчерашняя
гипотеза «оператор в cutover попал на canonical» опровергнута.

Root cause: ES (stack 33) публиковал 0.0.0.0:9200 мимо traefik. Free-ES
7.10 без auth → порт открыт всему интернету. Ransom-бот сносил индексы
by-name (мимо Control #1 destructive_requires_name), оставлял read_me с
BTC-выкупом. accessLog (Control #2) пуст — бот шёл прямо в порт, не через
traefik. firewalld бесполезен (docker-publish обходит INPUT-зоны).

Fix (Control #3): убрана публикация host-порта из stack 33 (Portainer
PUT), дыра закрыта; re-restore epz/products/artmone из daily-2026-05-25.

Отдельный баг: epz-поиск падал у ОБОИХ тенантов — getTenantIdSeller(
config.get("tenant")) через node-config, а tenant не задан ни в
default.json, ни в env-маппинге (TENANT env = мёртвый груз). Добавлен
tenant в overlay default.json (slovo/bookva). products работал —
отдельный код-путь.

accessLog откатан (сторожил не ту дверь).

- wiki concept: correction-блок + секция «Рецидив 2026-05-29» + exposure-audit
- tasks: restore-es reopened+reclosed; new  harden-books-vds-exposed-ports
- NEXT_SESSION handoff

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 09:31:25 +03:00

40 KiB
Raw Blame History

Admin Task Board

Updated: 2026-05-29 — РЕЦИДИВ того же инцидента, диагноз сменён. Вчерашняя гипотеза «оператор в cutover» опровергнута. Истинная причина: ES публиковал 0.0.0.0:9200 мимо traefik (free-ES без auth) → ransom-бот удалял индексы by-name (мимо Control #1), оставлял read_me с BTC-выкупом. accessLog (Control #2) пуст — бот шёл прямо в порт. Control #3 (fix): убрана публикация host-порта (Portainer PUT stack 33) → дыра закрыта; epz/products/artmone restore из daily-2026-05-25. Второй, отдельный баг: epz-поиск падал у ОБОИХ тенантов — config.get("tenant") (node-config) не задан ни в default.json, ни в env-маппинге, TENANT env был мёртвым грузом → добавил tenant в overlay default.json (slovo/bookva), резолвится, ошибки прекратились (products работал — другой код-путь). accessLog откатан (сторожил не ту дверь). Exposure-audit → harden-books-vds-exposed-ports. См. .wiki/concepts/es-destructive-delete-incident-2026-05-26.md § «Рецидив 2026-05-29». Updated: 2026-05-28 (вечер) — 🟢 restore-elasticsearch-indices-books-vds closed в ту же сессию через snapshot restore (46 сек) из daily-2026-05-25 (полные данные, ~2ч после оригинального reindex'а). RCA: 5 индексов (включая system .tasks) удалены через ES API DELETE _all за 1 сек на 2026-05-26 10:21 UTC, 1ч 11мин после создания bookva-es — оператор в cutover-prep попал на canonical вместо internal-only bookva-es. Caller identity не восстановим (audit log = X-Pack платный, traefik accessLog был выключен, Portainer audit = enterprise). 2 preventive фикса applied + verified: (1) ES env action.destructive_requires_name=trueDELETE _all / wildcard теперь 400; (2) traefik JSON accessLog в /letsencrypt/access.log — будущие DELETE оставят forensic след. См. таску + .wiki/concepts/es-destructive-delete-incident-2026-05-26.md. Updated: 2026-05-28 — prod incident: books-app slovo поиск товаров + EPZ сломаны, ES elasticsearch.kzntsv.site (books VDS stack 33) пуст (0 индексов из 3 ожидаемых; source elasticold.kzntsv.site rollback — все 928k docs на месте). Заведена restore-elasticsearch-indices-books-vds. Окно поломки: 27.05 10:24 → 28.05 13:59, в этом окне шла работа bookva-cutover-prep (bookva-es stack 37 создавался) — возможный конфликт. См. таску для playbook'а. Updated: 2026-05-28 — vds-kzntsv network-stack mismatch RESOLVED в 13:52 MSK. Сначала ~2.5ч активного outage (05:45-08:30) + ~5.5ч на эфемерной статике до окончательного fix хостером. Revised RCA: наш netplan+networkd поверх provider's expected ifupdown stack ломал их auto-recovery когда DHCP-binding разорвался на их стороне. Fix: systemctl mask netplan systemd-networkd (на running system, без stop — IP и SSH сохранились), Rusonyx ребутнули + положили чистый /etc/network/interfaces.d/ifcfg-eth0 с /18 netmask через свой start/ipadd procedure. Все 24 docker контейнера up. Disk after GC: 79% (132G→118G/158G). Anti-pattern закреплён: НЕ использовать netplan на Rusonyx VDS. Wiki updated с revised RCA + permanent-fix runbook + 2 quirks (#9 /18 layout, #10 ifupdown vs netplan). Updated: 2026-05-27 — modulair-rag-vds-redeploy 🟢 closed в ту же сессию: 4-контейнерный стек развёрнут на VDS (Portainer stack 15), acceptance 6/6. Образы пересобраны на самом VDS (push 3.36GB через traefik с дома падал 499); env/entrypoint/minio-host скорректированы под VDS-реальность. 3 follow-up'а переданы в modulair-rag handoff. Updated: 2026-05-27 — заведена modulair-rag-vds-redeploy (handoff из modulair-rag session). NAS-loss redeploy 4 контейнеров на VDS. Блокер MinIO снят — verified up на VDS 2026-05-27. postgres proxy-network reachability подтверждён (postgres:5432 резолвится из pipeline/mcp). Спека: modulair-rag concept nas-loss-vds-redeploy-context + compose.yml as-is. Updated: 2026-05-27 (ночь, после re-open) — ops-mcp multi-tenant activated (stack 26 PUT + BOOKVA_MARIADB_PASSWORD env). Smoke verified: slovo/bookva DB queries возвращают tenant-specific data (АФО2 vs Ира warehouses), bookva agendaJobs count=2818. Один host-level books-ops-mcp видит обе tenant DBs.

Updated: 2026-05-27 (ночь) — bookva-tenant deep-debug session. 6 багов одной природы (incomplete cutover-prep): (1) bookva-web без config volume → slovo DB leak; (2) bookva-{api,scheduler,task-runner} mount path /app→/usr/src/app; (3) NITRO/NUXT env не reference'или в compose; (4) bookva-scheduler без RECONCILER_ENABLED → dry-run handlers; (5) bookva-scheduler без healthcheck → CI timeout; (6) deploy.yml svc=job-scheduler vs secret=SCHEDULER mismatch. + composite agendaJobId display fix + books-web config mount (slovo also missed) + jobs.post.js agenda.db.collection→MongoClient. Ingested wiki concept tenant-overlay-config-volume-mount-path-pitfall (shared). ops-mcp multi-tenant код в image готов но stack 26 ещё не пере-PUT'нут (next session).

Updated: 2026-05-26 (поздний вечер) — books-ops-mcp-host-promote 🟢 closed в ту же сессию следом за заведением. Stack 26 (books-ops-mcp) in-place PUT в host-level compose без container churn. Stack 43 (bookva-ops-mcp) deleted. 2 Gitea secrets revoked. victor/books ae3ab14 + victor/bookva-overlay 50f5bbb + .admin host-stacks/books-vds/ops-mcp.compose.yml. ops-mcp теперь management-plane (manual Portainer).

Updated: 2026-05-26 (поздний вечер) — заведена books-ops-mcp-host-promote (deploy run#437/439 валился на bookva-ops-mcp restart-loop → hot-fix bookva-overlay 437d024 idle-stub interim → user ideology «books VDS = host, slovo/bookva = клиентские стэки» → promote books-docker-proxy-ro+books-ops-mcp в host-level stack, drop bookva-ops-mcp). Deploy run#440 зелёный.

Updated: 2026-05-26 (поздний вечер) — bookva-tenant-cutover-prep 🟢 closed — 6/7 в одну сессию: Steps 1+2+3+4+5+7. Step 6 delayed по spec (1-2 нед до cutover). bookva-ozon-mcp deferred (image не в registry). MinIO bucket copy deferred (cutover-time). 9 bookva Portainer stacks created (3 active: db/mongo/es internal-only; 6 stopped pre-cutover). bookva-overlay 4 commits: templates+mariadb:10.6+mongo:4.2+api internal-only+depends_on removed.

Updated: 2026-05-26 (вечер) — bookva-tenant-cutover-prep 🟡 paused, 4/7 steps done в этой сессии: Steps 1+5+7 closed, Step 3 partial (6 empty volumes + 4 populated). Maintenance-window remaining: Step 2 stacks, Step 3 stateful (Mongo/MariaDB/MinIO), Step 4 login-gate. bookva-overlay templates align'нуты к prod shape + design v3 (commit 5b173de pushed). books 75ea320 pushed (deploy/web.compose.yml embed-api refs). Pass entry gitea/victor-books-ci-bookva-overlay added.

Updated: 2026-05-26 — заведена bookva-tenant-cutover-prep (7-step prep на books VDS перед поднятием Bookva стека). Code-side done в victor/books master: embed-api с zod^4 fix, ES endpoint per-tenant, PWA redirect banner, deploy.yml per-tenant, overlay-репо finalize, ntfy per-tenant. Осталась только Portainer/Gitea-secrets/VDS работа.

Updated: 2026-05-25 (iis-migration-to-ruvds 🟢 closed Phase 1 per user decision — 9/24 hostnames live на RUVDS, source IIS оставлен running. migrate-elasticsearch-to-books-vds 🟢 closed ранее сегодня. 16 IIS hostnames + LE renewal pipeline + decommission — descoped в Closure note, не отдельные tracker tasks.)

🟢 [restore-elasticsearch-indices-books-vds] — closed 2026-05-28 — snapshot restore из kreknin:daily-2026-05-25 за 46 сек (epz=820604, products=105922, artmone=2621, counts == source). Forensics: 5 индексов удалены через ES API DELETE _all 26.05 10:21 UTC, через 1ч 11мин после создания bookva-es — оператор в cutover-prep попал на canonical вместо internal-only bookva. Caller identity unrecoverable (audit log = X-Pack платный, traefik accessLog был выключен). 2 preventive фикса applied: ES env action.destructive_requires_name=true + traefik JSON accessLog в /letsencrypt/access.log. См. restore-elasticsearch-indices-books-vds.md § Closure + .wiki/concepts/es-destructive-delete-incident-2026-05-26.md.

Branch: n/a


[harden-books-vds-exposed-ports] — закрыть публично торчащие host-порты на books VDS (89.253.255.133)

Status: ready Where I stopped: exposure-audit сделан (ES :9200 уже закрыт в рамках инцидента); mongo/books-db/bookva-db/minio/bookva-minio + rsync:873 торчат на 0.0.0.0, все credentialed (auth включён, не дыра-нараспашку как free-ES). Next action: по каждому сервису определить — приложение коннектится через docker-сеть или host-порт; нужен ли внешний admin (тогда SSH-туннель / DOCKER-USER IP-allowlist вместо публичного порта). Начать с mongo (EOL 4.2 + reuse пароля). Branch: n/a (admin ops)


🟢 [modulair-rag-vds-redeploy] — closed 2026-05-27 — 4-контейнерный стек развёрнут на VDS, acceptance 6/6

NAS помер → 4 контейнера пропали → fresh redeploy на VDS (89.253.255.94). Portainer stack id=15. Acceptance 6/6 green: 4 контейнера up без restart-loop; lightrag Uvicorn :9621; https://modulair-mcp.kzntsv.site→200; pipeline+tier1 0% CPU; pipeline-лог без postgres/minio ошибок.

Таска писалась по stale-предпосылкам — по ходу исправлено (см. modulair-rag-vds-redeploy.md Decisions): registry-образы отсутствовали (registry переустановлен 2026-05-20) → rebuild на VDS (push 3.36GB tier1-слоя с дома падал 499 через traefik → собрал на самом VDS, push локальный); MINIO_ENDPOINT=minio.vds.kzntsv.site (не minio.kzntsv.site=books VDS); traefik entrypoint httpswebsecure (NAS-имя в compose-лейбле); DB+bucket+scoped-svcacct созданы; ROUTERAI key в pass; DNS поправил user.

Follow-ups (не блокеры acceptance): modulair-rag/compose.yml entrypoint-фикс в репо; portainer-stack.md под VDS; lightrag embedding binding=ollama/model=None — проверить ROUTERAI_* маппинг. Все переданы в handoff (consumer repo / parallel session). Branch: master


🟢 [migrate-elasticsearch-to-books-vds] — closed 2026-05-25 — ES indices с Windows (elasticold.kzntsv.site, 7.10.1) → books VDS (elasticsearch.kzntsv.site, 7.10.0). 3 indices, 928616 docs. Reindex-from-remote через reindex.remote.whitelist env, добавленный в Portainer stack 33. Pre-migration snapshot в kreknin repo как rollback. Consumer configs sed'd, 3 containers restarted, smoke green. Source НЕ выключен.

См. migrate-elasticsearch-to-books-vds.md § Closure note.

🟢 [books-vds-stacks-to-portainer] — closed 2026-05-25 — 6 SSH-compose стеков на books VDS (89.253.255.133) мигрированы в Portainer-managed (endpoint 1, https://portainer.kzntsv.site). Skip traefik + portainer (management plane). Adapter scripts/books-vds-portainer-migration/migrate.sh. Wiki: portainer-stack-management-books-vds.md. Backup pipeline bind paths preserved (verified by inspection, full run pending next 06:00 MSK).

🟢 [books-vds-backup-daily-kreknin] — closed 2026-05-25 — daily backup books VDS (89.253.255.133, host4g.ru, CentOS 7) → kreknin в 06:00 MSK. DB dumps (mariadb/mongo×2) + ES snapshot via REST → rsync 4.87GB → ntfy/email (phone+inbox ✓). ES path.repo bootstrap + snapshot repo kreknin registered. См. books-vds-backup-daily-kreknin.md.

🟢 [unify-backup-notifications] — closed 2026-05-25 — единый формат push + email для VDS/RUVDS/windows-host (3 scripts), VDS run.sh импортирован в repo. См. unify-backup-notifications.md § Closed. windows-host self-deploy остаётся на user'е (elevated PS).


🟢 [bookva-tenant-cutover-prep] — closed 2026-05-26 — 7/7 (Step 6 descoped) + extension (bookva-minio + external port-bind)

Все ops-шаги VDS-side выполнены: Gitea secrets ✓ (7 шт), 10/11 Portainer bookva stacks ✓ (ozon-mcp deferred — image отсутствует в registry; bookva-minio добавлен post-closure), volumes ✓ (db+mongo cp -a, es+ntfy empty, 4 config volumes populated, bookva-minio-data cp с books bucket), login-gate SQL ✓ (bookva-db users id≥3 → UUID random pwd), DNS ✓ (pre-existed), books-web embed-api switch ✓, external access ✓ (bookva-db:33306 + bookva-minio:9001).

Контекст dev-source: victor/books .wiki/concepts/tenant-split.md rev v3. Code-side: 5e28fd1 embed-api+zod^4, 4a9cafc ES endpoint, c1e58cf PWA redirect, 88df172 deploy.yml per-tenant. Overlay-repos: bookva-overlay/main 8bffd68 (templates aligned + mariadb:10.6 + mongo:4.2 + api internal-only + depends_on removed).

Done в сессии 2026-05-26:

  • Step 1: 7 Gitea secrets (BOOKVA_OVERLAY_TOKEN, HEALTHCHECK_BOOKVA_{API,WEB}URL, PORTAINER_STACK_ID_BOOKVA{API=45,WEB=46,SCHEDULER=47,OPS_MCP=43}). books 2f7b539 (secret name fix).
  • Step 2: 9/10 Portainer stacks. Stack IDs: db=34, mongo=36, es=37, ops-mcp=43, ntfy=44, api=45, web=46, scheduler=47, task-runner=48. ozon-mcp deferred (image not in registry). User-facing stacks (api/web/scheduler/task-runner/ops-mcp/ntfy) stopped post-create (Status=2) — bookseller.kzntsv.site returns 000 как desired pre-cutover state. db/mongo/es оставлены running (внутренние, useful для testing).
  • Step 3: 6 volumes created. bookva-db-data (4.7G) = cp -a /usr/docker/books-db/data (downtime 59s). bookva-mongo-data (517M) = cp -a /opt/books/job-scheduler/mongo/db (downtime 5s). 4 config volumes populated из corrected overlay templates (api, scheduler, task-runner, web-branding). 2 empty by design (ntfy-data, es-data — Phase 2 snapshot/restore). MinIO deferred (no maintenance impact — mc cp books bookva at cutover, не rename).
  • Step 4: bookva-db login-gate UPDATE users SET password=UUID() WHERE id_user NOT IN (1,2) — 3 users scrambled (id=3,4,5), id=1 (Bookva founder) + id=2 (Slovo founder) untouched. books-db verified unaffected.
  • Step 5: DNS bookseller.kzntsv.site → 89.253.255.133 (был pre-existing, closed by inspection).
  • Step 7: Portainer books-web stack 24 PUT atomic (compose+env). Env: NUXT_PUBLIC_BOOKS_API_URL=/api, NUXT_AUTH_TOKEN, NUXT_JWT_SECRET_KEY. Embed-api на bookva.kzntsv.site/api/* живёт (smoke green, auth identical to books-api). books 75ea320 (compose template synced). Soak 24-48ч до books-api shutdown micro-task.
  • 🔵 Step 6: delayed по spec (за 1-2 нед до cutover — NUXT_PUBLIC_BOOKVA_REDIRECT_URL=https://bookseller.kzntsv.site env на books-web для PWA redirect banner user.id=1).

Deferred (отдельной micro-task):

  • bookva-ozon-mcp stack — image registry.kzntsv.site/books-ozon-mcp:master не существует в registry. Нужен build в victor/books CI (ozon-mcp service в build.yml?). Скорее всего service ещё не настроен — отдельная task'а.
  • Mongo agenda cleanup partial — 7 jobs с idSeller=2 удалены, осталось 2817 jobs (большинство — system-wide без idSeller). При cutover проверить если нужен дальнейший cleanup.

Branch: master | Closure pushes: books 2f7b539+75ea320 (master); bookva-overlay 5b173de8bffd68 (main): templates align + mariadb:10.6 + mongo:4.2 + api internal-only + depends_on removed. Pass: gitea/victor-books-ci-bookva-overlay.


🟢 [books-ops-mcp-host-promote] — closed 2026-05-26 — host-promote ops-mcp+docker-proxy в .admin/host-stacks/books-vds/, drop bookva-ops-mcp

Stack 26 (books-ops-mcp) in-place PUT без container churn (env MARIADB_PASSWORD preserved). Stack 43 (bookva-ops-mcp) deleted. Gitea secrets PORTAINER_STACK_ID_BOOKVA_OPS_MCP + PORTAINER_STACK_ID_OPS_MCP revoked. ops-mcp убран из tenant=slovo|bookva pipeline (deploy.yml). Management-plane: image updates через Portainer UI manual.

Design locks (см. books-ops-mcp-host-promote.md § Closure note): Q1 location=.admin/host-stacks/, Q2 deploy=manual Portainer, Q3 MariaDB scope=slovo's only (Option A), Q4 config volume path unchanged, Q5 audit прочих host-level кандидатов punt.

Commits: victor/books ae3ab14 + victor/bookva-overlay 50f5bbb + .admin (this commit). Branch: master


[stateful-split-volume-copy] — поднять bookva-db + slovo-db на VDS как копии books-db через cp -a volume

Ops-таска для Фазы 1 дизайна tenant-split из victor/books. Scope сужен 2026-05-25 (user: «просто поднимем 2 БД»): только MariaDB volume copy + up 2 контейнеров. Mongo / MinIO / DELETE / app-стеки — отдельными ops-тасками потом.

Контекст dev-source: дизайн в victor/books .wiki/concepts/tenant-split.md § «Фаза 1». Compose-файлы готовы в bookva-overlay / slovo-overlay (commit d0eb210 в books).

Acceptance: docker ps показывает живые bookva-db + slovo-db, оба отвечают SELECT 1. Текущий books-db стек снова в строю после maintenance window.

Status: ready Where I stopped: (not started — lean playbook готов в stateful-split-volume-copy.md) Next action: под maintenance window 5-10 мин на VDS — execute 5 команд из task-файла. Backup → stop books-db → cp -a в 2 volume'а → up 2 новых контейнера через Portainer → start books-db. Blocker:Branch: master


🟢 [iis-migration-to-ruvds] — closed 2026-05-25 per user decision — Phase 1 done: RUVDS infra setup + 8.66GB scp + IIS recreate + 25 HTTPS SNI bindings (LE R13 expire 2026-07-22) + 9 hostnames (kupimknigi.spb.ru, emspb±www, pilorama98±www, labtools.pro±www, rimiz±www) live на 80.64.31.36 with correct per-tenant content. 16 hostnames остаются на windows source per user pace (incl. tandemmebel scope-exception). Source IIS:8089 + traefik routes ALIVE для rollback. Decommission + LE renewal + cleanup descoped в Closure note. См. iis-migration-to-ruvds.md § Closure note.

Status: closed 2026-05-25 Where I stopped: 2026-05-24 — kupimknigi.spb.ru + emspb.ru (+ www.emspb.ru) DNS A flipped на 80.64.31.36, authoritative ns1.reg.ru правильный, public resolver caches expire'ятся (8.8.8.8=~6h, 1.1.1.1=~24h max). RUVDS state: snolla site (8.66 GB / 44725 files) transferred + IIS recreated + 25 HTTPS SNI bindings c LE certs (R13, valid до 2026-07-22), 7 prod hostnames live-smoke через VDS (третья сеть) → 200 OK / correct content. Source IIS:8089 + traefik routes ALIVE — rollback ready. 2026-05-25 close-time DNS probe: ещё 5 пар hostnames swap'нуты user'ом silently — итого 9 на RUVDS, 16 на source. Findings зафиксированы в iis-migration-to-ruvds.md Decisions log: (1) outbound 445 блокирует home ISP, не RUVDS-FW → SSH/scp = canonical transfer-метод; (2) home network HTTP-middlebox mangles Host header for direct external HTTP — real end-users не пострадают, тестировать через VDS; (3) traefik acme.json → IIS PFX recipe работает (extract + openssl pkcs12 -export + Import-PfxCertificate + AddSslCertificate by thumbprint); (4) IIS 10 HTTP/2 default; (5) maljarka.tandemmebel.ru + 3 rimiz hostnames → 502/404 — pre-existing CMS-tenant config gap, не migration defect.

Scope exception (2026-05-24 user decision): tandemmebel.ru + www.tandemmebel.ru ОСТАЮТСЯ на windows-IIS на неопределённый срок (отдельное решение user'а — site не готов к cutover ровно сейчас). DNS НЕ свапать. Cert на RUVDS уже импортирован, binding existing — может оставаться idle, traffic не пойдёт.

Next action:

  1. Снизить DNS TTL в reg.ru на оставшиеся 22 hostnames до 300s — pilorama98.ru, labtools.{ru,pro}, snolla.com + 11 snolla subdomains + rimiz.{ru,snolla.com} + maljarka.tandemmebel.ru (но не tandemmebel.ru — см. Scope exception). Сократит cache-tail с 24h до 5 мин.
  2. 24h soak kupimknigi + emspb — verify через cache-clean resolver (потом curl без --resolve).
  3. Bulk DNS swap оставшихся 22 hostnames на 80.64.31.36 (single sitting). tandemmebel.ru / www.tandemmebel.ru — пропустить.
  4. 1-week prod soak с RUVDS как live source для migrated hostnames.
  5. Decommission source IIS:8089 только для migrated hostnames (snolla catch-all site нельзя decommission'ить пока tandemmebel.ru на нём же). Решение defer до tandemmebel migration.
  6. LE renewal pipeline — win-acme + HTTP-01 на RUVDS после full cutover (LE certs expire 2026-07-22, soak window до ~07-15).
  7. Cleanup migration-temp: Remove-NetFirewallRule 'smb-from-source','ssh-from-source' на RUVDS, удалить ~/.ssh/ruvds-iis-migration* на source, очистить C:\ProgramData\ssh\administrators_authorized_keys на RUVDS.

Полный план + Completed + Open questions + Remaining steps — в iis-migration-to-ruvds.md. Branch: master

Branch: master


[infra-inventory] — two-tier инвентаризация: public-карта в global wiki + admin-detailed runbook локально

Status: ready Where I stopped: (not started — создана 2026-05-22 из инцидента gitea. vs git. + перепутанный source/dest IP в traefik access-логах; scope расширена в тот же день — single-page → two-tier per user «у все общее представление, у админа полное») Next action: см. полный план в infra-inventory.md. Шаги: (1) probing-фаза одна для обоих — nslookup известных subdomain'ов + vds-ops/synology-ops ops_docker_ps/inspect + knowledge_search по существующим bootstrap-concept'ам + rg "kzntsv.site" по ~/projects/. (2) Write A — light public в projects-wiki/concepts/infrastructure-inventory.md (машины, canonical hostnames, anti-aliases) → knowledge_ingest. (3) Write B — heavy admin в .admin/.wiki/concepts/infrastructure-stack.md (per-stack: версии, internal IPs, volumes, depends-on, backup paths, DR pointers) → commit + push. (4) Cross-refs A↔B + pointer в .admin/.wiki/CLAUDE.md Domain conventions. Branch: n/a


🔵 [books-vds-bookva-bootstrap] — Ops-таска для Фазы 4 дизайна tenant-split из victor/books (см. mcp__projects-meta__knowledge_get slug=concepts/tenant-split). Поднять новый VDS для Bookva — billing на юрлицо Bookva (учредители развелись, каждое юрлицо платит инфру напрямую провайдеру).

Контекст dev-source: дизайн в victor/books .wiki/concepts/tenant-split.md; brainstorm trace ~/projects/.workshop/.archive/2026-05-24-books-tenant-split.md.

Direction: Slovo остаётся на текущем VDS, Bookva переезжает на новый.

Acceptance:

  • Новый VDS у провайдера (Rusonyx или другой по предпочтению Bookva), billing-account оформлен на юрлицо Bookva.
  • Docker + traefik + certbot развёрнуты.
  • DNS *.bookva.<tld> готов резолвиться на новый IP (фактический switch — в books-dns-cutover-bookva).
  • SSH-ключи: только инженерные (core-разраб + админ Bookva, если есть). Аналитики Bookva — НЕ имеют SSH к новому VDS.

Status: blocked Where I stopped: (not started) Next action: Pre-step: прочитать concepts/tenant-split.md § «Фаза 4» в victor/books (через mcp__projects-meta__knowledge_get slug=concepts/tenant-split или git clone victor/books → .wiki/concepts/tenant-split.md).

  1. Согласовать с учредителем Bookva: provider, configuration (CPU/RAM/disk), регистрация billing'а на юрлицо Bookva.
  2. Provision VDS, базовая настройка (firewall, fail2ban, SSH ключи).
  3. Установить Docker + docker-compose.
  4. Развернуть traefik + certbot из overlay-репо victor/books-bookva/deploy/traefik.compose.yml.
  5. Подготовить DNS-зону bookva.<tld> (создать A-records, TTL=300 для возможности быстрого switch'а).
  6. Smoke: curl https://placeholder.bookva.<tld> → 200 от nginx-placeholder или из traefik.
  7. Документировать в victor/books-bookva/README.md: hostname, IP, как ssh, runbook smoke-теста.
  8. Закрыть с note: «VDS поднят, IP=<...>, готов к stack deploy». Blocker: OpeItcLoc03/admin books-stateful-split-execution (Фаза 1 — physical split на shared VDS, bookva-* контейнеры живут) + victor/books per-tenant-backup-and-observability (Фаза 2 — per-tenant backups настроены до физического переезда) Branch: n/a

🔵 [books-dns-cutover-bookva] — Ops-таска для Фазы 4, шаг 5 дизайна tenant-split из victor/books. DNS switch *.bookva.<tld> → новый VDS IP, с parallel-run старого bookva-стека на shared VDS 7 дней без публичного hostname.

Контекст dev-source: дизайн в victor/books .wiki/concepts/tenant-split.md § «Фаза 4».

Acceptance:

  • DNS *.bookva.<tld> (или конкретный hostname Bookva — определяется учредителем) указывает на новый VDS IP.
  • Старый bookva-стек на shared VDS остаётся запущенным, без публичного hostname (traefik labels убраны или disabled), доступен только по docker-сети для возможного отката.
  • Smoke с двух разных сетей (mobile + офисный wifi) — https://<bookva-hostname> отвечает с нового VDS.
  • Per-VDS backup'ы на новом VDS подтверждены — есть snapshot спустя 24ч после DNS-switch'а.
  • Через 7 дней parallel-run без откатов — decommission старого bookva-стека (см. Фаза 4, шаг 7 в spec).

Status: blocked Where I stopped: (not started) Next action: Pre-step: прочитать concepts/tenant-split.md § «Фаза 4, шаги 47» в victor/books.

  1. Maintenance window 12ч (объявить за 7 дней).
  2. Финальная синхронизация: mysqldump mariadb-bookva на текущем VDS → mysql на новом VDS (если schema/data разошлись с момента Фазы 2).
  3. Smoke на новом VDS до DNS-switch'а: hosts-файл override → проверить login + base flow.
  4. DNS-switch: меняем A-record(s) для bookva-hostname на new IP. TTL=300 уже стоял (Phase 4 step 1).
  5. Через 1ч (TTL expiry + propagation): smoke с двух сетей.
  6. На старом VDS — отключить traefik labels для bookva-стека (контейнеры продолжают работать, но не доступны снаружи).
  7. Parallel-run 7 дней. Каждый день — smoke. Per-VDS backup на новом VDS — проверить хотя бы 1 успешный snapshot.
  8. Если за 7 дней проблем нет → закрыть с note «cutover stable». Если есть — DNS rollback на старый IP, follow-up debug task в victor/books.
  9. Decommission старого bookva-стека на shared VDS (отдельный шаг, не в этой таске — финальный backup в архив + docker compose down + volume drop). Blocker: books-vds-bookva-bootstrap + victor/books pwa-redirect-handoff (SW bump выкачен в shared API за 12 недели до этой таски). Branch: n/a

[books-bookva-user-whitelist-gathering] — Процессная ops-таска для Фазы 3, шаг 1 дизайна tenant-split из victor/books. Получить от учредителя Bookva письменный список пользователей, которые допущены к Bookva-инсталляции. Без этого списка cutover не возможен — иначе утечка доступа: либо лишние юзеры получат доступ к Bookva (через копирование «всех users»), либо легитимные юзеры заблокированы (через пустой whitelist).

Direction: Bookva — новая инсталляция, начинается с whitelist'а (закрытый список). Slovo — остаётся на текущем стеке, получает копию всех текущих users (статус-кво).

Контекст dev-source: дизайн в victor/books .wiki/concepts/tenant-split.md § «Двухуровневая изоляция пользователей → App-level» и § «Фаза 3, шаг 1».

Не блокируется ничем — можно начинать gathering сразу, лаг до cutover'а может быть значительным.

Acceptance:

  • Письменный список от учредителя Bookva (email или подписанный документ): ФИО + role (если знают).
  • Список зафиксирован в OpeItcLoc03/admin/.wiki/concepts/books-bookva-whitelist.md или в отдельной таблице.
  • Для каждой строки списка сверка с текущей users-таблицей shared БД: user существует / роль соответствует / email актуален.
  • Любые расхождения (запрошенный user не существует в shared / роль другая) — обработаны: создать новый user в Bookva-БД post-cutover ИЛИ скорректировать список с учредителем.

Status: ready Where I stopped: (not started) Next action: 1. Отправить учредителю Bookva формальный запрос (email/мессенджер): «Для tenant-split нужен список пользователей, которые останутся в Bookva после разделения. Формат: ФИО + роль + email/login. Список будет применён на cutover'е как whitelist». 2. Дождаться ответа (это может занять дни/недели — нормально). 3. Получив список — сверить с users-таблицей shared БД:

SELECT id, login, email, role FROM users WHERE email IN (<list>) AND deleted_at IS NULL;
  1. Разобрать расхождения с учредителем (missing / role mismatch / inactive).
  2. Зафиксировать финальный список в OpeItcLoc03/admin/.wiki/concepts/books-bookva-whitelist.md (или в local-only encrypted file если NDA-чувствительно).
  3. Финальный список становится input'ом для Фазы 3, шаг 1: при настройке Bookva-БД в неё попадают только эти users.
  4. Закрыть с note: «whitelist (N users) подтверждён учредителем, готов к применению на cutover'е». Branch: n/a

🔵 [books-stateful-split-execution] — Ops-таска для Фазы 1 дизайна tenant-split из victor/books. Выполнить playbook на shared VDS в maintenance window — docker volume copy для MariaDB + Mongo + MinIO bucket rename + DELETE cleanup. Identical bit-for-bit копии через cp -a alpine helper. Никаких mysqldump/mongodump/mc cp.

Playbook (dev-source, читать целиком до начала): victor/books/.tasks/stateful-split-volume-copy.md.

Контекст дизайна: mcp__projects-meta__knowledge_get slug=concepts/tenant-split § «Фаза 1 — Physical stateful split + overlay-repos». Brainstorm trace: ~/projects/.workshop/.archive/2026-05-24-books-tenant-split.md.

Container naming: drop books- prefix → bookva-<svc> / slovo-<svc>.

TDD-режим: [skip-tdd: one-off migration on maintenance window] — verify через post-cleanup audit.

Acceptance:

  • bookva-db + slovo-db контейнеры живые, audit SELECT DISTINCT idSeller = {1} / {2} соответственно.
  • bookva-mongo + slovo-mongo контейнеры живые, agenda-история preserved, jobs чужого tenant'а cleanup'нуты.
  • MinIO (один shared) имеет bucket'ы bookva + slovo вместо books; объекты чужого tenant'а удалены.
  • Shared catalog tables (books, editions, epz, epz_pages, warehouses, products) — полные копии в обеих БД.
  • Smoke с двух сетей: login через bookva.<tld> → Bookva-данные; текущий URL → Slovo.
  • Через 7-14 дней parallel-run без откатов → cleanup старых books-* контейнеров и volumes (отдельный заход).

Status: blocked Where I stopped: (not started) Next action: Pre-step: прочитать victor/books/.tasks/stateful-split-volume-copy.md целиком. Убедиться что 7 open questions resolved (закрыто dev-prep таской victor/books stateful-split-volume-copy): FK CASCADE, units/receipts strategy, MinIO key structure, users whitelist, task_runs handling, chrome/traefik shared.

  1. Подтвердить overlay-репо victor/bookva-overlay + victor/slovo-overlay готовы — compose-файлы для bookva-* / slovo-* стеков на месте.
  2. Подтвердить per-tenant backups настроены (victor/books per-tenant-backup-and-observability) — иначе нет точки rollback.
  3. Объявить maintenance window 1-2ч (без записи).
  4. Execute Steps 0-11 из playbook на shared VDS.
  5. Post-execution audit: SELECT DISTINCT idSeller FROM <each>, db.agendaJobs.distinct('data.idSeller'), mc ls on MinIO.
  6. Smoke с двух сетей.
  7. Через 7-14 дней parallel-run без откатов → cleanup старых books-* контейнеров+volumes (отдельный заход).
  8. Closing note: «Physical split done, bookva-* + slovo-* контейнеры живут на shared VDS, audit clean, parallel-run start ». Blocker: victor/books stateful-split-volume-copy (dev-prep — 7 open questions resolved + dev-DB test passed) + victor/books per-tenant-backup-and-observability (Фаза 2 — per-tenant backups настроены) Branch: n/a