From d1f22fbcc8d482486c7eef50e76edb939567726a Mon Sep 17 00:00:00 2001 From: Mix Date: Mon, 21 Sep 2026 19:11:15 +0300 Subject: [PATCH] docs: draw every panel screen in the accepted redesign direction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stage 2 of the panel redesign. Direction C (Bulma) is the base — the important things in view, details one or two clicks away — with A's postal signature (postmark verdict, numbered mono section heads, the stamp's perforated edge) and B's icons. 29 screens under docs/assets/panel-redesign/panel/, linked into a click-through. Overview keeps verdicts only; the process, socket and interface 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. Every page is one of three layouts, so none picks its own width. Pages are built from panel/src fragments by panel/build.py so the navbar lives in one place; the built HTML is committed. The cover sheet now leads with the screen map and keeps the three stage-1 directions below. Mockups only; internal/web is untouched. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 9 + docs/assets/panel-redesign/index.html | 55 ++++- docs/assets/panel-redesign/panel/account.html | 106 ++++++++++ docs/assets/panel-redesign/panel/backup.html | 113 ++++++++++ docs/assets/panel-redesign/panel/build.py | 118 +++++++++++ .../panel-redesign/panel/dmarc-domain.html | 107 ++++++++++ .../panel-redesign/panel/dmarc-report.html | 116 +++++++++++ docs/assets/panel-redesign/panel/dmarc.html | 102 +++++++++ docs/assets/panel-redesign/panel/health.html | 148 +++++++++++++ docs/assets/panel-redesign/panel/help.html | 116 +++++++++++ .../panel/in-domain-delete.html | 98 +++++++++ .../panel-redesign/panel/in-domain.html | 155 ++++++++++++++ .../panel-redesign/panel/in-domains.html | 102 +++++++++ docs/assets/panel-redesign/panel/in-log.html | 98 +++++++++ .../assets/panel-redesign/panel/in-queue.html | 94 +++++++++ docs/assets/panel-redesign/panel/login.html | 39 ++++ .../panel-redesign/panel/out-app-created.html | 104 ++++++++++ docs/assets/panel-redesign/panel/out-app.html | 133 ++++++++++++ .../panel/out-domain-delete.html | 107 ++++++++++ .../panel/out-domain-settings.html | 143 +++++++++++++ .../panel-redesign/panel/out-domain.html | 176 ++++++++++++++++ .../panel-redesign/panel/out-domains.html | 104 ++++++++++ docs/assets/panel-redesign/panel/out-log.html | 101 +++++++++ .../panel-redesign/panel/out-message.html | 115 ++++++++++ .../panel-redesign/panel/out-queue.html | 99 +++++++++ .../assets/panel-redesign/panel/overview.html | 119 +++++++++++ .../assets/panel-redesign/panel/settings.html | 124 +++++++++++ docs/assets/panel-redesign/panel/setup.html | 44 ++++ .../panel-redesign/panel/src/account.html | 37 ++++ .../panel-redesign/panel/src/backup.html | 44 ++++ .../panel/src/dmarc-domain.html | 38 ++++ .../panel/src/dmarc-report.html | 47 +++++ .../panel-redesign/panel/src/dmarc.html | 33 +++ .../panel-redesign/panel/src/health.html | 79 +++++++ .../assets/panel-redesign/panel/src/help.html | 47 +++++ .../panel/src/in-domain-delete.html | 29 +++ .../panel-redesign/panel/src/in-domain.html | 86 ++++++++ .../panel-redesign/panel/src/in-domains.html | 33 +++ .../panel-redesign/panel/src/in-log.html | 29 +++ .../panel-redesign/panel/src/in-queue.html | 25 +++ .../panel-redesign/panel/src/login.html | 25 +++ .../panel/src/out-app-created.html | 35 ++++ .../panel-redesign/panel/src/out-app.html | 64 ++++++ .../panel/src/out-domain-delete.html | 38 ++++ .../panel/src/out-domain-settings.html | 74 +++++++ .../panel-redesign/panel/src/out-domain.html | 107 ++++++++++ .../panel-redesign/panel/src/out-domains.html | 35 ++++ .../panel-redesign/panel/src/out-log.html | 32 +++ .../panel-redesign/panel/src/out-message.html | 46 ++++ .../panel-redesign/panel/src/out-queue.html | 30 +++ .../panel-redesign/panel/src/overview.html | 50 +++++ .../panel-redesign/panel/src/settings.html | 55 +++++ .../panel-redesign/panel/src/setup.html | 30 +++ .../panel-redesign/panel/src/system-log.html | 22 ++ .../panel-redesign/panel/src/user-delete.html | 29 +++ .../assets/panel-redesign/panel/src/user.html | 41 ++++ .../panel-redesign/panel/src/users.html | 21 ++ .../panel-redesign/panel/system-log.html | 91 ++++++++ docs/assets/panel-redesign/panel/theme.css | 196 ++++++++++++++++++ .../panel-redesign/panel/user-delete.html | 98 +++++++++ docs/assets/panel-redesign/panel/user.html | 110 ++++++++++ docs/assets/panel-redesign/panel/users.html | 90 ++++++++ 62 files changed, 4786 insertions(+), 5 deletions(-) create mode 100644 docs/assets/panel-redesign/panel/account.html create mode 100644 docs/assets/panel-redesign/panel/backup.html create mode 100644 docs/assets/panel-redesign/panel/build.py create mode 100644 docs/assets/panel-redesign/panel/dmarc-domain.html create mode 100644 docs/assets/panel-redesign/panel/dmarc-report.html create mode 100644 docs/assets/panel-redesign/panel/dmarc.html create mode 100644 docs/assets/panel-redesign/panel/health.html create mode 100644 docs/assets/panel-redesign/panel/help.html create mode 100644 docs/assets/panel-redesign/panel/in-domain-delete.html create mode 100644 docs/assets/panel-redesign/panel/in-domain.html create mode 100644 docs/assets/panel-redesign/panel/in-domains.html create mode 100644 docs/assets/panel-redesign/panel/in-log.html create mode 100644 docs/assets/panel-redesign/panel/in-queue.html create mode 100644 docs/assets/panel-redesign/panel/login.html create mode 100644 docs/assets/panel-redesign/panel/out-app-created.html create mode 100644 docs/assets/panel-redesign/panel/out-app.html create mode 100644 docs/assets/panel-redesign/panel/out-domain-delete.html create mode 100644 docs/assets/panel-redesign/panel/out-domain-settings.html create mode 100644 docs/assets/panel-redesign/panel/out-domain.html create mode 100644 docs/assets/panel-redesign/panel/out-domains.html create mode 100644 docs/assets/panel-redesign/panel/out-log.html create mode 100644 docs/assets/panel-redesign/panel/out-message.html create mode 100644 docs/assets/panel-redesign/panel/out-queue.html create mode 100644 docs/assets/panel-redesign/panel/overview.html create mode 100644 docs/assets/panel-redesign/panel/settings.html create mode 100644 docs/assets/panel-redesign/panel/setup.html create mode 100644 docs/assets/panel-redesign/panel/src/account.html create mode 100644 docs/assets/panel-redesign/panel/src/backup.html create mode 100644 docs/assets/panel-redesign/panel/src/dmarc-domain.html create mode 100644 docs/assets/panel-redesign/panel/src/dmarc-report.html create mode 100644 docs/assets/panel-redesign/panel/src/dmarc.html create mode 100644 docs/assets/panel-redesign/panel/src/health.html create mode 100644 docs/assets/panel-redesign/panel/src/help.html create mode 100644 docs/assets/panel-redesign/panel/src/in-domain-delete.html create mode 100644 docs/assets/panel-redesign/panel/src/in-domain.html create mode 100644 docs/assets/panel-redesign/panel/src/in-domains.html create mode 100644 docs/assets/panel-redesign/panel/src/in-log.html create mode 100644 docs/assets/panel-redesign/panel/src/in-queue.html create mode 100644 docs/assets/panel-redesign/panel/src/login.html create mode 100644 docs/assets/panel-redesign/panel/src/out-app-created.html create mode 100644 docs/assets/panel-redesign/panel/src/out-app.html create mode 100644 docs/assets/panel-redesign/panel/src/out-domain-delete.html create mode 100644 docs/assets/panel-redesign/panel/src/out-domain-settings.html create mode 100644 docs/assets/panel-redesign/panel/src/out-domain.html create mode 100644 docs/assets/panel-redesign/panel/src/out-domains.html create mode 100644 docs/assets/panel-redesign/panel/src/out-log.html create mode 100644 docs/assets/panel-redesign/panel/src/out-message.html create mode 100644 docs/assets/panel-redesign/panel/src/out-queue.html create mode 100644 docs/assets/panel-redesign/panel/src/overview.html create mode 100644 docs/assets/panel-redesign/panel/src/settings.html create mode 100644 docs/assets/panel-redesign/panel/src/setup.html create mode 100644 docs/assets/panel-redesign/panel/src/system-log.html create mode 100644 docs/assets/panel-redesign/panel/src/user-delete.html create mode 100644 docs/assets/panel-redesign/panel/src/user.html create mode 100644 docs/assets/panel-redesign/panel/src/users.html create mode 100644 docs/assets/panel-redesign/panel/system-log.html create mode 100644 docs/assets/panel-redesign/panel/theme.css create mode 100644 docs/assets/panel-redesign/panel/user-delete.html create mode 100644 docs/assets/panel-redesign/panel/user.html create mode 100644 docs/assets/panel-redesign/panel/users.html diff --git a/CHANGELOG.md b/CHANGELOG.md index a5b528d..7ff23cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,15 @@ document is not part of the release. ### Added +- 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) — diff --git a/docs/assets/panel-redesign/index.html b/docs/assets/panel-redesign/index.html index d5579a1..f77de1d 100644 --- a/docs/assets/panel-redesign/index.html +++ b/docs/assets/panel-redesign/index.html @@ -9,11 +9,56 @@
-

