WEB board

всеканоны и докиворкеры↗ iOS↗ Легаси
WEB-686 · Дефект · Инфраструктура · web

P1 [бэкапы Hetzner]: файлы пользователей прода A1 не копируются — ежедневная копия storage идёт со старой Raspberry Pi (файлы с 02.09)

В работе P1 · важно ведёт: — эпик: WEB-082
Суть
Найдено 24.09 при чистке Storage Box (95 %). storage-*.tar.gz.gpg пишет Pi nc-secrets-storage-backup.timer (03:40 UTC) из /home/pi/note-clone/shared/storage — не менялся с 02.09. Прод A1 /home/ubuntu/prod/shared/storage/exports (~1 ГБ, 261 файл новее 02.09) на Hetzner не попадает. Владелец 24.09 14:19 одобрил направление и попросил карту копий — приложена ниже (источник: nc-ops-scripts/BACKUP-MAP.md на ноутбуке координатора).

# Карта резервных копий NC (что, откуда, куда, как часто)

Обновлено: 24.09.2026 ~14:40 UTC (координатор). Держать актуальной при любом изменении таймеров/путей.
Хранилище: Hetzner Storage Box u656648 (порт 23), квота ~1 ТБ. Ключ и параметры — `/etc/hetzbk/hetzbk.env` на A1 (значения не печатать).
Шифрование везде: gpg symmetric AES256, парольный файл из hetzbk.env (`HETZBK_PASSPHRASE_FILE`). Без этого файла копии бесполезны — его копия у владельца.

## 1. База данных прода (A1, PostgreSQL 16)

| Что | Кто пишет | Когда | Куда на Hetzner | Сколько хранится |
|---|---|---|---|---|
| Полная копия базы (base) | A1 `hetzbk-base.service` | ежедневно 03:10 UTC (до 24.09 — каждый час) | `walg-nc/hetzbk-prod-a1/base/` | всё за 48 ч + по одной в сутки, 30 дней (`hetzbk-retention` 03:30) |
| Журнал изменений (WAL) | A1 `hetzbk-wal-stream.service` (постоянно) + `hetzbk-local-prune` 02/08/14/20:17 | непрерывно | `walg-nc/hetzbk-prod-a1/wal/` | 30 дней |
| Манифест хэшей | A1 `hetzbk-manifest.service` (цикл) | непрерывно | `walg-nc/hetzbk-prod-a1/manifests/` | 30 дней |
| Пакет окружения прода (env, секреты прода) | A1 `hetzbk-env-pack.timer` | ежечасно, только если изменился | `walg-nc/hetzbk-prod-a1/…env…` | 30 дней |
| Артефакт текущего релиза | A1 `hetzbk-publish-current.timer` | ежечасно в :23 (идемпотентно) | `walg-nc/hetzbk-prod-a1/…releases…` | 30 дней |
| Старая линия wal-g | A1 `walg-retention.timer` (00:00) | удаление старше 30 дней | `walg-nc/basebackups_005` (~1.5 ГБ) | 30 дней |

Итого: восстановление базы на любую минуту за 30 дней = base не старше суток + WAL после него.
Сторож: `hetzbk-age-alert.timer` каждые 5 мин; порог «копия устарела» `HETZBK_MAX_BASE_AGE_SEC=93600` (26 ч).

## 2. Файлы пользователей (загрузки/экспорты)

| Что | Кто пишет | Когда | Куда | Статус |
|---|---|---|---|---|
| storage-ДАТА.tar.gz.gpg | Raspberry Pi `nc-secrets-storage-backup.timer` (`/usr/local/sbin/nc-secrets-storage-backup.sh`) | 03:40 UTC | `walg-nc/hetzbk-prod/storage/` | ⚠ ДЫРА: копирует `/home/pi/note-clone/shared/storage` — старая машина, файлы не менялись с 02.09. Прод-файлы A1 `/home/ubuntu/prod/shared/storage/exports` (~1 ГБ) НЕ копируются. План: перенести задачу на A1 (владелец одобрил направление 24.09 14:19). |
| secrets-ДАТА.tar.gz.gpg | тот же скрипт Pi | 03:40 UTC | `walg-nc/hetzbk-prod/secrets/` | Устаревшие секреты Pi. Секреты прода покрываются пакетом окружения A1 (п.1). |

