release: cut 1.0.0
Pin compose and local trial to ghcr.io/mixeme/selfpost:1.0.0, close the CHANGELOG cut, retire implementation-plan and v1.x-closure-plan, and point e2e/CI comments at development.md. Co-Authored-By: Composer <noreply@cursor.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,7 +7,7 @@ name: release
|
|||||||
# can never drift apart (the invariant restore's version check in spec 7.5.A
|
# can never drift apart (the invariant restore's version check in spec 7.5.A
|
||||||
# depends on).
|
# depends on).
|
||||||
#
|
#
|
||||||
# Native per-architecture builds (plan implementation-plan.md C.4), not qemu:
|
# Native per-architecture builds (see docs/development.md), not qemu:
|
||||||
# running the full Postfix/OpenDKIM stack under emulation for the e2e gate
|
# running the full Postfix/OpenDKIM stack under emulation for the e2e gate
|
||||||
# below is impractically slow. Each arch builds, e2e-gates and pushes its own
|
# below is impractically slow. Each arch builds, e2e-gates and pushes its own
|
||||||
# tag on its own native runner; a merge job then combines them into the one
|
# tag on its own native runner; a merge job then combines them into the one
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
go-version: "1.26"
|
go-version: "1.26"
|
||||||
cache-dependency-path: test/e2e/go.sum
|
cache-dependency-path: test/e2e/go.sum
|
||||||
|
|
||||||
- name: e2e (gates publishing — see docs/implementation-plan.md C.4)
|
- name: e2e (gates publishing — see docs/development.md)
|
||||||
run: cd test/e2e && go test -v -timeout 20m ./...
|
run: cd test/e2e && go test -v -timeout 20m ./...
|
||||||
|
|
||||||
- name: Log in to ghcr.io
|
- name: Log in to ghcr.io
|
||||||
|
|||||||
+11
-4
@@ -5,6 +5,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.0.0] - 2026-08-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- A **Delivery log** on each delivery's page (`/deliveries/{id}`): the
|
- A **Delivery log** on each delivery's page (`/deliveries/{id}`): the
|
||||||
@@ -61,10 +63,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
|||||||
layering and route table match `web`→`store` and `POST /domains/import`;
|
layering and route table match `web`→`store` and `POST /domains/import`;
|
||||||
OpenDKIM drops to `opendkim` via `UserID`; guide drops the archived
|
OpenDKIM drops to `opendkim` via `UserID`; guide drops the archived
|
||||||
"spec 7.5" pointer, clarifies `POSTFIX_SENDER_LOGIN_MAPS` vs panel writes,
|
"spec 7.5" pointer, clarifies `POSTFIX_SENDER_LOGIN_MAPS` vs panel writes,
|
||||||
and states logrotate keeps 14 daily files. Intermediate CHANGELOG cuts vs
|
and states logrotate keeps 14 daily files. Intermediate CHANGELOG cuts from
|
||||||
the still-pinned compose `0.1.0` image are called out in the guide and
|
before the published `1.0.0` image are called out in the guide and
|
||||||
`development.md`. Roadmap / implementation-plan point at CHANGELOG
|
`development.md`. Roadmap points at CHANGELOG `[0.5.0]` Security and
|
||||||
`[0.5.0]` Security and refreshed `internal/web` size / symbol links.
|
refreshed `internal/web` size / symbol links.
|
||||||
- Full backups no longer carry `/data/log`. It is Postfix's raw log plus its
|
- Full backups no longer carry `/data/log`. It is Postfix's raw log plus its
|
||||||
fourteen rotated copies — diagnostic output rather than state to restore, and
|
fourteen rotated copies — diagnostic output rather than state to restore, and
|
||||||
otherwise by far the largest thing in the archive.
|
otherwise by far the largest thing in the archive.
|
||||||
@@ -94,6 +96,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
|||||||
dev-host-specific workflow and `mixfed.ru` references removed from docs.
|
dev-host-specific workflow and `mixfed.ru` references removed from docs.
|
||||||
- `docs/development.md` and `.cursor/rules/agent-rules.mdc` translated to
|
- `docs/development.md` and `.cursor/rules/agent-rules.mdc` translated to
|
||||||
English; `roadmap.md` remains Russian (internal tracker).
|
English; `roadmap.md` remains Russian (internal tracker).
|
||||||
|
- Deploy pin and local-trial image tag set to `ghcr.io/mixeme/selfpost:1.0.0`
|
||||||
|
(compose and git tag `v1.0.0` cut together). Retired
|
||||||
|
`docs/implementation-plan.md` and `docs/v1.x-closure-plan.md`; Makefile,
|
||||||
|
`release.yml`, and e2e comments point at `docs/development.md`. Roadmap
|
||||||
|
v1.x documentation/deploy tail closed.
|
||||||
|
|
||||||
## [0.6.0] - 2026-08-08
|
## [0.6.0] - 2026-08-08
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ test:
|
|||||||
clean:
|
clean:
|
||||||
rm -rf bin
|
rm -rf bin
|
||||||
|
|
||||||
# Hermetic container e2e (plan implementation-plan.md C.4): separate Go module
|
# Hermetic container e2e (see docs/development.md): separate Go module
|
||||||
# under test/e2e so its test-only dependencies (DKIM verification) never enter
|
# under test/e2e so its test-only dependencies (DKIM verification) never enter
|
||||||
# this module's build graph. Builds the image fresh from this checkout, brings
|
# this module's build graph. Builds the image fresh from this checkout, brings
|
||||||
# up deploy/docker-compose.yml plus a test-only override on high ports and an
|
# up deploy/docker-compose.yml plus a test-only override on high ports and an
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ docker run --rm -d --name selfpost-try \
|
|||||||
-e SELFPOST_HOSTNAME=mail.local.test \
|
-e SELFPOST_HOSTNAME=mail.local.test \
|
||||||
-e PANEL_COOKIE_SECURE=false \
|
-e PANEL_COOKIE_SECURE=false \
|
||||||
-v selfpost-try-data:/data \
|
-v selfpost-try-data:/data \
|
||||||
ghcr.io/mixeme/selfpost:0.1.0
|
ghcr.io/mixeme/selfpost:1.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
**Get the setup URL** (pick one):
|
**Get the setup URL** (pick one):
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
selfpost:
|
selfpost:
|
||||||
image: ghcr.io/mixeme/selfpost:0.1.0
|
image: ghcr.io/mixeme/selfpost:1.0.0
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
SELFPOST_HOSTNAME: "${SELFPOST_HOSTNAME:?set the mail/panel hostname, e.g. mail.example.com}"
|
SELFPOST_HOSTNAME: "${SELFPOST_HOSTNAME:?set the mail/panel hostname, e.g. mail.example.com}"
|
||||||
|
|||||||
+12
-23
@@ -1,8 +1,7 @@
|
|||||||
# SelfPost — development
|
# SelfPost — development
|
||||||
|
|
||||||
**What this file is.** How to build, test, document, and ship changes. Open
|
**What this file is.** How to build, test, document, and ship changes. Open
|
||||||
work for v1.x and 2.x lives in [roadmap.md](roadmap.md) (and, until the tag,
|
work for 2.x lives in [roadmap.md](roadmap.md). Product boundaries:
|
||||||
[v1.x-closure-plan.md](v1.x-closure-plan.md)). Product boundaries:
|
|
||||||
[product.md](product.md). As-built layout: [architecture.md](architecture.md).
|
[product.md](product.md). As-built layout: [architecture.md](architecture.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -12,10 +11,8 @@ work for v1.x and 2.x lives in [roadmap.md](roadmap.md) (and, until the tag,
|
|||||||
After `/clear` or a fresh chat:
|
After `/clear` or a fresh chat:
|
||||||
|
|
||||||
1. Read this file (process, docs rules, model routing).
|
1. Read this file (process, docs rules, model routing).
|
||||||
2. Open [roadmap.md](roadmap.md) for open work; until `v1.0.0`, also
|
2. Open [roadmap.md](roadmap.md) for open work. Accepted risks —
|
||||||
[v1.x-closure-plan.md](v1.x-closure-plan.md) for the remaining closure
|
[security.md](security.md); as-built — [architecture.md](architecture.md).
|
||||||
checklist. Accepted risks — [security.md](security.md); as-built —
|
|
||||||
[architecture.md](architecture.md).
|
|
||||||
3. Skim [product.md](product.md) if scope is in doubt.
|
3. Skim [product.md](product.md) if scope is in doubt.
|
||||||
4. Continue from the next unchecked step in the active plan.
|
4. Continue from the next unchecked step in the active plan.
|
||||||
|
|
||||||
@@ -31,7 +28,7 @@ not duplicated here.
|
|||||||
| Security, infra, file permissions, Postfix/`postqueue`, open-relay risk | **Opus** | `mail.log` under `/data`, entrypoint permissions, queue reconcile |
|
| Security, infra, file permissions, Postfix/`postqueue`, open-relay risk | **Opus** | `mail.log` under `/data`, entrypoint permissions, queue reconcile |
|
||||||
| UI / JS / CSS, templates, documentation (English), README | **Sonnet** | adaptive polling, this file's Documentation section |
|
| UI / JS / CSS, templates, documentation (English), README | **Sonnet** | adaptive polling, this file's Documentation section |
|
||||||
| Trivial mechanics: retarget links, grep, compose bump, CHANGELOG cut | **Haiku** | Makefile / release.yml comment fixes, deleting closed plan files |
|
| Trivial mechanics: retarget links, grep, compose bump, CHANGELOG cut | **Haiku** | Makefile / release.yml comment fixes, deleting closed plan files |
|
||||||
| Security **review** (not authorship) | **Fable** | pre-release checklist pass ([implementation-plan.md](implementation-plan.md) § D — done) |
|
| Security **review** (not authorship) | **Fable** | pre-release checklist (CHANGELOG `[0.5.0]` Security / [security.md](security.md) — done) |
|
||||||
|
|
||||||
Default rule: risk-critical → Opus; UI / docs / boilerplate → Sonnet; trivial
|
Default rule: risk-critical → Opus; UI / docs / boilerplate → Sonnet; trivial
|
||||||
mechanics → Haiku. Reviewers must not be the author of the code under review.
|
mechanics → Haiku. Reviewers must not be the author of the code under review.
|
||||||
@@ -154,20 +151,16 @@ The release image is published **only on tag** `vX.Y.Z` (not on every push to
|
|||||||
|
|
||||||
**Steps (on explicit request):**
|
**Steps (on explicit request):**
|
||||||
|
|
||||||
1. Close `[Unreleased]` in [CHANGELOG.md](../CHANGELOG.md).
|
1. Close `[Unreleased]` in [CHANGELOG.md](../CHANGELOG.md) and bump the pinned
|
||||||
2. Create and push git tag `vX.Y.Z`.
|
tag in [deploy/docker-compose.yml](../deploy/docker-compose.yml) (and any
|
||||||
|
local-trial image references) in the **same** release commit.
|
||||||
|
2. Create and push git tag `vX.Y.Z` on that commit.
|
||||||
3. Workflow [release.yml](../.github/workflows/release.yml) builds, e2e-gates,
|
3. Workflow [release.yml](../.github/workflows/release.yml) builds, e2e-gates,
|
||||||
and publishes `ghcr.io/mixeme/selfpost:X.Y.Z`.
|
and publishes `ghcr.io/mixeme/selfpost:X.Y.Z`.
|
||||||
4. Update the pinned tag in
|
|
||||||
[deploy/docker-compose.yml](../deploy/docker-compose.yml) in the **same**
|
|
||||||
commit as the tag (see [roadmap.md](roadmap.md) § «v1.x — documentation and
|
|
||||||
deploy tail»).
|
|
||||||
|
|
||||||
Ordinary commits **do not** publish an image. Intermediate CHANGELOG version
|
Ordinary commits **do not** publish an image. The compose pin and the git tag
|
||||||
cuts (`0.2.0`…`0.6.0`) document history on `main`; the compose pin and the only
|
must match (`1.0.0` / `v1.0.0` for the first published release). Intermediate
|
||||||
git release tag may lag until an explicit image publish (today: compose
|
CHANGELOG sections (`0.2.0`…`0.6.0`) record development history before that cut.
|
||||||
`0.1.0`, tag `v0.0.1` — see [roadmap.md](roadmap.md) § «v1.x — documentation
|
|
||||||
and deploy tail»).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -280,13 +273,9 @@ There is no `docs/archive/` directory.
|
|||||||
| As-built design | [architecture.md](architecture.md) |
|
| As-built design | [architecture.md](architecture.md) |
|
||||||
| Development process (this file) | [development.md](development.md) |
|
| Development process (this file) | [development.md](development.md) |
|
||||||
| Security requirements and accepted risks | [security.md](security.md) |
|
| Security requirements and accepted risks | [security.md](security.md) |
|
||||||
| Internal roadmap (v1.x tail, 2.x) | [roadmap.md](roadmap.md) |
|
| Internal roadmap (2.x) | [roadmap.md](roadmap.md) |
|
||||||
| Release history | [CHANGELOG.md](../CHANGELOG.md) |
|
| Release history | [CHANGELOG.md](../CHANGELOG.md) |
|
||||||
|
|
||||||
`implementation-plan.md` remains only until the release cut (describes the
|
|
||||||
closed release gate); delete it in the release commit. Temporary
|
|
||||||
[v1.x-closure-plan.md](v1.x-closure-plan.md) goes away with that cut too.
|
|
||||||
|
|
||||||
### User-facing deliverables
|
### User-facing deliverables
|
||||||
|
|
||||||
| Artefact | Role |
|
| Artefact | Role |
|
||||||
|
|||||||
+8
-9
@@ -375,12 +375,11 @@ but it can look like an open port in external scans.
|
|||||||
## Fixed image tag
|
## Fixed image tag
|
||||||
|
|
||||||
`deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`),
|
`deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`),
|
||||||
deliberately never `:latest`. Until the `v1.0.0` cut the shipped pin is still
|
deliberately never `:latest`. The current pin is `1.0.0`. Intermediate
|
||||||
`0.1.0` — intermediate CHANGELOG sections (`0.2.0`…`0.6.0`) record development
|
CHANGELOG sections (`0.2.0`…`0.6.0`) record development cuts from before that
|
||||||
cuts and do not imply a published image of that tag. Pinning matters because of
|
image was published. Pinning matters because of the backup version check above:
|
||||||
the backup version check above: the panel binary's embedded version and the
|
the panel binary's embedded version and the image tag that produced it are the
|
||||||
image tag that produced it are the same value by construction (the release CI
|
same value by construction (the release CI stamps both from one git tag — see
|
||||||
stamps both from one git tag — see `.github/workflows/release.yml`), so the
|
`.github/workflows/release.yml`), so the pin is what makes "restore into the
|
||||||
pin is what makes "restore into the same version" a checkable fact rather than
|
same version" a checkable fact rather than a guess. Upgrade by bumping the tag
|
||||||
a guess. Upgrade by bumping the tag deliberately, not by riding a moving
|
deliberately, not by riding a moving target.
|
||||||
target.
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
# План реализации: SelfPost
|
|
||||||
|
|
||||||
**Статус:** линия v1.0/v1.x до тега релиза **закрыта** — предрелизная ревизия
|
|
||||||
безопасности (§ D) выполнена 2026-08-06, релизный гейт (e2e + ревизия) открыт.
|
|
||||||
B.1–B.3 и C.4 закрыты — as-built в [architecture.md](architecture.md),
|
|
||||||
e2e/CI в [development.md](development.md), принятые риски в
|
|
||||||
[security.md](security.md). Текущее состояние и следующий шаг:
|
|
||||||
[roadmap.md](roadmap.md) и [v1.x-closure-plan.md](v1.x-closure-plan.md).
|
|
||||||
Объём 2.x.x — [roadmap.md](roadmap.md).
|
|
||||||
|
|
||||||
**Основа:** [product.md](product.md) v1.0.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## D. Предрелизная ревизия безопасности — ВЫПОЛНЕНО (2026-08-06)
|
|
||||||
|
|
||||||
**Проверка моделью Fable** по дифу от аудита v1.0 (Фаза 11, `bd64e80` — тега
|
|
||||||
`v1.0.0` в репозитории нет, это его фактический эквивалент) до HEAD, плюс
|
|
||||||
полный повторный проход по чек-листу безопасности (бывшее ТЗ 7.6, теперь
|
|
||||||
[security.md](security.md)). Приоритеты из плана покрыты: аутентификация и
|
|
||||||
сессии, валидация ввода, запись в конфиги/map-файлы, `os/exec`, права в
|
|
||||||
`/data`, секреты.
|
|
||||||
|
|
||||||
**Результат.** Эксплуатируемых уязвимостей (high/medium) не найдено. Одна
|
|
||||||
находка defence-in-depth закрыта правкой до тега: логин приложения передаётся
|
|
||||||
в `saslpasswd2` после `--`, чтобы значение, начинающееся с `-` (допустимо
|
|
||||||
whitelist'ом), не могло быть разобрано getopt как флаг
|
|
||||||
([internal/app/sasl.go](../internal/app/sasl.go)). Принятые риски в
|
|
||||||
[security.md](security.md) не пополнились — существующие записи (origin-check
|
|
||||||
fallback, отсутствие CSRF-токенов, ложно-отрицательный `bounced` при сверке
|
|
||||||
с `postqueue`) покрывают всё найденное.
|
|
||||||
Сводка ревизии — в записи `Security` CHANGELOG `[0.5.0]`.
|
|
||||||
+12
-71
@@ -11,84 +11,25 @@
|
|||||||
решением.
|
решением.
|
||||||
|
|
||||||
**Основа:** [product.md](product.md) v1.0. Процесс и правила документации —
|
**Основа:** [product.md](product.md) v1.0. Процесс и правила документации —
|
||||||
[development.md](development.md). Несделанное для v1.0/v1.x до тега — в
|
[development.md](development.md). История закрытых фаз v1.x — в `git log` и
|
||||||
[implementation-plan.md](implementation-plan.md) и
|
[CHANGELOG.md](../CHANGELOG.md).
|
||||||
[v1.x-closure-plan.md](v1.x-closure-plan.md). Хвост закрытого
|
|
||||||
документационного прохода (D1–D9) — в секции ниже.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## v1.x — хвост документации и деплоя
|
## v1.x — хвост документации и деплоя
|
||||||
|
|
||||||
**Статус:** не блокирует релизный тег; бывший хвост закрытого
|
**Статус: закрыто** в релизе `1.0.0` / git-тег `v1.0.0`
|
||||||
документационного прохода (D1–D9). Делать по желанию или в релизном коммите,
|
(`ghcr.io/mixeme/selfpost:1.0.0`). План закрытия и `implementation-plan.md`
|
||||||
где указано. Сводка чек-листов до тега —
|
удалены — история в git и CHANGELOG; `docs/archive/` не храним.
|
||||||
[v1.x-closure-plan.md](v1.x-closure-plan.md).
|
|
||||||
|
|
||||||
**Тег образа в compose + git tag — один релизный коммит (R1).** В
|
| Тема | Итог |
|
||||||
[deploy/docker-compose.yml](../deploy/docker-compose.yml) поле `image:` бампить
|
|---|---|
|
||||||
до версии релиза **в том же коммите**, что и git-тег `vX.Y.Z` — не раньше.
|
| Адаптивный опрос мониторинга | 5 с / 30 с / 0 (скрытая вкладка) в `panel.js` |
|
||||||
Сейчас там `0.1.0`, то есть отстаёт от целевой версии; несовпадение мешает
|
| `mail.log` + reconcile | `/data/log/mail.log`; сверка с `postqueue -p` |
|
||||||
только до первого выката по тегу. Сам тег — последний шаг релизного гейта:
|
| Docs consolidation | процесс в [development.md](development.md); README Documentation |
|
||||||
содержательная часть (e2e C.4, ревизия § D) закрыта, режется по явной команде
|
| Compose pin + git tag | `1.0.0` / `v1.0.0` в одном релизном коммите |
|
||||||
оператора ([development.md](development.md) § Commits and release build). После
|
|
||||||
тега `release.yml` собирает и публикует `ghcr.io/mixeme/selfpost:X.Y.Z`,
|
|
||||||
поэтому compose с новым тегом и сам тег обязаны появиться вместе — иначе
|
|
||||||
compose неделю ссылается на несуществующий образ.
|
|
||||||
|
|
||||||
**Убрать `implementation-plan.md` — в релизном коммите.** Документ закрыт:
|
Открытая работа дальше — только секции 2.x ниже.
|
||||||
уникального содержания в нём нет, § D (предрелизная ревизия безопасности)
|
|
||||||
продублирован в [security.md](security.md) и CHANGELOG `[0.5.0]/Security`,
|
|
||||||
а разделы B.1–B.3 и C.4 вырезаны ещё в `22f86d1`. Держится до тега только
|
|
||||||
потому, что описывает релизный гейт, пока тот формально не закрыт. При резке
|
|
||||||
версии:
|
|
||||||
|
|
||||||
1. Удалить файл (история § D — в git и CHANGELOG; `docs/archive/` не храним).
|
|
||||||
2. Перецелить ссылки из кода и CI ([Makefile](../Makefile),
|
|
||||||
[.github/workflows/release.yml](../.github/workflows/release.yml),
|
|
||||||
[test/e2e/main_test.go](../test/e2e/main_test.go)) — они ссылаются на «план
|
|
||||||
C.4», секцию, которой в файле уже нет; актуальное описание e2e — в
|
|
||||||
[development.md](development.md).
|
|
||||||
3. Перецелить оставшиеся ссылки из документации на
|
|
||||||
[development.md](development.md) / [security.md](security.md) /
|
|
||||||
[roadmap.md](roadmap.md).
|
|
||||||
4. Удалить [v1.x-closure-plan.md](v1.x-closure-plan.md) в том же или следующем
|
|
||||||
коммите.
|
|
||||||
|
|
||||||
**Готово, когда:** тег образа в compose совпадает с релизом и рядом стоит
|
|
||||||
git-тег `vX.Y.Z`; `implementation-plan.md` и `v1.x-closure-plan.md` удалены,
|
|
||||||
ссылок на них в активных документах и в коде/CI не осталось.
|
|
||||||
|
|
||||||
(Закрыто и действия не требует: `docs/logo` как каталога нет — критерию «либо
|
|
||||||
содержит файлы, либо отсутствует» удовлетворяет; Quick start в
|
|
||||||
[README.md](../README.md) тянет `docker-compose.yml` и `.env.example` с
|
|
||||||
`raw.githubusercontent.com` — это и есть единственная площадка проекта, зеркал
|
|
||||||
больше нет.)
|
|
||||||
|
|
||||||
**Сводный индекс документации в README.** ~~Ссылки на `docs/` разбросаны по
|
|
||||||
тексту README…~~ **Закрыто (v1.x-closure Фаза 3):** секция Documentation в
|
|
||||||
[README.md](../README.md) — единый список operator docs + roadmap.
|
|
||||||
|
|
||||||
**Опрос мониторинга у открытой, но незанятой вкладки.** ~~Скрытая вкладка уже не
|
|
||||||
опрашивает сервер (фильтр на `htmx:beforeRequest` в
|
|
||||||
[panel.js](../internal/web/static/panel.js)). Остаток: вкладка на переднем
|
|
||||||
плане, с которой не работают, всё равно ходит раз в 5 с. Кандидат — адаптивный
|
|
||||||
интервал (5 с при активности, 30 с при простое) по `htmx:afterRequest` без
|
|
||||||
изменения `hx-trigger`. Ценность низкая: нагрузка — один SQL-запрос и рендер
|
|
||||||
фрагмента, так что это скорее гигиена, чем экономия. Допустимый исход —
|
|
||||||
осознанно не делать.~~ **Закрыто (v1.x-closure Фаза 1):** адаптивный интервал
|
|
||||||
5 с / 30 с / 0 (скрытая вкладка) в `panel.js` через `data-poll`.
|
|
||||||
|
|
||||||
**Send-log vs `mail.log`.** ~~Persist позиции чтения сделан (таблица
|
|
||||||
`logtail_state`, миграция `0003`): после рестарта панели log-tailer дочитывает
|
|
||||||
пропущенный хвост. Остаётся пересоздание контейнера — `mail.log` не в `/data` и
|
|
||||||
теряется вместе с ним, такие строки навсегда останутся `queued`. Кандидаты, если
|
|
||||||
станет больно: volume для лога, сверка зависших строк через `postqueue`.~~
|
|
||||||
**Закрыто (v1.x-closure Фаза 2):** сделаны оба кандидата — `mail.log` переехал в
|
|
||||||
`/data/log/`, а строки, чьи delivery-строки потеряны безвозвратно, закрываются
|
|
||||||
сверкой с `postqueue -p` (grace 2 мин → `bounced`). As-built и оставшийся риск
|
|
||||||
(ложный `bounced`): [architecture.md](architecture.md) § Log tailer,
|
|
||||||
[security.md](security.md).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -94,8 +94,8 @@ Hardening сверх обязательного (security-заголовки, п
|
|||||||
|
|
||||||
## Принятые риски
|
## Принятые риски
|
||||||
|
|
||||||
Здесь, а не в [implementation-plan.md](implementation-plan.md): план — про
|
Принятый риск — решение с условием возврата, а не отложенная задача из
|
||||||
несделанную работу, принятый риск — решение с условием возврата.
|
дорожной карты.
|
||||||
|
|
||||||
- **`POST` без `Sec-Fetch-Site` и без `Origin` пропускается.**
|
- **`POST` без `Sec-Fetch-Site` и без `Origin` пропускается.**
|
||||||
Клиент, не посылающий ни одного из двух — по-настоящему старый браузер или
|
Клиент, не посылающий ни одного из двух — по-настоящему старый браузер или
|
||||||
@@ -171,7 +171,7 @@ Origin-проверка закрывает это без изменения ни
|
|||||||
|
|
||||||
## Как этот список пополняется
|
## Как этот список пополняется
|
||||||
|
|
||||||
Предрелизная проверка на уязвимости ([implementation-plan.md](implementation-plan.md)
|
Предрелизная проверка на уязвимости (модель Fable; история — CHANGELOG
|
||||||
§ D, модель Fable) закрывает каждую находку одним из двух способов: правка до
|
`[0.5.0]` Security) закрывает каждую находку одним из двух способов: правка до
|
||||||
тега — либо запись сюда, с обоснованием и условием возврата, как у пунктов выше.
|
тега — либо запись сюда, с обоснованием и условием возврата, как у пунктов выше.
|
||||||
Третьего варианта («посмотрели и ладно») нет.
|
Третьего варианта («посмотрели и ладно») нет.
|
||||||
|
|||||||
@@ -1,209 +0,0 @@
|
|||||||
# План закрытия хвоста v1.x
|
|
||||||
|
|
||||||
**Статус:** в работе. **Целевой релиз:** `v1.0.0` / `ghcr.io/mixeme/selfpost:1.0.0`.
|
|
||||||
|
|
||||||
**Временный файл:** после закрытия v1.x удалить (история — git + CHANGELOG). Не часть постоянного пакета docs.
|
|
||||||
|
|
||||||
**Контекст:** релизный гейт по коду закрыт (B.1–B.3, C.4 e2e, § D Fable). Остаток — [roadmap.md](roadmap.md) § «v1.x — хвост документации и деплоя» + код из того же секции.
|
|
||||||
|
|
||||||
**Политика docs:** `docs/archive/` не храним; устаревшие планы удаляем после переноса полезного в [development.md](development.md).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Рекомендация по модели
|
|
||||||
|
|
||||||
Общее правило (переносится в development.md):
|
|
||||||
|
|
||||||
| Тип работы | Модель | Примеры в этом плане |
|
|
||||||
|------------|--------|---------------------|
|
|
||||||
| Безопасность, инфра, права файлов, postfix/postqueue, open-relay-риски | **Opus** | mail.log в `/data`, entrypoint permissions, postqueue reconcile |
|
|
||||||
| UI/JS/CSS, шаблоны, документация (English), README | **Sonnet** | adaptive polling, development.md § Documentation, README index |
|
|
||||||
| Тривиальная механика: retarget ссылок, grep, compose bump, CHANGELOG cut | **Haiku** | Makefile/release.yml комментарии, удаление файлов |
|
|
||||||
| Ревизия безопасности (**review**, не authorship) | **Fable** | не в этом плане (§ D уже выполнен) |
|
|
||||||
|
|
||||||
| Фаза / коммит | Модель | Почему |
|
|
||||||
|---------------|--------|--------|
|
|
||||||
| 1. Polling | Sonnet | panel.js + HTMX, CSP |
|
|
||||||
| 2. mail.log + reconcile | Opus (+ Sonnet на unit-тесты) | postfix path, logrotate, entrypoint, postqueue |
|
|
||||||
| 3. Docs consolidation | Sonnet | development.md, README, agent-rules |
|
|
||||||
| 4. Release `1.0.0` | Haiku / Sonnet | механика релиза, без новой логики |
|
|
||||||
| 5. Tag / push | **Оператор** | явная команда |
|
|
||||||
|
|
||||||
Сообщение коммита: трейлер `Co-Authored-By: Claude <модель> <noreply@anthropic.com>` с моделью, которая делала шаг.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Общий чек-лист прогресса
|
|
||||||
|
|
||||||
- [x] **Фаза 1** — адаптивный опрос мониторинга
|
|
||||||
- [x] **Фаза 2** — mail.log в `/data` + postqueue reconcile
|
|
||||||
- [x] **Фаза 3** — docs: development.md, README, удаление планов и `docs/archive/`
|
|
||||||
- [ ] **Фаза 4** — релизный коммит `1.0.0` (по явной команде)
|
|
||||||
- [ ] **Фаза 5** — tag `v1.0.0` + push (по явной команде)
|
|
||||||
- [ ] **Фаза 6** — выкат на прод (оператор)
|
|
||||||
|
|
||||||
**Гейт перед тегом:**
|
|
||||||
|
|
||||||
- [ ] `gofmt -l .` чистый
|
|
||||||
- [ ] `go vet ./...` чистый
|
|
||||||
- [ ] `go test ./...` чистый (Windows-падения domain/logtail — известны, не блокер)
|
|
||||||
- [ ] `make e2e` зелёный (если Docker доступен)
|
|
||||||
- [ ] grep: нет ссылок на удалённые docs в живых файлах
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Фаза 1 — Адаптивный опрос мониторинга
|
|
||||||
|
|
||||||
**Модель:** Sonnet.
|
|
||||||
|
|
||||||
**Цель:** 5 s при активности, 30 s при простое; скрытая вкладка — 0 запросов. Без `hx-trigger="every 5s [expr]"` (CSP / `unsafe-eval`).
|
|
||||||
|
|
||||||
### Чек-лист
|
|
||||||
|
|
||||||
- [x] `status_body.html`, `mail_queue_body.html`, `system_log_body.html`, `deliveries_rows.html` — `hx-trigger="load"` + маркер polling
|
|
||||||
- [x] `panel.js` — `lastActivity`, schedule после `htmx:afterRequest` (5s / 30s)
|
|
||||||
- [x] `htmx:afterSwap` — реинициализация polling-элементов
|
|
||||||
- [x] `beforeRequest` для hidden tab — оставлен
|
|
||||||
- [x] `architecture.md` — одна строка про polling
|
|
||||||
- [x] CHANGELOG `[Unreleased]`
|
|
||||||
- [ ] Стенд (опционально): idle → ~30s; активность → ~5s; hidden → 0 запросов
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Фаза 2 — Send-log vs `mail.log`
|
|
||||||
|
|
||||||
**Модель:** Opus (infra); Sonnet (тесты).
|
|
||||||
|
|
||||||
### 2.1 `mail.log` → `/data/log/mail.log`
|
|
||||||
|
|
||||||
- [x] `build/postfix-config.sh` — `maillog_file` из `MAIL_LOG`
|
|
||||||
- [x] `build/logrotate-mail.conf` — путь + `create 0640 postfix selfpost`
|
|
||||||
- [x] `build/entrypoint.sh` — `mkdir`, права (`2750 postfix:selfpost`, файл `0640`),
|
|
||||||
исключение `/data/log` из общего `chown` на `panel`
|
|
||||||
- [x] `cmd/panel/main.go` — default `MAIL_LOG`
|
|
||||||
- [x] `internal/backup` — исключить `log/` из архива (+ тест)
|
|
||||||
- [x] `test/e2e/mail_helpers.go` — путь в контейнере
|
|
||||||
- [x] `guide.md` — `MAIL_LOG`, System log, бэкап `./data`; README — рост диска
|
|
||||||
- [x] `envdoc_test` — `MAIL_LOG` в `buildScriptKeys`
|
|
||||||
|
|
||||||
### 2.2 Postqueue reconcile
|
|
||||||
|
|
||||||
- [x] `internal/postfix` — `QueueIDs` / парсер queue-id из `postqueue -p` + тест
|
|
||||||
- [x] `internal/store` — `ListQueuedOlderThan(cutoff)`
|
|
||||||
- [x] `internal/logtail` — periodic sweep (5 min), grace 2 min → `bounced`,
|
|
||||||
старт только после того, как tailer дочитал лог до конца
|
|
||||||
- [x] Тесты reconcile
|
|
||||||
|
|
||||||
### 2.3 Документация и риски
|
|
||||||
|
|
||||||
- [x] `architecture.md` — `/data/log`, reconcile; gap «container recreate» убран
|
|
||||||
- [x] `security.md` — риск «вечный queued» снят; на его месте — ложный `bounced`
|
|
||||||
и права на лог
|
|
||||||
- [x] CHANGELOG `[Unreleased]`
|
|
||||||
|
|
||||||
**Стенд (остаток, для оператора):** Docker на машине разработки недоступен —
|
|
||||||
сборка образа и старт контейнера не проверены. Проверить при выкате: `/data/log`
|
|
||||||
создаётся с нужными правами, панель читает `mail.log`, logrotate проворачивает
|
|
||||||
файл, `postqueue -p` читается из-под `panel`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Фаза 3 — Документация
|
|
||||||
|
|
||||||
**Модель:** Sonnet.
|
|
||||||
|
|
||||||
### 3.1 `documentation-plan.md` → development.md, затем delete
|
|
||||||
|
|
||||||
- [x] development.md § **Documentation**:
|
|
||||||
- [x] Documentation map (без archive; история = git + CHANGELOG)
|
|
||||||
- [x] User-facing deliverables
|
|
||||||
- [x] Maintaining documentation (§3 правила)
|
|
||||||
- [x] Verifying docs against code (полная таблица §2)
|
|
||||||
- [x] `architecture.md` шапка → development.md
|
|
||||||
- [x] Удалить `documentation-plan.md`
|
|
||||||
- [x] Retarget `roadmap.md`
|
|
||||||
|
|
||||||
### 3.2 `progress.md` → development.md, затем delete
|
|
||||||
|
|
||||||
- [x] development.md § **Resuming work**
|
|
||||||
- [x] development.md § **Model routing**
|
|
||||||
- [x] development.md § **Commits** (слить с Release build)
|
|
||||||
- [x] development.md § **Phase closure** (roadmap, не progress)
|
|
||||||
- [x] `agent-rules.mdc` → development.md
|
|
||||||
- [x] Удалить `progress.md`
|
|
||||||
- [x] Retarget все ссылки
|
|
||||||
|
|
||||||
### 3.3 Удалить `docs/archive/`
|
|
||||||
|
|
||||||
- [x] Удалить `docs/archive/specification-v1.0.md`
|
|
||||||
- [x] Удалить каталог `docs/archive/`
|
|
||||||
- [x] Убрать ссылки на archive из живых docs (CHANGELOG историю ниже 0.6.0 не трогать)
|
|
||||||
|
|
||||||
### 3.4 README
|
|
||||||
|
|
||||||
- [x] § Documentation: operator docs + roadmap (internal, Russian)
|
|
||||||
- [x] Без секции Archive
|
|
||||||
|
|
||||||
### 3.5 Проверка
|
|
||||||
|
|
||||||
- [x] `gofmt` / `vet` / `test`
|
|
||||||
- [x] grep живых ссылок на удалённые файлы
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Фаза 4 — Релизный коммит R1
|
|
||||||
|
|
||||||
**Модель:** Haiku / Sonnet. **Только по явной команде оператора.**
|
|
||||||
|
|
||||||
- [ ] CHANGELOG: `[Unreleased]` → `[1.0.0] - дата`, новая `[Unreleased]`
|
|
||||||
- [ ] `deploy/docker-compose.yml` → `ghcr.io/mixeme/selfpost:1.0.0`
|
|
||||||
- [ ] Удалить `implementation-plan.md` (если ещё есть)
|
|
||||||
- [ ] Makefile, `.github/workflows/release.yml`, `test/e2e/main_test.go` → development.md
|
|
||||||
- [ ] `roadmap.md` — секция v1.x **закрыта**; убрать «переместить в archive»
|
|
||||||
- [ ] Финальный grep ссылок
|
|
||||||
- [ ] Удалить этот файл (`v1.x-closure-plan.md`) в том же или следующем коммите
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Фаза 5 — Tag и push
|
|
||||||
|
|
||||||
**Оператор.** Явная команда.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git tag v1.0.0
|
|
||||||
git push origin v1.0.0
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] `release.yml` — build amd64+arm64, e2e gate, push GHCR `1.0.0`
|
|
||||||
- [ ] compose tag и git tag совпадают
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Фаза 6 — Прод (оператор)
|
|
||||||
|
|
||||||
- [ ] `docker compose pull` → `1.0.0`
|
|
||||||
- [ ] Разлогин (cookie `__Host-`)
|
|
||||||
- [ ] Reverse-proxy передаёт `Host`
|
|
||||||
- [ ] `./data/log/` создался после upgrade
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Критерий «хвост v1.x закрыт»
|
|
||||||
|
|
||||||
- [ ] Все чек-листы фаз 1–4 отмечены
|
|
||||||
- [ ] `compose` = `1.0.0`, тег `v1.0.0` в репо
|
|
||||||
- [ ] Нет `implementation-plan.md`, `documentation-plan.md`, `progress.md`, `docs/archive/`, `v1.x-closure-plan.md`
|
|
||||||
- [ ] Нет активных ссылок на удалённые пути
|
|
||||||
- [ ] `development.md` — процесс + Documentation + model routing
|
|
||||||
- [ ] `roadmap.md` — единственный internal tracker
|
|
||||||
- [ ] Polling 5s/30s; mail.log в `/data`; reconcile; риск security снят
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Разбивка коммитов
|
|
||||||
|
|
||||||
1. **Polling** — panel.js, templates, architecture (+ CHANGELOG)
|
|
||||||
2. **mail.log + reconcile** — build, logtail, store, postfix, docs (+ CHANGELOG)
|
|
||||||
3. **Docs** — development.md, README, agent-rules; delete documentation-plan, progress, archive; retarget (+ CHANGELOG)
|
|
||||||
4. **Release 1.0.0** — CHANGELOG cut, compose, delete implementation-plan, roadmap closure, delete `v1.x-closure-plan.md`
|
|
||||||
5. **Tag/push** — оператор
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
// Package e2e is the hermetic container e2e gate (plan C.4): it drives the
|
// Package e2e is the hermetic container e2e gate: it drives the shipped
|
||||||
// shipped deploy/docker-compose.yml (plus a test-only override) exactly as an
|
// deploy/docker-compose.yml (plus a test-only override) exactly as an
|
||||||
// administrator and their applications would, so the class of failure unit
|
// administrator and their applications would, so the class of failure unit
|
||||||
// tests cannot see — broken container wiring — has one place to be caught
|
// tests cannot see — broken container wiring — has one place to be caught
|
||||||
// before an image is published.
|
// before an image is published.
|
||||||
//
|
//
|
||||||
// It is a separate module on purpose (see ../../docs/implementation-plan.md,
|
// It is a separate module on purpose (see ../../docs/development.md):
|
||||||
// item C.4): `go test ./...` in the main module never pulls this in, and its
|
// `go test ./...` in the main module never pulls this in, and its test-only
|
||||||
// test-only dependencies (DKIM verification) never enter the shipped
|
// dependencies (DKIM verification) never enter the shipped binaries' build
|
||||||
// binaries' build graph.
|
// graph.
|
||||||
package e2e
|
package e2e
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
Reference in New Issue
Block a user