From 005922bc7d4a85a703e3bffd2d0d14eecb9afc71 Mon Sep 17 00:00:00 2001 From: Mix Date: Fri, 16 Oct 2026 00:15:46 +0300 Subject: [PATCH] design: panel-dkim-signing stage 2 - the pages without OpenDKIM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Health: the opendkim process row goes; the Milter sockets card shows the panel's signer on its two sockets, dkim-signer · applications (sign.sock) and · notifications (sign-local.sock) - Q3 of the plan; Configuration says it reloads Postfix. Domain delete: "stop signing mail as the domain" for the OpenDKIM reload. System log: mail.log is Postfix's log (OpenDKIM never wrote to it), its sample line goes. Message page: the sample's OpenDKIM line becomes a qmgr line. Help, sender check: the From: line is read as a mail program reads it and must be one address; DMARC holds the signature to it; the sentence the mockup carried twice is once. Overview: 5 of 5 sockets. Co-Authored-By: Claude Opus 5.5 --- docs/assets/panel-redesign/panel/health.html | 6 +++--- docs/assets/panel-redesign/panel/help.html | 2 +- docs/assets/panel-redesign/panel/out-domain-delete.html | 2 +- docs/assets/panel-redesign/panel/out-message.html | 2 +- docs/assets/panel-redesign/panel/overview.html | 2 +- docs/assets/panel-redesign/panel/src/health.html | 6 +++--- docs/assets/panel-redesign/panel/src/help.html | 2 +- docs/assets/panel-redesign/panel/src/out-domain-delete.html | 2 +- docs/assets/panel-redesign/panel/src/out-message.html | 2 +- docs/assets/panel-redesign/panel/src/overview.html | 2 +- docs/assets/panel-redesign/panel/src/system-log.html | 5 ++--- docs/assets/panel-redesign/panel/system-log.html | 5 ++--- 12 files changed, 18 insertions(+), 20 deletions(-) diff --git a/docs/assets/panel-redesign/panel/health.html b/docs/assets/panel-redesign/panel/health.html index 556ffd8..1fbcacb 100644 --- a/docs/assets/panel-redesign/panel/health.html +++ b/docs/assets/panel-redesign/panel/health.html @@ -82,7 +82,6 @@ ProgramStateDetail postfixrunningpid 41, uptime 18 days - opendkimrunningpid 39, uptime 18 days journal-milterrunningpid 40, uptime 18 days panelrunningpid 44, uptime 18 days dmarc-ingestrunningpid 47, uptime 18 days @@ -130,7 +129,8 @@
- + + @@ -165,7 +165,7 @@
08

Configuration

Safe to run at any time
-

Regenerates the OpenDKIM and Postfix configuration and the spam filter's lists from the database, removes the keys of deleted domains, and reloads both daemons. Use it after editing the files by hand, after restoring a backup, or when the running configuration looks out of step with the domain and application lists. It does not touch the queue or the TLS certificate.

+

Regenerates the Postfix configuration and the spam filter's lists from the database, removes the keys of deleted domains, and reloads Postfix. Use it after editing the files by hand, after restoring a backup, or when the running configuration looks out of step with the domain and application lists. It does not touch the queue or the TLS certificate.

diff --git a/docs/assets/panel-redesign/panel/help.html b/docs/assets/panel-redesign/panel/help.html index 156d9b5..1ae1557 100644 --- a/docs/assets/panel-redesign/panel/help.html +++ b/docs/assets/panel-redesign/panel/help.html @@ -125,7 +125,7 @@
02

Outbound

DNS records

DKIM proves the message was signed by this relay, SPF lists the addresses allowed to send for the domain, DMARC tells receivers what to do when both fail and where to report. Publish all three exactly as shown; merge SPF into an existing record rather than adding a second one. A domain's status is the worst of its three records, and results are cached for a few minutes, so use Re-check DNS after publishing. The SPF check is shallow: it looks for this server's address in the record itself and does not follow include: or redirect=, so a record that relies on them is reported as a warning, not a failure. p=none does not affect delivery; tighten to quarantine, then reject, once the reports look clean. A domain's report address is set in its Domain settings.

Applications

