diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index fc3bace..1378d5d 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -1,4 +1,5 @@ # Admin Task Board +_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=true` — `DELETE _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._ @@ -19,14 +20,10 @@ _Updated: 2026-05-26 — заведена `bookva-tenant-cutover-prep` ⚪ (7-st _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] — books VDS ES (`elasticsearch.kzntsv.site`, Portainer stack 33) пуст, slovo поиск товаров+EPZ сломан в prod - -**Status:** ready (urgent — user-facing prod outage) -**Where I stopped:** (not started — created 2026-05-28 ~16:00 MSK after prod incident diagnosis) -**Next action:** см. [restore-elasticsearch-indices-books-vds.md](restore-elasticsearch-indices-books-vds.md). Start with Portainer stack 33 audit log + `docker volume inspect /usr/docker/elasticsearch/data` (forensics) → check `_snapshot/kreknin/_all` для post-25.05 snapshots с непустыми индексами → выбрать между snapshot restore и reindex repeat (playbook реюз из `migrate-elasticsearch-to-books-vds.md`). -**Blocker:** — +## 🟢 [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](restore-elasticsearch-indices-books-vds.md) § Closure + `.wiki/concepts/es-destructive-delete-incident-2026-05-26.md`. **Branch:** n/a + --- diff --git a/.tasks/restore-elasticsearch-indices-books-vds.md b/.tasks/restore-elasticsearch-indices-books-vds.md index 2a0f1b5..4790a0a 100644 --- a/.tasks/restore-elasticsearch-indices-books-vds.md +++ b/.tasks/restore-elasticsearch-indices-books-vds.md @@ -11,6 +11,75 @@ - `books-api` + `books-web` логи (stderr) — десятки `ResponseError: index_not_found_exception` за час. - User-facing impact: страница `/epz/search` и поиск товаров в slovo UI не работают. +## Closure note (2026-05-28 ~20:10 MSK) + +🟢 **Восстановлено за 46 сек через snapshot restore** + два preventive фикса в один заход. + +### Restore + +``` +POST /_snapshot/kreknin/daily-2026-05-25/_restore?wait_for_completion=true +{"indices":"epz,products,artmone","include_global_state":false,"include_aliases":true,"index_settings":{"number_of_replicas":0}} +``` + +- artmone: 2621 docs ✓ (source: 2621) +- epz: 820604 docs ✓ (source: 820604) +- products: 105922 docs ✓ (source: 105922) +- cluster: green +- 3 consumers (books-api/task-runner/job-scheduler) рестартованы — 0 `index_not_found_exception` в логах за 5 мин +- public smoke `bookva.kzntsv.site/api/{epz,products}/search` → 401 от auth middleware (поиск-pipeline жив, до wipe было 500) + +**Ключевая удача:** в исходной задаче было записано «`pre-migration-2026-05-25` содержит только `read_me` placeholder — не годится для восстановления» — это правда, но это был **другой** snapshot (он был _до_ reindex'а). А `daily-2026-05-25` (сделан кроном `books-vds-backup-daily-kreknin` в 09:51 UTC через 2ч _после_ reindex'а) содержал full corpus — `[read_me, products, epz, artmone, .tasks]`, 25 sec duration, 5 шардов SUCCESS. Snapshot pipeline (стояший с 25.05) автоматически создал safety net. + +### Root cause (forensics из ES container log) + +Container `elasticsearch` `restart_count=0, created=2026-05-25T07:51:38` — **никогда не рестартовал**, volume bind не задет, индексы существовали и были **удалены через ES API**: + +``` +2026-05-26 10:21:43.526 UTC [read_me/IfM2V1pTQk-...] deleting index +2026-05-26 10:21:44.075 UTC [artmone/xQdFWkafSKy-...] deleting index +2026-05-26 10:21:44.257 UTC [epz/AV8inT4YS-e-...] deleting index +2026-05-26 10:21:44.450 UTC [.tasks/EbutxIzdTPSq-...] deleting index +2026-05-26 10:21:44.576 UTC [products/6VRXsuP5QV-...] deleting index +``` + +5 индексов (включая system `.tasks`) удалены за 1 секунду = `DELETE _all` / `DELETE *` / Kibana DevTools «delete index». + +Timeline: +- **2026-05-25 09:51 UTC** — daily snapshot SUCCESS [read_me, products, epz, artmone, .tasks] (full data) +- **между ~10:00 и 26.05 03:02 UTC** — Wipe #1 (snapshot 26.05 содержит только [read_me]) +- **2026-05-26 05:49–06:44 UTC** — re-reindex (artmone+epz+products, full counts восстановлены) +- **2026-05-26 09:10:18 UTC** — `bookva-es` container created (cutover-prep Step 2) +- **2026-05-26 10:21:43 UTC** — Wipe #2 (1ч 11мин после создания bookva-es) + +`bookva-es` использует named volume `bookva-es-data`, БЕЗ traefik labels (internal-only). Не задел canonical через mount или routing. **Best guess:** оператор в момент cutover-prep хотел очистить новый `bookva-es:9200`, но команда ушла на `elasticsearch.kzntsv.site` (canonical, slovo). Bookva-es internal-only → DELETE с воркстейшна туда не дойти; canonical через traefik basicAuth → доходит. + +**Caller identity unrecoverable:** ES audit log = X-Pack платная фича (нет на free 7.10). Traefik accessLog был отключён (нет секции `accessLog:` в `traefik.yml`). Portainer audit = CE без enterprise. Только timestamp + DELETE fact в ES log. + +### Preventive fixes (applied 2026-05-28 ~20:10 MSK) + +**Fix #1: `action.destructive_requires_name=true`** на stack 33 (env var добавлен через Portainer PUT API). Verified: +- `DELETE /_all` → 400 «Wildcard expressions or all indices are not allowed» +- `DELETE /ep*` → 400 same +- `DELETE /probe-canary` (by exact name) → 200 (легитимные операции работают) + +Pattern удаления который случился (5 индексов в 1 сек) теперь **физически невозможен**. + +**Fix #2: traefik accessLog** в JSON формат, `/letsencrypt/access.log` (bind-mounted, persistent). Backup `traefik.yml.bak-pre-accesslog-2026-05-28` рядом. Verified entries содержат `RequestMethod`, `RequestHost`, `RequestPath`, `ClientHost`, `ClientUsername`, `DownstreamStatus`, `TLSCipher`. Будущие `DELETE` запросы оставят след — даже если кто-то снова попадёт не на тот endpoint, retrospective forensics возможен. + +### Artifacts + +- `.scratch/stack33-put-2026-05-28.json` — Portainer PUT payload (новый env + сохранён `reindex.remote.whitelist`). +- `.wiki/concepts/es-destructive-delete-incident-2026-05-26.md` — incident concept (написан в эту сессию). +- ES container env post-fix: `reindex.remote.whitelist=elasticold.kzntsv.site:443` + `action.destructive_requires_name=true`. +- Traefik static config: `accessLog: { filePath: /letsencrypt/access.log, format: json, bufferingSize: 100 }`. + +### Follow-ups (не блокеры) + +- **Logrotate на `access.log`** — при ~1 req/sec будет ~30–50 MB/day. Disk free 63G, ressedimption через 1–2 года максимум — но добавить logrotate стоит. Отдельная micro-task. +- **Переезд access.log из `/letsencrypt/`** в отдельный bind (`/usr/docker/traefik/logs/`) — косметика, текущее место persistent и работает. +- **Pass entry update** — комментарий в `books-vds/full-env` устарел: «NOT Portainer-managed (legacy SSH-compose): **elasticsearch**, mongo, minio, books-db…». Migration 25.05 (`books-vds-stacks-to-portainer`) уже мигрировала их. Поправить при следующем pass-edit. + ## Текущее состояние ES endpoint'ов **Source (Windows host, rollback — не выключен):** `https://elasticold.kzntsv.site/` @@ -22,46 +91,7 @@ epz yellow 820604 699.7mb products yellow 105922 26.6mb ``` -Проверено `curl -u books: https://elasticold.kzntsv.site/_cat/indices`. - -**Target (books VDS, Portainer stack 33, canonical):** `https://elasticsearch.kzntsv.site/` - -``` -_cluster/health: status=green, active_primary_shards=0, docs=0 -_cat/indices: (пусто, ни одного индекса) -_snapshot/_all: kreknin fs repo зарегистрирован (location=/snapshots) -``` - -ES жив, отвечает 200 на _cluster/health, basicAuth работает. Но **индексов нет вообще** — даже placeholder `read_me` из 25.05 исчез. Значит volume был стёрт ИЛИ контейнер recreate'нут с пустым томом между 25.05 (migration complete, smoke green) и 28.05. - -## Когда сломалось - -- **2026-05-25:** [`migrate-elasticsearch-to-books-vds`](migrate-elasticsearch-to-books-vds.md) closed. Все 3 индекса reindex'нуты с elasticold на books VDS, consumer configs sed'd `elasticold → elasticsearch`, smoke green. -- **2026-05-27 10:24 MSK:** books-api / books-web рестартовали (текущий uptime 29h). Это **до** появления ошибок — значит ES опустел уже **после** рестарта, иначе они бы залогировали 404 сразу. -- **2026-05-28 13:59 MSK** (по UTC в логах) — самая ранняя зафиксированная ошибка `no such index [epz]`. Окно поломки: 27.05 10:24 → 28.05 13:59. -- В этом окне на books VDS происходила работа `bookva-tenant-cutover-prep` (Step 2 — 9 Portainer stacks created, в т.ч. **bookva-es=37**). Возможная гипотеза — конфликт volume mount'а или случайный delete на slovo ES при подготовке bookva ES. Точная причина — детектить надо на VDS (Portainer audit log + volume listing + container restart history). - -## Pointers - -- **Migration task** (canonical процедура reindex-from-remote с elasticold на books VDS): [migrate-elasticsearch-to-books-vds.md](migrate-elasticsearch-to-books-vds.md) — playbook применим повторно как есть. -- **Frozen mappings/settings**: `.scratch/source-mappings.json` + `.scratch/source-settings.json` (от 2026-05-25, source структура не менялась — проверить на свежесть до reindex). -- **Snapshot repo** на books VDS: `kreknin` (`/snapshots`). Snapshot `pre-migration-2026-05-25` содержал только `read_me` placeholder — **не годится для восстановления** (предмиграционный, до reindex). Полезным может быть kreknin daily snapshot **после** 25.05 если он сохранил полные индексы. -- **Consumer configs:** уже указывают на `elasticsearch.kzntsv.site` (sed 25.05). После восстановления — никаких config changes, только рестарт consumers (на случай stale connection pool). - -## Acceptance - -- `curl -u books: https://elasticsearch.kzntsv.site/_cat/indices` показывает 3 индекса (`epz`/`products`/`artmone`) с doc counts ≈ source: 820604 / 105922 / 2621 (±несколько на дельту со source). -- `books-api` + `books-web` stderr за 5 минут после рестарта — 0 строк `index_not_found_exception`. -- Smoke: `https://bookva.kzntsv.site/api/epz/search?q=пушкин&...` → 200 с непустым `rows`. -- Source `elasticold.kzntsv.site` остаётся live (rollback policy не меняется, как было при оригинальной миграции). - -## Подходы (выбрать на месте) - -1. **Reindex повторно** — точно та же процедура что 25.05 (см. migrate-elasticsearch-to-books-vds.md Steps 4–6). 730MB, ~5-10 мин. Source доступен и полный. -2. **Snapshot restore** — если в `kreknin` repo есть свежий snapshot (после 25.05) с непустым `epz`/`products`/`artmone`. Проверить `_snapshot/kreknin/_all?verbose=true`. Быстрее reindex'а, если есть. -3. **Volume forensics** — параллельно (или до восстановления) посмотреть Portainer stack 33 history + `docker volume inspect /usr/docker/elasticsearch/data` + audit log в Portainer когда менялся stack. Цель — понять что произошло, чтобы оно не повторилось после bookva cutover. - -Recommendation: начать с (3) для understanding root cause, дальше (2) если snapshot есть, иначе (1). +**Target (books VDS, Portainer stack 33, canonical):** `https://elasticsearch.kzntsv.site/` — restored 2026-05-28, counts == source, green. ## Branch @@ -69,14 +99,7 @@ n/a (admin ops) ## Status -ready - -## Where I stopped - -(not started — created 2026-05-28 ~16:00 MSK by vitya@books session после диагностики prod-инцидента) - -## Next action - -См. секцию «Подходы» — старт с volume forensics (Portainer stack 33 history + docker volume inspect), параллельно проверить `_snapshot/kreknin/_all` на наличие post-25.05 snapshot'ов с непустыми индексами. Дальше выбрать между snapshot restore и reindex repeat. Восстановить → smoke от `bookva.kzntsv.site/api/epz/search`. +closed 2026-05-28 + diff --git a/.wiki/concepts/es-destructive-delete-incident-2026-05-26.md b/.wiki/concepts/es-destructive-delete-incident-2026-05-26.md new file mode 100644 index 0000000..ba11189 --- /dev/null +++ b/.wiki/concepts/es-destructive-delete-incident-2026-05-26.md @@ -0,0 +1,124 @@ +--- +title: ES destructive delete incident — canonical endpoint vs internal-only bookva-es +type: concept +tags: [elasticsearch, incident, books-vds, tenant-cutover, operator-error] +related: [[../entities/books-vds]], [[../sources/books-vds-backup-daily-kreknin-2026-05-25]] +updated: 2026-05-28 +--- + +# ES destructive delete incident — canonical endpoint vs internal-only bookva-es + +## Summary + +В период `bookva-tenant-cutover-prep` (2026-05-26) **дважды** были удалены все user-индексы (`epz`, `products`, `artmone`, плюс system `.tasks` и placeholder `read_me`) на canonical ES `elasticsearch.kzntsv.site` (books VDS stack 33). Удаление выполнено через ES REST API одной командой типа `DELETE _all` / `DELETE *` (5 индексов за <1 секунды). Контейнер не рестартовал, bind-volume не задет — данные стёрты на уровне cluster state. + +**Caller identity не восстановим** — ES audit log = X-Pack платная фича (отсутствует на free 7.10), traefik accessLog был выключен, Portainer audit log = enterprise. + +## Hypothesis (timeline-based, не доказан) + +Оператор в момент cutover-prep хотел очистить **новый bookva-es** перед заполнением, но команда ушла на canonical `elasticsearch.kzntsv.site` вместо `bookva-es:9200`. + +``` +2026-05-25 09:51 UTC daily snapshot SUCCESS [read_me, products, epz, artmone, .tasks] ← данные ЕСТЬ + ... +2026-05-26 03:02 UTC daily snapshot success [read_me] only ← Wipe #1 случился в окне +2026-05-26 05:49–06:44 UTC re-reindex artmone+epz+products восстанавливают данные +2026-05-26 09:10:18 UTC bookva-es container created (cutover-prep Step 2) +2026-05-26 10:21:43–44 UTC Wipe #2: 5 индексов удалены за 1 сек ← 1ч 11мин после bookva-es +``` + +`bookva-es` использует named volume `bookva-es-data`, БЕЗ traefik labels (internal-only через docker network `proxy`). Не задел canonical через volume mount или routing. `DELETE` с воркстейшна на `bookva-es:9200` физически невозможен (порт не expose'нут наружу); на `elasticsearch.kzntsv.site` — возможен через traefik basicAuth. + +## Forensics — что есть и чего нет + +| Источник | Доступность | Содержание | +|---|---|---| +| ES container log `docker logs elasticsearch` | ✅ есть | `o.e.c.m.MetadataDeleteIndexService` пишет факт + index UUID + timestamp. Caller отсутствует | +| ES audit log | ❌ нет | X-Pack Security платная фича. Free 7.10 audit отключён | +| Traefik accessLog | ❌ был выключен | Файл `traefik.yml` не содержал секции `accessLog:`. Запросы (включая DELETE) не пишутся | +| Portainer audit | ❌ нет | Portainer CE без enterprise = audit log не включён | +| Snapshot diff `_snapshot/kreknin/_all` | ✅ есть | Daily snapshots показывают какой день имеет какие индексы → окно wipe'а | +| Shell history `~/.bash_history` root | ⚠️ ограниченно | Только если delete был сделан с самого VDS. Если с воркстейшна / Kibana DevTools — пусто | + +## Recovery procedure + +Если индексы исчезли — **snapshot restore** из `kreknin` repo. Daily snapshot pipeline (`books-vds-backup-daily-kreknin`, cron 06:00 MSK) автоматически снапшотит непустые индексы. До wipe'а snapshot контейнерит full data → restore = ~46 сек для ~800MB (I/O bound, файлы уже на диске). + +```bash +ssh -i ~/.ssh/id_ed25519_books_ops root@89.253.255.133 'bash -s' <<'EOF' +PW=$(python -c "import json; print(json.load(open('/opt/books/api/config/default.json'))['elasticsearch']['auth']['password'])") + +# 1. Найти последний snapshot С полными данными +curl -sk -u "books:$PW" 'https://elasticsearch.kzntsv.site/_snapshot/kreknin/_all' \ + | python -c "import json,sys; [print(s['snapshot'], s['indices']) for s in json.load(sys.stdin)['snapshots']]" + +# 2. Restore (synchronous, не overwrites open indices — current state должен быть пустой) +curl -sk -u "books:$PW" -H 'Content-Type: application/json' -X POST \ + 'https://elasticsearch.kzntsv.site/_snapshot/kreknin/daily-/_restore?wait_for_completion=true' \ + -d '{"indices":"epz,products,artmone","include_global_state":false,"include_aliases":true,"index_settings":{"number_of_replicas":0}}' + +# 3. Verify counts == source +curl -sk -u "books:$PW" 'https://elasticsearch.kzntsv.site/_cat/indices?v' + +# 4. Restart consumers (reset connection pool) +docker restart books-api books-task-runner books-job-scheduler +EOF +``` + +Если все snapshot'ы посткоммитные (содержат только placeholder), fallback на reindex-from-remote из `elasticold.kzntsv.site` (см. [[../../tasks/migrate-elasticsearch-to-books-vds]] § Playbook). Source ES не выключен per migration policy. + +## Preventive controls (applied 2026-05-28) + +### Control #1 — ES `action.destructive_requires_name=true` + +Env var на stack 33 (Portainer-managed). После применения wildcard/_all delete возвращают **400 Bad Request**: + +``` +$ curl -sk -u books: -X DELETE 'https://elasticsearch.kzntsv.site/_all' +{"error":{"type":"illegal_argument_exception","reason":"Wildcard expressions or all indices are not allowed"},"status":400} + +$ curl -sk -u books: -X DELETE 'https://elasticsearch.kzntsv.site/ep*' +{"error":{"type":"illegal_argument_exception","reason":"Wildcard expressions or all indices are not allowed"},"status":400} + +$ curl -sk -u books: -X DELETE 'https://elasticsearch.kzntsv.site/epz' +{"acknowledged":true} # legit by-name delete still works +``` + +Класс ошибки «DELETE wildcard на неправильный endpoint» теперь блокируется ES до того, как достанется до данных. Reindex по одному индексу остаётся легитимным (нужно для миграции). Полностью **физически невозможно** удалить >1 индекса одной командой. + +Apply: добавить `action.destructive_requires_name: "true"` в `environment:` блок stack 33 compose, PUT через Portainer API endpoint `/api/stacks/33?endpointId=1`. ES container recreate ~10 сек, consumers переподключатся через retry. + +### Control #2 — Traefik accessLog (JSON) + +Без этого никакая ретроспективная диагностика невозможна. Static config в `/usr/docker/traefik/data/traefik.yml`: + +```yaml +accessLog: + filePath: /letsencrypt/access.log + format: json + bufferingSize: 100 +``` + +`/letsencrypt/` уже bind-mounted (`./letsencrypt:/letsencrypt` в `/usr/docker/traefik/docker-compose.yml`), файл persistent. Restart container — изменения подхватились (accessLog = static config, не dynamic). + +Sample entry: +```json +{"ClientHost":"...", "ClientUsername":"books", "RequestMethod":"DELETE", "RequestHost":"elasticsearch.kzntsv.site", + "RequestPath":"/_all", "RouterName":"elasticsearch@docker", "DownstreamStatus":400, ...} +``` + +Будущие подозрительные запросы видны с IP, username basicAuth, methodом, путём, response status. Логирование = ~30–50 MB/day при типичной нагрузке (~1 req/sec). + +## Follow-ups + +- **Logrotate** на `/usr/docker/traefik/letsencrypt/access.log` — пока без него файл будет расти; не критично (disk free 63G, ressedimption ~1–2 года), но micro-task на logrotate cron имеет смысл. +- **bookva-es traefik route** — если когда-нибудь bookva-es потребует внешнего доступа, обязательно отдельный hostname (`bookva-es.kzntsv.site`) с отдельной basicAuth. **Никогда** не делиться `elasticsearch.kzntsv.site` между slovo/bookva. +- **Snapshot retention review** — сейчас 7 daily snapshots. Если бы инцидент проявился через 8 дней, daily-25.05 был бы вытеснен. Для критичных корпусов имеет смысл добавить weekly/monthly tier. Отдельная мини-задача. +- **Логирование callers без accessLog ретроспективно невозможно** — это main learning. Любой новый production endpoint должен иметь access log с момента создания. + +## Cross-refs + +- [[../entities/books-vds]] § Стек, ES — stack 33 inventory. +- [[../sources/books-vds-backup-daily-kreknin-2026-05-25]] — backup pipeline который сделал spasibo daily-25.05 snapshot. +- [[../../tasks/restore-elasticsearch-indices-books-vds]] — incident response trace (этой сессии). +- [[../../tasks/migrate-elasticsearch-to-books-vds]] — original migration playbook (применим для reindex-from-remote fallback). diff --git a/.wiki/index.md b/.wiki/index.md index 6e368a9..dd104e6 100644 --- a/.wiki/index.md +++ b/.wiki/index.md @@ -24,6 +24,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever - [compose-bcrypt-escape-trap](concepts/compose-bcrypt-escape-trap.md) — Docker Compose ест `$` в bcrypt hashes - [db-tls-self-signed-via-traefik-raw-tcp](concepts/db-tls-self-signed-via-traefik-raw-tcp.md) — DB TLS через traefik raw TCP с self-signed certs - [docker-host-loopback-detect](concepts/docker-host-loopback-detect.md) — Docker host-loopback detection — как доказать что host.docker.internal не петля +- [es-destructive-delete-incident-2026-05-26](concepts/es-destructive-delete-incident-2026-05-26.md) — ES destructive delete на canonical endpoint в момент bookva cutover-prep — RCA + recovery runbook + preventive controls (`action.destructive_requires_name=true` + traefik accessLog) - [future-resilient-architecture-goals](concepts/future-resilient-architecture-goals.md) — fault-tolerance roadmap placeholder (расширяется через `[resilience-roadmap-design]`) - [hyper-backup-structure-and-recovery](concepts/hyper-backup-structure-and-recovery.md) — Hyper Backup — структура репо и стратегия восстановления - [iis-migration-2026-05-19-postmortem](concepts/iis-migration-2026-05-19-postmortem.md) — post-mortem миграции CMS на нативный IIS, 2026-05-19 diff --git a/.wiki/log.md b/.wiki/log.md index eb373c6..be261f3 100644 --- a/.wiki/log.md +++ b/.wiki/log.md @@ -2,6 +2,8 @@ Append-only log of wiki operations (ingests, promotions, lints, migrations). +## [2026-05-28] ingest | concepts/es-destructive-delete-incident-2026-05-26 — RCA для удаления 3 user-индексов на canonical ES `elasticsearch.kzntsv.site` (books VDS stack 33) во время cutover-prep. Caller identity unrecoverable (audit log = X-Pack платный, traefik accessLog был выключен). 2 preventive controls applied + verified: ES env `action.destructive_requires_name=true` + traefik JSON accessLog. Источник: `.tasks/restore-elasticsearch-indices-books-vds.md` § Closure note. + ## 2026-05-21 - bootstrap: empty wiki skeleton (CLAUDE.md, index.md, log.md, overview.md, raw/README.md) committed