docs: turn the accepted panel design into an enforceable contract

panel-redesign.md is rewritten for the accepted direction: three layers
(Bulma as shipped, adapters, own sp- components), the page layouts, what
an implementer must not do, and how that is enforced — guard tests that
land before any template, a design-first CI step, and acceptance by
evidence (test output plus a screenshot of every page beside its mockup,
reviewed by a model that did not write the step).

A components page shows every building block in every state; a screen
may be built from nothing else. panel/check.py lints the mockups against
the contract (class vocabulary, CSS bans on !important, id and page
selectors and stray widths, page structure) and writes
panel/outlines.json, the per-screen component skeleton an implementation
is compared with, so a different design cannot pass as this one.

Own modifier classes now carry the sp- prefix (sp-warn, sp-fail, sp-big,
sp-danger-zone, ...) so the vocabulary is machine-checkable; the lint
also caught a width set outside the layout in the mockup's own CSS.

Agent rule .cursor/rules/panel-design.mdc; roadmap row and section
updated (0/15, still candidate).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
mix
2026-09-21 20:55:18 +03:00
co-authored by Claude Fable 5.1
parent d1f22fbcc8
commit 1bcfca1586
38 changed files with 1402 additions and 256 deletions
@@ -0,0 +1,194 @@
<!-- title: Components | section: reference -->
<div class="sp-head">
<div class="sp-postmark"><span>SELFPOST</span><b>REF</b><span>DESIGN CONTRACT</span></div>
<div class="sp-grow">
<p class="sp-kicker">Reference · page_head</p>
<h1 class="title is-3">Components</h1>
<p class="sp-lead">Every building block of the panel, in every state it has. A screen is assembled from these and from plain Bulma — nothing else. The title of each box is the name of its template partial.</p>
</div>
<div class="buttons"><a class="button" href="../index.html"><i class="ti ti-arrow-left"></i>Cover sheet</a></div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">00</span><h2>Page layouts</h2><div class="sp-end">A page is exactly one of these</div></div>
<div class="sp-box-body">
<dl class="sp-facts">
<div><dt>list</dt><dd>page_head, then full-width boxes: the add form first, the table second. <a href="out-domains.html">Example</a></dd></div>
<div><dt>detail</dt><dd>page_head, then <code>columns</code>: <code>is-3</code> side menu + <code>is-9</code> boxes. <a href="out-domain.html">Example</a></dd></div>
<div><dt>form</dt><dd>page_head, then boxes in equal <code>columns</code>, one submit row under them. <a href="out-app.html">Example</a></dd></div>
<div><dt>signed out</dt><dd>The split brand / form screen, no navbar. <a href="login.html">Example</a></dd></div>
</dl>
</div>
</div>
<div class="columns">
<div class="column is-6">
<div class="box">
<div class="sp-box-head"><span class="sp-no">01</span><h2>postmark</h2><div class="sp-end">Worst status of the page's checks</div></div>
<div class="sp-box-body">
<div class="buttons">
<div class="sp-postmark"><span>SELFPOST</span><b>OK</b><span>6 OF 6 CHECKS</span></div>
<div class="sp-postmark sp-warn"><span>SELFPOST</span><b>WARN</b><span>1 OF 6 CHECKS</span></div>
<div class="sp-postmark sp-fail"><span>DNS</span><b>FAIL</b><span>1 OF 3 RECORDS</span></div>
</div>
<p class="sp-muted">Only inside page_head, at most one per page, and only where the page has a verdict to give.</p>
</div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">03</span><h2>status_tag</h2><div class="sp-end">One mapping, one function</div></div>
<table class="table is-fullwidth">
<thead><tr><th>Status</th><th>Tag</th><th>Bulma classes</th></tr></thead>
<tbody>
<tr><td>ok, published, running, sent</td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">tag is-success is-light</td></tr>
<tr><td>warn, deferred, quiet</td><td><span class="tag is-warning is-light">warn</span></td><td class="sp-mono is-size-7">tag is-warning is-light</td></tr>
<tr><td>error, fail, mismatch, bounced</td><td><span class="tag is-danger is-light">fail</span></td><td class="sp-mono is-size-7">tag is-danger is-light</td></tr>
<tr><td>unknown, off, neutral counts</td><td><span class="tag is-light">off</span></td><td class="sp-mono is-size-7">tag is-light</td></tr>
<tr><td>role: global</td><td><span class="tag is-primary is-light">global</span></td><td class="sp-mono is-size-7">tag is-primary is-light</td></tr>
</tbody>
</table>
</div>
</div>
<div class="column is-6">
<div class="box">
<div class="sp-box-head"><span class="sp-no">02</span><h2>box · box_head</h2><div class="sp-end">The end slot: a note, a tag, a link or a filter form</div></div>
<div class="sp-box-body"><p>box_body — padded content. A <code>table</code> or a run of <code>sp-record</code> blocks sits directly in the box, without box_body.</p></div>
<div class="sp-box-foot"><span>box_foot — a legend, paging</span><span class="sp-end">end slot</span></div>
</div>
<div class="box sp-danger-zone">
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-alert-triangle"></i></span><h2>box · sp-danger-zone</h2></div>
<div class="sp-box-body"><p>For destructive confirmations and the "delete" entry of a settings page. The number may be an icon when the box is not part of a sequence.</p></div>
</div>
<div class="box sp-credential">
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-eye"></i></span><h2>box · sp-credential</h2><div class="sp-end">Shown once</div></div>
<div class="sp-box-body"><p>Only for a secret the panel will never show again.</p></div>
</div>
</div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">04</span><h2>health_card</h2><div class="sp-end">Overview only · each card links to its detail</div></div>
<div class="sp-health">
<a href="#"><span class="sp-h-icon"><i class="ti ti-server-cog"></i></span><span><span class="sp-h-name">Name</span><br><span class="sp-h-value">Verdict in words</span><br><span class="sp-h-sub">ok · one supporting fact</span></span></a>
<a href="#" class="sp-warn"><span class="sp-h-icon"><i class="ti ti-certificate"></i></span><span><span class="sp-h-name">Name</span><br><span class="sp-h-value">Expires in 12 days</span><br><span class="sp-h-sub">sp-warn</span></span></a>
<a href="#" class="sp-fail"><span class="sp-h-icon"><i class="ti ti-plug-connected-x"></i></span><span><span class="sp-h-name">Name</span><br><span class="sp-h-value">1 of 2 answering</span><br><span class="sp-h-sub">sp-fail</span></span></a>
</div>
</div>
<div class="columns">
<div class="column is-7">
<div class="box">
<div class="sp-box-head"><span class="sp-no">05</span><h2>dns_record · copy_field</h2><div class="sp-end">One block per record</div></div>
<div class="sp-record">
<div class="sp-record-head"><h3>Record</h3><span class="tag is-success is-light">published</span><small>A one-line note</small></div>
<div class="sp-row">
<div class="field"><label class="label">Host / name</label>
<div class="field has-addons"><div class="control is-expanded"><input class="input" readonly value="copy_field — single line"></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div></div>
<div class="field"><label class="label">Type</label><div class="control"><input class="input" readonly value="TXT"></div></div>
</div>
<div class="field"><label class="label">Value to publish</label>
<div class="field has-addons"><div class="control is-expanded"><textarea class="textarea" rows="2" readonly>copy_field — multi-line, for a DKIM key</textarea></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div></div>
</div>
<div class="sp-record">
<div class="sp-record-head"><h3>Record</h3><span class="tag is-danger is-light">mismatch</span><small>"In DNS now" appears only when it differs</small></div>
<div class="field"><label class="label">In DNS now</label>
<div class="control"><input class="input is-danger" readonly value="what the resolver returned"></div>
<p class="help is-danger">What is wrong and the exact fix.</p></div>
</div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">07</span><h2>Table conventions</h2></div>
<table class="table is-fullwidth is-hoverable">
<thead><tr><th>Time, UTC</th><th>Name</th><th>sp-clip</th><th>Status</th><th></th></tr></thead>
<tbody>
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 14:01:52</td><td><a href="#"><strong>the row's link</strong></a></td><td class="sp-clip">A long subject is clipped with an ellipsis instead of wrapping the row</td><td><span class="tag is-success is-light">sent</span></td><td class="sp-actions"><a href="#">Details</a><a class="has-text-danger" href="#">Delete</a></td></tr>
</tbody>
</table>
<div class="sp-box-foot"><span>Page 1 of 9</span><a href="#">Older →</a></div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">09</span><h2>log_pane</h2><div class="sp-end">sp-d time · sp-w warning · sp-e error</div></div>
<pre class="sp-log"><span class="sp-d">13:58:07</span> an ordinary line
<span class="sp-d">13:58:09</span> <span class="sp-w">a deferred delivery — sp-w</span>
<span class="sp-d">13:57:41</span> <span class="sp-e">a reject or a bounce — sp-e</span></pre>
</div>
</div>
<div class="column is-5">
<div class="box">
<div class="sp-box-head"><span class="sp-no">06</span><h2>facts</h2></div>
<div class="sp-box-body">
<dl class="sp-facts">
<div><dt>Label</dt><dd>Value</dd></div>
<div><dt>Mono value</dt><dd class="sp-mono">4XcB7k2Jm9z1</dd></div>
<div><dt>sp-big</dt><dd class="sp-big">5 days</dd><dd class="sp-muted">a supporting line</dd></div>
</dl>
</div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">08</span><h2>timeline</h2></div>
<div class="sp-box-body">
<ol class="sp-timeline">
<li><time>13:58:07 UTC</time><strong>Done</strong> — default</li>
<li class="sp-warn"><time>13:58:09 UTC</time><strong>Deferred</strong> — sp-warn</li>
<li class="sp-fail"><time>14:40:00 UTC</time><strong>Bounced</strong> — sp-fail</li>
<li class="sp-pending"><time>not yet</time>Expected next — sp-pending</li>
</ol>
</div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">10</span><h2>empty_state</h2></div>
<div class="sp-empty"><i class="ti ti-stack-2"></i>One sentence: what is absent and why that is fine.</div>
</div>
<div class="box">
<div class="sp-box-head"><span class="sp-no">11</span><h2>confirm_list</h2></div>
<div class="sp-box-body">
<ul class="sp-list-check">
<li><i class="ti ti-x"></i>each thing that will be lost, one per line;</li>
<li><i class="ti ti-x"></i>inside an sp-danger-zone box, above the danger button.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="columns">
<div class="column is-3">
<aside class="menu sp-side">
<p class="menu-label">side_menu</p>
<ul class="menu-list">
<li><a class="is-active" href="#"><i class="ti ti-world-check"></i>Current <span class="tag is-danger is-light">1</span></a></li>
<li><a href="#"><i class="ti ti-apps"></i>Section <span class="tag is-light">2</span></a></li>
</ul>
<p class="menu-label">Rarely changed</p>
<ul class="menu-list">
<li><a class="has-text-danger" href="#"><i class="ti ti-trash has-text-danger"></i>Delete</a></li>
</ul>
</aside>
</div>
<div class="column is-9">
<div class="box">
<div class="sp-box-head"><span class="sp-no">12</span><h2>Form rows · route · help_topic</h2></div>
<div class="sp-box-body">
<div class="sp-form">
<div class="sp-row">
<div class="field"><label class="label">sp-row — a wide field</label><div class="control"><input class="input" value="host"></div></div>
<div class="field"><label class="label">and a short one</label><div class="control"><input class="input" value="25"></div></div>
</div>
<div class="sp-pair">
<div class="field"><label class="label">sp-pair — two equal fields</label><div class="control"><input class="input" type="password"></div></div>
<div class="field"><label class="label">Repeat</label><div class="control"><input class="input" type="password"></div><p class="help">Help text lives in Bulma's <code>help</code>.</p></div>
</div>
</div>
<p class="sp-route"><span>from@example.org</span><i class="ti ti-arrow-right sp-muted"></i><span>to@example.net</span><span class="tag is-warning is-light">deferred</span></p>
</div>
<div class="sp-help-topic"><h3>help_topic</h3><p>A heading and a paragraph; topics stack inside one box on the Help page.</p></div>
</div>
</div>
</div>
@@ -1,6 +1,6 @@
<!-- title: shop.example.org · DMARC reports | section: outbound | page: dmarc -->
<div class="sp-head">
<div class="sp-postmark warn"><span>DMARC</span><b>99.6%</b><span>PASS · 7 DAYS</span></div>
<div class="sp-postmark sp-warn"><span>DMARC</span><b>99.6%</b><span>PASS · 7 DAYS</span></div>
<div class="sp-grow">
<p class="sp-kicker"><a href="dmarc.html">DMARC reports</a> /</p>
<h1 class="title is-3">shop.example.org</h1>
@@ -48,7 +48,7 @@
<div><dt>Expires</dt><dd class="sp-mono">2026-10-03 09:14 UTC</dd></div>
<div><dt>Names</dt><dd class="sp-mono">mail.example.org</dd></div>
</dl>
<p class="mt-4 has-text-danger-40">Expires in 12 days — renewal has not run.</p>
<p class="mt-4 has-text-danger">Expires in 12 days — renewal has not run.</p>
<p class="sp-muted">Renewal normally runs 30 days before expiry. Check that port 80 is reachable and look for <code>acme</code> lines in the <a href="system-log.html">system log</a>.</p>
</div>
</div>
@@ -8,7 +8,7 @@
<div class="columns">
<div class="column is-7">
<div class="box is-danger-zone">
<div class="box sp-danger-zone">
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-alert-triangle"></i></span><h2>This cannot be undone</h2></div>
<div class="sp-box-body">
<p class="mb-3">Deleting the inbound domain <strong>lists.example.org</strong> will:</p>
@@ -16,10 +16,10 @@
<div class="sp-box-head"><span class="sp-no">02</span><h2>How retries work</h2><div class="sp-end">This Postfix's policy, read at panel start</div></div>
<div class="sp-box-body">
<dl class="sp-facts">
<div><dt>First retry</dt><dd class="is-big">5 min</dd></div>
<div><dt>Later retries</dt><dd class="is-big">doubling</dd><dd class="sp-muted">capped at 66 min</dd></div>
<div><dt>Kept in queue</dt><dd class="is-big">5 days</dd></div>
<div><dt>Then</dt><dd class="is-big">bounced</dd><dd class="sp-muted">back to the original sender</dd></div>
<div><dt>First retry</dt><dd class="sp-big">5 min</dd></div>
<div><dt>Later retries</dt><dd class="sp-big">doubling</dd><dd class="sp-muted">capped at 66 min</dd></div>
<div><dt>Kept in queue</dt><dd class="sp-big">5 days</dd></div>
<div><dt>Then</dt><dd class="sp-big">bounced</dd><dd class="sp-muted">back to the original sender</dd></div>
</dl>
</div>
</div>
@@ -8,7 +8,7 @@
<div class="columns">
<div class="column is-7">
<div class="box is-credential">
<div class="box sp-credential">
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-eye"></i></span><h2>Shown once</h2><div class="sp-end">Not stored — copy it now</div></div>
<div class="sp-record">
<div class="field"><label class="label">Login</label>
@@ -8,7 +8,7 @@
<div class="columns">
<div class="column is-7">
<div class="box is-danger-zone">
<div class="box sp-danger-zone">
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-alert-triangle"></i></span><h2>This cannot be undone</h2></div>
<div class="sp-box-body">
<p class="mb-3">Deleting <strong>example.org</strong> will:</p>
@@ -63,7 +63,7 @@
</div>
</div>
<div class="box is-danger-zone">
<div class="box sp-danger-zone">
<div class="sp-box-head"><span class="sp-no">04</span><h2>Delete domain</h2></div>
<div class="sp-box-body">
<p class="mb-3">Removes the DKIM key and both applications. Asks once more before doing it.</p>
@@ -1,6 +1,6 @@
<!-- title: example.org · Outbound domains | section: outbound | page: domains -->
<div class="sp-head">
<div class="sp-postmark fail"><span>DNS</span><b>FAIL</b><span>1 OF 3 RECORDS</span></div>
<div class="sp-postmark sp-fail"><span>DNS</span><b>FAIL</b><span>1 OF 3 RECORDS</span></div>
<div class="sp-grow">
<p class="sp-kicker"><a href="out-domains.html">Outbound domains</a> /</p>
<h1 class="title is-3">example.org</h1>
@@ -29,8 +29,8 @@
<div class="sp-box-body">
<ol class="sp-timeline">
<li><time>13:58:07 UTC</time><strong>Accepted</strong> from <span class="sp-mono">alerts</span> at 198.51.100.7</li>
<li class="warn"><time>13:58:09 UTC</time><strong>Deferred</strong> — the receiving server asked to retry later</li>
<li class="pending"><time>next attempt ≈ 14:08 UTC</time>Still in the <a href="out-queue.html">queue</a>; kept for 5 days, then bounced</li>
<li class="sp-warn"><time>13:58:09 UTC</time><strong>Deferred</strong> — the receiving server asked to retry later</li>
<li class="sp-pending"><time>next attempt ≈ 14:08 UTC</time>Still in the <a href="out-queue.html">queue</a>; kept for 5 days, then bounced</li>
</ol>
</div>
</div>
@@ -39,8 +39,8 @@
<div class="box">
<div class="sp-box-head"><span class="sp-no">03</span><h2>Delivery log</h2><div class="sp-end">Lines from <code>mail.log</code> with this queue id</div></div>
<pre class="sp-log"><span class="d">13:58:07</span> postfix/smtpd[2114]: 4XcB7k2Jm9z1: client=unknown[198.51.100.7], sasl_username=alerts
<span class="d">13:58:07</span> postfix/cleanup[2120]: 4XcB7k2Jm9z1: message-id=&lt;a81f0c@alertmanager&gt;
<span class="d">13:58:07</span> opendkim[39]: 4XcB7k2Jm9z1: DKIM-Signature field added (s=sp2026, d=example.org)
<span class="d">13:58:09</span> <span class="w">postfix/smtp[2123]: 4XcB7k2Jm9z1: to=&lt;noc@example.org&gt;, 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)</span></pre>
<pre class="sp-log"><span class="sp-d">13:58:07</span> postfix/smtpd[2114]: 4XcB7k2Jm9z1: client=unknown[198.51.100.7], sasl_username=alerts
<span class="sp-d">13:58:07</span> postfix/cleanup[2120]: 4XcB7k2Jm9z1: message-id=&lt;a81f0c@alertmanager&gt;
<span class="sp-d">13:58:07</span> opendkim[39]: 4XcB7k2Jm9z1: DKIM-Signature field added (s=sp2026, d=example.org)
<span class="sp-d">13:58:09</span> <span class="sp-w">postfix/smtp[2123]: 4XcB7k2Jm9z1: to=&lt;noc@example.org&gt;, 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)</span></pre>
</div>
@@ -21,10 +21,10 @@
<div class="sp-box-head"><span class="sp-no">02</span><h2>How retries work</h2><div class="sp-end">This Postfix's policy, read at panel start</div></div>
<div class="sp-box-body">
<dl class="sp-facts">
<div><dt>First retry</dt><dd class="is-big">5 min</dd></div>
<div><dt>Later retries</dt><dd class="is-big">doubling</dd><dd class="sp-muted">capped at 66 min</dd></div>
<div><dt>Kept in queue</dt><dd class="is-big">5 days</dd></div>
<div><dt>Then</dt><dd class="is-big">bounced</dd><dd class="sp-muted">no attempt counter, only time</dd></div>
<div><dt>First retry</dt><dd class="sp-big">5 min</dd></div>
<div><dt>Later retries</dt><dd class="sp-big">doubling</dd><dd class="sp-muted">capped at 66 min</dd></div>
<div><dt>Kept in queue</dt><dd class="sp-big">5 days</dd></div>
<div><dt>Then</dt><dd class="sp-big">bounced</dd><dd class="sp-muted">no attempt counter, only time</dd></div>
</dl>
</div>
</div>
@@ -1,6 +1,6 @@
<!-- title: Overview | section: overview -->
<div class="sp-head">
<div class="sp-postmark warn"><span>SELFPOST</span><b>WARN</b><span>1 OF 6 CHECKS</span></div>
<div class="sp-postmark sp-warn"><span>SELFPOST</span><b>WARN</b><span>1 OF 6 CHECKS</span></div>
<div class="sp-grow">
<p class="sp-kicker">mail.example.org</p>
<h1 class="title is-3">Mail is flowing. One thing needs attention.</h1>
@@ -14,7 +14,7 @@
<div class="sp-health">
<a href="health.html#machine"><span class="sp-h-icon"><i class="ti ti-cpu"></i></span><span><span class="sp-h-name">Machine</span><br><span class="sp-h-value">CPU 12 % · RAM 41 %</span><br><span class="sp-h-sub">↓ 18 kB/s · ↑ 44 kB/s</span></span></a>
<a href="health.html#processes"><span class="sp-h-icon"><i class="ti ti-server-cog"></i></span><span><span class="sp-h-name">Processes</span><br><span class="sp-h-value">5 of 5 running</span><br><span class="sp-h-sub">up 18 days</span></span></a>
<a href="health.html#certificate" class="warn"><span class="sp-h-icon"><i class="ti ti-certificate"></i></span><span><span class="sp-h-name">TLS certificate</span><br><span class="sp-h-value">Expires in 12 days</span><br><span class="sp-h-sub">renewal has not run</span></span></a>
<a href="health.html#certificate" class="sp-warn"><span class="sp-h-icon"><i class="ti ti-certificate"></i></span><span><span class="sp-h-name">TLS certificate</span><br><span class="sp-h-value">Expires in 12 days</span><br><span class="sp-h-sub">renewal has not run</span></span></a>
<a href="out-queue.html"><span class="sp-h-icon"><i class="ti ti-stack-2"></i></span><span><span class="sp-h-name">Queues</span><br><span class="sp-h-value">Both empty</span><br><span class="sp-h-sub">outbound and inbound</span></span></a>
<a href="health.html#sockets"><span class="sp-h-icon"><i class="ti ti-plug-connected"></i></span><span><span class="sp-h-name">Milter sockets</span><br><span class="sp-h-value">2 of 2 answering</span><br><span class="sp-h-sub">opendkim, journal</span></span></a>
<a href="health.html#hostname"><span class="sp-h-icon"><i class="ti ti-arrows-exchange"></i></span><span><span class="sp-h-name">Reverse DNS</span><br><span class="sp-h-value">Forward = reverse</span><br><span class="sp-h-sub">203.0.113.25</span></span></a>
@@ -44,7 +44,7 @@
<div class="sp-box-head"><span class="sp-no">03</span><h2>Sending rate limits</h2><div class="sp-end">Read-only</div></div>
<div class="sp-box-body">
<dl class="sp-facts">
<div><dt>Level 1 · per client IP</dt><dd class="is-big">600 / h</dd><dd class="sp-muted">Set in <code>.env</code>; restart to change. Nothing below may exceed it.</dd></div>
<div><dt>Level 1 · per client IP</dt><dd class="sp-big">600 / h</dd><dd class="sp-muted">Set in <code>.env</code>; restart to change. Nothing below may exceed it.</dd></div>
<div><dt>Level 2 · domain, application</dt><dd>Set on each <a href="out-domain-settings.html#limit">domain</a> and <a href="out-app.html">application</a>.</dd></div>
</dl>
</div>
@@ -9,14 +9,14 @@
<div class="box">
<div class="sp-box-head"><span class="sp-no">01</span><h2>Recent lines</h2><div class="sp-end">Last 200 · refreshes every 10 s</div></div>
<pre class="sp-log"><span class="d">Sep 21 14:01:52</span> postfix/smtp[2131]: 4XcB9q0Lw2z1: to=&lt;m.keller@gmx.de&gt;, relay=mx00.gmx.net[212.227.15.9]:25, delay=0.8, status=sent (250 Requested mail action okay)
<span class="d">Sep 21 14:01:52</span> postfix/qmgr[58]: 4XcB9q0Lw2z1: removed
<span class="d">Sep 21 14:00:18</span> postfix/smtpd[2129]: connect from mail.uni.example[192.0.2.77]
<span class="d">Sep 21 14:00:18</span> postfix/smtp[2130]: 4XcB8z5Tn1z1: to=&lt;dev@lists.example.org&gt;, relay=mx.internal[10.0.4.2]:25, status=sent (250 2.0.0 Ok: queued)
<span class="d">Sep 21 13:58:09</span> <span class="w">postfix/smtp[2123]: 4XcB7k2Jm9z1: to=&lt;noc@example.org&gt;, relay=mx1.example.org[192.0.2.10]:25, status=deferred (451 4.7.1 Greylisted, try again in 5 minutes)</span>
<span class="d">Sep 21 13:57:41</span> <span class="e">postfix/smtpd[2119]: NOQUEUE: reject: RCPT from bulk.example[198.51.100.90]: 550 5.1.1 &lt;sales@lists.example.org&gt;: Recipient address rejected: unknown recipient</span>
<span class="d">Sep 21 13:44:02</span> <span class="w">postfix/smtp[2101]: 4XcB2m7Hd4z1: to=&lt;jobs@acme.io&gt;, relay=none, status=deferred (connect to 10.0.4.12[10.0.4.12]:2525: Connection timed out)</span>
<span class="d">Sep 21 13:41:19</span> <span class="e">postfix/smtp[2098]: 4XcB1c3Rk8z1: to=&lt;j.doe@nonexistent.example&gt;, status=bounced (Host or domain name not found)</span>
<span class="d">Sep 21 13:40:02</span> <span class="w">journal-milter[40]: rate limit: app=prod-server 201/200 in 3600s — tempfail at MAIL FROM</span>
<span class="d">Sep 21 13:00:00</span> opendkim[39]: key table reloaded (3 domains)</pre>
<pre class="sp-log"><span class="sp-d">Sep 21 14:01:52</span> postfix/smtp[2131]: 4XcB9q0Lw2z1: to=&lt;m.keller@gmx.de&gt;, relay=mx00.gmx.net[212.227.15.9]:25, delay=0.8, status=sent (250 Requested mail action okay)
<span class="sp-d">Sep 21 14:01:52</span> postfix/qmgr[58]: 4XcB9q0Lw2z1: removed
<span class="sp-d">Sep 21 14:00:18</span> postfix/smtpd[2129]: connect from mail.uni.example[192.0.2.77]
<span class="sp-d">Sep 21 14:00:18</span> postfix/smtp[2130]: 4XcB8z5Tn1z1: to=&lt;dev@lists.example.org&gt;, relay=mx.internal[10.0.4.2]:25, status=sent (250 2.0.0 Ok: queued)
<span class="sp-d">Sep 21 13:58:09</span> <span class="sp-w">postfix/smtp[2123]: 4XcB7k2Jm9z1: to=&lt;noc@example.org&gt;, relay=mx1.example.org[192.0.2.10]:25, status=deferred (451 4.7.1 Greylisted, try again in 5 minutes)</span>
<span class="sp-d">Sep 21 13:57:41</span> <span class="sp-e">postfix/smtpd[2119]: NOQUEUE: reject: RCPT from bulk.example[198.51.100.90]: 550 5.1.1 &lt;sales@lists.example.org&gt;: Recipient address rejected: unknown recipient</span>
<span class="sp-d">Sep 21 13:44:02</span> <span class="sp-w">postfix/smtp[2101]: 4XcB2m7Hd4z1: to=&lt;jobs@acme.io&gt;, relay=none, status=deferred (connect to 10.0.4.12[10.0.4.12]:2525: Connection timed out)</span>
<span class="sp-d">Sep 21 13:41:19</span> <span class="sp-e">postfix/smtp[2098]: 4XcB1c3Rk8z1: to=&lt;j.doe@nonexistent.example&gt;, status=bounced (Host or domain name not found)</span>
<span class="sp-d">Sep 21 13:40:02</span> <span class="sp-w">journal-milter[40]: rate limit: app=prod-server 201/200 in 3600s — tempfail at MAIL FROM</span>
<span class="sp-d">Sep 21 13:00:00</span> opendkim[39]: key table reloaded (3 domains)</pre>
</div>
@@ -8,7 +8,7 @@
<div class="columns">
<div class="column is-7">
<div class="box is-danger-zone">
<div class="box sp-danger-zone">
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-alert-triangle"></i></span><h2>This cannot be undone</h2></div>
<div class="sp-box-body">
<p class="mb-3">Deleting the panel user <strong>shop-team</strong> will:</p>