A new Encryption keys section in Settings, 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.
Two things the docs got wrong
Both established against your live 0.16.19 rather than assumed.
Ordinary users can manage their own keys. Stalwart's permissions table lists every sysPublicKey* permission as administrative. The server granted them to a normal account:
x:PublicKey/get → 200, empty list
x:PublicKey/query → 200
A create with a deliberately malformed key came back invalidProperties naming key — a rejection of the key, not of the person. Had the docs been right, this feature would have been useless to anyone but an admin, which is why it was worth probing before building.
Stalwart parses the key itself:
Failed to decode OpenPGP public key: Malformed packet:
Malformed CTB: MSB of ptag (0b01101000) not set.
So ihasmail does not validate key material. Anything it checked would be a second opinion, and the one that counts would still be the server's. Its message is shown verbatim, the way password-policy rejections already are.
What it deliberately doesn't claim
x:EncryptionAtRestdoes not exist on 0.16.19 — I probed for it. So what actually consumes a registered key is still unknown, and the page says what it knows:
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.
Claiming otherwise would be a guess dressed as a feature.
The mock
Now models the registry, including refusing a malformed key with the parser's own wording. A mock that accepted any string would let a client ship without ever handling a rejection — which is the shape of every bug this mock has since been taught to reproduce.
Verified through the UI, against the mock
check
result
bad key
refused; server's exact wording in the toast; form keeps its contents
good key
accepted, card renders
OpenPGP vs S/MIME
labelled from the armour header
rename
input appears on click, persists to the server
remove
confirms first, then empties the list
server unsupported
section shows a clear "not available" state
257 web + 75 server tests, typecheck and build clean.
Untested:destroy against a real server. The live probe was refused before anything was created, so there was nothing to destroy — worth confirming once this ships.
Next
Signing and encrypting is a separate question, not a follow-up commit. Verifying a signature needs only public keys and is tractable. Signing and decrypting need a private key in a page served by the same host that would handle it — a security model to agree before it's a feature to build.
Rebuilt from: session transcript.
A new **Encryption keys** section in Settings, 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.
## Two things the docs got wrong
Both established against your live 0.16.19 rather than assumed.
**Ordinary users can manage their own keys.** Stalwart's permissions table lists every `sysPublicKey*` permission as administrative. The server granted them to a normal account:
```
x:PublicKey/get → 200, empty list
x:PublicKey/query → 200
```
A create with a deliberately malformed key came back `invalidProperties` naming `key` — a rejection of the *key*, not of the person. Had the docs been right, this feature would have been useless to anyone but an admin, which is why it was worth probing before building.
**Stalwart parses the key itself:**
```
Failed to decode OpenPGP public key: Malformed packet:
Malformed CTB: MSB of ptag (0b01101000) not set.
```
So ihasmail does not validate key material. Anything it checked would be a second opinion, and the one that counts would still be the server's. Its message is shown verbatim, the way password-policy rejections already are.
## What it deliberately doesn't claim
`x:EncryptionAtRest` **does not exist** on 0.16.19 — I probed for it. So what actually consumes a registered key is still unknown, and the page says what it knows:
> 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.
Claiming otherwise would be a guess dressed as a feature.
## The mock
Now models the registry, **including refusing a malformed key with the parser's own wording**. A mock that accepted any string would let a client ship without ever handling a rejection — which is the shape of every bug this mock has since been taught to reproduce.
## Verified through the UI, against the mock
| check | result |
|---|---|
| bad key | refused; server's exact wording in the toast; form keeps its contents |
| good key | accepted, card renders |
| OpenPGP vs S/MIME | labelled from the armour header |
| rename | input appears on click, persists to the server |
| remove | confirms first, then empties the list |
| server unsupported | section shows a clear "not available" state |
257 web + 75 server tests, typecheck and build clean.
**Untested:** `destroy` against a real server. The live probe was refused before anything was created, so there was nothing to destroy — worth confirming once this ships.
## Next
Signing and encrypting is a separate question, not a follow-up commit. Verifying a signature needs only public keys and is tractable. Signing and decrypting need a private key in a page served by the same host that would handle it — a security model to agree before it's a feature to build.
<sub>Rebuilt from: session transcript.</sub>
Key management has no standalone value: on its own the keys do nothing, and the moment Stalwart's encryption-at-rest is switched on they make the account's mail unreadable in the webmail, because ihasmail cannot decrypt. One half is of no use without the other.
The other half is not a build decision, it is a security one. Email crypto has no forward secrecy, so a leaked private key exposes the entire archive retroactively rather than anything going forward, and a webmail decrypting in the browser is running code its own server handed it. Neither is an engineering problem to solve later.
Closing rather than parking: the branch is on the record if it is ever wanted, and main never carried it.
What was learned is worth keeping, since it cost live probing and contradicts the documentation:
ordinary users can read and write their own keys over x:PublicKey, despite the permissions table listing every sysPublicKey* permission as admin-only
Stalwart parses key material itself and returns a precise complaint, so a client should surface the server's message rather than invent one
x:EncryptionAtRest does not exist on 0.16.19
destroy was never verified against a real server
Dropping this, and the encryption work behind it.
Key management has no standalone value: on its own the keys do nothing, and the moment Stalwart's encryption-at-rest is switched on they make the account's mail unreadable in the webmail, because ihasmail cannot decrypt. One half is of no use without the other.
The other half is not a build decision, it is a security one. Email crypto has no forward secrecy, so a leaked private key exposes the entire archive retroactively rather than anything going forward, and a webmail decrypting in the browser is running code its own server handed it. Neither is an engineering problem to solve later.
Closing rather than parking: the branch is on the record if it is ever wanted, and main never carried it.
What was learned is worth keeping, since it cost live probing and contradicts the documentation:
- ordinary users *can* read and write their own keys over `x:PublicKey`, despite the permissions table listing every `sysPublicKey*` permission as admin-only
- Stalwart parses key material itself and returns a precise complaint, so a client should surface the server's message rather than invent one
- `x:EncryptionAtRest` does not exist on 0.16.19
- `destroy` was never verified against a real server
This repo is archived. You cannot comment on issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
A new Encryption keys section in Settings, 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.
Two things the docs got wrong
Both established against your live 0.16.19 rather than assumed.
Ordinary users can manage their own keys. Stalwart's permissions table lists every
sysPublicKey*permission as administrative. The server granted them to a normal account:A create with a deliberately malformed key came back
invalidPropertiesnamingkey— a rejection of the key, not of the person. Had the docs been right, this feature would have been useless to anyone but an admin, which is why it was worth probing before building.Stalwart parses the key itself:
So ihasmail does not validate key material. Anything it checked would be a second opinion, and the one that counts would still be the server's. Its message is shown verbatim, the way password-policy rejections already are.
What it deliberately doesn't claim
x:EncryptionAtRestdoes not exist on 0.16.19 — I probed for it. So what actually consumes a registered key is still unknown, and the page says what it knows:Claiming otherwise would be a guess dressed as a feature.
The mock
Now models the registry, including refusing a malformed key with the parser's own wording. A mock that accepted any string would let a client ship without ever handling a rejection — which is the shape of every bug this mock has since been taught to reproduce.
Verified through the UI, against the mock
257 web + 75 server tests, typecheck and build clean.
Untested:
destroyagainst a real server. The live probe was refused before anything was created, so there was nothing to destroy — worth confirming once this ships.Next
Signing and encrypting is a separate question, not a follow-up commit. Verifying a signature needs only public keys and is tractable. Signing and decrypting need a private key in a page served by the same host that would handle it — a security model to agree before it's a feature to build.
Rebuilt from: session transcript.
Dropping this, and the encryption work behind it.
Key management has no standalone value: on its own the keys do nothing, and the moment Stalwart's encryption-at-rest is switched on they make the account's mail unreadable in the webmail, because ihasmail cannot decrypt. One half is of no use without the other.
The other half is not a build decision, it is a security one. Email crypto has no forward secrecy, so a leaked private key exposes the entire archive retroactively rather than anything going forward, and a webmail decrypting in the browser is running code its own server handed it. Neither is an engineering problem to solve later.
Closing rather than parking: the branch is on the record if it is ever wanted, and main never carried it.
What was learned is worth keeping, since it cost live probing and contradicts the documentation:
x:PublicKey, despite the permissions table listing everysysPublicKey*permission as admin-onlyx:EncryptionAtRestdoes not exist on 0.16.19destroywas never verified against a real server