An application is one SASL login, unique across all domains. It may send as any address of its domain or only as listed addresses, and, with a list of client IPs set, only from those addresses. The password is shown once at creation or regeneration and is not stored in readable form.

-

Sender check

An application sends only as itself, and that holds for both senders a message has. The envelope sender is held against the login by Postfix. The From: line, the one a mail program shows and the one the DKIM signature is chosen by, is held against it by the panel, under the same rule: an application set to any address of its domain may write any address of that domain, and one with a list of addresses only those. A message with any other From: - another of your domains, the server's own name, an address somewhere else, or none at all - is refused for good with 550 5.7.1. The line is read the way the signer, OpenDKIM, reads it, so only its address counts: what a display name or a comment around it says is not judged, and a line holding more than one address is refused. The line is read the way the signer, OpenDKIM, reads it, so only its address counts: what a display name or a comment around it says is not judged, and a line holding more than one address is refused. It is a rejected row of the Outbound log, and its page says which address was refused. This is what keeps one application from getting mail signed with another domain's key.

The check is strict. While the panel does not answer, Postfix does not accept mail unchecked: it replies with a temporary error and the application sends again later. Server › Health counts what that costs: for how long the panel did not answer while Postfix itself was running; how many connections Postfix turned away because of it - it refuses from the connection on, before a message is offered, so a client that tries three times is three and a monitor that only connects is one; and how many messages were refused with a temporary error in the middle, when the panel stopped answering after a message had begun. A time marked ≈ includes a start at which supervisord could not say since when Postfix was running: the time since the panel's last sign of life is counted, which may be more than Postfix was up. The card warns when a connection was turned away or a message refused in the last 24 hours. If the numbers stay at nothing, the check is where it should be; if they do not, it belongs in a process of its own.

+

Sender check

An application sends only as itself, and that holds for both senders a message has. The envelope sender is held against the login by Postfix. The From: line, the one a mail program shows and the one DMARC holds the DKIM signature to, is held against it by the panel, under the same rule: an application set to any address of its domain may write any address of that domain, and one with a list of addresses only those. A message with any other From: - another of your domains, the server's own name, an address somewhere else, or none at all - is refused for good with 550 5.7.1. The line must hold one address, read as a mail program reads it: what a display name around it says is not judged, and a line holding more than one address is refused. It is a rejected row of the Outbound log, and its page says which address was refused. This is what keeps one application from sending, under this server's signature, as another domain or another application.

The check is strict. While the panel does not answer, Postfix does not accept mail unchecked: it replies with a temporary error and the application sends again later. Server › Health counts what that costs: for how long the panel did not answer while Postfix itself was running; how many connections Postfix turned away because of it - it refuses from the connection on, before a message is offered, so a client that tries three times is three and a monitor that only connects is one; and how many messages were refused with a temporary error in the middle, when the panel stopped answering after a message had begun. A time marked ≈ includes a start at which supervisord could not say since when Postfix was running: the time since the panel's last sign of life is counted, which may be more than Postfix was up. The card warns when a connection was turned away or a message refused in the last 24 hours. If the numbers stay at nothing, the check is where it should be; if they do not, it belongs in a process of its own.

Rate limits

Level 1 is a per-IP ceiling set in .env. Level 2 is optional, per domain and per application, entered by hand or computed from the last 30 days of sending, and can never exceed level 1. An application's own limit replaces its domain's.

Outbound log

One row per message and recipient, with the status Postfix last reported: queued (accepted, nothing reported yet), sent (the receiving server took it; what the mailbox does next is not reported back), deferred (not taken yet, Postfix tries again), bounced (failed for good, or the queue lifetime ran out; or closed by the panel when Postfix no longer held the message and mail.log never said what became of it) and rejected (refused before queueing, so it never reached the queue: under a level-2 rate limit, from a client address the application does not allow, or for a From: line that is not the application's - the message's page says which). A message's page lists these steps and the lines Postfix wrote about it in mail.log, found by queue id - and for a message refused before it had one, Postfix's one line about the refusal. The lines are kept with the row: they stay on the page after that file has rotated, for as long as the row does. A message accepted before version 2.2.0 shows them only while they are still in the current file. Rows are deleted after the retention set in Server › Settings, 90 days by default, and their lines with them. A domain administrator sees only the rows of their own domains.

Queue and retries

