You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add metanet-plesk as a remediation provider in records.json so both the CLI
(--provider metanet-plesk) and the web app emit METANET-Plesk-specific fix instructions
for the 13-record Thundermail set (1 MX, 5 SRV, 4 TXT, 3 DKIM CNAME).
METANET (metanet.ch, Swiss host) exposes DNS through the Plesk Administration Tool
("DNS-Einstellungen" → Ressourceneintrag zur Zone hinzufügen), in a German-language UI.
This is the first Plesk-based panel we support, and it is the most field-granular one so
far — the SRV form has seven inputs.
⚠️Ship as UNVERIFIED first, per the godaddy/ionos/ovh/hover/digitalocean/porkbun
precedent: prefix each header string UNVERIFIED — until confirmed end-to-end, then drop
the prefix. The SRV field layout below is confirmed from a screenshot of the live Ressourceneintrag bearbeiten form on thundermail.example.org; the TXT form is
confirmed from METANET's own docs; MX and CNAME field labels are not yet confirmed
(standard-Plesk guesses — see Open questions).
🎯 thundermail.example.org is the verification target. It is a METANET Plesk zone
delegated to ns1.hera.metanet.ch / ns2.hera.metanet.ch, so query it authoritatively: uv run verify_thundermail_dns.py thundermail.example.org --resolver ns1.hera.metanet.ch
Live web preview: https://thunderbird.github.io/dns-scripts/?domain=thundermail.example.org Current state: 8 passed, 5 failed — MX, all 4 TXT and all 3 DKIM CNAMEs are correct;
all 5 SRV records fail. See the blocker below.
🚨 Blocker: SRV weight 1 cannot be entered in Plesk
Plesk renders the SRV weight ("Relative Gewichtung für Einträge mit gleicher
Priorität") as a dropdown with fixed steps of 5, not a free-text field. The options are:
Our record set specifies weight: 1 for all five SRV records, and value_templates.SRV
uses match_mode: "exact" — so a METANET/Plesk user literally cannot make the checker
pass. This is confirmed live: the operator picked niedrig (0), and all five SRV records
now read 0 0 <port> mail.thundermail.com against an expected 0 1 <port> …:
(The _imaps target is separately wrong — the Zielhost field ended up holding the zone apex
instead of mail.thundermail.com; the operator can fix that one. The weight mismatch on all
five is the structural problem.)
This needs a decision before the provider block is finalized, because whatever we emit
for the Weight field is either unselectable or knowingly non-matching:
Option A (recommended): stop comparing SRV weight. With a single target in the RRset
the weight has no operational effect — relative weighting only matters between multiple
targets at the same priority. Add a match mode (e.g. exact_ignore_weight) to value_templates.SRV, implemented in both interpreters + a parity fixture + linter case.
This unblocks METANET/Plesk and any other panel with a constrained weight picker, and
costs nothing in correctness.
Option B: change the canonical weight to 0 in records.json (Thundermail-side call —
needs sign-off from whoever owns the published Thundermail setup instructions, and would
flag every domain that currently has weight 1).
Option C: emit niedrig (0) and accept a permanent FAIL for METANET users. Rejected —
it breaks the "exit 0 ⟺ everything correct" contract.
Option A is probably its own small issue that this one depends on; note the interaction
before writing the metanet-plesk block.
Add-record model
Plesk uses one add-record form with an Eintragstyp (entry type) dropdown — like bunny/godaddy/hover/digitalocean/porkbun, not per-type sections like cosmotown. Offered types: NS, A, AAAA, CNAME, MX, PTR, TXT, SRV, DS, CAA.
Path: Plesk → Websites & Domains → your domain → DNS-Einstellungen → Eintrag hinzufügen
→ pick Eintragstyp, fill the fields, Übernehmen (Apply) — then click Aktualisieren
(Update) on the yellow banner to commit.
Key conventions:
Apex host = BLANK. The Domainname field is left empty; the panel prints the zone
suffix (.thundermail.example.org.) immediately to the right of the input. So this is the
blank-apex pattern of bunny/cosmotown/ovh/porkbun — use {subhost} for
MX/TXT/CNAME (never @), and {srvsubhost} for the SRV Domainname (blank at the
apex, the token that exists for hover).
SRV splits Service and Protocol into separate fields — WITHOUT the leading underscore.
Both fields carry the hint "Beispiel: SIP (ohne Unterstrich)" ("Example: SIP, without
underscore"), and the live screenshot shows imaps / tcp typed bare. Our existing {service} / {protocol} tokens yield _imaps / _tcpwith the underscore
(godaddy/ionos/hover want them that way), so METANET needs two new interpreter tokens
— see below. This is the first provider that strips the underscore.
Priority and Weight are dropdowns, not text inputs.Priorität shows sehr hoch (0) for 0 (0 is selectable, so SRV priority is fine; MX priority 10 is in the
list too — METANET's own default template ships MX 10 mail.<domain>.). Relative Gewichtung is the constrained one — see the blocker above.
Weight, Port and Target are three separate fields (Relative Gewichtung, Zielport, Zielhost) — so SRV uses the individual {weight} / {port} / {target} tokens like namecheap/hover, not the packed {value}.
Targets are stored verbatim — NO trailing dot needed. Confirmed live: four of the five
SRV records hold exactly mail.thundermail.com. and the MX holds 10 mail.thundermail.com., i.e. Plesk appends the root dot itself and does not append the
zone name. So emit plain {target} like bunny/porkbun, not{target}. like ovh/digitalocean.
TXT values are auto-quoted by the panel. METANET's docs are explicit: "The entry type
TXT automatically sets the including quotation marks (example: "MS=ms14335100"). In this
example, you would therefore only enter MS=ms14335100." So emit the unquoted {value} —
same as cosmotown. Field label is TXT-Eintrag.
Two-step save. New/changed entries are marked with an exclamation mark and are not
live until Aktualisieren is clicked on the pending-changes banner; several edits can be
batched. Worth calling out in the headers so users don't add all 13 records and conclude
the tool is broken. Propagation can then take several hours.
New interpreter tokens
Add to the resolve_record / resolveRecord SRV-label split, in both Python and JS
(the sync rule), plus a tests/fixtures/parity_cases.json case:
Token
Value for _imaps._tcp
Why
{bareservice}
imaps
{service} minus the leading _ — Plesk's Service-Name
{bareprotocol}
tcp
{protocol} minus the leading _ — Plesk's Protokoll
(Name bikeshed welcome — {bareservice}/{bareprotocol} reads consistently with the
existing {service}/{protocol}/{srvhost}/{srvsubhost} family.) Computed for all
records like the rest of the split tokens, referenced only by the metanet-plesk SRV
template. Remember: the records.json linter asserts every template interpolates with no
unknown/leftover token, so the tokens must land before the provider block.
Field mapping
Type
Fields (Plesk label → value)
MX
Eintragstyp=MX, Domainname={subhost} (blank at apex), Mail-Exchange-Server={target}, Priorität={priority} (dropdown)
SRV
Eintragstyp=SRV, Service-Name={bareservice} (imaps, no underscore), Protokoll={bareprotocol} (tcp, no underscore), Domainname={srvsubhost} (blank at apex), Priorität={priority} (dropdown, sehr hoch (0)), Relative Gewichtung=see blocker, Zielhost={target}, Zielport={port}
TXT
Eintragstyp=TXT, Domainname={subhost} (blank at apex), TXT-Eintrag={value} (no quotes — the panel adds them)
SPF / MTA-STS / TLSRPT / DMARC are all plain TXT; DKIM here is a CNAME. Service-Name, Protokoll and Zielport are the SRV form's required (*) fields.
Work items
Resolve the SRV weight blocker (Option A recommended — a match_mode that ignores
SRV weight, in records.json + both interpreters + tests). Possibly a separate issue
this one depends on.
Add {bareservice} / {bareprotocol} to the SRV-label split in verify_thundermail_dns.pyandapp.js, plus a parity_cases.json fixture.
Add the metanet-plesk provider block to records.json (headers UNVERIFIED —
prefixed): {subhost} apex, {srvsubhost} SRV apex, bare service/protocol, separate
Weight/Port/Target, plain {target} (no trailing dot), unquoted TXT.
Decide the label language — German labels as shown in the panel, or German with an
English gloss (see Open questions).
Mention the Aktualisieren (Update) commit step in the headers.
uv run verify_thundermail_dns.py example.com --provider metanet-plesk renders all 13
records in both --fix-format table and long without error.
Web app "metanet-plesk" provider mirrors the CLI output (table + detailed).
uv run python -m unittest discover -s tests -t . -v green (linter + parity).
Update README.md provider list + RELEASE_NOTES.mdandRELEASE_NOTES.html.
Update CLAUDE.md crucial-decisions with METANET/Plesk's quirks (blank apex,
underscore-less Service/Protocol, dropdown Priority/Weight, verbatim targets,
auto-quoted TXT, two-step save, German UI).
Verify against thundermail.example.org (--resolver ns1.hera.metanet.ch): confirm
the MX and CNAME field labels on the live panel, fix the _imaps Zielhost, then drop
the UNVERIFIED — prefixes.
Open questions
Provider key / label.metanet-plesk (as in the ticket) vs plain plesk — the form
layout is stock Plesk and would serve any Plesk host, but the verbatim-target and
auto-quoting behaviours were only confirmed on METANET's build. Recommendation: ship metanet-plesk now; generalise to plesk only if another Plesk host shows up.
Label language. Every existing provider uses the panel's own English labels. METANET's
Plesk is German (Plesk can switch UI language). Emit the German labels verbatim
(Zielhost), or German + gloss (Zielhost (Target host))? Recommendation: German
verbatim, since that's what the user sees, with the English hint in the header text.
MX / CNAME field labels are unconfirmed.Mail-Exchange-Server and Kanonischer Name
above are standard-Plesk German labels, not read off a live METANET form — the docs only
screenshot the NS, TXT and SRV forms. Grab MX and CNAME add-record screenshots before
dropping UNVERIFIED.
Subdomain zone.thundermail.example.org is a delegated subdomain zone (its own
Plesk DNS zone, apex = thundermail.example.org). Nothing special for us — the tool just
takes that as the domain — but note it when reading the screenshot's .thundermail.example.org.
suffix.
Escape hatch. METANET sells a Premium Service: Individual DNS settings — email support@metanet.ch with the records in copyable form and they enter them. Worth a line in
the headers (or an SRV-specific fallback, as cosmotown routes SRV to support) if the
weight blocker isn't resolvable.
Local PDFs: THUNDERBIRD_2023/TBPRO/7194_METANET_PLESK/Plesk_ DNS-Verwaltung.pdf (German)
and translated-to-english-by-firefoxl-pesk_ DNS-Verwaltung.pdf (Firefox translation).
Live SRV form screenshot (with the Weight dropdown open, showing the 0/5/10…50 steps): THUNDERBIRD_2023/TBPRO/7194_METANET_PLESK/SRV Einträge 2.jpg
Add METANET (Plesk) provider support
Add
metanet-pleskas a remediation provider inrecords.jsonso both the CLI(
--provider metanet-plesk) and the web app emit METANET-Plesk-specific fix instructionsfor the 13-record Thundermail set (1 MX, 5 SRV, 4 TXT, 3 DKIM CNAME).
Internal ticket: TBPRO 7194
(
7194_METANET_PLESK).METANET (metanet.ch, Swiss host) exposes DNS through the Plesk Administration Tool
("DNS-Einstellungen" → Ressourceneintrag zur Zone hinzufügen), in a German-language UI.
This is the first Plesk-based panel we support, and it is the most field-granular one so
far — the SRV form has seven inputs.
🚨 Blocker: SRV weight
1cannot be entered in PleskPlesk renders the SRV weight ("Relative Gewichtung für Einträge mit gleicher
Priorität") as a dropdown with fixed steps of 5, not a free-text field. The options are:
Our record set specifies
weight: 1for all five SRV records, andvalue_templates.SRVuses
match_mode: "exact"— so a METANET/Plesk user literally cannot make the checkerpass. This is confirmed live: the operator picked
niedrig (0), and all five SRV recordsnow read
0 0 <port> mail.thundermail.comagainst an expected0 1 <port> …:(The
_imapstarget is separately wrong — the Zielhost field ended up holding the zone apexinstead of
mail.thundermail.com; the operator can fix that one. The weight mismatch on allfive is the structural problem.)
This needs a decision before the provider block is finalized, because whatever we emit
for the Weight field is either unselectable or knowingly non-matching:
the weight has no operational effect — relative weighting only matters between multiple
targets at the same priority. Add a match mode (e.g.
exact_ignore_weight) tovalue_templates.SRV, implemented in both interpreters + a parity fixture + linter case.This unblocks METANET/Plesk and any other panel with a constrained weight picker, and
costs nothing in correctness.
0inrecords.json(Thundermail-side call —needs sign-off from whoever owns the published Thundermail setup instructions, and would
flag every domain that currently has weight 1).
niedrig (0)and accept a permanent FAIL for METANET users. Rejected —it breaks the "exit 0 ⟺ everything correct" contract.
Option A is probably its own small issue that this one depends on; note the interaction
before writing the
metanet-pleskblock.Add-record model
Plesk uses one add-record form with an
Eintragstyp(entry type) dropdown — likebunny/godaddy/hover/digitalocean/porkbun, not per-type sections likecosmotown. Offered types:NS, A, AAAA, CNAME, MX, PTR, TXT, SRV, DS, CAA.Path: Plesk → Websites & Domains → your domain → DNS-Einstellungen →
Eintrag hinzufügen→ pick
Eintragstyp, fill the fields,Übernehmen(Apply) — then clickAktualisieren(Update) on the yellow banner to commit.
Key conventions:
Domainnamefield is left empty; the panel prints the zonesuffix (
.thundermail.example.org.) immediately to the right of the input. So this is theblank-apex pattern of
bunny/cosmotown/ovh/porkbun— use{subhost}forMX/TXT/CNAME (never
@), and{srvsubhost}for the SRVDomainname(blank at theapex, the token that exists for
hover).Both fields carry the hint "Beispiel: SIP (ohne Unterstrich)" ("Example: SIP, without
underscore"), and the live screenshot shows
imaps/tcptyped bare. Our existing{service}/{protocol}tokens yield_imaps/_tcpwith the underscore(godaddy/ionos/hover want them that way), so METANET needs two new interpreter tokens
— see below. This is the first provider that strips the underscore.
Prioritätshowssehr hoch (0)for 0 (0 is selectable, so SRV priority is fine; MX priority 10 is in thelist too — METANET's own default template ships
MX 10 mail.<domain>.).Relative Gewichtungis the constrained one — see the blocker above.Relative Gewichtung,Zielport,Zielhost) — so SRV uses the individual{weight}/{port}/{target}tokens likenamecheap/hover, not the packed{value}.SRV records hold exactly
mail.thundermail.com.and the MX holds10 mail.thundermail.com., i.e. Plesk appends the root dot itself and does not append thezone name. So emit plain
{target}likebunny/porkbun, not{target}.likeovh/digitalocean.TXT automatically sets the including quotation marks (example:
"MS=ms14335100"). In thisexample, you would therefore only enter
MS=ms14335100." So emit the unquoted{value}—same as
cosmotown. Field label isTXT-Eintrag.live until
Aktualisierenis clicked on the pending-changes banner; several edits can bebatched. Worth calling out in the headers so users don't add all 13 records and conclude
the tool is broken. Propagation can then take several hours.
New interpreter tokens
Add to the
resolve_record/resolveRecordSRV-label split, in both Python and JS(the sync rule), plus a
tests/fixtures/parity_cases.jsoncase:_imaps._tcp{bareservice}imaps{service}minus the leading_— Plesk'sService-Name{bareprotocol}tcp{protocol}minus the leading_— Plesk'sProtokoll(Name bikeshed welcome —
{bareservice}/{bareprotocol}reads consistently with theexisting
{service}/{protocol}/{srvhost}/{srvsubhost}family.) Computed for allrecords like the rest of the split tokens, referenced only by the
metanet-pleskSRVtemplate. Remember: the
records.jsonlinter asserts every template interpolates with nounknown/leftover token, so the tokens must land before the provider block.
Field mapping
Eintragstyp=MX,Domainname={subhost}(blank at apex),Mail-Exchange-Server={target},Priorität={priority}(dropdown)Eintragstyp=SRV,Service-Name={bareservice}(imaps, no underscore),Protokoll={bareprotocol}(tcp, no underscore),Domainname={srvsubhost}(blank at apex),Priorität={priority}(dropdown,sehr hoch (0)),Relative Gewichtung=see blocker,Zielhost={target},Zielport={port}Eintragstyp=TXT,Domainname={subhost}(blank at apex),TXT-Eintrag={value}(no quotes — the panel adds them)Eintragstyp=CNAME,Domainname={subhost}(DKIM label),Kanonischer Name={target}SPF / MTA-STS / TLSRPT / DMARC are all plain TXT; DKIM here is a CNAME.
Service-Name,ProtokollandZielportare the SRV form's required (*) fields.Work items
match_modethat ignoresSRV weight, in
records.json+ both interpreters + tests). Possibly a separate issuethis one depends on.
{bareservice}/{bareprotocol}to the SRV-label split inverify_thundermail_dns.pyandapp.js, plus aparity_cases.jsonfixture.metanet-pleskprovider block torecords.json(headersUNVERIFIED —prefixed):
{subhost}apex,{srvsubhost}SRV apex, bare service/protocol, separateWeight/Port/Target, plain
{target}(no trailing dot), unquoted TXT.English gloss (see Open questions).
Aktualisieren(Update) commit step in the headers.uv run verify_thundermail_dns.py example.com --provider metanet-pleskrenders all 13records in both
--fix-format tableandlongwithout error.uv run python -m unittest discover -s tests -t . -vgreen (linter + parity).README.mdprovider list +RELEASE_NOTES.mdandRELEASE_NOTES.html.CLAUDE.mdcrucial-decisions with METANET/Plesk's quirks (blank apex,underscore-less Service/Protocol, dropdown Priority/Weight, verbatim targets,
auto-quoted TXT, two-step save, German UI).
thundermail.example.org(--resolver ns1.hera.metanet.ch): confirmthe MX and CNAME field labels on the live panel, fix the
_imapsZielhost, then dropthe
UNVERIFIED —prefixes.Open questions
metanet-plesk(as in the ticket) vs plainplesk— the formlayout is stock Plesk and would serve any Plesk host, but the verbatim-target and
auto-quoting behaviours were only confirmed on METANET's build. Recommendation: ship
metanet-plesknow; generalise topleskonly if another Plesk host shows up.Plesk is German (Plesk can switch UI language). Emit the German labels verbatim
(
Zielhost), or German + gloss (Zielhost (Target host))? Recommendation: Germanverbatim, since that's what the user sees, with the English hint in the header text.
Mail-Exchange-ServerandKanonischer Nameabove are standard-Plesk German labels, not read off a live METANET form — the docs only
screenshot the NS, TXT and SRV forms. Grab MX and CNAME add-record screenshots before
dropping
UNVERIFIED.thundermail.example.orgis a delegated subdomain zone (its ownPlesk DNS zone, apex =
thundermail.example.org). Nothing special for us — the tool justtakes that as the domain — but note it when reading the screenshot's
.thundermail.example.org.suffix.
support@metanet.chwith the records in copyable form and they enter them. Worth a line inthe headers (or an SRV-specific fallback, as
cosmotownroutes SRV to support) if theweight blocker isn't resolvable.
Reference docs
THUNDERBIRD_2023/TBPRO/7194_METANET_PLESK/Plesk_ DNS-Verwaltung.pdf(German)and
translated-to-english-by-firefoxl-pesk_ DNS-Verwaltung.pdf(Firefox translation).THUNDERBIRD_2023/TBPRO/7194_METANET_PLESK/SRV Einträge 2.jpg🤖 Generated with Claude Code