## 3. Доска задач (M1)

| Что | Кто пишет | Когда | Куда |
|---|---|---|---|
| boards-ДАТА.tgz | M1 LaunchAgent `nc.board.backup` → `~/ops/board-daily-backup.sh` | 04:15 локально (~03:15 UTC) | A1 `/home/ubuntu/mirrors/boards/`, Pi `/tmp`, Hetzner `walg-nc/hetzbk-prod/boards/`; локально 30 дней |

## 4. Прочее

| Что | Кто | Когда | Куда |
|---|---|---|---|
| SIP-шлюз ag-voice | A1 `agvoice-backup.timer` | ежечасно в :47 | Hetzner (WEB-565/WEB-370) |
| Зеркало GitHub | M1 `github-backup-mirror` 03:00; M4 `github-ios-backup-mirror` | ежедневно | локальные зеркала |
| Стенд A2 (посевы 300/500, пулы ключей) | вручную после посева | по событию | ⚠ Сейчас на Hetzner только пул-ключи; дампа БД стенда нет. Решение: «золотой слепок» (pg_dump + файлы) после посева 500 → Hetzner. |

## 5. Старые данные (кандидаты на удаление, только с подтверждения)

- `walg-nc/hetzbk-prod/base` (115 копий, 27.08–01.09) и `…/wal` (2250 файлов) — старая линия Pi до переезда на A1.

## 6. История изменений

