WEB board

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

⭐ ИНФРА-КАРТА: где что живёт, ездит и хранится (постоянный тикет, обновлять при изменениях)

На проверке P1 · важно ведёт: —
Суть
# WEB-420 — ⭐ ИНФРА-КАРТА: где что живёт, ездит и хранится (постоянный тикет)

Правила обогащения: WEB-449. Предложение нового тела — координатор вставляет целиком в `body` (append, старый текст сохранить). Неизвестное помечено «(не подтверждено)».

---

**Якорь прода (13.09.2026):** живая линия **l115j**, коммит **133fe00cb0f86982eea1fd6d2029e5b35fcc9e40**, флип **08:50Z 13.09**. Линия: l115g → l115h → l115i → l115j. Прежний якорь l103 `d91ebefe` (04.09) устарел.

## СУТЬ
Единый справочник инфраструктуры: какие машины, что на каждой живёт, где прод/стенды/бэкапы/доска/паспорт, какие таймеры, дворники и роли БД. Обновлять при каждом изменении устройства.

## 1. МАШИНЫ
| Машина | Доступ | Что живёт |
|---|---|---|
| **A1** (Oracle Ashburn, прод) | `ssh a1nc` (129.213.25.105; наружу только прокси 3129; GitHub закрыт забором) | ПРОД веб: `nc-a1.service` (127.0.0.1:3010) + `nc-a1-b.service` (:3012) за nginx upstream `nc_backend` (ip_hash); БД Postgres `127.0.0.1:55432` (база `noteclone`); Redis; SIP `ag-sip-native.service`; диспетчер волн `/home/ubuntu/waves/queue/{active,running,done,hold,retired}`; репозиторий `/home/ubuntu/nc`; hetzbk-скрипты `/usr/local/libexec/hetzbk-*`; дворник диска `wave-disk-janitor` (каждые 10 мин) |
| **A2** (Oracle Ashburn, пассив) | (не подтверждено) | Пассив + PG16-standby (лаг ~0); capacity-стенд R3 (своя PG/Redis/провайдер-заглушка); дворник диска `wave-disk-janitor` (поставлен 13.09, порог 16 ГБ) |
| **M1** (Mac) | `ssh poolpooly@192.168.1.74` | Доска `web-board.sqlite` + HTTP API `http://127.0.0.1:8787/api/web/*`; паспорт `~/Projects/nc-agent-ops/board/`; автозапуск launchd `nc-agent-board` |
| **M4** (Mac) | `ssh milamarty@192.168.1.114` (в карте 04.09 был 192.168.1.110 — перепроверить) | Второй воркер волн; worktree прод-исходника; внешний диск `/Volumes/M4Ext` (объёмные артефакты/бандлы) |
| **Neo** (Mac) | `ssh neo`, пользователь `limamarty` | Воркер волн (добавлен 10.09): репо `/Users/limamarty/waves/.repo-NEOLINE`, LaunchAgent `com.nc.wavedispatcher`, MAXWAVES=2, MINFREE_GB=20 |
| **Pi** (малинка) | `ssh -p2046 pi@mailhub.duckdns.org` | Дев после переезда; cold standby (WEB-490) — текущая роль (не подтверждено) |
| **Ноут координатора** (Mac Intel, annakorin) | — | Подпись релизов, креда OCI/AWS, push на GitHub `line/current` |

## 2. ПРОД (веб)
- Якорь: l115j `133fe00c` (13.09 08:50Z).
- Два веб-процесса за nginx: `nc-a1.service` (:3010) и `nc-a1-b.service` (:3012), upstream `nc_backend` с ip_hash (engine.io polling и document-session живут в процессе). Посадка `land-lNNN.sh` флипает nc-a1, nc-a1-b, nc-a1-indexing, nc-a1-extraction и перезапускает бэкенды по очереди.
- БД: приложение ходит ролью **`web`** (НЕ суперпользователь) с 12.09 22:15Z. Роли посажены (см. §7).
- SIP: `ag-sip-native.service` («Antigravity SIP native stack»).

## 3. СТЕНДЫ
- **A2 capacity-стенд R3** — своя PG/Redis/провайдер-заглушка (нагрузочный/приёмочный контур).
- **M4 worktree прод-исходника** — чтение/правка исходника прода, воркер волн.
- **M1** — паспорт и доска `:8787`.

## 4. БЭКАПЫ
- Контур Hetzner Storage Box (u65664). Скрипты на A1 `/usr/local/libexec/hetzbk-*`, исходники в репо `infra/hetzbk/bin/`: `hetzbk-publish-artifact`, `hetzbk-manifest`, `hetzbk-wal-stream`, `hetzbk-restore`, `hetzbk-rehearse`, `hetzbk-retention`, `hetzbk-env-pack`, `hetzbk-env-unpack`, `hetzbk-verify`, `hetzbk-warm-pull`, `hetzbk-age-alert`, `hetzbk-base`, `hetzbk-prod-facts`, `hetzbk-artifact-unpack`.
- Таймеры (systemd, исходники `infra/hetzbk/systemd/`): `hetzbk-base` (base почасовой, `OnCalendar=hourly`), `hetzbk-wal-stream` (WAL непрерывно, сегмент ~3–4 мин), `hetzbk-manifest` (манифест каждые ~4 мин, `HETZBK_MANIFEST_INTERVAL_SECONDS=240`), `hetzbk-warm-pull` (тёплая база на A2, каждые 10 мин), `hetzbk-retention` (ежедневно 03:30), `hetzbk-age-alert` (каждые 5 мин), `hetzbk-env-pack` (почасовой).
- Публикация артефактов на Hetzner: таймер **`hetzbk-publish-current`** (ежечасно), `HETZBK_ENV_LINE=l115j-133fe00c` (хостовый таймер — в репо не найден).
- Вторая копия досок/паспорта: ежедневный снимок → A1 `/home/ubuntu/mirrors/boards/` + Hetzner `/home/board-backups/`.

## 5. ДОСКА И ПАСПОРТ
- Доска: M1 `web-board.sqlite` (таблицы web), HTTP API `http://127.0.0.1:8787/api/web/*`, id `WEB-NNN`. НЕ `board.sqlite` (iOS/легаси).
- Паспорт: публикуется из M1 `~/livepush/docs/PASSPORT/`; публикация — шаг после приёмки паспорта.

## 6. ТАЙМЕРЫ И ДВОРНИКИ (сводно)
- `hetzbk-publish-current` — ежечасно (публикация артефактов/бандлов).
- `wave-disk-janitor` — дворник диска на A1 (каждые 10 мин) и A2 (поставлен 13.09, порог 16 ГБ).
- Диспетчеры волн: A1 `/home/ubuntu/waves/queue/…`; воркеры — M4 и Neo.

## 7. РОЛИ БД (посажены 12.09 22:15Z)
| Роль | Назначение | Источник |
|---|---|---|
| `web` | приложение (DATABASE_URL): DML на app-таблицах, без миграционного леджера | `ops/sql/web078_roles.sql:51,74` |
| `web_migrator` | миграции (INHERIT web_owner, CREATE на public) | `ops/sql/web078_roles.sql:57,78` |
| `web_backup` | `pg_dump` (SELECT-only) | `ops/sql/web078_roles.sql:63,82` |
| `web_owner` | NOLOGIN группа-владелец объектов | `ops/sql/web078_roles.sql:45,71` |
| `analytics_readonly` | SELECT-only на 10 таблицах аналитики | `ops/sql/analytics_readonly_role.sql:36` |

Контракт ролей: `docs/runbooks/DB-LEAST-PRIVILEGE-ROLES.md:8-14`.

## 8. ПРОЧЕЕ / СТАТУС
- Эпик безопасности **WEB-515 закрыт 77/77**.
- Диспетчер волн и его пороги: WEB-478 (MINFREE_GB стережёт память, а не диск — гейта по диску нет).
- Ловушка Neo: Клод читает связку ключей только из launchd gui-домена; из ssh — «Not logged in».

## СЛЕДУЮЩЕЕ ДЕЙСТВИЕ
Держать якорь свежим при каждой посадке (линия + коммит + время флипа). Если строка якоря не совпадает с текущим прод-коммитом — карта устарела.

---

## история (тело до 13.09.2026)

## Суть
Постоянная инфра-карта: где что живёт, ездит и хранится. В done не переводится — держать свежей при каждом изменении (линия + коммит + время флипа).

## Где мы на 12.09
- **Якорь прода:** линия **l115g**, коммит **9c8a97629b3f7fd8c79faff162eed482670f499d** (12.09). По дереву `wt-wash-l115g` это integration-кандидат поверх l115f-r2 `92f7ae80`, а не собранный релиз — сборка/флип в дереве не зафиксированы. Прежний якорь l103 `d91ebefe` (04.09) устарел.
- **Доска:** живая — `web-board.sqlite` + HTTP API `http://127.0.0.1:8787/api/web/*` на M1 (`ssh poolpooly@192.168.1.74`), id `WEB-NNN`. ЛЕГАСИ/iOS — `board.sqlite` (таблицы `issues`/`ios_issues`, 2225 строк); поиск `WEB-086` там ничего не находит.
- **Веб-контур (A1):** два процесса одного релиза за nginx — `nc-a1.service` (:3010) и `nc-a1-b.service` (:3012), upstream `nc_backend` c `ip_hash` (обязательно: engine.io polling и document-session живут в процессе). Drop-in `nc-a1.service.d/ent093.conf` (DB_POOL_ROLE, NC_RELEASE_DIR, NC_RELEASE_ID) обязан быть ИДЕНТИЧЕН у обоих. Ранбук: `~/Projects/nc-agent-ops/board/specs/INFRA-A1-TWO-BACKENDS-LB.md`.
- **Прод-БД (A1):** `sudo -u postgres psql -p 55432 -d noteclone` (владелец `noteclone`, ~570 таблиц Prisma-схемы); тикетов там нет, доска отдельно на M1. Роли наименьших прав описаны в `docs/runbooks/DB-LEAST-PRIVILEGE-ROLES.md` (`web` — CONNECT/USAGE/DML без DDL, `web_migrator`, `web_backup`, `web_owner` NOLOGIN-группа). Переключение рантайма приложения на роль `web` — установочный/эксплуатационный шаг, не код кандидата; сдвиг «app под `web` с 12.09 22:15Z» в дереве не подтверждён (не подтверждено).
- **Бэкап-контур:** скрипты на A1 в `/usr/local/libexec/hetzbk-*` (`hetzbk-publish-artifact`, `hetzbk-publish-release`, `hetzbk-manifest`, `hetzbk-wal-stream`, `hetzbk-restore`, `hetzbk-rehearse`, `hetzbk-retention`, `hetzbk-env-pack/unpack`, `hetzbk-verify`, `hetzbk-warm-pull`, `hetzbk-age-alert`, `hetzbk-base`), таймер `hetzbk-manifest.service`. Хранилище — Hetzner Storage Box, WAL — каждые 3–4 мин. Таймер «hetzbk-publish-current» в дереве не найден (не подтверждено).
- **Волны:** диспетчер A1 (`/home/ubuntu/waves/queue/{active,running,done,hold,retired}`, журнал `queue/dispatcher.log`). Воркеры: M4 `192.168.1.110`, Neo (`ssh neo`, `/Users/limamarty/waves/`, MAXWAVES=2, MINFREE_GB=20, диск 125 ГБ). Волна, которой нет в очереди A1, скорее всего на M4/Neo.
- **Стенды A2 (R3 capacity-3516):** в дереве не подтверждено (не подтверждено).
- **Телефония:** живой SIP-шлюз `/home/pi/ag-voice` на A1, юнит `ag-sip-native.service`. 🔴 Живой шлюз опережает релизную линию ~2 500 строк; деплой зеркалит с удалением — посадка релиза на `infra/sip/gateway/` уничтожит работающую телефонию.
- **M1:** занят (8 ГБ + ~2.5 ГБ swap), два доп. слота не запущены. AWS rehearsal: ARM в eu-west-1 есть, третья машина не подтверждена/не приобретена.

## Остаток
1. Обновлять якорь прода при каждой посадке (линия + коммит + время флипа).
2. Подтвердить не подтверждённые в дереве пункты: роль `web` на проде, таймер hetzbk-publish-current, стенды A2 R3 capacity-3516.

## Кто/что дальше
Координатор дописывает карту при каждой посадке; если строка якоря не совпадает с текущим прод-коммитом — карте верить нельзя.

## История
- 04.09: якорь l101 `d825eb0d` → l102 `1b081c5a` → l103 `d91ebefe` (13:51Z) → l104 `4d858be7` (18:03Z).
- 04–05.09: на A1 два веб-процесса (nc-a1 :3010 / nc-a1-b :3012) за nginx с ip_hash.
- 06.09 23:02Z: посажен l113j `46393080` (EMAIL_TRANSPORT=resend, LEGAL_DOC_MODE=real).
- 07.09: цепочка l113k `d00b8f35` → l113l `da6799b2` → l113m `8f4ba1d9` → l113n `44d8af68` → l113o `df0376cf`.
- 10.09: подключён воркер Neo; уборка мусора дисков (A1 стоял на 5 ГБ, A2 печатал skip: free=9G); ENTERPRISE2-R1:RESOURCES (эпик WEB-626).
- 10.09 18:53Z: устранены два дефекта счётчика слотов (A1 process_labels; Neo сам съедал слот).
- 12.09: якорь l115g = `9c8a9762` (integration-кандидат поверх l115f-r2).
Доказательства
[28.08 18:50Z Фабл, по поручению владельца] ЕДИНЫЙ СПРАВОЧНИК. Когда запутались — начинать отсюда. Обновлять при каждом изменении устройства.

## 1. РЕЛИЗНАЯ ЛИНИЯ КОДА (веб)
- Источник истины: GitHub github.com/fandyy2023/0x2ALabs, ветка **line/current** (+архивные line/lNN). Пуш = обязательный шаг каждой посадки (делается с ноута координатора; с A1 GitHub закрыт забором).
- Машинная сверка одной парой команд: curl прода /api/ready/paid → attestation.receipt.sourceCommit ДОЛЖЕН равняться `git ls-remote .../0x2ALabs.git refs/heads/line/current`. Сейчас оба = 6508f013 (l64).
- Бандлы линии: A1:/home/ubuntu/mirrors/line-l64-6508f013.bundle · M4Ext:/Volumes/M4Ext/ops/line-bundles/ (sha bfb117d0).
- Последний рубеж: полный src КАЖДОГО релиза лежит на самой малинке в /home/pi/note-clone/releases/arm64-lNN-*/ (l61,l62,l63,l64 на месте) — так восстановили l63 после потери.
- Рабочее дерево линии: A1:/home/ubuntu/waves/wt-l64 (канон; l65 строится от него).

## 2. ПРОД (малинка nb.wool2.online)
- Служба note-clone-3010; конфигурация релиза = systemd drop-in /etc/systemd/system/note-clone-3010.service.d/standalone-artifact.conf (ExecStart→shared/run/lNN-<sha>/enforce-start-lNN.sh; там же spend-runtime-lNN.env со ВСЕМИ sha, spend-activation-lNN.json = подписанная аттестация, nc-team-release-public.pem). Откат = вернуть .bak drop-in + restart.
- env-цепочка systemd: shared/.env (DATABASE_URL) → shared/.env.local → /home/pi/web/.env → drop-in Environment (APP_TRUST_EDGE_FORWARDED_HEADERS, APP_ALLOWED_HOSTS...).
- БД: контейнер noteclone-pgvector, 127.0.0.1:55432.

