# Прогресс реализации SelfPost Живой трекер фаз. **Переживает `/clear`** — читается первым при возобновлении работы. План фаз: [implementation-plan.md](implementation-plan.md). ТЗ: [specification.md](specification.md). ## Как возобновить после сброса контекста 1. Прочитать этот файл (текущая фаза, статус, что сделано, что дальше). 2. Прочитать соответствующую фазу в `implementation-plan.md`. 3. При необходимости — детали в `specification.md`. 4. Продолжить с пункта «Следующий шаг». ## Рекомендуемая модель по фазам | Фаза | Модель | Почему | |---|---|---| | 0 — каркас + спайк milter | **Opus** | архитектура + главный технический риск (7.3) | | 1 — Docker/supervisord/обёртка | **Opus** | тонкая логика холодного старта сокетов | | 2 — SQLite/setup-link/auth | **Opus** | безопасность 7.6 (крипто-токен, сессии, bcrypt) | | 3 — домены + OpenDKIM | **Opus** | генерация конфигов + exec-safety (7.6.3–4) | | 4 — приложения + SASL + sender_login_maps | **Opus** | риск open relay / привязки отправителя | | 5 — полный Postfix | **Opus** | самый чувствительный тракт доставки | | 6 — journal-milter | **Opus** | наивысший риск (баг ломает релей) | | 7 — UI мониторинга | **Sonnet** | шаблоны/CRUD, рутинно | | 8 — rate limit L2 | **Opus** | логика лимитов в milter | | 9 — бэкап/restore/экспорт | **Opus** | целостность данных, версионирование | | 10 — деплой + docs | **Sonnet** | compose-файлы и документация | | 11 — security-проход | **Opus** | аудит соответствия 7.6 | Правило: безопасность / инфра / риск-критичное → **Opus**; UI / документация / бойлерплейт → **Sonnet**; тривиальная механика → **Haiku**. ## Коммиты Коммит на **каждом осмысленном шаге** (не каждое сохранение файла, но и не только конец фазы): рабочий под-функционал, зелёная сборка, конец фазы. Минимум — один коммит на закрытую фазу + промежуточные на связные под-шаги. Ветка `main` (если пользователь не попросит отдельную). Push/PR — только по явной команде. Сообщение коммита завершается трейлером `Co-Authored-By: Claude Opus 4.8 `. ## Протокол закрытия фазы Перед `/clear` в конце каждой фазы Claude: 1. Обновляет этот файл: статус фазы → ✅, заполняет «Сделано» и «Следующий шаг». 2. Проверяет критерии «Готово, когда…» из плана. 3. Пишет одну строку в журнал ниже. 4. Делает финальный коммит фазы. 5. Явно говорит: «Фаза N закрыта — можно `/clear`, следующая фаза N+1 на модели X». --- ## Текущее состояние - **Текущая фаза:** 7 ✅ **закрыта** → следующая **Фаза 8** (дифференцированные лимиты, rate limit уровень 2) на **Opus** (логика лимитов в milter — риск-критично). - **Ключевая находка Фазы 6 (исправлена):** go-milter хранит имена макросов **как их шлёт Postfix** — многосимвольные имена приходят в фигурных скобках (`{auth_authen}`, `{client_addr}`), односимвольные — голыми (`i`). Спайк Фазы 0 без SASL этого не увидел (`auth_authen` был пуст «и так»). Первый прогон в контейнере дал пустой `app_login`; фикс — brace-толерантный `macro(m,name)` (пробует голый ключ, затем `{name}`). Зафиксировано в памяти [[milter-implementation-facts]]. - **Прежняя фаза:** 5 ✅ закрыта (код `b2692e4`, доки `ec4d4b9`/`2dbd8d0`). - **Финальное подтверждение доставки** (2026-07-13): реальное письмо `dtester@test.example.org → selfpost@mixeme.ru` доставлено и принято `mx.example.net`, заголовок `Authentication-Results: dkim=pass (d=test.example.org s=selfpost) ... spf=pass ... dmarc=none` (прочитано по IMAP). Попало в Junk из-за репутации нового IP/домена (`IP_REPUTATION_SPAM`, Bayes, `MX_INVALID` — у test.example.org только A без MX) — это прогрев IP/DNS уровня деплоя (ТЗ 10), не дефект релея; аутентификация (зона ответственности SelfPost) идеальна. - **Тупик, который обошли (важно для будущих тестов доставки):** нельзя тестировать доставку, отправляя с домена, который хостит сам приёмник. `mx.example.net` хостит `mixeme.ru`, поэтому письма `mixeme.ru → mixeme.ru` он жёстко отбивал `554 does not meet our delivery requirements` (own-domain anti-spoofing) при валидном DKIM+SPF. Решение: отдельный домен-отправитель `test.example.org` (не на `mc`), которому приёмник доверяет как обычной входящей почте. Первый контакт был `451 Greylisted` (норма) → принят после авто-ретраев Postfix. - **Артефакты теста на сервере:** контейнер `p5` (домены mixeme.ru id1 / test.example.org id2), скрипт/лог `/tmp/p5retry.sh`+`/root/p5retry.log`, IMAP-читалка `/tmp/imapread5.py`. DNS `test.example.org` (A/SPF/DKIM) можно снять после Фазы 6-тестов; в ящике `selfpost@mixeme.ru` остались bounce-письма от ранних mixeme.ru→mixeme.ru попыток (шум, можно удалить). ### Сделано в Фазе 7 - **Три экрана мониторинга** (`internal/web/handlers_monitor.go` + шаблоны `sendlog.html`/`queue.html`/`logtail.html` + фрагменты `sendlog_rows.html`/`queue_body.html`/`logtail_body.html`): - **Журнал отправки** (`/sendlog`): таблица время/домен/приложение/From/To/Subject/статус, серверные фильтры по домену и логину приложения (`WHERE` через `store.SendLogFilter`, параметризовано), пагинация (50/страница, `LIMIT/OFFSET`, счётчик страниц через `CountSendLog`), HTMX-polling каждые 5с (`hx-trigger="every 5s"` на самообновляющемся `
`, `hx-swap="outerHTML"` — ответ фрагмента несёт те же hx-атрибуты, поэтому поллинг не обрывается). Вывод экранируется автоматически `html/template` (subject с `