Add Domains to Administration #335

Closed
opened 2026-09-13 22:39:09 +00:00 by jcoffey-dev · 0 comments
Owner

Summary

Stacked on #334 (Accounts and the ADMINISTRATION switch). Merge that first; GitHub retargets this PR to main when that branch is deleted.

A role that can read domains (sysDomainQuery + sysDomainGet) gets a Domains section beside Accounts:

  • List and search. Each domain shows its account count, other names, and whether its DNS records, DKIM keys and certificate are managed automatically or by hand. A tenant column appears when the role can read tenants.
  • Add a domain. The name is lower-cased and a trailing dot removed. Stalwart gives new domains automatic DKIM, so the records are ready to copy immediately.
  • Edit the description, other names (aliases), catch-all address and plus addressing. A plus-addressing rule set on the server is shown and left alone.
  • DNS records from Stalwart's per-domain dnsZoneFile, one row per record with its own copy button, plus "Copy all as a zone file". A long DKIM TXT, which the BIND serialiser splits into quoted chunks, is joined back into the single value a DNS provider's form expects. A line that can't be parsed is still shown as its own row.
  • DKIM keys with their stage (signing, published, retiring, retired), read-only. With automatic DKIM the server creates and rotates keys; adding one by hand requires its private key.
  • Remove, after typing the name to confirm. The domain's DKIM keys are deleted first, in the same request, because Stalwart won't remove a domain its keys still reference. Removal isn't offered while accounts use the domain, or when the role can't delete the keys. If the server still refuses, the error lists what it says is linked ("2 accounts, 1 mailing list").

The Administration nav is now built from whichever sections the role can read, and the menu appears if there is at least one.

Not in this PR: switching DNS/DKIM/certificate management between automatic and manual, and choosing a DNS or ACME provider. The panel shows their state; ROADMAP.md lists changing them as a next step.

Not yet verified against a live Stalwart

Built from the 0.16.22 source, dns-update's serialiser and the mock. Listed in KNOWN-ISSUES.md:

  • the zone file format (read from dns-update's main branch; Stalwart pins 0.5);
  • whether catchAllAddress takes a full address or a local part;
  • a domainId filter on x:DkimSignature/query;
  • the shape of linkedObjects;
  • removing keys and then the domain in a single request.

Testing

  • Driven in Chrome against npm run dev:mock:
    • domain list with account counts;
    • opening example.com: split RSA DKIM record joined, DNS provider name shown, DKIM keys listed, removal blocked by 24 accounts;
    • adding Shop.Example.com. (saved as shop.example.com, records shown straight away);
    • adding an alias and a catch-all address, confirmed with x:Domain/get;
    • the remove dialog naming the DKIM key that goes too, with its button disabled until the name is typed. Opened and cancelled; the final remove was not pressed.
  • Unit tests:
    • zone file parsing (split and escaped TXT, unparseable lines kept);
    • domain name normalisation and validation;
    • linked-object messages and DKIM algorithm names;
    • section gating;
    • the mock's domain refusals, removal once the keys are gone, and zone files;
    • a component test for the panel: records, both removal blocks, and a server-set plus-addressing rule.
  • Checks:
    • npm run typecheck: clean;
    • npm test -w server: 186/186;
    • npm test -w web: 1206/1206;
    • npm run i18n:check: clean;
    • npm run build: succeeds.

Translations

61 new keys (55 strings, 6 plurals) in all nine catalogues. Russian and Ukrainian carry one/few/many/other; Japanese and Chinese carry other only. Strings falling back to English: 16 before, 16 after in every language.

Merged 2026-09-13 as coffey-labs/ihasmail@b79db9098a

Rebuilt from: git history, session transcript.

## Summary Stacked on #334 (Accounts and the `ADMINISTRATION` switch). Merge that first; GitHub retargets this PR to `main` when that branch is deleted. A role that can read domains (`sysDomainQuery` + `sysDomainGet`) gets a **Domains** section beside Accounts: - **List and search.** Each domain shows its account count, other names, and whether its DNS records, DKIM keys and certificate are managed automatically or by hand. A tenant column appears when the role can read tenants. - **Add** a domain. The name is lower-cased and a trailing dot removed. Stalwart gives new domains automatic DKIM, so the records are ready to copy immediately. - **Edit** the description, other names (aliases), catch-all address and plus addressing. A plus-addressing rule set on the server is shown and left alone. - **DNS records** from Stalwart's per-domain `dnsZoneFile`, one row per record with its own copy button, plus "Copy all as a zone file". A long DKIM TXT, which the BIND serialiser splits into quoted chunks, is joined back into the single value a DNS provider's form expects. A line that can't be parsed is still shown as its own row. - **DKIM keys** with their stage (signing, published, retiring, retired), read-only. With automatic DKIM the server creates and rotates keys; adding one by hand requires its private key. - **Remove**, after typing the name to confirm. The domain's DKIM keys are deleted first, in the same request, because Stalwart won't remove a domain its keys still reference. Removal isn't offered while accounts use the domain, or when the role can't delete the keys. If the server still refuses, the error lists what it says is linked ("2 accounts, 1 mailing list"). The Administration nav is now built from whichever sections the role can read, and the menu appears if there is at least one. Not in this PR: switching DNS/DKIM/certificate management between automatic and manual, and choosing a DNS or ACME provider. The panel shows their state; ROADMAP.md lists changing them as a next step. ## Not yet verified against a live Stalwart Built from the 0.16.22 source, `dns-update`'s serialiser and the mock. Listed in KNOWN-ISSUES.md: - the zone file format (read from `dns-update`'s main branch; Stalwart pins 0.5); - whether `catchAllAddress` takes a full address or a local part; - a `domainId` filter on `x:DkimSignature/query`; - the shape of `linkedObjects`; - removing keys and then the domain in a single request. ## Testing - **Driven in Chrome** against `npm run dev:mock`: - domain list with account counts; - opening example.com: split RSA DKIM record joined, DNS provider name shown, DKIM keys listed, removal blocked by 24 accounts; - adding `Shop.Example.com.` (saved as `shop.example.com`, records shown straight away); - adding an alias and a catch-all address, confirmed with `x:Domain/get`; - the remove dialog naming the DKIM key that goes too, with its button disabled until the name is typed. Opened and cancelled; the final remove was not pressed. - **Unit tests:** - zone file parsing (split and escaped TXT, unparseable lines kept); - domain name normalisation and validation; - linked-object messages and DKIM algorithm names; - section gating; - the mock's domain refusals, removal once the keys are gone, and zone files; - a component test for the panel: records, both removal blocks, and a server-set plus-addressing rule. - **Checks:** - `npm run typecheck`: clean; - `npm test -w server`: 186/186; - `npm test -w web`: 1206/1206; - `npm run i18n:check`: clean; - `npm run build`: succeeds. ## Translations **61 new keys** (55 strings, 6 plurals) in all nine catalogues. Russian and Ukrainian carry one/few/many/other; Japanese and Chinese carry `other` only. Strings falling back to English: **16 before, 16 after** in every language. **Merged** 2026-09-13 as coffey-labs/ihasmail@b79db9098afe <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.