Agreed with the owner on 2026-10-13 (the panel in a chosen zone, "today"
that zone's day, the UTC labels gone) and settled on 2026-10-16/17: the
container runs in his zone ("Контейнер в моем поясе") - TZ in .env, read
by every program at its start - so the zone is shown on Settings, not
chosen there ("Да"), as the level-1 limit is.
Every mockup loses "UTC": the column heads are Time, Received, Held,
Arrived; a stamp is a date and a time. Settings gets box 05, Time zone,
read-only: the zone, its offset, where it is set, and that every time and
day of the panel is that zone's, as are mail.log's stamps. The help's
Settings topic says so. outlines.json: settings gains the box. check.py: 0
violations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner, 2026-10-16, of two ways for the navigation of a narrow screen
(pictures at 390 and 980 px): "Вариант 1. Но меню выглядит длинной
простынёй. Там бы сделать сворачивание разделов."
The cause, measured: the bar's menu was always open (navbar-menu
is-active, no burger), and Bulma lays it out below 1024 px as a sheet of
every section's pages, filling the first screen - on every phone, and on
a phone in "desktop site" mode (about 980 px) under columns that Bulma
puts side by side from 769 px; and a container has no side margin below
1024 px, so the page met the screen's edge.
Now, below 1024 px: the bar's menu is hidden (no is-active), and a Menu at
the right of the brand opens a panel over the page - Overview, then each
section as a fold, the current one open, the user's last (Account, Help,
Sign out). <details> all through: no script, like the folded DNS records.
The page and the strip of sibling pages get a 1rem gutter. From 1024 px
nothing changes. check.py: 0 violations; outlines unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner, 2026-10-16: "Да, бери notification-mail-recheck, потом режь
2.9.0."
Server › Settings gets Re-check DNS in its page head, as an outbound
domain's page has it beside its own DNS records box; the Notification
mail box's end slot says when the records were checked ("The server's
own name · checked 3 min ago"), as the domain's DNS box does. The help's
Notifications topic says Settings shows whether the records are in place
and re-checks them. The button sits in the page head rather than in the
box head: box heads hold a note, a tag, a link or a filter form, and the
page head is where the domain page and Health put the same action.
outlines.json: settings "head" -> "head+actions". check.py: 0 violations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner, 2026-10-16, on the pictures of both layouts with the records
missing and right: "Вариант 2: форма в левой колонке, кнопка под своими
полями."
Server › Settings: the form holds only the left column - 01 Log
retention, 02 Notifications, Save settings under them - so the button
sits under the fields it saves whatever the records' state. The right
column, outside the form, holds what is not a field: 03 Notification
mail (the records of the server's own name) and 04 Sending rate limits
(read-only). The boxes are numbered column by column, as before.
outlines.json rewritten for settings (check.py --write): the form moved
from around the columns into the first one; nothing else changed.
check.py: 0 violations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A record DNS already holds as shown is a <details> whose summary is the
record's head: its name, its tag and "Show values" (ti-eye - the icon
subset has no chevron); the host, type and value open behind it. The
head's note is hidden while folded, so the fold stays one line in a half
column (the server's SPF on Settings); it shows when opened. Missing,
weak, mismatched and unchecked records stay open as before.
Mockups: the kit's dns_record example, out-domain (DKIM and DMARC
folded, SPF open on its mismatch), in-domain (MX), settings (DKIM
folded, SPF missing and open); the help's DNS topic says so. Outlines
unchanged (the block keeps sp-record). check.py: 0 violations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Accepted by the owner on 2026-10-12 from screenshots, after two changes of
his: DKIM for the server's own name as well as SPF ("we need the best
deliverability for these messages"), and the records themselves under
Settings with only their state on Health.
- account: a checkbox "Send me notifications" in the Profile box, under the
e-mail it writes to. No box of its own, no choice per event.
- settings: box 03 Notifications with the instance switch; box 04
Notification mail with the two DNS records of the server's own name, DKIM
and SPF, drawn with the record component of a domain's DNS page. Sending
rate limits move to the left column as box 02.
- health: card 06 Notification mail - the state of each record and a link to
Settings - beside Configuration, now 07, each half the width.
- help: a Notifications topic (what is sent, when, to whom, what makes it
arrive); Settings and Your account each gain a clause.
outlines.json rewritten with check.py --write: the structure of settings and
health changed. build.py: 35 pages; check.py: 32 screens, 52 sp- classes,
0 violations. The plan records both of his changes; step 1 of 10.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mockup-first step of inbound-antispam-panel 6/12. The two screens were
drawn for the redesign before the feature existed; this brings them to
what steps 3-5 built.
in-log: the lead says where rows come from and how long they are kept;
a help link in the box head; Why is the engine and the detail as the
journal holds them (rspamd: action, score / threshold, weighted
symbols; postfix and a refusing filter: the reply text). No row
invents what the journal cannot hold: the "selfpost - upstream timed
out" tempfail and the Review link to a quarantine page that does not
exist are gone, a refused message has no subject, an allow-listed one
shows SELFPOST_ALLOW_FROM(-100.00).
in-filter-lists: Remove is a POST - the kit's row action, not a link;
a domain entry is shown as stored, without "@"; "Where the lists go"
names the directory and its six files instead of allow.map / deny.map,
says rspamd rereads them by itself and that Deny wins.
settings: "Keep inbound log rows, days" in the Log retention box.
help: two topics under Inbound, Inbound log and Filter lists, and a
sentence in Settings.
outlines.json rewritten: Help's Inbound box has six topics, was four.
check.py: 32 screens, 0 violations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seen by the owner on the test server (2026-10-10): following Report
address, Rate limit or Export from the domain's menu opened Domain
settings scrolled down to that box. The page is one screen and a bit, so
the anchor only pushed the title and the first row out of view. The
links (the domain menu, the DMARC record's note, the delete page, Server
> Settings) lose their #reports / #limit / #export.
check.py: 0 violations; outlines.json unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two decisions of the owner, 2026-10-10.
The report address. "Three choices: no reports, the SelfPost address, a
typed address. No reports by default. A button that fills the typed
address with one's own e-mail from the profile." This replaces the
2026-09-21 design in which a domain followed a named user's default: a
co-administrator of a domain was shown another user's login name, and
three explicit choices are simpler.
- src/out-domain-settings.html: the box is a select of the three
choices and the address field with a "Use my e-mail" button.
- src/out-domain.html: the DMARC record's note names the address, with
no user in it.
- src/account.html: the DMARC reports box is gone; Account is Profile
and Password, one box per column.
- src/settings.html: the lead no longer points at Account for the
report address.
- docs/plans/panel-redesign.md: Account e-mail rewritten, the Routes
row of account without /account/dmarc. docs/schema-migrations.md:
the columns that go.
Row actions. New password and Delete in a row of the Applications table
are POSTs, so each is a form with a button that looks like the links
beside it - as Sign out became one.
- src/out-domain.html: Edit / New password / Delete per row, the box
foot about confirmations is back; the login cell does not wrap.
- theme.css: .table td.sp-actions form sits inline with the links'
spacing, and its button takes the link's colour and type.
- src/components.html shows the pattern once.
check.py: 0 violations. outlines.json: only account changed (two equal
columns, one box each).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner's decision (2026-10-10): help is needed by almost every
section, not by Overview and Outbound alone. The Help mockup had six
topics while other screens already linked to topics it did not have
(#dmarc, #inbound).
- src/help.html: seventeen topics in five boxes - Overview (checks,
certificate, rdns), Outbound (dns, apps, limits, log, queue, dmarc),
Inbound (inbound, upstream, recipients, filter), Server (backup,
users, settings), Account (account) - one paragraph each, and the
side menu lists them by group.
- A box head gets the sp-help link where a topic now exists: health,
out-domain-settings, out-app, out-log, out-message, out-queue, the
three DMARC screens, in-domains, in-domain, backup, users, user,
settings. No link on boxes that explain themselves (confirmations,
the shown-once password, sign-in) and at most one per box.
- outlines.json regenerated (check.py --write): only help changed -
Outbound holds six topics, and Inbound, Server and Account are new
boxes. 0 violations.
The dns, dmarc and account topics describe the report-address scheme the
owner decided the same day (no reports / the SelfPost address / a typed
address, no user default); the account and out-domain-settings mockups
are redrawn for it in the change that follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Event notifications are not built (panel-notifications is a roadmap
candidate), and the outline of both screens forced a box that could only
say so. The owner's decision (2026-10-10): take the boxes out of the
mockups; they are drawn again as the first step of that feature's plan.
- src/account.html: box 03 Notifications removed, DMARC reports is 03;
the lead and the e-mail help no longer promise notifications or a
reset link.
- src/settings.html: box 02 Notifications removed, Sending rate limits
is 02; the lead no longer lists notifications.
- outlines.json regenerated (check.py --write): the two boxes are gone
from account and settings, nothing else moved. 0 violations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner's instruction (2026-10-09): in the kit Sign out had no hover tint and
stood out among Account and Help. The cause was in the design, not in the
implementation: the mockups drew Sign out as a link, the panel signs out
with a POST, and Bulma tints only a.navbar-item.
- build.py: Sign out is a form with a submit button carrying navbar-item,
as the real shell has to write it; all pages rebuilt.
- theme.css: the form stretches the button across the menu and the button
is handed Bulma's own hover / focus / active variables, so it answers
like the entries above it. No width, no colour of its own.
check.py: 32 screens, 48 sp- classes, 0 violation(s); outlines.json unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review pass over `1.4.0`…`1.9.1`: port 25 no longer shares the submission milter chain, DMARC reports are validated before they are stored, and the auto rate limit is correct for a non-default level-1 window. Schema migration `0010` (index only). Upgrading is a tag bump.
Added:
- SQLite migration `0010_send_log_app_login_index.sql` —
- DMARC aggregate reports are accepted as **zip** as well as gzip or plain XML
- e2e: `checkPort25ConfigCombinations` regenerates the Postfix configuration
Changed:
- README hero line and a **Positioning** block in `docs/product.md` record the
Fixed:
- The setup account's password is hashed at bcrypt cost `12` like every other
- Login/setup rate limiter: protecting blocked buckets from eviction is now
- `postqueue -p` output is bounded to 4 MiB (the timeout alone left the size of
- [schema-migrations.md](docs/schema-migrations.md): the schema section was
- postqueue reader is now time-bounded: `postqueue -p` runs under a 5-second
… (11 more in CHANGELOG)
Changed:
- [roadmap.md](docs/roadmap.md) now carries everything the 2026-08-19 code
- Extracted `internal/configsafe` — the empty-value and forbidden-character
- Build, deploy and migration comments no longer cite the archived
- Dropped the `parseDomainRateLimitForm` / `parseAppRateLimitForm` pass-through
- Extracted shared helpers: `internal/atomicfile` (atomic temp-write+rename) and
… (7 more in CHANGELOG)
Removed:
- Unused legacy asset `docs/assets/icon.png` (superseded by the four brand SVGs
- Plan documents for already-shipped features: `domain-stats-auto-ratelimit`,
Full detail: CHANGELOG.md (1.9.2).