WEB-400 · Задача · — · web
[WEB370-B] Atomic backup и restore состояния
Закрыт
P1 · важно
ведёт: —
эпик: WEB-395
Суть
Источник: WEB370PLAN2. Зависимости: WEB370-A. Оценка: 2 дня. Обязательный gate; plaintext secrets запрещены.
--- 30.08 ~10:35Z (Фабл): волна 766-web370b поставлена (owner 10:06: «хвост телефонии параллелить»). Ship-dark: atomic backup/restore с манифестом sha256+generation, секреты шифруются (plaintext запрещён), атомарный restore (staging+rename), негативы: оборванный restore/битый архив. База wave/web370b от release/l71-prep. Без прод-identities.
--- 30.08 ~12:05Z (Фабл): приёмка ACCWEB370B = NO-GO (11/13): (1) symlink в state tree не round-trip-ится (а реальные session-каталоги их содержат) — надо сохранять симлинки, отказывать только на path-escape наружу; (2) манифест со swapped generation/snapshotId принимается restore — нужна самоподпись целостности манифеста. Круг 2 поставлен (774-web370b2). Отчёт: A1 /home/ubuntu/waves/ACCWEB370B-REPORT.md.
--- 30.08 ~12:25Z (Фабл): круг 2 (web370b2) АВТОРСКИ GO — обе находки закрыты (симлинки сохраняются как симлинки с path-escape границей; манифест получил самоподпись целостности). Приёмка будет после текущей очереди (координатор поставит accweb370b2).
--- 30.08 ~13:45Z (Фабл): приёмка круга 2 = NO-GO: hardlink пишется как второй независимый файл (связь inode теряется при restore). Круг 3 поставлен (782-web370b3: детект (dev,ino), hardlink-entry в манифест, restore через link(2), негативы на группы и внешние inode). Отчёт: A1 ACCWEB370B2-REPORT.md.
--- 30.08 ~15:10Z (Фабл): круг 3 АВТОРСКИ GO (feb73a6d): hardlink группируется по (dev,ino), канонический путь по минимальному байтовому порядку. Приёмка 792-accweb370b3 в очереди — внимание приёмщика направлено на «ровно ПАРА имён» в отчёте автора (бриф требовал группы 3+; тройня = целевой негатив).
--- 30.08 ~15:25Z (Фабл): приёмка круга 3 = NO-GO ровно на целевом негативе координатора: тройня hardlink (3 имени одной inode) отвергается, «ровно пара» зашита в реализацию. Круг 4 поставлен (794-web370b4: группа N>=2, негативы N=3/5/группа+symlink/две группы). Отчёт: A1 ACCWEB370B3-REPORT.md.
--- 30.08 ~22:10Z (Фабл): ⭐ СТУПЕНЬ B ЗАКРЫТА: ACCWEB370B4 = GO (свежий независимый прогон, артефакты погибшей сессии не использовались). Эволюция линии: К1 базовый формат (манифест+шифрование+atomic restore) → приёмка: симлинки/подмена полей → К2 (+самоподпись манифеста) → приёмка: hardlink-пара → К3 → приёмка: тройня → К4 группы N>=2 → ФИНАЛ GO. Ветка wave/web370b4, кандидат l75. Отчёты: A1 ACCWEB370B/B2/B3/B4-REPORT.md, WEB370B/B2/B3/B4-REPORT.md → done.
Чем закрывается (приёмка)
Encrypted versioned atomic/application-consistent snapshots; latest_verified_age ≤ 5m; snapshot cycle ≤ 2m; Signal/app/bot restore проверен без plaintext secrets.
Доказательства
[2026-08-27 01:35Z] Диспатчнут web400b на Intel-ноуте (sol high, лёгкий слот по решению owner): snapshot/verify/restore скрипты, доказательный цикл на живом Pi в staging-каталог, DoD WEB370-B. Вход: web399-manifest.json.
[2026-08-27 01:50Z] ДОКУМЕНТЫ (путь эволюции): бриф laptop:~/waves-local/web400b-brief.md; вход = web399-manifest.json; сдача ожидается ~/waves-local/WEB400B-REPORT.md + wt-web400/ скрипты; DoD-источник A1:/home/ubuntu/waves/WEB370PLAN2-REPORT.md.
[2026-08-27 03:20Z] WEB400B DONE (Intel): ДОКАЗАННЫЙ цикл на живом Pi -> staging /home/pi/nc-backups-staging/: snapshot.sh 103.2s (<120s, capture->fsync->atomic rename 89s), verify.sh GREEN 39.2s (age 117s<300s), restore.sh --dry-run GREEN 15/15 артефактов. Шифрование openssl (proof-ключ 0600, НЕ прод-escrow), MANIFEST.sha256, .part->rename атомарность, latest symlink. Дизайн: M4-таймер по SSH, копия ciphertext на M4Ext (14дн hourly = 568GB reserve), off-host verify. ЧЕСТНАЯ SLA-семантика: hourly RPO + verification lag<=5m; литеральный age<=5m требует WAL/incremental (совпадает с памятью: RPO=WAL-архив). Ownership назначен (P0 инвентаря закрыт процедурой). Осталось координатору: боевой ключ (age), M4Ext capacity, установка таймера. Отчёт laptop:~/waves-local/WEB400B-REPORT.md, скрипты wt-web400/ (sha в отчёте). -> review.
[2026-08-27 14:10Z] accmigb на Intel: независимая приёмка бэкапов с негативами (повреждённый архив/подмена манифеста/чужой ключ/свежий полный цикл/плейнтекст-скан).
[2026-08-27 18:30Z] accmigb NO-GO по ТАЙМИНГУ (не по безопасности): все 5 негативов PASS (порча байта/подмена MANIFEST/чужой ключ/плейнтекст-скан 1.4GB чисто), но свежий цикл 127.4+81.2+63.4=272s vs DoD 120s. web400c на Intel (A1 до Pi не достаёт — проверено): zstd/параллель/поток, 3 прогона подряд, негативы не ломать; при >120s — предложение нормы. DoD-вопрос (полный цикл vs capture) зафиксирован для owner.
[2026-08-27 ~07:30Z web400c] Оптимизация snapshot-цикла (272s vs DoD 120s): волна на A1 встала BLOCKED — у A1 нет TCP до mailhub:2046; честный отчёт A1:/home/ubuntu/waves/WEB400C-REPORT.md (план профилирования внутри). Перезапущена на M1 (канал M1→Pi проверен OK): бриф M1:~/waves-m1/068-web400c-brief.md, лог M1:~/waves-m1/web400c-codex.log, сдача WEB400C-REPORT.md + маркер WEB400C_DONE. Рамки: писать только /home/pi/nc-backups-staging/, сервисы/секреты не трогать, негативы приёмки (flipped byte/tampered manifest/wrong key) обязательны.
[2026-08-27 ~07:45Z web400c DONE — DoD ВЫПОЛНЕН] Оптимизация принята автором: 3 полных цикла 111.6/110.6/113.7s (<120s DoD; было 272s), capture медиана 78.8s (-38%), verify 18.6s (-77%), restore-dry 14.2s (-78%). Средства: параллельные дампы 3 кластеров, pg_dump|pigz|openssl без промежуточного plaintext, sha256 через tee в потоке, VERIFY_JOBS=3 (4 хуже — замерено), atomic .part->rename. OPENSSL_ITER=200000 НЕ снижен (бенч: 220ms/KDF, узкое место — pg_dump 1.4GB, не крипта). Негативы автором: flipped byte/manifest/wrong key PASS без plaintext. Старая версия сохранена bin.pre-web400c-20260827. Отчёт M1:~/waves-m1/WEB400C-REPORT.md, серия на Pi .web400c-acceptance-20260827T073415Z. ЗАПУЩЕНА независимая приёмка accmigc (M1, luna xhigh ≠ автор-sol): свои 2 цикла, свои негативы + новые классы (SIGKILL-воркер без частичной публикации, состав 15 классов, атомарность latest, diff на тихие ослабления).
[2026-08-27 ~07:45Z web400c DONE — DoD ВЫПОЛНЕН] Оптимизация принята автором: 3 полных цикла 111.6/110.6/113.7s (<120s DoD; было 272s), capture медиана 78.8s (-38%), verify 18.6s (-77%), restore-dry 14.2s (-78%). Средства: параллельные дампы 3 кластеров, pg_dump|pigz|openssl без промежуточного plaintext, sha256 через tee в потоке, VERIFY_JOBS=3 (4 хуже — замерено), atomic .part->rename. OPENSSL_ITER=200000 НЕ снижен (бенч: 220ms/KDF, узкое место — pg_dump 1.4GB, не крипта). Негативы автором: flipped byte/manifest/wrong key PASS без plaintext. Старая версия сохранена bin.pre-web400c-20260827. Отчёт M1:~/waves-m1/WEB400C-REPORT.md, серия на Pi .web400c-acceptance-20260827T073415Z. ЗАПУЩЕНА независимая приёмка accmigc (M1, luna xhigh ≠ автор-sol): свои 2 цикла, свои негативы + новые классы (SIGKILL-воркер без частичной публикации, состав 15 классов, атомарность latest, diff на тихие ослабления).
[2026-08-27 ~09:24Z web400d GO — критдефект accmigc закрыт] КОРЕНЬ: profiled() вызывал worker в conditional-контексте "$@" || status=$? — Bash не применял errexit внутри worker-функции, поэтому убитый pg_dump маскировался успешным хвостом openssl|tee|sha256sum, и record_artifact публиковал согласованный SHA УСЕЧЁННОГО ciphertext. ФИКС: capture_artifact_pipeline() в bin/common.sh — FIFO + отдельные PID для producer/pigz/openssl/tee/sha256sum, КАЖДЫЙ обязательно wait-ится и логируется; любой сигнал/ненулевой статус ребёнка делает артефакт и весь snapshot неуспешными (нет публикации, latest не двигается). Дефектный evidence-snapshot 20260827T080058Z сохранён. Отчёт M1:~/waves-m1/WEB400D-REPORT.md. Следом — независимая перепроверка (не автор).
[2026-08-28 05:13Z tickacc6] ВЕРДИКТ: частично доказано
Что проверил: прочитал EVIDENCE/WEB400-RESUBMIT-20260827T182814Z/README.md, WEB400D-REPORT.md, 02-report-derived-results.txt, 03-live-pi-read-attempt.txt, 04-a1-raw-artifact-inventory.txt; выполнил sha256sum -c 01-report-custody.sha256; повторил read-only ssh -p2046 pi@mailhub.duckdns.org и TCP-проверку Pi.
Что увидел: 9/9 локальных копий трёх отчётов прошли custody-проверку; WEB400D содержит два post-fix цикла: full wall 116.950 s и 118.916 s, а также описывает негативы SIGKILL/byte/manifest/wrong-key. Но в локальном пакете нет ни одного raw .web400d-* лога и нет staging-копии; фактическое восстановление --apply не выполнялось, есть только restore-dry. Текущий Pi не прочитан: TCP 86.45.55.126:2046 timeout, ssh rc=255/команда не исполнилась.
Отрицательный тест: попытался пробить текущий Pi read-only подключением ssh -o BatchMode=yes -p2046; получил timeout до SSH-сессии, поэтому текущие latest/latest_verified, marker, manifest и restore проверить не удалось. Исторические kill/flip/wrong-key negatives приняты только как содержимое проверенного отчёта, не как свежий самостоятельный прогон.
Свежесть: не относится доказательно к текущему l63: пакет зафиксирован 2026-08-27T18:32Z, посадка l63 заявлена на 2026-08-27T225328Z; текущий l63 и v4-0a4f8c87 на Pi не подтверждены из-за недоступности Pi.
Чего не хватает: после l63 снять с Pi release/commit/label и sha256 текущих snapshot/verify/restore scripts; выполнить и сохранить raw-треки трёх последовательных healthy capture -> strongest verify -> restore циклов; подтвердить свежий latest_verified_age <=300 s; доказать disposable apply-restore Signal/app/bot с before/after inventory без plaintext вне bounded root; приложить raw manifests/logs и отдельную point-in-time consistency квитанцию. До этого статус должен быть in_progress.
[записал координатор: волна tickacc6 не смогла дописать сама — с A1 НЕТ сетевого доступа к M1 (ssh timeout), выводы перенесены вручную]
[28.08 ~13:20Z Фабл] web400b сдана: исполнимая процедура учения восстановления, 58 КБ, все шаги с командой/ожиданием/веткой «если иначе»; секреты через PGPASSFILE 0600, не в argv (замечание ACC400 №1). Заряжена приёмка **acc400b** (очередь 257): все 8 замечаний ACC400 построчно + сухой проход глазами + согласованность с WEB410. После GO — реальный прогон учения координатором с timings.tsv. Отчёты: WEB400B-REPORT.md, будет ACC400B-REPORT.md.
[28.08 14:10Z Фабл] ACC400B = NO-GO, 6 дефектов: timings.tsv существует только текстом (нет цикла трёх прогонов worst-of-three), 0600 не проверяется, pg_service.conf не создаётся на свежем RUN_ROOT, гейт extensions парсит выровненный вывод wc-l-ом, есть пути «непроведённая проверка = успех» (set +e lsof, permissive grep), реальный secondary restore не исполняется. Заряжен web400c (очередь 259). Отчёты: ACC400B-REPORT.md, будет WEB400C-REPORT.md.
[28.08 17:25Z Фабл] ACC400C = NO-GO: остаточные, включая 2.1 — sha256sum manifest.json из-под cd делает READY несовместимым с реальным hash-файлом. Заряжен web400d (очередь 266) с обязательным сухим прогоном на фикстуре. Отчёты: ACC400C-REPORT.md, будет WEB400D-REPORT.md.
[28.08 20:15Z Фабл] ACC400D = NO-GO: awk match++ — WAL gate синтаксически неисполним (match — встроенная функция). Заряжен web400e: исполнение каждого гейта на фикстуре, bash -n больше не доказательство. Отчёты: ACC400D-REPORT.md.
[28.08 WEB400G СДАН — B1 закрыт fixture-only, приёмка ACC400H идёт]
Автор web400g (дерево /home/ubuntu/waves/wt-web400b, отчёт /home/ubuntu/waves/WEB400G-REPORT.md): валидатор source/target/secondary блокирует transaction-control и side-effect function classes; обход через string/dollar-quoted комментарии закрыт; фикстура PASS=40 BLOCKED=72 FAIL=0 TOTAL=112 RC=0 (повторено); все 23 bash-блока проходят bash -n. Вердикт автора честный: WEB400G_VERDICT=B1-CLOSED_FIXTURE-GATES-PASS_OPERATIONAL-PROOF-NOT-RUN.
Приёмка ACC400H (A1, очередь 283) с главным вопросом по правилу «зелёное на фикстурах ≠ польза»: вызывается ли валидатор реальным путём исполнения (цепочка вызовов файлами:строками) или живёт только в тесте — если нигде не подключён, это находка первого класса. Плюс СВОИ обходы (вложенные/незакрытые комментарии, смена dollar-tag, омоглифы, регистр, переносы внутри токена, CTE/DO, точка с запятой в литерале), негативный тест, три вопроса.
[28.08 ~18:58Z ACC400H: NO-GO — валидатора нет в продукте, он живёт в отчёте]
Отчёт: /home/ubuntu/waves/ACC400H-REPORT.md.
ГЛАВНАЯ НАХОДКА (ровно по правилу «зелёное на фикстурах ≠ польза»): production caller валидатора НЕ СУЩЕСТВУЕТ. validate_sql_source живёт только в markdown-процедуре и в acceptance-фикстуре. Цепочка: WEB400E-fixture-gates.sh:8-12 через awk вырезает первый bash-блок ИЗ WEB400B-REPORT.md и делает eval; определения source/target/secondary лежат в markdown на строках 167-278, 2066-2161, 3427-3522; вызовы — в исполняемых сниппетах отчёта (911-929, 2270-2271, 2805/2819, 3935/3940). Поиск по src/scripts/tests/package.json: RUNTIME_VALIDATOR_SEARCH=NO_MATCHES. Зелёные 40/72/0/112 не защищают реальную миграцию — этот код в ней не участвует.
Ещё: межстрочный обход `START\nTRANSACTION` проходит валидатор; два `2>/dev/null` (WEB400B-REPORT.md:1329 fence rollback write, :1333 wait DB fence process) — глушение = блокер. Негативный тест приёмки покраснел как положено.
Круг починки запущен: WEB400H (очередь 285) — вынести валидатор из markdown в НАСТОЯЩИЙ модуль (один источник истины), заставить фикстуру вызывать модуль вместо awk+eval из отчёта, ПОДКЛЮЧИТЬ к реальному пути миграции (контур WEB-409/410; если единственный реальный путь — ручная процедура owner-а, сделать обязательный gate-скрипт и дать owner-у точную команду), закрыть межстрочный обход нормализацией и токенным распознаванием, убрать оба глушения.
[28.08 ~19:30Z WEB400H СДАН — валидатор вынесен в настоящий модуль, приёмка ACC400I идёт]
Коммит 4e4bb40818404b1532107e9712446b3672b5364a, дерево /home/ubuntu/waves/wt-web400h, отчёт /home/ubuntu/waves/WEB400H-REPORT.md.
Сделано: единственное определение валидатора теперь в scripts/web400/sql-validator.sh:19 (в WEB400B-REPORT.md его больше нет); добавлен исполняемый gate scripts/web400/web400-migration-gate.sh:37, который вызывает модуль. Цепочка до реального пути: локального production controller у WEB409K/WEB410L НЕТ — фактический SQL-switch остаётся ручной owner-процедурой с явными psql --file шагами, и теперь ПЕРЕД каждым из них (source WEB400B:822-839, target :2089 и :2624/:2638, secondary :3662/:3667) вызывается один и тот же gate.
Цифры: fixture PASS=40 BLOCKED=72 FAIL=0 TOTAL=112 (те же, но уже через модуль); START\nTRANSACTION и комментарийные варианты блокируются; ослабленный валидатор роняет fixture rc=1 (негативный тест валиден); suppression scan 0.
Приёмка ACC400I (очередь 291, база l65) с главным требованием: самому убедиться грепом, что тело валидатора существует ровно в одном месте и нигде не осталось eval кода из отчётов; проверить, что gate НЕЛЬЗЯ обойти молча в ручной процедуре; сочинить минимум восемь СВОИХ обходов; свой негативный тест; три вопроса.
---
## 2026-08-29 06:50Z — ACC400J: NO-GO, три НОВЫХ класса обхода
Принято: три причины ACC400I закрыты — порченый SQL (COM\nMIT, COM/*x*/MIT, кириллическая C, em-space) fail-closed rc=90 на реальном валидаторе; все psql-вызовы под гейтом; подмена WEB400_SQL_GATE отвергается; негативный тест на родителе валиден.
НОВЫЕ КЛАССЫ: (1) clustered psql-опции — web400-psql-entry.sh:136-187 проверяет только отдельные -c/-f/--command/--file, а psql трактует -Xc… и -Xf… как скрытые -c/-f; пробы -XcCOMMIT и -Xf/файл дали RC=0; (2) неполный denylist — sql-normalizer.py:20-35 разрешает любой SELECT/WITH, поэтому pg_backup_start/stop, pg_wal_replay_pause/resume, pg_replication_slot_advance, pg_log_backend_memory_contexts проходят с rc=0 вместо 90 (это не read-only); (3) WEB400_RELEASE_ROOT — путь entrypoint из внешней переменной, проверяется только «обычный исполняемый и не симлинк», digest не проверяется → поддельный release root принят (FAKE_ENTRY_ACCEPTED).
Круг WEB400K (queue/422): нормализующий разбор аргументов psql (склеенные опции разбирать и проверять), переход с denylist на allowlist для read-only контроля, проверка подлинности entrypoint (digest/подпись), негативы на все три класса.
---
## 2026-08-29 09:10Z — ✅ ACC400K: GO. SQL-страж закрыт
Принят коммит 544de778: все три класса обхода из ACC400J закрыты fail-closed (склеенные опции psql, админские функции под видом read-only, неаутентифицированный WEB400_RELEASE_ROOT). Штатный fixture rc=0, DYNAMIC_CASES=128; приёмка добавила свои независимые пробы.
Линия прошла путь: ACC400I NO-GO (3 причины) → ACC400J NO-GO (3 НОВЫХ класса) → ACC400K GO. Кандидат следующей сборки.
[BOARDTRIAGE] ACC400K GO 29.08 09:10Z закрыл три класса обхода SQL-стража; полный DoD WEB-400 — snapshot cycle, age≤5m и restore Signal/app/bot — отдельно не закрыт.
Воркер
не привязан — привязать:
curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \
-d '{"issueId":"WEB-400","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-30T17:08:02.197Z