## 3. БЭКАПЫ
- БД прода → сейф Hetzner Storage Box (u65664, sftp порт 23, /home/walg-nc/hetzbk-prod/): base+WAL непрерывно (hetzbk-wal-stream.service на Pi, конфиг /etc/hetzbk/hetzbk.env, ключи /etc/hetzbk/). RPO измерен ≈3.6 мин. Проверка сегмента: sidecar sha → gpg → pg_waldump (квитанция 28.08).
- ДОСКИ (M1 ~/Projects/nc-agent-ops/board/*.sqlite) → ЕЖЕСУТОЧНО (вахта координатора, первый тик после 03:00Z, скрипт board-daily-backup.sh, БЕЗ cron — macOS-диалоги): копии на ноуте, A1:/home/ubuntu/mirrors/boards/, Hetzner /boards/. Первая копия 28.08.
- A1-БД (wal-g) → тот же Hetzner.

## 4. НОЧНОЕ ЗЕРКАЛО GitHub (M1, cron 3:30 + launchd)
- iOS: fable-ios-workspace-m4 → github fandyy2023/note-clone-ios --all: РАБОТАЕТ честно.
- Веб-часть: пушит ~/livepush (ЗАСТРЯЛ на l38 — «пересохшее русло») → TODO перенацелить на живую линию. Лог /tmp/nightly-mirror.log.

## 5. ДОСКИ И КАНАЛЫ
- Доски живут на M1 (:8787, публично bugs.wool2.online через nginx Pi). Автозапуск launchd nc-agent-board.
- Страница /passport сервится из M1:~/livepush/docs/PASSPORT/ — обновляется ТОЛЬКО публикацией туда (шаг после приёмки паспорта).
- Каналы owner-а: notify-local.sh (токен с M1) и ЗАПАСНОЙ notify-local2.sh (токен с Pi shared/.env.local, chat 488357647) — на случай смерти M1. /me-мост: me-bridge daemon (M1 launchd) → tmux claude-main (рекордер ~/fable-recorder.sh + pipe-pane страховка) → ~/fable-direct-inbox.log; после ребута M1 pane пересоздавать.

## 6. КЛЮЧИ
- Подпись релизов: ~/Downloads/v3-checkout/owner-key-kit-output-3f155f3/owner-private-key.local-only (ТОЛЬКО ноут координатора; SPKI-pin da2e5642, PEM-pin 0b83e0d3 — ДВА пина одного ключа, не путать). Публичный = nc-team-release-public.pem в run-dir прода.
- ~/nc-owner-key/owner-private.pem (cf86d782) — ДРУГОЙ ключ, НЕ релизный.

## 7. МАШИНЫ (доступ)
- Ноут координатора = МАК-ИНТЕЛ (annakorin). M1 poolpooly@192.168.1.74 (tmux в /opt/homebrew/bin, node/claude/codex в ~/.nvm). M4 milamarty@192.168.1.114 (ключ noteclone_m4_ed25519; Lima noteclone-build; tmux -L gui; внешний диск /Volumes/M4Ext). A1 ssh a1nc (Oracle 129.213.25.105; наружу только прокси 3129; GitHub закрыт). Pi ssh -p2046 pi@mailhub.duckdns.org.
Дублируется в паспорте (раздел «Линии и зеркала», после приёмки). Связанные: WEB-320 (леджер), WEB-282 (эпик миграции).

[28.08 ~19:30Z LINECANON2 СДАН — приёмка ACCLINECANON2 идёт]
Коммит 4eb016ad, дерево /home/ubuntu/waves/wt-linecanon2, отчёт /home/ubuntu/waves/LINECANON2-REPORT.md.
Обновлены CANON.json, протокол, ZERO-AGENT-ONBOARDING, 00-INDEX, CHANGELOG. Leak-scan и git diff --check чистые. Публикация НЕ выполнялась (её делает координатор после GO); stamp-canon.sh не запускался — M1 с A1 недоступен, автор написал честно.
ВАЖНЫЙ НЮАНС: сверка автора вернула DRIFT — но не потому, что линия разошлась, а потому что с A1 нет аутентификации к GitHub (сетевой забор). Координатор проверил с ноутбука: refs/heads/line/current и refs/heads/line/l65 = f87f61a78e93881b8ce2e8e8af53a1f4ab1206cc, ровно прод. Это различие («нет доступа к проверке» против «линия действительно разошлась») передано приёмщику ACCLINECANON2 как обязательное требование к документу: читатель обязан их различать.
Приёмка ACCLINECANON2 (очередь 292) проверяет блокеры круга 1: описание состояния «line/current не запушен», конфликт с CANON.json, факты про бандлы, роль livepush (наружу публикуется он, ночное зеркало веб-части тянет ИЗ него и потому отражает устаревшее), якоря ссылок, отсутствие внутренних адресов и непроверяемых утверждений.

[28.08 ~19:50Z ACCLINECANON2: NO-GO — два блокера, оба важные]
Отчёт: /home/ubuntu/waves/ACCLINECANON2-REPORT.md. Исправления круга 2 по B1/B2/N1/N2/N4 приёмка признала существенными, раздел 13 прошёл статический leak-scan.
БЛОКЕР 1: канон описывает УСТАРЕВШЕЕ состояние как текущее. После push ~19:21Z refs line/current и line/l65 = f87f61a78e93881b8ce2e8e8af53a1f4ab1206cc (= прод l65), а CANON и раздел 13 всё ещё описывают not-pushed как состояние на 28.08. Документ описывает ветвление алгоритма, но не фиксирует ОБЕ временные фазы и текущую.
БЛОКЕР 2 (шире, чем ожидалось): публикационное требование не проходит ВЕСЬ изменённый пакет паспорта — CANON.json, ZERO-ONBOARDING и исторический CHANGELOG содержат внутренние пути, адреса, SSH-детали и номера портов. Чистого раздела 13 недостаточно: публикуется пакет целиком.
Приёмщик отдельно и правильно оговорил: факт push принят как данность координатора, с A1 GitHub отвечает ошибкой аутентификации, и это НЕ следует трактовать как DRIFT.
Круг 3 запущен: LINECANON3 (очередь 301) — зафиксировать обе фазы и текущую с её SHA; заставить документ РАЗЛИЧАТЬ три состояния (не запушено / запушено и совпадает / проверить невозможно) с командой распознавания на каждое — именно на смешении первого и третьего сегодня спотыкались дважды; вычистить внутреннюю инфраструктуру из всего публикуемого пакета с таблицей «что убрано → куда переехало» (полезное знание не выбрасывать, а вынести во внутренний непубликуемый файл); собственный leak-скан по всему пакету.

[28.08 ~20:25Z LINECANON3 СДАН — leak-scan чист]
Автор: обновлены документы канона линии; leak-scan по всем категориям без совпадений (leak_scan_status=0); оба рабочих дерева чистые; публикация и next build не выполнялись; GitHub и M1 с A1 недоступны, штатный stamp не запускался — написано честно.
Ждёт приёмки (заряжу следующим тиком). Напоминание для приёмки: проверить не только чистоту от внутренних адресов, но и что документ РАЗЛИЧАЕТ три состояния линии — не запушено / запушено и совпадает / проверить невозможно, — потому что смешение первого и третьего сегодня дважды вводило в заблуждение.

[28.08 ~20:30Z ИНФРА-КАРТА: что изменилось за смену — обновление по доказанному]
ПРОД: сейчас l64 (arm64-l64-20260828T171744Z, 6508f013). l65 посажен в 20:20Z и откачен в течение часа. Возврат к l65 возможен восстановлением standalone-artifact.conf.l65-kept-<TS>; релиз, run-каталог l65-f87f61a7, подписанная аттестация и линия в трёх копиях целы.
ЛИНИЯ В ТРЁХ КОПИЯХ (для l65): GitHub fandyy2023/0x2ALabs ветки line/current и line/l65 = f87f61a7 (сверено git ls-remote с ноутбука; с A1 GitHub НЕ аутентифицируется — это не расхождение, а отсутствие доступа); бандл A1 /home/ubuntu/mirrors/l65-line-f87f61a7.bundle (verify OK, 53c994b816b17dbc); бандл M4Ext /Volumes/M4Ext/ops/line-bundles/l65-line-f87f61a7.bundle.
НОВОЕ ПРО МАШИНЫ (проверено сегодня): claude на M4 работает ТОЛЬКО внутри tmux -L gui — из ssh-сессии он «Not logged in», потому что credentials лежат в связке ключей macOS. `timeout` на macOS отсутствует. В Lima НЕТ монтирований хоста — файлы только через limactl copy. С A1 нет доступа к проду, M1 и GitHub одновременно.
ДИСК: A1 82% (36 ГБ свободно) после чистки 26 деревьев; перед чисткой головы 19 деревьев вне линии забандлены в mirrors/waves-offline-heads-20260828T185015Z.bundle (verify OK).
ДОСКИ: суточный бэкап из вахты работает (A1 + Hetzner), stamp обновляется.
ПАСПОРТ: публикация ЗАПРЕЩЕНА до чистоты пакета (LINECANON3 сдан, ждёт приёмки). Процедура публикации утверждена приёмкой ACCPASSPORT4 и записана в хендоффе №3 §6.

---
## 2026-08-29 09:20Z — БЭКАПЫ: карта после ночной проверки (обогащение по просьбе owner-а)
Связанные тикеты: WEB-082 (бэкап-контур WAL-G+restic), WEB-400 (снапшоты Pi под миграцию), WEB-320 (журнал смены).

### Что где лежит (проверено фактами 29.08)
| Что | Где основная копия | Где вторая | Периодичность |
|---|---|---|---|
| Доски (board.sqlite, web-board.sqlite, ios-board.sqlite) | A1 /home/ubuntu/mirrors/boards/boards-YYYYMMDD.tgz | ⭐ Hetzner /home/board-backups/ (залито 29.08) | ручной снимок из вахты |
| ⭐ Паспорт опубликованный (~/livepush/docs/PASSPORT на M1) | A1 /home/ubuntu/mirrors/boards/passport-published-20260829.tgz (sha a83edb04…) | ⭐ Hetzner /home/board-backups/ | НОВОЕ, до 29.08 бэкапа НЕ БЫЛО ВООБЩЕ |
| Паспорт исходный (docs/PASSPORT в репо) | git-репозиторий (wt-l59-gdiag/.git) | бандлы линий в /home/ubuntu/mirrors | с каждым коммитом |
| Артефакты релизов (me2-standalone-*.tar.gz) | Hetzner /home/app-artifact | A1 (свежие) | с каждой посадкой |
| WAL-архив прод-БД | Hetzner /home/walg-nc/basebackups_005 | — | по расписанию WAL-G |
| Линии кода (l64…l68) | git + бандлы /home/ubuntu/mirrors/*.bundle | M4Ext | после каждого фикса (правило после инцидента с l67) |

### Найденные и закрытые дыры (29.08)
1. ⚠️ Паспорт НЕ входил в суточный бэкап доски вообще (проверено: `tar tzf boards-20260828.tgz | grep -ci passport` → 0). Закрыто: снят снимок, залит на A1 и Hetzner.
2. ⚠️ Суточный бэкап доски НЕ уходил на Hetzner — там были только артефакты релизов (последние 25-27.08). То есть потеря A1+M1 = потеря доски. Закрыто: создан /home/board-backups, залиты доска и паспорт.
3. ⚠️ Сервер доски/паспорта не имел автозапуска (жил под tmux-continuum). Закрыто: launchd-служба nc.board.server (RunAtLoad+KeepAlive, логи ~/Library/Logs/nc-board-server.log|.err), проверено снаружи после переключения — доска и паспорт 200.
4. Бандл линии l67 в зеркалах был БЕЗ правки миграции (chr(31)) — пересоздан как l67-line-51d5f075-with-fix.bundle. Правило: бандлить линию после КАЖДОГО фикса, не только после слияния.

### Осталось (не сделано)
- Регулярность: заливка доски+паспорта на Hetzner сейчас РУЧНАЯ. Нужно вшить в суточный скрипт бэкапа (запускается из вахты, ≥03:00Z).
- Ротация: на Hetzner /home/board-backups нет политики удаления старых копий.


[BOARDTRIAGE] 29.08: доска и паспорт впервые залиты на Hetzner, автозапуск сервера проверен; остаются автоматизация ежедневной заливки и политика ротации старых копий.
Лента
2026-09-04T15:08:17.262Z · coordinator
04.09 координатор: в body добавлен блок «ЭВОЛЮЦИЯ 04.09.2026» — состояние, причина с доказательством, что село, что осталось, ловушки.
2026-09-05T00:40:37.077Z · координатор
ИНФРА-КАРТА, изменение 04–05.09: на A1 два веб-процесса одного релиза за nginx — nc-a1.service (3010) и nc-a1-b.service (3012), upstream nc_backend с ip_hash (обязательно: engine.io polling и document-session живут в процессе). Юниты + drop-in nc-a1.service.d/ent093.conf (DB_POOL_ROLE, NC_RELEASE_DIR, NC_RELEASE_ID) обязаны быть ИДЕНТИЧНЫ у обоих. Посадка: land-lNNN.sh flip обновляет nc-a1, nc-a1-b, nc-a1-indexing, nc-a1-extraction и перезапускает бэкенды по очереди. Проверки в тик: /api/ready снаружи ×8 = все true; polling POST через edge = ok. Ранбук с архитектурой, посадкой и таблицей траблшутинга: ~/Projects/nc-agent-ops/board/specs/INFRA-A1-TWO-BACKENDS-LB.md. Крон-юниты (nc-a1-metrics-alerts, nc-a1-webhook-inbox-retry): секрет из /etc/nc-a1/metrics-alerts.secret.env (= run/flip-cron.secret), origin http://127.0.0.1:3010 без Host, `%s` в ExecStart экранировать как `%%s`. Правила: WEB-449.
2026-09-05T02:14:12.653Z · astra-security
[BLOCK2-SEC-003-INFRA | 2026-09-05 02:14Z / 07:44 IST | astra-security]
ДЛЯ ТИКЕТА. Правила: WEB-449. Связано: [[WEB-515]] / SEC-003 [[WEB-519]].
Состояние: repository-only hardening finding; живая инфра не проверялась и не менялась.
Причина: l106-line6930c683, infra/sip/gateway/index.js:2140–2141 выбирает cleanup files по подстроке call ID. Actual handleCall native VM с benign fake fs:2pass/1fail exit1, expected1/actual3. Lease-loss control green. Это дефект изоляции выборки; live call effect, Docker и peer/listener remain Needs Proof.
Что село: тест/отчёт и дочерний тикет; новый Claude brief1904 в M1 outbox, подхват pending. Live /home/pi/ag-voice опережает baseline: переносить туда audit clone нельзя.
Что осталось: независимая изолированная проверка exact per-call ownership и fix design; никаких live gateway/SSH probes в этом пакете.
КАРТА ДОКУМЕНТОВ: M1 /Users/poolpooly/audit/reports/2026-09-05-agi-boundary-block2.md; /Users/poolpooly/audit/evidence/block2/agi/cleanup-isolation.test.cjs; /Users/poolpooly/audit/outbox/1904-agi-cleanup-boundary-brief.md.
KNOWN ISSUES / ТРАБЛШУТИНГ: cleanup selection включает соседний fixture call → Node22 --test cleanup-isolation.test.cjs с NC_REVIEW_ROOT=изолированный checkout → includes(uniqueId) :2140 → exact ownership manifest/directory proposal; приёмка pending.
2026-09-05T05:09:16.843Z · astra-security
BLOCK5-SIP-INFRA-READONLY — 2026-09-05T05:09:16.706318+00:00 / IST 2026-09-05T10:39:16.706318+05:30
WEB515 childSEC014/WEB531: source-only infrastructure delta. infra/sip/gateway/stasisSnoopIngressSpike.js11196-11210 credential-bearingARIURL→log; boundedactualstatements/inertlogger4pass1red. Stasisexecution/logaccessconditionsunmeasured; appCVSSpending. HTTP/FastAGI/ARI/RTP bindtemplates are inventory, notliveportproof. NO gatewayedits/NO editbrief/NO livecalls/journal/SSH.
Report /Users/poolpooly/audit/reports/2026-09-05-sip-block5.md; evidence /Users/poolpooly/audit/evidence/block5/sip/ .
KNOWN ISSUES / ТРАБЛШУТИНГ: pinned6930 may differfromlivegateway; no rsync/landing request. ДЛЯ ТИКЕТА: report+evidenceonly; proposalread-only; runtimeacceptanceoutsideblock5.
2026-09-05T13:09:56.347Z · astra-security
BLOCK9-AGI-READONLY-HANDOFF — 2026-09-05T13:09:56.346745+00:00 / IST=2026-09-05T18:39:56.346754+05:30

Правила обогащения WEB-449. SEC003 WEB519: независимая1912 подтвердила per-call cleanup defect только на6930 harmless fixtures (12tests5controls/7red). Runtime UNKNOWN, current gateway snapshot не исследован. Координатор1373 готовит отдельный patch/window с владельцем. Родитель не создал gateway fix-brief и не меняет живой шлюз. В WEB519 опубликованы три acceptance groups: exact ownership/shared retention; native/inert-container lease parity; lifecycle/errors/unique artifact namespace. Manifest sketch не является принятым исправлением. KNOWN ISSUES / ТРАБЛШУТИНГ: application baseline не подменяет gateway HEAD; архив /Users/poolpooly/audit/inbox/done/AGI-CLEANUP-BOUNDARY-REPORT.md, квитанции /Users/poolpooly/audit/evidence/block9/inbox-trio-receipts.json. Новый runtime snapshot/окно только по coordinator process.
2026-09-06T23:02:02.438Z · coordinator
[06.09 23:02Z координатор] 🛬 ПОСАЖЕН l113j (46393080dd885f6ff25967f18639075564e12737) на прод 06.09 22:56:47Z (nc-a1) / 22:56:54Z (nc-a1-b) без простоя (контроллер по одному бэкенду, edge ×8 = 200, paid enforce_ready на обоих). Артефакт me2-standalone-linux-arm64-46393080d-20260906T225025Z sha 44b32502…; RUN l113j-46393080; откат = l113i 18f13093. Состав (L113JPREP2 GO, 15 коммитов): BARGEINRENDERFIX 28467587 (WEB-463: рендерер знает LIVE_UPSTREAM_ENABLED → 8000 идёт в live-upstream; на шлюз ещё нужен sync-plan --apply + рестарт в окне), ROLES5BREBASE c010b7de (roles 5b: admission ≤2000 + repair карантина, ACC GO), DOCENGINECOMPLETENESSR2 e3558f48 (ACC GO), INVITE502R2 (ACC GO), controller adopt ed2b3bf2 (writer, dormant до бинаря на хосте; r2 1fc3f8a9 → l113k), единый regen activation manifest. Env по решениям владельца: APP_ALLOWED_HOSTS += 127.0.0.1:3012 (SEC-007 F1), EMAIL_TRANSPORT=resend через nc-fence (allowlist += api.resend.com:443; было: транспорт off с 25.08 — старые ключи SMTP_HOST vs новые EMAIL_*), LEGAL_DOC_MODE=real + реквизиты DMCA-1079963. Миграций новых нет (P19 189). hetzbk publish/env-pack — см. следующий комментарий. Далее: батарея ролей на l113j, dry-run репейра карантина (--in-process-repair, --conditions=react-server), SIP фаза 3.
2026-09-06T23:08:21.591Z · coordinator
[06.09 23:08Z координатор] ПОПРАВКА К ЗАПИСИ О ПОСАДКЕ l113j (координатор, проверено по дереву /home/ubuntu/waves/wt-l113j-migrate = 46393080): в l113j ВОШЛИ (18f13093..46393080, 15 коммитов): 8e04c643 fix(sip): materialize live upstream before dialplan startup (= BARGEINRENDERFIX, WEB-463), 474f66fe WEB-528 pnpm/ws floors, 71da9514/21fe91d3/06b2540c SIP config root/x64/atomic publish, 8d6073c2/44c28245 notion diagnostics, 80183b6b l113g activation migration manifest refresh, f7a65d73 WEB-477 idempotency, 1ed5d212/593db10a public landing Vary/static cache, e7697d17/09c4b152 WEB-507 composer, c258d9b7 realtime squash, 46393080 regen activation manifest. НЕ ВОШЛИ (stage 2 L113JPREP2 — «проба на отдельной копии, в bundle/HEAD не входит» из-за prisma/migration-compatibility-manifest.json baseline l113i): ROLES5BREBASE c010b7de (roles 5b + quarantine repair: в дереве нет --in-process-repair/repairQuarantinedSource), DOCENGINECOMPLETENESSR2 e3558f48, controller adopt ed2b3bf2, INVITE502R2. Следствие: прод-репейр карантина на l113j НЕВОЗМОЖЕН; всё это + PODCASTRESOLVE a1248d77 (+миграция) + UPSTREAMCTRLADOPTR2 1fc3f8a9 + VOICERETRYAFTERGATE (после приёмки) собираются в l113k с явным решением манифеста миграций (P19 contract-фаза).
2026-09-06T23:38:56.523Z · coordinator
[06.09 23:38Z координатор] КАНДИДАТ l113k СОБРАН (волна 2490 L113KPREP, A2 sol xhigh, 23:34Z): VERDICT=GO, commit d00b8f35f1699d9372616f6230c51f1e2b73c0d1 над ПРОД 46393080 (bundle L113KPREP.bundle sha 63b691a2…). Replay полных цепочек: ROLES5BREBASE (roles 5b + repair карантина), DOCENGINECOMPLETENESSR2, UPSTREAMCTRLADOPTR2 (1 конфликт в activePassiveLease.test.ts — сведён), INVITE502R2 (1 конфликт в ingest/types.ts — сведён), PODCASTRESOLVE (3 коммита; финальный конфликт в generated-файлах — снят регенерацией). Манифест миграций пересобран штатными примитивами collectMigrationSet/migrationSetSha256 (write-CLI в дереве нет): 190 миграций, last 20260906113000_web536_podcast_audio_binding_quarantine, set sha 74cecf5c…, check-migration-compatibility ok:true; отдельный коммит 8d7a7727; финальный regen activation manifest d00b8f35. Сборка l113k запущена на A2 (l113kchain.sh); посадка после PACK_OK/подписи/dry-run (миграция 190 через migrate-стадию). НЕ в l113k (ждут приёмок → l113l): overdraft cd4684cb (ACC GO, не успел в сборку), C07 ранние правки 31ea4dc3, landing e5391fea, голосовые r2, C4-RU-3 r2.
2026-09-07T00:43:50.139Z · coordinator
[07.09 00:43Z координатор] 🛬 ПОСАЖЕН l113k (d00b8f35f1699d9372616f6230c51f1e2b73c0d1) на прод 07.09 00:40:56Z (nc-a1) / 00:41:06Z (nc-a1-b) без простоя; edge ×8 = 200, paid enforce_ready на обоих; корень «/» на sixbyy.com/www/app = 307, /login /legal/dmca /api/health = 200. Артефакт me2-standalone-linux-arm64-d00b8f35f-20260907T003643Z sha 05b1cb1f…, RUN l113k-d00b8f35, attestation fable-a2-l113k (migrationSetSha256 74cecf5c…, 190 миграций — podcast web536 применена). Состав (46393080..d00b8f35, 19 коммитов, L113KPREP GO): ROLES5BREBASE (roles 5b + quarantine repair, --in-process-repair), DOCENGINECOMPLETENESSR2 e3558f48, UPSTREAMCTRLADOPTR2 (writer; хостовый бинарь контроллера — отдельное окно), INVITE502R2, PODCASTRESOLVE (3 коммита + миграция), regen migration-compatibility manifest 8d7a7727 + activation manifest d00b8f35. Откат = l113j 46393080. Ловушка сборки: чужой симлинк node_modules/node_modules в wt-l113d → prepare-standalone отказ → снят, chain-resume. Далее: prod quarantine dry-run/apply, батарея, l113l.
2026-09-07T01:02:34.327Z · coordinator
[07.09 01:02Z координатор] КАНДИДАТ l113l (волна 2508 L113LPREP, A2 sol xhigh, 00:56Z): VERDICT=GO, commit 886cf5bc над ПРОД l113k d00b8f35 (bundle L113LPREP.bundle sha 3deb50f9…): OVERDRAFTONLYFUNDED cd4684cb (WEB-521 «Б»), LANDINGREWRITEFIX e5391fea (WEB-545), C4RU3CITATIONPOOL+R2 (WEB-099), DOCENGINEPANEL 4af7884c ×3 (WEB-086), regen activation manifest. Досборка 2516 L113LPREP2 добавляет P0-фикс QUARANTINEREPAIRCLI (GREEN: CLI apply --in-process-repair без headers() — completed=2 remainingQuarantine=0, идемпотентно) и финальный regen manifest → сборка → посадка.
2026-09-07T01:14:04.333Z · coordinator
[07.09 01:14Z координатор] КАНДИДАТ l113l ФИНАЛ (волна 2516 L113LPREP2, 01:11Z): VERDICT=GO, commit da6799b2 над ПРОД l113k d00b8f35 = 886cf5bc (overdraft «Б», витрина-фикс, цитаты r2, панель/подсветка ×3) + QUARANTINEREPAIRCLI f4eb5f85 (CLI apply без headers()) + regen activation manifest; lock-hash однострочник rc 0, schema-drift rc 0. Сборка l113l запущена на A2 (~45 мин), затем подпись/dry-run/посадка; после посадки: включение PUBLIC_LANDING_ENABLED с проверкой «/», прод-репейр карантина fixture→owner, батарея.
2026-09-07T05:33:44.002Z · coordinator
[07.09 05:33Z координатор] 🛬 ПОСАЖЕН l113l (da6799b2c7acd2d7da08ce4eeb36c55ee9ce8726) на прод 07.09 05:30:25Z (nc-a1) / 05:30:33Z (nc-a1-b) без простоя; edge ×8 200, paid enforce_ready, /login 200, webhook POST 401, attestation-ошибок 0. Артефакт me2-standalone-linux-arm64-da6799b2c-20260907T014612Z sha 6a49a417…, RUN l113l-da6799b2; миграций новых нет. Состав (L113LPREP2 GO): OVERDRAFTONLYFUNDED cd4684cb (WEB-521 «Б», ACC GO), LANDINGREWRITEFIX e5391fea (WEB-545, ACC GO; PUBLIC_LANDING_ENABLED=true с посадкой), C4RU3CITATIONPOOL+R2 (WEB-099, ACC GO), DOCENGINEPANEL 4af7884c ×3 (WEB-086, ACC GO), QUARANTINEREPAIRCLI f4eb5f85 (ACC GO на M4), regen activation manifest. Откат = l113k d00b8f35. Задержка посадки: артефакт был готов 01:46Z, координатор был разлогинен 01:40–05:25Z. Сразу после: корень «/» с включённым лендингом дал 403 (origin-lock отверг внутренний host localhost:<port> rewrite) → APP_ALLOWED_HOSTS += localhost:3010/3011/3012 + rolling restart.
2026-09-07T05:48:06.730Z · coordinator
[07.09 05:48Z координатор] КАНДИДАТ l113m (волна 2529 L113MPREP, 05:46Z): VERDICT=GO, commit dc3c3612 над ПРОД l113l da6799b2 (bundle sha aa16ca7e…): VOICERETRYAFTERGATE R1→R3 (WEB-564 голосовые, ACC GO), BETAREBASE 471dc38b (WEB-511/516, ACC GO), DOCENGINEPANELR2 214fc74f (WEB-086, ACC GO); lock-hash rc 0, migration compatibility 190/190, drift gate rc 0. Досборка 2533 L113MPREP2 добавляет C07 серию R1→R3 (ACC GO) → сборка → посадка.
2026-09-07T06:07:35.391Z · coordinator
[07.09 06:07Z координатор] БАТАРЕЯ РОЛЕЙ r43b на ПРОД l113l (da6799b2, 06:15Z): 45/50 (r42 на l113j = 40/50, r41c на l113i = 45/50). C1 7/7, C3 3/3 (карантин фикстуры починен repair-CLI: 3→0 чанков), C5 10/10, C6 3/3, C9 6/6, C4 6/7, C2 10/14. Падения: C2-timeline-run-03/04/06 + C2-boundary-neither-364 — цитаты вне окна дат (no_out_of_window_citation; в boundary ещё forbidden_note_absent), C4-CROSS-both-bills — 0 цитат/нет ru_total. Первый прогон r43 дал 17/50 из-за 402 insufficient_funds: правило «Б» (overdraft только пополнявшим) закрыло фикстуру → грант $10 через dryrun-admin (ledgerEntry cmtqdhy…), повтор. Разбор падений → волна 2537 BATTERYFAILS3. Evidence: battery-all-l113l-r43b.json, cases r43b на A2 inputs/battery-r43b/.
2026-09-07T06:39:41.917Z · coordinator
[07.09 06:39Z координатор] ПОСАЖЕНО: ПРОД = l113m (8f4ba1d9) с 06:32:59Z 07.09 (флип 06:32:03–06:32:59Z, rolling через upstream-controller). Над l113l: VOICERETRYAFTERGATE R1→R3 b266be5a (WEB-564), BETAREBASE 471dc38b (WEB-511/516/543), DOCENGINEPANELR2 214fc74f (WEB-086), C07EARLYEDITS R1→R3 1841ffb1 (WEB-564/086), regen manifest. Артефакт 5078b70a… (243 790 175 байт), attestation fable-a2-l113m, activation подписана владельцем. Миграций нет (190/190). Verify: ready 3010/3012 True 8f4ba1d92, paid enforce_ready ×2, edge ×8 = 200, корни app/sixbyy/www = 200, /login 200, nb → 301, POST {} на /api/messenger/v1/telegram/webhook = 401 (реальный путь вебхука; /api/telegram/webhook даёт 403 origin-lock по замыслу), AttestationError/BACKEND_NAME = 0, nginx errors = 0, LEASE_LOST = 0, Telegram getWebhookInfo без ошибок. hetzbk /home/app-artifact/l113m-8f4ba1d9, env-pack и Pi standby на l113m-8f4ba1d9. Откат = l113l da6799b2. Расписка L113M-RELEASE-RECEIPT.md в очередях A1/A2/M4. Post-QA: волна 2542 L113MPOSTQA (A1 live).
2026-09-07T06:45:20.957Z · coordinator
[07.09 06:45Z координатор] ГИГИЕНА ДОСКИ 2535 (M4, luna, срез 06:19Z, 626 карточек): закрыто 0 — ни один из 39 активных тикетов (22 in_progress, 17 review) не имеет полного трейла посадка+приёмка+post-QA одновременно; статусы не менялись. Блокирует закрытие: (а) последняя приёмка NO-GO/круг продолжается — WEB-492, 570, 082, 320, 370, 479, 519, 501, 423, 565, 546; (б) посадка доказанного кандидата не замкнута — WEB-472, 494, 470, 489, 516, 544, 557, 564 (516/564 → l113m посажен 06:33Z); (в) нет post-QA GO по пользовательскому пути — WEB-510, 496, 467, 481, 487, 448, 450, 478, 463; (г) ждут наблюдения/владельца — WEB-503 (48 ч без breach), 333, 300. ЛОЖНО ЗАКРЫТЫЕ (37, статус не трогали): последний NO-GO — WEB-511, 521, 524, 534, 543, 556; приёмка есть, посадка не доказана — WEB-477, 502, 507, 518, 528, 530, 532, 535, 538, 539, 547, 548, 549, 550, 551, 552, 553, 554, 555, 558, 560, 567, 568; свежий блокер — WEB-497, 522, 525, 529, 559; эпики без финальной посадки — WEB-077, 284; эпик закрыт при открытых детях — WEB-395. Отчёт M4 TICKETHYGIENE-REPORT.md. Дальше: после l113n + post-QA — повторный прогон с закрытием тех, у кого трейл замкнётся.
2026-09-07T06:55:58.592Z · coordinator
[07.09 06:55Z координатор] ERRORSWEEP после l113m (волна 2547, A1, luna, срез 06:32–06:44Z): VERDICT=CLEAN — нет пользовательских 5xx/499, unhandled rejection, ECONN/timeout к БД/прокси, 429, controller failure, lease denial; 33 readiness-записи ok; nginx error.log пуст. 403 на /api/telegram/webhook и /.env — сканеры/неверный путь (реальный вебхук /api/messenger/v1/telegram/webhook = 401). Отчёт A1 ERRORSWEEPL113M-REPORT.md.
2026-09-07T07:48:49.847Z · coordinator
[07.09 07:48Z координатор] ПОСАЖЕНО: ПРОД = l113n (44d8af68) с 07:47:36Z 07.09 (флип 07:46:41–07:47:36Z rolling). Над l113m: 88afe3ca Slack signed ingress (SEC-027/WEB-544), 83de1a1d push completion (WEB-549), 85768ecb account-delete completion (WEB-551) — три «принятых, но не посаженных» фикса блока 19 (аудит 2541 → r2 2549, ACC 2545); 41aea00b фиксы батареи ролей (WEB-99: окно T-365 по UTC-полуночи, C4-CROSS; ACC 2544 GO); 8c8a7bcf сип disclosure app-часть (WEB-463/519; ACC 2539 GO). Артефакт 37f136cf…, attestation fable-a2-l113n, активация подписана владельцем, миграций нет (190/190). Verify: ready/paid enforce_ready на обоих бэкендах, edge ×8 = 200, корни ×3 = 200, /login 200, вебхук 401, AttestationError/nginx/LEASE_LOST = 0, Telegram pending 0. hetzbk + env-pack + Pi → l113n-44d8af68. Откат = l113m. Расписка L113N-RELEASE-RECEIPT.md (A1/A2/M4 queue + inputs/release). Далее: gateway host-sync v5 (кит 2556) + контрольный звонок; post-QA 2572; батарея r44.
2026-09-07T08:08:32.818Z · coordinator
[07.09 08:08Z координатор] БАТАРЕЯ РОЛЕЙ r44 на ПРОД l113n (44d8af68, 08:00–08:28Z): 47/50 (r43b 45, r42 40). C2 14/14 — фиксы окна дат (41aea00b) подтверждены на проде; C3 3/3, C5 10/10, C9 6/6, C1 6/7, C4 6/7, C6 2/3. Падения: C1-1-true-claim (cites=0), C4-CROSS-both-bills (1 цитата вместо ≥2 — частично лучше, чем 0 в r43b), C6-council-thinking (!http_200 — HTTP-ошибка, проверить транзиентность). Evidence: cases-r44.jsonl/battery-all.json на A2 inputs/battery-r44. Разбор → волна 2574 BATTERYFAILS4.
2026-09-07T08:14:40.487Z · coordinator
[07.09 08:14Z координатор] POST-QA ЖИВОГО l113n (2572, A1, sol xhigh, 07:50–08:11Z): VERDICT=NO-GO. Живой дефект SEC-027/WEB-544: POST /api/slack/command без подписи и с неверной подписью → 503 с телом {"error":"SLACK_SIGNING_SECRET not configured","code":"SLACK_COMMAND_SIGNING_SECRET_UNCONFIGURED"} — вместо generic 401/403; утечка состояния конфигурации. Причина: на проде не задан SLACK_SIGNING_SECRET (ключ владельца не выдан) и код при unconfigured отвечает информационным 503. Фикс: волна 2578 (fail-closed generic 401 при unconfigured, без утечки) → l113o; секрет — владелец кладёт в env-файл (не CLI). Первый 5xx сработал как stop-condition, поэтому push-state и 3 кейса WEB-99 не прогонялись; каталог байт релиза l113n на A1 отсутствовал (пересоздан 08:50Z) → повтор post-QA после l113o. Статически подтверждено по exact source: WEB-549 saveSubscription возвращает false на non-2xx; WEB-551 committed latch + fullSignOut. Отчёт A1 L113NPOSTQA-REPORT.md.
2026-09-07T09:36:08.245Z · coordinator
[07.09 09:36Z координатор] ПОСАЖЕНО: ПРОД = l113o (df0376cf) с 09:34:36Z 07.09 (флип 09:33:41–09:34:36Z rolling). Над l113n: ROLES6CON r2 7eb7befd (WEB-99: публикация generation pointer батчами по 24 строки; +3 миграции, prisma migrate deploy 09:31:43–09:32:11Z, 193/193), PENDINGINDEX r4 d5d86103 (WEB-086/570: готовность по живым чанкам, ремонт карантина до quarantine-решения в chat-global и ноутбучном чате), DRIVEBODY r4 f44584c8 (WEB-570: серверные тела Google Drive — Docs/Sheets/Slides export, checkpoint v2, hard budget). Артефакт da22ddc6…, attestation fable-a2-l113o, активация подписана владельцем. Verify: ready/paid enforce_ready на обоих бэкендах, edge ×8 = 200, корни ×3 = 200, /login 200, вебхук 401, AttestationError/nginx/LEASE_LOST = 0, Telegram pending 0; воркеры indexing/extraction на l113o (timer-driven). Ловушка: spend-runtime env унаследовал SPEND_MIGRATION_SET_SHA256 от l113n → dry-run paid enforce_blocked до замены на значение att-build (7d49ad11…). hetzbk + env-pack + Pi → l113o-df0376cf. Откат = l113n (миграции additive). Расписка L113O-RELEASE-RECEIPT.md. Далее: post-QA 2593; операторский apply Drive-тел владельца (842 источника); батарея r45 после l113p.
2026-09-07T10:03:39.670Z · coordinator
[07.09 10:03Z координатор] POST-QA ЖИВОГО l113o (2593, A1, 09:40–10:02Z): VERDICT=NO-GO по состоянию кошелька QA-учётки, не по посаженным изменениям: global chat → HTTP 500 insufficient_funds (правило «Б»: QA c09 никогда не пополнялась), notebook chat → 200 со spending-limit notice без citation → pending-index и два roles6-кейса не приняты. GO: readiness/миграционный набор на обоих бэкендах, Drive r4 bytes/status, периметр. Координатор: грант $10 QA c09 через admin-route (10:05Z) → повтор 2593n (только пункты 1–2). Отдельная находка: unfunded на /api/chat-global отдаёт 500 вместо 402 (KNOWN ISSUE → волна после l113p).
2026-09-10T14:32:41.720Z · coordinator
ОБНОВЛЕНИЕ ИНФРА-КАРТЫ 10.09 — четыре изменения, все проверены живьём.

1. НОВАЯ МАШИНА: Мак Нео подключён воркером. `ssh neo`, пользователь limamarty,
   репозиторий волн /Users/limamarty/waves/.repo-NEOLINE, диспетчер com.nc.wavedispatcher
   (LaunchAgent), MAXWAVES=2, MINFREE_GB=20, диск 125 ГБ свободно.
   Клод и Codex оба живые. Сегодня выполнила там три волны: 3290, 3293, 3295.
   Ловушка: Клод читает связку ключей ТОЛЬКО из launchd gui-домена; из ssh даёт «Not logged in».

2. НА A1 ИСТЕКЛА СЕССИЯ КЛОДА. `claude --version` работает (2.1.233), любой запрос отдаёт
   `Failed to authenticate: OAuth session expired and could not be refreshed`.
   Последствие: ЛЮБАЯ волна с claude-моделью умирает на A1 за 46 секунд и оставляет бриф
   в queue/active, держа слот. Правило «приёмки безопасности не на OpenAI-моделях» на A1
   выполнить НЕЛЬЗЯ — вести их на Нео. Требует повторного входа, владелец уведомлён.

3. ПОРОГИ ДИСПЕТЧЕРОВ ВЫШЕ ПОРОГОВ ВЛАДЕЛЬЦА и это молчаливый стопор:
   A1 MINFREE_GB=12 (владелец говорит 8), A2 MINFREE_GB=10. Сегодня обе машины стояли:
   A1 на 5 ГБ, A2 печатал `skip: free=9G` каждые 45 секунд с 11:15Z. В journal этого НЕ видно,
   только в queue/dispatcher.log. Владелец видел простой, а systemctl показывал active.

4. ВНЕШНИЙ ДИСК M4Ext: при отвале путь /Volumes/M4Ext начинает разрешаться в СИСТЕМНЫЙ том M4,
   и сбор пишет резервные копии на внутренний диск. В harvest-nightly.sh добавлена защита:
   проверяется, что это действительно disk5 И что каталог читается, иначе сбор отменяется.
2026-09-10T18:55:03.881Z · coordinator
WAVE-SLOT-FIX-20260910T1853
10.09 18:53Z: устранены два дефекта счётчика слотов. A1: command substitution удалял переводы строк после # <wave>, поэтому process_labels читал 3332-wash-billingWhen, а marker — 3332-wash-billing; три работника считались шестью. runner теперь использует printf -v; диспетчер совместим с уже запущенными старыми prompt. Регресс 3/3 + newline 1/1; на живой A1 count 6→3, три tmux-сессии сохранены при restart (KillMode=process). После исправления запущены 3335 и 3339, теперь пять групп. Neo: счётчик включал сам wq-dispatcher и съедал один из двух слотов; имя исключено в обеих точках подсчёта, перезапущена только панель диспетчера, wash-roles сохранён. Бэкапы: A1 dispatcher.sh.pre-label-20260910 и wave-runner.sh.pre-label-20260910; Neo wave-dispatcher-neo.sh.pre-label-20260910. Исходники и тест: ноут /Users/annakorin/nc-ops-scripts/board-wash-20260910/. Это хостовые исправления; остальные критерии WEB-478 ещё проверяет 3333.
2026-09-10T19:40:36.755Z · coordinator
ENTERPRISE2-R1:RESOURCES
Эпик WEB-626. M1 занят и имеет 8 ГБ/около 2.5 ГБ swap: дополнительные два слота не запущены. A1 — только лёгкая подготовка, без saturation прода. A2/M4/Neo кандидаты будущего isolated contour после resource gate. AWS rehearsal: ARM offerings в eu-west-1 есть, чтение квот/support AccessDenied; третья машина НЕ подтверждена и не приобретена.
2026-09-12T22:17:55.090Z · coordinator
[12.09 22:17Z координатор] МОЙКА 12.09 (3566-wash-g1-infra): ИНФРА-КАРТА — постоянный тикет, в done не переводится. Якорь в теле устарел (l103), живой прод l115g. Остаток: обновить якорь и держать карту актуальной. Статус не меняю.
Остаток: Обновить якорь прода на актуальную линию (l115g).; Держать карту актуальной при дальнейших изменениях.
Отчёт: /Users/milamarty/waves/3566WASH*-REPORT.md (M4), копия nc-ops-scripts/shift-20260912-resume/wash-20260912/collected/. Проверка по исходнику прода l115g (9c8a9762).
2026-09-12T22:55:34.982Z · coordinator
[12.09 22:55Z координатор] ОСВЕЖЕНИЕ ТЕКСТА 12.09 (волна 3577, DeepSeek на M4): тело карточки переписано (якорь прода l103 d91ebefe → l115g 9c8a9762; роль web БД — помечена как установочный шаг, не код кандидата (22:15Z не подтверждено); таймер hetzbk-publish-current — не подтверждено в дереве; стенды A2 R3 capacity-3516 — не подтверждено в дереве; добавлены воркер Neo, ENTERPRISE2, уборка дисков 10.09). Старый текст сохранён координатором (/tmp/body-backup-WEB-420.md на M1, копия в nc-ops-scripts/shift-20260912-resume/3577/collected/). Отчёт /Users/milamarty/waves/3577BODYREFRESH-REPORT.md.
2026-09-13T10:34:56.668Z · coordinator
[13.09 10:34Z координатор] Карта инфраструктуры переписана на 13.09.2026 (волна 3647). Прежняя висела на 04.09 и не знала ни второй машины, ни ролей базы, ни дворников. Теперь в ней семь машин с путями, два бэкенда прода, стенды, бэкапы с таймерами, доска и паспорт, пять ролей базы. Старая карта сохранена ниже черты.
2026-09-13T12:23:00.782Z · coordinator
[13.09 12:22Z координатор] Паспорт обновлён дельтой 13.09 (волна 3684, применено координатором): прод l115j, эпик безопасности 77 из 77, названа причина трёх поломок ёмкости, записан результат практического экзамена бас-фактора и закрытая дыра с правами файлов окружения. Одиннадцать файлов паспорта, патч применён без единого отказа, опубликовано — паспорт отдаёт линию l115j.
2026-09-14T12:42:12.007Z · coordinator
[14.09 12:42Z координатор] VERDICT=GO

# WEB-420 (ИНФРА-КАРТА) — где что живёт, ездит и хранится

Дата: 2026-09-14 UTC. Волна: `3858-web420-infra-map`. База: `refs/waves/l115m` = `806c45d22f6e2616ae8135caabfb0c99180729b4`. Рабочая ветка: `wave/3858-web420-infra-map`. Дерево: `/Users/milamarty/waves/wt-3858-web420-infra-map`.

GO означает: карта собрана из проверяемых источников репозитория, каждая строка — со ссылкой на файл; непроверяемое помечено отдельно. Это НЕ доказательство живого состояния прода: живого доступа к A1/A2/Pi/Storage Box у этой волны нет, поэтому всё, что существует только на машинах, ниже помечено как «заявлено в репозитории, живым прогоном не подтверждено».

---

## 0. Порядок источников правды (сам прочитал, фиксирую)

Из `docs/PASSPORT/00-INDEX.md:5-7` и `docs/PASSPORT/08-CONTOUR-OPS.md:5-13`: канон чтения = паспорт → свежий `OPS-STATUS-LIVE` в ноутбуке владельца → `waves/queue/L113X-RELEASE-RECEIPT.md` → доска `bugs.wool2.online`. **Этого файла `OPS-STATUS-LIVE` и расписок в дереве нет** — значит живое serving-состояние из репозитория не выводится (см. раздел 7).

Ключевое противоречие, из-за которого эта карта вообще нужна:
- Паспорт `docs/PASSPORT/` сгенерирован **2026-09-07** и держит serving = **l113p** `1c850d92` (`docs/PASSPORT/03-TOPOLOGY-INFRA.md:2`, `docs/PASSPORT/CANON.json:4-9`).
- Дерево, в котором мы работаем, — **l115m** `806c45d22`, а фабрика линий знает уже про **l115f-r2**, севшую на A1 **12.09.2026** (`ops/line-factory/lines/l115f-r2.json:5`).
- То есть паспорт отстаёт от фактического контура минимум на одну-две посадки. Это и есть «карта устарела».

---

## 1. КАРТА: машины и их роли

| Машина | Роль (по репозиторию) | Источник |
|---|---|---|
| **A1 (OCI ARM)** | Прод-рантайм: nginx, активный PostgreSQL, посадка релизов. Единственный, кто сажает/флипает/синкает — координатор. | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:25-32`; `docs/PASSPORT/CANON.json:31-35` |
| **A2** | Сборка линии (артефакт tar + `att-build` эфемерным ключом), пассивный app-рантайм. | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:27-28`; `docs/PASSPORT/CANON.json:36-39` |
| **M4** | Координационные ворктри, очереди `waves/queue`, acceptance-пакеты. Линии получает только bundle/branch-апдейтом координатора. | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:29`; `docs/PASSPORT/CANON.json:40-44` |
| **Owner laptop** | Подпись activation ключом владельца; `OPS-STATUS-LIVE` в ноутбуке владельца. | `docs/PASSPORT/CANON.json:45-48` |
| **Pi** | Холодный standby PostgreSQL 16 на `:55433` + standby-линия. НЕ прод-хост приложения. | `infra/pi-standby/README.md:1-6`; `docs/dr/PI-COLD-STANDBY.md:1-6`; `docs/PASSPORT/CANON.json:49-52` |
| **Hetzner Storage Box** | Долговечное хранение артефактов релиза, env-пакетов, base/WAL бэкапов. | `infra/hetzbk/README.md:1-7`; `docs/PASSPORT/CANON.json:53-57` |
| **Git-зеркало `~/nc-mirror`** | Bare mirror, `mirror=true`, remote `ssh://ncmirror/x`; отсюда берутся все ворктри и refs `refs/waves/*`. | `~/nc-mirror/config` (наблюдение этой машины) |

ВНИМАНИЕ про смену ролей (исторический vs текущий): старые доки (`CLAUDE.md`, `docs/core/DEPLOYMENT.md:12-20`) описывают **Pi как прод-хост** (`pi@mailhub.duckdns.org:2046`, `/home/pi/note-clone/releases`). Текущие источники (`ops/line-factory/lines/l115f-r2.json`, `infra/pi-standby/README.md`) описывают **A1 как прод**, Pi как standby. Переезд зафиксирован в репозитории двумя разными поколениями документов, а не одним «актом переноса» — см. раздел 4.

---

## 2. КАРТА: домены и вход

| Имя | Назначение | Источник |
|---|---|---|
| `sixbyy.com` | Публичный корень / канонический продуктовый домен. | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:37` |
| `app.sixbyy.com` | Прод-вход приложения на A1. | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:38`; `docs/PASSPORT/CANON.json:61` |
| `dev.sixbyy.com` | Dev-контур; не использовать как доказательство прода. | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:39` |
| `sip.sixbyy.com` | Restricted VOICE/SIP вход; host-sync только координатором. | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:40` |
| `bugs.wool2.online` | Доска, оперативный монитор и `/passport`. | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:41` |
| `nb.wool2.online` / `dev.nb.wool2.online` | **Исторические** Pi-домены (прод/dev эпохи Pi). Сейчас в актуальной топологии не фигурируют. | `CLAUDE.md` (заголовок «Базовые параметры среды»); `docs/core/DEPLOYMENT.md:12` |

Канонический Telegram-вебхук: **`/api/messenger/v1/telegram/webhook`**; `{}` → 401 — ожидаемая форма без аутентификации. Устаревший `/api/telegram/webhook` использовать как верификатор нельзя. `docs/PASSPORT/03-TOPOLOGY-INFRA.md:44`.

---

## 3. КАРТА: база данных

| Факт | Значение | Источник |
|---|---|---|
| Прод-кластер на A1 | `noteclone`, порт **55432**, база `/noteclone`, хосты `127.0.0.1`/`localhost`, pgoptions `lock_timeout=5000 statement_timeout=120000`. | `ops/line-factory/lines/l115f-r2.json:50-54` |
| Кластер `noteclone:55432` — «боевой PostgreSQL», от волн закрыт peer-reject + pg_ident. | `infra/waves/install-wave-user.sh:5-6,35-36` |
| На A1 есть НЕСКОЛЬКО кластеров: `noteclone` (55432), `gate14bk`, `main`. Править pg_hba разрешено только у `noteclone`. | `infra/waves/install-wave-user.sh:129`; `infra/waves/COORDINATOR-DRYRUN.md:29` |
| Версия/расширения (снимок l113p, не живой замер): PostgreSQL 16.15 (Ubuntu), vector 0.6.0, pg_trgm 1.6. | `docs/PASSPORT/CANON.json:82-89` (помечено как снимок 07.09) |
| Числа миграций (снимок l113p): `_prisma_migrations` 211 строк vs 193 каталога миграций артефакта — расхождение +18 без имён/статусов, INPUT_GAP. | `docs/PASSPORT/CANON.json:82-89`; `docs/PASSPORT/08-CONTOUR-OPS.md:117` |
| Миграции посадки l115f: baseline l115e = 198 каталогов, кандидат l115f = 201. | `ops/line-factory/lines/l115f-r2.json:41-42` |
| Prisma CLI 5.22.0, путь `/home/wave/node_modules/prisma/build/index.js`. | `ops/line-factory/lines/l115f-r2.json:45-47` |
| Pi standby-кластер: PostgreSQL 16 на `:55433`, data dir `/var/lib/postgresql/16/noteclone`; старый PG15 main на 5432 не трогается. | `infra/pi-standby/README.md:58-68` |

**Смена владения базой (заявлено, живым прогоном не подтверждено).** Отдельного документа «перенос владения базой от даты X» в дереве нет. Перенос виден как разница двух поколений источников:
- Исторически прод-БД была на Pi: `DATABASE_URL=postgresql://noteclone:...@127.0.0.1:55432/noteclone` «локальная Pi DB», туннель `5433→55432`. `CLAUDE.md:68,175`.
- Сейчас прод-БД на A1: `database.hosts=[127.0.0.1,localhost]`, `port 55432`, `path /noteclone` в манифесте линии, которая сажается на A1. `ops/line-factory/lines/l115f-r2.json:50-54`.
- Pi переведён в холодный standby (WEB-490), восстановление из зеркала Hetzner, порт 55433. `infra/pi-standby/README.md:1-6`; `docs/dr/PI-COLD-STANDBY.md:1-6`.

Вывод: владение боевой БД перешло Pi→A1; точную дату и состояние «кто сейчас primary» репозиторий не называет — это в `OPS-STATUS-LIVE`/расписках (нет в дереве).

---

## 4. КАРТА: каталоги запуска (что где лежит на A1)

Актуальные пути — из манифеста севшей линии `l115f-r2` (`ops/line-factory/lines/l115f-r2.json`), это самый свежий источник путей в дереве:

| Путь | Что это | Источник |
|---|---|---|
| `/home/ubuntu` | Базовый каталог хоста A1. | `ops/line-factory/lines/l115f-r2.json:55-56` (`host.base`) |
| `/home/ubuntu/prod/shared` | Общий каталог рантайма (env, run, секреты). | `ops/line-factory/lines/l115f-r2.json:56` (`host.sharedDir`) |
| `/home/ubuntu/prod/releases/arm64-l115f-20260912T164636Z` | Каталог релиза линии (timestamp из имени артефакта). | `ops/line-factory/lines/l115f-r2.json:12-13` (`new.releaseDir`) |
| `/home/ubuntu/prod/shared/run/l115f-r2-92f7ae80` | Run-каталог линии (tag + первые 8 символов коммита). | `ops/line-factory/lines/l115f-r2.json:14-15` (`new.runDir`) |
| `/etc/systemd/system`, `/run/systemd/system` | Каталоги юнитов. | `ops/line-factory/lines/l115f-r2.json:57-58` |
| `/home/ubuntu/nc-upstream-controller/bin/nc-upstream-controller` | Контроллер выбора бэкенда (флип). | `ops/line-factory/lines/l115f-r2.json:65` (`host.controller`) |
| `/home/ubuntu/prod/shared/run/nc-upstream/controller.env` | Env контроллера. | `ops/line-factory/lines/l115f-r2.json:66` |
| `/home/ubuntu/prod/shared/.coordinator-deploy.lock` | Lock посадки координатора. | `ops/line-factory/lines/l115f-r2.json:67` |
| `/home/ubuntu/line-factory` | Каталог фабрики на хосте. | `ops/line-factory/lines/l115f-r2.json:61` (`host.factoryDir`) |
| `/home/ubuntu/l115f-r2-deploy` | Каталог состояния посадки. | `ops/line-factory/lines/l115f-r2.json:85` (`deploy.stateDir`) |
| `/home/ubuntu/l115f-r2-dryrun-pass.json` | Квитанция сухого прогона. | `ops/line-factory/lines/l115f-r2.json:86` |
| `/home/ubuntu/prod/shared/l115f-migration` | Каталог состояния миграций. | `ops/line-factory/lines/l115f-r2.json:37` (`migrations.stateDir`) |
| `/home/ubuntu/wt-l115f`, `/home/ubuntu/l115f-r2-artifact` | Сборочный ворктри и каталог артефакта на A2-стороне сборки. | `ops/line-factory/lines/l115f-r2.json:105-106` |

**Исторические (эпоха Pi) пути — для различения с текущими.** `/home/pi/note-clone/releases/<release>`, `/home/pi/note-clone/shared/`, юнит `note-clone-3010.service` и drop-in `standalone-artifact.conf`. `docs/core/DEPLOYMENT.md:12-20`; `CLAUDE.md` (раздел «Pi deploy»). Эти пути не действуют для контура A1 — их нельзя использовать как карту текущего запуска.

---

## 5. КАРТА: сервисы и юниты (что ездит)

### 5.1 Прод-юниты A1 (из манифеста l115f-r2)
| Юнит | Порт | Роль | Источник |
|---|---|---|---|
| `nc-a1` | 3010 | Основной web-бэкенд. | `ops/line-factory/lines/l115f-r2.json:70-83` |
| `nc-a1-b` | 3012 | Вторичный web-бэкенд (пара для флипа). | `ops/line-factory/lines/l115f-r2.json:70-83` |
| `nc-a1-indexing` (+timer) | — | Индексирование. | `ops/line-factory/lines/l115f-r2.json:73-75` |
| `nc-a1-extraction` (+timer) | — | Извлечение. | `ops/line-factory/lines/l115f-r2.json:73-75` |
| `nc-dryrun-l115f-r2` | 3011 | Сухой прогон перед флипом. | `ops/line-factory/lines/l115f-r2.json:76,79` |
| SIP | 4080 | Голосовой канал. | `ops/line-factory/lines/l115f-r2.json:81-82` |

Контроллер выбирает между `nc-a1:3010` и `nc-a1-b:3012`; оба обязаны пройти ready/paid. `docs/PASSPORT/08-CONTOUR-OPS.md:67`; `docs/PASSPORT/CANON.json:11-17`.

### 5.2 Инфраструктурные юниты (файлы `.service` в дереве)
- Бэкапы Hetzner: `hetzbk-wal-stream`, `hetzbk-base(+timer)`, `hetzbk-manifest(+timer)`, `hetzbk-retention(+timer)`, `hetzbk-age-alert(+timer)`, `hetzbk-env-pack(+timer)`, `hetzbk-warm-pull(+timer, только passive A2)`. `infra/hetzbk/README.md:218-224`.
- Pi standby: `pi-standby-postgres.service`, `pi-standby-warm-pull(+timer)`. `infra/pi-standby/systemd/`.
- Диспетчер волн: `wave-dispatcher-a2.service` (A2-воркер). `infra/wave-dispatcher/wave-dispatcher-a2.service`; `infra/wave-dispatcher/README.md:1-10`.
- Контроллер апстрима: `nc-upstream-controller.service(+timer)`. `infra/nginx/systemd/nc-upstream-controller.service`.
- Signal/телефония/SIP: `signal-poller`, `signal-shim`, `signal-call-bridge`, `signal-call-poller`, `signal-cli-daemon(+notify)`, `signal-pulse-ready-a1`, `telegram-call-native`, `ag-sip-native*`. `infra/signal-adapter/systemd/`, `infra/telephony/a1/systemd/`, `infra/sip/*.service`.
- Сверки/работы: `note-clone-spend-reconcile(+timer)`, `nc-a1-beta-authorization-reconcile(+timer)`, `note-clone-pricing-drift(+timer)`, `note-clone-webhook-inbox-retry(+timer)`, `note-clone-tasks(+timer)`, `note-clone-source-indexing(+timer)`, `note-clone-extraction(+timer)`. `infra/spend-reconcile/`, `infra/beta-authorization-reconcile/`, `infra/pricing-drift/`, `infra/webhook-inbox-retry/`, `infra/tasks/`, `infra/source-indexing-worker/`.
- Dev-стенд (A2): `nc-dev.service` (:3030), `nc-dev-pg.service` (:55450), `nc-dev-redis` (:6390). `infra/devstand/FILE-MANIFEST.md:13-15`.

### 5.3 Нерешённая двусмысленность юнита диспетчера
`infra/waves/install-wave-user.sh:47` использует `DISPATCHER_UNIT=wave-dispatcher.service`, но в дереве такого юнита нет — есть только `wave-dispatcher-a2.service`. Перед боевым прогоном координатор обязан подставить реальное имя на A1. `infra/waves/COORDINATOR-DRYRUN.md:57-64`.

---

## 6. КАРТА: фабрика линий и посадка

Фабрика — `ops/line-factory/`: `land.sh` (стадии на хосте), `transition.py` (координатор: снимок→сухой прогон→переключение→откат), `migrations.py` (миграции + квитанция), манифесты `lines/<line>.json`, валидатор `linefactory.py`. Раньше каждая линия была тремя отдельными скриптами с зашитыми константами; теперь скрипты общие, линия — данные в манифесте. `ops/line-factory/README.md:1-13`.

Стадии `land.sh` (выполняются по одной, руками, с проверкой между): **`unpack | env | dryrun | flip | verify`**. `ops/line-factory/land.sh:4-5,27`. Миграции — отдельно `migrations.py prepare-state|inspect|apply`; переключение — `transition.py prepare|dryrun|flip|verify|rollback`. `ops/line-factory/README.md:29-39`.

Полный цикл (из `docs/PASSPORT/08-CONTOUR-OPS.md:55-64`, `docs/PASSPORT/13-CONTOUR-PROTOCOL.md:50-64`):
```
A2: сборка артефакта (tar + att-build эфемерным ключом)
→ activation, подписанная owner-key на ноутбуке владельца
→ A1: land.sh  unpack→env→dryrun(:3011 paid enforce_ready)→migrate→flip(upstream-controller --await)→verify
→ hetzbk: артефакт + env-pack → Pi standby
→ L113X-RELEASE-RECEIPT.md на A1/A2/M4 → prod post-QA + батарея ролей
```

**Ссылка «сажается только принятый предок живой линии»**: `docs/PASSPORT/CANON.json:71-73` (`ancestryRule`).

---

## 7. ЧТО МЕНЯЕТСЯ ПРИ КАЖДОЙ ПОСАДКЕ (чтобы карта не устаревала молча)

Всё per-line из манифеста (`ops/line-factory/lines/l115f-r2.json`), валидируется `linefactory.py`:

| Что меняется | Формула / пример l115f-r2 | Источник |
|---|---|---|
| Каталог релиза `releaseDir` | `releaseDirPrefix + timestamp из имени артефакта`: `/home/ubuntu/prod/releases/arm64-l115f-20260912T164636Z`. | `ops/line-factory/lines/l115f-r2.json:12-13` |
| Run-каталог `runDir` | `runDirPrefix + первые 8 символов коммита`: `/home/ubuntu/prod/shared/run/l115f-r2-92f7ae80`. | `ops/line-factory/lines/l115f-r2.json:14-15` |
| Файл активации | `spend-activation-l115f.json`. | `ops/line-factory/lines/l115f-r2.json:16` |
| Файл env рантайма | `spend-runtime-l115f.env`. | `ops/line-factory/lines/l115f-r2.json:17` |
| Обёртки запуска | `enforce-start-l115f`, `enforce-worker-l115f`. | `ops/line-factory/lines/l115f-r2.json:18-19` |
| Имя dry-run юнита | `nc-dryrun-l115f-r2`. | `ops/line-factory/lines/l115f-r2.json:76` |
| Суффикс бэкапа юнитов | `.bak-l115f`. | `ops/line-factory/lines/l115f-r2.json:87` |
| Каталог состояния посадки | `/home/ubuntu/l115f-r2-deploy`. | `ops/line-factory/lines/l115f-r2.json:85` |
| Квитанция сухого прогона | `/home/ubuntu/l115f-r2-dryrun-pass.json`. | `ops/line-factory/lines/l115f-r2.json:86` |
| Каталог состояния миграций | `/home/ubuntu/prod/shared/l115f-migration`. | `ops/line-factory/lines/l115f-r2.json:37` |
| Сборочные пути (на стороне сборки) | `/home/ubuntu/wt-l115f`, `/home/ubuntu/l115f-r2-artifact`, `l115f-r2-hashes.env`, `nextpublic-prod-resolved-l115f.env` и т.д. | `ops/line-factory/lines/l115f-r2.json:104-134` |

Валидатор отказывает, если: имя артефакта/каталог релиза/run-каталог не сходятся между собой; теги старой и новой линии — подстроки друг друга; путь новой линии совпадает с целью отката; квитанции/бэкапы не именованы по линии; число миграций уменьшилось; расхождение каталога миграций с каталогом релиза не объяснено письменно. `ops/line-factory/README.md:70-84`.

Правило «что переписывает посадка»: `land.sh` проверяет, что `REL` (из имени артефакта) == `LF_REL` (из манифеста) и `RUN` (из коммита) == `LF_RUN`, до любого действия. `ops/line-factory/land.sh:29-38`.

---

## 8. ГДЕ ЖИВУТ КОПИИ (бэкапы, зеркала, артефакты)

| Копия | Что хранит | Где | Источник |
|---|---|---|---|
| **Hetzner base + WAL** | Зашифрованный base (pg_basebackup) + WAL-поток (`pg_receivewal`) + манифесты каждые 240 c. | Storage Box (пространства `base/`, `wal/`, `wal-partial/`, `manifests/`). | `infra/hetzbk/README.md:78-171` |
| **Hetzner артефакт релиза** | Иммутабельный каталог `/home/app-artifact/<release-id>/` с `artifact.tar.gz` + `.sha256` + `att-build.json` + `ready`. Хранит 5 новейших. | Storage Box. | `infra/hetzbk/README.md:70-74` |
| **Hetzner env-pack** | Зашифрованный env-пакет, `/home/walg-nc/hetzbk-prod/env/env-<line>-<ts>.tar.enc`; 10 новейших на линию. | Storage Box. | `infra/hetzbk/README.md:293-317` |
| **A2 warm-pull** | Новейший base-бандл на пассивном A2 (`/var/lib/hetzbk/warm`), 2 новейших ID. | A2. | `infra/hetzbk/README.md:78-101` |
| **Pi cold standby** | Локальное зеркало (base + manifest + WAL recovery-window) + восстановленный PostgreSQL 16 на `:55433`. | Pi. | `infra/pi-standby/README.md:1-30`; `docs/dr/PI-COLD-STANDBY.md:8-35` |
| **Аварийный комплект (busfactor)** | 4 файла: `kit.tar.age`, `manifest.json`, `manifest.sig.json`, `START-HERE.md` — шифруются на ≥2 получателей age. | Hetzner Storage Box + ВТОРАЯ независимая площадка (путь уточняет координатор). | `docs/operations/busfactor/EMERGENCY-KIT-COORDINATOR-STEPS.md:13-60`; `ops/busfactor-kit/README.md:60-74` |
| **Телефония state backup** | Версионированный USTAR-архив state (Signal/Telegram/SIP), секреты шифруются AES-256. | Файлы-входы предоставляет вызывающий; путь прода не фиксирован. | `infra/telephony-backup/README.md:1-47` |
| **Git-зеркало** | Bare mirror всех refs (`+refs/*:refs/*`), remote `ssh://ncmirror/x`. | `~/nc-mirror`. | `~/nc-mirror/config` |
| **Артефакт-манифест миграций** | `.artifact-manifest.json` (список `prisma/migrations/*/migration.sql`) внутри релиза. | В корне распакованного релиза. | `infra/hetzbk/README.md:9-41` |

Роли хранителей и резервный подписант аварийного комплекта — вне репозитория (решает владелец, R2). `docs/operations/busfactor/EMERGENCY-KIT-COORDINATOR-STEPS.md:44-60,122-137`.

---

## 9. ЧТО ПРОВЕРИТЬ НЕ УДАЛОСЬ (непроверенное, не выглядит как факт)

Правило волны: `docs/PASSPORT/08-CONTOUR-OPS.md:90-92` — `.env` не читается, прод/SSH только через координатора.

1. **Живое serving-состояние** — какая линия сейчас флипнута (l113p? l115f-r2? l115m?). В дереве есть только паспорт l113p и манифест l115f-r2; правда в `OPS-STATUS-LIVE`/расписках, которых в дереве нет.
2. **Факт «l115f-r2 села на A1 12.09.2026»** — это заявлено в `ops/line-factory/lines/l115f-r2.json:5` и `ops/line-factory/README.md:21`, но тесты фабрики «не запускают стадию посадки» (`ops/line-factory/README.md:110-112`). Расписка посадки (`L113X-RELEASE-RECEIPT.md`) в дереве отсутствует → статус «заявлено, не доказано из репозитория».
3. **Точно такая же для «двух севших линий»**: по манифесту это l115e (baseline 11.09) → l115f-r2 (12.09) (`ops/line-factory/lines/l115f-r2.json:22-41`), но это чтение манифеста, а не независимое подтверждение флипов.
4. **Перенос владения БД Pi→A1** — выведен из разницы поколений документов (раздел 3); точной даты/акта в дереве нет.
5. **Реальные числа БД** (версия, строки `_prisma_migrations`, состояние миграций) — в паспорте только снимок l113p и INPUT_GAP по +18 (`docs/PASSPORT/CANON.json:82-89`).
6. **Пути каталога hetzbk на A1** — манифест ставит `required: false` и просит координатора подтвердить `/var/lib/hetzbk/state/catalog.tsv` (`ops/line-factory/lines/l115f-r2.json:99-102`).
7. **Вторая копия аварийного комплекта** — путь уточняет координатор; волна её не создавала (`docs/operations/busfactor/EMERGENCY-KIT-COORDINATOR-STEPS.md:44-60`).
8. **Dev-стенд на A2** — юниты `nc-dev*` и `install.sh` лежат на невмёрженной ветке; в этом дереве их нет (`infra/devstand/FILE-MANIFEST.md:2-7`).

---

## 10. ЧЕГО В РЕПОЗИТОРИИ НЕТ ВОВСЕ (надо снимать с машин)

- **`OPS-STATUS-LIVE`** (ноутбук владельца) — единственный источник текущего serving-SHA.
- **`waves/queue/L113X-RELEASE-RECEIPT.md`** и SHA-addendum — расписки посадок; в дереве нет (grep по `RELEASE-RECEIPT` даёт только ссылки в паспорте и `scripts/ci/route-passport-receipt.mjs`).
- **`.env` / `.env.local` / `spend-runtime-*.env` / `flip-cron.secret`** — содержимое секретов; в репо только имена и пути.
- **Реальные права/под-аккаунты Hetzner**, `backup.pass`, `storagebox.key` — только на машине.
- **Имена/статусы миграций прод-БД** (для сверки 211 vs 193) — нужен санитизированный дамп с A1.
- **Фактические активные юниты на A1** (какой `wave-dispatcher*.service` реально стоит; сходится ли манифест с `/etc/systemd/system`).
- **Актуальный `catalog.tsv` hetzbk** и подтверждение, что каталог публикации артефактов тот же, что читает Pi standby.

---

## 11. ДЛЯ ТИКЕТА — детским языком, полный след для нулевого агента

**Симптом словами пользователя.** «Карта инфраструктуры устарела. За последние дни в контуре многое поменялось — сели две линии, базу передали другому хозяину, каталоги запуска переехали, появились новые деревья и артефакты. Собери свежую карту: где что живёт, ездит и хранится, и покажи, чего ты не проверил.»

**Как искал.** Создал detached-ворктри от `refs/waves/l115m` (`806c45d22`) и ветку `wave/3858-web420-infra-map`. Прочитал по порядку: паспорт (`docs/PASSPORT/00,03,08,13`, `CANON.json`, `CHANGELOG.md`), фабрику линий (`ops/line-factory/README.md`, `lines/l115f-r2.json`, `lines/l115g.json`, `land.sh`, `linefactory.py`), бэкапы/копии (`infra/hetzbk/README.md`, `infra/pi-standby/README.md`, `docs/dr/PI-COLD-STANDBY.md`, `infra/telephony-backup/README.md`, `ops/busfactor-kit/README.md`, `docs/operations/busfactor/EMERGENCY-KIT-COORDINATOR-STEPS.md`), диспетчер и волнового пользователя (`infra/wave-dispatcher/`, `infra/waves/`), dev-стенд и старые доки деплоя (`infra/devstand/`, `docs/core/DEPLOYMENT.md`, `CLAUDE.md`), плюс git-состояние зеркала (`~/nc-mirror/config`, `git show-ref`, `git worktree list`).

**Эволюция с тупиками.**
- Тупик №1: искал в дереве упоминания `l115m`/`l115n` — их в `.md/.json/.txt` нет. Паспорт отстаёт: он написан под l113p (07.09), а дерево уже l115m. → Понял, что единственный свежий источник путей — манифест `l115f-r2.json`.
- Тупик №2: искал буквальный документ «перенос владения базой» — grep по «владени/ownership/перенос» даёт только нерелевантное. → Понял, что перенос виден как разница двух поколений доков (Pi-эпоха vs A1-эпоха), а не как один акт.
- Тупик №3: не нашёл расписок посадки (`RELEASE-RECEIPT`) в дереве → следовательно «села ли l115f-r2 на A1» остаётся «заявлено», а не «доказано из репозитория».

**Что сделали со ссылками.** Каждая строка карты получила путь к файлу (и строку, где знаю номер). Числа брал только из этих файлов; ни одного числа не выдумал — что не прочитал, то пометил «заявлено/не измерено».

**Чем доказано и где граница.** Доказано из репозитория: топология машин, домены, порты, пути каталогов, состав юнитов, стадии посадки и «что меняется каждой посадкой», список мест хранения копий. Граница: всё, что живёт только на машинах (живое serving, реальные миграции, `.env`, расписки, каталог hetzbk) — из репозитория не доказывается и помечено в разделах 9 и 10.

**Что открыто.** Текущий serving-SHA; подтверждение двух флипов (l115e→l115f-r2); точная дата переноса БД; вторая площадка аварийного комплекта; реальное имя юнита диспетчера на A1.

**Troubleshooter (для следующего агента).**
- Карта снова устарела? → Сначала манифесты `ops/line-factory/lines/*.json` (там путь/порт/юнит текущей линии), потом `docs/PASSPORT/CANON.json`, потом запроси `OPS-STATUS-LIVE`.
- Паспорт называет l113p, а дерево l115m/l115n? → Это нормально, паспорт регенерируется вручную (`docs/PASSPORT/refresh.sh`); не считай его живой правдой.
- Нужна точная дата переноса БД? → Её нет в репозитории; спроси координатора за расписку или `OPS-STATUS-LIVE`.
- Не можешь подтвердить флип? → Ищи `waves/queue/L113X-RELEASE-RECEIPT.md` на A1/A2/M4 (в дереве его нет).

---

## 12. KNOWN ISSUES

1. **Паспорт отстаёт**: `docs/PASSPORT/` = l113p (07.09), а база этой волны — l115m, и фабрика знает про l115f-r2 (12.09). Карта устаревает молча, пока паспорт не регенерируют.
2. **«Две севшие линии» и «перенос владения базой» — только «заявлено»** в манифесте/разнице поколений доков; независимого подтверждения флипа и даты переноса в репозитории нет (нет расписок).
3. **INPUT_GAP миграций**: 211 строк `_prisma_migrations` vs 193 каталога артефакта (снимок l113p) без имён/статусов — не диагностируется агрегатом (`docs/PASSPORT/CANON.json:82-89`).
4. **Двусмысленность юнита диспетчера**: `install-wave-user.sh` ждёт `wave-dispatcher.service`, в дереве есть только `wave-dispatcher-a2.service` (`infra/waves/COORDINATOR-DRYRUN.md:57-64`).
5. **Вторая копия аварийного комплекта** и реальный `catalog.tsv` hetzbk не подтверждены координатором (`EMERGENCY-KIT-COORDINATOR-STEPS.md:44-60`; `lines/l115f-r2.json:99-102`).
6. **`START-HERE.md` аварийного комплекта не входит в подписанную опись** — известный пробел инструмента (`EMERGENCY-KIT-COORDINATOR-STEPS.md:226-250`).
7. **Старые Pi-доки (`CLAUDE.md`, `docs/core/DEPLOYMENT.md`) описывают прод на Pi** и противоречат текущей A1-топологии; при чтении их надо помечать как исторические.
8. **Дерево dev-стенда неполное**: `nc-dev*` юниты и `install.sh` — на невмёрженной ветке (`infra/devstand/FILE-MANIFEST.md:2-7`).

---

### Приложение: рефы/деревья, наблюдаемые на этой машине (для раздела «новые деревья»)

`git show-ref` (в `~/nc-mirror`): `refs/waves/l115l=1628b697d`, `l115m=806c45d22`, `l115m-full=9240e560a`, `l115n=d85bb2dad`; `refs/mirror-fetch/heads/l115a-prod`, `l115b-line`, `l115c-line`. `git worktree list`: кроме моего `wt-3858-web420-infra-map` есть соседние ворктри этой пачки (`wt-3855-web057`, `wt-3856-web478`, `wt-3857-web449`) и более ранние (`wt-3823…wt-3854`) — чужие деревья, не трогались.
2026-09-14T14:55:44.762Z · coordinator
[14.09 14:55Z координатор] **СЕТЬ A1↔A2: моё «100% потерь, нужен peering» было неверным — мерил ВНУТРЕННИЕ адреса.**

Проверено сейчас, пять раз подряд, с A2:
- `ping 129.213.25.105` (A1, внешний) → `3 packets transmitted, 3 received, 0% packet loss`, rtt min/avg/max 0.726/0.825/0.901 мс;
- `curl -k https://129.213.25.105/api/health -H "Host: sixbyy.com"` → **200**, total 0.0124 с; то же с `Host: nb.wool2.online` → **200**, 0.0138 с; корень с `Host: sixbyy.com` → **200**, 0.0130 с;
- порты A1 снаружи: 22 и 443 открыты, 3610/3000 закрыты.

Обратное направление A1→A2 (`ping 129.80.41.210`) — **100% потерь**. Связь **асимметричная**: A2 достаёт A1, A1 не достаёт A2.

Что это значит для эпика. Нужное направление — генератор нагрузки → app-host. Генератор на A2 достаёт приложение на A1 за 13 мс, то есть **CAP-09 (два физических app-host) и CAP-10 (лестница) сетью НЕ заблокированы**, и решение владельца по VCN peering не требуется. Ранее опубликованное «упирается в сеть, ждём решения» снимаю.

Ограничение, которое остаётся честно назвать: 13 мс — это через nginx с TLS на прод-контуре A1, а не до изолированного стенда; для лестницы понадобится отдельный app-процесс и свой адрес, иначе замер поедет по прод-vhost. Это следующий шаг, а не препятствие.
2026-09-14T15:04:06.604Z · coordinator
[14.09 15:04Z координатор] VERDICT=GO

# WEB-420 ИНФРА-КАРТА — сверка с новыми фактами и перепись (волна 3865)

Дата: 2026-09-14 UTC. Волна `3865-web420-infra-map`. База `refs/waves/l115n` = `d85bb2dad15a3ba52c773b0a2362748009a2c3b9`. Рабочая ветка `wave/3865-web420-infra-map`. Дерево `/Users/milamarty/waves/wt-3865-web420-infra-map`.

GO означает: карта переписана, каждая строка — со ссылкой на файл/тикет; строки, которые из этого дерева проверить нельзя, явно помечены «заявлено» или «не проверено отсюда». Живого доступа к A1/A2/M1/Pi/Hetzner у этой волны нет (SSH на другие машины запрещён), поэтому всё, что существует только на машинах, ниже помечено как «заявлено в диспетче/тикете» и НЕ выдаётся за измеренное мной.

---

## 0. Материал, который было велено прочитать — что нашлось, а что нет

| Велено | Нашлось? | Где / что вместо него |
|---|---|---|
| Тело тикета WEB-420 на доске | **Да, частично** | `waves/inputs/3647/WEB-420.json` (body от 04.09 + комментарии), `waves/3647-web282-and-infra-map-out/WEB-420-body.md` (body от **13.09**, якорь l115j) — это и есть «действующая карта», которую я сверял. |
| `MIRRORS.md` в дереве | **НЕТ** | По дереву `l115n`, `l115m`, `HEAD` и по всему диску файла `MIRRORS.md` не существует. Сведения о зеркалах собраны вместо него из `infra/hetzbk/README.md`, `infra/devstand/`, `docs/operations/busfactor/BF04-EXECUTABLE-RUNBOOK-v*.md`, `~/nc-mirror/config`. |
| Комментарии координатора к WEB-626/WEB-635/WEB-636 за **14.09** | **НЕТ** | Последний снимок доски в дереве — 12.09 (`3531BUSFACTOROFFLINEPACKAGE-package/boards/web-full.json`); `inputs/3642/WEB-626.json` обновлён 12.09. Комментарии за 14.09 живут на доске M1, к которой отсюда доступа нет. Суть этих комментариев передана тремя «новыми фактами» в самом диспетче — их я и вношу, помечая «заявлено в диспетче». |

**Вывод про материал:** два из трёх источников (MIRRORS.md и комментарии 14.09) в дереве отсутствуют. Это не тупик для задачи — новые факты даны в диспетче, а зеркала восстанавливаются из других файлов репозитория, — но в карту это надо записать как известный пробел (см. KNOWN ISSUES).

---

## 1. ⚠️ «Было / стало / чем опровергнуто» — новые факты против действующей карты

Действующая карта = тело WEB-420 от 13.09 (`WEB-420-body.md`). Новые факты = диспетч 3865.

### C1 (главное опровержение): A2 — НЕ «пассив + PG16-standby (лаг ~0)», это другая сеть Oracle, и A1→A2 закрыто

- **Было** (WEB-420 body 13.09 §1): «A2 (Oracle Ashburn, пассив) … Пассив + **PG16-standby (лаг ~0)**; capacity-стенд R3…».
- **Стало** (диспетч 3865): A1 (`129.213.25.105`) и A2 (`129.80.41.210`) — **разные сети Oracle**: по внутренним адресам `10.0.0.131` ↔ `10.1.1.219` связи **НЕТ (100% потерь)**. По внешним адресам **A2 достаёт A1** (ping 0.8 мс, HTTPS 13 мс, код 200), но **обратное A1→A2 закрыто** (ICMP не проходит). Связь **асимметричная**.
- **Чем опровергнуто:** standby с лагом ~0 требует непрерывной репликации WAL между primary (A1) и standby. Её физически нет: внутренней связи нет, A1→A2 не проходит вообще. Репозиторий это подтверждает независимо: `DEVSTANDA2R5-REPORT.md:101-102` — «A1 не достигает A2 по 10.x, оба prepared vhost используют публичный upstream `129.80.41.210:3030`». А «standby на A2» в дереве — это **процедура восстановления из hetzbk base**, а не текущее состояние: `docs/operations/busfactor/BF04-EXECUTABLE-RUNBOOK-v6.md:923` «Шаг 5b. Поднять standby на A2». Паспорт же называет standby — **Pi** (`docs/PASSPORT/03-TOPOLOGY-INFRA.md:31`, PG16 на `:55433`). Утверждение «PG16-standby (лаг ~0)» на A2 — неверное; цифру «лаг ~0» при этом тело само пометило доступом «(не подтверждено)», то есть оно и так было внутренне противоречиво.

### C2: очередь волн — не `/home/ubuntu/waves/queue/{…,hold,retired}`, а `/home/wave/waves/queue` (active/done/running)

- **Было** (WEB-420 body 13.09 §1): A1 «диспетчер волн `/home/ubuntu/waves/queue/{active,running,done,hold,retired}`».
- **Стало** (диспетч 3865): очередь **у обоих** A1 и A2 — `/home/wave/waves/queue`. Юнит A1 — `wave-dispatcher` (скрипт `/home/wave/waves/dispatcher.sh`), юнит A2 — `wave-dispatcher-a2` (скрипт `/home/ubuntu/wave-dispatcher-a2.sh`).
- **Чем опровергнуто:** `infra/waves/install-wave-user.sh:43-45,224-238` — реальная очередь `QUEUE_TARGET=/home/wave/waves/queue` (владелец `wave`), а `/home/ubuntu/waves/queue` — только **симлинк** на неё. Подкаталоги текущего диспетчера v3 (`infra/wave-dispatcher/wave-dispatcher-a2.sh:12,14-19`) — `active`, `done`, `running` + файлы `PAUSE`/`ALERT`/`CODEX_CAP`; подкаталогов `hold`/`retired` **нет** (это наследие старой версии диспетчера). Имя юнита A1 `wave-dispatcher.service` подтверждает `infra/waves/install-wave-user.sh:47` (`DISPATCHER_UNIT` по умолчанию); сам файл юнита A1 в репозитории отсутствует — есть только `wave-dispatcher-a2.service`.

### C3 (уточнение, не опровержение): порты A1 снаружи не были в карте

- **Было:** карта про A1 писала только «наружу только прокси 3129» и два бэкенда 3010/3012.
- **Стало** (диспетч 3865): снаружи на A1 слушают **22, 80, 443, 5060, 5061, 8088, 4080, 4084, 4573**; приложение сидит на `127.0.0.1:3010/3012/3031` за nginx; vhost'ы — `nb.wool2.online` и `sixbyy.com`.
- **Чем:** «наружу только прокси 3129» — это про **исходящий** доступ A1 (GitHub за забором), а перечисленные порты — **входящие**, поэтому прямого противоречия нет, но карта входящих портов была неполной. `4080` сходится с манифестом (`ops/line-factory/lines/l115f-r2.json:82`, `sip: [4080]`). Новый порт **3031** в манифесте линии отсутствует (там только 3010/3011/3012) — помечаю как «заявлено, в дереве источника нет».

### C4 (уточнение): vhost `nb.wool2.online` — живой, а не «исторический»

- **Было** (отчёт волны 3858 §2): `nb.wool2.online` отнесён к «историческим Pi-доменам».
- **Стало** (диспетч 3865): `nb.wool2.online` — живой vhost на A1, в паре с `sixbyy.com`.
- **Чем:** `ops/line-factory/lines/l115f-r2.json:61` — `readyHost: "nb.wool2.online"` (это хост проверки готовности, он обязан быть живым). Прошлый отчёт (3858) ошибся, объявив его историческим.

---

## 2. НОВАЯ КАРТА (каждая строка — с источником)

### 2.1 Машины

| Машина | IP / доступ | Роль (по источнику) | Источник |
|---|---|---|---|
| **A1** (OCI, прод) | `129.213.25.105`, `ssh a1nc` | Прод-рантайм: nginx, активный PostgreSQL, посадка релизов, wave-dispatcher. | IP и `ssh a1nc` — тело WEB-420 §1 (13.09); роль — `docs/PASSPORT/03-TOPOLOGY-INFRA.md` (строка A1) |
| **A2** (OCI) | `129.80.41.210` (публичный), внутр. `10.1.1.219` | Сборка линии (артефакт+attestation), пассивный рантайм, capacity-стенд R3, dev-стенд, диспетчер `wave-dispatcher-a2`. **НЕ standby с лагом ~0** (см. C1). | IP — диспетч 3865 (заявлено); роль сборки — `docs/PASSPORT/03-TOPOLOGY-INFRA.md`; dev-стенд — `infra/devstand/FILE-MANIFEST.md:13-15` |
| **M1** (Mac) | `ssh poolpooly@192.168.1.74` | Доска `web-board.sqlite` + HTTP API `:8787`, паспорт, автозапуск `nc-agent-board`. | тело WEB-420 §1 (заявлено, не проверено отсюда) |
| **M4** (Mac) | `ssh milamarty@192.168.1.114` (в теле: «перепроверить», 04.09 был `.110`) | Второй воркер волн, ворктри, внешний диск `/Volumes/M4Ext`. | тело WEB-420 §1 (заявлено; локальный IP этой машины отсюда не виден — `ipconfig getifaddr en0` пуст) |
| **Neo** (Mac) | `ssh neo`, юзер `limamarty` | Воркер волн: репо `/Users/limamarty/waves/.repo-NEOLINE`, LaunchAgent `com.nc.wavedispatcher`, MAXWAVES=2, MINFREE_GB=20. | комментарий координатора к WEB-420 (10.09 14:32Z), id 3469 (заявлено) |
| **Pi** (малинка) | `ssh -p2046 pi@mailhub.duckdns.org` | **Холодный standby PG16 на `:55433`** (по паспорту), dev после переезда. | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:31`; `infra/pi-standby/README.md` |
| **Ноут координатора** (Mac Intel, annakorin) | — | Подпись релизов, креда OCI/AWS, push GitHub `line/current`. | тело WEB-420 §1 (заявлено) |
| **Hetzner Storage Box** | u65664 | Долговечное хранение: артефакты, env-pack, base+WAL. | `infra/hetzbk/README.md`; тело WEB-420 §4 |

### 2.2 Сети и кто кого достаёт (главная новая запись)

| Направление | Результат | Источник |
|---|---|---|
| A1 ↔ A2 по **внутренним** адресам (`10.0.0.131` ↔ `10.1.1.219`) | **НЕТ связи (100% потерь)** — разные VCN Oracle | диспетч 3865 (заявлено); независимо: `DEVSTANDA2R5-REPORT.md:101-102` «A1 не достигает A2 по 10.x» |
| **A2 → A1** по внешним адресам | **Есть**: ping 0.8 мс, HTTPS к приложению 13 мс, код 200 | диспетч 3865 (заявлено, не измерено отсюда) |
| **A1 → A2** по внешним адресам | **Закрыто** (ICMP не проходит) | диспетч 3865 (заявлено) |
| A1 nginx → dev-стенд A2 | идёт по **публичному** `129.80.41.210:3030` (не 10.x) | `DEVSTANDA2R5-REPORT.md:102-103` |
| Итог | Связь **асимметричная**: A2 достаёт A1, A1 не достаёт A2. Любой сервис, требующий A1→A2, работать не может. | вывод из вышестоящих строк |

**Правило для нулевого агента:** не предполагай, что A1 и A2 — один контур с внутренней связью. Всё, что должно идти A1→A2 (WAL-stream на standby, репликация), требует внешнего маршрута или процедуры через Hetzner (см. `docs/operations/busfactor/BF04-EXECUTABLE-RUNBOOK-v6.md:923`).

### 2.3 Службы и порты

**A1 — приложение и рантайм** (пути из манифеста самой свежей реальной линии `l115f-r2`; манифест `l115g` — заглушка с placeholder-значениями `0123…`/`0000…`, см. 2.8):

| Что | Порт/путь | Источник |
|---|---|---|
| `nc-a1.service` (web) | `127.0.0.1:3010` | `ops/line-factory/lines/l115f-r2.json:71,79` |
| `nc-a1-b.service` (webSecondary) | `127.0.0.1:3012` | `ops/line-factory/lines/l115f-r2.json:72,81` |
| dry-run перед флипом | `127.0.0.1:3011` | `ops/line-factory/lines/l115f-r2.json:80` |
| **3031** (новый, в карте не было) | `127.0.0.1:3031` | диспетч 3865 (заявлено; в манифесте линии нет — уточнить у координатора, что это за третий процесс) |
| `nc-a1-indexing` + timer | — | `ops/line-factory/lines/l115f-r2.json:73-75` |
| `nc-a1-extraction` + timer | — | `ops/line-factory/lines/l115f-r2.json:73-75` |
| upstream-controller (флип) | `/home/ubuntu/nc-upstream-controller/bin/nc-upstream-controller` | `ops/line-factory/lines/l115f-r2.json:65` |
| SIP-канал | `4080` | `ops/line-factory/lines/l115f-r2.json:82` |
| БД Postgres | `127.0.0.1:55432`, база `noteclone` | тело WEB-420 §1 + §2 (заявлено); роли — §2.7 |
| Репозиторий на A1 | `/home/ubuntu/nc` | тело WEB-420 §1; `infra/waves/install-wave-user.sh:42` (`NC_DIR=/home/ubuntu/nc`) |

**A1 — входящие порты снаружи** (диспетч 3865, заявлено; в дереве источника полного списка нет): 22 (ssh), 80/443 (nginx), 5060/5061 (SIP/SIPS), 8088 (Asterisk ARI HTTP), 4080 (SIP-канал), 4084, 4573.

**A2 — стенды**:

| Что | Порт/путь | Источник |
|---|---|---|
| Dev-стенд `nc-dev.service` | `:3030`; PG `nc-dev-pg` `:55450`; Redis `nc-dev-redis` `:6390` | `infra/devstand/FILE-MANIFEST.md:13-15` |
| Capacity-стенд R3 (изолированный) | app `:3031`, PG `:55491`, свой Redis, провайдер-заглушка | WEB-626 комментарий 4399 (12.09 22:22Z) |

### 2.4 Репозитории и зеркала

| Что | Где | Источник |
|---|---|---|
| Истина релизной линии (веб) | GitHub `fandyy2023/0x2ALabs`, ветка `line/current`; push с ноута координатора (с A1 GitHub за забором) | `WEB-420.json` (evidence 28.08) |
| Git-зеркало этой машины | `~/nc-mirror` (bare, `mirror=true`, remote `ssh://ncmirror/x`), все refs `refs/waves/*` | `~/nc-mirror/config` (наблюдение этой машины) |
| Бандлы линий | A1 `/home/ubuntu/mirrors/*.bundle`; M4Ext `/Volumes/M4Ext/ops/line-bundles/` | `WEB-420.json` (evidence 28.08) |
| Доски/паспорт (вторая копия) | A1 `/home/ubuntu/mirrors/boards/` + Hetzner `/home/board-backups/` | тело WEB-420 §4 (заявлено) |
| Артефакты релизов | Hetzner `/home/app-artifact/<release-id>/` | `infra/hetzbk/README.md:70-74` |
| ⚠️ `MIRRORS.md` | **файла нет ни в дереве, ни на диске** — сведения о зеркалах надо снимать с машин | собственный поиск (grep по l115n/l115m/HEAD + find по диску) |

### 2.5 Очереди волн (диспетчеры)

| Машина | Юнит | Скрипт | Очередь | Источник |
|---|---|---|---|---|
| **A1** | `wave-dispatcher` | `/home/wave/waves/dispatcher.sh` | `/home/wave/waves/queue` | диспетч 3865 (заявлено); имя юнита — `infra/waves/install-wave-user.sh:47`; путь очереди — `:43-45` |
| **A2** | `wave-dispatcher-a2` | `/home/ubuntu/wave-dispatcher-a2.sh` | `/home/wave/waves/queue` | диспетч 3865; юнит+скрипт в репо — `infra/wave-dispatcher/wave-dispatcher-a2.service` и `.sh` |
| Подкаталоги очереди (v3) | `active`, `done`, `running` + файлы `PAUSE`/`ALERT`/`CODEX_CAP` | — | `infra/wave-dispatcher/wave-dispatcher-a2.sh:12,14-19` |
| Симлинк | `/home/ubuntu/waves/queue` → `/home/wave/waves/queue` | — | `infra/waves/install-wave-user.sh:224-238` |
| Пороги A2 | MAXWAVES=3, MINFREE_GB=10, MAX_HEAVY_PROCESSES=3 | — | `infra/wave-dispatcher/wave-dispatcher.env` |
| Примечание | Файл юнита A1 (`wave-dispatcher.service`) в репозитории **отсутствует** — есть только A2. Он живёт на A1 как host-артефакт. | — | `infra/wave-dispatcher/` (только `wave-dispatcher-a2.service`); `infra/waves/COORDINATOR-DRYRUN.md:58-59` |

### 2.6 Бэкапы (контур Hetzner)

| Компонент | Что делает / периодичность | Источник |
|---|---|---|
| Скрипты `hetzbk-*` | ставятся на A1 как `/usr/local/libexec/hetzbk-*` | `infra/hetzbk/README.md:1-5` |
| `hetzbk-base` (+timer) | почасовой base | `infra/hetzbk/systemd/hetzbk-base.timer` |
| `hetzbk-wal-stream` | WAL непрерывно | `infra/hetzbk/systemd/hetzbk-wal-stream.service` |
| `hetzbk-manifest` (+timer) | манифест каждые ~240 c | `infra/hetzbk/systemd/hetzbk-manifest.timer`; тело WEB-420 §4 |
| `hetzbk-warm-pull` (+timer) | тёплая база на **пассивном** узле, каждые 10 мин, только при `HETZBK_ROLE=passive`; **тянет из Hetzner, а не с A1** | `infra/hetzbk/systemd/hetzbk-warm-pull.service:11` + `.timer` |
| `hetzbk-retention` (+timer) | ротация, ежедневно 03:30 | тело WEB-420 §4 |
| `hetzbk-age-alert` (+timer) | возраст копий, каждые 5 мин | `infra/hetzbk/systemd/hetzbk-age-alert.timer` |
| `hetzbk-env-pack` (+timer) | env-пакет, почасовой | `infra/hetzbk/systemd/hetzbk-env-pack.timer` |
| `hetzbk-publish-current` | ежечасно публикует артефакт; **хостовый, в репо .service/.timer нет**, но имя упоминается в runbook | `docs/operations/busfactor/BF04-EXECUTABLE-RUNBOOK-v6.md:198,619` (имя/таймер); тело WEB-420 §4 («в репо не найден») |

Важно: `hetzbk-warm-pull` работает от A2 **наружу** к Hetzner, поэтому асимметрия A1↔A2 на него не влияет; влияет она только на всё, что должно ходить **A1→A2**.

### 2.7 Роли БД (сверка §7 действующей карты — подтверждается)

| Роль | Назначение | Источник (проверено) |
|---|---|---|
| `web` | приложение (DATABASE_URL), DML на app-таблицах | `ops/sql/web078_roles.sql:51` |
| `web_migrator` | миграции, член `web_owner`, CREATE на public | `ops/sql/web078_roles.sql:57,120,153` |
| `web_backup` | `pg_dump`, SELECT-only | `ops/sql/web078_roles.sql:63,221-222` |
| `web_owner` | NOLOGIN группа-владелец объектов | `ops/sql/web078_roles.sql:45` |
| `analytics_readonly` | SELECT-only на таблицах аналитики | `ops/sql/analytics_readonly_role.sql` (файл есть) |
| Контракт ролей | — | `docs/runbooks/DB-LEAST-PRIVILEGE-ROLES.md` (таблица ролей) |

Факт «приложение ходит ролью `web` с 12.09 22:15Z» — **заявлено** в теле WEB-420 §2; определение ролей в репо есть, но сам факт посадки на проде из дерева не выводится (это host/ops-факт).

### 2.8 Что меняется при каждой посадке (чтобы карта не устаревала молча)

Из манифеста линии `ops/line-factory/lines/l115f-r2.json` (валидируется `linefactory.py`):

- Каталог релиза: `releaseDir` = `/home/ubuntu/prod/releases/arm64-<line>-<timestamp>` (`:12-13`).
- Run-каталог: `runDir` = `/home/ubuntu/prod/shared/run/<line>-<sha8>` (`:14-15`).
- Файлы: `spend-activation-<line>.json`, `spend-runtime-<line>.env`, обёртки `enforce-start-<line>` / `enforce-worker-<line>` (`:16-19`).
- Юниты: `nc-a1`, `nc-a1-b`, `nc-a1-indexing`, `nc-a1-extraction`, dry-run `nc-dryrun-<line>` (`:71-76`).
- Порты: web 3010 / dryrun 3011 / webSecondary 3012 / sip 4080 (`:78-82`).
- `readyHost` = `nb.wool2.online` (`:61`).
- Якорь линии меняется каждый флип — **в дереве его нет** (правда в `OPS-STATUS-LIVE` и `L113X-RELEASE-RECEIPT.md`, которых в дереве нет).

⚠️ `ops/line-factory/lines/l115g.json` — **заглушка**: `sourceCommit=0123…`, `artifactSha256=0000…`, дата артефакта 20260920. Не считать её фактом посаженной линии. Реальная последняя линия в манифестах дерева — `l115f-r2`; якорь из тела (l115j, 13.09) и BASE этой волны (l115n) обгоняют манифесты.

---

## 3. Непроверенное отсюда (не выглядит как факт)

1. **Живой serving-якорь** (l115j `133fe00c` 13.09 по телу; BASE волны — l115n). Из дерева не выводится: манифесты есть только до l115f-r2, `OPS-STATUS-LIVE` и расписок нет. → Нужен `OPS-STATUS-LIVE` с ноутбука владельца или `/api/ready` снаружи (у меня нет доступа).
2. **Все числа сети** (0.8 мс, 13 мс, 100% потерь, код 200) и **список входящих портов** A1 — даны в диспетче, я их не мерил. → Доказать можно только живым прогоном с A2: `ping -c3 129.213.25.105`, `curl -skI https://129.213.25.105/…`; и с A1: `ping -c3 129.80.41.210`. Сценарий — в §4.
3. **Порт 3031 на A1** — в манифесте линии его нет. → `ss -ltnp` на A1 или вопрос координатору.
4. **A2 IP 129.80.41.210 / внутр. 10.1.1.219, A1 внутр. 10.0.0.131** — заявлено в диспетче; в дереве встречается только `129.80.41.210` (devstand) и `129.213.25.105` (devstand/passport). Внутренние адреса в дереве не зафиксированы.
5. **«A2 = пассив + PG16-standby (лаг ~0)» и «посажены роли 12.09 22:15Z», IP M1/M4** — host/ops-факты из тела тикета, из дерева не выводятся.
6. **`MIRRORS.md`** — файла нет; сведения о зеркалах собраны из других файлов и частично только «заявлено».

---

## 4. ДЛЯ ТИКЕТА — детским языком, полный след для нулевого агента

**Симптом словами пользователя.** «Карта инфраструктуры WEB-420 устарела. За сегодня появились новые факты про машины A1 и A2, и один из них противоречит тому, что в карте написано. Перепиши карту так, чтобы по ней можно было найти машину, службу, порт и путь, не задавая вопросов.»

**Как нашёл и почему не ловили раньше.** Я сверил каждую строку карты (тело WEB-420 от 13.09) с тем, что есть в моём дереве (`nc-mirror` на `refs/waves/l115n`) и в тикетах. Главное противоречие нашлось сразу: карта говорила «A2 — пассив + PG16-standby с лагом ~0», то есть «A2 — запасная копия базы A1, получающая данные почти мгновенно». А новые факты говорят: A1 и A2 — это **разные сети Oracle**, между их внутренними адресами связи нет вообще, и A1 не может достучаться до A2. Такое «запасное зеркало с лагом ~0» физически невозможно. Раньше это не ловили, потому что карту обновляли по другим событиям (посадки линий, роли БД), а сетевую топологию A1↔A2 никто не перепроверял — в карте даже доступ к A2 был помечен «не подтверждено».

**Эволюция с тупиками.**
- Тупик №1: `MIRRORS.md`, который велено прочитать, в дереве **не существует**. Проверил grep по `l115n`, `l115m`, `HEAD` и поиск по всему диску — файла нет. → Собрал сведения о зеркалах из `infra/hetzbk/README.md`, `infra/devstand/`, busfactor-runbook и `~/nc-mirror/config`, и записал отсутствие файла как KNOWN ISSUE.
- Тупик №2: комментарии координатора к WEB-626/635/636 за 14.09 в дереве тоже **отсутствуют** (последний снимок доски — 12.09). → Суть этих комментариев передана «новыми фактами» в самом диспетче; я внёс их с пометкой «заявлено в диспетче».
- Тупик №3: искал в дереве подтверждение «A2 — standby» — нашёл противоположное: паспорт называет standby **Pi** (`:55433`), а «standby на A2» — это процедура восстановления из Hetzner (`BF04…v6:923`), не текущее состояние. Это и есть независимое доказательство того, что строка карты про A2 неверна.
- Тупик №4: искал порт 3031 (из новых фактов) в манифестах линии — его там нет (только 3010/3011/3012). Значит 3031 — host-факт, который надо снять с A1, а не выдумывать.

**Что сделали со ссылками.** Каждая строка новой карты (раздел 2) получила источник — файл и строку (например, очередь волн: `infra/waves/install-wave-user.sh:43-45,224-238`; асимметрия сети: `DEVSTANDA2R5-REPORT.md:101-102`). Числа, которых я сам не получил (0.8 мс, 13 мс, 100%, порты, IP), помечены «заявлено в диспетче», а не «измерено».

**Чем доказано и ГДЕ ГРАНИЦА.** Доказано из дерева: топология машин (A1 прод / A2 сборка / Pi standby), порты приложения 3010/3011/3012/4080, пути каталогов запуска, состав юнитов диспетчера и бэкапов, роли БД, и — независимо — что «A1 не достигает A2 по 10.x» (`DEVSTANDA2R5-REPORT.md:101-102`). **Граница:** всё, что живёт только на машинах (живой serving-якорь, реальные сетевые замеры 0.8/13 мс, список входящих портов, порт 3031, факт посадки ролей, IP M1/M4) — из дерева не доказывается; помечено «заявлено/не проверено отсюда». Чтобы превратить «заявлено» в «доказано», нужен живой прогон (SSH на A1/A2 — у меня запрещён).

**Сценарий проверки сетевых фактов по шагам** (для того, у кого есть доступ):
1. С A2: `ping -c3 129.213.25.105` (ждём ответ) и `ping -c3 10.0.0.131` (ждём 100% loss) — подтверждает «A2 достаёт A1 только по внешнему адресу».
2. С A2: `curl -skI https://129.213.25.105/` (ждём 200, ~13 мс) — подтверждает HTTPS-путь.
3. С A1: `ping -c3 129.80.41.210` и `ping -c3 10.1.1.219` (ждём 100% loss) — подтверждает «A1→A2 закрыто».
4. С A1: `ss -ltnp` — сверить список входящих портов 22/80/443/5060/5061/8088/4080/4084/4573 и что за процесс на `127.0.0.1:3031`.
5. С A1: `readlink -f /home/ubuntu/waves/queue` → ждём `/home/wave/waves/queue` (подтверждает симлинк).

**Что осталось открытым.** Живой serving-якорь; реальные сетевые замеры; природа порта 3031 на A1; файл юнита `wave-dispatcher.service` на A1 (в репо его нет); сам `MIRRORS.md` (нет в дереве); комментарии 14.09 к WEB-626/635/636 (нет в дереве).

**Troubleshooter (для следующего агента).**
- Карта снова устарела? → Сначала `ops/line-factory/lines/*.json` (но осторожно: `l115g.json` — заглушка), затем `docs/PASSPORT/03-TOPOLOGY-INFRA.md`, затем запроси `OPS-STATUS-LIVE` у владельца.
- Нужен живой якорь/порты? → Из дерева не выводится; проси доступ к A1 или `OPS-STATUS-LIVE`.
- Проверяешь связь A1↔A2? → Помни: асимметрично. A2→A1 есть (внешний адрес), A1→A2 нет, внутренние 10.x не связаны.
- Ищешь standby? → По паспорту это Pi (`:55433`), а не A2. «Standby на A2» — процедура из Hetzner, не текущее состояние.
- Ищешь очередь волн? → Реальная `/home/wave/waves/queue` (владелец `wave`); `/home/ubuntu/waves/queue` — симлинк.

---

## 5. KNOWN ISSUES

1. **A2 не standby**: строка «PG16-standby (лаг ~0)» опровергнута — разные сети Oracle, A1→A2 закрыто; standby по паспорту — Pi `:55433`. В теле WEB-420 надо исправить §1 (строку A2) и убрать «лаг ~0».
2. **`MIRRORS.md` отсутствует** в дереве и на диске — материал, на который ссылается диспетч, не существует; зеркала собраны из других файлов.
3. **Комментарии координатора к WEB-626/635/636 за 14.09 отсутствуют** в дереве (последний снимок доски 12.09) — внесены только «новые факты» из диспетча, а не полный текст комментариев.
4. **Порт 3031 на A1** — заявлен, но в манифесте линии нет; неизвестно, что за процесс.
5. **Файл юнита A1 `wave-dispatcher.service`** в репозитории отсутствует (есть только `wave-dispatcher-a2.service`) — host-артефакт, в дерево не внесён.
6. **`ops/line-factory/lines/l115g.json` — заглушка** с placeholder-коммитом/SHA; легко принять за посаженную линию по ошибке.
7. **Список входящих портов A1 и все сетевые числа** — «заявлено в диспетче», не измерено этой волной (нет доступа к машинам).
8. **Паспорт отстаёт ещё сильнее тела**: `docs/PASSPORT/03-TOPOLOGY-INFRA.md` держит serving = l113p (07.09), тело WEB-420 = l115j (13.09), BASE волны = l115n.
9. **`nb.wool2.online` ошибочно объявлен «историческим» в отчёте волны 3858** — это живой `readyHost` и vhost (исправлено здесь в C4).

---

## 6. Приложение: рефы, наблюдаемые на этой машине

`git worktree list` (в `~/nc-mirror`): соседние ворктри этой пачки — `wt-3860-web573-drive-not-connected`, `wt-3861-web467-projection-timeout`, `wt-3862-web580-voice-focus`, и более ранние `wt-3823…wt-3858` (prunable). Мой: `wt-3865-web420-infra-map` = `d85bb2dad15a3ba52c773b0a2362748009a2c3b9` (`refs/waves/l115n`), ветка `wave/3865-web420-infra-map`. Чужие деревья не трогались.
2026-09-14T15:10:25.658Z · coordinator
[14.09 15:10Z координатор] VERDICT=GO

# WEB-420 — независимая приёмка волны 3858

Дата проверки: 2026-09-14. Проверяемая ветка: `refs/waves/3858/wave/3858-web420-infra-map`, SHA `60a4a09a0fb4a000ed956aed7e7013297077ec09`. Рабочее дерево: `/Users/limamarty/waves/wt-3872-acc-web420`, ветка приёмки: `acceptance/3872-acc-web420`.

## Короткий вердикт для нулевого агента

Я перепроверил карту автора по файлам, на которые она ссылается. Выбрал десять строк карты. Все десять совпали с названными источниками. Затем специально сломал один путь релиза и прогнал валидатор. Валидатор отверг сломанный вариант. Поэтому для той части работы, которую можно проверить из этой машины, вердикт `GO`.

`GO` здесь не означает «прод проверен». Автор сам написал, что живого доступа к A1/A2/Pi/Storage Box нет, и это подтвердилось границей текущей проверки. Живая линия, факт флипа, реальные БД и активные systemd-юниты не доказаны.

## Что я сделал

1. Проверил, что существует ровно нужная refs-ветка. Она существует и указывает на `60a4a09a0fb4a000ed956aed7e7013297077ec09`. Похожую ветку не подставлял.
2. Создал отдельное рабочее дерево из этой refs-ветки и отдельную ветку `acceptance/3872-acc-web420`.
3. Прочитал `3858WEB420IN-REPORT.md`, историю коммита и diff от базы `806c45d22f6e2616ae8135caabfb0c99180729b4`. В diff автора только отчёт; отдельного авторского evidence/bundle в этой ветке нет.
4. Локально поискал тело WEB-420 и комментарии доски. Результат: **доска отсюда не видна**; локального тела тикета и его комментариев не нашёл.
5. Запустил read-only проверку фабрики:

   `python3 ops/line-factory/linefactory.py validate --line l115f-r2 --refuse-placeholders`

   Результат: `OK l115f-r2`, `RC=0`.

6. Запустил независимый acceptance-тест только через `node --test`. В нём 10 проверок строк карты и 1 негативная проверка. Результат: `tests 11`, `pass 11`, `fail 0`, `RC=0`.
7. В негативной проверке сделал временную копию манифеста и изменил `new.releaseDir` на несовпадающий путь. Валидатор отверг её. В production, SSH, sudo и реальные хосты этот тест не ходил.
8. Проверил наличие release receipts. Файлов-расписок в дереве нет; найдены только ссылки на них в документации и отчёте автора.

Полный машинный след находится в `/Users/limamarty/waves/3872ACC420-evidence/`.

## Десять строк карты и результат

Во всех строках ниже я сверял не только наличие ссылки в отчёте, но и содержание указанного источника.

| № | Строка карты | Названный источник | Результат |
|---:|---|---|---|
| 1 | A1 — production runtime, nginx, активная PostgreSQL и landing | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:25-32` | Устояло как утверждение документа |
| 2 | `app.sixbyy.com` — production ingress на A1 | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:38` | Устояло |
| 3 | Канонический Telegram webhook — `/api/messenger/v1/telegram/webhook` | `docs/PASSPORT/03-TOPOLOGY-INFRA.md:44` | Устояло |
| 4 | DB endpoint: hosts `127.0.0.1`/`localhost`, port `55432`, path `/noteclone` | `ops/line-factory/lines/l115f-r2.json:50-54` | Устояло |
| 5 | Run directory `/home/ubuntu/prod/shared/run/l115f-r2-92f7ae80` | `ops/line-factory/lines/l115f-r2.json:14-15` | Устояло |
| 6 | `nc-a1`, `nc-a1-b`, dry-run `3011`, web ports `3010` и `3012` | `ops/line-factory/lines/l115f-r2.json:70-83` | Устояло |
| 7 | Pi — standby PostgreSQL 16, `PI_STANDBY_PORT=55433` | `infra/pi-standby/README.md:58-68` | Устояло как документированная конфигурация |
| 8 | Hetzner artifact layout: `/home/app-artifact/<release-id>/`, tar, sidecar, attestation, `ready` | `infra/hetzbk/README.md:70-74` | Устояло |
| 9 | A2 warm-pull — `/var/lib/hetzbk/warm`, сохраняются два новейших ID | `infra/hetzbk/README.md:78-101` | Устояло как правило/конфигурация |
| 10 | `~/nc-mirror` — bare mirror, `mirror=true`, remote `ssh://ncmirror/x` | `~/nc-mirror/config` | Устояло; прочитано локально |

## Числа: откуда взяты и что они значат

Я не принимал число только потому, что оно написано в отчёте. Проверил источники:

- `55432`, `/noteclone`, `3010`, `3011`, `3012` и `4080` взяты из манифеста `l115f-r2`, а не из живой БД или живых портов.
- `198` и `201` — поля `baselineCount` и `candidateCount` манифеста `l115f-r2:42-43`. Это заявленные counts миграционных наборов линии, не live DB readback.
- `211` и `193` — старый снимок паспорта в `docs/PASSPORT/CANON.json:82-88`; `docs/PASSPORT/08-CONTOUR-OPS.md:117` прямо говорит, что агрегат не содержит имён и статусов. Это снимок с датой, не текущий замер.
- `240` секунд для публикации manifest, пять новейших artifact release IDs, десять env-pack на линию и два warm ID находятся в `infra/hetzbk/README.md` в соответствующих разделах. Это параметры документа/скриптов, не наблюдение Storage Box.
- Временные метки и SHA, которые я указываю в этом отчёте, взяты из собственных команд `git`, `node --test` и read-only вывода источников; они сохранены в evidence.

## Негативный тест

Ожидаемое поведение: если `releaseDir` в манифесте не соответствует имени артефакта и префиксу, фабрика должна отказать до стадии посадки.

Сценарий:

1. Прочитать `ops/line-factory/lines/l115f-r2.json` во временную директорию.
2. Изменить только `new.releaseDir` на заведомо другой timestamp.
3. Запустить `python3 ops/line-factory/linefactory.py validate --line <временный-манифест> --refuse-placeholders` через независимый `node --test`.
4. Проверить, что процесс бросил ошибку с упоминанием release/invalid/mismatch.

Тест прошёл: изменённый манифест был отвергнут. Значит заявленное правило валидатора воспроизводится. Если бы валидатор пропустил такой манифест, это было бы основанием для `NO-GO`.

## Что устояло, а что нет

Устояло:

- ровно указанная ветка и отдельное worktree;
- десять выбранных строк карты и их названные источники;
- read-only validation реального манифеста `l115f-r2`;
- отказ валидатора на специально сломанном `releaseDir`;
- явное разделение автором repository claim и live claim;
- явное перечисление автором открытых вопросов и известных противоречий старых документов.

Не доказано и не превращено в факт:

- какая линия реально serving сейчас;
- что `l115f-r2` действительно села на A1 в указанную дату;
- что было два реальных флипа `l115e → l115f-r2`;
- что база действительно сейчас primary на A1;
- текущая версия PostgreSQL, live migration rows и live migration status;
- какие systemd units реально активны на A1;
- существование актуального `catalog.tsv` на A1;
- наличие второй независимой копии аварийного комплекта;
- содержимое `.env` и реальные права/подаккаунты Storage Box.

## Граница проверки и как закрыть оставшееся

Я не заходил на production, не использовал SSH, sudo или secrets, не запускал deployment/release/build/`next build`/full tsc и не читал production DB. Поэтому перечисленное выше нельзя закрыть чтением этой ветки.

Для закрытия live gaps координатору нужно выполнить следующий сценарий на разрешённых машинах и вернуть санитизированные receipts:

1. На owner laptop получить свежий `OPS-STATUS-LIVE` read-only снимок с текущими line/SHA и временем.
2. На A1/A2/M4 найти receipts `waves/queue/L113X-RELEASE-RECEIPT.md` и SHA-addendum для нужной линии; сверить artifact, activation, migration digest, flip и rollback predecessor.
3. На A1 без секретов снять read-only identity/status активных systemd units и serving controller; имя unit диспетчера сверить отдельно, потому что репозиторий содержит `wave-dispatcher.service` как default в `install-wave-user.sh`, а в дереве есть `wave-dispatcher-a2.service`.
4. Для DB предоставить только owner-approved sanitized snapshot: version, database identity, migration names/statuses и primary indication. Не присылать `.env`, passwords или keys.
5. Для Hetzner/Pi предоставить receipt с manifest/base IDs, artifact/env-pack identity, catalog location и успешной проверкой второй emergency-kit copy. Пути и права должны быть подтверждены фактическим листингом, а не только README.
6. После получения этих материалов повторить именно эти сверки в отдельном acceptance-пакете.

## KNOWN ISSUES

1. Доска отсюда не видна: локальные `BOARD-*` файлы не содержат тело WEB-420 или его комментарии.
2. В ветке автора есть только `3858WEB420IN-REPORT.md`; отдельные author evidence и author bundle не найдены. Этот приёмочный пакет использует собственное evidence.
3. Паспорт датирован 2026-09-07 и говорит о serving `l113p`, тогда как проверяемая база — `l115m`, а манифест фабрики описывает `l115f-r2`. Это заявленное противоречие документации, а не доказательство текущего serving.
4. В репозитории нет `OPS-STATUS-LIVE` и release receipts. Поэтому флип и живое состояние остаются открытыми.
5. `install-wave-user.sh` содержит default `wave-dispatcher.service`, но дерево содержит `wave-dispatcher-a2.service`; отчёт автора правильно оставляет это открытым.
6. В старых Pi-документах остаются исторические Pi production paths; отчёт автора правильно помечает их историческими, но оператору нельзя использовать их как current map.
7. Команда `timeout` в этой среде отсутствует (`command not found`), поэтому быстрые локальные команды были выполнены синхронно; длинные/боевые команды не запускались.

## Evidence

Каталог: `/Users/limamarty/waves/3872ACC420-evidence/`

- `map-claims.test.mjs` — независимый тест десяти строк и негативного сценария;
- `map-claims.test.log` — вывод `node --test`;
- `manifest-validation.log` — вывод read-only validation;
- `acceptance-context.txt` — refs, HEAD, diff-stat, worktrees и mirror config;
- `board-visibility.txt` — результат локального поиска доски;
- `receipt-presence.txt` — поиск реальных receipts и найденные только ссылки;
- `numbers-and-sources.txt` — проверенные числовые строки и окна источников;
- `scope-and-open-gaps.txt` — границы и открытые данные;
- `collect-evidence.sh` — воспроизводимая обвязка сбора.

SHA256 всех evidence-файлов записан в `SHA256SUMS`.
2026-09-14T15:16:28.848Z · coordinator
[14.09 15:16Z координатор] # WEB-420 - занято, не обогащалось

По вводной задачи WEB-420 сейчас в работе у волн 3865/3866/3868, поэтому я не готовил enrichment-блок и не предлагаю текст для публикации.

Минимальная отметка, проверенная отсюда: live API при финальной сверке показывает статус `review`; в локальном `web-board.sqlite` по WEB-420 видно 32 комментария, последний `id=5026` от 2026-09-14T15:04:06.604Z.

Ничего на доске не менял.
2026-09-15T22:05:58.150Z · coordinator
ENRICH-4132-WEB-420
```markdown
## ДЕЛЬТА ОБОГАЩЕНИЯ — 2026-09-15T21:54Z, M1/4132 (DeepSeek Flash 4.1, read-only аудит), VERDICT=DRAFT_DELTA

### 1. Вердикт
Якорь карты — l115j `133fe00c` (13.09 08:50Z) — устарел. Живая линия —
**l115o `68e25d8df5ae8263c5ac5466353631f57a17cfcc`**. Устройство парка с 13.09 изменилось
(вторая копия стенда, пороги диска, регулярное сжатие логов, реплика на A2), и этого в
карте нет. Приёмка волны 3872 дала GO, но с явной границей: живой прод/машины оттуда не
проверялись, проверено только то, что видно с одной машины.

### 2. КАРТА ДОКАЗАТЕЛЬСТВ
- Приёмка карты: доска WEB-420 id 5027 (2026-09-14T15:10:25.658Z), VERDICT=GO, ветка
  `refs/waves/3858/wave/3858-web420-infra-map` = `60a4a09a0fb4a000ed956aed7e7013297077ec09`,
  база `806c45d22f6e2616ae8135caabfb0c99180729b4`; проверено 10 строк карты + негативный
  контроль валидатора фабрики линий. **Граница приёмки: живая линия, факт флипа, реальные БД
  и активные systemd-юниты НЕ доказаны.**
- HANDOFF-LIVE.md §1 строки 445–450 (l115o, релизный каталог, артефакт, миграции 209/0).
- HANDOFF-LIVE.md §6в строки 580–589 (вторая копия стенда: pid 2597352, `127.0.0.1:3621`,
  релиз l115l, возраст 4 ч 40 мин на 04:35Z, жива на 20:14Z).
- HANDOFF-LIVE.md §7 строка 649 (регулярное сжатие логов: `/usr/local/bin/wave-log-compress.sh`
  + `wave-log-compress.timer`, ежечасно, `OnBootSec=10min`, лог `/var/log/wave-log-compress.log`).
- HANDOFF-LIVE.md §7 строка 650 (диск A1 доходил до 98 %; освобождено 15.6 ГБ, из них 13.1 ГБ
  дал `gzip -9` логов волн старше суток крупнее 5 МБ; НЕ трогать `/var/lib/hetzbk` 37 ГБ,
  `/var/lib/postgresql` 9.5 ГБ, каталоги релизов).
- HANDOFF-LIVE.md §8 строка 666 (A2 — не «просто сборочная машина»: живая потоковая реплика
  боевой базы, порт 55433, `online,recovery`, слот `a2_standby`).
- HANDOFF-LIVE.md §7 строка 648 (A2: протух вход в модель Клода, `OAuth session expired`;
  Codex на A2 живой).

### 3. ЭВОЛЮЦИЯ / ПОПРАВКИ (append-only)
- Якорь: l115j `133fe00c` → **l115o `68e25d8df5ae8263c5ac5466353631f57a17cfcc`**.
- Линия: l115g → l115h → l115i → l115j → l115k → l115l → l115m → l115n → **l115o**;
  попытка l115p `ca6c06ef…` откачена 15.09 21:15Z, прод снова l115o.
- Новое в раздел «ТАЙМЕРЫ И ДВОРНИКИ»: `wave-log-compress.timer` (ежечасно) — логи раньше не
  трогал ни один дворник, поэтому копились месяцами.
- Новое в раздел «СТЕНДЫ»: вторая копия стенда живёт вне каталога текущего релиза и мешает
  стадии `verify` (см. §6в хендоффа). Текущее состояние pid 2597352 отсюда **НЕ ПРОВЕРЕНО**.
- Новое в раздел «МАШИНЫ»: A2 несёт живую потоковую реплику боевой базы — её нельзя считать
  «просто сборочной».
- Долг бэкапов/диска: A1 доходил до 98 % (отказ ssh на прод-машине = потеря управления).

### 4. KNOWN ISSUES / ТРАБЛШУТИНГ
1. **Карта описывает парк, которого уже нет.**
   - Симптом: агент приходит на машину, которой в карте нет, или ищет стенд не там.
   - Проверка за 2 минуты: сверить строку «Якорь прода» карты с `checks.release.sourceCommit`
     живого `/api/ready`; пройти по списку машин и убедиться, что каждая запись имеет
     машину-источник.
   - Причина: карта обновляется реже, чем происходят посадки и изменения парка.
   - Лечение/статус: обновлять карту при каждом изменении устройства, а не при каждой мойке.
   - Ссылки: доска WEB-420 id 5027; HANDOFF-LIVE.md §1, §6в, §7.
2. **Приёмка карты не доказывает живой парк.**
   - Симптом: GO есть, а «живая линия, факт флипа, реальные БД и активные юниты» — не доказаны.
   - Проверка за 2 минуты: взять из карты два-три конкретных юнита и проверить их на машине
     (`systemctl is-active <unit>`), ожидание `active`.
   - Причина: у автора и приёмщика не было живого доступа к A1/A2/Pi/Storage Box.
   - Лечение/статус: считать карту «заявленной», пока нет живой сверки хотя бы по якорным
     строкам. Явно писать границу у каждой строки.
   - Ссылки: доска WEB-420 id 5027.
3. **Вторая копия стенда вне каталога релиза ломает посадку.** Симптом, проверка, причина и
   лечение — HANDOFF-LIVE.md §6в; пример pid/порт/релиз там же, строка 589.

### 5. ТЕКУЩИЙ ОСТАТОК (ответственный)
1. Обновить якорь на l115o и добавить в карту: вторую копию стенда, `wave-log-compress.timer`,
   статус диска A1, реплику A2 — координатор.
2. Живая сверка карты хотя бы по якорным строкам (машина, юнит, порт) — координатор.
3. Держать карту при каждом изменении устройства — координатор, постоянно.

### 6. ПЕРВЫЙ ШАГ НУЛЕВОГО АГЕНТА (без агентов)
Сверить якорь карты с живым `/api/ready`. Затем пройти по разделу «МАШИНЫ» и по каждой
машине подтвердить одну якорную строку (`systemctl is-active` для службы, `readlink /proc/$pid/cwd`
для процесса). Строку, которую подтвердить не удалось, пометить «не подтверждено» с датой —
не удалять.
```

---
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-420","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-09-14T15:03:08.112Z