thunderbird / thunderbird/dns-scripts
Add METANET (Plesk) provider support
Nobody has claimed this yet.
- Dominant language
- HTML
- Stars
- 3
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Add METANET (Plesk) provider support
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).
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.
⚠️ Ship as UNVERIFIED first, per the godaddy/ionos/ovh/hover/digitalocean/porkbun
precedent: prefix each header stringUNVERIFIED —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 onthundermail.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.orgis the verification target. It is a METANET Plesk zone
delegated tons1.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:
sehr hoch (50), 45, 40, 35, 30, 25, 20, 15, 10, 5, niedrig (0)
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> …:
FAIL _jmap._tcp expected: 0 1 443 mail.thundermail.com
got: 0 0 443 mail.thundermail.com
FAIL _caldavs._tcp expected: 0 1 443 mail.thundermail.com
got: 0 0 443 mail.thundermail.com
FAIL _carddavs._tcp expected: 0 1 443 mail.thundermail.com
got: 0 0 443 mail.thundermail.com
FAIL _imaps._tcp expected: 0 1 993 mail.thundermail.com
got: 0 0 993 thundermail.example.org <- also a wrong target
FAIL _submission._tcp expected: 0 1 587 mail.thundermail.com
got: 0 0 587 mail.thundermail.com
(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
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). - 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
Domainnamefield 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 ofbunny/cosmotown/ovh/porkbun— use{subhost}for
MX/TXT/CNAME (never@), and{srvsubhost}for the SRVDomainname(blank at the
apex, the token that exists forhover). - 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 showsimaps/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. - Priority and Weight are dropdowns, not text inputs.
Prioritätshows
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 shipsMX 10 mail.<domain>.).Relative Gewichtungis 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 exactlymail.thundermail.com.and the MX holds10 mail.thundermail.com., i.e. Plesk appends the root dot itself and does not append the
zone name. So emit plain{target}likebunny/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 enterMS=ms14335100." So emit the unquoted{value}—
same ascosmotown. Field label isTXT-Eintrag. - Two-step save. New/changed entries are marked with an exclamation mark and are not
live untilAktualisierenis 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) |
| CNAME | 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, Protokoll and Zielport are the SRV form's required (*) fields.
Work items
- Resolve the SRV weight blocker (Option A recommended — a
match_modethat ignores
SRV weight, inrecords.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 aparity_cases.jsonfixture. - Add the
metanet-pleskprovider block torecords.json(headersUNVERIFIED —
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-pleskrenders all 13
records in both--fix-format tableandlongwithout error. - Web app "metanet-plesk" provider mirrors the CLI output (table + detailed).
-
uv run python -m unittest discover -s tests -t . -vgreen (linter + parity). - Update
README.mdprovider list +RELEASE_NOTES.mdandRELEASE_NOTES.html. - Update
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). - Verify against
thundermail.example.org(--resolver ns1.hera.metanet.ch): confirm
the MX and CNAME field labels on the live panel, fix the_imapsZielhost, then drop
theUNVERIFIED —prefixes.
Open questions
- Provider key / label.
metanet-plesk(as in the ticket) vs plainplesk— 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-plesknow; generalise topleskonly 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-ServerandKanonischer 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
droppingUNVERIFIED. - Subdomain zone.
thundermail.example.orgis 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.chwith the records in copyable form and they enter them. Worth a line in
the headers (or an SRV-specific fallback, ascosmotownroutes SRV to support) if the
weight blocker isn't resolvable.
Reference docs
- Plesk: DNS-Verwaltung (German, official) — https://www.metanet.ch/de/support/dns-nameserver/dns/plesk-dns-verwaltung
- Local PDFs:
THUNDERBIRD_2023/TBPRO/7194_METANET_PLESK/Plesk_ DNS-Verwaltung.pdf(German)
andtranslated-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
🤖 Generated with Claude Code
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading records.json, the SRV token handling in verify_thundermail_dns.py and app.js, and tests/fixtures/parity_cases.json; run the unittest suite to understand the current linter and parity checks. Resolve the SRV-weight decision before finalizing the provider mapping. Done means the CLI and web app render all 13 records, parity and linter tests pass, documentation files are updated, and verification against the stated resolver is complete.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, python
- Domain
- cli, testing, tooling, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100