The page lists, as postqueue -p prints it, the messages Postfix has accepted and not yet delivered: mail being sent right now and mail deferred for a later attempt. Only the global role can open it, and it only shows; nothing here deletes or forces a message. A deferred message is tried again after the first retry delay, then at gaps that double up to a cap, until the receiving server takes it or the queue lifetime runs out and it bounces; there is no attempt counter, and the box on the page shows this server's values. A queue that keeps growing means a receiver is refusing or not answering: open the message from the Outbound log to read the reason Postfix logged.

diff --git a/docs/assets/panel-redesign/panel/out-domain-delete.html b/docs/assets/panel-redesign/panel/out-domain-delete.html index e8b6631..6182d17 100644 --- a/docs/assets/panel-redesign/panel/out-domain-delete.html +++ b/docs/assets/panel-redesign/panel/out-domain-delete.html @@ -81,7 +81,7 @@
  • permanently delete its DKIM signing key;
  • delete both applications (prod-server, alerts) with their credentials and sender bindings;
  • -
  • reload OpenDKIM so the domain is no longer signed.
  • +
  • stop signing mail as the domain.

The published DKIM record becomes invalid — remove it from DNS unless you plan to re-add the domain. Inbound domains are not touched.

diff --git a/docs/assets/panel-redesign/panel/out-message.html b/docs/assets/panel-redesign/panel/out-message.html index e5d204a..6fb6e05 100644 --- a/docs/assets/panel-redesign/panel/out-message.html +++ b/docs/assets/panel-redesign/panel/out-message.html @@ -107,7 +107,7 @@
03

Delivery log

Lines from mail.log with this queue id
13:58:07 postfix/smtpd[2114]: 4XcB7k2Jm9z1: client=unknown[198.51.100.7], sasl_username=alerts
 13:58:07 postfix/cleanup[2120]: 4XcB7k2Jm9z1: message-id=<a81f0c@alertmanager>
-13:58:07 opendkim[39]: 4XcB7k2Jm9z1: DKIM-Signature field added (s=sp2026, d=example.org)
+13:58:07 postfix/qmgr[2061]: 4XcB7k2Jm9z1: from=<alerts@example.org>, size=2214, nrcpt=1 (queue active)
 13:58:09 postfix/smtp[2123]: 4XcB7k2Jm9z1: to=<noc@example.org>, relay=mx1.example.org[192.0.2.10]:25, delay=1.9, status=deferred (host mx1.example.org said: 451 4.7.1 Greylisted, try again in 5 minutes)
diff --git a/docs/assets/panel-redesign/panel/overview.html b/docs/assets/panel-redesign/panel/overview.html index a99592b..a58f499 100644 --- a/docs/assets/panel-redesign/panel/overview.html +++ b/docs/assets/panel-redesign/panel/overview.html @@ -82,7 +82,7 @@ Processes5 of 5 runningup 18 daysTLS certificateExpires in 12 daysrenewal has not runQueuesBoth emptyoutbound and inbound - Milter sockets3 of 3 answeringincl. the spam filter + Milter sockets5 of 5 answeringincl. the spam filterReverse DNSForward = reverse203.0.113.25 diff --git a/docs/assets/panel-redesign/panel/src/health.html b/docs/assets/panel-redesign/panel/src/health.html index 6b39073..c78693a 100644 --- a/docs/assets/panel-redesign/panel/src/health.html +++ b/docs/assets/panel-redesign/panel/src/health.html @@ -16,7 +16,6 @@ - @@ -64,7 +63,8 @@
MilterState
opendkim
inet:127.0.0.1:8891
ok
dkim-signer · applications
unix:/run/selfpost/sign.sock
ok
dkim-signer · notifications
unix:/run/selfpost/sign-local.sock
ok
journal-milter
unix:/run/selfpost/journal.sock
ok
sender-check
unix:/run/selfpost/sender.sock
ok
rspamd · inbound spam filter
inet:antispam:11332
ok
ProgramStateDetail
postfixrunningpid 41, uptime 18 days
opendkimrunningpid 39, uptime 18 days
journal-milterrunningpid 40, uptime 18 days
panelrunningpid 44, uptime 18 days
dmarc-ingestrunningpid 47, uptime 18 days
- + + @@ -99,7 +99,7 @@
08

