Manage public keys, over Stalwart's x:PublicKey registry

A new Settings section, next to Identities & signatures: list, add,
rename and remove the OpenPGP public keys and S/MIME certificates
published on this account. Only public material -- no private key is
stored, requested or sent by any of this.

This is PR #67 revived. That branch was built against 0.16.19, closed
unmerged on 2026-08-26, and shares no ancestry with main after the email
scrub, so it is ported rather than rebased: the four files it added are
carried over, the three it edited are applied by hand, and everything it
claimed was re-probed against the live 0.16.20 on 2026-09-05. The i18n
work is new -- nine catalogues landed on 2026-08-31, after that branch
was written.

What the re-probe confirmed, unchanged from 0.16.19:

  - An ordinary user may read *and* write their own keys, though the
    permissions table lists every sysPublicKey* permission as
    administrative. get and query both answered for a normal account,
    and a malformed create came back invalidProperties naming `key`
    rather than forbidden -- a rejection of the key, not of the person.

  - The server parses the key and says precisely what is wrong. So
    ihasmail does not validate key material; the server's message is
    shown verbatim, as password-policy rejections already are.

  - urn:stalwart:jmap is still absent from the session's top-level
    capabilities and present per-account, so the check that reads all
    three places is still the one that works.

What it added, none of which was known before:

  - A key can parse perfectly and still be refused, with different
    words: a sign-and-certify key with no encryption subkey -- what
    `gpg --quick-generate-key` produces -- gets "Could not find any
    suitable keys in OpenPGP public key". That is the rejection somebody
    exporting from GnuPG will actually meet, and it is not a paste
    error, so collapsing both to "invalid key" would send them back to
    the clipboard for a problem that is in the key.

  - emailAddresses comes back as {} when empty -- an object where a JMAP
    list property should be an array. It type-checks, then throws in
    join() while the list renders. normalize() checked the shape
    already; there is now a test saying why, and the mock answers {} the
    same way, because one that helpfully returned [] would let that
    crash ship.

  - A create answers with the id alone, no createdAt, so adding a key
    reloads rather than believing the response.

  - destroy works and leaves the registry empty. PR #67 shipped that
    path untested -- its live probe was refused before anything was
    created, so there was nothing to destroy.

  - Patching `key` is allowed by the server. The mock still refuses it,
    now deliberately rather than for want of evidence: ihasmail replaces
    a key by adding one and removing the old, which keeps createdAt
    meaning what it says.

x:EncryptionAtRest still does not exist on 0.16.20 -- asking for it is
an unknownMethod. encryptionAtRest is a field on x:AccountSettings, and
its value is a typed object ({"@type":"Disabled"}) rather than the bare
string ROADMAP described. Nothing here writes it.

An empty description is now sent as empty rather than filled in with
"Key". The description is stored on the server, so a default invented in
the client would be whichever language the adder happened to be using;
the list labels a blank one at render time instead.

Verified in a browser against the mock, not only in tests: both
rejections reach the toast in the server's own words with the form still
filled in, a good key renders its card, the kind is labelled from the
armour header, renaming persists, removing asks first and empties the
list, and the whole section reads correctly in German.
This commit is contained in:
jcoffey-dev committed 2026-09-05 00:56:31 -07:00
1 parent 1f9c17ad18
commit e93d42d27e
19 files changed
+1087 -3

No files matched your search

+30
View File
@@ -1300,6 +1300,36 @@ export const catalog: Catalog = {
"Empty “{name}”?": "„{name}” leegmaken?",
"Delete them": "Verwijderen",
"Nothing was deleted": "Er is niets verwijderd",
// ── Encryption keys, over Stalwart's x:PublicKey registry (#67) ──
"Encryption keys": "Versleutelingssleutels",
"Not available on this server": "Niet beschikbaar op deze server",
"Public keys are kept in Stalwart's registry, which this server does not offer. ihasmail needs Stalwart 0.16 or newer for it.": "Openbare sleutels worden bewaard in het register van Stalwart, dat deze server niet aanbiedt. ihasmail heeft daarvoor Stalwart 0.16 of nieuwer nodig.",
"Public keys for this account — what other people encrypt to, and what a signature is checked against. These are public by nature: no private key is stored, requested, or sent by ihasmail.": "Openbare sleutels voor dit account — waarmee anderen naar u versleutelen, en waaraan een handtekening wordt gecontroleerd. Ze zijn van nature openbaar: ihasmail bewaart, vraagt en verstuurt geen enkele privésleutel.",
"No keys yet": "Nog geen sleutels",
"Add an OpenPGP public key or an S/MIME certificate to publish it on this account.": "Voeg een openbare OpenPGP-sleutel of een S/MIME-certificaat toe om het op dit account te publiceren.",
"Key added": "Sleutel toegevoegd",
"Key removed": "Sleutel verwijderd",
"Remove “{name}”?": "“{name}” verwijderen?",
"this key": "deze sleutel",
"Anyone holding it can still use it — removing it here only stops this account offering it.": "Wie hem al heeft, kan hem blijven gebruiken — hem hier verwijderen zorgt er alleen voor dat dit account hem niet meer aanbiedt.",
"Remove key": "Sleutel verwijderen",
"Key description": "Sleutelomschrijving",
"Untitled key": "Naamloze sleutel",
"Click to rename": "Klik om te hernoemen",
"Expired": "Verlopen",
"Added": "Toegevoegd",
"Addresses": "Adressen",
"No expiry set": "Geen vervaldatum ingesteld",
"Any address on this account": "Elk adres van dit account",
"cryptography\u0004Key": "Sleutel",
"Public key": "Openbare sleutel",
"Work key": "Werksleutel",
"Paste the whole armoured block, headers included. The server checks it and says what is wrong if it cannot read it.": "Plak het hele beveiligde blok, inclusief de kopregels. De server controleert het en zegt wat er mis is als hij het niet kan lezen.",
"Add key": "Sleutel toevoegen",
"Add a key": "Een sleutel toevoegen",
"Stalwart stores these keys, and this release does no more than manage them: ihasmail does not yet sign, encrypt, decrypt or verify anything with them. Adding one does not by itself start encrypting your mail.": "Stalwart bewaart deze sleutels, en deze versie doet niet meer dan ze beheren: ihasmail ondertekent, versleutelt, ontsleutelt en verifieert er nog niets mee. Er een toevoegen begint niet vanzelf uw e-mail te versleutelen.",
"Unrecognised": "Niet herkend",
"No account to add a key to.": "Geen account om een sleutel aan toe te voegen.",
},
plurals: {
// ── Third pass ─────────────────────────────────────────────────────