- 24.09: ящик 95 % → проредили base в `hetzbk-prod-a1` 508 → 66 (48 ч целиком + одна в сутки); base раз в сутки вместо ежечасно; порог возраста 2 ч → 26 ч; найдена дыра с файлами пользователей (п.2).
Чем закрывается (приёмка)
На A1 ежедневная зашифрованная копия /home/ubuntu/prod/shared/storage на Hetzner (hetzbk gpg), проверка доставки + пробное расшифрование/распаковка одного файла; задача на Pi отключена или перенаправлена; BACKUP-MAP.md обновлён.
Доказательства
Pi: find storage -newermt 2026-09-02 = 0 файлов; A1: 261 файл, 990M; hetzbk uid не читает каталог (нужен доступ на чтение).
Лента
2026-09-25T09:27:12.272Z · coordinator
[25.09 09:27Z координатор] Взят в работу волной 4988 (M1, Codex Luna): скрипт hetzbk-storage-pack + restore + юниты (таймер 03:55 UTC) + удержание 7д/4н по образцу hetzbk-env-pack, полная офлайн-проверка (roundtrip diff, if-changed, симлинк наружу, неверный пароль, нехватка места). Раскатка на A1 — отдельной волной после одобрения владельца.
2026-09-25T12:11:33.568Z · coordinator
[25.09 12:11Z координатор] 4995 (Neo, Opus 5.5, ревью 4993): VERDICT=NO-GO. BLOCKER 1 (оценка места «мало диска» занижена в 1.5–5.3 раза, пик — три полные копии на разделе WAL-спула), MAJOR 7 (гонка ретеншна с pack, застревание ретеншна, открытый незашифрованный tar на диске A1, изменение живого дерева валит день, молчаливое выпадение поддеревьев за symlink, ложный алерт возраста при --if-changed, ROLLOUT дописывает в env через >>), MINOR 14. Утечек секретов нет, ретеншн не удаляет чужие копии, тесты 7/7. Исправления → 4998 (Luna, Neo).
2026-09-25T12:14:18.635Z · coordinator
[25.09 12:14Z координатор] 4993 (Neo, Luna): VERDICT=GO. WEB-686 скрипты зашифрованной ежедневной копии файлов прода (pack/restore/retention + юниты, таймер 03:55 UTC, 20 Мбит/с): круговой прогон diff пуст, повтор без изменений пропускается, symlink-выход блокируется, неверный пароль падает, мало диска RC 5, ретеншн 7/4, shellcheck 0, существующие режимы hetzbk не изменены. Выкат НЕ делался (операторский, по ROLLOUT.md). Ревью 4995 (Opus 5.5, Neo). Копия сдачи на ноутбуке координатора.
2026-09-25T12:43:26.584Z · coordinator
[25.09 12:43Z координатор] 25.09 12:45Z — АВТОПОДХВАТ ДОСКИ ВКЛЮЧЁН (решение владельца, «делай как говоришь»).
Команде знать ничего не нужно: адрес тот же — https://bugs.wool2.online.
Как работает:
• Малинка (nginx) раз в минуту проверяет M1 (systemd nc-board-watchdog.timer).
• M1 молчит 5 минут подряд → на Малинке сама стартует копия доски, адрес переключается на неё, владельцу приходит сообщение в Telegram.
• Синк M1 → Малинка теперь каждые 5 минут (было 15). Потеря при падении — не больше 5 минут записей.
• Пока доска работает на Малинке, синк с M1 выключен (чтобы не затереть новые записи).
• Возврат на M1 — вручную, на M1: bash ~/ops/board-failback.sh (забирает базы с Малинки, прежние кладёт в *.pre-failback-*, переключает адрес обратно).
Проверено учением в песочнице (/tmp, порт 8799, боевой nginx не трогали): 5 промахов → старт доски → конфиг адреса переключён → сообщение → доска отвечает 200.
Файлы: Малинка /home/pi/board-standby/{watchdog.sh,README.txt,state,watchdog.log}; M1 ~/ops/{board-standby-sync.sh,board-failback.sh}.
Не проверено вживую: настоящее отключение M1 (не роняла сознательно). Возврат (failback) не прогонялся.
2026-09-25T13:03:31.012Z · coordinator
[25.09 13:03Z координатор] 25.09 13:10Z — волна 4998 (Neo, Luna) — исправления WEB-686 r2 по ревью 4995: GO, манифест сверен.
BLOCKER 1/1 (предпроверка места: 3 консервативных размера tar + резерв WAL 1 ГиБ, нехватка → RC 5 до упаковки), MAJOR 7/7 (потоковая упаковка/восстановление, общий lock pack/retention, идемпотентная ретенция, повтор при изменении дерева, видимые исключения symlink, heartbeat при unchanged, атомарная замена при выкатке), MINOR 8/14 (6 с причиной в fixes.md).
Тесты 18/18 (17 репро из ревью как тесты + roundtrip/retention), diff roundtrip пуст, ShellCheck 0, секретов не печаталось. Только офлайн-заглушка транспорта — на прод НЕ выкатывалось.
Дальше: независимое повторное ревью, затем выкатка на A1 по ROLLOUT.md по слову владельца.
Доказательства: Neo ~/waves/4998WEB686R2FIXES-evidence (ROLLOUT.md, patches, tests.md); коммит 4478b6e поверх 3f4a10f.
2026-09-25T13:39:55.649Z · coordinator
[25.09 13:39Z координатор] 2026-09-25 13:39Z — 5001: окончательное независимое ревью NO-GO, DONE есть, SHA256SUMS RC=0. BLOCKERS=0 MAJOR=5 MINOR=3; подтверждено 5/8 исправлений, точный запуск 17/18. Найдены гонка fallback lock, ошибка восстановления относительного symlink, установка синтаксически битого env, остатки незавершённой выгрузки, частичное дерево при сбое restore. Выкатка на A1 не разрешена этим вердиктом. Доказательства M1 ~/waves/5001REVIEWWEB686R2-evidence/.
2026-09-25T13:56:06.707Z · coordinator
[25.09 13:56Z координатор] 2026-09-25 13:56Z — исправления пяти MAJOR и трёх MINOR приёмки 5001 переданы в волну 5003 на Neo. Только offline; прод не меняется. Предыдущий NO-GO остаётся в силе до сдачи и независимой проверки.
2026-09-25T14:40:10.633Z · coordinator
[25.09 14:40Z координатор] 2026-09-25 14:40Z 5003: отчёт, DONE, dispatcher done и полный SHA256SUMS RC=0 проверены. Автор заявляет 42/42, это ещё не независимая приёмка. 5007 независимая приёмка Astra xhigh запущена на M1: START=2026-09-25T14:38:54Z, w5007 жив. WEB-686 rollout не выполнялся и остаётся только по слову владельца.
2026-09-25T15:06:47.562Z · coordinator
[25.09 15:06Z координатор] 2026-09-25 15:06Z 5007 независимая приёмка WEB-686: VERDICT=NO-GO, TESTS=54/67, FINDINGS=7. REPORT, BACKUPR3REVIEW_DONE, EXIT=0 (15:03:17Z) и полный SHA256SUMS из evidence проверены, RC=0. Блокеры: reclaim удаляет lock нового владельца; зависимые symlinks дают невосстанавливаемый архив; оценка ciphertext занижена; rollback оставляет signature/catalog; env helper игнорирует chmod failure; known-hosts directory не останавливает процедуру; activating oneshot не блокирует rollout. Исходные тесты проходят, новые репро обнаруживают нарушения. Linux/systemd и реальный SFTP не проверены. Авторский ROLLOUT_SAFE не подтверждён независимой приёмкой. Выкладки не было. Следующий раунд должен исправить семь воспроизводимых дефектов и повторить исходные и независимые репро. Evidence: m1:/Users/poolpooly/waves/5007BACKUPR3REVIEW-evidence; bundle: /Users/poolpooly/waves/5007BACKUPR3REVIEW.bundle.
2026-09-25T15:25:07.599Z · coordinator
[25.09 15:25Z координатор] 2026-09-25 15:25Z 5008 WEB-686 r4 запущена на M1: START=2026-09-25T15:24:37Z, w5008 жив, gpt-6-astra/xhigh. Материал 5003 и независимая приёмка 5007 скопированы в новый 5008-material, оба полных SHA256SUMS проверены RC=0. Бриф прошёл brief-guard. Задача: исправить семь воспроизводимых блокеров с RED/GREEN и повтором исходных/независимых тестов. Только отдельное локальное дерево, production rollout запрещён. Сдача ожидается: ~/waves/5008BACKUPR4FIXES-REPORT.md, evidence/SHA256SUMS, BACKUPR4FIXES_DONE и EXIT в 5008-backup-r4-fixes-codex.log. Запуск не означает приёмку или устранение блокеров.
2026-09-25T15:56:59.954Z · coordinator
[25.09 15:56Z координатор] 5008: авторский GO, FIXED=7/7, TESTS=76/76. REPORT, BACKUPR4FIXES_DONE, EXIT=0 и полный SHA256SUMS RC=0 подтверждены. Прочитан tests.md: на кандидате 29 исходных независимых cases выполнены непосредственно, 3 адаптера изменены в R4; поэтому это не независимое повторное принятие всех 32 без изменений. Нужна проверка адаптеров и исправлений; Linux/systemd/SFTP NOT_RUN. Rollout не выполнялся и не разрешён этой сдачей.
2026-09-25T16:16:18.664Z · coordinator
[25.09 16:16Z координатор] 5010 Linux backup review на A1 завершился WAVE_EXIT=1: соединение Codex с backend refused, после started процесс не удержался. Приёмка НЕ выполнена. Материалы с полным SHA256SUMS RC=0 перенесены на A2; повтор 5012 через dispatch-wave.sh прошёл guard/preflight и поставлен в очередь. Старт 5012 пока НЕ подтверждён.
2026-09-25T16:22:07.218Z · coordinator
[25.09 16:22Z координатор] 2026-09-25 16:22Z По прямому указанию владельца независимые волны запускать параллельно с учётом каталога, ресурсов, модели и статуса. Основной исполнитель Luna; Terra/Sol для сложных исправлений; Astra для сложной диагностики и независимой приёмки. Не назначать Astra всем задачам по умолчанию. Причина ожидания 5012 подтверждена в dispatcher: DISPATCH: SOLO блокировал старт при running>0. У ожидающего, ещё не active брифа снят SOLO; guard/preflight и штатная dispatch-wave.sh пройдены. 5012 started 16:21:45Z, running=2, PID 1811768. 5011 PID 1784678: параллельно на A2. На M1 5009 CPU и 5013 роли, логи обновляются, EXIT не найден. 5010 A1 завершился transport failure; A1 не считать работающим. Ограничения ресурсов не менялись; прод и rollout не изменялись.
2026-09-25T16:54:52.969Z · coordinator
[25.09 16:54Z координатор] 2026-09-25 16:54Z 5012 Linux backup review: REPORT прочитан, полный SHA256SUMS RC=0, WAVE_EXIT=0 и dispatcher done 16:40:16Z подтверждены. GO ограничен выполненными Linux fixtures, 91/91 unique checks. Две находки в тестовых адаптерах, native raw suites не полностью зелёные; независимое покрытие описано в отчёте. Реальные systemd/SFTP/privileged restore NOT_RUN. Rollout не выполнен и остаётся только по слову владельца.
2026-09-25T17:57:18.282Z · coordinator
[25.09 17:57Z координатор] 
## WEB-686 2026-09-25 17:57Z — разрешение владельца и реальная проверка
- Владелец 25.09 17:38:18Z явно разрешил приступить к настоящему бэкапу и включению после проверки. Прежнее ожидание разрешения отменено.
- A1 /opt/web686-r4: отдельная установка, общие backup scripts/env не заменялись. Полный storage: 4989 файлов, 2806272593 bytes, skipped=0. Дополнительно video-assets, legacy public/podcasts и S3: 271 объект, 406367098 bytes.
- Все четыре pack завершились RC=0 с обратным чтением Hetzner. video/legacy/cloud restore RC=0, все 271 восстановленных cloud object hashes проверены. storage restore ещё работает в bg5kiui3m; НЕ запускать дубль.
- web686-backup.service установлен и запущен вручную 17:56:31Z для проверки реального sandbox. daily backup и age timers установлены, НЕ enabled. Доставка предупреждений тестом PASS; notify.json root-only. После storage restore и успешного service run включить оба таймера.
- 5026 roles NO-GO: SMOKE_REQUIRED FAIL, review evidence исчез с M1 при повторном чтении; нужен сохранённый bundle/report и repro, live NOT_RUN. 5025 R2 PASS, D1 offline кандидат требует отдельной приёмки; manifest RC=0, dispatcher done 17:49:42Z. 5024 CPU build ещё без финального REPORT, A2 около 6 GiB; не добавлять сборок.
2026-09-25T18:02:58.008Z · coordinator
[25.09 18:02Z координатор] 
## WEB-686 2026-09-25 18:02Z — бэкап включён после реального восстановления
- Все четыре pack и restore RC=0: storage, video, legacy, cloud. Сверены manifest, содержимое, права и UID/GID; отдельно 271 cloud object SHA256 PASS. Initial receipt сохранён на A1 и ноуте в web686-recovery-20260925.
- Полная daily процедура под реальным systemd sandbox завершилась 18:01:51Z: inactive/dead, Result=success, ExecMainStatus=0, DAILY_COMPLETE присутствует. Retention выполнен, removed=0.
- web686-backup.timer и web686-backup-age.timer enabled/active. Следующий backup 26.09 03:55:46 UTC; контроль свежести каждые 15 минут, порог 36 часов. Age service Result=success/ExecMainStatus=0; реальная доставка Telegram проверена. nc-a1.service active/running.
- Recovery tools и инструкция сохранены вне A1: /Users/annakorin/nc-ops-scripts/web686-recovery-20260925/recovery-tools.tar. Секреты в этот архив не включены. Восстановление S3 проверено до локальных bytes+map; импорт в новый bucket и полное аварийное восстановление приложения не проверялись. WEB-686 не выдавать за полный DR.
2026-09-27T15:30:29.906Z · coordinator
[27.09 15:30Z координатор] Сюда слит WEB-082 (бэкап-контур, WAL-G+restic, restore-drill) по слову владельца 27.09 15:28Z.
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-686","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-09-25T09:27:12.613Z