Configuration

Safe to run at any time
-

Regenerates the OpenDKIM and Postfix configuration and the spam filter's lists from the database, removes the keys of deleted domains, and reloads both daemons. Use it after editing the files by hand, after restoring a backup, or when the running configuration looks out of step with the domain and application lists. It does not touch the queue or the TLS certificate.

+

Regenerates the Postfix configuration and the spam filter's lists from the database, removes the keys of deleted domains, and reloads Postfix. Use it after editing the files by hand, after restoring a backup, or when the running configuration looks out of step with the domain and application lists. It does not touch the queue or the TLS certificate.

diff --git a/docs/assets/panel-redesign/panel/src/help.html b/docs/assets/panel-redesign/panel/src/help.html index 4283b6c..4797860 100644 --- a/docs/assets/panel-redesign/panel/src/help.html +++ b/docs/assets/panel-redesign/panel/src/help.html @@ -59,7 +59,7 @@
02

Outbound

DNS records

DKIM proves the message was signed by this relay, SPF lists the addresses allowed to send for the domain, DMARC tells receivers what to do when both fail and where to report. Publish all three exactly as shown; merge SPF into an existing record rather than adding a second one. A domain's status is the worst of its three records, and results are cached for a few minutes, so use Re-check DNS after publishing. The SPF check is shallow: it looks for this server's address in the record itself and does not follow include: or redirect=, so a record that relies on them is reported as a warning, not a failure. p=none does not affect delivery; tighten to quarantine, then reject, once the reports look clean. A domain's report address is set in its Domain settings.

Applications

An application is one SASL login, unique across all domains. It may send as any address of its domain or only as listed addresses, and, with a list of client IPs set, only from those addresses. The password is shown once at creation or regeneration and is not stored in readable form.

-

Sender check

An application sends only as itself, and that holds for both senders a message has. The envelope sender is held against the login by Postfix. The From: line, the one a mail program shows and the one the DKIM signature is chosen by, is held against it by the panel, under the same rule: an application set to any address of its domain may write any address of that domain, and one with a list of addresses only those. A message with any other From: - another of your domains, the server's own name, an address somewhere else, or none at all - is refused for good with 550 5.7.1. The line is read the way the signer, OpenDKIM, reads it, so only its address counts: what a display name or a comment around it says is not judged, and a line holding more than one address is refused. The line is read the way the signer, OpenDKIM, reads it, so only its address counts: what a display name or a comment around it says is not judged, and a line holding more than one address is refused. It is a rejected row of the Outbound log, and its page says which address was refused. This is what keeps one application from getting mail signed with another domain's key.

The check is strict. While the panel does not answer, Postfix does not accept mail unchecked: it replies with a temporary error and the application sends again later. Server › Health counts what that costs: for how long the panel did not answer while Postfix itself was running; how many connections Postfix turned away because of it - it refuses from the connection on, before a message is offered, so a client that tries three times is three and a monitor that only connects is one; and how many messages were refused with a temporary error in the middle, when the panel stopped answering after a message had begun. A time marked ≈ includes a start at which supervisord could not say since when Postfix was running: the time since the panel's last sign of life is counted, which may be more than Postfix was up. The card warns when a connection was turned away or a message refused in the last 24 hours. If the numbers stay at nothing, the check is where it should be; if they do not, it belongs in a process of its own.

+

Sender check

An application sends only as itself, and that holds for both senders a message has. The envelope sender is held against the login by Postfix. The From: line, the one a mail program shows and the one DMARC holds the DKIM signature to, is held against it by the panel, under the same rule: an application set to any address of its domain may write any address of that domain, and one with a list of addresses only those. A message with any other From: - another of your domains, the server's own name, an address somewhere else, or none at all - is refused for good with 550 5.7.1. The line must hold one address, read as a mail program reads it: what a display name around it says is not judged, and a line holding more than one address is refused. It is a rejected row of the Outbound log, and its page says which address was refused. This is what keeps one application from sending, under this server's signature, as another domain or another application.

