Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f7b5d95c3 | ||
|
|
5e0adb2de5 | ||
|
|
1bcfca1586 | ||
|
|
d1f22fbcc8 | ||
|
|
2b69605cf9 |
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
description: The panel's design is fixed by accepted mockups; implement it, do not reinterpret it.
|
||||||
|
globs: internal/web/view/**,docs/assets/panel-redesign/**
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Panel design contract
|
||||||
|
|
||||||
|
Source of truth, in this order: the mockups in
|
||||||
|
[docs/assets/panel-redesign/panel/](docs/assets/panel-redesign/index.html),
|
||||||
|
the [components page](docs/assets/panel-redesign/panel/components.html),
|
||||||
|
`panel/outlines.json`, then
|
||||||
|
[docs/plans/panel-redesign.md](docs/plans/panel-redesign.md). Read the plan's
|
||||||
|
**The contract** and **Enforcement** sections before touching a template.
|
||||||
|
|
||||||
|
You are implementing a finished design. You are not designing.
|
||||||
|
|
||||||
|
- Build pages only from Bulma classes in the `BULMA` list of `panel/check.py`
|
||||||
|
and from the `sp-` components on the components page, through their
|
||||||
|
partials. No other class, no hand-written component markup, no inline style.
|
||||||
|
- No selector in `panel.css` that names a page or an id, uses `!important`, or
|
||||||
|
sets a width outside `WIDTH_OK`. If a page looks wrong, the layout choice is
|
||||||
|
wrong — do not patch it locally.
|
||||||
|
- If a screen seems to need a component, state or layout that does not exist:
|
||||||
|
**stop and ask.** Do not approximate it, and do not substitute a look of
|
||||||
|
your own.
|
||||||
|
- Never edit a mockup, `outlines.json`, `check.py`, the vendored Bulma or a
|
||||||
|
guard test to make your work pass. A failing guard means the implementation
|
||||||
|
is wrong. Design changes go mockup-first in a separate `design:` commit on
|
||||||
|
the owner's explicit instruction.
|
||||||
|
- "Done" is evidence, not a description: passing `go test ./internal/web/...`
|
||||||
|
output plus a screenshot of each restyled page from the running container,
|
||||||
|
saved under `docs/assets/panel-redesign/evidence/` beside its mockup's name.
|
||||||
|
Report every difference from the mockup you could not remove; do not
|
||||||
|
describe a different result as the requested one.
|
||||||
|
|
||||||
|
After editing a fragment in `panel/src/`: `python panel/build.py`, then
|
||||||
|
`python panel/check.py` (add `--write` only when the structure change is the
|
||||||
|
point of the commit).
|
||||||
@@ -23,6 +23,66 @@ document is not part of the release.
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Panel redesign, the roadmap's screens: **Inbound › Filter lists** and the
|
||||||
|
adjusted **Inbound › Log** (client, decision, engine and reason; *Allow /
|
||||||
|
Deny sender*) for inbound-antispam-panel, **Inbound › Quarantine** (list and
|
||||||
|
held message with Release / Discard) for inbound-quarantine, and **Server ›
|
||||||
|
Preflight** for preflight — all from existing components; 33 screens. Their
|
||||||
|
paths join § Routes; the quarantine and preflight plans link their mockups.
|
||||||
|
A mockup is the target look, not a decision to build: each item keeps its
|
||||||
|
status, and quarantine's open questions stay open.
|
||||||
|
- Panel redesign, two owner decisions recorded in
|
||||||
|
`docs/plans/panel-redesign.md`: **no compatibility** — the redesign is
|
||||||
|
`2.0.0`, old paths are removed without redirects, the SQLite migration chain
|
||||||
|
is replaced by one baseline and 2.0 starts from an empty data directory
|
||||||
|
(schema-squash ships as a step of this plan, without its upgrade gate; the
|
||||||
|
only live installation is the owner's); and
|
||||||
|
**inbound delegation** — a domain administrator manages assigned inbound
|
||||||
|
domains, granted separately from outbound (`user_inbound_domains`, a second
|
||||||
|
list on the user form, per-domain authorization under `/inbound/`). Mockups `users`, `user`,
|
||||||
|
`user-delete` redrawn for the two lists. Checklist `3/21`.
|
||||||
|
- Panel redesign, routes: the owner decided that paths follow the new names
|
||||||
|
and structure. `docs/plans/panel-redesign.md` § Routes maps every screen to
|
||||||
|
its new path (`/overview`, `/outbound/…`, `/inbound/…`, `/server/…`,
|
||||||
|
`/account`) with the rules that come along — one `<page>/fragment` name, the
|
||||||
|
application form as a single POST, 301s from old GET paths for one MINOR,
|
||||||
|
subtree `requireGlobal()` under security review, and
|
||||||
|
`TestRoutesFollowNavigation`. Mockups gained what the first pass missed: the
|
||||||
|
*Reload configuration* box (now on Server › Health), the `flash` component
|
||||||
|
and the `sp-help` link that replaces the help drawer. Checklist `1/17`.
|
||||||
|
- Panel redesign, design contract:
|
||||||
|
[docs/plans/panel-redesign.md](docs/plans/panel-redesign.md) rewritten for
|
||||||
|
the accepted direction — three layers (Bulma as shipped, adapters, own `sp-`
|
||||||
|
components), three page layouts, what an implementer must not do, the guard
|
||||||
|
tests that land before any template, a `design-first` CI step and
|
||||||
|
evidence-based acceptance. A
|
||||||
|
[components page](docs/assets/panel-redesign/panel/components.html) shows
|
||||||
|
every building block in every state; `panel/check.py` lints the mockups
|
||||||
|
against the contract and writes `panel/outlines.json`, the per-screen
|
||||||
|
component skeleton an implementation is compared with. Own modifier classes
|
||||||
|
renamed to carry the `sp-` prefix so the vocabulary is machine-checkable.
|
||||||
|
Agent rule `.cursor/rules/panel-design.mdc`; roadmap row updated (`0/15`).
|
||||||
|
- Panel redesign, stage 2: the accepted direction drawn on every screen —
|
||||||
|
29 pages under [docs/assets/panel-redesign/panel/](docs/assets/panel-redesign/index.html).
|
||||||
|
Direction C (Bulma) is the base, with A's postal signature (postmark verdict,
|
||||||
|
numbered mono section heads, the stamp's perforated edge) and B's icons.
|
||||||
|
Overview keeps verdicts only and the tables move to a new **Server › Health**
|
||||||
|
page; the own password becomes **Account**; queues are drawn as tables
|
||||||
|
(needs `postqueue -j`) and **Inbound** gets its own Log and Queue. Pages are
|
||||||
|
built from fragments by `panel/build.py`; the built HTML is committed.
|
||||||
|
Mockups only; nothing in `internal/web` changes.
|
||||||
|
- Panel redesign, stage 1: three directions drawn on the three key screens
|
||||||
|
(Overview, an outbound domain, sign-in) under
|
||||||
|
[docs/assets/panel-redesign/](docs/assets/panel-redesign/index.html) —
|
||||||
|
**A** Pico CSS ("Blank"), **B** Tabler ("Console"), **C** Bulma ("Counter"),
|
||||||
|
with a cover sheet comparing size, JavaScript, CSP fit and own CSS. All three
|
||||||
|
share a navigation grouped by mail direction (Overview · Outbound · Inbound ·
|
||||||
|
Server, with a symmetric Log and Queue on both sides), one block per DNS
|
||||||
|
record instead of separate "status" and "records to publish" cards, and
|
||||||
|
applications as a table. Mockups only; nothing in `internal/web` changes.
|
||||||
|
The brief behind them supersedes the "no component library" decision in
|
||||||
|
`docs/plans/panel-redesign.md`; that file is rewritten once a direction is
|
||||||
|
accepted.
|
||||||
- Roadmap item and givens for a panel visual redesign
|
- Roadmap item and givens for a panel visual redesign
|
||||||
([docs/plans/panel-redesign.md](docs/plans/panel-redesign.md)) — layout
|
([docs/plans/panel-redesign.md](docs/plans/panel-redesign.md)) — layout
|
||||||
grammar (`measure` / `fill` plus Web Awesome layout CSS), no JS component
|
grammar (`measure` / `fill` plus Web Awesome layout CSS), no JS component
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>example.org · Outbound domains · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2.1.1/css/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="sp-bar">
|
||||||
|
<div class="sp-wrap">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a class="sp-brand" href="status.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="sp-sections">
|
||||||
|
<li><a href="status.html">Overview</a></li>
|
||||||
|
<li><a href="domain.html" aria-current="true">Outbound</a></li>
|
||||||
|
<li><a href="#">Inbound</a></li>
|
||||||
|
<li><a href="#">Server</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="sp-user">
|
||||||
|
<li>
|
||||||
|
<details class="dropdown">
|
||||||
|
<summary>admin</summary>
|
||||||
|
<ul dir="rtl">
|
||||||
|
<li><a href="#">Account</a></li>
|
||||||
|
<li><a href="#">Help</a></li>
|
||||||
|
<li><a href="login.html">Sign out</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="sp-pages">
|
||||||
|
<div class="sp-wrap">
|
||||||
|
<a href="#" aria-current="true">Domains</a>
|
||||||
|
<a href="#">Log</a>
|
||||||
|
<a href="#">Queue</a>
|
||||||
|
<a href="#">DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<main class="sp-wrap">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div>
|
||||||
|
<p class="sp-kicker"><a href="#">Outbound domains</a> /</p>
|
||||||
|
<h1>example.org</h1>
|
||||||
|
</div>
|
||||||
|
<small>1 284 messages in 30 days · peak 96 msg/h · average 1.8 msg/h</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="sp-sheet">
|
||||||
|
<header><span class="sp-no">01</span><h2>Connection</h2><small>The same for every domain</small></header>
|
||||||
|
<div class="sp-body sp-cols">
|
||||||
|
<dl class="sp-kv">
|
||||||
|
<dt>Server</dt>
|
||||||
|
<dd><span class="sp-value">mail.example.org</span><button type="button" class="outline sp-copy">Copy</button></dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="sp-kv">
|
||||||
|
<dt>Ports</dt>
|
||||||
|
<dd><span class="sp-value">465 — SSL/TLS (implicit)<br>587 — STARTTLS (submission)</span></dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sp-sheet">
|
||||||
|
<header>
|
||||||
|
<span class="sp-no">02</span><h2>DNS records</h2>
|
||||||
|
<small>Checked 3 min ago</small>
|
||||||
|
<form method="post" action="#"><button type="submit" class="outline secondary sp-btn-sm">Re-check</button></form>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>DKIM</h3><span class="sp-tag ok">published</span><small>Not a secret · selector <code>sp2026</code></small></div>
|
||||||
|
<dl class="sp-kv">
|
||||||
|
<dt>Host</dt><dd><span class="sp-value">sp2026._domainkey.example.org</span><button type="button" class="outline sp-copy">Copy</button></dd>
|
||||||
|
<dt>Type</dt><dd><span class="sp-value plain">TXT</span></dd>
|
||||||
|
<dt>Value</dt><dd><span class="sp-value">v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3kqV0mJd8e2f1Yb7Qw5uT9sLr4nHc6pZaXo1vKe0yWm2gBt8RjN5dCq3hUf7lPs9aEi4oMz6xTb1kGv0nYw…IDAQAB</span><button type="button" class="outline sp-copy">Copy</button></dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>SPF</h3><span class="sp-tag fail">mismatch</span><small>Merge into the existing record — never publish a second one</small></div>
|
||||||
|
<dl class="sp-kv">
|
||||||
|
<dt>Host</dt><dd><span class="sp-value">example.org</span><button type="button" class="outline sp-copy">Copy</button></dd>
|
||||||
|
<dt>Type</dt><dd><span class="sp-value plain">TXT</span></dd>
|
||||||
|
<dt>Value</dt><dd><span class="sp-value">v=spf1 ip4:203.0.113.25 ~all</span><button type="button" class="outline sp-copy">Copy</button></dd>
|
||||||
|
<dt>In DNS now</dt><dd><span class="sp-value">v=spf1 include:_spf.google.com ~all</span></dd>
|
||||||
|
</dl>
|
||||||
|
<p class="sp-note fail">The published record does not authorize 203.0.113.25. Add <code>ip4:203.0.113.25</code> before <code>~all</code>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>DMARC</h3><span class="sp-tag ok">published</span><small>Report address from <a href="#">Settings</a></small></div>
|
||||||
|
<dl class="sp-kv">
|
||||||
|
<dt>Host</dt><dd><span class="sp-value">_dmarc.example.org</span><button type="button" class="outline sp-copy">Copy</button></dd>
|
||||||
|
<dt>Type</dt><dd><span class="sp-value plain">TXT</span></dd>
|
||||||
|
<dt>Value</dt><dd><span class="sp-value">v=DMARC1; p=none; rua=mailto:dmarc@mail.example.org</span><button type="button" class="outline sp-copy">Copy</button></dd>
|
||||||
|
</dl>
|
||||||
|
<p class="sp-note"><code>p=none</code> does not affect delivery. Tighten to <code>quarantine</code>, then <code>reject</code>, once the <a href="#">reports</a> look clean.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sp-sheet">
|
||||||
|
<header>
|
||||||
|
<span class="sp-no">03</span><h2>Applications</h2>
|
||||||
|
<small>SASL logins that may send as this domain</small>
|
||||||
|
<a href="#" role="button" class="sp-btn-sm">Add application</a>
|
||||||
|
</header>
|
||||||
|
<table class="sp-apps">
|
||||||
|
<thead><tr><th>Login</th><th>May send as</th><th>30 days</th><th>Limits</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="sp-login">prod-server</td>
|
||||||
|
<td>*@example.org</td>
|
||||||
|
<td>1 102 msg · peak 96/h</td>
|
||||||
|
<td><span class="sp-tag ok">200 / h</span></td>
|
||||||
|
<td><a class="sp-link-btn" href="#">Edit</a><a class="sp-link-btn" href="#">New password</a><a class="sp-link-btn danger" href="#">Delete</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sp-login">alerts</td>
|
||||||
|
<td>alerts@example.org, noc@example.org</td>
|
||||||
|
<td>182 msg · peak 14/h</td>
|
||||||
|
<td><span class="sp-tag">domain</span> <span class="sp-tag ok">2 IPs</span></td>
|
||||||
|
<td><a class="sp-link-btn" href="#">Edit</a><a class="sp-link-btn" href="#">New password</a><a class="sp-link-btn danger" href="#">Delete</a></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sp-sheet sp-ledger">
|
||||||
|
<header><span class="sp-no">04</span><h2>Domain settings</h2><small>Rarely changed</small></header>
|
||||||
|
<details>
|
||||||
|
<summary><strong>DMARC reports</strong><span class="sp-muted">Same as Settings — dmarc@mail.example.org</span><span class="sp-tag">default</span></summary>
|
||||||
|
<div class="sp-detail"><p class="sp-muted">The form opens here.</p></div>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary><strong>Rate limit</strong><span class="sp-muted">Auto ×3 — 288 messages / hour, recalculated yesterday</span><span class="sp-tag ok">active</span></summary>
|
||||||
|
<div class="sp-detail"><p class="sp-muted">The form opens here.</p></div>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary><strong>Export</strong><span class="sp-muted">DKIM key and applications as one file, optionally encrypted</span><span></span></summary>
|
||||||
|
<div class="sp-detail"><p class="sp-muted">The form opens here.</p></div>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary><strong>Delete domain</strong><span class="sp-muted">Removes the DKIM key and both applications</span><span></span></summary>
|
||||||
|
<div class="sp-detail"><p class="sp-muted">The confirmation opens here.</p></div>
|
||||||
|
</details>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="sp-foot">
|
||||||
|
SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Sign in · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2.1.1/css/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="sp-signin">
|
||||||
|
<div class="sp-signin-card">
|
||||||
|
<img src="../../selfpost-stamp.svg" alt="SelfPost">
|
||||||
|
<section class="sp-sheet">
|
||||||
|
<header><span class="sp-no">→</span><h2>Sign in</h2></header>
|
||||||
|
<div class="sp-body">
|
||||||
|
<form method="post" action="status.html">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input id="username" name="username" autocomplete="username" required>
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input id="password" name="password" type="password" autocomplete="current-password" required>
|
||||||
|
<button type="submit">Sign in</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<p class="sp-tagline">YOUR SMTP. YOUR RULES.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-wrap sp-foot">
|
||||||
|
© 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Overview · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2.1.1/css/pico.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="sp-bar">
|
||||||
|
<div class="sp-wrap">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a class="sp-brand" href="status.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="sp-sections">
|
||||||
|
<li><a href="status.html" aria-current="page">Overview</a></li>
|
||||||
|
<li><a href="domain.html">Outbound</a></li>
|
||||||
|
<li><a href="#">Inbound</a></li>
|
||||||
|
<li><a href="#">Server</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="sp-user">
|
||||||
|
<li>
|
||||||
|
<details class="dropdown">
|
||||||
|
<summary>admin</summary>
|
||||||
|
<ul dir="rtl">
|
||||||
|
<li><a href="#">Account</a></li>
|
||||||
|
<li><a href="#">Help</a></li>
|
||||||
|
<li><a href="login.html">Sign out</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="sp-wrap">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div>
|
||||||
|
<p class="sp-kicker">mail.example.org</p>
|
||||||
|
<h1>Overview</h1>
|
||||||
|
</div>
|
||||||
|
<small>Updated 14:02:31 UTC · refreshes every 10 s</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="sp-sheet sp-verdict">
|
||||||
|
<div class="sp-postmark warn"><span>SELFPOST</span><b>WARN</b><span>1 OF 6 CHECKS</span></div>
|
||||||
|
<div>
|
||||||
|
<h2>Mail is flowing. One thing needs attention.</h2>
|
||||||
|
<p>The TLS certificate expires in 12 days and has not renewed yet. Everything else is healthy.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sp-sheet sp-ledger">
|
||||||
|
<header><span class="sp-no">01</span><h2>Checks</h2><small>Open a row for its detail</small></header>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Processes</strong><span class="sp-muted">5 of 5 running</span><span class="sp-tag ok">ok</span></summary>
|
||||||
|
<div class="sp-detail">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Program</th><th>State</th><th>Detail</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>postfix</td><td><span class="sp-tag ok">running</span></td><td class="sp-muted">pid 41, uptime 18 days</td></tr>
|
||||||
|
<tr><td>opendkim</td><td><span class="sp-tag ok">running</span></td><td class="sp-muted">pid 39, uptime 18 days</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary><strong>TLS certificate</strong><span class="sp-muted">Expires in 12 days — renewal has not run</span><span class="sp-tag warn">warn</span></summary>
|
||||||
|
<div class="sp-detail">
|
||||||
|
<dl class="sp-kv">
|
||||||
|
<dt>Expires</dt><dd><span class="sp-value">2026-10-03 09:14 UTC</span></dd>
|
||||||
|
<dt>Names</dt><dd><span class="sp-value">mail.example.org</span></dd>
|
||||||
|
</dl>
|
||||||
|
<p class="sp-note">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</a>.</p>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Machine</strong><span class="sp-muted">CPU 12 % · memory 41 % · ↓ 18 kB/s ↑ 44 kB/s</span><span class="sp-tag ok">ok</span></summary>
|
||||||
|
<div class="sp-detail">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Resource</th><th>Usage</th><th>Detail</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>CPU</td><td><meter value="12" min="0" max="100" low="70" high="90" optimum="10"></meter>12 %</td><td class="sp-muted">2 cores, load 0.21</td></tr>
|
||||||
|
<tr><td>Memory</td><td><meter value="41" min="0" max="100" low="70" high="90" optimum="10"></meter>41 %</td><td class="sp-muted">812 MB of 1.9 GB</td></tr>
|
||||||
|
<tr><td>Network</td><td>↓ 18 kB/s · ↑ 44 kB/s</td><td class="sp-muted">eth0: 2.1 GB in, 6.4 GB out</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Queues</strong><span class="sp-muted">Outbound empty · inbound empty</span><span class="sp-tag ok">ok</span></summary>
|
||||||
|
<div class="sp-detail"><p>No messages are waiting. Open the <a href="#">outbound queue</a> or the <a href="#">inbound queue</a>.</p></div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Milter sockets</strong><span class="sp-muted">2 of 2 answering</span><span class="sp-tag ok">ok</span></summary>
|
||||||
|
<div class="sp-detail">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Milter</th><th>State</th><th>Detail</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>opendkim</td><td><span class="sp-tag ok">ok</span></td><td class="sp-muted">inet:127.0.0.1:8891</td></tr>
|
||||||
|
<tr><td>journal-milter</td><td><span class="sp-tag ok">ok</span></td><td class="sp-muted">unix:/run/selfpost/journal.sock</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>Hostname and reverse DNS</strong><span class="sp-muted">mail.example.org ⇄ 203.0.113.25</span><span class="sp-tag ok">ok</span></summary>
|
||||||
|
<div class="sp-detail">
|
||||||
|
<dl class="sp-kv">
|
||||||
|
<dt>Hostname</dt><dd><span class="sp-value">mail.example.org</span></dd>
|
||||||
|
<dt>Lookup</dt><dd><span class="sp-value">mail.example.org → 203.0.113.25<br>203.0.113.25 → mail.example.org</span></dd>
|
||||||
|
</dl>
|
||||||
|
<form method="post" action="#"><button type="submit" class="outline secondary sp-btn-sm">Re-check DNS</button></form>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="sp-foot">
|
||||||
|
SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
/* Direction A — "Blank" (the postal form). Pico CSS v2 carries every generic
|
||||||
|
control (type, buttons, inputs, tables, details, dropdown); this file maps
|
||||||
|
the brand onto Pico's variables and adds only what is SelfPost's own:
|
||||||
|
the two-row header, the sheet, numbered form sections, the postmark and
|
||||||
|
the DNS record block. */
|
||||||
|
|
||||||
|
/* :root[...] rather than [data-theme=light] alone: Pico declares its light
|
||||||
|
palette on :root:not([data-theme=dark]), which a bare attribute selector
|
||||||
|
loses to. */
|
||||||
|
:root[data-theme=light] {
|
||||||
|
--pico-font-family-sans-serif: var(--sp-sans);
|
||||||
|
--pico-font-family-monospace: var(--sp-mono);
|
||||||
|
--pico-font-family: var(--sp-sans);
|
||||||
|
--pico-border-radius: 2px;
|
||||||
|
--pico-spacing: .9rem;
|
||||||
|
--pico-typography-spacing-vertical: .9rem;
|
||||||
|
--pico-form-element-spacing-vertical: .5rem;
|
||||||
|
--pico-form-element-spacing-horizontal: .75rem;
|
||||||
|
|
||||||
|
--pico-background-color: var(--sp-paper);
|
||||||
|
--pico-color: var(--sp-ink);
|
||||||
|
--pico-muted-color: var(--sp-muted);
|
||||||
|
--pico-muted-border-color: var(--sp-rule);
|
||||||
|
--pico-h1-color: var(--sp-ink); --pico-h2-color: var(--sp-ink);
|
||||||
|
--pico-h3-color: var(--sp-ink); --pico-h4-color: var(--sp-ink);
|
||||||
|
|
||||||
|
--pico-primary: var(--sp-brick);
|
||||||
|
--pico-primary-background: var(--sp-brick);
|
||||||
|
--pico-primary-border: var(--sp-brick);
|
||||||
|
--pico-primary-underline: rgb(122 59 46 / .35);
|
||||||
|
--pico-primary-hover: var(--sp-brick-dark);
|
||||||
|
--pico-primary-hover-background: var(--sp-brick-dark);
|
||||||
|
--pico-primary-hover-border: var(--sp-brick-dark);
|
||||||
|
--pico-primary-hover-underline: var(--sp-brick-dark);
|
||||||
|
--pico-primary-focus: rgb(122 59 46 / .25);
|
||||||
|
--pico-primary-inverse: #fff;
|
||||||
|
|
||||||
|
--pico-secondary: var(--sp-ink-2);
|
||||||
|
--pico-secondary-background: var(--sp-ink-2);
|
||||||
|
--pico-secondary-border: var(--sp-ink-2);
|
||||||
|
--pico-secondary-hover: var(--sp-ink);
|
||||||
|
--pico-secondary-hover-background: var(--sp-ink);
|
||||||
|
--pico-secondary-hover-border: var(--sp-ink);
|
||||||
|
--pico-secondary-focus: rgb(18 22 28 / .2);
|
||||||
|
--pico-secondary-inverse: #fff;
|
||||||
|
|
||||||
|
--pico-form-element-background-color: #fff;
|
||||||
|
--pico-form-element-border-color: var(--sp-rule-strong);
|
||||||
|
--pico-form-element-active-border-color: var(--sp-brick);
|
||||||
|
--pico-form-element-focus-color: rgb(122 59 46 / .25);
|
||||||
|
--pico-table-border-color: var(--sp-rule);
|
||||||
|
--pico-code-background-color: var(--sp-well);
|
||||||
|
--pico-code-color: var(--sp-ink);
|
||||||
|
--pico-dropdown-background-color: #fff;
|
||||||
|
--pico-dropdown-border-color: var(--sp-rule);
|
||||||
|
--pico-dropdown-hover-background-color: var(--sp-brick-tint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pico grows the root font with the viewport (up to 131%). An ops panel wants
|
||||||
|
one size everywhere. */
|
||||||
|
:root { font-size: 15px; }
|
||||||
|
body { min-height: 100vh; display: flex; flex-direction: column; }
|
||||||
|
h1 { font-size: 1.7rem; font-weight: 600; margin: 0; }
|
||||||
|
small, .sp-muted { color: var(--sp-muted); }
|
||||||
|
code, .sp-mono { font-family: var(--sp-mono); font-size: .88em; }
|
||||||
|
|
||||||
|
/* ---- header: sections on the first row, the section's pages on the second */
|
||||||
|
.sp-wrap { width: 100%; max-width: 72rem; margin-inline: auto; padding-inline: 1.5rem; }
|
||||||
|
.sp-bar { background: var(--sp-sheet); border-bottom: 1px solid var(--sp-rule); }
|
||||||
|
.sp-bar nav { align-items: center; }
|
||||||
|
.sp-bar nav li { padding-block: .45rem; }
|
||||||
|
.sp-brand img { height: 46px; width: auto; display: block; }
|
||||||
|
.sp-sections a {
|
||||||
|
font-family: var(--sp-mono); font-size: .8rem; font-weight: 600;
|
||||||
|
letter-spacing: .09em; text-transform: uppercase; text-decoration: none;
|
||||||
|
color: var(--sp-muted); padding: .5rem .8rem; margin: 0; border-radius: 0;
|
||||||
|
}
|
||||||
|
.sp-sections a:hover { color: var(--sp-ink); }
|
||||||
|
.sp-sections a[aria-current] { color: var(--sp-brick); box-shadow: inset 0 -3px 0 var(--sp-brick); }
|
||||||
|
.sp-user summary { font-size: .9rem; }
|
||||||
|
.sp-pages { background: var(--sp-paper); border-bottom: 1px solid var(--sp-rule); }
|
||||||
|
.sp-pages .sp-wrap { display: flex; gap: 1.6rem; }
|
||||||
|
.sp-pages a { color: var(--sp-muted); text-decoration: none; padding: .6rem 0; font-size: .95rem; }
|
||||||
|
.sp-pages a:hover { color: var(--sp-ink); }
|
||||||
|
.sp-pages a[aria-current] { color: var(--sp-ink); font-weight: 600; box-shadow: inset 0 -2px 0 var(--sp-ink); }
|
||||||
|
|
||||||
|
/* ---- the sheet: one column, one width, every page */
|
||||||
|
main.sp-wrap { flex: 1; padding-block: 1.8rem 2.5rem; }
|
||||||
|
.sp-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.4rem; }
|
||||||
|
.sp-head .sp-kicker { font-family: var(--sp-mono); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase; color: var(--sp-muted); margin: 0 0 .15rem; }
|
||||||
|
.sp-head .sp-kicker a { color: inherit; }
|
||||||
|
.sp-head-actions { display: flex; gap: .5rem; align-items: center; }
|
||||||
|
.sp-head-actions > * { margin: 0; }
|
||||||
|
.sp-sheet { background: var(--sp-sheet); border: 1px solid var(--sp-rule); margin-bottom: 1.4rem; }
|
||||||
|
.sp-sheet > header {
|
||||||
|
display: flex; align-items: baseline; gap: .8rem; padding: .7rem 1.2rem;
|
||||||
|
border-bottom: 1px solid var(--sp-rule);
|
||||||
|
}
|
||||||
|
.sp-sheet > header .sp-no { font-family: var(--sp-mono); font-size: .75rem; color: var(--sp-brick); font-weight: 600; }
|
||||||
|
.sp-sheet > header h2 {
|
||||||
|
font-family: var(--sp-mono); font-size: .8rem; font-weight: 600; letter-spacing: .1em;
|
||||||
|
text-transform: uppercase; margin: 0; flex: 1;
|
||||||
|
}
|
||||||
|
.sp-sheet > header small { font-size: .85rem; }
|
||||||
|
.sp-sheet > .sp-body { padding: 1.1rem 1.2rem; }
|
||||||
|
.sp-sheet > .sp-body > :last-child { margin-bottom: 0; }
|
||||||
|
.sp-foot { color: var(--sp-muted); font-size: .8rem; border-top: 1px solid var(--sp-rule); padding-block: 1rem; }
|
||||||
|
.sp-foot a { color: inherit; }
|
||||||
|
|
||||||
|
/* ---- status tag and the postmark */
|
||||||
|
.sp-tag {
|
||||||
|
display: inline-block; font-family: var(--sp-mono); font-size: .72rem; font-weight: 600;
|
||||||
|
letter-spacing: .08em; text-transform: uppercase; padding: .1rem .5rem;
|
||||||
|
border: 1px solid var(--sp-idle-rule); background: var(--sp-idle-bg); color: var(--sp-idle);
|
||||||
|
border-radius: 2px; white-space: nowrap; text-decoration: none;
|
||||||
|
}
|
||||||
|
.sp-tag.ok { color: var(--sp-ok); background: var(--sp-ok-bg); border-color: var(--sp-ok-rule); }
|
||||||
|
.sp-tag.warn { color: var(--sp-warn); background: var(--sp-warn-bg); border-color: var(--sp-warn-rule); }
|
||||||
|
.sp-tag.fail { color: var(--sp-fail); background: var(--sp-fail-bg); border-color: var(--sp-fail-rule); }
|
||||||
|
|
||||||
|
.sp-verdict { display: flex; align-items: center; gap: 1.5rem; padding: 1.3rem 1.4rem; }
|
||||||
|
.sp-verdict h2 { margin: 0 0 .2rem; font-size: 1.35rem; font-weight: 600; }
|
||||||
|
.sp-verdict p { margin: 0; color: var(--sp-muted); }
|
||||||
|
.sp-postmark {
|
||||||
|
flex: none; width: 5.6rem; height: 5.6rem; border-radius: 50%;
|
||||||
|
border: 2px solid currentColor; outline: 1px solid currentColor; outline-offset: 3px;
|
||||||
|
display: grid; place-content: center; text-align: center; transform: rotate(-9deg);
|
||||||
|
font-family: var(--sp-mono); font-weight: 600; letter-spacing: .08em; line-height: 1.15;
|
||||||
|
color: var(--sp-ok);
|
||||||
|
}
|
||||||
|
.sp-postmark.warn { color: var(--sp-warn); }
|
||||||
|
.sp-postmark.fail { color: var(--sp-fail); }
|
||||||
|
.sp-postmark b { font-size: 1.25rem; display: block; }
|
||||||
|
.sp-postmark span { font-size: .55rem; display: block; }
|
||||||
|
|
||||||
|
/* ---- ledger: one row per check, the row opens into its detail */
|
||||||
|
.sp-ledger details { margin: 0; padding: 0; border-bottom: 1px solid var(--sp-rule); }
|
||||||
|
.sp-ledger details:last-child { border-bottom: 0; }
|
||||||
|
.sp-ledger summary {
|
||||||
|
display: grid; grid-template-columns: 13rem 1fr auto 1.2rem; gap: 1rem; align-items: center;
|
||||||
|
padding: .75rem 1.2rem; margin: 0; color: var(--sp-ink);
|
||||||
|
}
|
||||||
|
.sp-ledger summary::after { grid-column: 4; margin: 0; float: none; }
|
||||||
|
.sp-ledger summary:hover { background: var(--sp-paper); }
|
||||||
|
.sp-ledger summary strong { font-weight: 600; }
|
||||||
|
.sp-ledger summary span.sp-muted { font-size: .93rem; }
|
||||||
|
.sp-ledger .sp-detail { padding: .2rem 1.2rem 1.1rem 15.2rem; }
|
||||||
|
.sp-ledger .sp-detail table { margin: 0; font-size: .93rem; }
|
||||||
|
.sp-ledger .sp-detail > :last-child { margin-bottom: 0; }
|
||||||
|
.sp-ledger meter { width: 9rem; height: .7rem; vertical-align: middle; margin-right: .5rem; }
|
||||||
|
|
||||||
|
/* ---- label / value / copy: the DNS record block and connection settings */
|
||||||
|
.sp-record { padding: 1.1rem 1.2rem; border-bottom: 1px solid var(--sp-rule); }
|
||||||
|
.sp-record:last-child { border-bottom: 0; }
|
||||||
|
.sp-record-head { display: flex; align-items: center; gap: .7rem; margin-bottom: .7rem; }
|
||||||
|
.sp-record-head h3 { margin: 0; font-size: 1.05rem; font-weight: 600; }
|
||||||
|
.sp-record-head small { margin-left: auto; }
|
||||||
|
.sp-kv { display: grid; grid-template-columns: 6.5rem 1fr; gap: .45rem 1rem; align-items: start; margin: 0; }
|
||||||
|
.sp-kv dt {
|
||||||
|
font-family: var(--sp-mono); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
|
||||||
|
color: var(--sp-muted); padding-top: .5rem; margin: 0;
|
||||||
|
}
|
||||||
|
.sp-kv dd { margin: 0; display: flex; gap: .4rem; align-items: stretch; min-width: 0; }
|
||||||
|
.sp-value {
|
||||||
|
flex: 1; min-width: 0; font-family: var(--sp-mono); font-size: .86rem; line-height: 1.5;
|
||||||
|
background: var(--sp-well); border: 1px solid var(--sp-rule); padding: .4rem .65rem;
|
||||||
|
overflow-wrap: anywhere; border-radius: 2px;
|
||||||
|
}
|
||||||
|
.sp-value.plain { background: none; border-color: transparent; padding-left: 0; font-family: var(--sp-sans); font-size: .95rem; }
|
||||||
|
.sp-copy {
|
||||||
|
flex: none; align-self: flex-start; height: 2.15rem; margin: 0; padding: 0 .7rem; font-size: .8rem;
|
||||||
|
--pico-background-color: #fff; --pico-border-color: var(--sp-rule-strong); --pico-color: var(--sp-ink-2);
|
||||||
|
}
|
||||||
|
.sp-copy:hover { --pico-background-color: var(--sp-paper); --pico-border-color: var(--sp-ink-2); --pico-color: var(--sp-ink); }
|
||||||
|
.sp-note { margin: .7rem 0 0 7.5rem; font-size: .9rem; color: var(--sp-muted); }
|
||||||
|
.sp-note.fail { color: var(--sp-fail); }
|
||||||
|
.sp-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 0 2.4rem; }
|
||||||
|
|
||||||
|
/* ---- applications */
|
||||||
|
.sp-apps { margin: 0; font-size: .95rem; }
|
||||||
|
.sp-apps td, .sp-apps th { padding-inline: 1.2rem .4rem; vertical-align: middle; }
|
||||||
|
.sp-apps th { font-family: var(--sp-mono); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); font-weight: 400; }
|
||||||
|
.sp-apps td:last-child { text-align: right; padding-right: 1.2rem; white-space: nowrap; }
|
||||||
|
.sp-apps .sp-login { font-family: var(--sp-mono); font-weight: 600; }
|
||||||
|
.sp-link-btn { font-size: .9rem; margin-left: .9rem; }
|
||||||
|
.sp-link-btn.danger { color: var(--sp-fail); --pico-underline: rgb(180 35 24 / .35); text-decoration-color: rgb(180 35 24 / .35); }
|
||||||
|
.sp-btn-sm { padding: .35rem .8rem; font-size: .9rem; margin: 0; width: auto; }
|
||||||
|
|
||||||
|
/* ---- sign-in */
|
||||||
|
.sp-signin { flex: 1; display: grid; place-content: center; padding: 2rem 1.5rem; }
|
||||||
|
.sp-signin-card { width: 23rem; max-width: 100%; }
|
||||||
|
.sp-signin-card img { display: block; width: 15rem; height: auto; margin: 0 auto 1.6rem; }
|
||||||
|
.sp-signin-card .sp-body { padding: 1.4rem 1.4rem 1.2rem; }
|
||||||
|
.sp-signin-card button { margin-bottom: 0; }
|
||||||
|
.sp-tagline { text-align: center; font-family: var(--sp-mono); font-size: .75rem; letter-spacing: .16em; color: var(--sp-muted); margin-top: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 48rem) {
|
||||||
|
.sp-ledger summary { grid-template-columns: 1fr auto 1.2rem; }
|
||||||
|
.sp-ledger summary span.sp-muted { grid-column: 1 / -1; grid-row: 2; }
|
||||||
|
.sp-ledger summary::after { grid-column: 3; }
|
||||||
|
.sp-ledger .sp-detail { padding-left: 1.2rem; }
|
||||||
|
.sp-kv { grid-template-columns: 1fr; gap: .15rem; }
|
||||||
|
.sp-kv dt { padding-top: .5rem; }
|
||||||
|
.sp-note { margin-left: 0; }
|
||||||
|
.sp-pages .sp-wrap { overflow-x: auto; white-space: nowrap; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-bs-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>example.org · Outbound domains · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.5.1/dist/css/tabler.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<aside class="navbar navbar-vertical navbar-expand-lg" data-bs-theme="dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="status.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a>
|
||||||
|
<div class="navbar-collapse collapse show">
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item"><a class="nav-link" href="status.html"><span class="nav-link-icon"><i class="ti ti-activity-heartbeat"></i></span><span class="nav-link-title">Overview</span></a></li>
|
||||||
|
|
||||||
|
<li class="sp-nav-label">Outbound</li>
|
||||||
|
<li class="nav-item active"><a class="nav-link" href="domain.html"><span class="nav-link-icon"><i class="ti ti-world-upload"></i></span><span class="nav-link-title">Domains</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-list-details"></i></span><span class="nav-link-title">Log</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-stack-2"></i></span><span class="nav-link-title">Queue</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-report-analytics"></i></span><span class="nav-link-title">DMARC reports</span></a></li>
|
||||||
|
|
||||||
|
<li class="sp-nav-label">Inbound</li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-world-download"></i></span><span class="nav-link-title">Domains</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-list-details"></i></span><span class="nav-link-title">Log</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-stack-2"></i></span><span class="nav-link-title">Queue</span></a></li>
|
||||||
|
|
||||||
|
<li class="sp-nav-label">Server</li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-file-text"></i></span><span class="nav-link-title">System log</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-archive"></i></span><span class="nav-link-title">Backup</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-users"></i></span><span class="nav-link-title">Users</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-settings"></i></span><span class="nav-link-title">Settings</span></a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav sp-nav-foot">
|
||||||
|
<li class="sp-nav-user">Signed in as admin</li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-user-circle"></i></span><span class="nav-link-title">Account</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-help"></i></span><span class="nav-link-title">Help</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="login.html"><span class="nav-link-icon"><i class="ti ti-logout"></i></span><span class="nav-link-title">Sign out</span></a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="container-xl">
|
||||||
|
<div class="row g-2 align-items-center">
|
||||||
|
<div class="col">
|
||||||
|
<div class="page-pretitle"><a href="#" class="text-reset">Outbound domains</a> /</div>
|
||||||
|
<h2 class="page-title">example.org <span class="badge bg-red-lt ms-2">DNS: 1 mismatch</span></h2>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="btn-list">
|
||||||
|
<form method="post" action="#"><button type="submit" class="btn"><i class="ti ti-refresh me-1"></i>Re-check DNS</button></form>
|
||||||
|
<a href="#" class="btn btn-primary"><i class="ti ti-plus me-1"></i>Add application</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page-body">
|
||||||
|
<div class="container-xl">
|
||||||
|
<div class="row row-cards">
|
||||||
|
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><h3 class="card-title">DNS records</h3><div class="card-actions text-secondary small">Checked 3 min ago</div></div>
|
||||||
|
|
||||||
|
<div class="card-body sp-record">
|
||||||
|
<div class="d-flex align-items-center mb-3">
|
||||||
|
<h4 class="m-0 me-2">DKIM</h4><span class="badge bg-green-lt">published</span>
|
||||||
|
<span class="ms-auto text-secondary small">Not a secret · selector <code>sp2026</code></span>
|
||||||
|
</div>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-9">
|
||||||
|
<label class="form-label">Host / name</label>
|
||||||
|
<div class="input-group"><input class="form-control" readonly value="sp2026._domainkey.example.org"><button class="btn" type="button"><i class="ti ti-copy me-1"></i>Copy</button></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label">Type</label>
|
||||||
|
<input class="form-control" readonly value="TXT">
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">Value to publish</label>
|
||||||
|
<div class="input-group"><textarea class="form-control" rows="3" readonly>v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3kqV0mJd8e2f1Yb7Qw5uT9sLr4nHc6pZaXo1vKe0yWm2gBt8RjN5dCq3hUf7lPs9aEi4oMz6xTb1kGv0nYw…IDAQAB</textarea><button class="btn" type="button"><i class="ti ti-copy me-1"></i>Copy</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body sp-record">
|
||||||
|
<div class="d-flex align-items-center mb-3">
|
||||||
|
<h4 class="m-0 me-2">SPF</h4><span class="badge bg-red-lt">mismatch</span>
|
||||||
|
<span class="ms-auto text-secondary small">Merge into the existing record — never publish a second one</span>
|
||||||
|
</div>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-9">
|
||||||
|
<label class="form-label">Host / name</label>
|
||||||
|
<div class="input-group"><input class="form-control" readonly value="example.org"><button class="btn" type="button"><i class="ti ti-copy me-1"></i>Copy</button></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label">Type</label>
|
||||||
|
<input class="form-control" readonly value="TXT">
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">Value to publish</label>
|
||||||
|
<div class="input-group"><input class="form-control" readonly value="v=spf1 ip4:203.0.113.25 ~all"><button class="btn" type="button"><i class="ti ti-copy me-1"></i>Copy</button></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">In DNS now</label>
|
||||||
|
<input class="form-control" readonly value="v=spf1 include:_spf.google.com ~all">
|
||||||
|
<div class="text-danger small mt-2">The published record does not authorize 203.0.113.25. Add <code>ip4:203.0.113.25</code> before <code>~all</code>.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body sp-record">
|
||||||
|
<div class="d-flex align-items-center mb-3">
|
||||||
|
<h4 class="m-0 me-2">DMARC</h4><span class="badge bg-green-lt">published</span>
|
||||||
|
<span class="ms-auto text-secondary small">Report address from <a href="#">Settings</a></span>
|
||||||
|
</div>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-9">
|
||||||
|
<label class="form-label">Host / name</label>
|
||||||
|
<div class="input-group"><input class="form-control" readonly value="_dmarc.example.org"><button class="btn" type="button"><i class="ti ti-copy me-1"></i>Copy</button></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label">Type</label>
|
||||||
|
<input class="form-control" readonly value="TXT">
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">Value to publish</label>
|
||||||
|
<div class="input-group"><input class="form-control" readonly value="v=DMARC1; p=none; rua=mailto:dmarc@mail.example.org"><button class="btn" type="button"><i class="ti ti-copy me-1"></i>Copy</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header"><h3 class="card-title">Connection</h3></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="datagrid">
|
||||||
|
<div class="datagrid-item"><div class="datagrid-title">Server</div><div class="datagrid-content font-monospace">mail.example.org</div></div>
|
||||||
|
<div class="datagrid-item"><div class="datagrid-title">Ports</div><div class="datagrid-content font-monospace">465 SSL/TLS<br>587 STARTTLS</div></div>
|
||||||
|
</div>
|
||||||
|
<p class="text-secondary small mt-3 mb-0">The same for every domain. Authenticate with an application login.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header"><h3 class="card-title">Sent, 30 days</h3></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="datagrid">
|
||||||
|
<div class="datagrid-item"><div class="datagrid-title">Messages</div><div class="datagrid-content h2 m-0">1 284</div></div>
|
||||||
|
<div class="datagrid-item"><div class="datagrid-title">Peak</div><div class="datagrid-content h2 m-0">96 <span class="text-secondary fs-5">/h</span></div></div>
|
||||||
|
<div class="datagrid-item"><div class="datagrid-title">Average</div><div class="datagrid-content h2 m-0">1.8 <span class="text-secondary fs-5">/h</span></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><h3 class="card-title">Domain settings</h3></div>
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
<a href="#" class="list-group-item list-group-item-action d-flex align-items-center"><div><div>DMARC reports</div><div class="text-secondary small">Same as Settings</div></div><i class="ti ti-chevron-right ms-auto text-secondary"></i></a>
|
||||||
|
<a href="#" class="list-group-item list-group-item-action d-flex align-items-center"><div><div>Rate limit <span class="badge bg-green-lt ms-1">active</span></div><div class="text-secondary small">Auto ×3 — 288 messages / hour</div></div><i class="ti ti-chevron-right ms-auto text-secondary"></i></a>
|
||||||
|
<a href="#" class="list-group-item list-group-item-action d-flex align-items-center"><div><div>Export</div><div class="text-secondary small">Key and applications as one file</div></div><i class="ti ti-chevron-right ms-auto text-secondary"></i></a>
|
||||||
|
<a href="#" class="list-group-item list-group-item-action d-flex align-items-center text-danger"><div>Delete domain</div><i class="ti ti-chevron-right ms-auto"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><h3 class="card-title">Applications</h3><div class="card-actions text-secondary small">SASL logins that may send as this domain</div></div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-vcenter card-table">
|
||||||
|
<thead><tr><th>Login</th><th>May send as</th><th>30 days</th><th>Limits</th><th class="w-1"></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="font-monospace fw-semibold">prod-server</td><td>*@example.org</td><td class="text-secondary">1 102 msg · peak 96/h</td>
|
||||||
|
<td><span class="badge bg-green-lt">200 / h</span></td>
|
||||||
|
<td><div class="btn-list flex-nowrap"><a href="#" class="btn btn-sm">Edit</a><a href="#" class="btn btn-sm">New password</a><a href="#" class="btn btn-sm btn-ghost-danger">Delete</a></div></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="font-monospace fw-semibold">alerts</td><td>alerts@example.org, noc@example.org</td><td class="text-secondary">182 msg · peak 14/h</td>
|
||||||
|
<td><span class="badge bg-secondary-lt">domain</span> <span class="badge bg-green-lt">2 IPs</span></td>
|
||||||
|
<td><div class="btn-list flex-nowrap"><a href="#" class="btn btn-sm">Edit</a><a href="#" class="btn btn-sm">New password</a><a href="#" class="btn btn-sm btn-ghost-danger">Delete</a></div></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer footer-transparent">
|
||||||
|
<div class="container-xl sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-bs-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Sign in · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.5.1/dist/css/tabler.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body class="d-flex flex-column">
|
||||||
|
<div class="page page-center">
|
||||||
|
<div class="container container-tight py-4">
|
||||||
|
<div class="text-center mb-4"><img class="sp-signin-mark" src="../../selfpost-stamp.svg" alt="SelfPost"></div>
|
||||||
|
<div class="card card-md">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h2 text-center mb-4">Sign in</h2>
|
||||||
|
<form method="post" action="status.html">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="username">Username</label>
|
||||||
|
<input class="form-control" id="username" name="username" autocomplete="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label" for="password">Password</label>
|
||||||
|
<input class="form-control" id="password" name="password" type="password" autocomplete="current-password" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-footer"><button type="submit" class="btn btn-primary w-100">Sign in</button></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center sp-tagline mt-4">YOUR SMTP. YOUR RULES.</div>
|
||||||
|
<div class="text-center sp-foot mt-3">© 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-bs-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Overview · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.5.1/dist/css/tabler.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<aside class="navbar navbar-vertical navbar-expand-lg" data-bs-theme="dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="status.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a>
|
||||||
|
<div class="navbar-collapse collapse show">
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item active"><a class="nav-link" href="status.html"><span class="nav-link-icon"><i class="ti ti-activity-heartbeat"></i></span><span class="nav-link-title">Overview</span></a></li>
|
||||||
|
|
||||||
|
<li class="sp-nav-label">Outbound</li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="domain.html"><span class="nav-link-icon"><i class="ti ti-world-upload"></i></span><span class="nav-link-title">Domains</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-list-details"></i></span><span class="nav-link-title">Log</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-stack-2"></i></span><span class="nav-link-title">Queue</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-report-analytics"></i></span><span class="nav-link-title">DMARC reports</span></a></li>
|
||||||
|
|
||||||
|
<li class="sp-nav-label">Inbound</li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-world-download"></i></span><span class="nav-link-title">Domains</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-list-details"></i></span><span class="nav-link-title">Log</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-stack-2"></i></span><span class="nav-link-title">Queue</span></a></li>
|
||||||
|
|
||||||
|
<li class="sp-nav-label">Server</li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-file-text"></i></span><span class="nav-link-title">System log</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-archive"></i></span><span class="nav-link-title">Backup</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-users"></i></span><span class="nav-link-title">Users</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-settings"></i></span><span class="nav-link-title">Settings</span></a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav sp-nav-foot">
|
||||||
|
<li class="sp-nav-user">Signed in as admin</li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-user-circle"></i></span><span class="nav-link-title">Account</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#"><span class="nav-link-icon"><i class="ti ti-help"></i></span><span class="nav-link-title">Help</span></a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="login.html"><span class="nav-link-icon"><i class="ti ti-logout"></i></span><span class="nav-link-title">Sign out</span></a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="container-xl">
|
||||||
|
<div class="row g-2 align-items-center">
|
||||||
|
<div class="col">
|
||||||
|
<div class="page-pretitle">mail.example.org</div>
|
||||||
|
<h2 class="page-title">Overview</h2>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto text-secondary small">Updated 14:02:31 UTC · refreshes every 10 s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page-body">
|
||||||
|
<div class="container-xl">
|
||||||
|
|
||||||
|
<div class="alert alert-warning bg-white" role="alert">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div><i class="ti ti-alert-triangle alert-icon"></i></div>
|
||||||
|
<div>
|
||||||
|
<h4 class="alert-title">Mail is flowing. One thing needs attention.</h4>
|
||||||
|
<div class="text-secondary">The TLS certificate expires in 12 days and has not renewed yet.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row row-cards mb-3">
|
||||||
|
<div class="col-sm-6 col-lg-4"><div class="card card-sm sp-tile"><div class="card-body"><i class="ti ti-cpu sp-tile-icon"></i><div><div class="fw-semibold">Machine</div><div class="text-secondary small">CPU 12 % · mem 41 %</div></div><span class="status status-green"><span class="status-dot"></span>OK</span></div></div></div>
|
||||||
|
<div class="col-sm-6 col-lg-4"><div class="card card-sm sp-tile"><div class="card-body"><i class="ti ti-server-cog sp-tile-icon"></i><div><div class="fw-semibold">Processes</div><div class="text-secondary small">5 of 5 running</div></div><span class="status status-green"><span class="status-dot"></span>OK</span></div></div></div>
|
||||||
|
<div class="col-sm-6 col-lg-4"><div class="card card-sm sp-tile"><div class="card-body"><i class="ti ti-stack-2 sp-tile-icon"></i><div><div class="fw-semibold">Queues</div><div class="text-secondary small">Both empty</div></div><span class="status status-green"><span class="status-dot"></span>OK</span></div></div></div>
|
||||||
|
<div class="col-sm-6 col-lg-4"><div class="card card-sm sp-tile"><div class="card-body"><i class="ti ti-certificate sp-tile-icon"></i><div><div class="fw-semibold">TLS certificate</div><div class="text-secondary small">Expires in 12 days</div></div><span class="status status-yellow"><span class="status-dot"></span>Warn</span></div></div></div>
|
||||||
|
<div class="col-sm-6 col-lg-4"><div class="card card-sm sp-tile"><div class="card-body"><i class="ti ti-plug-connected sp-tile-icon"></i><div><div class="fw-semibold">Milter sockets</div><div class="text-secondary small">2 of 2 answering</div></div><span class="status status-green"><span class="status-dot"></span>OK</span></div></div></div>
|
||||||
|
<div class="col-sm-6 col-lg-4"><div class="card card-sm sp-tile"><div class="card-body"><i class="ti ti-arrows-exchange sp-tile-icon"></i><div><div class="fw-semibold">Reverse DNS</div><div class="text-secondary small">Forward = reverse</div></div><span class="status status-green"><span class="status-dot"></span>OK</span></div></div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row row-deck row-cards">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><h3 class="card-title">Machine</h3><div class="card-actions"><span class="badge bg-green-lt">ok</span></div></div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-vcenter card-table">
|
||||||
|
<thead><tr><th>Resource</th><th>Usage</th><th>Detail</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>CPU</td><td><progress class="sp-bar" value="12" max="100"></progress>12 %</td><td class="text-secondary">2 cores, load 0.21</td></tr>
|
||||||
|
<tr><td>Memory</td><td><progress class="sp-bar" value="41" max="100"></progress>41 %</td><td class="text-secondary">812 MB of 1.9 GB</td></tr>
|
||||||
|
<tr><td>Network</td><td>↓ 18 kB/s · ↑ 44 kB/s</td><td class="text-secondary">eth0: 2.1 GB in, 6.4 GB out</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><h3 class="card-title">Processes</h3><div class="card-actions"><span class="badge bg-green-lt">ok</span></div></div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-vcenter card-table">
|
||||||
|
<thead><tr><th>Program</th><th>State</th><th>Detail</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>postfix</td><td><span class="badge bg-green-lt">running</span></td><td class="text-secondary">pid 41, uptime 18 days</td></tr>
|
||||||
|
<tr><td>opendkim</td><td><span class="badge bg-green-lt">running</span></td><td class="text-secondary">pid 39, uptime 18 days</td></tr>
|
||||||
|
<tr><td>journal-milter</td><td><span class="badge bg-green-lt">running</span></td><td class="text-secondary">pid 40, uptime 18 days</td></tr>
|
||||||
|
<tr><td>panel</td><td><span class="badge bg-green-lt">running</span></td><td class="text-secondary">pid 44, uptime 18 days</td></tr>
|
||||||
|
<tr><td>dmarc-ingest</td><td><span class="badge bg-green-lt">running</span></td><td class="text-secondary">pid 47, uptime 18 days</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><h3 class="card-title">TLS certificate</h3><div class="card-actions"><span class="badge bg-yellow-lt">warn</span></div></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="datagrid">
|
||||||
|
<div class="datagrid-item"><div class="datagrid-title">Expires</div><div class="datagrid-content font-monospace">2026-10-03 09:14 UTC</div></div>
|
||||||
|
<div class="datagrid-item"><div class="datagrid-title">Names</div><div class="datagrid-content font-monospace">mail.example.org</div></div>
|
||||||
|
</div>
|
||||||
|
<p class="text-secondary mt-3 mb-0">Renewal normally runs 30 days before expiry. Check port 80 and the <a href="#">system log</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><h3 class="card-title">Milter sockets</h3><div class="card-actions"><span class="badge bg-green-lt">ok</span></div></div>
|
||||||
|
<table class="table table-vcenter card-table">
|
||||||
|
<thead><tr><th>Milter</th><th>State</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>opendkim<div class="text-secondary small font-monospace">inet:127.0.0.1:8891</div></td><td><span class="badge bg-green-lt">ok</span></td></tr>
|
||||||
|
<tr><td>journal-milter<div class="text-secondary small font-monospace">unix:/run/selfpost/journal.sock</div></td><td><span class="badge bg-green-lt">ok</span></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><h3 class="card-title">Hostname and reverse DNS</h3><div class="card-actions"><span class="badge bg-green-lt">ok</span></div></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="datagrid">
|
||||||
|
<div class="datagrid-item"><div class="datagrid-title">Hostname</div><div class="datagrid-content font-monospace">mail.example.org</div></div>
|
||||||
|
<div class="datagrid-item"><div class="datagrid-title">Lookup</div><div class="datagrid-content font-monospace">→ 203.0.113.25<br>← mail.example.org</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer"><form method="post" action="#"><button type="submit" class="btn btn-sm"><i class="ti ti-refresh me-1"></i>Re-check DNS</button></form></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer footer-transparent">
|
||||||
|
<div class="container-xl sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/* Direction B — "Console". Tabler (Bootstrap 5 admin kit) carries the shell,
|
||||||
|
cards, tables, badges, input groups and the sign-in page as shipped; this
|
||||||
|
file only re-points Tabler's variables at the brand and adds the two things
|
||||||
|
Tabler has no word for: a sidebar group label and the DNS record block. */
|
||||||
|
|
||||||
|
:root, [data-bs-theme=light] {
|
||||||
|
--tblr-font-sans-serif: var(--sp-sans);
|
||||||
|
--tblr-font-monospace: var(--sp-mono);
|
||||||
|
--tblr-body-bg: var(--sp-paper);
|
||||||
|
--tblr-body-color: var(--sp-ink);
|
||||||
|
--tblr-secondary: var(--sp-muted);
|
||||||
|
--tblr-border-color: var(--sp-rule);
|
||||||
|
--tblr-border-radius: 3px;
|
||||||
|
--tblr-border-radius-lg: 4px;
|
||||||
|
|
||||||
|
--tblr-primary: #7a3b2e;
|
||||||
|
--tblr-primary-rgb: 122, 59, 46;
|
||||||
|
--tblr-link-color: #7a3b2e;
|
||||||
|
--tblr-link-color-rgb: 122, 59, 46;
|
||||||
|
--tblr-link-hover-color: #632f25;
|
||||||
|
--tblr-link-hover-color-rgb: 99, 47, 37;
|
||||||
|
|
||||||
|
--tblr-green: #2c6b43; --tblr-green-rgb: 44, 107, 67;
|
||||||
|
--tblr-yellow: #b5700f; --tblr-yellow-rgb: 181, 112, 15;
|
||||||
|
--tblr-red: #b42318; --tblr-red-rgb: 180, 35, 24;
|
||||||
|
--tblr-success: #2c6b43; --tblr-success-rgb: 44, 107, 67;
|
||||||
|
--tblr-warning: #b5700f; --tblr-warning-rgb: 181, 112, 15;
|
||||||
|
--tblr-danger: #b42318; --tblr-danger-rgb: 180, 35, 24;
|
||||||
|
}
|
||||||
|
body { font-feature-settings: normal; }
|
||||||
|
.btn-primary { --tblr-btn-hover-bg: #632f25; --tblr-btn-active-bg: #632f25; }
|
||||||
|
code, .font-monospace { font-family: var(--sp-mono); }
|
||||||
|
|
||||||
|
/* ---- sidebar: ink, with the stamp on its own paper */
|
||||||
|
.navbar-vertical {
|
||||||
|
--tblr-navbar-bg: var(--sp-ink); background: var(--sp-ink);
|
||||||
|
--tblr-navbar-color: #aab2bd; --tblr-navbar-hover-color: #fff; --tblr-navbar-active-color: #fff;
|
||||||
|
--tblr-navbar-border-color: transparent;
|
||||||
|
}
|
||||||
|
.navbar-vertical .navbar-brand { padding: 1.1rem 0 .9rem; justify-content: flex-start; }
|
||||||
|
.navbar-vertical .navbar-brand img { height: 52px; width: auto; }
|
||||||
|
.navbar-vertical .nav-link { padding-block: .42rem; }
|
||||||
|
.navbar-vertical .nav-link-icon { font-size: 1.15rem; }
|
||||||
|
.navbar-vertical .nav-item.active > .nav-link {
|
||||||
|
background: rgb(255 255 255 / .07); box-shadow: inset 3px 0 0 #c4705e; color: #fff;
|
||||||
|
}
|
||||||
|
.sp-nav-label {
|
||||||
|
font-family: var(--sp-mono); font-size: .66rem; font-weight: 600; letter-spacing: .14em;
|
||||||
|
text-transform: uppercase; color: #6f7a88; padding: 1.1rem 1.5rem .3rem;
|
||||||
|
}
|
||||||
|
.sp-nav-foot { margin-top: auto; padding-bottom: .8rem; border-top: 1px solid rgb(255 255 255 / .08); padding-top: .6rem; }
|
||||||
|
.sp-nav-user { color: #6f7a88; font-size: .8rem; padding: .3rem 1.5rem; }
|
||||||
|
|
||||||
|
/* ---- page */
|
||||||
|
.page-pretitle { font-family: var(--sp-mono); letter-spacing: .1em; }
|
||||||
|
.card-title { font-weight: 600; }
|
||||||
|
.table thead th { font-family: var(--sp-mono); letter-spacing: .08em; }
|
||||||
|
.sp-tile .card-body { display: flex; align-items: center; gap: .8rem; padding: .85rem 1rem; }
|
||||||
|
.sp-tile .sp-tile-icon { font-size: 1.5rem; color: var(--sp-muted); }
|
||||||
|
.sp-tile .fw-semibold { line-height: 1.2; }
|
||||||
|
.sp-tile .status { margin-left: auto; }
|
||||||
|
progress.sp-bar {
|
||||||
|
appearance: none; -webkit-appearance: none; width: 8rem; height: .4rem; border: 0;
|
||||||
|
border-radius: 1rem; overflow: hidden; vertical-align: middle; margin-right: .6rem; background: var(--sp-well);
|
||||||
|
}
|
||||||
|
progress.sp-bar::-webkit-progress-bar { background: var(--sp-well); }
|
||||||
|
progress.sp-bar::-webkit-progress-value { background: var(--tblr-green); }
|
||||||
|
progress.sp-bar::-moz-progress-bar { background: var(--tblr-green); }
|
||||||
|
|
||||||
|
/* ---- the DNS record block: Tabler's read-only input group, plus a status */
|
||||||
|
.sp-record + .sp-record { border-top: 1px solid var(--sp-rule); }
|
||||||
|
.sp-record .form-control[readonly], .sp-record textarea[readonly] {
|
||||||
|
font-family: var(--sp-mono); font-size: .8rem; background: var(--sp-well);
|
||||||
|
}
|
||||||
|
.sp-record textarea { resize: none; }
|
||||||
|
.sp-record .form-label { font-size: .75rem; color: var(--sp-muted); margin-bottom: .25rem; }
|
||||||
|
|
||||||
|
.sp-foot { color: var(--sp-muted); font-size: .8rem; }
|
||||||
|
.sp-signin-mark { width: 15rem; height: auto; }
|
||||||
|
.sp-tagline { font-family: var(--sp-mono); font-size: .72rem; letter-spacing: .16em; color: var(--sp-muted); }
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>example.org · Outbound domains · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand">
|
||||||
|
<a class="navbar-item" href="status.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="status.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="domain.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="domain.html">Domains</a>
|
||||||
|
<a class="navbar-item" href="#">Log</a>
|
||||||
|
<a class="navbar-item" href="#">Queue</a>
|
||||||
|
<a class="navbar-item" href="#">DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="#">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="#">Domains</a>
|
||||||
|
<a class="navbar-item" href="#">Log</a>
|
||||||
|
<a class="navbar-item" href="#">Queue</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="#">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="#">System log</a>
|
||||||
|
<a class="navbar-item" href="#">Backup</a>
|
||||||
|
<a class="navbar-item" href="#">Users</a>
|
||||||
|
<a class="navbar-item" href="#">Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="#">admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="#">Account</a>
|
||||||
|
<a class="navbar-item" href="#">Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html">Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="level mb-5">
|
||||||
|
<div class="level-left">
|
||||||
|
<div>
|
||||||
|
<p class="sp-kicker"><a href="status.html">Outbound domains</a> /</p>
|
||||||
|
<h1 class="title is-3 mb-0">example.org</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="level-right">
|
||||||
|
<div class="buttons">
|
||||||
|
<form method="post" action="#"><button type="submit" class="button">Re-check DNS</button></form>
|
||||||
|
<a class="button is-primary" href="#">Add application</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<aside class="menu sp-side">
|
||||||
|
<p class="menu-label">This domain</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="is-active" href="#dns">DNS records <span class="tag is-danger is-light">1</span></a></li>
|
||||||
|
<li><a href="#apps">Applications <span class="tag is-light">2</span></a></li>
|
||||||
|
<li><a href="#connection">Connection</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Rarely changed</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="#">DMARC reports</a></li>
|
||||||
|
<li><a href="#">Rate limit <span class="tag is-success is-light">active</span></a></li>
|
||||||
|
<li><a href="#">Export</a></li>
|
||||||
|
<li><a class="has-text-danger" href="#">Delete domain</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-9">
|
||||||
|
<div class="box" id="dns">
|
||||||
|
<div class="sp-box-head"><h2>DNS records</h2><div class="sp-end">Checked 3 min ago</div></div>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>DKIM</h3><span class="tag is-success is-light">published</span><small>Not a secret · selector <code>sp2026</code></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="sp2026._domainkey.example.org"></div><div class="control"><button type="button" class="button">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="3" readonly>v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3kqV0mJd8e2f1Yb7Qw5uT9sLr4nHc6pZaXo1vKe0yWm2gBt8RjN5dCq3hUf7lPs9aEi4oMz6xTb1kGv0nYw…IDAQAB</textarea></div><div class="control"><button type="button" class="button">Copy</button></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>SPF</h3><span class="tag is-danger is-light">mismatch</span><small>Merge into the existing record — never publish a second one</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="example.org"></div><div class="control"><button type="button" class="button">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"><input class="input" readonly value="v=spf1 ip4:203.0.113.25 ~all"></div><div class="control"><button type="button" class="button">Copy</button></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">In DNS now</label>
|
||||||
|
<div class="control"><input class="input" readonly value="v=spf1 include:_spf.google.com ~all"></div>
|
||||||
|
<p class="help is-danger">The published record does not authorize 203.0.113.25. Add <code>ip4:203.0.113.25</code> before <code>~all</code>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>DMARC</h3><span class="tag is-success is-light">published</span><small>Report address from <a href="#">Settings</a></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="_dmarc.example.org"></div><div class="control"><button type="button" class="button">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"><input class="input" readonly value="v=DMARC1; p=none; rua=mailto:dmarc@mail.example.org"></div><div class="control"><button type="button" class="button">Copy</button></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="apps">
|
||||||
|
<div class="sp-box-head"><h2>Applications</h2><div class="sp-end">SASL logins that may send as this domain</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Login</th><th>May send as</th><th>30 days</th><th>Limits</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="sp-mono"><strong>prod-server</strong></td><td>*@example.org</td><td class="sp-muted">1 102 msg · peak 96/h</td>
|
||||||
|
<td><span class="tag is-success is-light">200 / h</span></td>
|
||||||
|
<td><div class="buttons are-small is-right is-flex-wrap-nowrap"><a class="button" href="#">Edit</a><a class="button" href="#">New password</a><a class="button is-danger is-light" href="#">Delete</a></div></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sp-mono"><strong>alerts</strong></td><td>alerts@example.org, noc@example.org</td><td class="sp-muted">182 msg · peak 14/h</td>
|
||||||
|
<td><span class="tag is-light">domain</span> <span class="tag is-success is-light">2 IPs</span></td>
|
||||||
|
<td><div class="buttons are-small is-right is-flex-wrap-nowrap"><a class="button" href="#">Edit</a><a class="button" href="#">New password</a><a class="button is-danger is-light" href="#">Delete</a></div></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<div class="box" id="connection">
|
||||||
|
<div class="sp-box-head"><h2>Connection</h2><div class="sp-end">Same for every domain</div></div>
|
||||||
|
<p class="sp-mono">mail.example.org</p>
|
||||||
|
<p class="sp-mono sp-muted">465 SSL/TLS · 587 STARTTLS</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><h2>Sent, 30 days</h2></div>
|
||||||
|
<div class="sp-stats">
|
||||||
|
<div><b>1 284</b><span class="sp-muted">messages</span></div>
|
||||||
|
<div><b>96</b><span class="sp-muted">peak / h</span></div>
|
||||||
|
<div><b>1.8</b><span class="sp-muted">average / h</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Sign in · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="columns is-gapless sp-signin">
|
||||||
|
<div class="column is-half sp-signin-brand">
|
||||||
|
<div>
|
||||||
|
<img src="../../selfpost-stamp.svg" alt="SelfPost">
|
||||||
|
<p>YOUR SMTP. YOUR RULES.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-half sp-signin-form">
|
||||||
|
<form method="post" action="status.html">
|
||||||
|
<h1 class="title is-3">Sign in</h1>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="username">Username</label>
|
||||||
|
<div class="control"><input class="input" id="username" name="username" autocomplete="username" required></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password">Password</label>
|
||||||
|
<div class="control"><input class="input" id="password" name="password" type="password" autocomplete="current-password" required></div>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-5"><button type="submit" class="button is-primary is-fullwidth">Sign in</button></div>
|
||||||
|
</form>
|
||||||
|
<p class="sp-foot">© 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Overview · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand">
|
||||||
|
<a class="navbar-item" href="status.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item is-active" href="status.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="domain.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="domain.html">Domains</a>
|
||||||
|
<a class="navbar-item" href="#">Log</a>
|
||||||
|
<a class="navbar-item" href="#">Queue</a>
|
||||||
|
<a class="navbar-item" href="#">DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="#">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="#">Domains</a>
|
||||||
|
<a class="navbar-item" href="#">Log</a>
|
||||||
|
<a class="navbar-item" href="#">Queue</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="#">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="#">System log</a>
|
||||||
|
<a class="navbar-item" href="#">Backup</a>
|
||||||
|
<a class="navbar-item" href="#">Users</a>
|
||||||
|
<a class="navbar-item" href="#">Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="#">admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="#">Account</a>
|
||||||
|
<a class="navbar-item" href="#">Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html">Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="level mb-5">
|
||||||
|
<div class="level-left">
|
||||||
|
<div>
|
||||||
|
<p class="sp-kicker">mail.example.org</p>
|
||||||
|
<h1 class="title is-3 mb-0">Overview</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="level-right sp-muted is-size-7">Updated 14:02:31 UTC · refreshes every 10 s</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box sp-health">
|
||||||
|
<a href="#"><div class="sp-h-name">Machine</div><div class="sp-h-value">12 % CPU</div><div class="sp-h-sub">41 % memory</div></a>
|
||||||
|
<a href="#"><div class="sp-h-name">Processes</div><div class="sp-h-value">5 of 5</div><div class="sp-h-sub">running</div></a>
|
||||||
|
<a href="#"><div class="sp-h-name">Queues</div><div class="sp-h-value">Empty</div><div class="sp-h-sub">outbound and inbound</div></a>
|
||||||
|
<a href="#" class="warn"><div class="sp-h-name">TLS certificate</div><div class="sp-h-value">12 days</div><div class="sp-h-sub">renewal has not run</div></a>
|
||||||
|
<a href="#"><div class="sp-h-name">Milter sockets</div><div class="sp-h-value">2 of 2</div><div class="sp-h-sub">answering</div></a>
|
||||||
|
<a href="#"><div class="sp-h-name">Reverse DNS</div><div class="sp-h-value">Match</div><div class="sp-h-sub">203.0.113.25</div></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><h2>Outbound domains</h2><div class="sp-end"><a class="button is-small is-primary" href="#">Add domain</a></div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>DNS</th><th>Apps</th><th>30 days</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="domain.html"><strong>example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-danger is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td>2</td><td class="sp-muted">1 284 msg</td></tr>
|
||||||
|
<tr><td><a href="#"><strong>shop.example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td>1</td><td class="sp-muted">18 920 msg</td></tr>
|
||||||
|
<tr><td><a href="#"><strong>notify.acme.io</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-warning is-light">DMARC</span></td><td>3</td><td class="sp-muted">402 msg</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><h2>Inbound domains</h2><div class="sp-end"><a class="button is-small" href="#">Add domain</a></div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>MX</th><th>Upstream</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="#"><strong>lists.example.org</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-muted sp-mono is-size-7">mx.internal:25</td></tr>
|
||||||
|
<tr><td><a href="#"><strong>acme.io</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-muted sp-mono is-size-7">10.0.4.12:2525</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/* Direction C — "Counter". Bulma v1 (pure CSS, no JavaScript, no data: URIs)
|
||||||
|
carries the navbar with hover dropdowns, columns, boxes, tables, tags,
|
||||||
|
the side menu and input-with-addon fields. This file points Bulma's HSL
|
||||||
|
variables at the brand and adds the health strip and the record block. */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bulma-family-primary: var(--sp-sans);
|
||||||
|
--bulma-family-secondary: var(--sp-sans);
|
||||||
|
--bulma-family-code: var(--sp-mono);
|
||||||
|
--bulma-primary-h: 10deg; --bulma-primary-s: 45%; --bulma-primary-l: 33%;
|
||||||
|
--bulma-link-h: 10deg; --bulma-link-s: 45%; --bulma-link-l: 33%;
|
||||||
|
--bulma-success-h: 138deg; --bulma-success-s: 42%; --bulma-success-l: 30%;
|
||||||
|
--bulma-warning-h: 36deg; --bulma-warning-s: 85%; --bulma-warning-l: 55%;
|
||||||
|
--bulma-danger-h: 4deg; --bulma-danger-s: 76%; --bulma-danger-l: 40%;
|
||||||
|
--bulma-radius: 6px; --bulma-radius-large: 10px;
|
||||||
|
--bulma-body-size: 15px;
|
||||||
|
/* Bulma derives link text and the on-primary colour from lightness steps;
|
||||||
|
at the brick's 33 % both land off-brand, so they are pinned. */
|
||||||
|
--bulma-link-text: var(--sp-brick);
|
||||||
|
--bulma-link-text-hover: var(--sp-brick-dark);
|
||||||
|
}
|
||||||
|
.button.is-primary, .button.is-primary:hover { color: #fff; }
|
||||||
|
a strong { color: inherit; }
|
||||||
|
html { background: var(--sp-paper); }
|
||||||
|
body { background: var(--sp-paper); color: var(--sp-ink); min-height: 100vh; display: flex; flex-direction: column; }
|
||||||
|
main { flex: 1; }
|
||||||
|
code { background: var(--sp-well); color: var(--sp-ink); border-radius: 3px; padding: .1em .35em; font-size: .85em; }
|
||||||
|
|
||||||
|
/* ---- navbar: brick, the stamp on it, hover dropdowns */
|
||||||
|
.navbar.is-primary { --bulma-navbar-height: 4rem; }
|
||||||
|
.navbar-brand img { max-height: 3rem; height: 3rem; }
|
||||||
|
.navbar.is-primary .navbar-item, .navbar.is-primary .navbar-link { color: var(--sp-on-brick); font-weight: 500; }
|
||||||
|
.navbar.is-primary .navbar-link::after { border-color: var(--sp-on-brick); }
|
||||||
|
.navbar.is-primary .navbar-start > .navbar-item.is-active,
|
||||||
|
.navbar.is-primary .navbar-start > .has-dropdown.sp-current > .navbar-link { box-shadow: inset 0 -3px 0 var(--sp-on-brick); }
|
||||||
|
.navbar-dropdown { border-top: 0; box-shadow: 0 8px 24px rgb(18 22 28 / .14); }
|
||||||
|
.navbar.is-primary .navbar-dropdown .navbar-item { color: var(--sp-ink); font-weight: 400; }
|
||||||
|
.navbar.is-primary .navbar-dropdown .navbar-item.is-active { color: var(--sp-brick); font-weight: 600; background: var(--sp-brick-tint); }
|
||||||
|
|
||||||
|
/* ---- page */
|
||||||
|
.sp-page { padding-block: 2rem 2.5rem; }
|
||||||
|
.sp-kicker { font-family: var(--sp-mono); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase; color: var(--sp-muted); }
|
||||||
|
.sp-kicker a { color: inherit; }
|
||||||
|
.title { color: var(--sp-ink); font-weight: 600; }
|
||||||
|
.box { box-shadow: none; border: 1px solid var(--sp-rule); }
|
||||||
|
.box > .sp-box-head { display: flex; align-items: center; gap: .75rem; margin-bottom: 1rem; }
|
||||||
|
.box > .sp-box-head h2 { font-size: 1.1rem; font-weight: 600; margin: 0; }
|
||||||
|
.box > .sp-box-head .sp-end { margin-left: auto; color: var(--sp-muted); font-size: .85rem; display: flex; gap: .6rem; align-items: center; }
|
||||||
|
.table { background: transparent; }
|
||||||
|
.table thead th { font-family: var(--sp-mono); font-size: .7rem; font-weight: 400; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); }
|
||||||
|
.table td { vertical-align: middle; }
|
||||||
|
.tag { font-family: var(--sp-mono); font-weight: 600; letter-spacing: .04em; }
|
||||||
|
.sp-muted { color: var(--sp-muted); }
|
||||||
|
.sp-mono { font-family: var(--sp-mono); }
|
||||||
|
|
||||||
|
/* ---- health strip */
|
||||||
|
.sp-health { display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); gap: 0; padding: 0; overflow: hidden; }
|
||||||
|
.sp-health > a {
|
||||||
|
display: block; padding: 1rem 1.2rem; color: var(--sp-ink); border-right: 1px solid var(--sp-rule);
|
||||||
|
box-shadow: inset 0 3px 0 var(--sp-ok);
|
||||||
|
}
|
||||||
|
.sp-health > a:last-child { border-right: 0; }
|
||||||
|
.sp-health > a:hover { background: var(--sp-paper); }
|
||||||
|
.sp-health > a.warn { box-shadow: inset 0 3px 0 #d9962b; background: var(--sp-warn-bg); }
|
||||||
|
.sp-health > a.fail { box-shadow: inset 0 3px 0 var(--sp-fail); background: var(--sp-fail-bg); }
|
||||||
|
.sp-health .sp-h-name { font-family: var(--sp-mono); font-size: .68rem; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); }
|
||||||
|
.sp-health .sp-h-value { font-size: 1.15rem; font-weight: 600; line-height: 1.3; }
|
||||||
|
.sp-health .sp-h-sub { font-size: .8rem; color: var(--sp-muted); }
|
||||||
|
|
||||||
|
/* ---- domain page */
|
||||||
|
.sp-side { position: sticky; top: 1.5rem; }
|
||||||
|
.menu-label { font-family: var(--sp-mono); letter-spacing: .1em; }
|
||||||
|
.menu-list a { display: flex; align-items: center; gap: .5rem; }
|
||||||
|
.menu-list a .tag { margin-left: auto; }
|
||||||
|
.sp-record + .sp-record { border-top: 1px solid var(--sp-rule); margin-top: 1.25rem; padding-top: 1.25rem; }
|
||||||
|
.sp-record-head { display: flex; align-items: center; gap: .6rem; margin-bottom: .75rem; }
|
||||||
|
.sp-record-head h3 { font-size: 1rem; font-weight: 600; margin: 0; }
|
||||||
|
.sp-record-head small { margin-left: auto; color: var(--sp-muted); }
|
||||||
|
.sp-record .label { font-size: .75rem; font-weight: 400; color: var(--sp-muted); margin-bottom: .25rem; }
|
||||||
|
.sp-record .input[readonly], .sp-record .textarea[readonly] {
|
||||||
|
font-family: var(--sp-mono); font-size: .82rem; background: var(--sp-well); box-shadow: none;
|
||||||
|
}
|
||||||
|
.sp-record .textarea { resize: none; min-height: 0; }
|
||||||
|
.sp-record .field:not(:last-child) { margin-bottom: .6rem; }
|
||||||
|
.sp-row { display: grid; grid-template-columns: 1fr 7rem; gap: .75rem; }
|
||||||
|
.sp-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
|
||||||
|
.sp-stats b { display: block; font-size: 1.5rem; font-weight: 600; line-height: 1.2; }
|
||||||
|
|
||||||
|
/* ---- sign-in */
|
||||||
|
.sp-signin { min-height: 100vh; margin: 0; }
|
||||||
|
.sp-signin-brand { background: var(--sp-brick); display: grid; place-content: center; text-align: center; padding: 3rem; }
|
||||||
|
.sp-signin-brand img { width: 20rem; max-width: 100%; height: auto; }
|
||||||
|
.sp-signin-brand p { font-family: var(--sp-mono); letter-spacing: .18em; font-size: .8rem; color: var(--sp-on-brick); margin-top: 1.6rem; }
|
||||||
|
.sp-signin-form { display: flex; flex-direction: column; justify-content: center; padding: 3rem; }
|
||||||
|
.sp-signin-form form { width: 21rem; max-width: 100%; margin-inline: auto; }
|
||||||
|
.sp-foot { color: var(--sp-muted); font-size: .8rem; padding-block: 1.2rem; }
|
||||||
|
.sp-foot a { color: inherit; text-decoration: underline; }
|
||||||
|
.sp-signin-form .sp-foot { text-align: center; margin-top: 2.5rem; }
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/* Cover sheet for the three directions. No library on purpose: it must not
|
||||||
|
look like any one of them. */
|
||||||
|
body { margin: 0; background: var(--sp-paper); color: var(--sp-ink); font: 16px/1.55 var(--sp-sans); }
|
||||||
|
main { max-width: 68rem; margin: 0 auto; padding: 3rem 1.5rem 4rem; }
|
||||||
|
h1 { font-size: 2rem; font-weight: 600; margin: .2rem 0 1rem; text-wrap: balance; }
|
||||||
|
h2 { font-size: 1.15rem; font-weight: 600; margin: 2.6rem 0 .8rem; }
|
||||||
|
h3 { font-size: 1.4rem; font-weight: 600; margin: .1rem 0 .6rem; }
|
||||||
|
.kicker { font-family: var(--sp-mono); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase; color: var(--sp-brick); margin: 0; }
|
||||||
|
.lead { max-width: 46rem; color: var(--sp-ink-2); }
|
||||||
|
ul { padding-left: 1.2rem; max-width: 52rem; }
|
||||||
|
li { margin-bottom: .5rem; }
|
||||||
|
a { color: var(--sp-brick); }
|
||||||
|
code { font-family: var(--sp-mono); font-size: .85em; background: var(--sp-well); padding: .1em .35em; border-radius: 3px; }
|
||||||
|
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 1.2rem; }
|
||||||
|
.cards section { background: var(--sp-sheet); border: 1px solid var(--sp-rule); border-top: 3px solid var(--sp-brick); padding: 1.3rem 1.4rem; display: flex; flex-direction: column; }
|
||||||
|
.cards p { margin: 0 0 1rem; }
|
||||||
|
.links { margin-top: auto !important; margin-bottom: 0 !important; display: flex; gap: 1.1rem; font-weight: 600; }
|
||||||
|
.scroll { overflow-x: auto; }
|
||||||
|
table { border-collapse: collapse; width: 100%; background: var(--sp-sheet); border: 1px solid var(--sp-rule); font-size: .93rem; }
|
||||||
|
th, td { text-align: left; vertical-align: top; padding: .6rem .9rem; border-bottom: 1px solid var(--sp-rule); }
|
||||||
|
thead th { font-family: var(--sp-mono); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); font-weight: 400; }
|
||||||
|
tbody th { font-weight: 600; white-space: nowrap; }
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Panel redesign · three directions</title>
|
||||||
|
<link rel="stylesheet" href="shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="index.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<p class="kicker">SelfPost · panel redesign · stage 2</p>
|
||||||
|
<h1>The accepted direction, every screen</h1>
|
||||||
|
<p class="lead">Direction <strong>C</strong> (Bulma) was picked as the base: the important things in view, details one
|
||||||
|
or two clicks away. It takes the postal signature of <strong>A</strong> — the postmark verdict, numbered mono section
|
||||||
|
heads, the stamp's perforated edge — and the icons of <strong>B</strong>. Thirty-three screens and a components page live in
|
||||||
|
<a href="panel/overview.html">panel/</a>. Serve this folder from the repository root: the pages load the panel's own
|
||||||
|
fonts and mark by relative path.</p>
|
||||||
|
|
||||||
|
<h2>Screen map</h2>
|
||||||
|
<div class="scroll">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr><th>Signed out</th><td><a href="panel/login.html">Sign in</a> · <a href="panel/setup.html">Create administrator</a></td></tr>
|
||||||
|
<tr><th>Overview</th><td><a href="panel/overview.html">Overview</a> — postmark verdict, six health cards, both domain lists</td></tr>
|
||||||
|
<tr><th>Outbound</th><td><a href="panel/out-domains.html">Domains</a> · <a href="panel/out-domain.html">domain</a> ·
|
||||||
|
<a href="panel/out-domain-settings.html">domain settings</a> · <a href="panel/out-app.html">application form</a> ·
|
||||||
|
<a href="panel/out-app-created.html">password shown once</a> · <a href="panel/out-domain-delete.html">delete domain</a><br>
|
||||||
|
<a href="panel/out-log.html">Log</a> · <a href="panel/out-message.html">message</a> ·
|
||||||
|
<a href="panel/out-queue.html">Queue</a> · <a href="panel/dmarc.html">DMARC reports</a> ·
|
||||||
|
<a href="panel/dmarc-domain.html">by domain</a> · <a href="panel/dmarc-report.html">one report</a></td></tr>
|
||||||
|
<tr><th>Inbound</th><td><a href="panel/in-domains.html">Domains</a> · <a href="panel/in-domain.html">domain</a> ·
|
||||||
|
<a href="panel/in-domain-delete.html">delete domain</a> · <a href="panel/in-log.html">Log</a> ·
|
||||||
|
<a href="panel/in-queue.html">Queue</a> (empty state)<br>
|
||||||
|
<a href="panel/in-quarantine.html">Quarantine</a> · <a href="panel/in-quarantine-message.html">held message</a> ·
|
||||||
|
<a href="panel/in-filter-lists.html">Filter lists</a> — roadmap items, drawn as their target look</td></tr>
|
||||||
|
<tr><th>Server</th><td><a href="panel/health.html">Health</a> · <a href="panel/preflight.html">Preflight</a> (roadmap) · <a href="panel/system-log.html">System log</a> ·
|
||||||
|
<a href="panel/backup.html">Backup</a> · <a href="panel/users.html">Users</a> · <a href="panel/user.html">user form</a> ·
|
||||||
|
<a href="panel/user-delete.html">delete user</a> · <a href="panel/settings.html">Settings</a></td></tr>
|
||||||
|
<tr><th>User menu</th><td><a href="panel/account.html">Account</a> · <a href="panel/help.html">Help</a></td></tr>
|
||||||
|
<tr><th>Contract</th><td><a href="panel/components.html">Components</a> — every building block in every state ·
|
||||||
|
<a href="../../plans/panel-redesign.md">the plan</a> · <code>panel/check.py</code> lints the mockups and writes <code>panel/outlines.json</code></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>What changed against today's panel</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Overview shows verdicts, Health shows tables.</strong> Process lists, socket paths and interface counters moved to
|
||||||
|
<em>Server › Health</em>, one click from each card.</li>
|
||||||
|
<li><strong>Every page is one of three layouts</strong>: a list (form on top, table below), a detail page (sticky side menu +
|
||||||
|
numbered boxes), or a form page (boxes in equal columns). No page picks its own width.</li>
|
||||||
|
<li><strong>Own password is Account</strong>, instance defaults are Settings. Export sits with the domain, import with Backup,
|
||||||
|
and each links to the other.</li>
|
||||||
|
<li><strong>Queues are tables</strong>, not a <code>postqueue -p</code> dump — this needs <code>postqueue -j</code> parsing.
|
||||||
|
The inbound queue is the same queue filtered by recipient domain.</li>
|
||||||
|
<li><strong>Confirm pages say what will be lost</strong> as a list, beside a hint for the usual alternative (export before delete).</li>
|
||||||
|
<li>Own components on top of Bulma: postmark, perforated edge, numbered box head, health card, DNS record block, facts grid,
|
||||||
|
timeline, log pane. The mockup's <code>theme.css</code> is about 200 lines.</li>
|
||||||
|
<li><code>panel/build.py</code> wraps the fragments in <code>panel/src/</code> with the shared navbar; the built pages are committed,
|
||||||
|
so nothing has to be run to view them.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Stage 1 · three directions on three key screens</h2>
|
||||||
|
<p class="lead">Kept for the record: Overview, an outbound domain and sign-in, drawn three ways.</p>
|
||||||
|
|
||||||
|
<h2>Shared by all three</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Navigation by mail direction.</strong> Overview · <em>Outbound</em> (Domains, Log, Queue, DMARC reports) ·
|
||||||
|
<em>Inbound</em> (Domains, Log, Queue) · <em>Server</em> (System log, Backup, Users, Settings). Account, Help and
|
||||||
|
Sign out live with the user name. Eleven flat entries become four groups.</li>
|
||||||
|
<li><strong>One block per DNS record.</strong> Today "DNS status" and "records to publish" show every host twice.
|
||||||
|
Here a record is one block: its status, what to publish, and — only on a mismatch — what DNS holds now.</li>
|
||||||
|
<li><strong>The domain page stops being nine cards.</strong> DNS and applications are the page; limits, report address,
|
||||||
|
export and delete are "rarely changed" and sit behind one click.</li>
|
||||||
|
<li><strong>Applications are a table</strong>; editing one is its own form page instead of a three-column panel inside a list row.</li>
|
||||||
|
<li>Brand carriers stay: the stamp, brick, IBM Plex. No inline <code>style</code> or script in any mockup.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>The directions</h2>
|
||||||
|
<div class="cards">
|
||||||
|
<section>
|
||||||
|
<p class="kicker">A · Pico CSS 2.1</p>
|
||||||
|
<h3>Blank</h3>
|
||||||
|
<p>The panel as a postal form: one centred sheet, numbered sections, mono field labels, a postmark for the
|
||||||
|
verdict. Overview is a ledger — one row per check, the row opens into its detail. Sections on the first
|
||||||
|
header row, the section's pages on the second.</p>
|
||||||
|
<p class="links"><a href="a/status.html">Overview</a><a href="a/domain.html">Domain</a><a href="a/login.html">Sign in</a></p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<p class="kicker">B · Tabler 1.5 (Bootstrap 5)</p>
|
||||||
|
<h3>Console</h3>
|
||||||
|
<p>The classic admin kit: ink sidebar with every page one click away, status tiles, cards and tables in a
|
||||||
|
12-column grid, read-only input groups with Copy. Almost nothing is ours; it also looks the most like
|
||||||
|
every other admin panel.</p>
|
||||||
|
<p class="links"><a href="b/status.html">Overview</a><a href="b/domain.html">Domain</a><a href="b/login.html">Sign in</a></p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<p class="kicker">C · Bulma 1.0</p>
|
||||||
|
<h3>Counter</h3>
|
||||||
|
<p>Brick top bar with CSS-only hover menus. Overview is the whole service on one screen: a health strip over
|
||||||
|
the outbound and inbound domain lists. The domain page gets a sticky side menu. Softer, rounder, the
|
||||||
|
boldest use of the brand colour.</p>
|
||||||
|
<p class="links"><a href="c/status.html">Overview</a><a href="c/domain.html">Domain</a><a href="c/login.html">Sign in</a></p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>What each one costs</h2>
|
||||||
|
<div class="scroll">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th></th><th>A · Pico</th><th>B · Tabler</th><th>C · Bulma</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><th>Licence</th><td>MIT</td><td>MIT</td><td>MIT</td></tr>
|
||||||
|
<tr><th>Vendored CSS (minified)</th><td>83 KB</td><td>694 KB</td><td>678 KB</td></tr>
|
||||||
|
<tr><th>JavaScript</th><td>none</td><td>none for these screens; Bootstrap bundle only if dropdowns / modals are wanted</td><td>none</td></tr>
|
||||||
|
<tr><th>Build step</th><td>none</td><td>none</td><td>none (a Sass build could trim it; not needed)</td></tr>
|
||||||
|
<tr><th>CSP <code>default-src 'self'</code></th>
|
||||||
|
<td>needs <code>img-src 'self' data:</code> — 14 <code>data:</code> SVGs (select chevron, checkbox tick)</td>
|
||||||
|
<td>needs <code>img-src 'self' data:</code> — 28 <code>data:</code> SVGs</td>
|
||||||
|
<td>works as is — no <code>data:</code> URIs</td></tr>
|
||||||
|
<tr><th>Our own CSS in the mockup</th><td>≈ 200 lines</td><td>≈ 80 lines</td><td>≈ 100 lines</td></tr>
|
||||||
|
<tr><th>Markup style</th><td>semantic HTML, few classes — closest to today's templates</td><td>class-heavy Bootstrap markup</td><td>class-based, moderate</td></tr>
|
||||||
|
<tr><th>Own components</th><td>sheet, ledger row, postmark, record block</td><td>sidebar group label, record block</td><td>health strip, record block</td></tr>
|
||||||
|
<tr><th>Recognisable</th><td>most</td><td>least</td><td>middle</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Open points</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Icons in B come from Tabler Icons (MIT) through its webfont for the mockup; the panel would vendor only the used glyphs as an SVG sprite.</li>
|
||||||
|
<li><em>Inbound log</em> rides on the agreed <code>inbound-antispam-panel</code> journal; <em>Inbound queue</em> is a new filter over the one Postfix queue and needs its own roadmap row.</li>
|
||||||
|
<li>Dark scheme and the narrow-screen shell are not drawn at this stage; all three libraries ship both.</li>
|
||||||
|
<li>The earlier <a href="../panel-ui/index.html">panel-ui</a> experiment and <a href="../../plans/panel-redesign.md">panel-redesign.md</a> are superseded by whichever direction is accepted.</li>
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Account · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Signed in as admin · global administrator</p>
|
||||||
|
<h1 class="title is-3">Account</h1>
|
||||||
|
<p class="sp-lead">Your sign-in to this panel. Applications keep their own logins and passwords and are not affected.</p>
|
||||||
|
</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>Panel credentials</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="username">Username</label><div class="control"><input class="input" id="username" name="username" value="admin" autocomplete="username" spellcheck="false" required></div></div>
|
||||||
|
<div class="field"><label class="label" for="cur">Current password</label><div class="control"><input class="input" id="cur" name="current_password" type="password" autocomplete="current-password" required></div></div>
|
||||||
|
<div class="sp-pair">
|
||||||
|
<div class="field"><label class="label" for="new">New password</label><div class="control"><input class="input" id="new" name="new_password" type="password" autocomplete="new-password"></div></div>
|
||||||
|
<div class="field"><label class="label" for="new2">Confirm new password</label><div class="control"><input class="input" id="new2" name="new_password_confirm" type="password" autocomplete="new-password"></div></div>
|
||||||
|
</div>
|
||||||
|
<p class="help mb-3">Leave both new-password fields empty to change the username only.</p>
|
||||||
|
<button type="submit" class="button is-primary">Save changes</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-info-circle"></i></span><h2>What happens</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-2">Changing the password signs out every other session of this account. This one stays signed in.</p>
|
||||||
|
<p class="sp-muted">A domain administrator sees this same page, the Outbound and Inbound domains assigned to them, and nothing under Server.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Backup · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item is-active" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="health.html"><i class="ti ti-heartbeat"></i>Health</a><a href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a><a href="system-log.html"><i class="ti ti-file-text"></i>System log</a><a href="backup.html" aria-current="page"><i class="ti ti-archive"></i>Backup</a><a href="users.html"><i class="ti ti-users"></i>Users</a><a href="settings.html"><i class="ti ti-settings"></i>Settings</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Backup</h1>
|
||||||
|
<p class="sp-lead">Take the whole instance with you, or bring one domain in from another SelfPost. Both files are secrets.</p>
|
||||||
|
</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>Full backup</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3"><code>data/</code> (database, DKIM keys, credentials, the queue), <code>docker-compose.yml</code>, <code>.env</code> and <code>certs/</code> in one archive. Restore into an empty project directory and start the <strong>same SelfPost version</strong>. The reverse-proxy vhost is not included.</p>
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="encrypt" checked> Encrypt with a password (<code>.spbk</code>)</label></div>
|
||||||
|
<div class="sp-pair">
|
||||||
|
<div class="field"><label class="label" for="pw">Password</label><div class="control"><input class="input" id="pw" type="password" placeholder="at least 12 characters"></div></div>
|
||||||
|
<div class="field"><label class="label" for="pw2">Repeat password</label><div class="control"><input class="input" id="pw2" type="password"></div></div>
|
||||||
|
</div>
|
||||||
|
<p class="help mb-3">Keep this password: SelfPost does not store it, and the file cannot be opened without it.</p>
|
||||||
|
<button type="submit" class="button is-primary"><i class="ti ti-download"></i>Download full backup</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="import">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Import a domain</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">A domain export from another instance — plain <code>.json</code> or encrypted <code>.spde</code>. Its DKIM key and application passwords come across, so the published DNS needs no change. The export itself is made on the <a href="out-domain-settings.html#export">domain's settings</a>.</p>
|
||||||
|
<form class="sp-form" method="post" action="out-domain.html" enctype="multipart/form-data">
|
||||||
|
<div class="field"><label class="label">Domain export file</label>
|
||||||
|
<div class="file has-name is-fullwidth"><label class="file-label"><input class="file-input" type="file" name="file" accept=".json,.spde"><span class="file-cta"><i class="ti ti-upload"></i><span class="file-label ml-2">Choose a file…</span></span><span class="file-name">example.org.spde</span></label></div></div>
|
||||||
|
<div class="field"><label class="label" for="ipw">Password</label><div class="control"><input class="input" id="ipw" name="import_password" type="password" autocomplete="off"></div>
|
||||||
|
<p class="help">Asked only for an encrypted <code>.spde</code> file.</p></div>
|
||||||
|
<button type="submit" class="button is-primary"><i class="ti ti-file-import"></i>Import domain</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
"""Wrap every fragment in src/ with the panel shell and write it next to this file.
|
||||||
|
|
||||||
|
The mockups are static HTML, but thirty pages share one navbar; keeping that
|
||||||
|
navbar in one place is the only reason this script exists. Run it from
|
||||||
|
anywhere: python docs/assets/panel-redesign/panel/build.py
|
||||||
|
|
||||||
|
A fragment starts with one meta comment:
|
||||||
|
<!-- title: Outbound domains | section: outbound | page: domains -->
|
||||||
|
section/page pick the highlighted entries; "shell: bare" skips the navbar
|
||||||
|
(sign-in, setup).
|
||||||
|
"""
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
|
||||||
|
HERE = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
|
NAV = [
|
||||||
|
("overview", "Overview", "overview.html", "ti-activity-heartbeat", []),
|
||||||
|
("outbound", "Outbound", "out-domains.html", "ti-send", [
|
||||||
|
("domains", "Domains", "out-domains.html", "ti-world-upload"),
|
||||||
|
("log", "Log", "out-log.html", "ti-list-details"),
|
||||||
|
("queue", "Queue", "out-queue.html", "ti-stack-2"),
|
||||||
|
("dmarc", "DMARC reports", "dmarc.html", "ti-report-analytics"),
|
||||||
|
]),
|
||||||
|
("inbound", "Inbound", "in-domains.html", "ti-inbox", [
|
||||||
|
("domains", "Domains", "in-domains.html", "ti-world-download"),
|
||||||
|
("log", "Log", "in-log.html", "ti-list-details"),
|
||||||
|
("queue", "Queue", "in-queue.html", "ti-stack-2"),
|
||||||
|
("quarantine", "Quarantine", "in-quarantine.html", "ti-mail-pause"),
|
||||||
|
("filter-lists", "Filter lists", "in-filter-lists.html", "ti-filter"),
|
||||||
|
]),
|
||||||
|
("server", "Server", "health.html", "ti-server-2", [
|
||||||
|
("health", "Health", "health.html", "ti-heartbeat"),
|
||||||
|
("preflight", "Preflight", "preflight.html", "ti-checklist"),
|
||||||
|
("system-log", "System log", "system-log.html", "ti-file-text"),
|
||||||
|
("backup", "Backup", "backup.html", "ti-archive"),
|
||||||
|
("users", "Users", "users.html", "ti-users"),
|
||||||
|
("settings", "Settings", "settings.html", "ti-settings"),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
|
||||||
|
HEAD = """<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{title} · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
"""
|
||||||
|
|
||||||
|
FOOT = ('<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a>'
|
||||||
|
' · <a href="#">Source</a> · No warranty</footer>')
|
||||||
|
|
||||||
|
|
||||||
|
def navbar(section, page):
|
||||||
|
out = ['<nav class="navbar is-primary" aria-label="main navigation">', ' <div class="container">',
|
||||||
|
' <div class="navbar-brand"><a class="navbar-item" href="overview.html">'
|
||||||
|
'<img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>',
|
||||||
|
' <div class="navbar-menu is-active">', ' <div class="navbar-start">']
|
||||||
|
for key, label, href, _icon, pages in NAV:
|
||||||
|
cur = " sp-current" if key == section else ""
|
||||||
|
if not pages:
|
||||||
|
out.append(f' <a class="navbar-item{cur}" href="{href}">{label}</a>')
|
||||||
|
continue
|
||||||
|
out.append(f' <div class="navbar-item has-dropdown is-hoverable{cur}">')
|
||||||
|
out.append(f' <a class="navbar-link" href="{href}">{label}</a>')
|
||||||
|
out.append(' <div class="navbar-dropdown">')
|
||||||
|
for pkey, plabel, phref, picon in pages:
|
||||||
|
act = " is-active" if key == section and pkey == page else ""
|
||||||
|
out.append(f' <a class="navbar-item{act}" href="{phref}"><i class="ti {picon}"></i>{plabel}</a>')
|
||||||
|
out.append(' </div>')
|
||||||
|
out.append(' </div>')
|
||||||
|
out += [' </div>', ' <div class="navbar-end">',
|
||||||
|
' <div class="navbar-item has-dropdown is-hoverable%s">' % (" sp-current" if section == "user" else ""),
|
||||||
|
' <a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>',
|
||||||
|
' <div class="navbar-dropdown is-right">',
|
||||||
|
' <a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>',
|
||||||
|
' <a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>',
|
||||||
|
' <hr class="navbar-divider">',
|
||||||
|
' <a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>',
|
||||||
|
' </div>', ' </div>', ' </div>', ' </div>', ' </div>', '</nav>',
|
||||||
|
'<div class="sp-perf" aria-hidden="true"></div>']
|
||||||
|
return "\n".join(out)
|
||||||
|
|
||||||
|
|
||||||
|
def subnav(section, page):
|
||||||
|
for key, _label, _href, _icon, pages in NAV:
|
||||||
|
if key == section and pages:
|
||||||
|
cur = ' aria-current="page"'
|
||||||
|
links = "".join(
|
||||||
|
f'<a href="{phref}"{cur if pkey == page else ""}><i class="ti {picon}"></i>{plabel}</a>'
|
||||||
|
for pkey, plabel, phref, picon in pages)
|
||||||
|
return f'<div class="sp-subnav"><div class="container">{links}</div></div>'
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def build(path):
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
m = re.match(r"\s*<!--(.*?)-->\s*", text, re.S)
|
||||||
|
meta = dict(part.split(":", 1) for part in m.group(1).split("|"))
|
||||||
|
meta = {k.strip(): v.strip() for k, v in meta.items()}
|
||||||
|
body = text[m.end():]
|
||||||
|
html = HEAD.format(title=meta["title"])
|
||||||
|
if meta.get("shell") == "bare":
|
||||||
|
html += f"<body>\n{body}\n</body>\n</html>\n"
|
||||||
|
else:
|
||||||
|
section, page = meta.get("section", ""), meta.get("page", "")
|
||||||
|
html += (f"<body>\n{navbar(section, page)}\n{subnav(section, page)}\n<main>\n"
|
||||||
|
f' <div class="container sp-page">\n{body}\n {FOOT}\n </div>\n</main>\n</body>\n</html>\n')
|
||||||
|
(HERE / path.name).write_text(html, encoding="utf-8", newline="\n")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pages = sorted((HERE / "src").glob("*.html"))
|
||||||
|
for p in pages:
|
||||||
|
build(p)
|
||||||
|
print(f"built {len(pages)} pages")
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
"""Design-contract lint for the panel mockups, and the source of outlines.json.
|
||||||
|
|
||||||
|
The contract (docs/plans/panel-redesign.md) is only worth something if a
|
||||||
|
machine can say "no". This script is that machine for the mockups; the same
|
||||||
|
rules are ported to a Go test over internal/web templates when the redesign is
|
||||||
|
implemented, and that test compares rendered pages with outlines.json.
|
||||||
|
|
||||||
|
python docs/assets/panel-redesign/panel/check.py # lint
|
||||||
|
python docs/assets/panel-redesign/panel/check.py --write # lint + rewrite outlines.json
|
||||||
|
|
||||||
|
Exit code 1 on any violation.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from html.parser import HTMLParser
|
||||||
|
|
||||||
|
HERE = pathlib.Path(__file__).parent
|
||||||
|
VOID = {"input", "img", "br", "hr", "meta", "link"}
|
||||||
|
|
||||||
|
# The subset of Bulma the panel uses. Reaching for a Bulma class outside this
|
||||||
|
# list is a design decision, not an implementation detail: add it here and to
|
||||||
|
# the components page in the same change, or do without it.
|
||||||
|
BULMA = set("""
|
||||||
|
box button buttons checkbox column columns control field file file-cta file-input file-label
|
||||||
|
file-name has-addons has-icons-left has-name has-text-danger help icon input label menu menu-label
|
||||||
|
menu-list notification progress select table tag textarea title
|
||||||
|
is-3 is-4 is-5 is-6 is-7 is-9 is-active is-danger is-expanded is-fullwidth is-gapless is-half
|
||||||
|
is-hoverable is-left is-light is-primary is-size-7 is-small is-success is-text is-warning
|
||||||
|
mb-1 mb-2 mb-3 mb-4 ml-2 mt-4 mt-5
|
||||||
|
""".split())
|
||||||
|
# Classes the shell (build.py, later layout.html) owns; screens never write them.
|
||||||
|
SHELL = {"sp-page", "sp-perf", "sp-subnav", "sp-current", "sp-foot"}
|
||||||
|
# The signed-out screens have no shell, so they carry their own footer.
|
||||||
|
SIGNED_OUT = {"sp-signin", "sp-signin-brand", "sp-signin-form", "sp-foot"}
|
||||||
|
# Selectors allowed to set a width. Everything else gets its width from the
|
||||||
|
# layout it sits in — that is the whole point of the redesign.
|
||||||
|
WIDTH_OK = (".navbar-brand img", ".sp-head .sp-lead", ".sp-head .sp-grow", ".sp-postmark", ".table td.sp-clip",
|
||||||
|
".sp-h-icon", ".sp-signin", ".sp-timeline li::before", ".sp-perf")
|
||||||
|
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
def __init__(self, tag, attrs, parent):
|
||||||
|
self.tag, self.attrs, self.parent, self.kids, self.text = tag, dict(attrs), parent, [], ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def classes(self):
|
||||||
|
return (self.attrs.get("class") or "").split()
|
||||||
|
|
||||||
|
def has(self, cls):
|
||||||
|
return cls in self.classes
|
||||||
|
|
||||||
|
def walk(self):
|
||||||
|
yield self
|
||||||
|
for k in self.kids:
|
||||||
|
yield from k.walk()
|
||||||
|
|
||||||
|
def find(self, cls):
|
||||||
|
return [n for n in self.walk() if n.has(cls)]
|
||||||
|
|
||||||
|
|
||||||
|
class Tree(HTMLParser):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.root = Node("root", [], None)
|
||||||
|
self.cur = self.root
|
||||||
|
|
||||||
|
def handle_starttag(self, tag, attrs):
|
||||||
|
n = Node(tag, attrs, self.cur)
|
||||||
|
self.cur.kids.append(n)
|
||||||
|
if tag not in VOID:
|
||||||
|
self.cur = n
|
||||||
|
|
||||||
|
def handle_endtag(self, tag):
|
||||||
|
n = self.cur
|
||||||
|
while n is not self.root and n.tag != tag:
|
||||||
|
n = n.parent
|
||||||
|
if n is not self.root:
|
||||||
|
self.cur = n.parent
|
||||||
|
|
||||||
|
def handle_data(self, data):
|
||||||
|
self.cur.text += data
|
||||||
|
|
||||||
|
|
||||||
|
def parse(text):
|
||||||
|
t = Tree()
|
||||||
|
t.feed(text)
|
||||||
|
return t.root
|
||||||
|
|
||||||
|
|
||||||
|
def text_of(node):
|
||||||
|
return re.sub(r"\s+", " ", "".join(n.text for n in node.walk())).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def css_rules(css):
|
||||||
|
css = re.sub(r"/\*.*?\*/", "", css, flags=re.S)
|
||||||
|
css = re.sub(r"@media[^{]*\{", "", css)
|
||||||
|
for sel, body in re.findall(r"([^{}]+)\{([^{}]*)\}", css):
|
||||||
|
yield sel.strip(), body
|
||||||
|
|
||||||
|
|
||||||
|
def box_kind(box):
|
||||||
|
kinds = []
|
||||||
|
for k in box.kids:
|
||||||
|
if k.has("sp-box-head") or k.has("sp-box-foot"):
|
||||||
|
continue
|
||||||
|
for cls, name in (("sp-record", "record"), ("sp-health", "health"), ("sp-empty", "empty"),
|
||||||
|
("sp-log", "log"), ("sp-help-topic", "help"), ("sp-box-body", "body")):
|
||||||
|
if k.has(cls):
|
||||||
|
kinds.append(name)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
kinds.append("table" if k.tag == "table" else k.tag)
|
||||||
|
out = []
|
||||||
|
for k in kinds:
|
||||||
|
if not out or out[-1].split("×")[0] != k:
|
||||||
|
out.append(k)
|
||||||
|
else:
|
||||||
|
n = int(out[-1].split("×")[1]) + 1 if "×" in out[-1] else 2
|
||||||
|
out[-1] = f"{k}×{n}"
|
||||||
|
return "+".join(out)
|
||||||
|
|
||||||
|
|
||||||
|
def outline(node):
|
||||||
|
"""The component skeleton of a page: what a reviewer sees with the text blurred."""
|
||||||
|
out = []
|
||||||
|
for k in node.kids:
|
||||||
|
if k.has("sp-head"):
|
||||||
|
parts = ["head"] + [p for p, c in (("postmark", "sp-postmark"), ("actions", "buttons")) if k.find(c)]
|
||||||
|
if not k.find("buttons") and any(n.tag in ("a", "form") and n.parent is k for n in k.walk()):
|
||||||
|
parts.append("actions")
|
||||||
|
out.append("+".join(parts))
|
||||||
|
elif k.has("box"):
|
||||||
|
variant = next((v for v in ("sp-danger-zone", "sp-credential") if k.has(v)), "")
|
||||||
|
head = k.find("sp-box-head")
|
||||||
|
title = text_of(head[0].find("sp-no")[0].parent.kids[1]) if head else "?"
|
||||||
|
out.append(f"box{'.' + variant if variant else ''}[{title}]:{box_kind(k)}")
|
||||||
|
elif k.has("columns"):
|
||||||
|
cols = []
|
||||||
|
for c in k.kids:
|
||||||
|
size = next((x for x in c.classes if re.fullmatch(r"is-\d+", x)), "auto")
|
||||||
|
cols.append({size: outline(c)})
|
||||||
|
out.append({"columns": cols})
|
||||||
|
elif k.tag == "form" or k.has("menu") or k.tag == "aside":
|
||||||
|
out.append({"form": outline(k)} if k.tag == "form" else "side_menu")
|
||||||
|
elif k.has("buttons"):
|
||||||
|
out.append("submit_row")
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
errors = []
|
||||||
|
css = (HERE / "theme.css").read_text(encoding="utf-8")
|
||||||
|
defined = set(re.findall(r"\.(sp-[a-z0-9-]+)", css))
|
||||||
|
|
||||||
|
for sel, body in css_rules(css):
|
||||||
|
if "!important" in body:
|
||||||
|
errors.append(f"theme.css: !important in `{sel}`")
|
||||||
|
if re.search(r"(^|[\s,>+~])#[a-z]", sel):
|
||||||
|
errors.append(f"theme.css: id selector `{sel}` — style components, not one element on one page")
|
||||||
|
if re.search(r"(?<![-\w])(max-width|min-width|width)\s*:", body) and not any(sel.startswith(w) or w in sel for w in WIDTH_OK):
|
||||||
|
errors.append(f"theme.css: `{sel}` sets a width; widths come from the page layout (WIDTH_OK)")
|
||||||
|
|
||||||
|
used, documented, outlines = set(), set(), {}
|
||||||
|
for src in sorted((HERE / "src").glob("*.html")):
|
||||||
|
text = src.read_text(encoding="utf-8")
|
||||||
|
name = src.stem
|
||||||
|
bare = "shell: bare" in text.split("-->")[0]
|
||||||
|
for bad in ("style=", "<style", "<script", " onclick=", " id=\"page-"):
|
||||||
|
if bad in text:
|
||||||
|
errors.append(f"{name}: `{bad}` is forbidden in a screen")
|
||||||
|
root = parse(text)
|
||||||
|
for n in root.walk():
|
||||||
|
for c in n.classes:
|
||||||
|
if c.startswith("sp-"):
|
||||||
|
(documented if name == "components" else used).add(c)
|
||||||
|
if c in SHELL and not (bare and c in SIGNED_OUT):
|
||||||
|
errors.append(f"{name}: `{c}` belongs to the shell")
|
||||||
|
elif c not in defined:
|
||||||
|
errors.append(f"{name}: `{c}` is not defined in theme.css")
|
||||||
|
elif c == "ti" or c.startswith("ti-"):
|
||||||
|
continue
|
||||||
|
elif c not in BULMA:
|
||||||
|
errors.append(f"{name}: class `{c}` is neither in the Bulma subset nor an sp- component")
|
||||||
|
if bare:
|
||||||
|
continue
|
||||||
|
top = [k for k in root.kids if k.tag != "root"]
|
||||||
|
if not top or not top[0].has("sp-head"):
|
||||||
|
errors.append(f"{name}: the page must open with page_head (.sp-head)")
|
||||||
|
if len([n for n in root.walk() if n.tag == "h1"]) != 1:
|
||||||
|
errors.append(f"{name}: exactly one <h1>")
|
||||||
|
marks = root.find("sp-postmark")
|
||||||
|
if name != "components" and (len(marks) > 1 or any(not m.parent.has("sp-head") for m in marks)):
|
||||||
|
errors.append(f"{name}: at most one postmark, inside page_head")
|
||||||
|
for box in root.find("box"):
|
||||||
|
if not box.kids or not box.kids[0].has("sp-box-head"):
|
||||||
|
errors.append(f"{name}: a box must start with box_head")
|
||||||
|
for k in top[1:]:
|
||||||
|
# A flash is conditional, so it is allowed at the top level but never part of an outline.
|
||||||
|
ok = k.has("box") or k.has("columns") or k.has("notification") or (k.tag == "form" and k.has("sp-form"))
|
||||||
|
if not ok:
|
||||||
|
errors.append(f"{name}: top-level <{k.tag} class=\"{' '.join(k.classes)}\"> — a page is boxes, columns or one form")
|
||||||
|
if name != "components":
|
||||||
|
outlines[name] = outline(root)
|
||||||
|
|
||||||
|
for c in sorted(used - documented - SIGNED_OUT):
|
||||||
|
errors.append(f"components page does not show `{c}` — document it or stop using it")
|
||||||
|
shell_used = set(re.findall(r"sp-[a-z0-9-]+", (HERE / "build.py").read_text(encoding="utf-8")))
|
||||||
|
for c in sorted(defined - used - documented - shell_used):
|
||||||
|
errors.append(f"theme.css defines `{c}` but nothing uses it")
|
||||||
|
|
||||||
|
if "--write" in sys.argv and not errors:
|
||||||
|
(HERE / "outlines.json").write_text(json.dumps(outlines, ensure_ascii=False, indent=1) + "\n",
|
||||||
|
encoding="utf-8", newline="\n")
|
||||||
|
golden = HERE / "outlines.json"
|
||||||
|
if golden.exists() and "--write" not in sys.argv:
|
||||||
|
if json.loads(golden.read_text(encoding="utf-8")) != outlines:
|
||||||
|
errors.append("outlines.json is stale — a screen's structure changed; re-run with --write and say so in the commit")
|
||||||
|
|
||||||
|
for e in errors:
|
||||||
|
print("FAIL", e)
|
||||||
|
print(f"{len(outlines) + 1} screens, {len(used | documented)} sp- classes, {len(errors)} violation(s)")
|
||||||
|
return 1 if errors else 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Components · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<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="notification is-success is-light"><i class="ti ti-circle-check"></i><strong>flash</strong> — the result of the last action, between page_head and the first box. Success, or…</div>
|
||||||
|
<div class="notification is-danger is-light"><i class="ti ti-alert-circle"></i>…<strong>flash, error</strong> — a failure that belongs to the page, not to one field. A field's error stays under the field in Bulma's <code>help is-danger</code>.</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>held: quarantine · role: global</td><td><span class="tag is-primary is-light">quarantine</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><a class="sp-help" href="help.html#checks" title="Optional: the section's Help topic"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<div class="sp-box-body"><p class="mb-2">The <i class="ti ti-help-circle"></i> after the end slot is <code>sp-help</code>: an optional link to the section's topic on the Help page. It replaces today's help drawer.</p><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>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>shop.example.org · DMARC reports · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item is-active" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html" aria-current="page"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<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>
|
||||||
|
<p class="sp-lead">11 204 pass · 41 fail in the last 7 days. Failures come from a source that is not this relay — review before tightening <code>p=</code>.</p>
|
||||||
|
</div>
|
||||||
|
<a class="button" href="out-domain.html"><i class="ti ti-world-upload"></i>Domain page</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Sources · 7 days</h2></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Source</th><th>Pass</th><th>Fail</th><th>Disposition</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">203.0.113.25 <span class="tag is-light">this relay</span></td><td>11 204</td><td>0</td><td><span class="tag is-success is-light">none</span></td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">198.51.100.44</td><td>0</td><td>41</td><td><span class="tag is-warning is-light">none</span></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Reports</h2></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Received, UTC</th><th>Reporter</th><th>Window</th><th>Pass</th><th>Fail</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 04:40</td><td class="sp-muted">Outlook.com</td><td>20 Sep</td><td>1 903</td><td><span class="tag is-warning is-light">7</span></td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 02:15</td><td class="sp-muted">google.com</td><td>20 Sep</td><td>6 410</td><td>0</td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-20 04:38</td><td class="sp-muted">Outlook.com</td><td>19 Sep</td><td>1 877</td><td><span class="tag is-warning is-light">34</span></td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Outlook.com · 20 Sep · DMARC · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item is-active" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html" aria-current="page"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="dmarc.html">DMARC reports</a> / <a href="dmarc-domain.html">shop.example.org</a> /</p>
|
||||||
|
<h1 class="title is-4">Outlook.com · 20 Sep 2026</h1>
|
||||||
|
<p class="sp-route"><span>shop.example.org</span><span class="tag is-success is-light">1 903 pass</span><span class="tag is-warning is-light">7 fail</span></p>
|
||||||
|
</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>Report</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Reporter</dt><dd>Outlook.com</dd></div>
|
||||||
|
<div><dt>Report id</dt><dd class="sp-mono is-size-7">5f1c9a0e7b2d4e61a3</dd></div>
|
||||||
|
<div><dt>Window</dt><dd>20 Sep 00:00 – 21 Sep 00:00 UTC</dd></div>
|
||||||
|
<div><dt>Received</dt><dd>2026-09-21 04:40 UTC</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Published policy</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>p / sp / pct</dt><dd class="sp-mono">none / none / 100</dd></div>
|
||||||
|
<div><dt>adkim / aspf</dt><dd class="sp-mono">r / r</dd></div>
|
||||||
|
<div><dt>rua</dt><dd class="sp-mono is-size-7">dmarc@mail.example.org</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Records</h2><div class="sp-end">Parsed from the aggregate XML</div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Source</th><th>Count</th><th>Disposition</th><th>SPF</th><th>DKIM</th><th>Header from</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">203.0.113.25 <span class="tag is-light">this relay</span></td><td>1 903</td><td><span class="tag is-success is-light">none</span></td><td><span class="tag is-success is-light">pass</span></td><td><span class="tag is-success is-light">pass</span></td><td>shop.example.org</td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">198.51.100.44</td><td>7</td><td><span class="tag is-warning is-light">none</span></td><td><span class="tag is-danger is-light">fail</span></td><td><span class="tag is-danger is-light">fail</span></td><td>shop.example.org</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>DMARC reports · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item is-active" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html" aria-current="page"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Outbound</p>
|
||||||
|
<h1 class="title is-3">DMARC reports</h1>
|
||||||
|
<p class="sp-lead">How receivers saw mail claiming to be from your domains. Aggregate (<code>rua=</code>) only; open a report for the parsed XML.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Ingest</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Last report</dt><dd>2026-09-21 06:12 UTC</dd></div>
|
||||||
|
<div><dt>This week</dt><dd>14 kept · 0 parse failures</dd></div>
|
||||||
|
<div><dt>Hosted address</dt><dd class="sp-mono">dmarc@mail.example.org</dd></div>
|
||||||
|
<div><dt>Retention</dt><dd>500 reports or 90 days</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Recent reports</h2></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Received, UTC</th><th>Domain</th><th>Reporter</th><th>Window</th><th>Pass</th><th>Fail</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 06:12</td><td><a href="dmarc-domain.html">example.org</a></td><td class="sp-muted">google.com</td><td>20 Sep</td><td>412</td><td>0</td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 04:40</td><td><a href="dmarc-domain.html">shop.example.org</a></td><td class="sp-muted">Outlook.com</td><td>20 Sep</td><td>1 903</td><td><span class="tag is-warning is-light">7</span></td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-20 23:58</td><td><a href="dmarc-domain.html">example.org</a></td><td class="sp-muted">Yahoo</td><td>19 Sep</td><td>38</td><td>0</td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-20 06:09</td><td><a href="dmarc-domain.html">notify.acme.io</a></td><td class="sp-muted">google.com</td><td>19 Sep</td><td>61</td><td>0</td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Health · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="health.html" aria-current="page"><i class="ti ti-heartbeat"></i>Health</a><a href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a><a href="system-log.html"><i class="ti ti-file-text"></i>System log</a><a href="backup.html"><i class="ti ti-archive"></i>Backup</a><a href="users.html"><i class="ti ti-users"></i>Users</a><a href="settings.html"><i class="ti ti-settings"></i>Settings</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Health</h1>
|
||||||
|
<p class="sp-lead">Everything behind the six cards on Overview. Refreshes every 10 s.</p>
|
||||||
|
</div>
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-refresh"></i>Re-check DNS</button></form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="machine">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Machine</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Resource</th><th>Usage</th><th>Detail</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>CPU</td><td><progress class="progress is-small is-success mb-1" value="12" max="100"></progress>12 %</td><td class="sp-muted">2 cores, load 0.21</td></tr>
|
||||||
|
<tr><td>Memory</td><td><progress class="progress is-small is-success mb-1" value="41" max="100"></progress>41 %</td><td class="sp-muted">812 MB of 1.9 GB</td></tr>
|
||||||
|
<tr><td>Network</td><td class="sp-nowrap">↓ 18 kB/s · ↑ 44 kB/s</td><td class="sp-muted">eth0: 2.1 GB in, 6.4 GB out</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="processes">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Processes</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Program</th><th>State</th><th>Detail</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>postfix</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 41, uptime 18 days</td></tr>
|
||||||
|
<tr><td>opendkim</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 39, uptime 18 days</td></tr>
|
||||||
|
<tr><td>journal-milter</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 40, uptime 18 days</td></tr>
|
||||||
|
<tr><td>panel</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 44, uptime 18 days</td></tr>
|
||||||
|
<tr><td>dmarc-ingest</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 47, uptime 18 days</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box" id="certificate">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>TLS certificate</h2><div class="sp-end"><span class="tag is-warning is-light">warn</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<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">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>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box" id="sockets">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">04</span><h2>Milter sockets</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Milter</th><th>State</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>opendkim<br><span class="sp-muted sp-mono is-size-7">inet:127.0.0.1:8891</span></td><td><span class="tag is-success is-light">ok</span></td></tr>
|
||||||
|
<tr><td>journal-milter<br><span class="sp-muted sp-mono is-size-7">unix:/run/selfpost/journal.sock</span></td><td><span class="tag is-success is-light">ok</span></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box" id="hostname">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">05</span><h2>Hostname and reverse DNS</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Hostname</dt><dd class="sp-mono">mail.example.org</dd></div>
|
||||||
|
<div><dt>Lookup</dt><dd class="sp-mono">→ 203.0.113.25<br>← mail.example.org</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="configuration">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">06</span><h2>Configuration</h2><div class="sp-end">Safe to run at any time</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">Regenerates the OpenDKIM and Postfix configuration from the database 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.</p>
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-reload"></i>Reload configuration</button></form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Help · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Reference</p>
|
||||||
|
<h1 class="title is-3">Help</h1>
|
||||||
|
<p class="sp-lead">What each screen checks and what to do when it is not green. Every numbered section on a page links to its topic here.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<aside class="menu sp-side">
|
||||||
|
<p class="menu-label">Overview</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="is-active" href="#checks"><i class="ti ti-activity-heartbeat"></i>Health checks</a></li>
|
||||||
|
<li><a href="#certificate"><i class="ti ti-certificate"></i>TLS certificate</a></li>
|
||||||
|
<li><a href="#rdns"><i class="ti ti-arrows-exchange"></i>Reverse DNS</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Outbound</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="#dns"><i class="ti ti-world-check"></i>DNS records</a></li>
|
||||||
|
<li><a href="#apps"><i class="ti ti-apps"></i>Applications</a></li>
|
||||||
|
<li><a href="#limits"><i class="ti ti-gauge"></i>Rate limits</a></li>
|
||||||
|
<li><a href="#queue"><i class="ti ti-stack-2"></i>Queue and retries</a></li>
|
||||||
|
<li><a href="#dmarc"><i class="ti ti-report-analytics"></i>DMARC reports</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Inbound</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="#inbound"><i class="ti ti-inbox"></i>Backup-MX and forwarding</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<div class="column is-9">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Overview</h2></div>
|
||||||
|
<div class="sp-help-topic" id="checks"><h3>Health checks</h3><p>Six checks, the worst of them is the postmark. <span class="tag is-success is-light">ok</span> needs nothing; <span class="tag is-warning is-light">warn</span> still delivers mail but will stop doing so if ignored; <span class="tag is-danger is-light">fail</span> means mail is being lost or refused now.</p></div>
|
||||||
|
<div class="sp-help-topic" id="certificate"><h3>TLS certificate</h3><p>The certificate Postfix presents on 465 and 587. It warns 14 days before expiry and fails once expired — clients that verify certificates then refuse to connect.</p></div>
|
||||||
|
<div class="sp-help-topic" id="rdns"><h3>Hostname and reverse DNS</h3><p>The hostname must resolve to this server's address, and that address must resolve back to the hostname. Large receivers reject or spam-folder mail from hosts where the two disagree. The PTR record is set at your hosting provider, not in your DNS zone.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Outbound</h2></div>
|
||||||
|
<div class="sp-help-topic" id="dns"><h3>DNS records</h3><p>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.</p></div>
|
||||||
|
<div class="sp-help-topic" id="apps"><h3>Applications</h3><p>An application is one SASL login. It may send as any address of its domain or only as listed addresses. The password is shown once at creation or regeneration and is not stored in readable form.</p></div>
|
||||||
|
<div class="sp-help-topic" id="limits"><h3>Rate limits</h3><p>Level 1 is a per-IP ceiling set in <code>.env</code>. Level 2 is optional, per domain and per application, manual or computed from 30-day statistics, and can never exceed level 1.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Delete lists.example.org · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="in-domains.html" aria-current="page"><i class="ti ti-world-download"></i>Domains</a><a href="in-log.html"><i class="ti ti-list-details"></i>Log</a><a href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a><a href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="in-domains.html">Inbound domains</a> / <a href="in-domain.html">lists.example.org</a> /</p>
|
||||||
|
<h1 class="title is-3">Delete lists.example.org</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<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>
|
||||||
|
<ul class="sp-list-check">
|
||||||
|
<li><i class="ti ti-x"></i>stop accepting mail for it on port 25 — senders get a relay-denied reply;</li>
|
||||||
|
<li><i class="ti ti-x"></i>remove its upstream and its 4 listed recipients.</li>
|
||||||
|
</ul>
|
||||||
|
<p class="sp-muted mb-4">Remove or repoint the MX record first, or mail for the domain will bounce. Outbound domains are not touched.</p>
|
||||||
|
<form method="post" action="in-domains.html">
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete lists.example.org</button>
|
||||||
|
<a class="button is-text" href="in-domain.html">Keep it</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>lists.example.org · Inbound domains · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="in-domains.html" aria-current="page"><i class="ti ti-world-download"></i>Domains</a><a href="in-log.html"><i class="ti ti-list-details"></i>Log</a><a href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a><a href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-postmark"><span>DNS</span><b>OK</b><span>MX POINTS HERE</span></div>
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="in-domains.html">Inbound domains</a> /</p>
|
||||||
|
<h1 class="title is-3">lists.example.org</h1>
|
||||||
|
<p class="sp-lead">Accepted on port 25, handed to <span class="sp-mono">mx.internal:25</span> over required TLS · 4 recipients</p>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-refresh"></i>Re-check DNS</button></form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<aside class="menu sp-side">
|
||||||
|
<p class="menu-label">This domain</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="is-active" href="#dns"><i class="ti ti-world-check"></i>MX record <span class="tag is-success is-light">ok</span></a></li>
|
||||||
|
<li><a href="#upstream"><i class="ti ti-arrow-forward-up"></i>Upstream</a></li>
|
||||||
|
<li><a href="#recipients"><i class="ti ti-address-book"></i>Recipients <span class="tag is-light">4</span></a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">See also</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="in-log.html"><i class="ti ti-list-details"></i>Log for this domain</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Rarely changed</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="has-text-danger" href="in-domain-delete.html"><i class="ti ti-trash has-text-danger"></i>Delete domain</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-9">
|
||||||
|
<div class="box" id="dns">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>MX record</h2><div class="sp-end">Checked 1 min ago</div><a class="sp-help" href="help.html#inbound" title="Backup-MX and forwarding"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>MX</h3><span class="tag is-success is-light">published</span><small>Keep any existing primary MX if this is a backup-MX</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="lists.example.org"></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="MX"></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label class="label">Value to publish</label>
|
||||||
|
<div class="field has-addons"><div class="control is-expanded"><input class="input" readonly value="10 mail.example.org."></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="upstream">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Upstream</h2><div class="sp-end">Not a mailbox</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="sp-row">
|
||||||
|
<div class="field"><label class="label" for="host">Host</label><div class="control"><input class="input sp-mono" id="host" name="host" value="mx.internal" autocomplete="off" spellcheck="false" required></div></div>
|
||||||
|
<div class="field"><label class="label" for="port">Port</label><div class="control"><input class="input sp-mono" id="port" name="port" inputmode="numeric" value="25" required></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label class="label" for="tls">TLS to upstream</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="tls" name="tls_mode"><option>Opportunistic</option><option selected>Required</option><option>Off</option></select></div></div></div>
|
||||||
|
<button type="submit" class="button is-primary">Save upstream</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="recipients">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Valid recipients</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="rmode">Who to accept</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="rmode" name="recipient_mode"><option selected>Listed addresses only</option><option>Any recipient at this domain</option></select></div></div></div>
|
||||||
|
<div class="field"><label class="label" for="addresses">Addresses</label>
|
||||||
|
<div class="control"><textarea class="textarea sp-mono" id="addresses" name="addresses" rows="4">announce@lists.example.org
|
||||||
|
dev@lists.example.org
|
||||||
|
owner@lists.example.org
|
||||||
|
security@lists.example.org</textarea></div>
|
||||||
|
<p class="help">Unknown recipients are rejected at RCPT, so this relay generates no backscatter. Prefer a list unless the upstream itself rejects unknowns.</p></div>
|
||||||
|
<button type="submit" class="button is-primary">Save recipients</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Inbound domains · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="in-domains.html" aria-current="page"><i class="ti ti-world-download"></i>Domains</a><a href="in-log.html"><i class="ti ti-list-details"></i>Log</a><a href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a><a href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound</p>
|
||||||
|
<h1 class="title is-3">Inbound domains</h1>
|
||||||
|
<p class="sp-lead">Backup-MX / forwarder. Port 25 accepts mail only for the domains listed here and hands it to their upstream. No mailboxes.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Add a domain</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="in-domain.html">
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded has-icons-left"><input class="input" name="name" type="text" placeholder="lists.example.com" autocomplete="off" autocapitalize="none" spellcheck="false" required><span class="icon is-left"><i class="ti ti-world"></i></span></div>
|
||||||
|
<div class="control"><button type="submit" class="button is-primary"><i class="ti ti-plus"></i>Add domain</button></div>
|
||||||
|
</div>
|
||||||
|
<p class="help">Mail is not accepted until an upstream host is saved on the domain's page. Adding and deleting domains is for a global administrator; a domain administrator sees the list without this form and without Delete.</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Domains</h2><div class="sp-end">2 domains · DNS cached a few minutes</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>DNS</th><th>Upstream</th><th>Recipients</th><th>TLS to upstream</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="in-domain.html"><strong>lists.example.org</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-mono sp-muted">mx.internal:25</td><td>4 addresses</td><td><span class="tag is-success is-light">required</span></td><td class="sp-actions"><a href="in-domain.html">Open</a><a class="has-text-danger" href="in-domain-delete.html">Delete</a></td></tr>
|
||||||
|
<tr><td><a href="in-domain.html"><strong>acme.io</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-mono sp-muted">10.0.4.12:2525</td><td>any address</td><td><span class="tag is-light">opportunistic</span></td><td class="sp-actions"><a href="in-domain.html">Open</a><a class="has-text-danger" href="in-domain-delete.html">Delete</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot">The MX tag is green when at least one MX of the domain points at this server.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Filter lists · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item is-active" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a><a href="in-log.html"><i class="ti ti-list-details"></i>Log</a><a href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a><a href="in-filter-lists.html" aria-current="page"><i class="ti ti-filter"></i>Filter lists</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound · global administrator</p>
|
||||||
|
<h1 class="title is-3">Filter lists</h1>
|
||||||
|
<p class="sp-lead">Correct the spam filter by hand: senders it must always let through, and senders it must always turn away. One pair of lists for the whole instance.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Add an entry</h2><a class="sp-help" href="help.html#inbound" title="How the lists are applied"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<div class="field"><label class="label" for="list">List</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="list" name="list_type"><option>Deny — always turn away</option><option>Allow — always let through</option></select></div></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-3">
|
||||||
|
<div class="field"><label class="label" for="etype">Entry is</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="etype" name="entry_type"><option>a sender address</option><option>a sender domain</option><option>a client IP or CIDR</option></select></div></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="field"><label class="label" for="value">Value</label>
|
||||||
|
<div class="control"><input class="input sp-mono" id="value" name="value" placeholder="promo@bulk.example" autocomplete="off" autocapitalize="none" spellcheck="false" required></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label class="label" for="note">Note</label>
|
||||||
|
<div class="control"><input class="input" id="note" name="note" placeholder="Why — for whoever reads this list next year"></div></div>
|
||||||
|
<button type="submit" class="button is-primary"><i class="ti ti-plus"></i>Add entry</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Deny</h2><div class="sp-end">3 entries · wins over Allow</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Value</th><th>Note</th><th>Added</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">@bulk.example <span class="tag is-light">domain</span></td><td class="sp-muted">newsletter nobody subscribed to</td><td class="sp-muted sp-nowrap is-size-7">2026-09-02</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">198.51.100.0/24 <span class="tag is-light">ip</span></td><td class="sp-muted">dictionary attack on lists.example.org</td><td class="sp-muted sp-nowrap is-size-7">2026-08-19</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">win@lottery.example <span class="tag is-light">address</span></td><td class="sp-muted">—</td><td class="sp-muted sp-nowrap is-size-7">2026-07-30</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Allow</h2><div class="sp-end">2 entries</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Value</th><th>Note</th><th>Added</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">@partner.example <span class="tag is-light">domain</span></td><td class="sp-muted">invoices were scored as spam</td><td class="sp-muted sp-nowrap is-size-7">2026-09-11</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">noreply@github.example <span class="tag is-light">address</span></td><td class="sp-muted">security advisories</td><td class="sp-muted sp-nowrap is-size-7">2026-06-04</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">04</span><h2>Where the lists go</h2><div class="sp-end"><span class="tag is-success is-light">applied</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Applied by</dt><dd>rspamd, from map files</dd></div>
|
||||||
|
<div><dt>Files</dt><dd class="sp-mono is-size-7">/data/antispam/allow.map<br>/data/antispam/deny.map</dd></div>
|
||||||
|
<div><dt>Last written</dt><dd>2026-09-11 08:40 UTC</dd></div>
|
||||||
|
<div><dt>When the filter is off</dt><dd class="sp-muted">The lists are kept and editable, but nothing reads them until <code>INBOUND_ANTISPAM_MILTER</code> is set.</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Inbound log · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item is-active" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a><a href="in-log.html" aria-current="page"><i class="ti ti-list-details"></i>Log</a><a href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a><a href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound</p>
|
||||||
|
<h1 class="title is-3">Inbound log</h1>
|
||||||
|
<p class="sp-lead">What port 25 decided about each message, one row per recipient. Accepts come from the inbound journal, rejects from <code>mail.log</code>; the filter's reasons appear when the spam filter is on.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head">
|
||||||
|
<span class="sp-no">01</span><h2>Messages</h2>
|
||||||
|
<form class="sp-end" method="get" action="#">
|
||||||
|
<div class="select is-small"><select name="domain"><option>All domains</option><option>lists.example.org</option><option>acme.io</option></select></div>
|
||||||
|
<div class="select is-small"><select name="decision"><option>Any decision</option><option>accept</option><option>reject</option><option>tempfail</option><option>quarantine</option></select></div>
|
||||||
|
<input class="input is-small" type="date" name="from" value="2026-09-14" aria-label="From date">
|
||||||
|
<input class="input is-small" type="date" name="to" value="2026-09-21" aria-label="To date">
|
||||||
|
<button type="submit" class="button is-small"><i class="ti ti-filter"></i>Filter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Time, UTC</th><th>Client</th><th>From</th><th>To</th><th>Subject</th><th>Decision</th><th>Why</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 14:00:18</td><td class="sp-mono is-size-7">192.0.2.77</td><td>t.berg@uni.example</td><td>dev@lists.example.org</td><td class="sp-clip">Re: [dev] patch series v3</td><td><span class="tag is-success is-light">accept</span></td><td class="sp-muted is-size-7">rspamd · score 0.4</td><td class="sp-actions"><a href="in-filter-lists.html">Deny sender</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:59:02</td><td class="sp-mono is-size-7">203.0.113.140</td><td>invoice@partner-billing.example</td><td>owner@lists.example.org</td><td class="sp-clip">Outstanding invoice — action required</td><td><span class="tag is-primary is-light">quarantine</span></td><td class="sp-muted is-size-7">rspamd · score 9.8 · PHISHING, FORGED_SENDER</td><td class="sp-actions"><a href="in-quarantine-message.html">Review</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:57:41</td><td class="sp-mono is-size-7">198.51.100.90</td><td>promo@bulk.example</td><td>sales@lists.example.org</td><td class="sp-clip sp-muted">—</td><td><span class="tag is-danger is-light">reject</span></td><td class="sp-muted is-size-7">postfix · 550 5.1.1 unknown recipient</td><td class="sp-actions"><a href="in-filter-lists.html">Deny sender</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:51:30</td><td class="sp-mono is-size-7">198.51.100.12</td><td>billing@partner.example</td><td>owner@lists.example.org</td><td class="sp-clip">Invoice 2026-0917</td><td><span class="tag is-danger is-light">reject</span></td><td class="sp-muted is-size-7">rspamd · score 16.2 · BAYES_SPAM</td><td class="sp-actions"><a href="in-filter-lists.html">Allow sender</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:44:02</td><td class="sp-mono is-size-7">192.0.2.31</td><td>hr@partner.example</td><td>jobs@acme.io</td><td class="sp-clip">Application: backend engineer</td><td><span class="tag is-warning is-light">tempfail</span></td><td class="sp-muted is-size-7">selfpost · upstream 10.0.4.12 timed out</td><td class="sp-actions"></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:30:55</td><td class="sp-mono is-size-7">192.0.2.200</td><td>noreply@github.example</td><td>security@lists.example.org</td><td class="sp-clip">[advisory] new report</td><td><span class="tag is-success is-light">accept</span></td><td class="sp-muted is-size-7">rspamd · allow list</td><td class="sp-actions"></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot"><span>Page 1 of 38</span><a href="#">Older →</a><span class="sp-end">Allow / Deny sender opens <a href="in-filter-lists.html">Filter lists</a> with the entry filled in · global administrator only</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Held message · Quarantine · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item is-active" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a><a href="in-log.html"><i class="ti ti-list-details"></i>Log</a><a href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="in-quarantine.html" aria-current="page"><i class="ti ti-mail-pause"></i>Quarantine</a><a href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="in-quarantine.html">Quarantine</a> /</p>
|
||||||
|
<h1 class="title is-4">Outstanding invoice — action required</h1>
|
||||||
|
<p class="sp-route"><span>invoice@partner-billing.example</span><i class="ti ti-arrow-right sp-muted"></i><span>owner@lists.example.org</span><span class="tag is-primary is-light">quarantine</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Message</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Inbound domain</dt><dd><a href="in-domain.html">lists.example.org</a></dd></div>
|
||||||
|
<div><dt>Client</dt><dd class="sp-mono">203.0.113.140</dd></div>
|
||||||
|
<div><dt>Held</dt><dd>2026-09-21 13:59:02 UTC</dd></div>
|
||||||
|
<div><dt>Deleted on</dt><dd>2026-10-05 · in 14 days</dd></div>
|
||||||
|
<div><dt>Size</dt><dd>48 kB · 1 attachment (<span class="sp-mono">invoice.html</span>)</dd></div>
|
||||||
|
<div><dt>Would go to</dt><dd class="sp-mono">mx.internal:25</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Why it was held</h2><div class="sp-end">rspamd · score 9.8</div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Symbol</th><th>Score</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">PHISHING</td><td>5.0</td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">FORGED_SENDER</td><td>3.0</td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">HTML_ATTACHMENT</td><td>1.8</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Headers</h2><div class="sp-end">The body and attachments are never rendered in the panel</div></div>
|
||||||
|
<pre class="sp-log"><span class="sp-d">Received:</span> from mail.partner-billing.example (unknown [203.0.113.140]) by mail.example.org
|
||||||
|
<span class="sp-d">From:</span> "Partner Billing" <invoice@partner-billing.example>
|
||||||
|
<span class="sp-d">Reply-To:</span> <span class="sp-w">payments.dept@freemail.example</span>
|
||||||
|
<span class="sp-d">To:</span> owner@lists.example.org
|
||||||
|
<span class="sp-d">Subject:</span> Outstanding invoice — action required
|
||||||
|
<span class="sp-d">Date:</span> Mon, 21 Sep 2026 13:58:57 +0000
|
||||||
|
<span class="sp-d">Authentication-Results:</span> <span class="sp-e">spf=fail dkim=none dmarc=fail</span></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">04</span><h2>Release</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">Hands the message to <span class="sp-mono">mx.internal:25</span> unchanged, as if it had just arrived. It leaves the quarantine.</p>
|
||||||
|
<form class="sp-form" method="post" action="in-quarantine.html">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="allow_sender"> Also add <span class="sp-mono">invoice@partner-billing.example</span> to the Allow list</label></div>
|
||||||
|
<button type="submit" class="button is-primary"><i class="ti ti-send"></i>Release to upstream</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box sp-danger-zone">
|
||||||
|
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-alert-triangle"></i></span><h2>Discard</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">Deletes the message now instead of in 14 days. The sender is not told. This cannot be undone.</p>
|
||||||
|
<form class="sp-form" method="post" action="in-quarantine.html">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="deny_sender" checked> Also add <span class="sp-mono">@partner-billing.example</span> to the Deny list</label></div>
|
||||||
|
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Discard message</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Quarantine · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item is-active" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a><a href="in-log.html"><i class="ti ti-list-details"></i>Log</a><a href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="in-quarantine.html" aria-current="page"><i class="ti ti-mail-pause"></i>Quarantine</a><a href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound</p>
|
||||||
|
<h1 class="title is-3">Quarantine</h1>
|
||||||
|
<p class="sp-lead">Mail the filter found suspicious enough to hold, but not to refuse. Release hands a message to its upstream as if it had just arrived; Discard deletes it. Anything left is deleted after 14 days.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head">
|
||||||
|
<span class="sp-no">01</span><h2>Held messages</h2>
|
||||||
|
<form class="sp-end" method="get" action="#">
|
||||||
|
<div class="select is-small"><select name="domain"><option>All domains</option><option>lists.example.org</option><option>acme.io</option></select></div>
|
||||||
|
<button type="submit" class="button is-small"><i class="ti ti-filter"></i>Filter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Held, UTC</th><th>From</th><th>To</th><th>Subject</th><th>Why</th><th>Size</th><th>Deleted in</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:59:02</td><td>invoice@partner-billing.example</td><td>owner@lists.example.org</td><td class="sp-clip"><a href="in-quarantine-message.html"><strong>Outstanding invoice — action required</strong></a></td><td class="sp-muted is-size-7">score 9.8 · PHISHING</td><td class="sp-muted sp-nowrap">48 kB</td><td class="sp-nowrap">14 days</td><td class="sp-actions"><a href="in-quarantine-message.html">Review</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-20 22:14:40</td><td>no-reply@shipping-update.example</td><td>jobs@acme.io</td><td class="sp-clip"><a href="in-quarantine-message.html"><strong>Your parcel could not be delivered</strong></a></td><td class="sp-muted is-size-7">score 8.1 · URL_SHORTENER</td><td class="sp-muted sp-nowrap">12 kB</td><td class="sp-nowrap">13 days</td><td class="sp-actions"><a href="in-quarantine-message.html">Review</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-09 07:02:11</td><td>m.ortega@supplier.example</td><td>owner@lists.example.org</td><td class="sp-clip"><a href="in-quarantine-message.html"><strong>Fwd: scanned contract (3 attachments)</strong></a></td><td class="sp-muted is-size-7">score 7.4 · MIME_ARCHIVE</td><td class="sp-muted sp-nowrap">2.9 MB</td><td class="sp-nowrap"><span class="tag is-warning is-light">2 days</span></td><td class="sp-actions"><a href="in-quarantine-message.html">Review</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot"><span>3 messages · 3.0 MB of 500 MB</span><span class="sp-end">A domain administrator sees only their assigned inbound domains</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Inbound queue · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item is-active" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a><a href="in-log.html"><i class="ti ti-list-details"></i>Log</a><a href="in-queue.html" aria-current="page"><i class="ti ti-stack-2"></i>Queue</a><a href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a><a href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound</p>
|
||||||
|
<h1 class="title is-3">Inbound queue</h1>
|
||||||
|
<p class="sp-lead">Mail accepted on port 25 and still waiting for its upstream. The same Postfix queue as <a href="out-queue.html">outbound</a>, filtered by recipient domain.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Waiting</h2><div class="sp-end">Refreshes every 10 s</div></div>
|
||||||
|
<div class="sp-empty"><i class="ti ti-stack-2"></i>Nothing is waiting. Every accepted message has reached its upstream.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<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="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>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Sign in · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="columns is-gapless sp-signin">
|
||||||
|
<div class="column is-half sp-signin-brand">
|
||||||
|
<div>
|
||||||
|
<img src="../../selfpost-stamp.svg" alt="SelfPost">
|
||||||
|
<p>YOUR SMTP. YOUR RULES.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-half sp-signin-form">
|
||||||
|
<form class="sp-form" method="post" action="overview.html">
|
||||||
|
<p class="sp-kicker">mail.example.org</p>
|
||||||
|
<h1 class="title is-3">Sign in</h1>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="username">Username</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="username" name="username" autocomplete="username" required><span class="icon is-left"><i class="ti ti-user"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password">Password</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="password" name="password" type="password" autocomplete="current-password" required><span class="icon is-left"><i class="ti ti-lock"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-5"><button type="submit" class="button is-primary is-fullwidth">Sign in</button></div>
|
||||||
|
</form>
|
||||||
|
<p class="sp-foot">© 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>New password · prod-server · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html" aria-current="page"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-domains.html">Outbound domains</a> / <a href="out-domain.html">example.org</a> / Applications /</p>
|
||||||
|
<h1 class="title is-3">New password for <span class="sp-mono">prod-server</span></h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<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>
|
||||||
|
<div class="field has-addons"><div class="control is-expanded"><input class="input" readonly value="prod-server"></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">Password</label>
|
||||||
|
<div class="field has-addons"><div class="control is-expanded"><input class="input" readonly value="kT7v-Qm2x-Lp9d-Ue4s-Hn6b-Rz3w"></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div>
|
||||||
|
<p class="help">The previous password stopped working when this one was generated. If this one is lost, generate another.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="sp-box-foot"><a class="button is-primary" href="out-domain.html">Done — back to example.org</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-plug"></i></span><h2>Connect with</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Server</dt><dd class="sp-mono">mail.example.org</dd></div>
|
||||||
|
<div><dt>Port</dt><dd>465 SSL/TLS · 587 STARTTLS</dd></div>
|
||||||
|
<div><dt>May send as</dt><dd class="sp-mono">*@example.org</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>prod-server · example.org · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html" aria-current="page"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-domains.html">Outbound domains</a> / <a href="out-domain.html">example.org</a> / Applications /</p>
|
||||||
|
<h1 class="title is-3 sp-mono">prod-server</h1>
|
||||||
|
<p class="sp-lead">1 102 messages in 30 days · peak 96 msg/h · average 1.5 msg/h. The same form, empty, adds a new application.</p>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<a class="button" href="out-app-created.html"><i class="ti ti-key"></i>New password</a>
|
||||||
|
<a class="button is-danger is-light" href="#"><i class="ti ti-trash"></i>Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="sp-form" method="post" action="out-domain.html">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Sender</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="label" for="login">Login</label>
|
||||||
|
<div class="control"><input class="input sp-mono" id="login" name="login" value="prod-server" autocomplete="off" spellcheck="false" required></div>
|
||||||
|
<p class="help">Unique across domains; letters, digits, <code>.</code> <code>-</code> <code>_</code>.</p></div>
|
||||||
|
<div class="field"><label class="label" for="amode">May send as</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="amode" name="mode"><option selected>Any address of the domain</option><option>Specific addresses (list)</option></select></div></div></div>
|
||||||
|
<div class="field"><label class="label" for="addresses">Addresses</label>
|
||||||
|
<div class="control"><textarea class="textarea" id="addresses" name="addresses" rows="3" placeholder="alerts@example.org" disabled></textarea></div>
|
||||||
|
<p class="help">One per line. Used only with a list.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Client IP allow-list</h2><div class="sp-end"><span class="tag is-light">off</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="auth_ip_restrict"> Restrict to listed IPs</label></div>
|
||||||
|
<div class="field"><label class="label" for="ips">Allowed client IPs</label>
|
||||||
|
<div class="control"><textarea class="textarea sp-mono" id="ips" name="auth_allowed_ips" rows="3" placeholder="203.0.113.10"></textarea></div>
|
||||||
|
<p class="help">A login from anywhere else is refused with a 4xx at <code>MAIL FROM</code>. A sending control, fail-open — not an authentication boundary.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Rate limit</h2><div class="sp-end"><span class="tag is-success is-light">active</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="label" for="rmode">Limit mode</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="rmode" name="rl_mode"><option>Use the domain limit</option><option selected>Manual</option><option>Auto (from statistics)</option></select></div></div></div>
|
||||||
|
<div class="sp-pair">
|
||||||
|
<div class="field"><label class="label" for="max">Messages (max 600)</label><div class="control"><input class="input" id="max" name="max_messages" type="number" min="1" max="600" value="200"></div></div>
|
||||||
|
<div class="field"><label class="label" for="win">Window, s</label><div class="control"><input class="input" id="win" name="window_seconds" type="number" min="1" value="3600"></div></div>
|
||||||
|
</div>
|
||||||
|
<p class="help">Overrides the domain limit (288 / h) for this application; may be higher or lower, never above level 1.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-primary">Save application</button>
|
||||||
|
<a class="button is-text" href="out-domain.html">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Delete example.org · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html" aria-current="page"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-domains.html">Outbound domains</a> / <a href="out-domain.html">example.org</a> /</p>
|
||||||
|
<h1 class="title is-3">Delete example.org</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<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>
|
||||||
|
<ul class="sp-list-check">
|
||||||
|
<li><i class="ti ti-x"></i>permanently delete its DKIM signing key;</li>
|
||||||
|
<li><i class="ti ti-x"></i>delete <strong>both applications</strong> (<span class="sp-mono">prod-server</span>, <span class="sp-mono">alerts</span>) with their credentials and sender bindings;</li>
|
||||||
|
<li><i class="ti ti-x"></i>reload OpenDKIM so the domain is no longer signed.</li>
|
||||||
|
</ul>
|
||||||
|
<p class="sp-muted mb-4">The published DKIM record becomes invalid — remove it from DNS unless you plan to re-add the domain. Inbound domains are not touched.</p>
|
||||||
|
<form method="post" action="out-domains.html">
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete example.org</button>
|
||||||
|
<a class="button is-text" href="out-domain.html">Keep it</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-file-export"></i></span><h2>Moving it instead?</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p><a href="out-domain-settings.html#export">Export the domain</a> first. The file carries the key and the application passwords, so the new instance needs no DNS change.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Settings · example.org · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html" aria-current="page"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-domains.html">Outbound domains</a> / <a href="out-domain.html">example.org</a> /</p>
|
||||||
|
<h1 class="title is-3">Domain settings</h1>
|
||||||
|
<p class="sp-lead">The things set once and rarely touched: where DMARC reports go, the sending ceiling, moving the domain elsewhere.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="reports">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>DMARC report address</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="rua">Aggregate reports (rua=)</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="rua" name="dmarc_rua_mode">
|
||||||
|
<option selected>Same as Settings (dmarc@mail.example.org)</option>
|
||||||
|
<option>SelfPost hosted (example.org@dmarc.mail.example.org)</option>
|
||||||
|
<option>No aggregate reports</option>
|
||||||
|
<option>Custom address</option>
|
||||||
|
</select></div></div>
|
||||||
|
<p class="help">The default comes from <a href="settings.html">Settings</a>. Changing it changes the DMARC record to publish.</p></div>
|
||||||
|
<button type="submit" class="button is-primary">Save report address</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="export">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Export domain</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">One file with the DKIM key and every application's password hash — import it on another SelfPost and the published DNS needs no change. <strong>The file is a secret.</strong></p>
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="encrypt" checked> Encrypt with a password (<code>.spde</code>)</label></div>
|
||||||
|
<div class="sp-pair">
|
||||||
|
<div class="field"><label class="label" for="encpw">Password</label><div class="control"><input class="input" id="encpw" type="password" placeholder="at least 12 characters"></div></div>
|
||||||
|
<div class="field"><label class="label" for="encpw2">Repeat password</label><div class="control"><input class="input" id="encpw2" type="password"></div></div>
|
||||||
|
</div>
|
||||||
|
<p class="help mb-3">Keep this password: SelfPost does not store it, and the file cannot be opened without it.</p>
|
||||||
|
<button type="submit" class="button"><i class="ti ti-download"></i>Export domain</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="limit">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Rate limit</h2><div class="sp-end"><span class="tag is-success is-light">active</span></div><a class="sp-help" href="help.html#limits" title="Levels 1 and 2"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="mode">Limit mode</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="mode" name="mode"><option>Manual</option><option selected>Auto (from statistics)</option></select></div></div></div>
|
||||||
|
<div class="field"><label class="label" for="mult">Multiplier (1.5–10, default 3)</label>
|
||||||
|
<div class="control"><input class="input" id="mult" name="auto_multiplier" type="number" min="1.5" max="10" step="0.1" value="3"></div>
|
||||||
|
<p class="help">Computed limit: <strong>288</strong> messages / 3600 s. Peak was 96 msg/h. Last recalculated yesterday 03:00 UTC.</p></div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-primary">Save limit</button>
|
||||||
|
<button type="submit" class="button" formaction="#recalc">Recalculate now</button>
|
||||||
|
<button type="submit" class="button is-danger is-light" formaction="#clear">Remove limit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<p class="sp-muted mt-4">Applies to every sender on this domain and cannot exceed level 1 (600 messages / 3600 s per client IP, set in <code>.env</code>). An application may carry its own limit above or below this one.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<a class="button is-danger is-light" href="out-domain-delete.html"><i class="ti ti-trash"></i>Delete example.org…</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>example.org · Outbound domains · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html" aria-current="page"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<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>
|
||||||
|
<p class="sp-lead">1 284 messages in 30 days · peak 96 msg/h · average 1.8 msg/h</p>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-refresh"></i>Re-check DNS</button></form>
|
||||||
|
<a class="button is-primary" href="out-app.html"><i class="ti ti-plus"></i>Add application</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<aside class="menu sp-side">
|
||||||
|
<p class="menu-label">This domain</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="is-active" href="#dns"><i class="ti ti-world-check"></i>DNS records <span class="tag is-danger is-light">1</span></a></li>
|
||||||
|
<li><a href="#apps"><i class="ti ti-apps"></i>Applications <span class="tag is-light">2</span></a></li>
|
||||||
|
<li><a href="#connection"><i class="ti ti-plug"></i>Connection</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">See also</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="out-log.html"><i class="ti ti-list-details"></i>Log for this domain</a></li>
|
||||||
|
<li><a href="dmarc-domain.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Rarely changed</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="out-domain-settings.html#reports"><i class="ti ti-mail-forward"></i>Report address</a></li>
|
||||||
|
<li><a href="out-domain-settings.html#limit"><i class="ti ti-gauge"></i>Rate limit <span class="tag is-success is-light">auto</span></a></li>
|
||||||
|
<li><a href="out-domain-settings.html#export"><i class="ti ti-file-export"></i>Export</a></li>
|
||||||
|
<li><a class="has-text-danger" href="out-domain-delete.html"><i class="ti ti-trash has-text-danger"></i>Delete domain</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-9">
|
||||||
|
<div class="box" id="dns">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>DNS records</h2><div class="sp-end">Checked 3 min ago</div><a class="sp-help" href="help.html#dns" title="What to publish and why"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>DKIM</h3><span class="tag is-success is-light">published</span><small>Not a secret · selector <code>sp2026</code></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="sp2026._domainkey.example.org"></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="3" readonly>v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3kqV0mJd8e2f1Yb7Qw5uT9sLr4nHc6pZaXo1vKe0yWm2gBt8RjN5dCq3hUf7lPs9aEi4oMz6xTb1kGv0nYw…IDAQAB</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>SPF</h3><span class="tag is-danger is-light">mismatch</span><small>Merge into the existing record — never publish a second one</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="example.org"></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"><input class="input" readonly value="v=spf1 ip4:203.0.113.25 ~all"></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">In DNS now</label>
|
||||||
|
<div class="control"><input class="input is-danger" readonly value="v=spf1 include:_spf.google.com ~all"></div>
|
||||||
|
<p class="help is-danger">The published record does not authorize 203.0.113.25. Add <code>ip4:203.0.113.25</code> before <code>~all</code>. Shallow check: <code>include:</code> is not followed.</p></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>DMARC</h3><span class="tag is-success is-light">published</span><small>Report address <a href="out-domain-settings.html#reports">from Settings</a></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="_dmarc.example.org"></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"><input class="input" readonly value="v=DMARC1; p=none; rua=mailto:dmarc@mail.example.org"></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div>
|
||||||
|
<p class="help"><code>p=none</code> does not affect delivery. Tighten to <code>quarantine</code>, then <code>reject</code>, once the <a href="dmarc-domain.html">reports</a> look clean.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="apps">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Applications</h2><div class="sp-end">SASL logins that may send as this domain</div><a class="sp-help" href="help.html#apps" title="What an application is"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Login</th><th>May send as</th><th>30 days</th><th>Limits</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono"><strong>prod-server</strong></td><td>*@example.org</td><td class="sp-muted">1 102 msg · peak 96/h</td><td><span class="tag is-success is-light">200 / h</span></td>
|
||||||
|
<td class="sp-actions"><a href="out-app.html">Edit</a><a href="out-app-created.html">New password</a><a class="has-text-danger" href="#">Delete</a></td></tr>
|
||||||
|
<tr><td class="sp-mono"><strong>alerts</strong></td><td>alerts@example.org, noc@example.org</td><td class="sp-muted">182 msg · peak 14/h</td><td><span class="tag is-light">domain</span> <span class="tag is-success is-light">2 IPs</span></td>
|
||||||
|
<td class="sp-actions"><a href="out-app.html">Edit</a><a href="out-app-created.html">New password</a><a class="has-text-danger" href="#">Delete</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot">New password and Delete ask for confirmation; the old password stops working at once.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="connection">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Connection</h2><div class="sp-end">The same for every domain</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Server</dt><dd class="sp-mono">mail.example.org</dd></div>
|
||||||
|
<div><dt>Port 465</dt><dd>SSL/TLS (implicit)</dd></div>
|
||||||
|
<div><dt>Port 587</dt><dd>STARTTLS (submission)</dd></div>
|
||||||
|
<div><dt>Authentication</dt><dd>Application login, required on every port</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Outbound domains · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item is-active" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html" aria-current="page"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Outbound</p>
|
||||||
|
<h1 class="title is-3">Outbound domains</h1>
|
||||||
|
<p class="sp-lead">Domains this relay signs and sends for. Each gets a DKIM key and its own application logins.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Add a domain</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="out-domain.html">
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded has-icons-left"><input class="input" name="name" type="text" placeholder="example.com" autocomplete="off" autocapitalize="none" spellcheck="false" required><span class="icon is-left"><i class="ti ti-world"></i></span></div>
|
||||||
|
<div class="control"><button type="submit" class="button is-primary"><i class="ti ti-plus"></i>Add domain</button></div>
|
||||||
|
</div>
|
||||||
|
<p class="help">A DKIM key is generated at once; the next page shows the three DNS records to publish.
|
||||||
|
Moving a domain from another SelfPost? <a href="backup.html#import">Import it</a> instead — the key and passwords come across.</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Domains</h2><div class="sp-end">3 domains · DNS cached a few minutes</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>DNS</th><th>Selector</th><th>Apps</th><th>30 days</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-danger is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td class="sp-muted sp-mono">sp2026</td><td>2</td><td class="sp-muted">1 284 msg · peak 96/h</td><td class="sp-actions"><a href="out-domain.html">Open</a><a class="has-text-danger" href="out-domain-delete.html">Delete</a></td></tr>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>shop.example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td class="sp-muted sp-mono">sp2026</td><td>1</td><td class="sp-muted">18 920 msg · peak 410/h</td><td class="sp-actions"><a href="out-domain.html">Open</a><a class="has-text-danger" href="out-domain-delete.html">Delete</a></td></tr>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>notify.acme.io</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-warning is-light">DMARC</span></td><td class="sp-muted sp-mono">sp2025</td><td>3</td><td class="sp-muted">402 msg · peak 22/h</td><td class="sp-actions"><a href="out-domain.html">Open</a><a class="has-text-danger" href="out-domain-delete.html">Delete</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot">Each tag is one check. Green — published and correct; amber — published but weak; red — missing or wrong.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Outbound log · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item is-active" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html" aria-current="page"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Outbound</p>
|
||||||
|
<h1 class="title is-3">Outbound log</h1>
|
||||||
|
<p class="sp-lead">Every message an application handed to the relay. Rows older than 30 days are deleted — change that in <a href="settings.html">Settings</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head">
|
||||||
|
<span class="sp-no">01</span><h2>Messages</h2>
|
||||||
|
<form class="sp-end" method="get" action="#">
|
||||||
|
<div class="select is-small"><select name="domain"><option>All domains</option><option>example.org</option><option>shop.example.org</option><option>notify.acme.io</option></select></div>
|
||||||
|
<div class="select is-small"><select name="app"><option>All applications</option><option>prod-server</option><option>alerts</option></select></div>
|
||||||
|
<div class="select is-small"><select name="status"><option>Any status</option><option>sent</option><option>deferred</option><option>bounced</option><option>rate-limited</option></select></div>
|
||||||
|
<button type="submit" class="button is-small"><i class="ti ti-filter"></i>Filter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Time, UTC</th><th>From</th><th>To</th><th>Subject</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>shop@shop.example.org</td><td>m.keller@gmx.de</td><td class="sp-clip">Your order #48213 has shipped</td><td><span class="tag is-success is-light">sent</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 14:01:50</td><td>shop@shop.example.org</td><td>anna.lind@outlook.com</td><td class="sp-clip">Your order #48212 has shipped</td><td><span class="tag is-success is-light">sent</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:58:07</td><td>alerts@example.org</td><td>noc@example.org</td><td class="sp-clip">[FIRING] disk usage above 90 % on db-2</td><td><span class="tag is-warning is-light">deferred</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:41:19</td><td>no-reply@notify.acme.io</td><td>j.doe@nonexistent.example</td><td class="sp-clip">Reset your password</td><td><span class="tag is-danger is-light">bounced</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:40:02</td><td>prod@example.org</td><td>billing@partner.example</td><td class="sp-clip">Invoice 2026-0917</td><td><span class="tag is-light">rate-limited</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:12:44</td><td>shop@shop.example.org</td><td>p.novak@seznam.cz</td><td class="sp-clip">We received your return request</td><td><span class="tag is-success is-light">sent</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot"><span>Page 1 of 412</span><a href="#">Older →</a><span class="sp-end">New rows appear on their own</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Message · Outbound log · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item is-active" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html" aria-current="page"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-log.html">Outbound log</a> /</p>
|
||||||
|
<h1 class="title is-4">[FIRING] disk usage above 90 % on db-2</h1>
|
||||||
|
<p class="sp-route"><span>alerts@example.org</span><i class="ti ti-arrow-right sp-muted"></i><span>noc@example.org</span><span class="tag is-warning is-light">deferred</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Message</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Domain</dt><dd><a href="out-domain.html">example.org</a></dd></div>
|
||||||
|
<div><dt>Application</dt><dd class="sp-mono"><a href="out-app.html">alerts</a></dd></div>
|
||||||
|
<div><dt>Accepted</dt><dd>2026-09-21 13:58:07 UTC</dd></div>
|
||||||
|
<div><dt>Status reported</dt><dd>2026-09-21 13:58:09 UTC</dd></div>
|
||||||
|
<div><dt>Queue id</dt><dd class="sp-mono">4XcB7k2Jm9z1</dd></div>
|
||||||
|
<div><dt>Journal id</dt><dd class="sp-mono">184220</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>History</h2></div>
|
||||||
|
<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="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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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="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=<a81f0c@alertmanager>
|
||||||
|
<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=<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)</span></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Outbound queue · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item is-active" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a><a href="out-log.html"><i class="ti ti-list-details"></i>Log</a><a href="out-queue.html" aria-current="page"><i class="ti ti-stack-2"></i>Queue</a><a href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Outbound</p>
|
||||||
|
<h1 class="title is-3">Outbound queue</h1>
|
||||||
|
<p class="sp-lead">Messages accepted from applications and still waiting for the receiving server. Refreshes every 10 s.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Waiting</h2><div class="sp-end">1 message · 3.1 kB</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Queue id</th><th>Arrived, UTC</th><th>From</th><th>To</th><th>Last reply</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono">4XcB7k2Jm9z1</td><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:58:07</td><td>alerts@example.org</td><td>noc@example.org</td><td class="sp-muted">451 4.7.1 Greylisted, try again in 5 minutes</td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<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="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>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,476 @@
|
|||||||
|
{
|
||||||
|
"account": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Panel credentials]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[What happens]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"backup": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Full backup]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Import a domain]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dmarc-domain": [
|
||||||
|
"head+postmark+actions",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-5": [
|
||||||
|
"box[Sources · 7 days]:table"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-7": [
|
||||||
|
"box[Reports]:table"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dmarc-report": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Report]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Published policy]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"box[Records]:table"
|
||||||
|
],
|
||||||
|
"dmarc": [
|
||||||
|
"head",
|
||||||
|
"box[Ingest]:body",
|
||||||
|
"box[Recent reports]:table"
|
||||||
|
],
|
||||||
|
"health": [
|
||||||
|
"head+actions",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Machine]:table"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Processes]:table"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-4": [
|
||||||
|
"box[TLS certificate]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-4": [
|
||||||
|
"box[Milter sockets]:table"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-4": [
|
||||||
|
"box[Hostname and reverse DNS]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"box[Configuration]:body"
|
||||||
|
],
|
||||||
|
"help": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-3": [
|
||||||
|
"side_menu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-9": [
|
||||||
|
"box[Overview]:help×3",
|
||||||
|
"box[Outbound]:help×3"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"in-domain-delete": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-7": [
|
||||||
|
"box.sp-danger-zone[This cannot be undone]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"in-domain": [
|
||||||
|
"head+postmark+actions",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-3": [
|
||||||
|
"side_menu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-9": [
|
||||||
|
"box[MX record]:record",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Upstream]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Valid recipients]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"in-domains": [
|
||||||
|
"head",
|
||||||
|
"box[Add a domain]:body",
|
||||||
|
"box[Domains]:table"
|
||||||
|
],
|
||||||
|
"in-filter-lists": [
|
||||||
|
"head",
|
||||||
|
"box[Add an entry]:body",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Deny]:table"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Allow]:table"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"box[Where the lists go]:body"
|
||||||
|
],
|
||||||
|
"in-log": [
|
||||||
|
"head",
|
||||||
|
"box[Messages]:table"
|
||||||
|
],
|
||||||
|
"in-quarantine-message": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-7": [
|
||||||
|
"box[Message]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-5": [
|
||||||
|
"box[Why it was held]:table"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"box[Headers]:log",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Release]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box.sp-danger-zone[Discard]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"in-quarantine": [
|
||||||
|
"head",
|
||||||
|
"box[Held messages]:table"
|
||||||
|
],
|
||||||
|
"in-queue": [
|
||||||
|
"head",
|
||||||
|
"box[Waiting]:empty",
|
||||||
|
"box[How retries work]:body"
|
||||||
|
],
|
||||||
|
"out-app-created": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-7": [
|
||||||
|
"box.sp-credential[Shown once]:record"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-5": [
|
||||||
|
"box[Connect with]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"out-app": [
|
||||||
|
"head+actions",
|
||||||
|
{
|
||||||
|
"form": [
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-4": [
|
||||||
|
"box[Sender]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-4": [
|
||||||
|
"box[Client IP allow-list]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-4": [
|
||||||
|
"box[Rate limit]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"submit_row"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"out-domain-delete": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-7": [
|
||||||
|
"box.sp-danger-zone[This cannot be undone]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-5": [
|
||||||
|
"box[Moving it instead?]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"out-domain-settings": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[DMARC report address]:body",
|
||||||
|
"box[Export domain]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Rate limit]:body",
|
||||||
|
"box.sp-danger-zone[Delete domain]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"out-domain": [
|
||||||
|
"head+postmark+actions",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-3": [
|
||||||
|
"side_menu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-9": [
|
||||||
|
"box[DNS records]:record×3",
|
||||||
|
"box[Applications]:table",
|
||||||
|
"box[Connection]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"out-domains": [
|
||||||
|
"head",
|
||||||
|
"box[Add a domain]:body",
|
||||||
|
"box[Domains]:table"
|
||||||
|
],
|
||||||
|
"out-log": [
|
||||||
|
"head",
|
||||||
|
"box[Messages]:table"
|
||||||
|
],
|
||||||
|
"out-message": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-7": [
|
||||||
|
"box[Message]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-5": [
|
||||||
|
"box[History]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"box[Delivery log]:log"
|
||||||
|
],
|
||||||
|
"out-queue": [
|
||||||
|
"head",
|
||||||
|
"box[Waiting]:table",
|
||||||
|
"box[How retries work]:body"
|
||||||
|
],
|
||||||
|
"overview": [
|
||||||
|
"head+postmark",
|
||||||
|
"box[Server health]:health",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-7": [
|
||||||
|
"box[Outbound domains]:table"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-5": [
|
||||||
|
"box[Inbound domains]:table"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preflight": [
|
||||||
|
"head+postmark+actions",
|
||||||
|
"box[Checks]:table",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Send a test e-mail]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Not checked here]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"form": [
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-7": [
|
||||||
|
"box[DMARC aggregate reports]:record×2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-5": [
|
||||||
|
"box[Log retention]:body",
|
||||||
|
"box[Sending rate limits]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"submit_row"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"system-log": [
|
||||||
|
"head",
|
||||||
|
"box[Recent lines]:log"
|
||||||
|
],
|
||||||
|
"user-delete": [
|
||||||
|
"head",
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-7": [
|
||||||
|
"box.sp-danger-zone[This cannot be undone]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"user": [
|
||||||
|
"head+actions",
|
||||||
|
{
|
||||||
|
"form": [
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Sign-in]:body"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is-6": [
|
||||||
|
"box[Outbound domains]:body",
|
||||||
|
"box[Inbound domains]:body"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"submit_row"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"users": [
|
||||||
|
"head+actions",
|
||||||
|
"box[Panel users]:table"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Overview · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item sp-current" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<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>
|
||||||
|
<p class="sp-lead">The TLS certificate expires in 12 days and has not renewed yet.</p>
|
||||||
|
</div>
|
||||||
|
<div class="sp-muted is-size-7">Updated 14:02:31 UTC<br>refreshes every 10 s</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Server health</h2><div class="sp-end"><a href="health.html">All details</a></div><a class="sp-help" href="help.html#checks" title="What these checks mean"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<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="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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Outbound domains</h2><div class="sp-end"><a href="out-domains.html">Manage</a></div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>DNS</th><th>Apps</th><th>30 days</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-danger is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td>2</td><td class="sp-muted">1 284 msg</td></tr>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>shop.example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td>1</td><td class="sp-muted">18 920 msg</td></tr>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>notify.acme.io</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-warning is-light">DMARC</span></td><td>3</td><td class="sp-muted">402 msg</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Inbound domains</h2><div class="sp-end"><a href="in-domains.html">Manage</a></div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>MX</th><th>Upstream</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="in-domain.html"><strong>lists.example.org</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-muted sp-mono is-size-7">mx.internal:25</td></tr>
|
||||||
|
<tr><td><a href="in-domain.html"><strong>acme.io</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-muted sp-mono is-size-7">10.0.4.12:2525</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Preflight · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item is-active" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="health.html"><i class="ti ti-heartbeat"></i>Health</a><a href="preflight.html" aria-current="page"><i class="ti ti-checklist"></i>Preflight</a><a href="system-log.html"><i class="ti ti-file-text"></i>System log</a><a href="backup.html"><i class="ti ti-archive"></i>Backup</a><a href="users.html"><i class="ti ti-users"></i>Users</a><a href="settings.html"><i class="ti ti-settings"></i>Settings</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-postmark sp-warn"><span>PREFLIGHT</span><b>WARN</b><span>1 OF 7 CHECKS</span></div>
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Preflight</h1>
|
||||||
|
<p class="sp-lead">Is this installation ready to deliver mail? Run it after the first start, after moving the server, or when receivers start refusing you. Health watches what runs; Preflight checks what the outside world sees.</p>
|
||||||
|
</div>
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-refresh"></i>Run again</button></form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Checks</h2><div class="sp-end">Ran 14:02 UTC · 3.1 s</div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Check</th><th>Result</th><th>Found</th><th>What to do</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><strong>Reverse DNS</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">203.0.113.25 ⇄ mail.example.org</td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>TLS certificate</strong></td><td><span class="tag is-warning is-light">warn</span></td><td class="is-size-7">Valid for <span class="sp-mono">mail.example.org</span>, expires in 12 days</td><td>Renewal has not run. Check that port 80 reaches the ACME client and look for <code>acme</code> in the <a href="system-log.html">system log</a>.</td></tr>
|
||||||
|
<tr><td><strong>Port 25</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">220 mail.example.org ESMTP</td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>Ports 465, 587</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">TLS handshake, 220 banner on both</td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>HELO name</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">mail.example.org</td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>Proxy headers</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="is-size-7">Peer <span class="sp-mono">172.18.0.2</span> is trusted; <span class="sp-mono">X-Forwarded-Proto: https</span></td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>DKIM signing</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">inet:127.0.0.1:8891 answers</td><td class="sp-muted">—</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot">Ports are dialled from inside the container at the public address; a firewall that treats inside and outside differently can make this pass while the world is still blocked.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Send a test e-mail</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="rcpt">Recipient</label>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded has-icons-left"><input class="input" id="rcpt" name="recipient" type="email" placeholder="you@elsewhere.example" required><span class="icon is-left"><i class="ti ti-mail"></i></span></div>
|
||||||
|
<div class="control"><button type="submit" class="button is-primary"><i class="ti ti-send"></i>Send</button></div>
|
||||||
|
</div>
|
||||||
|
<p class="help">Sent from <span class="sp-mono">postmaster@mail.example.org</span> through the local Postfix. This confirms the relay accepted it — follow the delivery in the <a href="out-log.html">outbound log</a>.</p></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Not checked here</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-2">SPF, DKIM and DMARC records are per domain — each <a href="out-domains.html">outbound domain</a> shows its own.</p>
|
||||||
|
<p class="sp-muted">Whether a receiver finally accepted the test message is not tracked; only that it left.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Settings · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item is-active" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="health.html"><i class="ti ti-heartbeat"></i>Health</a><a href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a><a href="system-log.html"><i class="ti ti-file-text"></i>System log</a><a href="backup.html"><i class="ti ti-archive"></i>Backup</a><a href="users.html"><i class="ti ti-users"></i>Users</a><a href="settings.html" aria-current="page"><i class="ti ti-settings"></i>Settings</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Settings</h1>
|
||||||
|
<p class="sp-lead">Defaults for the whole instance. Your own username and password are under <a href="account.html">Account</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>DMARC aggregate reports</h2><div class="sp-end"><span class="tag is-success is-light">authorized</span></div></div>
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="field"><label class="label" for="rua">Default report address</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="rua" name="dmarc_report_email" type="email" value="dmarc@mail.example.org"><span class="icon is-left"><i class="ti ti-mail"></i></span></div>
|
||||||
|
<p class="help">The default <code>rua=</code> for every outbound domain; a domain may override it. An address on <code>mail.example.org</code> is received and parsed by SelfPost itself.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>Report authorization</h3><span class="tag is-success is-light">published</span><small>Needed when <code>rua=</code> points at another domain</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="*._report._dmarc.mail.example.org"></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"><input class="input" readonly value="v=DMARC1"></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Log retention</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="label" for="ret">Keep outbound log rows, days</label>
|
||||||
|
<div class="control"><input class="input" id="ret" name="send_log_retention_days" type="number" min="7" max="365" value="30" required></div>
|
||||||
|
<p class="help">The main driver of <code>/data</code> growth. Does not affect <code>mail.log</code> rotation (fourteen daily files).</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="rate-limits">
|
||||||
|
<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="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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons"><button type="submit" class="button is-primary">Save settings</button></div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Create administrator · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="columns is-gapless sp-signin">
|
||||||
|
<div class="column is-half sp-signin-brand">
|
||||||
|
<div>
|
||||||
|
<img src="../../selfpost-stamp.svg" alt="SelfPost">
|
||||||
|
<p>YOUR SMTP. YOUR RULES.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-half sp-signin-form">
|
||||||
|
<form class="sp-form" method="post" action="overview.html">
|
||||||
|
<p class="sp-kicker">First run · one-time link</p>
|
||||||
|
<h1 class="title is-3">Create administrator</h1>
|
||||||
|
<p class="sp-muted mb-4">This link creates the single global administrator. After you submit, it stops working for good.</p>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="username">Username</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="username" name="username" autocomplete="username" required><span class="icon is-left"><i class="ti ti-user"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password">Password</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="password" name="password" type="password" autocomplete="new-password" required><span class="icon is-left"><i class="ti ti-lock"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password_confirm">Confirm password</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="password_confirm" name="password_confirm" type="password" autocomplete="new-password" required><span class="icon is-left"><i class="ti ti-lock"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-5"><button type="submit" class="button is-primary is-fullwidth">Create administrator</button></div>
|
||||||
|
</form>
|
||||||
|
<p class="sp-foot">© 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<!-- title: Account | section: user -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Signed in as admin · global administrator</p>
|
||||||
|
<h1 class="title is-3">Account</h1>
|
||||||
|
<p class="sp-lead">Your sign-in to this panel. Applications keep their own logins and passwords and are not affected.</p>
|
||||||
|
</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>Panel credentials</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="username">Username</label><div class="control"><input class="input" id="username" name="username" value="admin" autocomplete="username" spellcheck="false" required></div></div>
|
||||||
|
<div class="field"><label class="label" for="cur">Current password</label><div class="control"><input class="input" id="cur" name="current_password" type="password" autocomplete="current-password" required></div></div>
|
||||||
|
<div class="sp-pair">
|
||||||
|
<div class="field"><label class="label" for="new">New password</label><div class="control"><input class="input" id="new" name="new_password" type="password" autocomplete="new-password"></div></div>
|
||||||
|
<div class="field"><label class="label" for="new2">Confirm new password</label><div class="control"><input class="input" id="new2" name="new_password_confirm" type="password" autocomplete="new-password"></div></div>
|
||||||
|
</div>
|
||||||
|
<p class="help mb-3">Leave both new-password fields empty to change the username only.</p>
|
||||||
|
<button type="submit" class="button is-primary">Save changes</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-info-circle"></i></span><h2>What happens</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-2">Changing the password signs out every other session of this account. This one stays signed in.</p>
|
||||||
|
<p class="sp-muted">A domain administrator sees this same page, the Outbound and Inbound domains assigned to them, and nothing under Server.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<!-- title: Backup | section: server | page: backup -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Backup</h1>
|
||||||
|
<p class="sp-lead">Take the whole instance with you, or bring one domain in from another SelfPost. Both files are secrets.</p>
|
||||||
|
</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>Full backup</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3"><code>data/</code> (database, DKIM keys, credentials, the queue), <code>docker-compose.yml</code>, <code>.env</code> and <code>certs/</code> in one archive. Restore into an empty project directory and start the <strong>same SelfPost version</strong>. The reverse-proxy vhost is not included.</p>
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="encrypt" checked> Encrypt with a password (<code>.spbk</code>)</label></div>
|
||||||
|
<div class="sp-pair">
|
||||||
|
<div class="field"><label class="label" for="pw">Password</label><div class="control"><input class="input" id="pw" type="password" placeholder="at least 12 characters"></div></div>
|
||||||
|
<div class="field"><label class="label" for="pw2">Repeat password</label><div class="control"><input class="input" id="pw2" type="password"></div></div>
|
||||||
|
</div>
|
||||||
|
<p class="help mb-3">Keep this password: SelfPost does not store it, and the file cannot be opened without it.</p>
|
||||||
|
<button type="submit" class="button is-primary"><i class="ti ti-download"></i>Download full backup</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="import">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Import a domain</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">A domain export from another instance — plain <code>.json</code> or encrypted <code>.spde</code>. Its DKIM key and application passwords come across, so the published DNS needs no change. The export itself is made on the <a href="out-domain-settings.html#export">domain's settings</a>.</p>
|
||||||
|
<form class="sp-form" method="post" action="out-domain.html" enctype="multipart/form-data">
|
||||||
|
<div class="field"><label class="label">Domain export file</label>
|
||||||
|
<div class="file has-name is-fullwidth"><label class="file-label"><input class="file-input" type="file" name="file" accept=".json,.spde"><span class="file-cta"><i class="ti ti-upload"></i><span class="file-label ml-2">Choose a file…</span></span><span class="file-name">example.org.spde</span></label></div></div>
|
||||||
|
<div class="field"><label class="label" for="ipw">Password</label><div class="control"><input class="input" id="ipw" name="import_password" type="password" autocomplete="off"></div>
|
||||||
|
<p class="help">Asked only for an encrypted <code>.spde</code> file.</p></div>
|
||||||
|
<button type="submit" class="button is-primary"><i class="ti ti-file-import"></i>Import domain</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
<!-- 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="notification is-success is-light"><i class="ti ti-circle-check"></i><strong>flash</strong> — the result of the last action, between page_head and the first box. Success, or…</div>
|
||||||
|
<div class="notification is-danger is-light"><i class="ti ti-alert-circle"></i>…<strong>flash, error</strong> — a failure that belongs to the page, not to one field. A field's error stays under the field in Bulma's <code>help is-danger</code>.</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>held: quarantine · role: global</td><td><span class="tag is-primary is-light">quarantine</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><a class="sp-help" href="help.html#checks" title="Optional: the section's Help topic"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<div class="sp-box-body"><p class="mb-2">The <i class="ti ti-help-circle"></i> after the end slot is <code>sp-help</code>: an optional link to the section's topic on the Help page. It replaces today's help drawer.</p><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>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<!-- title: shop.example.org · DMARC reports | section: outbound | page: dmarc -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<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>
|
||||||
|
<p class="sp-lead">11 204 pass · 41 fail in the last 7 days. Failures come from a source that is not this relay — review before tightening <code>p=</code>.</p>
|
||||||
|
</div>
|
||||||
|
<a class="button" href="out-domain.html"><i class="ti ti-world-upload"></i>Domain page</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Sources · 7 days</h2></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Source</th><th>Pass</th><th>Fail</th><th>Disposition</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">203.0.113.25 <span class="tag is-light">this relay</span></td><td>11 204</td><td>0</td><td><span class="tag is-success is-light">none</span></td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">198.51.100.44</td><td>0</td><td>41</td><td><span class="tag is-warning is-light">none</span></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Reports</h2></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Received, UTC</th><th>Reporter</th><th>Window</th><th>Pass</th><th>Fail</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 04:40</td><td class="sp-muted">Outlook.com</td><td>20 Sep</td><td>1 903</td><td><span class="tag is-warning is-light">7</span></td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 02:15</td><td class="sp-muted">google.com</td><td>20 Sep</td><td>6 410</td><td>0</td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-20 04:38</td><td class="sp-muted">Outlook.com</td><td>19 Sep</td><td>1 877</td><td><span class="tag is-warning is-light">34</span></td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<!-- title: Outlook.com · 20 Sep · DMARC | section: outbound | page: dmarc -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="dmarc.html">DMARC reports</a> / <a href="dmarc-domain.html">shop.example.org</a> /</p>
|
||||||
|
<h1 class="title is-4">Outlook.com · 20 Sep 2026</h1>
|
||||||
|
<p class="sp-route"><span>shop.example.org</span><span class="tag is-success is-light">1 903 pass</span><span class="tag is-warning is-light">7 fail</span></p>
|
||||||
|
</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>Report</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Reporter</dt><dd>Outlook.com</dd></div>
|
||||||
|
<div><dt>Report id</dt><dd class="sp-mono is-size-7">5f1c9a0e7b2d4e61a3</dd></div>
|
||||||
|
<div><dt>Window</dt><dd>20 Sep 00:00 – 21 Sep 00:00 UTC</dd></div>
|
||||||
|
<div><dt>Received</dt><dd>2026-09-21 04:40 UTC</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Published policy</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>p / sp / pct</dt><dd class="sp-mono">none / none / 100</dd></div>
|
||||||
|
<div><dt>adkim / aspf</dt><dd class="sp-mono">r / r</dd></div>
|
||||||
|
<div><dt>rua</dt><dd class="sp-mono is-size-7">dmarc@mail.example.org</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Records</h2><div class="sp-end">Parsed from the aggregate XML</div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Source</th><th>Count</th><th>Disposition</th><th>SPF</th><th>DKIM</th><th>Header from</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">203.0.113.25 <span class="tag is-light">this relay</span></td><td>1 903</td><td><span class="tag is-success is-light">none</span></td><td><span class="tag is-success is-light">pass</span></td><td><span class="tag is-success is-light">pass</span></td><td>shop.example.org</td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">198.51.100.44</td><td>7</td><td><span class="tag is-warning is-light">none</span></td><td><span class="tag is-danger is-light">fail</span></td><td><span class="tag is-danger is-light">fail</span></td><td>shop.example.org</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<!-- title: DMARC reports | section: outbound | page: dmarc -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Outbound</p>
|
||||||
|
<h1 class="title is-3">DMARC reports</h1>
|
||||||
|
<p class="sp-lead">How receivers saw mail claiming to be from your domains. Aggregate (<code>rua=</code>) only; open a report for the parsed XML.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Ingest</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Last report</dt><dd>2026-09-21 06:12 UTC</dd></div>
|
||||||
|
<div><dt>This week</dt><dd>14 kept · 0 parse failures</dd></div>
|
||||||
|
<div><dt>Hosted address</dt><dd class="sp-mono">dmarc@mail.example.org</dd></div>
|
||||||
|
<div><dt>Retention</dt><dd>500 reports or 90 days</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Recent reports</h2></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Received, UTC</th><th>Domain</th><th>Reporter</th><th>Window</th><th>Pass</th><th>Fail</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 06:12</td><td><a href="dmarc-domain.html">example.org</a></td><td class="sp-muted">google.com</td><td>20 Sep</td><td>412</td><td>0</td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 04:40</td><td><a href="dmarc-domain.html">shop.example.org</a></td><td class="sp-muted">Outlook.com</td><td>20 Sep</td><td>1 903</td><td><span class="tag is-warning is-light">7</span></td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-20 23:58</td><td><a href="dmarc-domain.html">example.org</a></td><td class="sp-muted">Yahoo</td><td>19 Sep</td><td>38</td><td>0</td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-20 06:09</td><td><a href="dmarc-domain.html">notify.acme.io</a></td><td class="sp-muted">google.com</td><td>19 Sep</td><td>61</td><td>0</td><td class="sp-actions"><a href="dmarc-report.html">View</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
<!-- title: Health | section: server | page: health -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Health</h1>
|
||||||
|
<p class="sp-lead">Everything behind the six cards on Overview. Refreshes every 10 s.</p>
|
||||||
|
</div>
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-refresh"></i>Re-check DNS</button></form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="machine">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Machine</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Resource</th><th>Usage</th><th>Detail</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>CPU</td><td><progress class="progress is-small is-success mb-1" value="12" max="100"></progress>12 %</td><td class="sp-muted">2 cores, load 0.21</td></tr>
|
||||||
|
<tr><td>Memory</td><td><progress class="progress is-small is-success mb-1" value="41" max="100"></progress>41 %</td><td class="sp-muted">812 MB of 1.9 GB</td></tr>
|
||||||
|
<tr><td>Network</td><td class="sp-nowrap">↓ 18 kB/s · ↑ 44 kB/s</td><td class="sp-muted">eth0: 2.1 GB in, 6.4 GB out</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="processes">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Processes</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Program</th><th>State</th><th>Detail</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>postfix</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 41, uptime 18 days</td></tr>
|
||||||
|
<tr><td>opendkim</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 39, uptime 18 days</td></tr>
|
||||||
|
<tr><td>journal-milter</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 40, uptime 18 days</td></tr>
|
||||||
|
<tr><td>panel</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 44, uptime 18 days</td></tr>
|
||||||
|
<tr><td>dmarc-ingest</td><td><span class="tag is-success is-light">running</span></td><td class="sp-muted">pid 47, uptime 18 days</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box" id="certificate">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>TLS certificate</h2><div class="sp-end"><span class="tag is-warning is-light">warn</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<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">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>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box" id="sockets">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">04</span><h2>Milter sockets</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Milter</th><th>State</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>opendkim<br><span class="sp-muted sp-mono is-size-7">inet:127.0.0.1:8891</span></td><td><span class="tag is-success is-light">ok</span></td></tr>
|
||||||
|
<tr><td>journal-milter<br><span class="sp-muted sp-mono is-size-7">unix:/run/selfpost/journal.sock</span></td><td><span class="tag is-success is-light">ok</span></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box" id="hostname">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">05</span><h2>Hostname and reverse DNS</h2><div class="sp-end"><span class="tag is-success is-light">ok</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Hostname</dt><dd class="sp-mono">mail.example.org</dd></div>
|
||||||
|
<div><dt>Lookup</dt><dd class="sp-mono">→ 203.0.113.25<br>← mail.example.org</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="configuration">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">06</span><h2>Configuration</h2><div class="sp-end">Safe to run at any time</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">Regenerates the OpenDKIM and Postfix configuration from the database 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.</p>
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-reload"></i>Reload configuration</button></form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<!-- title: Help | section: user -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Reference</p>
|
||||||
|
<h1 class="title is-3">Help</h1>
|
||||||
|
<p class="sp-lead">What each screen checks and what to do when it is not green. Every numbered section on a page links to its topic here.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<aside class="menu sp-side">
|
||||||
|
<p class="menu-label">Overview</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="is-active" href="#checks"><i class="ti ti-activity-heartbeat"></i>Health checks</a></li>
|
||||||
|
<li><a href="#certificate"><i class="ti ti-certificate"></i>TLS certificate</a></li>
|
||||||
|
<li><a href="#rdns"><i class="ti ti-arrows-exchange"></i>Reverse DNS</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Outbound</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="#dns"><i class="ti ti-world-check"></i>DNS records</a></li>
|
||||||
|
<li><a href="#apps"><i class="ti ti-apps"></i>Applications</a></li>
|
||||||
|
<li><a href="#limits"><i class="ti ti-gauge"></i>Rate limits</a></li>
|
||||||
|
<li><a href="#queue"><i class="ti ti-stack-2"></i>Queue and retries</a></li>
|
||||||
|
<li><a href="#dmarc"><i class="ti ti-report-analytics"></i>DMARC reports</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Inbound</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="#inbound"><i class="ti ti-inbox"></i>Backup-MX and forwarding</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<div class="column is-9">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Overview</h2></div>
|
||||||
|
<div class="sp-help-topic" id="checks"><h3>Health checks</h3><p>Six checks, the worst of them is the postmark. <span class="tag is-success is-light">ok</span> needs nothing; <span class="tag is-warning is-light">warn</span> still delivers mail but will stop doing so if ignored; <span class="tag is-danger is-light">fail</span> means mail is being lost or refused now.</p></div>
|
||||||
|
<div class="sp-help-topic" id="certificate"><h3>TLS certificate</h3><p>The certificate Postfix presents on 465 and 587. It warns 14 days before expiry and fails once expired — clients that verify certificates then refuse to connect.</p></div>
|
||||||
|
<div class="sp-help-topic" id="rdns"><h3>Hostname and reverse DNS</h3><p>The hostname must resolve to this server's address, and that address must resolve back to the hostname. Large receivers reject or spam-folder mail from hosts where the two disagree. The PTR record is set at your hosting provider, not in your DNS zone.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Outbound</h2></div>
|
||||||
|
<div class="sp-help-topic" id="dns"><h3>DNS records</h3><p>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.</p></div>
|
||||||
|
<div class="sp-help-topic" id="apps"><h3>Applications</h3><p>An application is one SASL login. It may send as any address of its domain or only as listed addresses. The password is shown once at creation or regeneration and is not stored in readable form.</p></div>
|
||||||
|
<div class="sp-help-topic" id="limits"><h3>Rate limits</h3><p>Level 1 is a per-IP ceiling set in <code>.env</code>. Level 2 is optional, per domain and per application, manual or computed from 30-day statistics, and can never exceed level 1.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!-- title: Delete lists.example.org | section: inbound | page: domains -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="in-domains.html">Inbound domains</a> / <a href="in-domain.html">lists.example.org</a> /</p>
|
||||||
|
<h1 class="title is-3">Delete lists.example.org</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<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>
|
||||||
|
<ul class="sp-list-check">
|
||||||
|
<li><i class="ti ti-x"></i>stop accepting mail for it on port 25 — senders get a relay-denied reply;</li>
|
||||||
|
<li><i class="ti ti-x"></i>remove its upstream and its 4 listed recipients.</li>
|
||||||
|
</ul>
|
||||||
|
<p class="sp-muted mb-4">Remove or repoint the MX record first, or mail for the domain will bounce. Outbound domains are not touched.</p>
|
||||||
|
<form method="post" action="in-domains.html">
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete lists.example.org</button>
|
||||||
|
<a class="button is-text" href="in-domain.html">Keep it</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<!-- title: lists.example.org · Inbound domains | section: inbound | page: domains -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-postmark"><span>DNS</span><b>OK</b><span>MX POINTS HERE</span></div>
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="in-domains.html">Inbound domains</a> /</p>
|
||||||
|
<h1 class="title is-3">lists.example.org</h1>
|
||||||
|
<p class="sp-lead">Accepted on port 25, handed to <span class="sp-mono">mx.internal:25</span> over required TLS · 4 recipients</p>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-refresh"></i>Re-check DNS</button></form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<aside class="menu sp-side">
|
||||||
|
<p class="menu-label">This domain</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="is-active" href="#dns"><i class="ti ti-world-check"></i>MX record <span class="tag is-success is-light">ok</span></a></li>
|
||||||
|
<li><a href="#upstream"><i class="ti ti-arrow-forward-up"></i>Upstream</a></li>
|
||||||
|
<li><a href="#recipients"><i class="ti ti-address-book"></i>Recipients <span class="tag is-light">4</span></a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">See also</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="in-log.html"><i class="ti ti-list-details"></i>Log for this domain</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Rarely changed</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="has-text-danger" href="in-domain-delete.html"><i class="ti ti-trash has-text-danger"></i>Delete domain</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-9">
|
||||||
|
<div class="box" id="dns">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>MX record</h2><div class="sp-end">Checked 1 min ago</div><a class="sp-help" href="help.html#inbound" title="Backup-MX and forwarding"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>MX</h3><span class="tag is-success is-light">published</span><small>Keep any existing primary MX if this is a backup-MX</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="lists.example.org"></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="MX"></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label class="label">Value to publish</label>
|
||||||
|
<div class="field has-addons"><div class="control is-expanded"><input class="input" readonly value="10 mail.example.org."></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="upstream">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Upstream</h2><div class="sp-end">Not a mailbox</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="sp-row">
|
||||||
|
<div class="field"><label class="label" for="host">Host</label><div class="control"><input class="input sp-mono" id="host" name="host" value="mx.internal" autocomplete="off" spellcheck="false" required></div></div>
|
||||||
|
<div class="field"><label class="label" for="port">Port</label><div class="control"><input class="input sp-mono" id="port" name="port" inputmode="numeric" value="25" required></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label class="label" for="tls">TLS to upstream</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="tls" name="tls_mode"><option>Opportunistic</option><option selected>Required</option><option>Off</option></select></div></div></div>
|
||||||
|
<button type="submit" class="button is-primary">Save upstream</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="recipients">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Valid recipients</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="rmode">Who to accept</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="rmode" name="recipient_mode"><option selected>Listed addresses only</option><option>Any recipient at this domain</option></select></div></div></div>
|
||||||
|
<div class="field"><label class="label" for="addresses">Addresses</label>
|
||||||
|
<div class="control"><textarea class="textarea sp-mono" id="addresses" name="addresses" rows="4">announce@lists.example.org
|
||||||
|
dev@lists.example.org
|
||||||
|
owner@lists.example.org
|
||||||
|
security@lists.example.org</textarea></div>
|
||||||
|
<p class="help">Unknown recipients are rejected at RCPT, so this relay generates no backscatter. Prefer a list unless the upstream itself rejects unknowns.</p></div>
|
||||||
|
<button type="submit" class="button is-primary">Save recipients</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<!-- title: Inbound domains | section: inbound | page: domains -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound</p>
|
||||||
|
<h1 class="title is-3">Inbound domains</h1>
|
||||||
|
<p class="sp-lead">Backup-MX / forwarder. Port 25 accepts mail only for the domains listed here and hands it to their upstream. No mailboxes.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Add a domain</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="in-domain.html">
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded has-icons-left"><input class="input" name="name" type="text" placeholder="lists.example.com" autocomplete="off" autocapitalize="none" spellcheck="false" required><span class="icon is-left"><i class="ti ti-world"></i></span></div>
|
||||||
|
<div class="control"><button type="submit" class="button is-primary"><i class="ti ti-plus"></i>Add domain</button></div>
|
||||||
|
</div>
|
||||||
|
<p class="help">Mail is not accepted until an upstream host is saved on the domain's page. Adding and deleting domains is for a global administrator; a domain administrator sees the list without this form and without Delete.</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Domains</h2><div class="sp-end">2 domains · DNS cached a few minutes</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>DNS</th><th>Upstream</th><th>Recipients</th><th>TLS to upstream</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="in-domain.html"><strong>lists.example.org</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-mono sp-muted">mx.internal:25</td><td>4 addresses</td><td><span class="tag is-success is-light">required</span></td><td class="sp-actions"><a href="in-domain.html">Open</a><a class="has-text-danger" href="in-domain-delete.html">Delete</a></td></tr>
|
||||||
|
<tr><td><a href="in-domain.html"><strong>acme.io</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-mono sp-muted">10.0.4.12:2525</td><td>any address</td><td><span class="tag is-light">opportunistic</span></td><td class="sp-actions"><a href="in-domain.html">Open</a><a class="has-text-danger" href="in-domain-delete.html">Delete</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot">The MX tag is green when at least one MX of the domain points at this server.</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<!-- title: Filter lists | section: inbound | page: filter-lists -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound · global administrator</p>
|
||||||
|
<h1 class="title is-3">Filter lists</h1>
|
||||||
|
<p class="sp-lead">Correct the spam filter by hand: senders it must always let through, and senders it must always turn away. One pair of lists for the whole instance.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Add an entry</h2><a class="sp-help" href="help.html#inbound" title="How the lists are applied"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<div class="field"><label class="label" for="list">List</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="list" name="list_type"><option>Deny — always turn away</option><option>Allow — always let through</option></select></div></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-3">
|
||||||
|
<div class="field"><label class="label" for="etype">Entry is</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="etype" name="entry_type"><option>a sender address</option><option>a sender domain</option><option>a client IP or CIDR</option></select></div></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="field"><label class="label" for="value">Value</label>
|
||||||
|
<div class="control"><input class="input sp-mono" id="value" name="value" placeholder="promo@bulk.example" autocomplete="off" autocapitalize="none" spellcheck="false" required></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field"><label class="label" for="note">Note</label>
|
||||||
|
<div class="control"><input class="input" id="note" name="note" placeholder="Why — for whoever reads this list next year"></div></div>
|
||||||
|
<button type="submit" class="button is-primary"><i class="ti ti-plus"></i>Add entry</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Deny</h2><div class="sp-end">3 entries · wins over Allow</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Value</th><th>Note</th><th>Added</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">@bulk.example <span class="tag is-light">domain</span></td><td class="sp-muted">newsletter nobody subscribed to</td><td class="sp-muted sp-nowrap is-size-7">2026-09-02</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">198.51.100.0/24 <span class="tag is-light">ip</span></td><td class="sp-muted">dictionary attack on lists.example.org</td><td class="sp-muted sp-nowrap is-size-7">2026-08-19</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">win@lottery.example <span class="tag is-light">address</span></td><td class="sp-muted">—</td><td class="sp-muted sp-nowrap is-size-7">2026-07-30</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Allow</h2><div class="sp-end">2 entries</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Value</th><th>Note</th><th>Added</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">@partner.example <span class="tag is-light">domain</span></td><td class="sp-muted">invoices were scored as spam</td><td class="sp-muted sp-nowrap is-size-7">2026-09-11</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">noreply@github.example <span class="tag is-light">address</span></td><td class="sp-muted">security advisories</td><td class="sp-muted sp-nowrap is-size-7">2026-06-04</td><td class="sp-actions"><a class="has-text-danger" href="#">Remove</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">04</span><h2>Where the lists go</h2><div class="sp-end"><span class="tag is-success is-light">applied</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Applied by</dt><dd>rspamd, from map files</dd></div>
|
||||||
|
<div><dt>Files</dt><dd class="sp-mono is-size-7">/data/antispam/allow.map<br>/data/antispam/deny.map</dd></div>
|
||||||
|
<div><dt>Last written</dt><dd>2026-09-11 08:40 UTC</dd></div>
|
||||||
|
<div><dt>When the filter is off</dt><dd class="sp-muted">The lists are kept and editable, but nothing reads them until <code>INBOUND_ANTISPAM_MILTER</code> is set.</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<!-- title: Inbound log | section: inbound | page: log -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound</p>
|
||||||
|
<h1 class="title is-3">Inbound log</h1>
|
||||||
|
<p class="sp-lead">What port 25 decided about each message, one row per recipient. Accepts come from the inbound journal, rejects from <code>mail.log</code>; the filter's reasons appear when the spam filter is on.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head">
|
||||||
|
<span class="sp-no">01</span><h2>Messages</h2>
|
||||||
|
<form class="sp-end" method="get" action="#">
|
||||||
|
<div class="select is-small"><select name="domain"><option>All domains</option><option>lists.example.org</option><option>acme.io</option></select></div>
|
||||||
|
<div class="select is-small"><select name="decision"><option>Any decision</option><option>accept</option><option>reject</option><option>tempfail</option><option>quarantine</option></select></div>
|
||||||
|
<input class="input is-small" type="date" name="from" value="2026-09-14" aria-label="From date">
|
||||||
|
<input class="input is-small" type="date" name="to" value="2026-09-21" aria-label="To date">
|
||||||
|
<button type="submit" class="button is-small"><i class="ti ti-filter"></i>Filter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Time, UTC</th><th>Client</th><th>From</th><th>To</th><th>Subject</th><th>Decision</th><th>Why</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 14:00:18</td><td class="sp-mono is-size-7">192.0.2.77</td><td>t.berg@uni.example</td><td>dev@lists.example.org</td><td class="sp-clip">Re: [dev] patch series v3</td><td><span class="tag is-success is-light">accept</span></td><td class="sp-muted is-size-7">rspamd · score 0.4</td><td class="sp-actions"><a href="in-filter-lists.html">Deny sender</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:59:02</td><td class="sp-mono is-size-7">203.0.113.140</td><td>invoice@partner-billing.example</td><td>owner@lists.example.org</td><td class="sp-clip">Outstanding invoice — action required</td><td><span class="tag is-primary is-light">quarantine</span></td><td class="sp-muted is-size-7">rspamd · score 9.8 · PHISHING, FORGED_SENDER</td><td class="sp-actions"><a href="in-quarantine-message.html">Review</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:57:41</td><td class="sp-mono is-size-7">198.51.100.90</td><td>promo@bulk.example</td><td>sales@lists.example.org</td><td class="sp-clip sp-muted">—</td><td><span class="tag is-danger is-light">reject</span></td><td class="sp-muted is-size-7">postfix · 550 5.1.1 unknown recipient</td><td class="sp-actions"><a href="in-filter-lists.html">Deny sender</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:51:30</td><td class="sp-mono is-size-7">198.51.100.12</td><td>billing@partner.example</td><td>owner@lists.example.org</td><td class="sp-clip">Invoice 2026-0917</td><td><span class="tag is-danger is-light">reject</span></td><td class="sp-muted is-size-7">rspamd · score 16.2 · BAYES_SPAM</td><td class="sp-actions"><a href="in-filter-lists.html">Allow sender</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:44:02</td><td class="sp-mono is-size-7">192.0.2.31</td><td>hr@partner.example</td><td>jobs@acme.io</td><td class="sp-clip">Application: backend engineer</td><td><span class="tag is-warning is-light">tempfail</span></td><td class="sp-muted is-size-7">selfpost · upstream 10.0.4.12 timed out</td><td class="sp-actions"></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:30:55</td><td class="sp-mono is-size-7">192.0.2.200</td><td>noreply@github.example</td><td>security@lists.example.org</td><td class="sp-clip">[advisory] new report</td><td><span class="tag is-success is-light">accept</span></td><td class="sp-muted is-size-7">rspamd · allow list</td><td class="sp-actions"></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot"><span>Page 1 of 38</span><a href="#">Older →</a><span class="sp-end">Allow / Deny sender opens <a href="in-filter-lists.html">Filter lists</a> with the entry filled in · global administrator only</span></div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<!-- title: Held message · Quarantine | section: inbound | page: quarantine -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="in-quarantine.html">Quarantine</a> /</p>
|
||||||
|
<h1 class="title is-4">Outstanding invoice — action required</h1>
|
||||||
|
<p class="sp-route"><span>invoice@partner-billing.example</span><i class="ti ti-arrow-right sp-muted"></i><span>owner@lists.example.org</span><span class="tag is-primary is-light">quarantine</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Message</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Inbound domain</dt><dd><a href="in-domain.html">lists.example.org</a></dd></div>
|
||||||
|
<div><dt>Client</dt><dd class="sp-mono">203.0.113.140</dd></div>
|
||||||
|
<div><dt>Held</dt><dd>2026-09-21 13:59:02 UTC</dd></div>
|
||||||
|
<div><dt>Deleted on</dt><dd>2026-10-05 · in 14 days</dd></div>
|
||||||
|
<div><dt>Size</dt><dd>48 kB · 1 attachment (<span class="sp-mono">invoice.html</span>)</dd></div>
|
||||||
|
<div><dt>Would go to</dt><dd class="sp-mono">mx.internal:25</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Why it was held</h2><div class="sp-end">rspamd · score 9.8</div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Symbol</th><th>Score</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono is-size-7">PHISHING</td><td>5.0</td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">FORGED_SENDER</td><td>3.0</td></tr>
|
||||||
|
<tr><td class="sp-mono is-size-7">HTML_ATTACHMENT</td><td>1.8</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Headers</h2><div class="sp-end">The body and attachments are never rendered in the panel</div></div>
|
||||||
|
<pre class="sp-log"><span class="sp-d">Received:</span> from mail.partner-billing.example (unknown [203.0.113.140]) by mail.example.org
|
||||||
|
<span class="sp-d">From:</span> "Partner Billing" <invoice@partner-billing.example>
|
||||||
|
<span class="sp-d">Reply-To:</span> <span class="sp-w">payments.dept@freemail.example</span>
|
||||||
|
<span class="sp-d">To:</span> owner@lists.example.org
|
||||||
|
<span class="sp-d">Subject:</span> Outstanding invoice — action required
|
||||||
|
<span class="sp-d">Date:</span> Mon, 21 Sep 2026 13:58:57 +0000
|
||||||
|
<span class="sp-d">Authentication-Results:</span> <span class="sp-e">spf=fail dkim=none dmarc=fail</span></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">04</span><h2>Release</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">Hands the message to <span class="sp-mono">mx.internal:25</span> unchanged, as if it had just arrived. It leaves the quarantine.</p>
|
||||||
|
<form class="sp-form" method="post" action="in-quarantine.html">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="allow_sender"> Also add <span class="sp-mono">invoice@partner-billing.example</span> to the Allow list</label></div>
|
||||||
|
<button type="submit" class="button is-primary"><i class="ti ti-send"></i>Release to upstream</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box sp-danger-zone">
|
||||||
|
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-alert-triangle"></i></span><h2>Discard</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">Deletes the message now instead of in 14 days. The sender is not told. This cannot be undone.</p>
|
||||||
|
<form class="sp-form" method="post" action="in-quarantine.html">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="deny_sender" checked> Also add <span class="sp-mono">@partner-billing.example</span> to the Deny list</label></div>
|
||||||
|
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Discard message</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<!-- title: Quarantine | section: inbound | page: quarantine -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound</p>
|
||||||
|
<h1 class="title is-3">Quarantine</h1>
|
||||||
|
<p class="sp-lead">Mail the filter found suspicious enough to hold, but not to refuse. Release hands a message to its upstream as if it had just arrived; Discard deletes it. Anything left is deleted after 14 days.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head">
|
||||||
|
<span class="sp-no">01</span><h2>Held messages</h2>
|
||||||
|
<form class="sp-end" method="get" action="#">
|
||||||
|
<div class="select is-small"><select name="domain"><option>All domains</option><option>lists.example.org</option><option>acme.io</option></select></div>
|
||||||
|
<button type="submit" class="button is-small"><i class="ti ti-filter"></i>Filter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Held, UTC</th><th>From</th><th>To</th><th>Subject</th><th>Why</th><th>Size</th><th>Deleted in</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:59:02</td><td>invoice@partner-billing.example</td><td>owner@lists.example.org</td><td class="sp-clip"><a href="in-quarantine-message.html"><strong>Outstanding invoice — action required</strong></a></td><td class="sp-muted is-size-7">score 9.8 · PHISHING</td><td class="sp-muted sp-nowrap">48 kB</td><td class="sp-nowrap">14 days</td><td class="sp-actions"><a href="in-quarantine-message.html">Review</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-20 22:14:40</td><td>no-reply@shipping-update.example</td><td>jobs@acme.io</td><td class="sp-clip"><a href="in-quarantine-message.html"><strong>Your parcel could not be delivered</strong></a></td><td class="sp-muted is-size-7">score 8.1 · URL_SHORTENER</td><td class="sp-muted sp-nowrap">12 kB</td><td class="sp-nowrap">13 days</td><td class="sp-actions"><a href="in-quarantine-message.html">Review</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-09 07:02:11</td><td>m.ortega@supplier.example</td><td>owner@lists.example.org</td><td class="sp-clip"><a href="in-quarantine-message.html"><strong>Fwd: scanned contract (3 attachments)</strong></a></td><td class="sp-muted is-size-7">score 7.4 · MIME_ARCHIVE</td><td class="sp-muted sp-nowrap">2.9 MB</td><td class="sp-nowrap"><span class="tag is-warning is-light">2 days</span></td><td class="sp-actions"><a href="in-quarantine-message.html">Review</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot"><span>3 messages · 3.0 MB of 500 MB</span><span class="sp-end">A domain administrator sees only their assigned inbound domains</span></div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<!-- title: Inbound queue | section: inbound | page: queue -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Inbound</p>
|
||||||
|
<h1 class="title is-3">Inbound queue</h1>
|
||||||
|
<p class="sp-lead">Mail accepted on port 25 and still waiting for its upstream. The same Postfix queue as <a href="out-queue.html">outbound</a>, filtered by recipient domain.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Waiting</h2><div class="sp-end">Refreshes every 10 s</div></div>
|
||||||
|
<div class="sp-empty"><i class="ti ti-stack-2"></i>Nothing is waiting. Every accepted message has reached its upstream.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<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="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>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<!-- title: Sign in | shell: bare -->
|
||||||
|
<div class="columns is-gapless sp-signin">
|
||||||
|
<div class="column is-half sp-signin-brand">
|
||||||
|
<div>
|
||||||
|
<img src="../../selfpost-stamp.svg" alt="SelfPost">
|
||||||
|
<p>YOUR SMTP. YOUR RULES.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-half sp-signin-form">
|
||||||
|
<form class="sp-form" method="post" action="overview.html">
|
||||||
|
<p class="sp-kicker">mail.example.org</p>
|
||||||
|
<h1 class="title is-3">Sign in</h1>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="username">Username</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="username" name="username" autocomplete="username" required><span class="icon is-left"><i class="ti ti-user"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password">Password</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="password" name="password" type="password" autocomplete="current-password" required><span class="icon is-left"><i class="ti ti-lock"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-5"><button type="submit" class="button is-primary is-fullwidth">Sign in</button></div>
|
||||||
|
</form>
|
||||||
|
<p class="sp-foot">© 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<!-- title: New password · prod-server | section: outbound | page: domains -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-domains.html">Outbound domains</a> / <a href="out-domain.html">example.org</a> / Applications /</p>
|
||||||
|
<h1 class="title is-3">New password for <span class="sp-mono">prod-server</span></h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<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>
|
||||||
|
<div class="field has-addons"><div class="control is-expanded"><input class="input" readonly value="prod-server"></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">Password</label>
|
||||||
|
<div class="field has-addons"><div class="control is-expanded"><input class="input" readonly value="kT7v-Qm2x-Lp9d-Ue4s-Hn6b-Rz3w"></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div>
|
||||||
|
<p class="help">The previous password stopped working when this one was generated. If this one is lost, generate another.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="sp-box-foot"><a class="button is-primary" href="out-domain.html">Done — back to example.org</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-plug"></i></span><h2>Connect with</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Server</dt><dd class="sp-mono">mail.example.org</dd></div>
|
||||||
|
<div><dt>Port</dt><dd>465 SSL/TLS · 587 STARTTLS</dd></div>
|
||||||
|
<div><dt>May send as</dt><dd class="sp-mono">*@example.org</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<!-- title: prod-server · example.org | section: outbound | page: domains -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-domains.html">Outbound domains</a> / <a href="out-domain.html">example.org</a> / Applications /</p>
|
||||||
|
<h1 class="title is-3 sp-mono">prod-server</h1>
|
||||||
|
<p class="sp-lead">1 102 messages in 30 days · peak 96 msg/h · average 1.5 msg/h. The same form, empty, adds a new application.</p>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<a class="button" href="out-app-created.html"><i class="ti ti-key"></i>New password</a>
|
||||||
|
<a class="button is-danger is-light" href="#"><i class="ti ti-trash"></i>Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="sp-form" method="post" action="out-domain.html">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Sender</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="label" for="login">Login</label>
|
||||||
|
<div class="control"><input class="input sp-mono" id="login" name="login" value="prod-server" autocomplete="off" spellcheck="false" required></div>
|
||||||
|
<p class="help">Unique across domains; letters, digits, <code>.</code> <code>-</code> <code>_</code>.</p></div>
|
||||||
|
<div class="field"><label class="label" for="amode">May send as</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="amode" name="mode"><option selected>Any address of the domain</option><option>Specific addresses (list)</option></select></div></div></div>
|
||||||
|
<div class="field"><label class="label" for="addresses">Addresses</label>
|
||||||
|
<div class="control"><textarea class="textarea" id="addresses" name="addresses" rows="3" placeholder="alerts@example.org" disabled></textarea></div>
|
||||||
|
<p class="help">One per line. Used only with a list.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Client IP allow-list</h2><div class="sp-end"><span class="tag is-light">off</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="auth_ip_restrict"> Restrict to listed IPs</label></div>
|
||||||
|
<div class="field"><label class="label" for="ips">Allowed client IPs</label>
|
||||||
|
<div class="control"><textarea class="textarea sp-mono" id="ips" name="auth_allowed_ips" rows="3" placeholder="203.0.113.10"></textarea></div>
|
||||||
|
<p class="help">A login from anywhere else is refused with a 4xx at <code>MAIL FROM</code>. A sending control, fail-open — not an authentication boundary.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Rate limit</h2><div class="sp-end"><span class="tag is-success is-light">active</span></div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="label" for="rmode">Limit mode</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="rmode" name="rl_mode"><option>Use the domain limit</option><option selected>Manual</option><option>Auto (from statistics)</option></select></div></div></div>
|
||||||
|
<div class="sp-pair">
|
||||||
|
<div class="field"><label class="label" for="max">Messages (max 600)</label><div class="control"><input class="input" id="max" name="max_messages" type="number" min="1" max="600" value="200"></div></div>
|
||||||
|
<div class="field"><label class="label" for="win">Window, s</label><div class="control"><input class="input" id="win" name="window_seconds" type="number" min="1" value="3600"></div></div>
|
||||||
|
</div>
|
||||||
|
<p class="help">Overrides the domain limit (288 / h) for this application; may be higher or lower, never above level 1.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-primary">Save application</button>
|
||||||
|
<a class="button is-text" href="out-domain.html">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<!-- title: Delete example.org | section: outbound | page: domains -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-domains.html">Outbound domains</a> / <a href="out-domain.html">example.org</a> /</p>
|
||||||
|
<h1 class="title is-3">Delete example.org</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<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>
|
||||||
|
<ul class="sp-list-check">
|
||||||
|
<li><i class="ti ti-x"></i>permanently delete its DKIM signing key;</li>
|
||||||
|
<li><i class="ti ti-x"></i>delete <strong>both applications</strong> (<span class="sp-mono">prod-server</span>, <span class="sp-mono">alerts</span>) with their credentials and sender bindings;</li>
|
||||||
|
<li><i class="ti ti-x"></i>reload OpenDKIM so the domain is no longer signed.</li>
|
||||||
|
</ul>
|
||||||
|
<p class="sp-muted mb-4">The published DKIM record becomes invalid — remove it from DNS unless you plan to re-add the domain. Inbound domains are not touched.</p>
|
||||||
|
<form method="post" action="out-domains.html">
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete example.org</button>
|
||||||
|
<a class="button is-text" href="out-domain.html">Keep it</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no"><i class="ti ti-file-export"></i></span><h2>Moving it instead?</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p><a href="out-domain-settings.html#export">Export the domain</a> first. The file carries the key and the application passwords, so the new instance needs no DNS change.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<!-- title: Settings · example.org | section: outbound | page: domains -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-domains.html">Outbound domains</a> / <a href="out-domain.html">example.org</a> /</p>
|
||||||
|
<h1 class="title is-3">Domain settings</h1>
|
||||||
|
<p class="sp-lead">The things set once and rarely touched: where DMARC reports go, the sending ceiling, moving the domain elsewhere.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="reports">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>DMARC report address</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="rua">Aggregate reports (rua=)</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="rua" name="dmarc_rua_mode">
|
||||||
|
<option selected>Same as Settings (dmarc@mail.example.org)</option>
|
||||||
|
<option>SelfPost hosted (example.org@dmarc.mail.example.org)</option>
|
||||||
|
<option>No aggregate reports</option>
|
||||||
|
<option>Custom address</option>
|
||||||
|
</select></div></div>
|
||||||
|
<p class="help">The default comes from <a href="settings.html">Settings</a>. Changing it changes the DMARC record to publish.</p></div>
|
||||||
|
<button type="submit" class="button is-primary">Save report address</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="export">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Export domain</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-3">One file with the DKIM key and every application's password hash — import it on another SelfPost and the published DNS needs no change. <strong>The file is a secret.</strong></p>
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="encrypt" checked> Encrypt with a password (<code>.spde</code>)</label></div>
|
||||||
|
<div class="sp-pair">
|
||||||
|
<div class="field"><label class="label" for="encpw">Password</label><div class="control"><input class="input" id="encpw" type="password" placeholder="at least 12 characters"></div></div>
|
||||||
|
<div class="field"><label class="label" for="encpw2">Repeat password</label><div class="control"><input class="input" id="encpw2" type="password"></div></div>
|
||||||
|
</div>
|
||||||
|
<p class="help mb-3">Keep this password: SelfPost does not store it, and the file cannot be opened without it.</p>
|
||||||
|
<button type="submit" class="button"><i class="ti ti-download"></i>Export domain</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box" id="limit">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Rate limit</h2><div class="sp-end"><span class="tag is-success is-light">active</span></div><a class="sp-help" href="help.html#limits" title="Levels 1 and 2"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="mode">Limit mode</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="mode" name="mode"><option>Manual</option><option selected>Auto (from statistics)</option></select></div></div></div>
|
||||||
|
<div class="field"><label class="label" for="mult">Multiplier (1.5–10, default 3)</label>
|
||||||
|
<div class="control"><input class="input" id="mult" name="auto_multiplier" type="number" min="1.5" max="10" step="0.1" value="3"></div>
|
||||||
|
<p class="help">Computed limit: <strong>288</strong> messages / 3600 s. Peak was 96 msg/h. Last recalculated yesterday 03:00 UTC.</p></div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-primary">Save limit</button>
|
||||||
|
<button type="submit" class="button" formaction="#recalc">Recalculate now</button>
|
||||||
|
<button type="submit" class="button is-danger is-light" formaction="#clear">Remove limit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<p class="sp-muted mt-4">Applies to every sender on this domain and cannot exceed level 1 (600 messages / 3600 s per client IP, set in <code>.env</code>). An application may carry its own limit above or below this one.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<a class="button is-danger is-light" href="out-domain-delete.html"><i class="ti ti-trash"></i>Delete example.org…</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<!-- title: example.org · Outbound domains | section: outbound | page: domains -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<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>
|
||||||
|
<p class="sp-lead">1 284 messages in 30 days · peak 96 msg/h · average 1.8 msg/h</p>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-refresh"></i>Re-check DNS</button></form>
|
||||||
|
<a class="button is-primary" href="out-app.html"><i class="ti ti-plus"></i>Add application</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<aside class="menu sp-side">
|
||||||
|
<p class="menu-label">This domain</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a class="is-active" href="#dns"><i class="ti ti-world-check"></i>DNS records <span class="tag is-danger is-light">1</span></a></li>
|
||||||
|
<li><a href="#apps"><i class="ti ti-apps"></i>Applications <span class="tag is-light">2</span></a></li>
|
||||||
|
<li><a href="#connection"><i class="ti ti-plug"></i>Connection</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">See also</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="out-log.html"><i class="ti ti-list-details"></i>Log for this domain</a></li>
|
||||||
|
<li><a href="dmarc-domain.html"><i class="ti ti-report-analytics"></i>DMARC reports</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Rarely changed</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href="out-domain-settings.html#reports"><i class="ti ti-mail-forward"></i>Report address</a></li>
|
||||||
|
<li><a href="out-domain-settings.html#limit"><i class="ti ti-gauge"></i>Rate limit <span class="tag is-success is-light">auto</span></a></li>
|
||||||
|
<li><a href="out-domain-settings.html#export"><i class="ti ti-file-export"></i>Export</a></li>
|
||||||
|
<li><a class="has-text-danger" href="out-domain-delete.html"><i class="ti ti-trash has-text-danger"></i>Delete domain</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-9">
|
||||||
|
<div class="box" id="dns">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>DNS records</h2><div class="sp-end">Checked 3 min ago</div><a class="sp-help" href="help.html#dns" title="What to publish and why"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>DKIM</h3><span class="tag is-success is-light">published</span><small>Not a secret · selector <code>sp2026</code></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="sp2026._domainkey.example.org"></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="3" readonly>v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3kqV0mJd8e2f1Yb7Qw5uT9sLr4nHc6pZaXo1vKe0yWm2gBt8RjN5dCq3hUf7lPs9aEi4oMz6xTb1kGv0nYw…IDAQAB</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>SPF</h3><span class="tag is-danger is-light">mismatch</span><small>Merge into the existing record — never publish a second one</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="example.org"></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"><input class="input" readonly value="v=spf1 ip4:203.0.113.25 ~all"></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">In DNS now</label>
|
||||||
|
<div class="control"><input class="input is-danger" readonly value="v=spf1 include:_spf.google.com ~all"></div>
|
||||||
|
<p class="help is-danger">The published record does not authorize 203.0.113.25. Add <code>ip4:203.0.113.25</code> before <code>~all</code>. Shallow check: <code>include:</code> is not followed.</p></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>DMARC</h3><span class="tag is-success is-light">published</span><small>Report address <a href="out-domain-settings.html#reports">from Settings</a></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="_dmarc.example.org"></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"><input class="input" readonly value="v=DMARC1; p=none; rua=mailto:dmarc@mail.example.org"></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div>
|
||||||
|
<p class="help"><code>p=none</code> does not affect delivery. Tighten to <code>quarantine</code>, then <code>reject</code>, once the <a href="dmarc-domain.html">reports</a> look clean.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="apps">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Applications</h2><div class="sp-end">SASL logins that may send as this domain</div><a class="sp-help" href="help.html#apps" title="What an application is"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Login</th><th>May send as</th><th>30 days</th><th>Limits</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono"><strong>prod-server</strong></td><td>*@example.org</td><td class="sp-muted">1 102 msg · peak 96/h</td><td><span class="tag is-success is-light">200 / h</span></td>
|
||||||
|
<td class="sp-actions"><a href="out-app.html">Edit</a><a href="out-app-created.html">New password</a><a class="has-text-danger" href="#">Delete</a></td></tr>
|
||||||
|
<tr><td class="sp-mono"><strong>alerts</strong></td><td>alerts@example.org, noc@example.org</td><td class="sp-muted">182 msg · peak 14/h</td><td><span class="tag is-light">domain</span> <span class="tag is-success is-light">2 IPs</span></td>
|
||||||
|
<td class="sp-actions"><a href="out-app.html">Edit</a><a href="out-app-created.html">New password</a><a class="has-text-danger" href="#">Delete</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot">New password and Delete ask for confirmation; the old password stops working at once.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="connection">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Connection</h2><div class="sp-end">The same for every domain</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Server</dt><dd class="sp-mono">mail.example.org</dd></div>
|
||||||
|
<div><dt>Port 465</dt><dd>SSL/TLS (implicit)</dd></div>
|
||||||
|
<div><dt>Port 587</dt><dd>STARTTLS (submission)</dd></div>
|
||||||
|
<div><dt>Authentication</dt><dd>Application login, required on every port</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<!-- title: Outbound domains | section: outbound | page: domains -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Outbound</p>
|
||||||
|
<h1 class="title is-3">Outbound domains</h1>
|
||||||
|
<p class="sp-lead">Domains this relay signs and sends for. Each gets a DKIM key and its own application logins.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Add a domain</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="out-domain.html">
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded has-icons-left"><input class="input" name="name" type="text" placeholder="example.com" autocomplete="off" autocapitalize="none" spellcheck="false" required><span class="icon is-left"><i class="ti ti-world"></i></span></div>
|
||||||
|
<div class="control"><button type="submit" class="button is-primary"><i class="ti ti-plus"></i>Add domain</button></div>
|
||||||
|
</div>
|
||||||
|
<p class="help">A DKIM key is generated at once; the next page shows the three DNS records to publish.
|
||||||
|
Moving a domain from another SelfPost? <a href="backup.html#import">Import it</a> instead — the key and passwords come across.</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Domains</h2><div class="sp-end">3 domains · DNS cached a few minutes</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>DNS</th><th>Selector</th><th>Apps</th><th>30 days</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-danger is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td class="sp-muted sp-mono">sp2026</td><td>2</td><td class="sp-muted">1 284 msg · peak 96/h</td><td class="sp-actions"><a href="out-domain.html">Open</a><a class="has-text-danger" href="out-domain-delete.html">Delete</a></td></tr>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>shop.example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td class="sp-muted sp-mono">sp2026</td><td>1</td><td class="sp-muted">18 920 msg · peak 410/h</td><td class="sp-actions"><a href="out-domain.html">Open</a><a class="has-text-danger" href="out-domain-delete.html">Delete</a></td></tr>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>notify.acme.io</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-warning is-light">DMARC</span></td><td class="sp-muted sp-mono">sp2025</td><td>3</td><td class="sp-muted">402 msg · peak 22/h</td><td class="sp-actions"><a href="out-domain.html">Open</a><a class="has-text-danger" href="out-domain-delete.html">Delete</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot">Each tag is one check. Green — published and correct; amber — published but weak; red — missing or wrong.</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<!-- title: Outbound log | section: outbound | page: log -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Outbound</p>
|
||||||
|
<h1 class="title is-3">Outbound log</h1>
|
||||||
|
<p class="sp-lead">Every message an application handed to the relay. Rows older than 30 days are deleted — change that in <a href="settings.html">Settings</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head">
|
||||||
|
<span class="sp-no">01</span><h2>Messages</h2>
|
||||||
|
<form class="sp-end" method="get" action="#">
|
||||||
|
<div class="select is-small"><select name="domain"><option>All domains</option><option>example.org</option><option>shop.example.org</option><option>notify.acme.io</option></select></div>
|
||||||
|
<div class="select is-small"><select name="app"><option>All applications</option><option>prod-server</option><option>alerts</option></select></div>
|
||||||
|
<div class="select is-small"><select name="status"><option>Any status</option><option>sent</option><option>deferred</option><option>bounced</option><option>rate-limited</option></select></div>
|
||||||
|
<button type="submit" class="button is-small"><i class="ti ti-filter"></i>Filter</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Time, UTC</th><th>From</th><th>To</th><th>Subject</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>shop@shop.example.org</td><td>m.keller@gmx.de</td><td class="sp-clip">Your order #48213 has shipped</td><td><span class="tag is-success is-light">sent</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 14:01:50</td><td>shop@shop.example.org</td><td>anna.lind@outlook.com</td><td class="sp-clip">Your order #48212 has shipped</td><td><span class="tag is-success is-light">sent</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:58:07</td><td>alerts@example.org</td><td>noc@example.org</td><td class="sp-clip">[FIRING] disk usage above 90 % on db-2</td><td><span class="tag is-warning is-light">deferred</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:41:19</td><td>no-reply@notify.acme.io</td><td>j.doe@nonexistent.example</td><td class="sp-clip">Reset your password</td><td><span class="tag is-danger is-light">bounced</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:40:02</td><td>prod@example.org</td><td>billing@partner.example</td><td class="sp-clip">Invoice 2026-0917</td><td><span class="tag is-light">rate-limited</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
<tr><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:12:44</td><td>shop@shop.example.org</td><td>p.novak@seznam.cz</td><td class="sp-clip">We received your return request</td><td><span class="tag is-success is-light">sent</span></td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot"><span>Page 1 of 412</span><a href="#">Older →</a><span class="sp-end">New rows appear on their own</span></div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!-- title: Message · Outbound log | section: outbound | page: log -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="out-log.html">Outbound log</a> /</p>
|
||||||
|
<h1 class="title is-4">[FIRING] disk usage above 90 % on db-2</h1>
|
||||||
|
<p class="sp-route"><span>alerts@example.org</span><i class="ti ti-arrow-right sp-muted"></i><span>noc@example.org</span><span class="tag is-warning is-light">deferred</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Message</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<dl class="sp-facts">
|
||||||
|
<div><dt>Domain</dt><dd><a href="out-domain.html">example.org</a></dd></div>
|
||||||
|
<div><dt>Application</dt><dd class="sp-mono"><a href="out-app.html">alerts</a></dd></div>
|
||||||
|
<div><dt>Accepted</dt><dd>2026-09-21 13:58:07 UTC</dd></div>
|
||||||
|
<div><dt>Status reported</dt><dd>2026-09-21 13:58:09 UTC</dd></div>
|
||||||
|
<div><dt>Queue id</dt><dd class="sp-mono">4XcB7k2Jm9z1</dd></div>
|
||||||
|
<div><dt>Journal id</dt><dd class="sp-mono">184220</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>History</h2></div>
|
||||||
|
<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="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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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="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=<a81f0c@alertmanager>
|
||||||
|
<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=<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)</span></pre>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<!-- title: Outbound queue | section: outbound | page: queue -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Outbound</p>
|
||||||
|
<h1 class="title is-3">Outbound queue</h1>
|
||||||
|
<p class="sp-lead">Messages accepted from applications and still waiting for the receiving server. Refreshes every 10 s.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Waiting</h2><div class="sp-end">1 message · 3.1 kB</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Queue id</th><th>Arrived, UTC</th><th>From</th><th>To</th><th>Last reply</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td class="sp-mono">4XcB7k2Jm9z1</td><td class="sp-mono sp-muted sp-nowrap is-size-7">09-21 13:58:07</td><td>alerts@example.org</td><td>noc@example.org</td><td class="sp-muted">451 4.7.1 Greylisted, try again in 5 minutes</td><td class="sp-actions"><a href="out-message.html">Details</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<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="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>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<!-- title: Overview | section: overview -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<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>
|
||||||
|
<p class="sp-lead">The TLS certificate expires in 12 days and has not renewed yet.</p>
|
||||||
|
</div>
|
||||||
|
<div class="sp-muted is-size-7">Updated 14:02:31 UTC<br>refreshes every 10 s</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Server health</h2><div class="sp-end"><a href="health.html">All details</a></div><a class="sp-help" href="help.html#checks" title="What these checks mean"><i class="ti ti-help-circle"></i></a></div>
|
||||||
|
<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="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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Outbound domains</h2><div class="sp-end"><a href="out-domains.html">Manage</a></div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>DNS</th><th>Apps</th><th>30 days</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-danger is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td>2</td><td class="sp-muted">1 284 msg</td></tr>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>shop.example.org</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-success is-light">DMARC</span></td><td>1</td><td class="sp-muted">18 920 msg</td></tr>
|
||||||
|
<tr><td><a href="out-domain.html"><strong>notify.acme.io</strong></a></td><td><span class="tag is-success is-light">DKIM</span> <span class="tag is-success is-light">SPF</span> <span class="tag is-warning is-light">DMARC</span></td><td>3</td><td class="sp-muted">402 msg</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Inbound domains</h2><div class="sp-end"><a href="in-domains.html">Manage</a></div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Domain</th><th>MX</th><th>Upstream</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><a href="in-domain.html"><strong>lists.example.org</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-muted sp-mono is-size-7">mx.internal:25</td></tr>
|
||||||
|
<tr><td><a href="in-domain.html"><strong>acme.io</strong></a></td><td><span class="tag is-success is-light">MX</span></td><td class="sp-muted sp-mono is-size-7">10.0.4.12:2525</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<!-- title: Preflight | section: server | page: preflight -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-postmark sp-warn"><span>PREFLIGHT</span><b>WARN</b><span>1 OF 7 CHECKS</span></div>
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Preflight</h1>
|
||||||
|
<p class="sp-lead">Is this installation ready to deliver mail? Run it after the first start, after moving the server, or when receivers start refusing you. Health watches what runs; Preflight checks what the outside world sees.</p>
|
||||||
|
</div>
|
||||||
|
<form method="post" action="#"><button type="submit" class="button"><i class="ti ti-refresh"></i>Run again</button></form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Checks</h2><div class="sp-end">Ran 14:02 UTC · 3.1 s</div></div>
|
||||||
|
<table class="table is-fullwidth">
|
||||||
|
<thead><tr><th>Check</th><th>Result</th><th>Found</th><th>What to do</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><strong>Reverse DNS</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">203.0.113.25 ⇄ mail.example.org</td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>TLS certificate</strong></td><td><span class="tag is-warning is-light">warn</span></td><td class="is-size-7">Valid for <span class="sp-mono">mail.example.org</span>, expires in 12 days</td><td>Renewal has not run. Check that port 80 reaches the ACME client and look for <code>acme</code> in the <a href="system-log.html">system log</a>.</td></tr>
|
||||||
|
<tr><td><strong>Port 25</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">220 mail.example.org ESMTP</td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>Ports 465, 587</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">TLS handshake, 220 banner on both</td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>HELO name</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">mail.example.org</td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>Proxy headers</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="is-size-7">Peer <span class="sp-mono">172.18.0.2</span> is trusted; <span class="sp-mono">X-Forwarded-Proto: https</span></td><td class="sp-muted">—</td></tr>
|
||||||
|
<tr><td><strong>DKIM signing</strong></td><td><span class="tag is-success is-light">ok</span></td><td class="sp-mono is-size-7">inet:127.0.0.1:8891 answers</td><td class="sp-muted">—</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="sp-box-foot">Ports are dialled from inside the container at the public address; a firewall that treats inside and outside differently can make this pass while the world is still blocked.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Send a test e-mail</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="field"><label class="label" for="rcpt">Recipient</label>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded has-icons-left"><input class="input" id="rcpt" name="recipient" type="email" placeholder="you@elsewhere.example" required><span class="icon is-left"><i class="ti ti-mail"></i></span></div>
|
||||||
|
<div class="control"><button type="submit" class="button is-primary"><i class="ti ti-send"></i>Send</button></div>
|
||||||
|
</div>
|
||||||
|
<p class="help">Sent from <span class="sp-mono">postmaster@mail.example.org</span> through the local Postfix. This confirms the relay accepted it — follow the delivery in the <a href="out-log.html">outbound log</a>.</p></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Not checked here</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<p class="mb-2">SPF, DKIM and DMARC records are per domain — each <a href="out-domains.html">outbound domain</a> shows its own.</p>
|
||||||
|
<p class="sp-muted">Whether a receiver finally accepted the test message is not tracked; only that it left.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<!-- title: Settings | section: server | page: settings -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Settings</h1>
|
||||||
|
<p class="sp-lead">Defaults for the whole instance. Your own username and password are under <a href="account.html">Account</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="sp-form" method="post" action="#">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>DMARC aggregate reports</h2><div class="sp-end"><span class="tag is-success is-light">authorized</span></div></div>
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="field"><label class="label" for="rua">Default report address</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="rua" name="dmarc_report_email" type="email" value="dmarc@mail.example.org"><span class="icon is-left"><i class="ti ti-mail"></i></span></div>
|
||||||
|
<p class="help">The default <code>rua=</code> for every outbound domain; a domain may override it. An address on <code>mail.example.org</code> is received and parsed by SelfPost itself.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="sp-record">
|
||||||
|
<div class="sp-record-head"><h3>Report authorization</h3><span class="tag is-success is-light">published</span><small>Needed when <code>rua=</code> points at another domain</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="*._report._dmarc.mail.example.org"></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"><input class="input" readonly value="v=DMARC1"></div><div class="control"><button type="button" class="button"><i class="ti ti-copy"></i>Copy</button></div></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-5">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Log retention</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="label" for="ret">Keep outbound log rows, days</label>
|
||||||
|
<div class="control"><input class="input" id="ret" name="send_log_retention_days" type="number" min="7" max="365" value="30" required></div>
|
||||||
|
<p class="help">The main driver of <code>/data</code> growth. Does not affect <code>mail.log</code> rotation (fourteen daily files).</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" id="rate-limits">
|
||||||
|
<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="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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons"><button type="submit" class="button is-primary">Save settings</button></div>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<!-- title: Create administrator | shell: bare -->
|
||||||
|
<div class="columns is-gapless sp-signin">
|
||||||
|
<div class="column is-half sp-signin-brand">
|
||||||
|
<div>
|
||||||
|
<img src="../../selfpost-stamp.svg" alt="SelfPost">
|
||||||
|
<p>YOUR SMTP. YOUR RULES.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-half sp-signin-form">
|
||||||
|
<form class="sp-form" method="post" action="overview.html">
|
||||||
|
<p class="sp-kicker">First run · one-time link</p>
|
||||||
|
<h1 class="title is-3">Create administrator</h1>
|
||||||
|
<p class="sp-muted mb-4">This link creates the single global administrator. After you submit, it stops working for good.</p>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="username">Username</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="username" name="username" autocomplete="username" required><span class="icon is-left"><i class="ti ti-user"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password">Password</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="password" name="password" type="password" autocomplete="new-password" required><span class="icon is-left"><i class="ti ti-lock"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password_confirm">Confirm password</label>
|
||||||
|
<div class="control has-icons-left"><input class="input" id="password_confirm" name="password_confirm" type="password" autocomplete="new-password" required><span class="icon is-left"><i class="ti ti-lock"></i></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="field mt-5"><button type="submit" class="button is-primary is-fullwidth">Create administrator</button></div>
|
||||||
|
</form>
|
||||||
|
<p class="sp-foot">© 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<!-- title: System log | section: server | page: system-log -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">System log</h1>
|
||||||
|
<p class="sp-lead">The tail of <code>mail.log</code> — Postfix, OpenDKIM and the milters. For one message, open it from a log page instead.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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="sp-d">Sep 21 14:01:52</span> postfix/smtp[2131]: 4XcB9q0Lw2z1: to=<m.keller@gmx.de>, 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=<dev@lists.example.org>, 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=<noc@example.org>, 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 <sales@lists.example.org>: 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=<jobs@acme.io>, 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=<j.doe@nonexistent.example>, 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>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!-- title: Delete acme-ops | section: server | page: users -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="users.html">Users</a> / <a href="user.html">acme-ops</a> /</p>
|
||||||
|
<h1 class="title is-3">Delete acme-ops</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<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>acme-ops</strong> will:</p>
|
||||||
|
<ul class="sp-list-check">
|
||||||
|
<li><i class="ti ti-x"></i>end any signed-in session of this user at once;</li>
|
||||||
|
<li><i class="ti ti-x"></i>remove the assignments to <strong>notify.acme.io</strong> (outbound) and <strong>acme.io</strong> (inbound).</li>
|
||||||
|
</ul>
|
||||||
|
<p class="sp-muted mb-4">The domains, their applications and passwords, upstreams and recipients are not affected.</p>
|
||||||
|
<form method="post" action="users.html">
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete acme-ops</button>
|
||||||
|
<a class="button is-text" href="user.html">Keep it</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<!-- title: acme-ops · Users | section: server | page: users -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="users.html">Users</a> /</p>
|
||||||
|
<h1 class="title is-3">acme-ops</h1>
|
||||||
|
<p class="sp-lead">The same form, empty, creates a new user.</p>
|
||||||
|
</div>
|
||||||
|
<a class="button is-danger is-light" href="user-delete.html"><i class="ti ti-trash"></i>Delete user</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="sp-form" method="post" action="users.html">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Sign-in</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="label" for="username">Username</label><div class="control"><input class="input" id="username" name="username" value="acme-ops" autocomplete="username" spellcheck="false" required></div></div>
|
||||||
|
<div class="field"><label class="label" for="password">Password</label><div class="control"><input class="input" id="password" name="password" type="password" autocomplete="new-password"></div>
|
||||||
|
<p class="help">Leave empty to keep the current one.</p></div>
|
||||||
|
<div class="field"><label class="label" for="role">Role</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="role" name="role"><option selected>Domain administrator</option><option>Global administrator</option></select></div></div>
|
||||||
|
<p class="help">A global administrator sees everything, including Server, and is the only one who adds or deletes domains. The only global administrator cannot be demoted or deleted.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Outbound domains</h2><div class="sp-end">DNS records, applications, log</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> example.org</label></div>
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> shop.example.org</label></div>
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids" checked> notify.acme.io</label></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Inbound domains</h2><div class="sp-end">Upstream, recipients, log</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="inbound_domain_ids"> lists.example.org</label></div>
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="inbound_domain_ids" checked> acme.io</label></div>
|
||||||
|
<p class="help">Granted separately from outbound: the same name on both lists is two assignments. A domain administrator needs at least one domain on either list.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-primary">Save user</button>
|
||||||
|
<a class="button is-text" href="users.html">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<!-- title: Users | section: server | page: users -->
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Users</h1>
|
||||||
|
<p class="sp-lead">Who may sign in to this panel. A domain administrator sees only the domains assigned to them — outbound and inbound are granted separately.</p>
|
||||||
|
</div>
|
||||||
|
<a class="button is-primary" href="user.html"><i class="ti ti-user-plus"></i>Create user</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification is-success is-light"><i class="ti ti-circle-check"></i>User <strong>acme-ops</strong> created.</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Panel users</h2><div class="sp-end">3 users</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Username</th><th>Role</th><th>Outbound domains</th><th>Inbound domains</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><strong>admin</strong> <span class="tag is-light">you</span></td><td><span class="tag is-primary is-light">global</span></td><td class="sp-muted">All</td><td class="sp-muted">All</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
|
||||||
|
<tr><td><strong>shop-team</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">shop.example.org</td><td class="sp-muted">—</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
|
||||||
|
<tr><td><strong>acme-ops</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">notify.acme.io</td><td class="sp-muted">acme.io</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>System log · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item is-active" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="health.html"><i class="ti ti-heartbeat"></i>Health</a><a href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a><a href="system-log.html" aria-current="page"><i class="ti ti-file-text"></i>System log</a><a href="backup.html"><i class="ti ti-archive"></i>Backup</a><a href="users.html"><i class="ti ti-users"></i>Users</a><a href="settings.html"><i class="ti ti-settings"></i>Settings</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">System log</h1>
|
||||||
|
<p class="sp-lead">The tail of <code>mail.log</code> — Postfix, OpenDKIM and the milters. For one message, open it from a log page instead.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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="sp-d">Sep 21 14:01:52</span> postfix/smtp[2131]: 4XcB9q0Lw2z1: to=<m.keller@gmx.de>, 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=<dev@lists.example.org>, 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=<noc@example.org>, 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 <sales@lists.example.org>: 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=<jobs@acme.io>, 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=<j.doe@nonexistent.example>, 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>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/* The accepted direction: C ("Counter", Bulma) as the base, the postal
|
||||||
|
signature of A (postmark, numbered mono section heads, the stamp's
|
||||||
|
perforation) and the icons of B. Bulma carries navbar, dropdowns, columns,
|
||||||
|
boxes, tables, tags, menu, forms; what follows is brand mapping plus the
|
||||||
|
handful of components that are SelfPost's own. */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bulma-family-primary: var(--sp-sans);
|
||||||
|
--bulma-family-secondary: var(--sp-sans);
|
||||||
|
--bulma-family-code: var(--sp-mono);
|
||||||
|
--bulma-primary-h: 10deg; --bulma-primary-s: 45%; --bulma-primary-l: 33%;
|
||||||
|
--bulma-link-h: 10deg; --bulma-link-s: 45%; --bulma-link-l: 33%;
|
||||||
|
--bulma-success-h: 138deg; --bulma-success-s: 42%; --bulma-success-l: 30%;
|
||||||
|
--bulma-warning-h: 36deg; --bulma-warning-s: 85%; --bulma-warning-l: 55%;
|
||||||
|
--bulma-danger-h: 4deg; --bulma-danger-s: 76%; --bulma-danger-l: 40%;
|
||||||
|
--bulma-radius: 6px; --bulma-radius-large: 10px;
|
||||||
|
--bulma-body-size: 15px;
|
||||||
|
--bulma-link-text: var(--sp-brick);
|
||||||
|
--bulma-link-text-hover: var(--sp-brick-dark);
|
||||||
|
}
|
||||||
|
.button.is-primary, .button.is-primary:hover { color: #fff; }
|
||||||
|
a strong { color: inherit; }
|
||||||
|
html, body { background: var(--sp-paper); }
|
||||||
|
body { color: var(--sp-ink); min-height: 100vh; display: flex; flex-direction: column; }
|
||||||
|
main { flex: 1; }
|
||||||
|
code { background: var(--sp-well); color: var(--sp-ink); border-radius: 3px; padding: .1em .35em; font-size: .85em; }
|
||||||
|
.ti { font-size: 1.15em; vertical-align: -.12em; }
|
||||||
|
.button .ti { margin-right: .35rem; }
|
||||||
|
.sp-muted { color: var(--sp-muted); }
|
||||||
|
.sp-mono { font-family: var(--sp-mono); }
|
||||||
|
.sp-nowrap { white-space: nowrap; }
|
||||||
|
|
||||||
|
/* ---- navbar, the perforated edge under it, sibling pages below */
|
||||||
|
.navbar.is-primary { --bulma-navbar-height: 4rem; }
|
||||||
|
.navbar-brand img { max-height: 3rem; height: 3rem; }
|
||||||
|
.navbar.is-primary .navbar-item, .navbar.is-primary .navbar-link { color: var(--sp-on-brick); font-weight: 500; }
|
||||||
|
.navbar.is-primary .navbar-link::after { border-color: var(--sp-on-brick); }
|
||||||
|
.navbar.is-primary .navbar-link .ti { margin-right: .4rem; }
|
||||||
|
.navbar-dropdown { border-top: 0; box-shadow: 0 8px 24px rgb(18 22 28 / .14); }
|
||||||
|
.navbar.is-primary .navbar-dropdown .navbar-item { color: var(--sp-ink); font-weight: 400; padding-right: 2rem; }
|
||||||
|
.navbar.is-primary .navbar-dropdown .navbar-item .ti { margin-right: .6rem; color: var(--sp-muted); }
|
||||||
|
.navbar.is-primary .navbar-dropdown .navbar-item.is-active { color: var(--sp-brick); font-weight: 600; background: var(--sp-brick-tint); }
|
||||||
|
.navbar.is-primary .navbar-dropdown .navbar-item.is-active .ti { color: var(--sp-brick); }
|
||||||
|
.navbar.is-primary .sp-current > .navbar-link, .navbar.is-primary a.navbar-item.sp-current { background: rgb(0 0 0 / .18); }
|
||||||
|
/* The stamp's own edge: a row of punched half-circles hanging off the bar. */
|
||||||
|
.sp-perf {
|
||||||
|
height: 7px; background: radial-gradient(circle at 7px 0, var(--sp-brick) 5px, transparent 5.6px);
|
||||||
|
background-size: 14px 7px; background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
.sp-subnav { border-bottom: 1px solid var(--sp-rule); margin-top: -1px; }
|
||||||
|
.sp-subnav .container { display: flex; gap: 1.8rem; overflow-x: auto; }
|
||||||
|
.sp-subnav a { color: var(--sp-muted); padding: .75rem 0 .65rem; white-space: nowrap; border-bottom: 2px solid transparent; }
|
||||||
|
.sp-subnav a .ti { margin-right: .4rem; }
|
||||||
|
.sp-subnav a:hover { color: var(--sp-ink); }
|
||||||
|
.sp-subnav a[aria-current] { color: var(--sp-ink); font-weight: 600; border-bottom-color: var(--sp-brick); }
|
||||||
|
|
||||||
|
/* ---- page head */
|
||||||
|
.sp-page { padding-block: 1.8rem 2.5rem; }
|
||||||
|
.sp-head { display: flex; align-items: center; gap: 1.2rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
|
||||||
|
.sp-head .sp-grow { flex: 1; min-width: 14rem; }
|
||||||
|
.sp-kicker { font-family: var(--sp-mono); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase; color: var(--sp-muted); }
|
||||||
|
.sp-kicker a { color: inherit; }
|
||||||
|
.sp-kicker a:hover { color: var(--sp-brick); }
|
||||||
|
.title { color: var(--sp-ink); font-weight: 600; }
|
||||||
|
.sp-head .title { margin: 0; overflow-wrap: anywhere; }
|
||||||
|
.sp-head .sp-lead { color: var(--sp-muted); margin-top: .2rem; max-width: 48rem; }
|
||||||
|
.sp-head .buttons { margin: 0; }
|
||||||
|
.sp-head .buttons > * { margin-bottom: 0; }
|
||||||
|
|
||||||
|
/* ---- postmark */
|
||||||
|
.sp-postmark {
|
||||||
|
flex: none; width: 5.4rem; height: 5.4rem; border-radius: 50%; transform: rotate(-9deg);
|
||||||
|
border: 2px solid currentColor; outline: 1px solid currentColor; outline-offset: 3px; margin: .3rem .5rem;
|
||||||
|
display: grid; place-content: center; text-align: center; color: var(--sp-ok);
|
||||||
|
font-family: var(--sp-mono); font-weight: 600; letter-spacing: .08em; line-height: 1.15;
|
||||||
|
}
|
||||||
|
.sp-postmark.sp-warn { color: #a66a0e; }
|
||||||
|
.sp-postmark.sp-fail { color: var(--sp-fail); }
|
||||||
|
.sp-postmark b { font-size: 1.2rem; display: block; }
|
||||||
|
.sp-postmark span { font-size: .52rem; display: block; }
|
||||||
|
|
||||||
|
/* ---- box with a numbered mono head */
|
||||||
|
.box { box-shadow: none; border: 1px solid var(--sp-rule); padding: 0; overflow: hidden; }
|
||||||
|
.box > .sp-box-head {
|
||||||
|
display: flex; align-items: center; gap: .7rem; padding: .7rem 1.25rem; border-bottom: 1px solid var(--sp-rule);
|
||||||
|
}
|
||||||
|
.sp-box-head .sp-no { font-family: var(--sp-mono); font-size: .75rem; font-weight: 600; color: var(--sp-brick); }
|
||||||
|
.sp-box-head h2 { font-family: var(--sp-mono); font-size: .8rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; margin: 0; }
|
||||||
|
.sp-box-head .sp-end { margin-left: auto; color: var(--sp-muted); font-size: .85rem; display: flex; gap: .6rem; align-items: center; }
|
||||||
|
.sp-box-head .sp-help { color: var(--sp-muted); line-height: 1; }
|
||||||
|
.sp-box-head .sp-help:hover { color: var(--sp-brick); }
|
||||||
|
.sp-box-head .sp-end + .sp-help { margin-left: 0; }
|
||||||
|
.sp-box-head h2 + .sp-help { margin-left: auto; }
|
||||||
|
.notification { border: 1px solid transparent; padding: .8rem 1.25rem; }
|
||||||
|
.notification.is-success.is-light { border-color: var(--sp-ok-rule); }
|
||||||
|
.notification.is-danger.is-light { border-color: var(--sp-fail-rule); }
|
||||||
|
.notification .ti { margin-right: .5rem; }
|
||||||
|
.box > .sp-box-body { padding: 1.25rem; }
|
||||||
|
.box > .sp-box-body > :last-child { margin-bottom: 0; }
|
||||||
|
.box > .table { margin: 0; }
|
||||||
|
.sp-box-foot .sp-end { margin-left: auto; }
|
||||||
|
.sp-box-head form.sp-end { margin-block: -.2rem; }
|
||||||
|
.box > .sp-box-foot {padding: .7rem 1.25rem; border-top: 1px solid var(--sp-rule); color: var(--sp-muted); font-size: .85rem; display: flex; gap: 1rem; align-items: center; }
|
||||||
|
.box.sp-danger-zone { border-color: var(--sp-fail-rule); }
|
||||||
|
.box.sp-danger-zone .sp-box-head { background: var(--sp-fail-bg); border-color: var(--sp-fail-rule); }
|
||||||
|
.box.sp-credential { border-color: #e0c874; }
|
||||||
|
.box.sp-credential .sp-box-head { background: #fcf6e4; border-color: #e0c874; }
|
||||||
|
|
||||||
|
.table { background: transparent; }
|
||||||
|
.table thead th { font-family: var(--sp-mono); font-size: .7rem; font-weight: 400; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); }
|
||||||
|
.table td, .table th { vertical-align: middle; padding: .6rem .75rem; }
|
||||||
|
.box > .table td:first-child, .box > .table th:first-child { padding-left: 1.25rem; }
|
||||||
|
.box > .table td:last-child, .box > .table th:last-child { padding-right: 1.25rem; }
|
||||||
|
.table td.sp-actions { text-align: right; white-space: nowrap; }
|
||||||
|
.table td.sp-actions a { margin-left: .9rem; }
|
||||||
|
.table td.sp-clip { max-width: 18rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.tag { font-family: var(--sp-mono); font-weight: 600; letter-spacing: .04em; }
|
||||||
|
.sp-empty { padding: 2.2rem 1.25rem; text-align: center; color: var(--sp-muted); }
|
||||||
|
.sp-empty .ti { font-size: 2rem; display: block; margin-bottom: .4rem; color: var(--sp-rule-strong); }
|
||||||
|
|
||||||
|
/* ---- health cards */
|
||||||
|
.sp-health { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); gap: 0; }
|
||||||
|
.sp-health > a {
|
||||||
|
display: flex; gap: .8rem; align-items: center; padding: .95rem 1.1rem; color: var(--sp-ink);
|
||||||
|
border-right: 1px solid var(--sp-rule); border-bottom: 1px solid var(--sp-rule); margin: 0 -1px -1px 0;
|
||||||
|
}
|
||||||
|
.sp-health > a:hover { background: var(--sp-paper); }
|
||||||
|
.sp-h-icon { flex: none; width: 2.6rem; height: 2.6rem; border-radius: 8px; display: grid; place-content: center; background: var(--sp-ok-bg); color: var(--sp-ok); }
|
||||||
|
.sp-h-icon .ti { font-size: 1.5rem; vertical-align: 0; }
|
||||||
|
.sp-health > a.sp-warn { background: var(--sp-warn-bg); }
|
||||||
|
.sp-health > a.sp-warn .sp-h-icon { background: #f6e2b8; color: #8a5510; }
|
||||||
|
.sp-health > a.sp-fail { background: var(--sp-fail-bg); }
|
||||||
|
.sp-health > a.sp-fail .sp-h-icon { background: #f5d3cc; color: var(--sp-fail); }
|
||||||
|
.sp-h-name { font-family: var(--sp-mono); font-size: .66rem; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); }
|
||||||
|
.sp-h-value { font-weight: 600; line-height: 1.25; }
|
||||||
|
.sp-h-sub { font-size: .8rem; color: var(--sp-muted); }
|
||||||
|
|
||||||
|
/* ---- facts, records, side menu */
|
||||||
|
.sp-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 1rem 1.5rem; margin: 0; }
|
||||||
|
.sp-facts dt { font-family: var(--sp-mono); font-size: .68rem; letter-spacing: .08em; text-transform: uppercase; color: var(--sp-muted); }
|
||||||
|
.sp-facts dd { margin: 0; overflow-wrap: anywhere; }
|
||||||
|
.sp-facts dd.sp-big { font-size: 1.5rem; font-weight: 600; line-height: 1.2; }
|
||||||
|
.sp-side { position: sticky; top: 1.5rem; }
|
||||||
|
.menu-label { font-family: var(--sp-mono); letter-spacing: .1em; }
|
||||||
|
.menu-list a { display: flex; align-items: center; gap: .55rem; }
|
||||||
|
.menu-list a .ti { color: var(--sp-muted); }
|
||||||
|
.menu-list a.is-active .ti { color: inherit; }
|
||||||
|
.menu-list a .tag { margin-left: auto; }
|
||||||
|
.sp-record { padding: 1.25rem; }
|
||||||
|
.sp-record + .sp-record { border-top: 1px solid var(--sp-rule); }
|
||||||
|
.sp-record-head { display: flex; align-items: center; gap: .6rem; margin-bottom: .75rem; }
|
||||||
|
.sp-record-head h3 { font-size: 1rem; font-weight: 600; margin: 0; }
|
||||||
|
.sp-record-head small { margin-left: auto; color: var(--sp-muted); text-align: right; }
|
||||||
|
.sp-record .label, .sp-form .label { font-size: .78rem; font-weight: 500; color: var(--sp-ink-2); margin-bottom: .25rem; }
|
||||||
|
.sp-record .label { font-weight: 400; color: var(--sp-muted); }
|
||||||
|
.input[readonly], .textarea[readonly] { font-family: var(--sp-mono); font-size: .82rem; background: var(--sp-well); box-shadow: none; }
|
||||||
|
.textarea[readonly] { resize: none; min-height: 0; }
|
||||||
|
.sp-record .field:not(:last-child) { margin-bottom: .6rem; }
|
||||||
|
.sp-row { display: grid; grid-template-columns: 1fr 7rem; gap: .75rem; }
|
||||||
|
.sp-pair { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; }
|
||||||
|
|
||||||
|
/* ---- logs, timeline, help */
|
||||||
|
.sp-log {
|
||||||
|
margin: 0; padding: 1rem 1.25rem; background: #191c21; color: #d9d5cc; border-radius: 0;
|
||||||
|
font-family: var(--sp-mono); font-size: .78rem; line-height: 1.6; overflow-x: auto; white-space: pre;
|
||||||
|
}
|
||||||
|
.sp-log .sp-w { color: #e0b15c; } .sp-log .sp-e { color: #ef8f82; } .sp-log .sp-d { color: #7f8894; }
|
||||||
|
.sp-timeline { list-style: none; margin: 0; padding: 0; }
|
||||||
|
.sp-timeline li { position: relative; padding: 0 0 1.1rem 1.6rem; border-left: 2px solid var(--sp-rule); margin-left: .4rem; }
|
||||||
|
.sp-timeline li:last-child { padding-bottom: 0; border-left-color: transparent; }
|
||||||
|
.sp-timeline li::before { content: ""; position: absolute; left: -7px; top: .25rem; width: 12px; height: 12px; border-radius: 50%; background: var(--sp-ok); box-shadow: 0 0 0 3px #fff; }
|
||||||
|
.sp-timeline li.sp-warn::before { background: #d9962b; }
|
||||||
|
.sp-timeline li.sp-fail::before { background: var(--sp-fail); }
|
||||||
|
.sp-timeline li.sp-pending::before { background: #fff; border: 2px solid var(--sp-rule-strong); }
|
||||||
|
.sp-timeline time { font-family: var(--sp-mono); font-size: .75rem; color: var(--sp-muted); display: block; }
|
||||||
|
.sp-route { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; font-family: var(--sp-mono); font-size: .9rem; margin-top: .3rem; }
|
||||||
|
.sp-help-topic + .sp-help-topic { border-top: 1px solid var(--sp-rule); }
|
||||||
|
.sp-help-topic { padding: 1rem 1.25rem; }
|
||||||
|
.sp-help-topic h3 { font-weight: 600; margin-bottom: .3rem; }
|
||||||
|
.sp-list-check { list-style: none; margin: 0 0 1rem; }
|
||||||
|
.sp-list-check li { padding-left: 1.7rem; position: relative; margin-bottom: .45rem; }
|
||||||
|
.sp-list-check li .ti { position: absolute; left: 0; top: .15rem; color: var(--sp-fail); }
|
||||||
|
|
||||||
|
/* ---- signed-out pages */
|
||||||
|
.sp-signin { min-height: 100vh; margin: 0; }
|
||||||
|
.sp-signin-brand { background: var(--sp-brick); display: grid; place-content: center; text-align: center; padding: 3rem; position: relative; }
|
||||||
|
.sp-signin-brand::after {
|
||||||
|
content: ""; position: absolute; top: 0; bottom: 0; right: -7px; width: 7px; z-index: 1;
|
||||||
|
background: radial-gradient(circle at 0 7px, var(--sp-brick) 5px, transparent 5.6px); background-size: 7px 14px;
|
||||||
|
}
|
||||||
|
.sp-signin-brand img { width: 20rem; max-width: 100%; height: auto; }
|
||||||
|
.sp-signin-brand p { font-family: var(--sp-mono); letter-spacing: .18em; font-size: .8rem; color: var(--sp-on-brick); margin-top: 1.6rem; }
|
||||||
|
.sp-signin-form { display: flex; flex-direction: column; justify-content: center; padding: 3rem; }
|
||||||
|
.sp-signin-form form { width: 22rem; max-width: 100%; margin-inline: auto; }
|
||||||
|
.sp-foot { color: var(--sp-muted); font-size: .8rem; padding-block: 1.5rem 0; }
|
||||||
|
.sp-foot a { color: inherit; text-decoration: underline; }
|
||||||
|
.sp-signin-form .sp-foot { text-align: center; margin-top: 2.5rem; }
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sp-row, .sp-pair { grid-template-columns: 1fr; }
|
||||||
|
.sp-record-head { flex-wrap: wrap; }
|
||||||
|
.sp-record-head small { margin-left: 0; text-align: left; flex-basis: 100%; }
|
||||||
|
.sp-postmark { display: none; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Delete acme-ops · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item is-active" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="health.html"><i class="ti ti-heartbeat"></i>Health</a><a href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a><a href="system-log.html"><i class="ti ti-file-text"></i>System log</a><a href="backup.html"><i class="ti ti-archive"></i>Backup</a><a href="users.html" aria-current="page"><i class="ti ti-users"></i>Users</a><a href="settings.html"><i class="ti ti-settings"></i>Settings</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="users.html">Users</a> / <a href="user.html">acme-ops</a> /</p>
|
||||||
|
<h1 class="title is-3">Delete acme-ops</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-7">
|
||||||
|
<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>acme-ops</strong> will:</p>
|
||||||
|
<ul class="sp-list-check">
|
||||||
|
<li><i class="ti ti-x"></i>end any signed-in session of this user at once;</li>
|
||||||
|
<li><i class="ti ti-x"></i>remove the assignments to <strong>notify.acme.io</strong> (outbound) and <strong>acme.io</strong> (inbound).</li>
|
||||||
|
</ul>
|
||||||
|
<p class="sp-muted mb-4">The domains, their applications and passwords, upstreams and recipients are not affected.</p>
|
||||||
|
<form method="post" action="users.html">
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-danger"><i class="ti ti-trash"></i>Delete acme-ops</button>
|
||||||
|
<a class="button is-text" href="user.html">Keep it</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>acme-ops · Users · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item is-active" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="health.html"><i class="ti ti-heartbeat"></i>Health</a><a href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a><a href="system-log.html"><i class="ti ti-file-text"></i>System log</a><a href="backup.html"><i class="ti ti-archive"></i>Backup</a><a href="users.html" aria-current="page"><i class="ti ti-users"></i>Users</a><a href="settings.html"><i class="ti ti-settings"></i>Settings</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker"><a href="users.html">Users</a> /</p>
|
||||||
|
<h1 class="title is-3">acme-ops</h1>
|
||||||
|
<p class="sp-lead">The same form, empty, creates a new user.</p>
|
||||||
|
</div>
|
||||||
|
<a class="button is-danger is-light" href="user-delete.html"><i class="ti ti-trash"></i>Delete user</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="sp-form" method="post" action="users.html">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Sign-in</h2></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="label" for="username">Username</label><div class="control"><input class="input" id="username" name="username" value="acme-ops" autocomplete="username" spellcheck="false" required></div></div>
|
||||||
|
<div class="field"><label class="label" for="password">Password</label><div class="control"><input class="input" id="password" name="password" type="password" autocomplete="new-password"></div>
|
||||||
|
<p class="help">Leave empty to keep the current one.</p></div>
|
||||||
|
<div class="field"><label class="label" for="role">Role</label>
|
||||||
|
<div class="control"><div class="select is-fullwidth"><select id="role" name="role"><option selected>Domain administrator</option><option>Global administrator</option></select></div></div>
|
||||||
|
<p class="help">A global administrator sees everything, including Server, and is the only one who adds or deletes domains. The only global administrator cannot be demoted or deleted.</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">02</span><h2>Outbound domains</h2><div class="sp-end">DNS records, applications, log</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> example.org</label></div>
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids"> shop.example.org</label></div>
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="domain_ids" checked> notify.acme.io</label></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">03</span><h2>Inbound domains</h2><div class="sp-end">Upstream, recipients, log</div></div>
|
||||||
|
<div class="sp-box-body">
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="inbound_domain_ids"> lists.example.org</label></div>
|
||||||
|
<div class="field"><label class="checkbox"><input type="checkbox" name="inbound_domain_ids" checked> acme.io</label></div>
|
||||||
|
<p class="help">Granted separately from outbound: the same name on both lists is two assignments. A domain administrator needs at least one domain on either list.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button is-primary">Save user</button>
|
||||||
|
<a class="button is-text" href="users.html">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="light">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Users · SelfPost</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3/dist/tabler-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../shared/brand.css">
|
||||||
|
<link rel="stylesheet" href="theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar is-primary" aria-label="main navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand"><a class="navbar-item" href="overview.html"><img src="../../selfpost-stamp-compact.svg" alt="SelfPost"></a></div>
|
||||||
|
<div class="navbar-menu is-active">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="overview.html">Overview</a>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="out-domains.html">Outbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="out-domains.html"><i class="ti ti-world-upload"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="out-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="out-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="dmarc.html"><i class="ti ti-report-analytics"></i>DMARC reports</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="in-domains.html">Inbound</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="in-domains.html"><i class="ti ti-world-download"></i>Domains</a>
|
||||||
|
<a class="navbar-item" href="in-log.html"><i class="ti ti-list-details"></i>Log</a>
|
||||||
|
<a class="navbar-item" href="in-queue.html"><i class="ti ti-stack-2"></i>Queue</a>
|
||||||
|
<a class="navbar-item" href="in-quarantine.html"><i class="ti ti-mail-pause"></i>Quarantine</a>
|
||||||
|
<a class="navbar-item" href="in-filter-lists.html"><i class="ti ti-filter"></i>Filter lists</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable sp-current">
|
||||||
|
<a class="navbar-link" href="health.html">Server</a>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a class="navbar-item" href="health.html"><i class="ti ti-heartbeat"></i>Health</a>
|
||||||
|
<a class="navbar-item" href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a>
|
||||||
|
<a class="navbar-item" href="system-log.html"><i class="ti ti-file-text"></i>System log</a>
|
||||||
|
<a class="navbar-item" href="backup.html"><i class="ti ti-archive"></i>Backup</a>
|
||||||
|
<a class="navbar-item is-active" href="users.html"><i class="ti ti-users"></i>Users</a>
|
||||||
|
<a class="navbar-item" href="settings.html"><i class="ti ti-settings"></i>Settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<a class="navbar-link" href="account.html"><i class="ti ti-user-circle"></i>admin</a>
|
||||||
|
<div class="navbar-dropdown is-right">
|
||||||
|
<a class="navbar-item" href="account.html"><i class="ti ti-key"></i>Account</a>
|
||||||
|
<a class="navbar-item" href="help.html"><i class="ti ti-help"></i>Help</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a class="navbar-item" href="login.html"><i class="ti ti-logout"></i>Sign out</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="sp-perf" aria-hidden="true"></div>
|
||||||
|
<div class="sp-subnav"><div class="container"><a href="health.html"><i class="ti ti-heartbeat"></i>Health</a><a href="preflight.html"><i class="ti ti-checklist"></i>Preflight</a><a href="system-log.html"><i class="ti ti-file-text"></i>System log</a><a href="backup.html"><i class="ti ti-archive"></i>Backup</a><a href="users.html" aria-current="page"><i class="ti ti-users"></i>Users</a><a href="settings.html"><i class="ti ti-settings"></i>Settings</a></div></div>
|
||||||
|
<main>
|
||||||
|
<div class="container sp-page">
|
||||||
|
<div class="sp-head">
|
||||||
|
<div class="sp-grow">
|
||||||
|
<p class="sp-kicker">Server</p>
|
||||||
|
<h1 class="title is-3">Users</h1>
|
||||||
|
<p class="sp-lead">Who may sign in to this panel. A domain administrator sees only the domains assigned to them — outbound and inbound are granted separately.</p>
|
||||||
|
</div>
|
||||||
|
<a class="button is-primary" href="user.html"><i class="ti ti-user-plus"></i>Create user</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="notification is-success is-light"><i class="ti ti-circle-check"></i>User <strong>acme-ops</strong> created.</div>
|
||||||
|
|
||||||
|
<div class="box">
|
||||||
|
<div class="sp-box-head"><span class="sp-no">01</span><h2>Panel users</h2><div class="sp-end">3 users</div></div>
|
||||||
|
<table class="table is-fullwidth is-hoverable">
|
||||||
|
<thead><tr><th>Username</th><th>Role</th><th>Outbound domains</th><th>Inbound domains</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><strong>admin</strong> <span class="tag is-light">you</span></td><td><span class="tag is-primary is-light">global</span></td><td class="sp-muted">All</td><td class="sp-muted">All</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
|
||||||
|
<tr><td><strong>shop-team</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">shop.example.org</td><td class="sp-muted">—</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
|
||||||
|
<tr><td><strong>acme-ops</strong></td><td><span class="tag is-light">domain admin</span></td><td class="sp-muted">notify.acme.io</td><td class="sp-muted">acme.io</td><td class="sp-actions"><a href="user.html">Edit</a></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="sp-foot">SelfPost 1.8.0 · © 2026 Mix · <a href="#">License (AGPL-3.0)</a> · <a href="#">Source</a> · No warranty</footer>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/* Brand layer shared by the three redesign directions: the panel's own fonts
|
||||||
|
and the palette of the mark (docs/assets/selfpost-proof.html). Each
|
||||||
|
direction maps these tokens onto its library's variables in its own
|
||||||
|
theme.css — nothing here knows about Pico, Tabler or Bulma.
|
||||||
|
|
||||||
|
Mockup note: no inline style="" and no inline <script> anywhere in this
|
||||||
|
folder, so every screen is drawable under the panel's CSP as it stands. */
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "IBM Plex Sans"; font-style: normal; font-weight: 100 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("../../../../internal/web/view/static/ibm-plex-sans.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("../../../../internal/web/view/static/ibm-plex-mono-400.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "IBM Plex Mono"; font-style: normal; font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("../../../../internal/web/view/static/ibm-plex-mono-600.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--sp-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||||
|
--sp-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||||
|
|
||||||
|
--sp-paper: #f4f2ed; /* page */
|
||||||
|
--sp-stamp-paper: #f3ede1; /* the mark's own paper */
|
||||||
|
--sp-sheet: #fff;
|
||||||
|
--sp-ink: #12161c;
|
||||||
|
--sp-ink-2: #2a313b;
|
||||||
|
--sp-muted: #6b7684;
|
||||||
|
--sp-rule: #dedcd7;
|
||||||
|
--sp-rule-strong: #cbc8c1;
|
||||||
|
--sp-well: #efede9; /* code / read-only values */
|
||||||
|
|
||||||
|
--sp-brick: #7a3b2e;
|
||||||
|
--sp-brick-dark: #632f25;
|
||||||
|
--sp-brick-tint: #ede4de;
|
||||||
|
--sp-on-brick: #f6ede2;
|
||||||
|
|
||||||
|
--sp-ok: #2c6b43; --sp-ok-bg: #edf6ef; --sp-ok-rule: #c2e0cc;
|
||||||
|
--sp-warn: #8a5510; --sp-warn-bg: #fbf2e2; --sp-warn-rule: #ebd5a6;
|
||||||
|
--sp-fail: #b42318; --sp-fail-bg: #fbedea; --sp-fail-rule: #efccc4;
|
||||||
|
--sp-idle: #6b7684; --sp-idle-bg: #efede9; --sp-idle-rule: #dedcd7;
|
||||||
|
}
|
||||||
@@ -121,9 +121,12 @@ No separate feature flag beyond inbound relay + existing antispam env vars.
|
|||||||
|
|
||||||
### UI
|
### UI
|
||||||
|
|
||||||
New nav item **Inbound spam** (or subsection under **Inbound**) — journal table
|
The journal is **Inbound › Log** and list management is a new **Inbound ›
|
||||||
with filters (decision, domain, date range), list management on the same page or
|
Filter lists** page (global-only: the lists are instance-wide) — see
|
||||||
a tab. Mockup: add `docs/assets/panel-ui/inbound_spam.html` in the panel step.
|
[panel-redesign.md](panel-redesign.md) § Room for roadmap items. The journal
|
||||||
|
table has filters (decision, domain, date range). Mockups are drawn first, in
|
||||||
|
`docs/assets/panel-redesign/panel/src/` under that plan's design contract —
|
||||||
|
not in the superseded `docs/assets/panel-ui/`.
|
||||||
|
|
||||||
## Schema (migration `0011_inbound_spam_log.sql`)
|
## Schema (migration `0011_inbound_spam_log.sql`)
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ so we can compare approaches before committing.
|
|||||||
reference to them) under `/data`, retention, release workflow, and a wider
|
reference to them) under `/data`, retention, release workflow, and a wider
|
||||||
attack/backup surface — closer to "mini-mailbox" than pure relay.
|
attack/backup surface — closer to "mini-mailbox" than pure relay.
|
||||||
|
|
||||||
|
## Mockup
|
||||||
|
|
||||||
|
A target look is drawn — list and detail —
|
||||||
|
[in-quarantine](../assets/panel-redesign/panel/in-quarantine.html),
|
||||||
|
[in-quarantine-message](../assets/panel-redesign/panel/in-quarantine-message.html)
|
||||||
|
— under the design contract of [panel-redesign.md](panel-redesign.md). It
|
||||||
|
assumes answers to the questions below (SelfPost holds the mail, release goes
|
||||||
|
to the upstream, 14 days, headers only in the panel); it does not settle them.
|
||||||
|
|
||||||
## Open questions (to decide before a checklist)
|
## Open questions (to decide before a checklist)
|
||||||
|
|
||||||
1. **Where mail lives**
|
1. **Where mail lives**
|
||||||
|
|||||||
+289
-152
@@ -1,197 +1,334 @@
|
|||||||
# Plan: panel-redesign
|
# Plan: panel-redesign
|
||||||
|
|
||||||
**Status:** candidate
|
**Status:** candidate
|
||||||
**Date:** 2026-09-08
|
**Date:** 2026-09-21 (replaces the 2026-09-08 givens)
|
||||||
**Version:** `1.x` MINOR once implementation is agreed; this file is givens, not a
|
**Version:** `2.0.0` MAJOR — a breaking release by the owner's decision (§ No compatibility).
|
||||||
build plan.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What this file is
|
## What this file is
|
||||||
|
|
||||||
Source data for a visual redesign of the control panel. Mockups have not been
|
The design contract for the control panel and the plan to implement it. The
|
||||||
drawn yet. Implementation in `internal/web` waits on those mockups and on
|
design itself is drawn and accepted:
|
||||||
roadmap status **agreed**.
|
|
||||||
|
|
||||||
HTML click-throughs in [docs/assets/panel-ui/](../assets/panel-ui/index.html)
|
| Artefact | Role |
|
||||||
are **not** the brief. They were an earlier layout experiment; new screens are
|
|---|---|
|
||||||
drawn from this file's vocabulary, not copied from that folder.
|
| [docs/assets/panel-redesign/panel/](../assets/panel-redesign/index.html) | 33 screens — **the** reference for what each page looks like |
|
||||||
|
| [panel/components.html](../assets/panel-redesign/panel/components.html) | every component in every state — the only parts a page may be built from |
|
||||||
|
| [panel/outlines.json](../assets/panel-redesign/panel/outlines.json) | the component skeleton of each screen, generated from the mockups |
|
||||||
|
| [panel/check.py](../assets/panel-redesign/panel/check.py) | the contract as a program; its rules are ported to Go tests below |
|
||||||
|
|
||||||
|
When this file and a mockup disagree, the mockup wins and this file is fixed.
|
||||||
|
When an implementation and a mockup disagree, the implementation is wrong.
|
||||||
|
|
||||||
|
The 2026-09-08 givens ("no component library, Web Awesome layout CSS only") are
|
||||||
|
withdrawn. `docs/assets/panel-ui/` and the stage-1 directions `a/`, `b/`, `c/`
|
||||||
|
are history, not a brief.
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
|
|
||||||
Stop hand-tuning every card on every page to hide meaningless empty space.
|
A panel an operator opens a few times a year and still finds their way in: the
|
||||||
Operators should get a panel whose width rules are the same on Status, a
|
important things in view, details one or two clicks away, one visual language
|
||||||
delete-confirm, and a six-column log: a form stays at a reading measure, ops
|
on every page, and no page that tunes its own width.
|
||||||
content uses the remaining column, and a card fills its cell.
|
|
||||||
|
|
||||||
This is a visual and CSS-grammar change. Routes, POST behaviour, and htmx
|
## Delivery constraints (unchanged)
|
||||||
fragments stay as they are unless a later mockup proves a control must move.
|
|
||||||
|
|
||||||
## Pain (why)
|
|
||||||
|
|
||||||
[panel.css](../../internal/web/view/static/panel.css) already juggles several
|
|
||||||
widths (`64rem` column, `48rem` measure, `main.wide`, `.card.narrow`,
|
|
||||||
`page-login`) and two names for one pairing (`.split` everywhere, `.pair` on
|
|
||||||
DMARC). Each new screen picks a width by eye. On a wide monitor that leaves
|
|
||||||
voids; stretching a single input to the window is unreadable. The fix is a
|
|
||||||
layout grammar, not another button library.
|
|
||||||
|
|
||||||
## Delivery constraints (fixed unless product says otherwise)
|
|
||||||
|
|
||||||
The panel is not an SPA. Changing any of these needs an explicit decision, not
|
|
||||||
a mockup flourish:
|
|
||||||
|
|
||||||
| Constraint | Where |
|
| Constraint | Where |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Go `html/template`, server-rendered pages | [internal/web/view/](../../internal/web/view/) |
|
| Go `html/template`, server-rendered; htmx 2.x for polled fragments | [internal/web/view/](../../internal/web/view/) |
|
||||||
| Vendored htmx 2.x for polling fragments | [layout.html](../../internal/web/view/templates/layout.html), [development.md](../development.md) § Vendored front-end |
|
|
||||||
| Assets embedded (`//go:embed`), no CDN at runtime | [view.go](../../internal/web/view/view.go) |
|
| Assets embedded (`//go:embed`), no CDN at runtime | [view.go](../../internal/web/view/view.go) |
|
||||||
| CSP `default-src 'self'` — no inline script or `style=""` | [security.go](../../internal/web/security.go), `TestNoTemplateUsesInlineScriptOrStyle` |
|
| CSP `default-src 'self'` — no inline script, no `style=""` | [security.go](../../internal/web/security.go) |
|
||||||
| State changes are native HTML POST | templates under `internal/web/view/templates/` |
|
| State changes are native HTML POST; no script is the only way to submit | templates |
|
||||||
| AGPL-3.0; new front-end must be MIT-or-better and listed in NOTICE | [NOTICE](../../NOTICE), [development.md](../development.md) |
|
| New front-end is MIT-or-better and listed in NOTICE | [NOTICE](../../NOTICE) |
|
||||||
|
|
||||||
JavaScript is not a product requirement. [security.md](../security.md) records
|
Bulma was chosen partly because it needs nothing from this table relaxed: no
|
||||||
progressive enhancement so **authorization does not depend on JS** (origin
|
JavaScript, no `data:` URIs.
|
||||||
check, session, RBAC). `data-confirm` is deliberately not a security boundary.
|
|
||||||
The redesign may use JS for chrome (copy, `<dialog>`). It must not make a
|
|
||||||
script the only way to submit a form.
|
|
||||||
|
|
||||||
Brand carriers today: stamp, brick, IBM Plex — [product.md](../product.md) and
|
## Information architecture
|
||||||
[docs/assets/selfpost-proof.html](../assets/selfpost-proof.html). New mockups
|
|
||||||
may restyle; they must not silently drop the mark or invent a second product
|
|
||||||
look.
|
|
||||||
|
|
||||||
## Screens in scope (information architecture)
|
Navigation is grouped by mail direction. Page titles carry the group
|
||||||
|
(**Outbound domains**, **Inbound log**); menu entries do not repeat it.
|
||||||
|
|
||||||
Same pages as the live panel. Visibility flags (inbound, DMARC, global vs
|
| Group | Pages | Today |
|
||||||
domain-admin) stay as in [web.go](../../internal/web/web.go).
|
|---|---|---|
|
||||||
|
| Overview | Overview | `/status` (verdicts only) |
|
||||||
|
| Outbound | Domains · Log · Queue · DMARC reports | `/domains`, `/deliveries`, `/mail-queue`, `/dmarc` |
|
||||||
|
| Inbound | Domains · Log · Queue | `/inbound`; Log and Queue are new |
|
||||||
|
| Server | Health · System log · Backup · Users · Settings | Health is new (the tables that left `/status`) |
|
||||||
|
| User menu | Account · Help · Sign out | Account is the credentials half of `/settings` |
|
||||||
|
|
||||||
| Kind | Pages |
|
A hidden page leaves no gap in the menu. The inbound and DMARC feature flags
|
||||||
|
stay as in [web.go](../../internal/web/web.go); who sees what changes — see
|
||||||
|
§ Who sees what.
|
||||||
|
|
||||||
|
## No compatibility
|
||||||
|
|
||||||
|
**Decided 2026-09-21 (owner): nothing is kept compatible across the redesign,
|
||||||
|
and the migration chain is deleted to zero.** The redesign is `2.0.0`.
|
||||||
|
|
||||||
|
- **URLs.** Old paths are not redirected; they are gone. Bookmarks break once.
|
||||||
|
- **Schema.** `internal/store/migrations/` is emptied and replaced by one
|
||||||
|
baseline, `0001_init.sql`, equal to the 2.0 schema (including the new
|
||||||
|
assignment table below). No historical `admin` table, no create-then-drop.
|
||||||
|
This is [schema-squash](../roadmap.md#schema-squash) with its trigger now
|
||||||
|
decided and without the upgrade gate that item planned: 2.0 starts from an
|
||||||
|
empty data directory.
|
||||||
|
- **Docs.** `schema-migrations.md` shrinks to the 2.0 baseline; the 1.x chain
|
||||||
|
stays in git history.
|
||||||
|
|
||||||
|
The only live installation is the owner's, and it is set up again on 2.0.
|
||||||
|
Nothing is built to ease that move, and nothing needs to be.
|
||||||
|
|
||||||
|
## Who sees what
|
||||||
|
|
||||||
|
**Decided 2026-09-21 (owner): a domain administrator manages inbound domains
|
||||||
|
too, and that is granted separately from outbound.** Today every inbound
|
||||||
|
handler is `requireGlobal()` and `user_domains` references outbound domains
|
||||||
|
only.
|
||||||
|
|
||||||
|
| | Global administrator | Domain administrator |
|
||||||
|
|---|---|---|
|
||||||
|
| Overview, Server | yes | no (404) |
|
||||||
|
| Outbound: domain, applications, settings, log, DMARC | all domains | assigned outbound domains |
|
||||||
|
| Inbound: domain (upstream, recipients), log | all domains | assigned inbound domains |
|
||||||
|
| Add / delete a domain, either direction | yes | no |
|
||||||
|
| Queues, either direction | yes | no, until the queue is a filterable table |
|
||||||
|
| Inbound › Quarantine | all domains | assigned inbound domains |
|
||||||
|
| Inbound › Filter lists, Server › Preflight | yes | no — the lists are instance-wide |
|
||||||
|
| Account, Help | yes | yes |
|
||||||
|
|
||||||
|
- New table `user_inbound_domains (user_id, inbound_domain_id)`, mirroring
|
||||||
|
`user_domains`; both live in the 2.0 baseline. The same name on both lists
|
||||||
|
is two assignments — the two kinds of domain stay separate entities.
|
||||||
|
- The user form has two lists (mockup `user.html`); a domain administrator
|
||||||
|
needs at least one assignment on either.
|
||||||
|
- A group with nothing assigned is absent from that user's menu.
|
||||||
|
- Authorization helpers come in pairs: `requireDomain(id)` and
|
||||||
|
`requireInboundDomain(id)`; lists and logs are filtered by assignment in the
|
||||||
|
query, not in the template.
|
||||||
|
|
||||||
|
## Routes
|
||||||
|
|
||||||
|
**Decided 2026-09-21 (owner): paths are renamed to follow the new names and
|
||||||
|
structure.** A URL reads like the menu: `/<group>/<page>`, an entity under its
|
||||||
|
list, an action under the thing it changes.
|
||||||
|
|
||||||
|
| Screen (mockup) | New | Today |
|
||||||
|
|---|---|---|
|
||||||
|
| overview | `GET /overview` | `/status` |
|
||||||
|
| health | `GET /server/health` · `POST …/recheck` · `POST …/reload` | tables of `/status` · `/status/recheck` · `/reload` |
|
||||||
|
| preflight | `GET /server/preflight` · `POST …/preflight/run` · `POST …/preflight/test-email` | — (preflight; its plan says `/preflight`) |
|
||||||
|
| out-domains | `GET, POST /outbound/domains` | `/domains` |
|
||||||
|
| out-domain | `GET /outbound/domains/{id}` · `POST …/dns-recheck` | `/domains/{id}` |
|
||||||
|
| out-domain-settings | `GET /outbound/domains/{id}/settings` · `POST …/settings/reports` · `…/settings/ratelimit` · `…/settings/ratelimit/recalc` · `…/settings/export` | cards of `/domains/{id}` · `…/dmarc` · `…/ratelimit` · `…/ratelimit/recalc` · `…/export` |
|
||||||
|
| out-domain-delete | `GET, POST /outbound/domains/{id}/delete` | `/domains/{id}/delete` |
|
||||||
|
| out-app | `GET, POST /outbound/domains/{id}/applications/new` · `GET, POST …/applications/{aid}` · `POST …/{aid}/ratelimit/recalc` · `POST …/{aid}/delete` | `POST /domains/{id}/applications` · `POST /applications/{aid}/mode`, `/authips`, `/ratelimit` · `…/recalc` · `…/delete` |
|
||||||
|
| out-app-created | the response of `POST …/applications/new` and `POST …/{aid}/password`; no GET path, `Cache-Control: no-store` | a card on `/domains/{id}` after the POST |
|
||||||
|
| out-log · out-message | `GET /outbound/log` · `GET /outbound/log/{id}` | `/deliveries` · `/deliveries/{id}` |
|
||||||
|
| out-queue | `GET /outbound/queue` | `/mail-queue` |
|
||||||
|
| dmarc · dmarc-domain · dmarc-report | `GET /outbound/dmarc` · `…/dmarc/domains/{id}` · `…/dmarc/reports/{id}` | `/dmarc/…` |
|
||||||
|
| in-domains · in-domain | `GET, POST /inbound/domains` · `GET /inbound/domains/{id}` · `POST …/dns-recheck`, `…/upstream`, `…/recipients` | `/inbound` · `/inbound/{id}/…` |
|
||||||
|
| in-domain-delete | `GET, POST /inbound/domains/{id}/delete` | `/inbound/{id}/delete` |
|
||||||
|
| in-log · in-queue | `GET /inbound/log` · `GET /inbound/queue` | — (new, see below) |
|
||||||
|
| in-quarantine · in-quarantine-message | `GET /inbound/quarantine` · `GET …/quarantine/{id}` · `POST …/{id}/release` · `POST …/{id}/discard` | — (inbound-quarantine) |
|
||||||
|
| in-filter-lists | `GET, POST /inbound/filter-lists` · `POST …/filter-lists/{id}/delete` | — (inbound-antispam-panel) |
|
||||||
|
| system-log | `GET /server/log` | `/system-log` |
|
||||||
|
| backup | `GET, POST /server/backup` · `POST /server/backup/import` | `/backup` · `POST /domains/import` |
|
||||||
|
| users · user · user-delete | `/server/users` · `…/new` · `…/{uid}` · `…/{uid}/delete` | `/users/…` |
|
||||||
|
| settings | `GET, POST /server/settings` | the instance half of `/settings` |
|
||||||
|
| account | `GET, POST /account` | the credentials half of `/settings` (today `/account` redirects there) |
|
||||||
|
| help · components | `GET /help` · `GET /server/components` | `/help` · — |
|
||||||
|
|
||||||
|
Unchanged: `/healthz`, `/license`, `/static/`, `/setup/{token}`, `/login`,
|
||||||
|
`/logout`. `GET /` goes to `/overview`, or to `/outbound/domains` for a domain
|
||||||
|
administrator.
|
||||||
|
|
||||||
|
Rules that come with the rename:
|
||||||
|
|
||||||
|
- **One fragment name.** Every polled page answers `<page>/fragment`
|
||||||
|
(`/overview/fragment`, `/outbound/log/fragment`, …) — today it is
|
||||||
|
`fragment`, `rows` and `body`.
|
||||||
|
- **The application form is one POST.** The mockup has a single *Save
|
||||||
|
application*; sender, IP allow-list and rate limit are validated and saved
|
||||||
|
together, or not at all. The three separate POSTs go away.
|
||||||
|
- **No redirects.** Old paths, GET and POST alike, are removed (§ No
|
||||||
|
compatibility), including today's `/account` → `/settings` redirect. The
|
||||||
|
full list goes under *Removed* in the CHANGELOG.
|
||||||
|
- **Authorization can follow the tree — for `/server/` only.** Everything
|
||||||
|
under `/server/` is global-only, so `requireGlobal()` can guard that subtree
|
||||||
|
once instead of once per handler. `/outbound/` and `/inbound/` are checked
|
||||||
|
per domain (§ Who sees what). Both are security-relevant: they go through
|
||||||
|
the security review, and a per-route test asserts 404 for a domain
|
||||||
|
administrator on every path they must not reach — including another
|
||||||
|
tenant's domain id.
|
||||||
|
- `TestRoutesFollowNavigation` — every path in this table is registered with
|
||||||
|
the listed methods, every menu entry points at a registered path, every old
|
||||||
|
path answers 404, and no template contains an old path.
|
||||||
|
|
||||||
|
**Needs its own roadmap decision before it is built** (the mockups show the
|
||||||
|
target; none of this is smuggled in with the restyle):
|
||||||
|
|
||||||
|
- Queue as a table — parse `postqueue -j` instead of printing `postqueue -p`.
|
||||||
|
- Inbound queue — the same queue filtered by recipient domain.
|
||||||
|
- Inbound log — rides on `inbound-antispam-panel` (`1.10.0`).
|
||||||
|
- Server › Health — a route for the tables removed from Overview, and the
|
||||||
|
home of *Reload configuration* (today a card on `/status`).
|
||||||
|
|
||||||
|
Until a row above is agreed, its page ships in the old content inside the new
|
||||||
|
shell, and its menu entry is hidden if the page does not exist.
|
||||||
|
|
||||||
|
## Room for roadmap items
|
||||||
|
|
||||||
|
The mockups draw today's features, the four rows above, and the screens of
|
||||||
|
the open roadmap items — none of them needed a new component or layout. A
|
||||||
|
mockup of a feature is its target look, not a decision to build it: each item
|
||||||
|
keeps its own status and plan. From here on a feature's screens are drawn
|
||||||
|
mockup-first in this system (`panel/src/`, `build.py`, `check.py --write`) as
|
||||||
|
the first step of that feature's plan — not invented during implementation.
|
||||||
|
|
||||||
|
| Roadmap item | Where it lands | Built from | Drawn |
|
||||||
|
|---|---|---|---|
|
||||||
|
| inbound-antispam-panel (agreed, `1.10.0`) | journal = **Inbound › Log** (add client IP and engine to the mockup's columns; `quarantine` joins the decisions); lists = new **Inbound › Filter lists**, global-only because the lists are instance-wide | list layout: add form + two tables, `status_tag`, filter form in `box_head`; the log links *Allow / Deny sender* to the lists | `in-log`, `in-filter-lists` |
|
||||||
|
| inbound-quarantine (candidate) | new **Inbound › Quarantine**, list + detail with *Release* / *Discard* | list layout; detail like `out-message` with `facts`, `log_pane` for headers only, Release beside a `sp-danger-zone` Discard | `in-quarantine`, `in-quarantine-message` — the open questions of its plan (where mail lives, what release means, retention, RBAC) stay open; the mockup assumes SelfPost-held mail, release to upstream, 14 days, delegated like the rest of Inbound |
|
||||||
|
| preflight (candidate) | new **Server › Preflight**, next to Health: Health is what runs now, Preflight is the deeper on-demand installation check with a test e-mail form | `postmark` verdict, a table of checks with `status_tag` and a *What to do* column, one form box for the test e-mail | `preflight` |
|
||||||
|
| csrf-tokens | no screen; every form gains a hidden field — forms are rendered through one helper so the token cannot be forgotten | — | n/a |
|
||||||
|
| template-data-typing | no screen; the component partials are the natural first users of typed data | — | n/a |
|
||||||
|
| structured-logging, contributing, review follow-ups | none | — | n/a |
|
||||||
|
|
||||||
|
The sibling strip under the navbar holds five or six entries per group without
|
||||||
|
wrapping; Inbound would reach five (Domains · Log · Queue · Quarantine ·
|
||||||
|
Filter lists), Server six. Past that a group is split rather than squeezed.
|
||||||
|
|
||||||
|
Ordering with 2.0: inbound-antispam-panel ships first in `1.10.0` and adds its
|
||||||
|
migration to the 1.x chain as planned; the 2.0 baseline then folds it in.
|
||||||
|
|
||||||
|
## Three layers
|
||||||
|
|
||||||
|
1. **Bulma, as shipped.** Vendored `bulma.min.css`, never edited. Only the
|
||||||
|
classes listed in `check.py` (`BULMA`) are used; reaching for another is a
|
||||||
|
design change.
|
||||||
|
2. **Adapters** — where display logic lives once:
|
||||||
|
`status_tag` (status → `tag is-… is-light`) as a template function, and
|
||||||
|
`copy_field` (the read-only value with its Copy button).
|
||||||
|
3. **Own components** — everything with an `sp-` class, styled in one
|
||||||
|
`panel.css`, each rendered by one partial in `components.html`:
|
||||||
|
|
||||||
|
| Partial | What it is |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Signed-out | login, setup |
|
| `page_head` | path, `<h1>`, lead, actions; optionally a `postmark` |
|
||||||
| Global ops | Status (plus polled fragment), mail queue, system log, backup, users |
|
| `postmark` | the round verdict stamp: ok / `sp-warn` / `sp-fail` |
|
||||||
| Shared | domains, domain detail, deliveries, delivery, help, settings |
|
| `box`, `box_head`, `box_foot` | numbered mono head with an end slot and an optional `sp-help` link to the section's Help topic (replaces the help drawer); variants `sp-danger-zone`, `sp-credential` |
|
||||||
| Optional | inbound (+ domain, delete), DMARC hub / domain / report |
|
| `flash` | Bulma `notification is-success / is-danger is-light` between `page_head` and the first box; a field's error stays in `help is-danger` |
|
||||||
| Confirms / forms | domain delete, inbound delete, user create/edit/delete |
|
| `health_card` | Overview only; ok / `sp-warn` / `sp-fail`; links to its detail |
|
||||||
|
| `dns_record` | one block per record: status, what to publish, "in DNS now" only on a mismatch |
|
||||||
|
| `facts` | label / value grid, `sp-big` for headline numbers |
|
||||||
|
| `side_menu` | Bulma `menu` with icons and count tags; sticky |
|
||||||
|
| `timeline`, `log_pane`, `empty_state`, `confirm_list`, `help_topic` | as on the components page |
|
||||||
|
| form rows | `sp-row` (wide + short), `sp-pair` (two equal) |
|
||||||
|
|
||||||
## Layout layer (decided)
|
The shell — brick navbar with hover menus, perforated edge, sibling-page
|
||||||
|
strip, footer — belongs to `layout.html` alone.
|
||||||
|
|
||||||
**Hybrid.** Page-level regions are ours. In-page composition is vendored Web
|
## Layout rules
|
||||||
Awesome **layout CSS only** (MIT Core), not the component library and not the
|
|
||||||
full `utilities.css` barrel.
|
|
||||||
|
|
||||||
Every Layout ([every-layout.dev](https://every-layout.dev/)) is the *school*
|
A page is exactly one of:
|
||||||
(Stack, Cluster, Sidebar, Grid). It is a paid book; redistribution is
|
|
||||||
forbidden. Do not vendor it, do not buy it for the image, do not put their
|
|
||||||
custom elements in the tree. Web Awesome `layout.css` is the MIT packaging of
|
|
||||||
those recipes (`wa-stack` ≈ Stack, `wa-cluster` ≈ Cluster, `wa-flank` ≈
|
|
||||||
Sidebar, `wa-grid` ≈ Grid/Switcher).
|
|
||||||
|
|
||||||
### Regions (our CSS)
|
- **list** — `page_head`, then full-width boxes: the add form, then the table.
|
||||||
|
- **detail** — `page_head`, then `columns`: `is-3` `side_menu` + `is-9` boxes.
|
||||||
|
- **form** — `page_head`, then boxes in equal `columns`, one submit row below.
|
||||||
|
- **signed out** — the split brand / form screen.
|
||||||
|
|
||||||
Shell: navigation column plus content that takes the rest (today's `.shell`,
|
Width comes from the layout, never from the page. A narrow form does not get a
|
||||||
without a centred `64rem` well that leaves side voids).
|
`max-width`; it gets a column and a neighbour that explains it.
|
||||||
|
|
||||||
Inside the content column, **two modes only**. A page must not invent a third
|
## The contract
|
||||||
width:
|
|
||||||
|
|
||||||
- **`measure`** — one readable form (login, setup, delete confirm, user form).
|
An implementer — human or model — **must not**:
|
||||||
Cap around `42rem`, **left-aligned**. Fields fill the measure, not the
|
|
||||||
window. Empty space to the right of a form on a wide monitor is acceptable.
|
|
||||||
- **`fill`** — ops: Status, tables, logs, queue, DNS, paired cards. One column
|
|
||||||
cap for all fill pages (pick a single value in mockups, e.g. ~`90rem`; do
|
|
||||||
not keep both `48rem` and `64rem`). A card fills its grid cell.
|
|
||||||
|
|
||||||
There is no `main.wide` or `.card.narrow` as a width exception. A narrow card
|
1. Write an `sp-` class, a Bulma class outside the subset, or any other class
|
||||||
lives in `measure`. A wide table lives in `fill`.
|
that the components page does not show.
|
||||||
|
2. Add a component, a state or a layout. If a screen seems to need one, **stop
|
||||||
|
and ask**; do not approximate it with ad-hoc markup.
|
||||||
|
3. Put a selector in `panel.css` that names a page or an element id, uses
|
||||||
|
`!important`, or sets a width outside the `WIDTH_OK` list.
|
||||||
|
4. Write component markup by hand in a page template instead of calling the
|
||||||
|
partial.
|
||||||
|
5. Edit the vendored Bulma, or load a second CSS framework, icon set or any
|
||||||
|
JavaScript widget.
|
||||||
|
6. Change a mockup, `outlines.json`, `check.py` or a guard test in the same
|
||||||
|
commit as `internal/web`, or at all without the owner's explicit
|
||||||
|
instruction. **The guards are not the implementer's to adjust.** A failing
|
||||||
|
guard means the implementation is wrong, not the guard.
|
||||||
|
7. Report a step as done on the strength of a description. Done means the
|
||||||
|
evidence below exists.
|
||||||
|
|
||||||
### In-page (vendored WA CSS)
|
**Changing the design** is allowed and has one route: mockup first. Edit the
|
||||||
|
fragment in `panel/src/`, the components page if a part changed, run
|
||||||
|
`build.py` and `check.py --write`, commit as `design: …` with the reason, get
|
||||||
|
it accepted — and only then touch `internal/web`.
|
||||||
|
|
||||||
Embed under `/static`, same as htmx — never a runtime CDN.
|
## Enforcement
|
||||||
|
|
||||||
| Take | Do not take |
|
Guard tests live in `internal/web/view` and run in `go test ./...` and CI.
|
||||||
|
They land **before** any template is restyled (checklist step 2), written and
|
||||||
|
reviewed by models other than the one that implements the templates.
|
||||||
|
|
||||||
|
| Test | Fails when |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `utilities/layout.css` | barrel `utilities.css` |
|
| `TestPanelClassVocabulary` | a template uses a class outside the Bulma subset + the `sp-` classes of `panel.css`; an `sp-` class is defined but unused, or used but absent from `components.html` |
|
||||||
| `utilities/gap.css` | `themes/default.css`, `native.css` |
|
| `TestPanelCSSContract` | `panel.css` has `!important`, an id or page-name selector, or a width outside `WIDTH_OK` |
|
||||||
| `align-items.css`, `justify-content.css`, `flex-wrap.css` if a mockup needs them | variants, prose, FOUCE, JS, icons, Pro |
|
| `TestTemplatesUseComponents` | a page template contains `sp-box-head`, `sp-postmark`, `sp-record`, `sp-facts`, … literally instead of `{{template "…"}}` |
|
||||||
| A short `:root` stub for `--wa-space-*` only | their `layers.css` (it styles `wa-page`) |
|
| `TestPanelOutlinesMatchMockups` | a page rendered with fixtures has a different component skeleton than `outlines.json` — a different design cannot pass as this one |
|
||||||
|
| `TestVendoredBulmaChecksum` | `bulma.min.css` differs from the pinned SHA-256 |
|
||||||
|
| `TestNoTemplateUsesInlineScriptOrStyle` | (exists) inline `style` / script |
|
||||||
|
|
||||||
Declare `@layer wa-utilities` locally. One spacing scale for the whole layer
|
CI adds one step, `design-first`: a commit that touches
|
||||||
(map to today's `1.2rem` gaps if that looks right; do not keep a second scale).
|
`internal/web/view/**` must not touch `docs/assets/panel-redesign/panel/**`
|
||||||
|
or the guard tests.
|
||||||
|
|
||||||
`.card` with slots (head / body / actions) stays ours so a `wa-grid` cell can
|
### Evidence for "done"
|
||||||
stretch. Do not use `wa-card`.
|
|
||||||
|
|
||||||
### Class vocabulary
|
Every checklist step that restyles pages ends with, in the commit:
|
||||||
|
|
||||||
**Paired cards are `wa-grid`, not `wa-split`.** In Web Awesome, `wa-split` is
|
- `go test ./internal/web/...` output in the commit message body (pass);
|
||||||
space-between (title ‖ action). `wa-grid` is `auto-fit` + `--min-column-size`,
|
- a screenshot of each restyled page at 1440×900, saved next to its mockup's
|
||||||
which is today's `.split { minmax(22rem, 1fr) }`. Freeze `--min-column-size`
|
name under `docs/assets/panel-redesign/evidence/`, rendered from the running
|
||||||
once (same order as `22rem`); do not retune it per page.
|
container with the e2e fixtures — not from the mockup;
|
||||||
|
- a review by a model that did not write the step
|
||||||
|
([development.md](../development.md) § Model routing): mockup and screenshot
|
||||||
|
side by side, every difference either fixed or listed for the owner.
|
||||||
|
|
||||||
| Class | Use |
|
The owner accepts a step by looking at the pairs. A step without them is not
|
||||||
|---|---|
|
reviewed.
|
||||||
| `measure` / `fill` | page region only, never inside a card |
|
|
||||||
| `wa-stack` | form or card body; children stretch on the block axis |
|
|
||||||
| `wa-cluster` | button rows, tags, Copy beside a value |
|
|
||||||
| `wa-grid` | two or more equal cards or field columns |
|
|
||||||
| `wa-split` | card header: title ‖ help/action; label ‖ value |
|
|
||||||
| `wa-flank` | rare: fixed + fluid (icon beside a field) |
|
|
||||||
| `wa-gap-*` | change a gap; not a substitute for a region |
|
|
||||||
|
|
||||||
`wa-frame` is for media boxes; the panel has none — omit from mockups.
|
## Out of scope
|
||||||
|
|
||||||
One child in `wa-grid` occupies the row (`auto-fit`, not `auto-fill`): a
|
- New features beyond the four rows listed under Information architecture.
|
||||||
domain-admin Settings card must not leave an empty second column.
|
- Dark scheme and the narrow-screen shell: Bulma ships both; they are drawn
|
||||||
|
and agreed as a `design:` change before anyone implements them.
|
||||||
**Forbidden in mockups and later in templates:** a one-off `max-width` “for
|
- Weakening CSP; a JS bundler; a front-end framework.
|
||||||
this page”; a second name for the same pairing (today `.pair` vs `.split`);
|
|
||||||
`margin-inline: auto` on a card inside a grid (that already collapsed `.split`
|
|
||||||
tracks).
|
|
||||||
|
|
||||||
**Ready when:** Status (`fill` + `wa-grid` pairs) and login (`measure` +
|
|
||||||
`wa-stack`) can be drawn with no extra width rule. If a third screen wants a
|
|
||||||
new `max-width`, the vocabulary is unfinished.
|
|
||||||
|
|
||||||
## Components (decided)
|
|
||||||
|
|
||||||
**No JavaScript component library.** Pico, Bootstrap JS, DaisyUI, FAST,
|
|
||||||
Shoelace, and Web Awesome custom elements (`wa-button`, `wa-dialog`,
|
|
||||||
`wa-drawer`, `wa-card`) are out. They do not fix empty cards; they add Lit or
|
|
||||||
another runtime, Shadow DOM, CSP risk, and friction with htmx swaps.
|
|
||||||
|
|
||||||
What the live panel actually uses — and what mockups should assume:
|
|
||||||
|
|
||||||
| Need | Source |
|
|
||||||
|---|---|
|
|
||||||
| Button, input, select, textarea, table | native HTML, our CSS (brick / Plex) |
|
|
||||||
| Card | our `.card` + slots |
|
|
||||||
| Flash, status tint | our CSS |
|
|
||||||
| Help / application panels | CSS disclosure (checkbox today); or `<details>` / Popover if a mockup prefers |
|
|
||||||
| Destructive confirm | `data-confirm` / `window.confirm`, plus existing confirm pages for high blast radius |
|
|
||||||
| Overlay, if a mockup truly needs one | HTML [`<dialog>`](https://developer.mozilla.org/docs/Web/HTML/Element/dialog), not `wa-dialog` |
|
|
||||||
| Copy-to-clipboard | existing `panel.js` |
|
|
||||||
|
|
||||||
There is no combobox, date picker, tab set, tree, or chart in the panel. If a
|
|
||||||
future mockup invents a widget native HTML cannot express, choose a library
|
|
||||||
then, for that widget only.
|
|
||||||
|
|
||||||
## Out of scope for this item
|
|
||||||
|
|
||||||
- New panel features, routes, or copy (those are other roadmap rows).
|
|
||||||
- Weakening CSP to accommodate a widget library.
|
|
||||||
- Web Awesome Pro, Font Awesome Kit, a JS bundler for Lit.
|
|
||||||
- Replacing htmx with a front-end framework.
|
|
||||||
- Implementing `docs/assets/panel-ui/` as-is.
|
|
||||||
|
|
||||||
## Next (not this file's job)
|
|
||||||
|
|
||||||
1. Draw new mockups using only the vocabulary above.
|
|
||||||
2. After mockups are accepted and this item is **agreed**: vendor the WA CSS
|
|
||||||
files, NOTICE / development.md rows, then restyle templates without changing
|
|
||||||
behaviour.
|
|
||||||
|
|
||||||
## Implementation checklist
|
## Implementation checklist
|
||||||
|
No code until the roadmap status is **agreed**.
|
||||||
|
|
||||||
None until mockups exist and the roadmap status is **agreed**. Do not start
|
- [x] Owner decision recorded here: keep today's paths or rename with redirects — **owner** (2026-09-21: rename, § Routes)
|
||||||
vendoring or rewriting `internal/web` from this file alone.
|
- [x] Owner decision: no compatibility, migrations to zero, `2.0.0` — **owner** (2026-09-21, § No compatibility)
|
||||||
|
- [x] Owner decision: inbound is delegated per user, separately from outbound — **owner** (2026-09-21, § Who sees what)
|
||||||
|
- [ ] Vendor Bulma 1.0.4 and the used Tabler icons (MIT) under `/static`, NOTICE and development.md rows, pinned checksum — **Haiku**
|
||||||
|
- [ ] Guard tests from the table above and `TestRoutesFollowNavigation`, red against today's panel where expected; `design-first` CI step — **Opus**
|
||||||
|
- [ ] Schema from zero: single `0001_init.sql` baseline with `user_inbound_domains`, store tests, `schema-migrations.md` rewritten — **Opus**
|
||||||
|
- [ ] Inbound delegation: `requireInboundDomain`, filtered lists and log, two-list user form, per-route 404 tests for both roles — **Opus**
|
||||||
|
- [ ] Routes renamed per § Routes: new paths, one fragment name, old paths removed, subtree `requireGlobal()` for `/server/` — **Opus**
|
||||||
|
- [ ] `panel.css` from `panel/theme.css`; `components.html` partials; `status_tag`; a `/components` page behind the global role rendering every partial — **Sonnet**
|
||||||
|
- [ ] `layout.html`: navbar, perforated edge, sibling strip, user menu, footer; visibility flags — **Sonnet**
|
||||||
|
- [ ] Signed-out pages: login, setup — **Sonnet**
|
||||||
|
- [ ] Overview (verdicts) and Account / Settings split — **Sonnet**
|
||||||
|
- [ ] Outbound: domains, domain, domain settings, application form as one POST, shown-once password, delete — **Sonnet**
|
||||||
|
- [ ] Outbound: log, message, DMARC hub / domain / report — **Sonnet**
|
||||||
|
- [ ] Inbound: domains, domain, delete — **Sonnet**
|
||||||
|
- [ ] Server: system log, backup, users, user form, delete; Help — **Sonnet**
|
||||||
|
- [ ] Remove the old `panel.css` rules and classes nothing references; vocabulary test green with no allow-list — **Sonnet**
|
||||||
|
- [ ] Technical review of the whole against the mockups, evidence pairs complete — **Opus**
|
||||||
|
- [ ] Security review of the route rename, subtree authorization and inbound delegation — **Fable**
|
||||||
|
- [ ] guide.md screenshots and wording for the new navigation; architecture.md § Panel HTTP surface and § Persistence; security.md roles; *Removed* in the CHANGELOG with one line that 2.0 starts from an empty data directory — **Sonnet**
|
||||||
|
- [ ] `go vet`, `go test ./...`, e2e suite — **Haiku**
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ checks and helps the administrator confirm the instance is fully configured and
|
|||||||
ready to deliver mail. Each failed check shows an actionable recommendation
|
ready to deliver mail. Each failed check shows an actionable recommendation
|
||||||
(what to do or where to look).
|
(what to do or where to look).
|
||||||
|
|
||||||
|
## Mockup
|
||||||
|
|
||||||
|
[preflight](../assets/panel-redesign/panel/preflight.html), under the design
|
||||||
|
contract of [panel-redesign.md](panel-redesign.md): **Server › Preflight**,
|
||||||
|
path `/server/preflight` once the routes are renamed.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
**In:**
|
**In:**
|
||||||
|
|||||||
+33
-16
@@ -36,7 +36,7 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md).
|
|||||||
| template-data-typing | Typed structs for template data instead of `map[string]any` | candidate | — | — |
|
| template-data-typing | Typed structs for template data instead of `map[string]any` | candidate | — | — |
|
||||||
| structured-logging | `log/slog` with levels and fields | candidate | — | — |
|
| structured-logging | `log/slog` with levels and fields | candidate | — | — |
|
||||||
| review-2026-08-followups | Remaining findings of the 2026-08-19 code review | candidate | — | — |
|
| review-2026-08-followups | Remaining findings of the 2026-08-19 code review | candidate | — | — |
|
||||||
| panel-redesign | Panel visual redesign (layout grammar) | candidate | — | [plans/panel-redesign.md](plans/panel-redesign.md) |
|
| panel-redesign | Panel redesign (accepted mockups, design contract) | candidate | 3/21 | [plans/panel-redesign.md](plans/panel-redesign.md) |
|
||||||
| schema-squash | Squash SQLite migrations into a 2.x baseline | **2.x** | — | — |
|
| schema-squash | Squash SQLite migrations into a 2.x baseline | **2.x** | — | — |
|
||||||
|
|
||||||
**Recommended order** (not binding): the next feature is
|
**Recommended order** (not binding): the next feature is
|
||||||
@@ -297,26 +297,36 @@ and tagging it would only delete cross-platform test coverage.
|
|||||||
|
|
||||||
## panel-redesign
|
## panel-redesign
|
||||||
|
|
||||||
**Goal:** a panel layout grammar so screens stop inventing per-page widths to
|
**Goal:** a panel an operator opens a few times a year and still finds their
|
||||||
hide empty space. Forms stay on a reading measure; ops pages use the remaining
|
way in — the important things in view, details one or two clicks away, one
|
||||||
column; cards fill their cells. Givens (hybrid WA layout CSS + own `measure` /
|
visual language, no page tuning its own width. The design is drawn and
|
||||||
`fill`, no JS component library, new mockups rather than
|
accepted: 29 screens and a components page under
|
||||||
[docs/assets/panel-ui/](assets/panel-ui/index.html)):
|
[docs/assets/panel-redesign/](assets/panel-redesign/index.html) (Bulma base,
|
||||||
|
postmark / numbered heads / perforated edge, icons). Contract and checklist:
|
||||||
[plans/panel-redesign.md](plans/panel-redesign.md).
|
[plans/panel-redesign.md](plans/panel-redesign.md).
|
||||||
|
|
||||||
**Boundary:** visual CSS and templates after mockups. No new routes or mail-path
|
**Boundary:** templates, one `panel.css`, shared partials, vendored Bulma and
|
||||||
behaviour. Not a front-end framework, not Web Awesome components, not Pico /
|
icons. Navigation regroups by mail direction (Overview · Outbound · Inbound ·
|
||||||
Bootstrap. The older HTML mockups in `docs/assets/panel-ui/` are not the brief.
|
Server) and paths are renamed to match. **No compatibility is kept** (owner,
|
||||||
|
2026-09-21): no redirects, the migration chain is replaced by one baseline
|
||||||
|
and 2.0 starts from an empty data directory — the only live installation is
|
||||||
|
the owner's. Inbound domains become delegable to a
|
||||||
|
domain administrator, separately from outbound. Four things the mockups show need their own decision and are not
|
||||||
|
smuggled in with the restyle: queue as a table (`postqueue -j`), inbound
|
||||||
|
queue, inbound log (rides on inbound-antispam-panel), Server › Health. No CSP
|
||||||
|
change, no JavaScript widgets, no second framework.
|
||||||
|
|
||||||
**Done when:** mockups exist that use only the frozen vocabulary, then — once
|
**Done when:** every page renders with the component skeleton recorded in
|
||||||
this item is agreed — the live panel matches them without one-off `max-width`
|
`panel/outlines.json`, the guard tests in the plan are green with no
|
||||||
exceptions. The givens file itself is not “done”.
|
allow-list, and each page has an evidence screenshot accepted against its
|
||||||
|
mockup.
|
||||||
|
|
||||||
**Dependencies / risks:** competes with feature work for attention; easy to
|
**Dependencies / risks:** an implementer substituting local patches or a look
|
||||||
start restyling `internal/web` before the vocabulary is proven on paper.
|
of their own — answered by guard tests that land before the templates, a
|
||||||
Vendoring WA CSS needs NOTICE / licence rows. CSP stays `default-src 'self'`.
|
`design-first` CI step and evidence-based acceptance (plan § Enforcement).
|
||||||
|
Competes with feature work; Bulma is ~680 kB minified, embedded once.
|
||||||
|
|
||||||
**Version:** `1.x` MINOR when implementation is agreed; `candidate` until then.
|
**Version:** `2.0.0` MAJOR; `candidate` until agreed.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -342,6 +352,13 @@ replace the squash upgrade gate.
|
|||||||
it); the gate in [schema-migrations.md](schema-migrations.md) is tested; the
|
it); the gate in [schema-migrations.md](schema-migrations.md) is tested; the
|
||||||
operator guide says a 2.x image will not open an unfinished 1.x database.
|
operator guide says a 2.x image will not open an unfinished 1.x database.
|
||||||
|
|
||||||
|
**Trigger decided 2026-09-21:** [panel-redesign](#panel-redesign) is the 2.0
|
||||||
|
cut, and the owner dropped the upgrade gate with it — the chain is deleted to
|
||||||
|
zero and 2.0 starts from an empty data directory. The
|
||||||
|
**Boundary** and **Done when** above are superseded on that point by
|
||||||
|
[plans/panel-redesign.md](plans/panel-redesign.md) § No compatibility; this
|
||||||
|
item ships as a step of that plan, not on its own.
|
||||||
|
|
||||||
**Dependencies / risks:** a decided 2.x cut (another breaking change, or an
|
**Dependencies / risks:** a decided 2.x cut (another breaking change, or an
|
||||||
explicit major). Squashing the current short chain is not a reason to cut 2.x on
|
explicit major). Squashing the current short chain is not a reason to cut 2.x on
|
||||||
its own. A missed gate leaves a mid-chain 1.x database silently stuck.
|
its own. A missed gate leaves a mid-chain 1.x database silently stuck.
|
||||||
|
|||||||
Reference in New Issue
Block a user