SelfPost · panel redesign · stage 1

-

Three directions, three key screens

-

Overview, an outbound domain and sign-in, drawn three ways. Pick one (or a mix); - the remaining screens are then drawn in that direction only. Serve this folder from the repository - root — the pages load the panel's own fonts and mark by relative path.

+

SelfPost · panel redesign · stage 2

+

The accepted direction, every screen

+

Direction C (Bulma) was picked as the base: the important things in view, details one + or two clicks away. It takes the postal signature of A — the postmark verdict, numbered mono section + heads, the stamp's perforated edge — and the icons of B. Twenty-nine screens live in + panel/. Serve this folder from the repository root: the pages load the panel's own + fonts and mark by relative path.

+ +

Screen map

+
+ + + + + + + + + +
Signed outSign in · Create administrator
OverviewOverview — postmark verdict, six health cards, both domain lists
OutboundDomains · domain · + domain settings · application form · + password shown once · delete domain
+ Log · message · + Queue · DMARC reports · + by domain · one report
InboundDomains · domain · + delete domain · Log · + Queue (empty state)
ServerHealth · System log · + Backup · Users · user form · + delete user · Settings
User menuAccount · Help
+
+ +

What changed against today's panel

+
    +
  • Overview shows verdicts, Health shows tables. Process lists, socket paths and interface counters moved to + Server › Health, one click from each card.
  • +
  • Every page is one of three layouts: 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.
  • +
  • Own password is Account, instance defaults are Settings. Export sits with the domain, import with Backup, + and each links to the other.
  • +
  • Queues are tables, not a postqueue -p dump — this needs postqueue -j parsing. + The inbound queue is the same queue filtered by recipient domain.
  • +
  • Confirm pages say what will be lost as a list, beside a hint for the usual alternative (export before delete).
  • +
  • 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 theme.css is about 200 lines.
  • +
  • panel/build.py wraps the fragments in panel/src/ with the shared navbar; the built pages are committed, + so nothing has to be run to view them.
  • +
+ +

Stage 1 · three directions on three key screens

+

Kept for the record: Overview, an outbound domain and sign-in, drawn three ways.