The check is strict. While the panel does not answer, Postfix does not accept mail unchecked: it replies with a temporary error and the application sends again later. Server › Health counts what that costs: for how long the panel did not answer while Postfix itself was running; how many connections Postfix turned away because of it - it refuses from the connection on, before a message is offered, so a client that tries three times is three and a monitor that only connects is one; and how many messages were refused with a temporary error in the middle, when the panel stopped answering after a message had begun. A time marked ≈ includes a start at which supervisord could not say since when Postfix was running: the time since the panel's last sign of life is counted, which may be more than Postfix was up. The card warns when a connection was turned away or a message refused in the last 24 hours. If the numbers stay at nothing, the check is where it should be; if they do not, it belongs in a process of its own.

Rate limits

Level 1 is a per-IP ceiling set in .env. Level 2 is optional, per domain and per application, entered by hand or computed from the last 30 days of sending, and can never exceed level 1. An application's own limit replaces its domain's.

Outbound log

One row per message and recipient, with the status Postfix last reported: queued (accepted, nothing reported yet), sent (the receiving server took it; what the mailbox does next is not reported back), deferred (not taken yet, Postfix tries again), bounced (failed for good, or the queue lifetime ran out; or closed by the panel when Postfix no longer held the message and mail.log never said what became of it) and rejected (refused before queueing, so it never reached the queue: under a level-2 rate limit, from a client address the application does not allow, or for a From: line that is not the application's - the message's page says which). A message's page lists these steps and the lines Postfix wrote about it in mail.log, found by queue id - and for a message refused before it had one, Postfix's one line about the refusal. The lines are kept with the row: they stay on the page after that file has rotated, for as long as the row does. A message accepted before version 2.2.0 shows them only while they are still in the current file. Rows are deleted after the retention set in Server › Settings, 90 days by default, and their lines with them. A domain administrator sees only the rows of their own domains.

Queue and retries

The page lists, as postqueue -p prints it, the messages Postfix has accepted and not yet delivered: mail being sent right now and mail deferred for a later attempt. Only the global role can open it, and it only shows; nothing here deletes or forces a message. A deferred message is tried again after the first retry delay, then at gaps that double up to a cap, until the receiving server takes it or the queue lifetime runs out and it bounces; there is no attempt counter, and the box on the page shows this server's values. A queue that keeps growing means a receiver is refusing or not answering: open the message from the Outbound log to read the reason Postfix logged.

diff --git a/docs/assets/panel-redesign/panel/src/out-domain-delete.html b/docs/assets/panel-redesign/panel/src/out-domain-delete.html index 940eee9..5ed7821 100644 --- a/docs/assets/panel-redesign/panel/src/out-domain-delete.html +++ b/docs/assets/panel-redesign/panel/src/out-domain-delete.html @@ -15,7 +15,7 @@
  • permanently delete its DKIM signing key;
  • delete both applications (prod-server, alerts) with their credentials and sender bindings;
  • -
  • reload OpenDKIM so the domain is no longer signed.
  • +
  • stop signing mail as the domain.

The published DKIM record becomes invalid — remove it from DNS unless you plan to re-add the domain. Inbound domains are not touched.

diff --git a/docs/assets/panel-redesign/panel/src/out-message.html b/docs/assets/panel-redesign/panel/src/out-message.html index 52d5330..a2bead1 100644 --- a/docs/assets/panel-redesign/panel/src/out-message.html +++ b/docs/assets/panel-redesign/panel/src/out-message.html @@ -41,6 +41,6 @@
03

Delivery log

Lines from mail.log with this queue id
13:58:07 postfix/smtpd[2114]: 4XcB7k2Jm9z1: client=unknown[198.51.100.7], sasl_username=alerts
 13:58:07 postfix/cleanup[2120]: 4XcB7k2Jm9z1: message-id=<a81f0c@alertmanager>
-13:58:07 opendkim[39]: 4XcB7k2Jm9z1: DKIM-Signature field added (s=sp2026, d=example.org)
+13:58:07 postfix/qmgr[2061]: 4XcB7k2Jm9z1: from=<alerts@example.org>, size=2214, nrcpt=1 (queue active)
 13:58:09 postfix/smtp[2123]: 4XcB7k2Jm9z1: to=<noc@example.org>, relay=mx1.example.org[192.0.2.10]:25, delay=1.9, status=deferred (host mx1.example.org said: 451 4.7.1 Greylisted, try again in 5 minutes)
