# Прогресс реализации SelfPost Живой трекер состояния. **Переживает `/clear`** — читается первым при возобновлении работы. План (открытые вопросы + опциональные фазы): [implementation-plan.md](implementation-plan.md). ТЗ: [specification.md](specification.md). История релизов: [CHANGELOG.md](../CHANGELOG.md). История сделанного по фазам (0→11, все закрыты) — в `git log` и в CHANGELOG, здесь не дублируется. ## Как возобновить после сброса контекста 1. Прочитать этот файл (текущее состояние, что дальше). 2. Открыть `implementation-plan.md` — там нерешённые вопросы и опциональная линия 2.x.x (Фаза O1+). 3. При необходимости — детали в `specification.md`. 4. Продолжить с пункта «Следующий шаг». ## Модель по типу работы Правило: безопасность / инфра / риск-критичное → **Opus**; UI / документация / бойлерплейт → **Sonnet**; тривиальная механика → **Haiku**. ## Коммиты Коммит на **каждом осмысленном шаге** (не каждое сохранение файла, но и не только конец фазы): рабочий под-функционал, зелёная сборка, конец фазы. Минимум — один коммит на закрытую фазу + промежуточные на связные под-шаги. Ветка `main` (если пользователь не попросит отдельную). Push/PR — только по явной команде. Сообщение коммита завершается трейлером `Co-Authored-By: Claude Opus 4.8 `. На каждом таком шаге — запись в [CHANGELOG.md](../CHANGELOG.md) под `[Unreleased]` (формат Keep a Changelog). При явном решении зарезать версию — секция `[Unreleased]` переименовывается в `[X.Y.Z] - дата`, заводится новая пустая `[Unreleased]`. Тег/пуш образа — только по явному запросу (см. workflow release.yml). ## Протокол закрытия фазы/крупного шага Перед `/clear` в конце каждого законченного шага Claude: 1. Обновляет этот файл: «Текущее состояние» → что изменилось, что дальше. 2. Проверяет применимые критерии «Готово, когда…». 3. Дописывает `CHANGELOG.md` под `[Unreleased]`. 4. Делает финальный коммит шага. --- ## Текущее состояние - **Базовый линейный план 0→11 (v1.0) полностью выполнен и принят** (аудит безопасности ТЗ 7.6 — полное соответствие, деплой в проде подтверждён). Подробности — в git-истории и `CHANGELOG.md`. - **Фаза 12 (v1.x, UI/UX) выполнена:** общий `nav`-partial в `layout.html` (нав на каждой аутентифицированной странице + подсветка текущей через `Active`), `/account` (смена логина/пароля админа с проверкой текущего пароля и инвалидацией остальных сессий, `store.UpdateAdmin`), `/backup` (полный бэкап и импорт домена — две отдельные карточки, убраны с дашборда), карточка «Sending server settings» на странице домена (сервер/465/587 по `SUBMISSION_ENABLE`), кнопки Copy у DKIM-записи и нового пароля приложения, скрытие поля «Addresses» в режиме wildcard (`static/panel.js`). Проверено в контейнере на dev-сервере (setup→login→домен→приложение→смена пароля→импорт), `gofmt`/`vet`/`test`/`docker build` зелёные. - **Фаза 13 (v1.x, статус + DNS) выполнена:** новый пакет `internal/health` (supervisorctl-статус процессов, срок действия TLS-сертификата, milter-сокеты + общий словарь статусов ok/warn/error/unknown) и `internal/dnscheck` (FCrDNS для `SELFPOST_HOSTNAME`, DKIM против реального ключа, поверхностная SPF-эвристика, DMARC; кеш + таймаут, резолвер за интерфейсом — юнит-тесты без сети). Страница `/status` (карточки + HTMX-фрагмент `/status/fragment` на 5с) стала стартовой: `GET /` → 303 на `/status`, список доменов переехал на `GET /domains`, кнопка Reload перенесена на `/status` с пояснением. На странице домена — карточка «DNS status» с кнопкой Re-check (`POST /domains/{id}/dns-recheck`). Проверено в контейнере на dev-сервере против реального DNS: PTR совпадает (`selfpost.example.com`) и не совпадает (`example.com`), DKIM отсутствует/не совпадает, SPF отсутствует/через `include:` («не могу сказать»), DMARC `p=quarantine`/`p=reject`/нет; таймаут DNS деградирует в «unknown», страница не виснет. - **Попутно исправлен давний дефект:** панель никогда не могла прочитать очередь (`postqueue -p`) в штатном деплое — `postqueue` полагается на setgid-бит `postdrop`, а `no-new-privileges` из поставляемого compose его отключает, поэтому экран Queue всегда показывал «Could not read the mail queue» (в т.ч. в released 1.0.0). Пользователь `panel` теперь состоит в группе `postdrop` (`build/Dockerfile`). - **Дальше — то, что перечислено в `implementation-plan.md`:** открытые вопросы (раздел A-D — hardening сверх обязательного 7.6, надёжность, CI/тесты), **Фаза 14** — security-заголовки + документация про `/data/setup-token`; опциональная **Фаза O1+** (входящий релей, линия 2.x.x, требует согласования). - **Прод:** `selfpost.example.com`, реальный Let's Encrypt сертификат, живой e2e (DKIM/SPF pass). ## Рабочая петля (dev loop) — ВАЖНО Локально (Windows, ``) **нет Go и Docker** — только редактирование и git. Вся сборка/тесты идут на dev-сервере `selfpost.example.com` (Debian 12 bookworm, тот же, что базовый образ; провижён под разработку). Цикл: править локально → залить дерево на сервер → `go build`/`go vet`/`docker build`/тесты там. `rsync` в локальном git-bash нет, поэтому дерево едет tar'ом по ssh: `tar -czf - --exclude=.git . | ssh root@selfpost.example.com 'rm -rf /root/selfpost-src && mkdir -p /root/selfpost-src && tar -xzf - -C /root/selfpost-src'`; Go на сервере — в `/usr/local/go/bin` (не в `PATH` по умолчанию); образ — `docker build -f build/Dockerfile -t selfpost:dev --build-arg VERSION=dev .`. Источник истины и git-история — локальный репозиторий; сервер — только исполнитель сборки/тестов. Подключение: `ssh root@selfpost.example.com` (по ключу).