Shared by all three

    diff --git a/docs/assets/panel-redesign/panel/account.html b/docs/assets/panel-redesign/panel/account.html new file mode 100644 index 0000000..f617ef4 --- /dev/null +++ b/docs/assets/panel-redesign/panel/account.html @@ -0,0 +1,106 @@ + + + + + +Account · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Signed in as admin · global administrator

    +

    Account

    +

    Your sign-in to this panel. Applications keep their own logins and passwords and are not affected.

    +
    +
    + +
    +
    +
    +
    01

    Panel credentials

    +
    +
    +
    +
    +
    +
    +
    +
    +

    Leave both new-password fields empty to change the username only.

    + +
    +
    +
    +
    +
    +
    +

    What happens

    +
    +

    Changing the password signs out every other session of this account. This one stays signed in.

    +

    A domain administrator sees this same page, and nothing under Server.

    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/backup.html b/docs/assets/panel-redesign/panel/backup.html new file mode 100644 index 0000000..4c50c4d --- /dev/null +++ b/docs/assets/panel-redesign/panel/backup.html @@ -0,0 +1,113 @@ + + + + + +Backup · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Server

    +

    Backup

    +

    Take the whole instance with you, or bring one domain in from another SelfPost. Both files are secrets.

    +
    +
    + +
    +
    +
    +
    01

    Full backup

    +
    +

    data/ (database, DKIM keys, credentials, the queue), docker-compose.yml, .env and certs/ in one archive. Restore into an empty project directory and start the same SelfPost version. The reverse-proxy vhost is not included.

    +
    +
    +
    +
    +
    +
    +

    Keep this password: SelfPost does not store it, and the file cannot be opened without it.

    + +
    +
    +
    +
    + +
    +
    +
    02

    Import a domain

    +
    +

    A domain export from another instance — plain .json or encrypted .spde. Its DKIM key and application passwords come across, so the published DNS needs no change. The export itself is made on the domain's settings.

    +
    +
    +
    +
    +

    Asked only for an encrypted .spde file.

    + +
    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/build.py b/docs/assets/panel-redesign/panel/build.py new file mode 100644 index 0000000..0f1010b --- /dev/null +++ b/docs/assets/panel-redesign/panel/build.py @@ -0,0 +1,118 @@ +"""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: + +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"), + ]), + ("server", "Server", "health.html", "ti-server-2", [ + ("health", "Health", "health.html", "ti-heartbeat"), + ("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 = """ + + + + +{title} · SelfPost + + + + + +""" + +FOOT = ('') + + +def navbar(section, page): + out = ['', + ''] + 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'{plabel}' + for pkey, plabel, phref, picon in pages) + return f'
    {links}
    ' + 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"\n{body}\n\n\n" + else: + section, page = meta.get("section", ""), meta.get("page", "") + html += (f"\n{navbar(section, page)}\n{subnav(section, page)}\n
    \n" + f'
    \n{body}\n {FOOT}\n
    \n
    \n\n\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") diff --git a/docs/assets/panel-redesign/panel/dmarc-domain.html b/docs/assets/panel-redesign/panel/dmarc-domain.html new file mode 100644 index 0000000..1c2d8dd --- /dev/null +++ b/docs/assets/panel-redesign/panel/dmarc-domain.html @@ -0,0 +1,107 @@ + + + + + +shop.example.org · DMARC reports · SelfPost + + + + + + + + + +
    +
    +
    +
    DMARC99.6%PASS · 7 DAYS
    +
    +

    DMARC reports /

    +

    shop.example.org

    +

    11 204 pass · 41 fail in the last 7 days. Failures come from a source that is not this relay — review before tightening p=.

    +
    + Domain page +
    + +
    +
    +
    +
    01

    Sources · 7 days

    + + + + + + +
    SourcePassFailDisposition
    203.0.113.25 this relay11 2040none
    198.51.100.44041none
    +
    +
    +
    +
    +
    02

    Reports

    + + + + + + + +
    Received, UTCReporterWindowPassFail
    09-21 04:40Outlook.com20 Sep1 9037View
    09-21 02:15google.com20 Sep6 4100View
    09-20 04:38Outlook.com19 Sep1 87734View
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/dmarc-report.html b/docs/assets/panel-redesign/panel/dmarc-report.html new file mode 100644 index 0000000..773d1a6 --- /dev/null +++ b/docs/assets/panel-redesign/panel/dmarc-report.html @@ -0,0 +1,116 @@ + + + + + +Outlook.com · 20 Sep · DMARC · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    DMARC reports / shop.example.org /

    +

    Outlook.com · 20 Sep 2026

    +

    shop.example.org1 903 pass7 fail

    +
    +
    + +
    +
    +
    +
    01

    Report

    +
    +
    +
    Reporter
    Outlook.com
    +
    Report id
    5f1c9a0e7b2d4e61a3
    +
    Window
    20 Sep 00:00 – 21 Sep 00:00 UTC
    +
    Received
    2026-09-21 04:40 UTC
    +
    +
    +
    +
    +
    +
    +
    02

    Published policy

    +
    +
    +
    p / sp / pct
    none / none / 100
    +
    adkim / aspf
    r / r
    +
    rua
    dmarc@mail.example.org
    +
    +
    +
    +
    +
    + +
    +
    03

    Records

    Parsed from the aggregate XML
    + + + + + + +
    SourceCountDispositionSPFDKIMHeader from
    203.0.113.25 this relay1 903nonepasspassshop.example.org
    198.51.100.447nonefailfailshop.example.org
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/dmarc.html b/docs/assets/panel-redesign/panel/dmarc.html new file mode 100644 index 0000000..03f49c7 --- /dev/null +++ b/docs/assets/panel-redesign/panel/dmarc.html @@ -0,0 +1,102 @@ + + + + + +DMARC reports · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Outbound

    +

    DMARC reports

    +

    How receivers saw mail claiming to be from your domains. Aggregate (rua=) only; open a report for the parsed XML.

    +
    +
    + +
    +
    01

    Ingest

    ok
    +
    +
    +
    Last report
    2026-09-21 06:12 UTC
    +
    This week
    14 kept · 0 parse failures
    +
    Hosted address
    dmarc@mail.example.org
    +
    Retention
    500 reports or 90 days
    +
    +
    +
    + +
    +
    02

    Recent reports

    + + + + + + + + +
    Received, UTCDomainReporterWindowPassFail
    09-21 06:12example.orggoogle.com20 Sep4120View
    09-21 04:40shop.example.orgOutlook.com20 Sep1 9037View
    09-20 23:58example.orgYahoo19 Sep380View
    09-20 06:09notify.acme.iogoogle.com19 Sep610View
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/health.html b/docs/assets/panel-redesign/panel/health.html new file mode 100644 index 0000000..ce87a13 --- /dev/null +++ b/docs/assets/panel-redesign/panel/health.html @@ -0,0 +1,148 @@ + + + + + +Health · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Server

    +

    Health

    +

    Everything behind the six cards on Overview. Refreshes every 10 s.

    +
    +
    +
    + +
    +
    +
    +
    01

    Machine

    ok
    + + + + + + + +
    ResourceUsageDetail
    CPU12 %2 cores, load 0.21
    Memory41 %812 MB of 1.9 GB
    Network↓ 18 kB/s · ↑ 44 kB/seth0: 2.1 GB in, 6.4 GB out
    +
    +
    +
    +
    +
    02

    Processes

    ok
    + + + + + + + + + +
    ProgramStateDetail
    postfixrunningpid 41, uptime 18 days
    opendkimrunningpid 39, uptime 18 days
    journal-milterrunningpid 40, uptime 18 days
    panelrunningpid 44, uptime 18 days
    dmarc-ingestrunningpid 47, uptime 18 days
    +
    +
    +
    + +
    +
    +
    +
    03

    TLS certificate

    warn
    +
    +
    +
    Expires
    2026-10-03 09:14 UTC
    +
    Names
    mail.example.org
    +
    +

    Expires in 12 days — renewal has not run.

    +

    Renewal normally runs 30 days before expiry. Check that port 80 is reachable and look for acme lines in the system log.

    +
    +
    +
    +
    +
    +
    04

    Milter sockets

    ok
    + + + + + + +
    MilterState
    opendkim
    inet:127.0.0.1:8891
    ok
    journal-milter
    unix:/run/selfpost/journal.sock
    ok
    +
    +
    +
    +
    +
    05

    Hostname and reverse DNS

    ok
    +
    +
    +
    Hostname
    mail.example.org
    +
    Lookup
    → 203.0.113.25
    ← mail.example.org
    +
    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/help.html b/docs/assets/panel-redesign/panel/help.html new file mode 100644 index 0000000..982ae5b --- /dev/null +++ b/docs/assets/panel-redesign/panel/help.html @@ -0,0 +1,116 @@ + + + + + +Help · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Reference

    +

    Help

    +

    What each screen checks and what to do when it is not green. Every numbered section on a page links to its topic here.

    +
    +
    + +
    + +
    +
    +
    01

    Overview

    +

    Health checks

    Six checks, the worst of them is the postmark. ok needs nothing; warn still delivers mail but will stop doing so if ignored; fail means mail is being lost or refused now.

    +

    TLS certificate

    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.

    +

    Hostname and reverse DNS

    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.

    +
    +
    +
    02

    Outbound

    +

    DNS records

    DKIM proves the message was signed by this relay, SPF lists the addresses allowed to send for the domain, DMARC tells receivers what to do when both fail and where to report. Publish all three exactly as shown; merge SPF into an existing record rather than adding a second one.

    +

    Applications

    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.

    +

    Rate limits

    Level 1 is a per-IP ceiling set in .env. Level 2 is optional, per domain and per application, manual or computed from 30-day statistics, and can never exceed level 1.

    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/in-domain-delete.html b/docs/assets/panel-redesign/panel/in-domain-delete.html new file mode 100644 index 0000000..0dc2e31 --- /dev/null +++ b/docs/assets/panel-redesign/panel/in-domain-delete.html @@ -0,0 +1,98 @@ + + + + + +Delete lists.example.org · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Inbound domains / lists.example.org /

    +

    Delete lists.example.org

    +
    +
    + +
    +
    +
    +

    This cannot be undone

    +
    +

    Deleting the inbound domain lists.example.org will:

    +
      +
    • stop accepting mail for it on port 25 — senders get a relay-denied reply;
    • +
    • remove its upstream and its 4 listed recipients.
    • +
    +

    Remove or repoint the MX record first, or mail for the domain will bounce. Outbound domains are not touched.

    +
    +
    + + Keep it +
    +
    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/in-domain.html b/docs/assets/panel-redesign/panel/in-domain.html new file mode 100644 index 0000000..0a879e9 --- /dev/null +++ b/docs/assets/panel-redesign/panel/in-domain.html @@ -0,0 +1,155 @@ + + + + + +lists.example.org · Inbound domains · SelfPost + + + + + + + + + +
    +
    +
    +
    DNSOKMX POINTS HERE
    +
    +

    Inbound domains /

    +

    lists.example.org

    +

    Accepted on port 25, handed to mx.internal:25 over required TLS · 4 recipients

    +
    +
    +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    01

    MX record

    Checked 1 min ago
    +
    +

    MX

    publishedKeep any existing primary MX if this is a backup-MX
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    02

    Upstream

    Not a mailbox
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    03

    Valid recipients

    +
    +
    +
    +
    +
    +
    +

    Unknown recipients are rejected at RCPT, so this relay generates no backscatter. Prefer a list unless the upstream itself rejects unknowns.

    + +
    +
    +
    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/in-domains.html b/docs/assets/panel-redesign/panel/in-domains.html new file mode 100644 index 0000000..e94bf97 --- /dev/null +++ b/docs/assets/panel-redesign/panel/in-domains.html @@ -0,0 +1,102 @@ + + + + + +Inbound domains · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Inbound

    +

    Inbound domains

    +

    Backup-MX / forwarder. Port 25 accepts mail only for the domains listed here and hands it to their upstream. No mailboxes.

    +
    +
    + +
    +
    01

    Add a domain

    +
    +
    +
    +
    +
    +
    +

    Mail is not accepted until an upstream host is saved on the domain's page.

    +
    +
    +
    + +
    +
    02

    Domains

    2 domains · DNS cached a few minutes
    + + + + + + +
    DomainDNSUpstreamRecipientsTLS to upstream
    lists.example.orgMXmx.internal:254 addressesrequiredOpenDelete
    acme.ioMX10.0.4.12:2525any addressopportunisticOpenDelete
    +
    The MX tag is green when at least one MX of the domain points at this server.
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/in-log.html b/docs/assets/panel-redesign/panel/in-log.html new file mode 100644 index 0000000..b94fda9 --- /dev/null +++ b/docs/assets/panel-redesign/panel/in-log.html @@ -0,0 +1,98 @@ + + + + + +Inbound log · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Inbound

    +

    Inbound log

    +

    What port 25 accepted and what it turned away. Accepts come from the inbound journal, rejects from mail.log.

    +
    +
    + +
    +
    + 01

    Messages

    +
    +
    +
    + +
    +
    + + + + + + + + +
    Time, UTCFromToSubjectActionReason
    09-21 14:00:18t.berg@uni.exampledev@lists.example.orgRe: [dev] patch series v3forwarded→ mx.internal:25
    09-21 13:57:41promo@bulk.examplesales@lists.example.org—rejected550 5.1.1 unknown recipient
    09-21 13:44:02hr@partner.examplejobs@acme.ioApplication: backend engineerdeferredupstream 10.0.4.12 timed out
    09-21 13:30:55noreply@github.examplesecurity@lists.example.org[advisory] new reportforwarded→ mx.internal:25
    +
    Page 1 of 38Older →New rows appear on their own
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/in-queue.html b/docs/assets/panel-redesign/panel/in-queue.html new file mode 100644 index 0000000..0f41fb5 --- /dev/null +++ b/docs/assets/panel-redesign/panel/in-queue.html @@ -0,0 +1,94 @@ + + + + + +Inbound queue · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Inbound

    +

    Inbound queue

    +

    Mail accepted on port 25 and still waiting for its upstream. The same Postfix queue as outbound, filtered by recipient domain.

    +
    +
    + +
    +
    01

    Waiting

    Refreshes every 10 s
    +
    Nothing is waiting. Every accepted message has reached its upstream.
    +
    + +
    +
    02

    How retries work

    This Postfix's policy, read at panel start
    +
    +
    +
    First retry
    5 min
    +
    Later retries
    doubling
    capped at 66 min
    +
    Kept in queue
    5 days
    +
    Then
    bounced
    back to the original sender
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/login.html b/docs/assets/panel-redesign/panel/login.html new file mode 100644 index 0000000..997db18 --- /dev/null +++ b/docs/assets/panel-redesign/panel/login.html @@ -0,0 +1,39 @@ + + + + + +Sign in · SelfPost + + + + + + + + + + diff --git a/docs/assets/panel-redesign/panel/out-app-created.html b/docs/assets/panel-redesign/panel/out-app-created.html new file mode 100644 index 0000000..c6966c3 --- /dev/null +++ b/docs/assets/panel-redesign/panel/out-app-created.html @@ -0,0 +1,104 @@ + + + + + +New password · prod-server · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Outbound domains / example.org / Applications /

    +

    New password for prod-server

    +
    +
    + +
    +
    +
    +

    Shown once

    Not stored — copy it now
    +
    +
    +
    +
    +
    +

    The previous password stopped working when this one was generated. If this one is lost, generate another.

    +
    + +
    +
    +
    +
    +

    Connect with

    +
    +
    +
    Server
    mail.example.org
    +
    Port
    465 SSL/TLS · 587 STARTTLS
    +
    May send as
    *@example.org
    +
    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/out-app.html b/docs/assets/panel-redesign/panel/out-app.html new file mode 100644 index 0000000..3816884 --- /dev/null +++ b/docs/assets/panel-redesign/panel/out-app.html @@ -0,0 +1,133 @@ + + + + + +prod-server · example.org · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Outbound domains / example.org / Applications /

    +

    prod-server

    +

    1 102 messages in 30 days · peak 96 msg/h · average 1.5 msg/h. The same form, empty, adds a new application.

    +
    + +
    + +
    +
    +
    +
    +
    01

    Sender

    +
    +
    +
    +

    Unique across domains; letters, digits, . - _.

    +
    +
    +
    +
    +

    One per line. Used only with a list.

    +
    +
    +
    + +
    +
    +
    02

    Client IP allow-list

    off
    +
    +
    +
    +
    +

    A login from anywhere else is refused with a 4xx at MAIL FROM. A sending control, fail-open — not an authentication boundary.

    +
    +
    +
    + +
    +
    +
    03

    Rate limit

    active
    +
    +
    +
    +
    +
    +
    +
    +

    Overrides the domain limit (288 / h) for this application; may be higher or lower, never above level 1.

    +
    +
    +
    +
    + +
    + + Cancel +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/out-domain-delete.html b/docs/assets/panel-redesign/panel/out-domain-delete.html new file mode 100644 index 0000000..5989907 --- /dev/null +++ b/docs/assets/panel-redesign/panel/out-domain-delete.html @@ -0,0 +1,107 @@ + + + + + +Delete example.org · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Outbound domains / example.org /

    +

    Delete example.org

    +
    +
    + +
    +
    +
    +

    This cannot be undone

    +
    +

    Deleting example.org will:

    +
      +
    • permanently delete its DKIM signing key;
    • +
    • delete both applications (prod-server, alerts) with their credentials and sender bindings;
    • +
    • reload OpenDKIM so the domain is no longer signed.
    • +
    +

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

    +
    +
    + + Keep it +
    +
    +
    +
    +
    +
    +
    +

    Moving it instead?

    +
    +

    Export the domain first. The file carries the key and the application passwords, so the new instance needs no DNS change.

    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/out-domain-settings.html b/docs/assets/panel-redesign/panel/out-domain-settings.html new file mode 100644 index 0000000..c5bbcf7 --- /dev/null +++ b/docs/assets/panel-redesign/panel/out-domain-settings.html @@ -0,0 +1,143 @@ + + + + + +Settings · example.org · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Outbound domains / example.org /

    +

    Domain settings

    +

    The things set once and rarely touched: where DMARC reports go, the sending ceiling, moving the domain elsewhere.

    +
    +
    + +
    +
    +
    +
    01

    DMARC report address

    +
    +
    +
    +
    +

    The default comes from Settings. Changing it changes the DMARC record to publish.

    + +
    +
    +
    + +
    +
    03

    Export domain

    +
    +

    One file with the DKIM key and every application's password hash — import it on another SelfPost and the published DNS needs no change. The file is a secret.

    +
    +
    +
    +
    +
    +
    +

    Keep this password: SelfPost does not store it, and the file cannot be opened without it.

    + +
    +
    +
    +
    + +
    +
    +
    02

    Rate limit

    active
    +
    +
    +
    +
    +
    +
    +

    Computed limit: 288 messages / 3600 s. Peak was 96 msg/h. Last recalculated yesterday 03:00 UTC.

    +
    + + + +
    +
    +

    Applies to every sender on this domain and cannot exceed level 1 (600 messages / 3600 s per client IP, set in .env). An application may carry its own limit above or below this one.

    +
    +
    + +
    +
    04

    Delete domain

    +
    +

    Removes the DKIM key and both applications. Asks once more before doing it.

    + Delete example.org… +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/out-domain.html b/docs/assets/panel-redesign/panel/out-domain.html new file mode 100644 index 0000000..8f73f2b --- /dev/null +++ b/docs/assets/panel-redesign/panel/out-domain.html @@ -0,0 +1,176 @@ + + + + + +example.org · Outbound domains · SelfPost + + + + + + + + + +
    +
    +
    +
    DNSFAIL1 OF 3 RECORDS
    +
    +

    Outbound domains /

    +

    example.org

    +

    1 284 messages in 30 days · peak 96 msg/h · average 1.8 msg/h

    +
    +
    +
    + Add application +
    +
    + +
    +
    + +
    + +
    +
    +
    01

    DNS records

    Checked 3 min ago
    + +
    +

    DKIM

    publishedNot a secret · selector sp2026
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    SPF

    mismatchMerge into the existing record — never publish a second one
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    The published record does not authorize 203.0.113.25. Add ip4:203.0.113.25 before ~all. Shallow check: include: is not followed.

    +
    + +
    +

    DMARC

    publishedReport address from Settings
    +
    +
    +
    +
    +
    +
    +
    +

    p=none does not affect delivery. Tighten to quarantine, then reject, once the reports look clean.

    +
    +
    + +
    +
    02

    Applications

    SASL logins that may send as this domain
    + + + + + + + + +
    LoginMay send as30 daysLimits
    prod-server*@example.org1 102 msg · peak 96/h200 / hEditNew passwordDelete
    alertsalerts@example.org, noc@example.org182 msg · peak 14/hdomain 2 IPsEditNew passwordDelete
    +
    New password and Delete ask for confirmation; the old password stops working at once.
    +
    + +
    +
    03

    Connection

    The same for every domain
    +
    +
    +
    Server
    mail.example.org
    +
    Port 465
    SSL/TLS (implicit)
    +
    Port 587
    STARTTLS (submission)
    +
    Authentication
    Application login, required on every port
    +
    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/out-domains.html b/docs/assets/panel-redesign/panel/out-domains.html new file mode 100644 index 0000000..3725d6d --- /dev/null +++ b/docs/assets/panel-redesign/panel/out-domains.html @@ -0,0 +1,104 @@ + + + + + +Outbound domains · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Outbound

    +

    Outbound domains

    +

    Domains this relay signs and sends for. Each gets a DKIM key and its own application logins.

    +
    +
    + +
    +
    01

    Add a domain

    +
    +
    +
    +
    +
    +
    +

    A DKIM key is generated at once; the next page shows the three DNS records to publish. + Moving a domain from another SelfPost? Import it instead — the key and passwords come across.

    +
    +
    +
    + +
    +
    02

    Domains

    3 domains · DNS cached a few minutes
    + + + + + + + +
    DomainDNSSelectorApps30 days
    example.orgDKIM SPF DMARCsp202621 284 msg · peak 96/hOpenDelete
    shop.example.orgDKIM SPF DMARCsp2026118 920 msg · peak 410/hOpenDelete
    notify.acme.ioDKIM SPF DMARCsp20253402 msg · peak 22/hOpenDelete
    +
    Each tag is one check. Green — published and correct; amber — published but weak; red — missing or wrong.
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/out-log.html b/docs/assets/panel-redesign/panel/out-log.html new file mode 100644 index 0000000..3fa85a5 --- /dev/null +++ b/docs/assets/panel-redesign/panel/out-log.html @@ -0,0 +1,101 @@ + + + + + +Outbound log · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Outbound

    +

    Outbound log

    +

    Every message an application handed to the relay. Rows older than 30 days are deleted — change that in Settings.

    +
    +
    + +
    +
    + 01

    Messages

    +
    +
    +
    +
    + +
    +
    + + + + + + + + + + +
    Time, UTCFromToSubjectStatus
    09-21 14:01:52shop@shop.example.orgm.keller@gmx.deYour order #48213 has shippedsentDetails
    09-21 14:01:50shop@shop.example.organna.lind@outlook.comYour order #48212 has shippedsentDetails
    09-21 13:58:07alerts@example.orgnoc@example.org[FIRING] disk usage above 90 % on db-2deferredDetails
    09-21 13:41:19no-reply@notify.acme.ioj.doe@nonexistent.exampleReset your passwordbouncedDetails
    09-21 13:40:02prod@example.orgbilling@partner.exampleInvoice 2026-0917rate-limitedDetails
    09-21 13:12:44shop@shop.example.orgp.novak@seznam.czWe received your return requestsentDetails
    +
    Page 1 of 412Older →New rows appear on their own
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/out-message.html b/docs/assets/panel-redesign/panel/out-message.html new file mode 100644 index 0000000..49dbc0e --- /dev/null +++ b/docs/assets/panel-redesign/panel/out-message.html @@ -0,0 +1,115 @@ + + + + + +Message · Outbound log · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Outbound log /

    +

    [FIRING] disk usage above 90 % on db-2

    +

    alerts@example.orgnoc@example.orgdeferred

    +
    +
    + +
    +
    +
    +
    01

    Message

    +
    +
    + +
    Application
    alerts
    +
    Accepted
    2026-09-21 13:58:07 UTC
    +
    Status reported
    2026-09-21 13:58:09 UTC
    +
    Queue id
    4XcB7k2Jm9z1
    +
    Journal id
    184220
    +
    +
    +
    +
    +
    +
    +
    02

    History

    +
    +
      +
    1. Accepted from alerts at 198.51.100.7
    2. +
    3. Deferred — the receiving server asked to retry later
    4. +
    5. Still in the queue; kept for 5 days, then bounced
    6. +
    +
    +
    +
    +
    + +
    +
    03

    Delivery log

    Lines from mail.log with this queue id
    +
    13:58:07 postfix/smtpd[2114]: 4XcB7k2Jm9z1: client=unknown[198.51.100.7], sasl_username=alerts
    +13:58:07 postfix/cleanup[2120]: 4XcB7k2Jm9z1: message-id=<a81f0c@alertmanager>
    +13:58:07 opendkim[39]: 4XcB7k2Jm9z1: DKIM-Signature field added (s=sp2026, d=example.org)
    +13:58:09 postfix/smtp[2123]: 4XcB7k2Jm9z1: to=<noc@example.org>, relay=mx1.example.org[192.0.2.10]:25, delay=1.9, status=deferred (host mx1.example.org said: 451 4.7.1 Greylisted, try again in 5 minutes)
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/out-queue.html b/docs/assets/panel-redesign/panel/out-queue.html new file mode 100644 index 0000000..ff7e0e3 --- /dev/null +++ b/docs/assets/panel-redesign/panel/out-queue.html @@ -0,0 +1,99 @@ + + + + + +Outbound queue · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Outbound

    +

    Outbound queue

    +

    Messages accepted from applications and still waiting for the receiving server. Refreshes every 10 s.

    +
    +
    + +
    +
    01

    Waiting

    1 message · 3.1 kB
    + + + + + +
    Queue idArrived, UTCFromToLast reply
    4XcB7k2Jm9z109-21 13:58:07alerts@example.orgnoc@example.org451 4.7.1 Greylisted, try again in 5 minutesDetails
    +
    + +
    +
    02

    How retries work

    This Postfix's policy, read at panel start
    +
    +
    +
    First retry
    5 min
    +
    Later retries
    doubling
    capped at 66 min
    +
    Kept in queue
    5 days
    +
    Then
    bounced
    no attempt counter, only time
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/overview.html b/docs/assets/panel-redesign/panel/overview.html new file mode 100644 index 0000000..e118e0b --- /dev/null +++ b/docs/assets/panel-redesign/panel/overview.html @@ -0,0 +1,119 @@ + + + + + +Overview · SelfPost + + + + + + + + + +
    +
    +
    +
    SELFPOSTWARN1 OF 6 CHECKS
    +
    +

    mail.example.org

    +

    Mail is flowing. One thing needs attention.

    +

    The TLS certificate expires in 12 days and has not renewed yet.

    +
    +
    Updated 14:02:31 UTC
    refreshes every 10 s
    +
    + + + +
    +
    +
    +
    02

    Outbound domains

    + + + + + + + +
    DomainDNSApps30 days
    example.orgDKIM SPF DMARC21 284 msg
    shop.example.orgDKIM SPF DMARC118 920 msg
    notify.acme.ioDKIM SPF DMARC3402 msg
    +
    +
    +
    +
    +
    03

    Inbound domains

    + + + + + + +
    DomainMXUpstream
    lists.example.orgMXmx.internal:25
    acme.ioMX10.0.4.12:2525
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/settings.html b/docs/assets/panel-redesign/panel/settings.html new file mode 100644 index 0000000..6d8a340 --- /dev/null +++ b/docs/assets/panel-redesign/panel/settings.html @@ -0,0 +1,124 @@ + + + + + +Settings · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Server

    +

    Settings

    +

    Defaults for the whole instance. Your own username and password are under Account.

    +
    +
    + +
    +
    +
    +
    +
    01

    DMARC aggregate reports

    authorized
    +
    +
    +
    +

    The default rua= for every outbound domain; a domain may override it. An address on mail.example.org is received and parsed by SelfPost itself.

    +
    +
    +

    Report authorization

    publishedNeeded when rua= points at another domain
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    02

    Log retention

    +
    +
    +
    +

    The main driver of /data growth. Does not affect mail.log rotation (fourteen daily files).

    +
    +
    + +
    +
    03

    Sending rate limits

    Read-only
    +
    +
    +
    Level 1 · per client IP
    600 / h
    Set in .env; restart to change. Nothing below may exceed it.
    +
    Level 2 · domain, application
    Set on each domain and application.
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/setup.html b/docs/assets/panel-redesign/panel/setup.html new file mode 100644 index 0000000..42840ba --- /dev/null +++ b/docs/assets/panel-redesign/panel/setup.html @@ -0,0 +1,44 @@ + + + + + +Create administrator · SelfPost + + + + + + + + + + diff --git a/docs/assets/panel-redesign/panel/src/account.html b/docs/assets/panel-redesign/panel/src/account.html new file mode 100644 index 0000000..48723ab --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/account.html @@ -0,0 +1,37 @@ + +
    +
    +

    Signed in as admin · global administrator

    +

    Account

    +

    Your sign-in to this panel. Applications keep their own logins and passwords and are not affected.

    +
    +
    + +
    +
    +
    +
    01

    Panel credentials

    +
    +
    +
    +
    +
    +
    +
    +
    +

    Leave both new-password fields empty to change the username only.

    + +
    +
    +
    +
    +
    +
    +

    What happens

    +
    +

    Changing the password signs out every other session of this account. This one stays signed in.

    +

    A domain administrator sees this same page, and nothing under Server.

    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/backup.html b/docs/assets/panel-redesign/panel/src/backup.html new file mode 100644 index 0000000..d492c03 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/backup.html @@ -0,0 +1,44 @@ + +
    +
    +

    Server

    +

    Backup

    +

    Take the whole instance with you, or bring one domain in from another SelfPost. Both files are secrets.

    +
    +
    + +
    +
    +
    +
    01

    Full backup

    +
    +

    data/ (database, DKIM keys, credentials, the queue), docker-compose.yml, .env and certs/ in one archive. Restore into an empty project directory and start the same SelfPost version. The reverse-proxy vhost is not included.

    +
    +
    +
    +
    +
    +
    +

    Keep this password: SelfPost does not store it, and the file cannot be opened without it.

    + +
    +
    +
    +
    + +
    +
    +
    02

    Import a domain

    +
    +

    A domain export from another instance — plain .json or encrypted .spde. Its DKIM key and application passwords come across, so the published DNS needs no change. The export itself is made on the domain's settings.

    +
    +
    +
    +
    +

    Asked only for an encrypted .spde file.

    + +
    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/dmarc-domain.html b/docs/assets/panel-redesign/panel/src/dmarc-domain.html new file mode 100644 index 0000000..631b2a7 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/dmarc-domain.html @@ -0,0 +1,38 @@ + +
    +
    DMARC99.6%PASS · 7 DAYS
    +
    +

    DMARC reports /

    +

    shop.example.org

    +

    11 204 pass · 41 fail in the last 7 days. Failures come from a source that is not this relay — review before tightening p=.

    +
    + Domain page +
    + +
    +
    +
    +
    01

    Sources · 7 days

    + + + + + + +
    SourcePassFailDisposition
    203.0.113.25 this relay11 2040none
    198.51.100.44041none
    +
    +
    +
    +
    +
    02

    Reports

    + + + + + + + +
    Received, UTCReporterWindowPassFail
    09-21 04:40Outlook.com20 Sep1 9037View
    09-21 02:15google.com20 Sep6 4100View
    09-20 04:38Outlook.com19 Sep1 87734View
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/dmarc-report.html b/docs/assets/panel-redesign/panel/src/dmarc-report.html new file mode 100644 index 0000000..c1ee0a2 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/dmarc-report.html @@ -0,0 +1,47 @@ + +
    +
    +

    DMARC reports / shop.example.org /

    +

    Outlook.com · 20 Sep 2026

    +

    shop.example.org1 903 pass7 fail

    +
    +
    + +
    +
    +
    +
    01

    Report

    +
    +
    +
    Reporter
    Outlook.com
    +
    Report id
    5f1c9a0e7b2d4e61a3
    +
    Window
    20 Sep 00:00 – 21 Sep 00:00 UTC
    +
    Received
    2026-09-21 04:40 UTC
    +
    +
    +
    +
    +
    +
    +
    02

    Published policy

    +
    +
    +
    p / sp / pct
    none / none / 100
    +
    adkim / aspf
    r / r
    +
    rua
    dmarc@mail.example.org
    +
    +
    +
    +
    +
    + +
    +
    03

    Records

    Parsed from the aggregate XML
    + + + + + + +
    SourceCountDispositionSPFDKIMHeader from
    203.0.113.25 this relay1 903nonepasspassshop.example.org
    198.51.100.447nonefailfailshop.example.org
    +
    diff --git a/docs/assets/panel-redesign/panel/src/dmarc.html b/docs/assets/panel-redesign/panel/src/dmarc.html new file mode 100644 index 0000000..0d52a07 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/dmarc.html @@ -0,0 +1,33 @@ + +
    +
    +

    Outbound

    +

    DMARC reports

    +

    How receivers saw mail claiming to be from your domains. Aggregate (rua=) only; open a report for the parsed XML.

    +
    +
    + +
    +
    01

    Ingest

    ok
    +
    +
    +
    Last report
    2026-09-21 06:12 UTC
    +
    This week
    14 kept · 0 parse failures
    +
    Hosted address
    dmarc@mail.example.org
    +
    Retention
    500 reports or 90 days
    +
    +
    +
    + +
    +
    02

    Recent reports

    + + + + + + + + +
    Received, UTCDomainReporterWindowPassFail
    09-21 06:12example.orggoogle.com20 Sep4120View
    09-21 04:40shop.example.orgOutlook.com20 Sep1 9037View
    09-20 23:58example.orgYahoo19 Sep380View
    09-20 06:09notify.acme.iogoogle.com19 Sep610View
    +
    diff --git a/docs/assets/panel-redesign/panel/src/health.html b/docs/assets/panel-redesign/panel/src/health.html new file mode 100644 index 0000000..9d76580 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/health.html @@ -0,0 +1,79 @@ + +
    +
    +

    Server

    +

    Health

    +

    Everything behind the six cards on Overview. Refreshes every 10 s.

    +
    +
    +
    + +
    +
    +
    +
    01

    Machine

    ok
    + + + + + + + +
    ResourceUsageDetail
    CPU12 %2 cores, load 0.21
    Memory41 %812 MB of 1.9 GB
    Network↓ 18 kB/s · ↑ 44 kB/seth0: 2.1 GB in, 6.4 GB out
    +
    +
    +
    +
    +
    02

    Processes

    ok
    + + + + + + + + + +
    ProgramStateDetail
    postfixrunningpid 41, uptime 18 days
    opendkimrunningpid 39, uptime 18 days
    journal-milterrunningpid 40, uptime 18 days
    panelrunningpid 44, uptime 18 days
    dmarc-ingestrunningpid 47, uptime 18 days
    +
    +
    +
    + +
    +
    +
    +
    03

    TLS certificate

    warn
    +
    +
    +
    Expires
    2026-10-03 09:14 UTC
    +
    Names
    mail.example.org
    +
    +

    Expires in 12 days — renewal has not run.

    +

    Renewal normally runs 30 days before expiry. Check that port 80 is reachable and look for acme lines in the system log.

    +
    +
    +
    +
    +
    +
    04

    Milter sockets

    ok
    + + + + + + +
    MilterState
    opendkim
    inet:127.0.0.1:8891
    ok
    journal-milter
    unix:/run/selfpost/journal.sock
    ok
    +
    +
    +
    +
    +
    05

    Hostname and reverse DNS

    ok
    +
    +
    +
    Hostname
    mail.example.org
    +
    Lookup
    → 203.0.113.25
    ← mail.example.org
    +
    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/help.html b/docs/assets/panel-redesign/panel/src/help.html new file mode 100644 index 0000000..7a6abb5 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/help.html @@ -0,0 +1,47 @@ + +
    +
    +

    Reference

    +

    Help

    +

    What each screen checks and what to do when it is not green. Every numbered section on a page links to its topic here.

    +
    +
    + +
    + +
    +
    +
    01

    Overview

    +

    Health checks

    Six checks, the worst of them is the postmark. ok needs nothing; warn still delivers mail but will stop doing so if ignored; fail means mail is being lost or refused now.

    +

    TLS certificate

    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.

    +

    Hostname and reverse DNS

    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.

    +
    +
    +
    02

    Outbound

    +

    DNS records

    DKIM proves the message was signed by this relay, SPF lists the addresses allowed to send for the domain, DMARC tells receivers what to do when both fail and where to report. Publish all three exactly as shown; merge SPF into an existing record rather than adding a second one.

    +

    Applications

    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.

    +

    Rate limits

    Level 1 is a per-IP ceiling set in .env. Level 2 is optional, per domain and per application, manual or computed from 30-day statistics, and can never exceed level 1.

    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/in-domain-delete.html b/docs/assets/panel-redesign/panel/src/in-domain-delete.html new file mode 100644 index 0000000..fdaa486 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/in-domain-delete.html @@ -0,0 +1,29 @@ + +
    +
    +

    Inbound domains / lists.example.org /

    +

    Delete lists.example.org

    +
    +
    + +
    +
    +
    +

    This cannot be undone

    +
    +

    Deleting the inbound domain lists.example.org will:

    +
      +
    • stop accepting mail for it on port 25 — senders get a relay-denied reply;
    • +
    • remove its upstream and its 4 listed recipients.
    • +
    +

    Remove or repoint the MX record first, or mail for the domain will bounce. Outbound domains are not touched.

    +
    +
    + + Keep it +
    +
    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/in-domain.html b/docs/assets/panel-redesign/panel/src/in-domain.html new file mode 100644 index 0000000..3e68ac6 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/in-domain.html @@ -0,0 +1,86 @@ + +
    +
    DNSOKMX POINTS HERE
    +
    +

    Inbound domains /

    +

    lists.example.org

    +

    Accepted on port 25, handed to mx.internal:25 over required TLS · 4 recipients

    +
    +
    +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    01

    MX record

    Checked 1 min ago
    +
    +

    MX

    publishedKeep any existing primary MX if this is a backup-MX
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    02

    Upstream

    Not a mailbox
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    03

    Valid recipients

    +
    +
    +
    +
    +
    +
    +

    Unknown recipients are rejected at RCPT, so this relay generates no backscatter. Prefer a list unless the upstream itself rejects unknowns.

    + +
    +
    +
    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/in-domains.html b/docs/assets/panel-redesign/panel/src/in-domains.html new file mode 100644 index 0000000..2072bb6 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/in-domains.html @@ -0,0 +1,33 @@ + +
    +
    +

    Inbound

    +

    Inbound domains

    +

    Backup-MX / forwarder. Port 25 accepts mail only for the domains listed here and hands it to their upstream. No mailboxes.

    +
    +
    + +
    +
    01

    Add a domain

    +
    +
    +
    +
    +
    +
    +

    Mail is not accepted until an upstream host is saved on the domain's page.

    +
    +
    +
    + +
    +
    02

    Domains

    2 domains · DNS cached a few minutes
    + + + + + + +
    DomainDNSUpstreamRecipientsTLS to upstream
    lists.example.orgMXmx.internal:254 addressesrequiredOpenDelete
    acme.ioMX10.0.4.12:2525any addressopportunisticOpenDelete
    +
    The MX tag is green when at least one MX of the domain points at this server.
    +
    diff --git a/docs/assets/panel-redesign/panel/src/in-log.html b/docs/assets/panel-redesign/panel/src/in-log.html new file mode 100644 index 0000000..2bcaf69 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/in-log.html @@ -0,0 +1,29 @@ + +
    +
    +

    Inbound

    +

    Inbound log

    +

    What port 25 accepted and what it turned away. Accepts come from the inbound journal, rejects from mail.log.

    +
    +
    + +
    +
    + 01

    Messages

    +
    +
    +
    + +
    +
    + + + + + + + + +
    Time, UTCFromToSubjectActionReason
    09-21 14:00:18t.berg@uni.exampledev@lists.example.orgRe: [dev] patch series v3forwarded→ mx.internal:25
    09-21 13:57:41promo@bulk.examplesales@lists.example.org—rejected550 5.1.1 unknown recipient
    09-21 13:44:02hr@partner.examplejobs@acme.ioApplication: backend engineerdeferredupstream 10.0.4.12 timed out
    09-21 13:30:55noreply@github.examplesecurity@lists.example.org[advisory] new reportforwarded→ mx.internal:25
    +
    Page 1 of 38Older →New rows appear on their own
    +
    diff --git a/docs/assets/panel-redesign/panel/src/in-queue.html b/docs/assets/panel-redesign/panel/src/in-queue.html new file mode 100644 index 0000000..42bffff --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/in-queue.html @@ -0,0 +1,25 @@ + +
    +
    +

    Inbound

    +

    Inbound queue

    +

    Mail accepted on port 25 and still waiting for its upstream. The same Postfix queue as outbound, filtered by recipient domain.

    +
    +
    + +
    +
    01

    Waiting

    Refreshes every 10 s
    +
    Nothing is waiting. Every accepted message has reached its upstream.
    +
    + +
    +
    02

    How retries work

    This Postfix's policy, read at panel start
    +
    +
    +
    First retry
    5 min
    +
    Later retries
    doubling
    capped at 66 min
    +
    Kept in queue
    5 days
    +
    Then
    bounced
    back to the original sender
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/login.html b/docs/assets/panel-redesign/panel/src/login.html new file mode 100644 index 0000000..18e70c7 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/login.html @@ -0,0 +1,25 @@ + + diff --git a/docs/assets/panel-redesign/panel/src/out-app-created.html b/docs/assets/panel-redesign/panel/src/out-app-created.html new file mode 100644 index 0000000..294e2de --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/out-app-created.html @@ -0,0 +1,35 @@ + +
    +
    +

    Outbound domains / example.org / Applications /

    +

    New password for prod-server

    +
    +
    + +
    +
    +
    +

    Shown once

    Not stored — copy it now
    +
    +
    +
    +
    +
    +

    The previous password stopped working when this one was generated. If this one is lost, generate another.

    +
    + +
    +
    +
    +
    +

    Connect with

    +
    +
    +
    Server
    mail.example.org
    +
    Port
    465 SSL/TLS · 587 STARTTLS
    +
    May send as
    *@example.org
    +
    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/out-app.html b/docs/assets/panel-redesign/panel/src/out-app.html new file mode 100644 index 0000000..ffb61c6 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/out-app.html @@ -0,0 +1,64 @@ + +
    +
    +

    Outbound domains / example.org / Applications /

    +

    prod-server

    +

    1 102 messages in 30 days · peak 96 msg/h · average 1.5 msg/h. The same form, empty, adds a new application.

    +
    + +
    + +
    +
    +
    +
    +
    01

    Sender

    +
    +
    +
    +

    Unique across domains; letters, digits, . - _.

    +
    +
    +
    +
    +

    One per line. Used only with a list.

    +
    +
    +
    + +
    +
    +
    02

    Client IP allow-list

    off
    +
    +
    +
    +
    +

    A login from anywhere else is refused with a 4xx at MAIL FROM. A sending control, fail-open — not an authentication boundary.

    +
    +
    +
    + +
    +
    +
    03

    Rate limit

    active
    +
    +
    +
    +
    +
    +
    +
    +

    Overrides the domain limit (288 / h) for this application; may be higher or lower, never above level 1.

    +
    +
    +
    +
    + +
    + + Cancel +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/out-domain-delete.html b/docs/assets/panel-redesign/panel/src/out-domain-delete.html new file mode 100644 index 0000000..5da34d6 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/out-domain-delete.html @@ -0,0 +1,38 @@ + +
    +
    +

    Outbound domains / example.org /

    +

    Delete example.org

    +
    +
    + +
    +
    +
    +

    This cannot be undone

    +
    +

    Deleting example.org will:

    +
      +
    • permanently delete its DKIM signing key;
    • +
    • delete both applications (prod-server, alerts) with their credentials and sender bindings;
    • +
    • reload OpenDKIM so the domain is no longer signed.
    • +
    +

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

    +
    +
    + + Keep it +
    +
    +
    +
    +
    +
    +
    +

    Moving it instead?

    +
    +

    Export the domain first. The file carries the key and the application passwords, so the new instance needs no DNS change.

    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/out-domain-settings.html b/docs/assets/panel-redesign/panel/src/out-domain-settings.html new file mode 100644 index 0000000..9dc076e --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/out-domain-settings.html @@ -0,0 +1,74 @@ + +
    +
    +

    Outbound domains / example.org /

    +

    Domain settings

    +

    The things set once and rarely touched: where DMARC reports go, the sending ceiling, moving the domain elsewhere.

    +
    +
    + +
    +
    +
    +
    01

    DMARC report address

    +
    +
    +
    +
    +

    The default comes from Settings. Changing it changes the DMARC record to publish.

    + +
    +
    +
    + +
    +
    03

    Export domain

    +
    +

    One file with the DKIM key and every application's password hash — import it on another SelfPost and the published DNS needs no change. The file is a secret.

    +
    +
    +
    +
    +
    +
    +

    Keep this password: SelfPost does not store it, and the file cannot be opened without it.

    + +
    +
    +
    +
    + +
    +
    +
    02

    Rate limit

    active
    +
    +
    +
    +
    +
    +
    +

    Computed limit: 288 messages / 3600 s. Peak was 96 msg/h. Last recalculated yesterday 03:00 UTC.

    +
    + + + +
    +
    +

    Applies to every sender on this domain and cannot exceed level 1 (600 messages / 3600 s per client IP, set in .env). An application may carry its own limit above or below this one.

    +
    +
    + +
    +
    04

    Delete domain

    +
    +

    Removes the DKIM key and both applications. Asks once more before doing it.

    + Delete example.org… +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/out-domain.html b/docs/assets/panel-redesign/panel/src/out-domain.html new file mode 100644 index 0000000..66eddf4 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/out-domain.html @@ -0,0 +1,107 @@ + +
    +
    DNSFAIL1 OF 3 RECORDS
    +
    +

    Outbound domains /

    +

    example.org

    +

    1 284 messages in 30 days · peak 96 msg/h · average 1.8 msg/h

    +
    +
    +
    + Add application +
    +
    + +
    +
    + +
    + +
    +
    +
    01

    DNS records

    Checked 3 min ago
    + +
    +

    DKIM

    publishedNot a secret · selector sp2026
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +

    SPF

    mismatchMerge into the existing record — never publish a second one
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    The published record does not authorize 203.0.113.25. Add ip4:203.0.113.25 before ~all. Shallow check: include: is not followed.

    +
    + +
    +

    DMARC

    publishedReport address from Settings
    +
    +
    +
    +
    +
    +
    +
    +

    p=none does not affect delivery. Tighten to quarantine, then reject, once the reports look clean.

    +
    +
    + +
    +
    02

    Applications

    SASL logins that may send as this domain
    + + + + + + + + +
    LoginMay send as30 daysLimits
    prod-server*@example.org1 102 msg · peak 96/h200 / hEditNew passwordDelete
    alertsalerts@example.org, noc@example.org182 msg · peak 14/hdomain 2 IPsEditNew passwordDelete
    +
    New password and Delete ask for confirmation; the old password stops working at once.
    +
    + +
    +
    03

    Connection

    The same for every domain
    +
    +
    +
    Server
    mail.example.org
    +
    Port 465
    SSL/TLS (implicit)
    +
    Port 587
    STARTTLS (submission)
    +
    Authentication
    Application login, required on every port
    +
    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/out-domains.html b/docs/assets/panel-redesign/panel/src/out-domains.html new file mode 100644 index 0000000..94b4ce7 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/out-domains.html @@ -0,0 +1,35 @@ + +
    +
    +

    Outbound

    +

    Outbound domains

    +

    Domains this relay signs and sends for. Each gets a DKIM key and its own application logins.

    +
    +
    + +
    +
    01

    Add a domain

    +
    +
    +
    +
    +
    +
    +

    A DKIM key is generated at once; the next page shows the three DNS records to publish. + Moving a domain from another SelfPost? Import it instead — the key and passwords come across.

    +
    +
    +
    + +
    +
    02

    Domains

    3 domains · DNS cached a few minutes
    + + + + + + + +
    DomainDNSSelectorApps30 days
    example.orgDKIM SPF DMARCsp202621 284 msg · peak 96/hOpenDelete
    shop.example.orgDKIM SPF DMARCsp2026118 920 msg · peak 410/hOpenDelete
    notify.acme.ioDKIM SPF DMARCsp20253402 msg · peak 22/hOpenDelete
    +
    Each tag is one check. Green — published and correct; amber — published but weak; red — missing or wrong.
    +
    diff --git a/docs/assets/panel-redesign/panel/src/out-log.html b/docs/assets/panel-redesign/panel/src/out-log.html new file mode 100644 index 0000000..8525198 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/out-log.html @@ -0,0 +1,32 @@ + +
    +
    +

    Outbound

    +

    Outbound log

    +

    Every message an application handed to the relay. Rows older than 30 days are deleted — change that in Settings.

    +
    +
    + +
    +
    + 01

    Messages

    +
    +
    +
    +
    + +
    +
    + + + + + + + + + + +
    Time, UTCFromToSubjectStatus
    09-21 14:01:52shop@shop.example.orgm.keller@gmx.deYour order #48213 has shippedsentDetails
    09-21 14:01:50shop@shop.example.organna.lind@outlook.comYour order #48212 has shippedsentDetails
    09-21 13:58:07alerts@example.orgnoc@example.org[FIRING] disk usage above 90 % on db-2deferredDetails
    09-21 13:41:19no-reply@notify.acme.ioj.doe@nonexistent.exampleReset your passwordbouncedDetails
    09-21 13:40:02prod@example.orgbilling@partner.exampleInvoice 2026-0917rate-limitedDetails
    09-21 13:12:44shop@shop.example.orgp.novak@seznam.czWe received your return requestsentDetails
    +
    Page 1 of 412Older →New rows appear on their own
    +
    diff --git a/docs/assets/panel-redesign/panel/src/out-message.html b/docs/assets/panel-redesign/panel/src/out-message.html new file mode 100644 index 0000000..cd479e0 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/out-message.html @@ -0,0 +1,46 @@ + +
    +
    +

    Outbound log /

    +

    [FIRING] disk usage above 90 % on db-2

    +

    alerts@example.orgnoc@example.orgdeferred

    +
    +
    + +
    +
    +
    +
    01

    Message

    +
    +
    + +
    Application
    alerts
    +
    Accepted
    2026-09-21 13:58:07 UTC
    +
    Status reported
    2026-09-21 13:58:09 UTC
    +
    Queue id
    4XcB7k2Jm9z1
    +
    Journal id
    184220
    +
    +
    +
    +
    +
    +
    +
    02

    History

    +
    +
      +
    1. Accepted from alerts at 198.51.100.7
    2. +
    3. Deferred — the receiving server asked to retry later
    4. +
    5. Still in the queue; kept for 5 days, then bounced
    6. +
    +
    +
    +
    +
    + +
    +
    03

    Delivery log

    Lines from mail.log with this queue id
    +
    13:58:07 postfix/smtpd[2114]: 4XcB7k2Jm9z1: client=unknown[198.51.100.7], sasl_username=alerts
    +13:58:07 postfix/cleanup[2120]: 4XcB7k2Jm9z1: message-id=<a81f0c@alertmanager>
    +13:58:07 opendkim[39]: 4XcB7k2Jm9z1: DKIM-Signature field added (s=sp2026, d=example.org)
    +13:58:09 postfix/smtp[2123]: 4XcB7k2Jm9z1: to=<noc@example.org>, relay=mx1.example.org[192.0.2.10]:25, delay=1.9, status=deferred (host mx1.example.org said: 451 4.7.1 Greylisted, try again in 5 minutes)
    +
    diff --git a/docs/assets/panel-redesign/panel/src/out-queue.html b/docs/assets/panel-redesign/panel/src/out-queue.html new file mode 100644 index 0000000..0e7847d --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/out-queue.html @@ -0,0 +1,30 @@ + +
    +
    +

    Outbound

    +

    Outbound queue

    +

    Messages accepted from applications and still waiting for the receiving server. Refreshes every 10 s.

    +
    +
    + +
    +
    01

    Waiting

    1 message · 3.1 kB
    + + + + + +
    Queue idArrived, UTCFromToLast reply
    4XcB7k2Jm9z109-21 13:58:07alerts@example.orgnoc@example.org451 4.7.1 Greylisted, try again in 5 minutesDetails
    +
    + +
    +
    02

    How retries work

    This Postfix's policy, read at panel start
    +
    +
    +
    First retry
    5 min
    +
    Later retries
    doubling
    capped at 66 min
    +
    Kept in queue
    5 days
    +
    Then
    bounced
    no attempt counter, only time
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/overview.html b/docs/assets/panel-redesign/panel/src/overview.html new file mode 100644 index 0000000..07838a7 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/overview.html @@ -0,0 +1,50 @@ + +
    +
    SELFPOSTWARN1 OF 6 CHECKS
    +
    +

    mail.example.org

    +

    Mail is flowing. One thing needs attention.

    +

    The TLS certificate expires in 12 days and has not renewed yet.

    +
    +
    Updated 14:02:31 UTC
    refreshes every 10 s
    +
    + + + +
    +
    +
    +
    02

    Outbound domains

    + + + + + + + +
    DomainDNSApps30 days
    example.orgDKIM SPF DMARC21 284 msg
    shop.example.orgDKIM SPF DMARC118 920 msg
    notify.acme.ioDKIM SPF DMARC3402 msg
    +
    +
    +
    +
    +
    03

    Inbound domains

    + + + + + + +
    DomainMXUpstream
    lists.example.orgMXmx.internal:25
    acme.ioMX10.0.4.12:2525
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/settings.html b/docs/assets/panel-redesign/panel/src/settings.html new file mode 100644 index 0000000..2bbb481 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/settings.html @@ -0,0 +1,55 @@ + +
    +
    +

    Server

    +

    Settings

    +

    Defaults for the whole instance. Your own username and password are under Account.

    +
    +
    + +
    +
    +
    +
    +
    01

    DMARC aggregate reports

    authorized
    +
    +
    +
    +

    The default rua= for every outbound domain; a domain may override it. An address on mail.example.org is received and parsed by SelfPost itself.

    +
    +
    +

    Report authorization

    publishedNeeded when rua= points at another domain
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    02

    Log retention

    +
    +
    +
    +

    The main driver of /data growth. Does not affect mail.log rotation (fourteen daily files).

    +
    +
    + +
    +
    03

    Sending rate limits

    Read-only
    +
    +
    +
    Level 1 · per client IP
    600 / h
    Set in .env; restart to change. Nothing below may exceed it.
    +
    Level 2 · domain, application
    Set on each domain and application.
    +
    +
    +
    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/setup.html b/docs/assets/panel-redesign/panel/src/setup.html new file mode 100644 index 0000000..f7c3d76 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/setup.html @@ -0,0 +1,30 @@ + + diff --git a/docs/assets/panel-redesign/panel/src/system-log.html b/docs/assets/panel-redesign/panel/src/system-log.html new file mode 100644 index 0000000..81a66ff --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/system-log.html @@ -0,0 +1,22 @@ + +
    +
    +

    Server

    +

    System log

    +

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

    +
    +
    + +
    +
    01

    Recent lines

    Last 200 · refreshes every 10 s
    +
    Sep 21 14:01:52 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)
    +Sep 21 14:01:52 postfix/qmgr[58]: 4XcB9q0Lw2z1: removed
    +Sep 21 14:00:18 postfix/smtpd[2129]: connect from mail.uni.example[192.0.2.77]
    +Sep 21 14:00:18 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)
    +Sep 21 13:58:09 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)
    +Sep 21 13:57:41 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
    +Sep 21 13:44:02 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)
    +Sep 21 13:41:19 postfix/smtp[2098]: 4XcB1c3Rk8z1: to=<j.doe@nonexistent.example>, status=bounced (Host or domain name not found)
    +Sep 21 13:40:02 journal-milter[40]: rate limit: app=prod-server 201/200 in 3600s — tempfail at MAIL FROM
    +Sep 21 13:00:00 opendkim[39]: key table reloaded (3 domains)
    +
    diff --git a/docs/assets/panel-redesign/panel/src/user-delete.html b/docs/assets/panel-redesign/panel/src/user-delete.html new file mode 100644 index 0000000..8ec8e58 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/user-delete.html @@ -0,0 +1,29 @@ + +
    +
    +

    Users / shop-team /

    +

    Delete shop-team

    +
    +
    + +
    +
    +
    +

    This cannot be undone

    +
    +

    Deleting the panel user shop-team will:

    +
      +
    • end any signed-in session of this user at once;
    • +
    • remove the assignment to shop.example.org.
    • +
    +

    The domain, its applications and their passwords are not affected.

    +
    +
    + + Keep it +
    +
    +
    +
    +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/user.html b/docs/assets/panel-redesign/panel/src/user.html new file mode 100644 index 0000000..5738428 --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/user.html @@ -0,0 +1,41 @@ + +
    +
    +

    Users /

    +

    shop-team

    +

    The same form, empty, creates a new user.

    +
    + Delete user +
    + +
    +
    +
    +
    +
    01

    Sign-in

    +
    +
    +
    +

    Leave empty to keep the current one.

    +
    +
    +

    A global administrator sees everything, including Inbound and Server. The only global administrator cannot be demoted or deleted.

    +
    +
    +
    +
    +
    +
    02

    Assigned domains

    Required for a domain administrator
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + Cancel +
    +
    diff --git a/docs/assets/panel-redesign/panel/src/users.html b/docs/assets/panel-redesign/panel/src/users.html new file mode 100644 index 0000000..b1c7dbe --- /dev/null +++ b/docs/assets/panel-redesign/panel/src/users.html @@ -0,0 +1,21 @@ + +
    +
    +

    Server

    +

    Users

    +

    Who may sign in to this panel. A domain administrator sees only the outbound domains assigned to them.

    +
    + Create user +
    + +
    +
    01

    Panel users

    3 users
    + + + + + + + +
    UsernameRoleDomains
    admin youglobalAllEdit
    shop-teamdomain adminshop.example.orgEdit
    acme-opsdomain adminnotify.acme.ioEdit
    +
    diff --git a/docs/assets/panel-redesign/panel/system-log.html b/docs/assets/panel-redesign/panel/system-log.html new file mode 100644 index 0000000..57c76f6 --- /dev/null +++ b/docs/assets/panel-redesign/panel/system-log.html @@ -0,0 +1,91 @@ + + + + + +System log · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Server

    +

    System log

    +

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

    +
    +
    + +
    +
    01

    Recent lines

    Last 200 · refreshes every 10 s
    +
    Sep 21 14:01:52 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)
    +Sep 21 14:01:52 postfix/qmgr[58]: 4XcB9q0Lw2z1: removed
    +Sep 21 14:00:18 postfix/smtpd[2129]: connect from mail.uni.example[192.0.2.77]
    +Sep 21 14:00:18 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)
    +Sep 21 13:58:09 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)
    +Sep 21 13:57:41 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
    +Sep 21 13:44:02 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)
    +Sep 21 13:41:19 postfix/smtp[2098]: 4XcB1c3Rk8z1: to=<j.doe@nonexistent.example>, status=bounced (Host or domain name not found)
    +Sep 21 13:40:02 journal-milter[40]: rate limit: app=prod-server 201/200 in 3600s — tempfail at MAIL FROM
    +Sep 21 13:00:00 opendkim[39]: key table reloaded (3 domains)
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/theme.css b/docs/assets/panel-redesign/panel/theme.css new file mode 100644 index 0000000..555f783 --- /dev/null +++ b/docs/assets/panel-redesign/panel/theme.css @@ -0,0 +1,196 @@ +/* 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.warn { color: #a66a0e; } +.sp-postmark.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; } +.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.is-danger-zone { border-color: var(--sp-fail-rule); } +.box.is-danger-zone .sp-box-head { background: var(--sp-fail-bg); border-color: var(--sp-fail-rule); } +.box.is-credential { border-color: #e0c874; } +.box.is-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.warn { background: var(--sp-warn-bg); } +.sp-health > a.warn .sp-h-icon { background: #f6e2b8; color: #8a5510; } +.sp-health > a.fail { background: var(--sp-fail-bg); } +.sp-health > a.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.is-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 .w { color: #e0b15c; } .sp-log .e { color: #ef8f82; } .sp-log .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.warn::before { background: #d9962b; } +.sp-timeline li.fail::before { background: var(--sp-fail); } +.sp-timeline li.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, .sp-signin-form .sp-signin-note { 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; width: 100%; } + .sp-postmark { display: none; } +} diff --git a/docs/assets/panel-redesign/panel/user-delete.html b/docs/assets/panel-redesign/panel/user-delete.html new file mode 100644 index 0000000..c2229e0 --- /dev/null +++ b/docs/assets/panel-redesign/panel/user-delete.html @@ -0,0 +1,98 @@ + + + + + +Delete shop-team · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Users / shop-team /

    +

    Delete shop-team

    +
    +
    + +
    +
    +
    +

    This cannot be undone

    +
    +

    Deleting the panel user shop-team will:

    +
      +
    • end any signed-in session of this user at once;
    • +
    • remove the assignment to shop.example.org.
    • +
    +

    The domain, its applications and their passwords are not affected.

    +
    +
    + + Keep it +
    +
    +
    +
    +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/user.html b/docs/assets/panel-redesign/panel/user.html new file mode 100644 index 0000000..d3a5b86 --- /dev/null +++ b/docs/assets/panel-redesign/panel/user.html @@ -0,0 +1,110 @@ + + + + + +shop-team · Users · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Users /

    +

    shop-team

    +

    The same form, empty, creates a new user.

    +
    + Delete user +
    + +
    +
    +
    +
    +
    01

    Sign-in

    +
    +
    +
    +

    Leave empty to keep the current one.

    +
    +
    +

    A global administrator sees everything, including Inbound and Server. The only global administrator cannot be demoted or deleted.

    +
    +
    +
    +
    +
    +
    02

    Assigned domains

    Required for a domain administrator
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + Cancel +
    +
    + + +
    +
    + + diff --git a/docs/assets/panel-redesign/panel/users.html b/docs/assets/panel-redesign/panel/users.html new file mode 100644 index 0000000..dc1d9e9 --- /dev/null +++ b/docs/assets/panel-redesign/panel/users.html @@ -0,0 +1,90 @@ + + + + + +Users · SelfPost + + + + + + + + + +
    +
    +
    +
    +

    Server

    +

    Users

    +

    Who may sign in to this panel. A domain administrator sees only the outbound domains assigned to them.

    +
    + Create user +
    + +
    +
    01

    Panel users

    3 users
    + + + + + + + +
    UsernameRoleDomains
    admin youglobalAllEdit
    shop-teamdomain adminshop.example.orgEdit
    acme-opsdomain adminnotify.acme.ioEdit
    +
    + + +
    +
    + +