diff --git a/docs/assets/panel-redesign/panel/src/overview.html b/docs/assets/panel-redesign/panel/src/overview.html index 534b5c4..bf9fa44 100644 --- a/docs/assets/panel-redesign/panel/src/overview.html +++ b/docs/assets/panel-redesign/panel/src/overview.html @@ -16,7 +16,7 @@ Processes5 of 5 runningup 18 daysTLS certificateExpires in 12 daysrenewal has not runQueuesBoth emptyoutbound and inbound - Milter sockets3 of 3 answeringincl. the spam filter + Milter sockets5 of 5 answeringincl. the spam filterReverse DNSForward = reverse203.0.113.25 diff --git a/docs/assets/panel-redesign/panel/src/system-log.html b/docs/assets/panel-redesign/panel/src/system-log.html index c0a37ee..8258a7d 100644 --- a/docs/assets/panel-redesign/panel/src/system-log.html +++ b/docs/assets/panel-redesign/panel/src/system-log.html @@ -3,7 +3,7 @@

Server

System log

-

The tail of mail.log — Postfix, OpenDKIM and the milters. For one message, open it from a log page instead.

+

The tail of mail.log, the log Postfix writes. For one message, open it from a log page instead.

@@ -17,6 +17,5 @@ Sep 21 13:57:41postfix/smtpd[2119]: NOQUEUE: reject: RCPT from bulk.example[198.51.100.90]: 550 5.1.1 <sales@lists.example.org>: Recipient address rejected: unknown recipientSep 21 13:44:02postfix/smtp[2101]: 4XcB2m7Hd4z1: to=<jobs@acme.io>, relay=none, status=deferred (connect to 10.0.4.12[10.0.4.12]:2525: Connection timed out)Sep 21 13:41:19postfix/smtp[2098]: 4XcB1c3Rk8z1: to=<j.doe@nonexistent.example>, status=bounced (Host or domain name not found) -Sep 21 13:40:02journal-milter[40]: rate limit: app=prod-server 201/200 in 3600s — tempfail at MAIL FROM -Sep 21 13:00:00 opendkim[39]: key table reloaded (3 domains) +Sep 21 13:40:02journal-milter[40]: rate limit: app=prod-server 201/200 in 3600s — tempfail at MAIL FROM diff --git a/docs/assets/panel-redesign/panel/system-log.html b/docs/assets/panel-redesign/panel/system-log.html index d24cf59..53678c3 100644 --- a/docs/assets/panel-redesign/panel/system-log.html +++ b/docs/assets/panel-redesign/panel/system-log.html @@ -69,7 +69,7 @@

Server

System log

-

The tail of mail.log — Postfix, OpenDKIM and the milters. For one message, open it from a log page instead.

+

The tail of mail.log, the log Postfix writes. For one message, open it from a log page instead.

@@ -83,8 +83,7 @@ Sep 21 13:57:41postfix/smtpd[2119]: NOQUEUE: reject: RCPT from bulk.example[198.51.100.90]: 550 5.1.1 <sales@lists.example.org>: Recipient address rejected: unknown recipientSep 21 13:44:02postfix/smtp[2101]: 4XcB2m7Hd4z1: to=<jobs@acme.io>, relay=none, status=deferred (connect to 10.0.4.12[10.0.4.12]:2525: Connection timed out)Sep 21 13:41:19postfix/smtp[2098]: 4XcB1c3Rk8z1: to=<j.doe@nonexistent.example>, status=bounced (Host or domain name not found) -Sep 21 13:40:02journal-milter[40]: rate limit: app=prod-server 201/200 in 3600s — tempfail at MAIL FROM -Sep 21 13:00:00 opendkim[39]: key table reloaded (3 domains) +Sep 21 13:40:02journal-milter[40]: rate limit: app=prod-server 201/200 in 3600s — tempfail at MAIL FROM
MilterState
opendkim
inet:127.0.0.1:8891
ok
dkim-signer · applications
unix:/run/selfpost/sign.sock
ok
dkim-signer · notifications
unix:/run/selfpost/sign-local.sock
ok
journal-milter
unix:/run/selfpost/journal.sock
ok
sender-check
unix:/run/selfpost/sender.sock
ok
rspamd · inbound spam filter
inet:antispam:11332
ok