GSoC 2017 - Post title goes here

Signature hash tracking#

It would be relatively easy to replay a signed message to a mailing list by a user as no kind of challenge-response is done on posting.

While signature replay checking is usually done on the end users point with against his keyring and messages he has so far received and their context, I think it is kind of expected of PGP enabled Mailman to also do this as it relays the messages.

On a successful posting to a PGP enabled mailing list (AcceptedEvent to be precise), the message is searched for PGP signatures and their digests and key fingerprints are added into the sighash table.

The signature rule then checks the digests from a posting against those in the sighash table. If it finds it has previously accepted a message with any of those hashes it takes the duplicate_sig_action which is per-list configurable.

This of course means that if the duplicate_sig_action is not set to Action.defer, which means the message gets rejected or dropped, that a signature that was sent to a list cannot be sent again. Of course if a user wants to send a signed message again, he can just resign it and send it again, the hashes won’t match. However sometimes it might be useful to post the message as signed originally, for example to prove something. However, I think it is worth it to keep this as a configurable option. Maybe with another option disabling the collection of signature hashes, for performance reasons.

Outgoing processing#

Since a the plugin needs to process the messages for outgoing encryption on a per-recipient or per-chunk basis, it couldn’t be implemented as a Handler, I thought about implementing it as a custom OutgoingRunner but that didn’t work out either. However the IMailTransportAgentDelivery interface is great for what the plugin needs to do. So there are two custom PGP enabled delivery classes that get selected by a custom [mta] outgoing callable, one for bulk delivery and other for individual delivery.


I got the pgpmailman-proposal repository almost completely up to date on changes to the plugin and my current proposed Core/Client/Postorius/HyperKitty changes and MRs.

Next up#

More testing#

As I remembered to ignore coverage of tests it dropped to 88% and I think it is currently in order to add more comprehensive tests for features implemented that check the edge cases which currently remain.

Setup live development instance#

I believe I got pretty far into the development without having a proper live development instance of Mailman Core + plugins and Postorius + HyperKitty + Client, so I’m going to set that up now and test manually. With time I might set this up as a public site/mailing list server, to demonstrate the features of the PGP plugin.


Currently thinking about implementing two encrypted archivers, one local, one remote. The local one would be similar to the prototype archiver but store messages encrypted, TBD how. The remote one will send the messages encrypted to a receiving archiver, the django-pgpmailman instance running next to HyperKitty.


With most of the essential stuff in the core plugin done, the web ui part of development can begin.

GSoC 2017 - The point of no return

This week is around the halfway of GSoC 2017 project timeline, hence the title.

Key management#

As I set out to do, I have now implemented most of the after subscription key management. The key change command. The pre subscription key management is done via a custom dynamically loaded subscription policy/workflow. This is possible with my pluggable-workflows branch/MR which introduces dynamically loaded workflows, which subscription and unsubscription policies are a part of.


On subscription the PGP enabled mailing list needs a users public key. An example conversation establishing that follows (shows a list with moderation + pgp enabled subscription policy):

Mailman                                                  User
-------                                                  ----                               
                    <- subscribe                         1.

2.                  key set "token1" ->
                    + instructions to reply and attach user key
                    (signed by list)

                    <- key set "token1"                  3.
                    (key attached as per RFC3156(PGP/MIME)
                    or inline and we do our best to find it.)

4                   key confirm "token2" ->
                    + statement containing "token2" and
                    user key fingerprint in body, with instructions
                    to sign it
                    (signed by list, encrypted to user key)

                    <- key confirm "token2"              5.
                    + must contain signed statement from step 4.
                    (required to be signed by user key)

6.                  ... the rest of the subscription steps (moderation)...

Steps 3 and 5 can be optionally encrypted to the list key.

This workflow gives “some” assurance that the user really has the key she is subscribing with, however the trust in the address/user/key has to come from the list moderator and both my proposal and implementation rely on that. This workflow only proves that Mailman is communicating with someone that has the user key (can decrypt "token2" and sign the statement), not that that user is

Key change#

Works similar to the key set command on subscription. Works via a custom workflow, but not a subscription workflow/policy, just a workflow.

Mailman                                                  User
-------                                                  ----                               
                    <- key change                        1.
                    (new! key attached as per RFC3156(PGP/MIME)
                    or inline and we do our best to find it.)

2.                  key confirm "token1" ->
                    + statement containing "token1" and
                    new! user key fingerprint in body, with instructions
                    to sign it
                    (signed by list, encrypted to new! user key)

                    <- key confirm "token1"              3.
                    + must contain signed statement from step 2.
                    (required to be signed by old! user key)

Steps 1 and 3 can again be optionally encrypted.

This ensures that only someone with possesion of the old user key can change it. It requires the user to decrypt with the new key and sign a statement with the new key fingerprint as well as the token in it with the old key.

PyPi package#

mailman-pgp @ PyPi I have now created a PyPi package for the mailman-pgp plugin as it’s maturing, and while still unusable I wanted the name.

Test coverage#

I got test coverage of mailman-pgp to 95% which now actually means something as large parts of the codebase are implemented and not just stubs. Since this plugin requires features not merged into Mailman Core (or PGPy) for pipelines to work I maintain forks of both with branches that merge all required branches into a plugin branch for Mailman Core and dev branch for PGPy.

Next up#


I had a look at working in outgoing encryption of messages for pgp enabled mailing lists. Encryption cannot be done as a handler, as it requires to process the messages per-recipient or per-batch of recipients. The only configurable/dynamic option currently in Mailman Core is the [mta].outgoing callable. Which is not ideal since it itself has more functionality than simply dispatching to a correct Delivery instance based on the list configuration, that would have to be duplicated in mailman-pgp. The IMailTransportAgentDelivery interface looks promising though, so I’m thinking about refactoring the outgoing callable a bit to allow setting a custom one without the duplicities.

Signature hash tracking#

To prevent replay attacks for things where always sending and requiring a signed confirmation token in response is not practically doable, the pgp plugin will keep signature hashes with timestamps and posssibly key fingerprints in a DB/some other quickly searchable structure, and will check signatures it receives against the hashes in that structure. It will then act accordingly, for example not allowing a user to post the same message (with the same signature) again, since it could have been replayed.


I wanted to get the pgpmailman-proposal repository updated with current state/MRs but didn’t get to it this week, so might find time for it over the weekend as well as for some code documentation.

GSoC 2017 - Another quick update

Just a quick update about the state of the project before I disappear from civilization for this weekend.

Pluggable workflows#

MR @ gitlab

I have now finally got the pluggable workflows branch to test succesfully! With workflows being loaded dynamically, with workflow steps being saved completely, full backward compatibility, also database migrations and REST backward compatibility.

That branch is still missing more tests for diffcov to pass.

^^python 3.4 CI envs are currently broken, since aiosmtpd 1.1 is python 3.5 only, so don’t mind that.

Key management#

branch @ gitlab

Building up on the pluggable workflows I have a base implementation of how the subscribers key gets to the mailing list and its moderator and also of how it’s confirmed.


MR @ gitlab

During this week I also got to add SMTP over SSL/TLS support to aiosmtpd, a lib which Mailman uses for it’s SMTP tests, so with that the SMTPS+STARTTLS MR has tests which pass and I think is ready for merging. Thanks Barry for the quick responses and help debugging over at aiosmtpd!

Next up#

For next week I plan on cleaning up the pluggable workflows branch, finishing the pre-subscription key management, implementing after-subscription key management as well as getting back on track with documenting the mailman-pgp architecture and changes at the pgpmailman-proposal repo, which has been lacking.

GSoC 2017 - Pluggable Workflows

As I note in one of my previous GSoC project updates, the PGP-enabled mailing lists require two new features from Mailman Core, both related to workflows. The first is to be able to inject steps into a subscription workflow to request the users pubkey before moderation checks. The second it to be able to require confirmation on all commands (to prevent replay attacks), although this could be avoided by storing the hashes from all the user sent signed commands. In this post I propose a way of refactoring the workflows into pluggable components.

Current workflows#

Both of these requirements require changes to the way [Un]SubscriptionWorkflows and workflows in general work. Currently the subscription policy of a mailing list is determined by its subscription_policy attribute of the SubscriptionPolicy enum. That policy is enforced in the SubscriptionWorkflow state machine, which curently works with a queue of states, and looks like this:


It is quite complex, and the UnsubscriptionWorkflow has much of the same code except the verification and moderation parts.


What I propose is to make the [Un]SubscriptionWorkflows, more generally workflows, pluggable components which would work on a stack state machine and be composed of mixins like so:


This way a mailing list will have the name of its SubscriptionWorkflow component in the subscription_policy attribute. Which would mean for example an would be transformed into a class like so:

class OpenSubscriptionPolicy(SubscriptionBase, VerificationMixin):

    name = 'policy-open'
    description = 'An open subscription policy, only requires verification.'
    initial_state = 'prepare'

    def _step_prepare(self):

I believe this simplifies the policies and also allows chaining and composing of workflow steps, which would enable plugins to add custom subscription policies enforcing more steps/checks during the [un]subscription process. For example an organization-only mailing list could add a check that checks for the subscribing address in the organization database, therefore removing the need for manual moderation of subscriptions.

This should also allow for requiring confirmation of all commands, by attaching a workflow instance to them. For the membership commands, this would be the corresponding [Un]SubscriptionWorkflows, and for other commands it would be currently disabled/empty workflow. Then a plugin could set the confirm workflow on commands it needs to.


I am currently working on this as a MR, although I am not sure if this is the right track to go on with workflows. I think something to split the monolithic [Un]SubscriptionWorkflows would be a nice thing in Mailman Core, as they share a lot of the same code and are overly complex for what they are, a linear series of checks with some skipping possible.

GSoC 2017 - First evaluation

The first evaluation period is here, I have already sent in my evaluation so I thought I would recap the state of the project.

During the first two weeks or so, I mostly focused on features in Mailman Core that would be necessary for making a plugin like this work, my MRs introduce an easy to use/configure plugin infrastructure. Where plugins can provide components and even custom REST routes in Mailman Core. This with just installing the plugin into the same environment as Mailman Core and some config additions. Also some minor additions of selecting the list style on creation in Postorius and appropriate changes in mailman-client.

During the last two weeks I focused on work on the mailman-pgp plugin itself, along with its helper plugin mailman-rest-events. The REST events plugin is finished, apart from some tests and bug-fixing and it does its job of sending Mailman Core events to RESTful application endpoints fine.

The mailman-pgp plugin currently has working list-key generation, decryption in incoming runner, signature checking rule and general inline and PGP/MIME message handling utilities. I also got its tests to work with Mailman Core test layers so it can be actually integration tested as it would work with Mailman Core. Along with that I setup Gitlab CI pipelines with coverage testing and currently tests pass!

As I worked in PGP handling in messages I needed a good python PGP library, which I found, but since some features mailman-pgp requires were not implemented/buggy I made quite a lot of PRs to this library as well.

Key management is the next thing I would like to work on after finishing tests of the current implementation. After that outgoing message encryption and signing. When these things work I would then move on to implement the appropriate things in the django-pgpmailman web app which hooks into Postorius and HyperKitty.

Overall I am quite confident in where the project is now. Seeing it run tests with Mailman Core running and pass (just some basic tests atm) feels good.

GSoC 2017 - PGP handling progress

This week things moved along with the mailman-pgp plugin. As I finally had a good PGP library to work with as well as some basic plugin API patches outstanding to Mailman Core.

So with that I implemented large parts of the mailman_pgp.pgp package, including tests, as well as a basic signature checking rule. Also, as the package now contains tests, I added a gitlab CI config and started running it.

repo @ repo @ gitlab


Created two classes, which wrap an email.message.Message instance (or and expose a common public PGP handling API on said instance. One understands PGP/MIME (as per RFC1847 and RFC3156) and the other somewhat understands inline PGP. They both give methods to check presence of a PGP signed/encrypted message or PGP keys, to verify signatures of a message, to decrypt it or collect the attached keys. There are currently almost complete tests for handling signatures and partial for encryption.


This one was rather easy to implement, as RFC3156 and RFC1847 are both very precise but also rather simple standards (well compared that to 90+ page RFC4880 [OpenPGP]).


As there is really no “Inline PGP standard” this wrapper improvises with how it looks for signatures/messages/keys. In it’s current form it only accepts a non-multipart message and looks for ASCII-armored blobs in it.

Signature rule#

The concrete way of integrating the PGP plugin into Mailman Core has changed over time. The original version of my proposal planned on working in the Mailman Core tree and as that changed to a plugin, new requirements of integration popped up, us stuff couldn’t just be changed for this one plugin to work.

Because of the nature of encryption the PGP plugin needs to get it’s hands on the messages before the rest of Mailmans rules. This could be done with a rule in a custom chain, however as I understand rules, they are, well, rules, they shouldn’t change the message they are processing in any way. In my understanding of this architecture, if Mailman was written in a language with a const identifier, the message would have it in the chain links process method. It would work as a rule, just wouldn’t feel nice and fit in with the architecture.

That’s why I first wanted to implement all handling of incoming messages in a custom incoming runner. This runners class is then set as the runner for the in queue and the default runner is moved to another queue, perhaps in_default. The custom incoming runner does it’s thing before passing messages back to the default incoming runners queue. However only encryption has this issue of needing to change the message, as rules and the rest need it in cleartext. So I decided to split off signature handling to a custom rule, and only handle encryption in the incoming runner.

I would like to get rid of the not-so-nice redirection of the default incoming runner altogether but I see no other way to decrypt the incoming message to a PGP enabled list before other rules get it.

The signature rule processes messages using the wrappers described above according to a per-list signature handling configuration:

Option Default
unsigned_msg_action bounce
inline_pgp_action None (pass-through)
expired_sig_action bounce
revoked_sig_action bounce
malformed_sig_action bounce
duplicate_sig_action bounce
strip_original_sig False
sign_outgoing False

I think the options are quite self-explanatory. They are checked in order and taken if the message matches the option and the set action is not None. So with the default configuration of the PGP enabled mailing list, an unsigned message will be bounced, inline PGP is allowed, signatures by expired keys are bounced (expired at time of checking), signatures by revoked keys are bounced, signatures that don’t verify are bounced (maybe a better name for them than malformed?) and finally signatures that the Mailman PGP plugin already processed are bounced as well. This is to prevent replay attacks and is something I think people would expect Mailman to do, although I am not sure if the plugin should do it.

This signature rule is almost complete, with some edge-cases remaining and the checking of duplicate signatures also remaining.

GSoC 2017 - Finally a nice PGP library

Since my last post, I’ve been looking for a suitable PGP python library as I realized my original candidate python-pgp just wouldn’t work:

  • Its not actively maintained.
  • Its tests/docs etc.. are lacking.
  • Its security is questionable.

I’ve since found and tried using python-gnupg, gnupg, python-gpgmime and finally PGPy.



repo@bitbucket pypi A library that uses the GNUPG binary and its status interface to provide functions similar to GNUPG command-line functionality.


repo@github pypi A fork of the original python-gnupg, that fixes security issues mainly considering unvalidated input passed to the GNUPG binary. However it diverges from the original library and doesn’t offer some functionality the original does.


repo@github pypi This library provides PGP/MIME functionality on top of python-gnupg, although I got it to work with the gnupg fork after some changes. Only handles RFC 3156 partially (no combined method, sign + encrypt).

However, all of these libraries require the GPG binary, mostly work only with some specific versions of it and break terribly when it replies unexpectedly. They all suffer from having to work with the GPG binary and having to adjust to its API changes, where one additional response word (in a new version of GPG) causes the operation to fail completely. And since these work with the GPG binary they only provide what functionality it offers, so no direct PGP packet manipulation, no signing with keys from a different keyring and so on.



repo@github pypi This library is an (almost complete) implementaton of the OpenPGP format (RFC 4880), that doesn’t use the GPG binary but implements packet parsing, encryption/decryption, signing and other PGP features using python and OpenSSL. Also it seems that the library is in active development, accepts PRs and has an up-to-date pypi distribution. It has an extensive test suite, when counting parametrized tests it sits at around 800+ tests.

Since it allows access to OpenPGP packets (and parses around 90% of them), it enables the Mailman PGP plugin to not strip the original senders signature (if configured to do so), or encrypt to recipients anonymously (zeroing keyids in PKESK) and so on.

GSoC 2017 - About the plugin

As last time I described outstanding PRs to Mailman Core and other Mailman’s components, this time I am going to describe the plugin I am building on top of those components to enable PGP encrypted mailing lists.

In fact there are two plugins to present, with the first one being a general and quick plugin example that showcases how the new plugin API might be used and also provides some nice services for the PGP plugin.


repo @ gitlab

This plugin subscribes itself to receive events in Mailman Core and sends them to urls configured. It is a variation of what mailman!264 is intending to do, based on the plugin API implemented in my MRs. It currently offers only basic functionality, a URL is specified in config per endpoint along with an API key (very similar to mailman-hyperkitty) and a regex of event class names that will be sent. The data sent currently is only the event class name in a JSON object. Config can have many endpoints specified with the form shown below.

url: http://localhost/django/api
api_key: Something_secret_here
events: .*

Results in a call such as this one:

POST /django/api?key=Something_secret_here HTTP/1.1
Content-Type: application/json

{"event": "DomainCreatedEvent"}

The plugin is currently very bare bones and proper serialization of events is underway. I am thinking of recursively serializing the non-private event attributes until an easily serializable object is reached, such as an Address or a MailingList or anything that has a unique string associated with it and can be queried using the REST API and such a string. So for example:

  "name": "DomainCreatedEvent",
  "event": {
    "domain": ""


  "name": "AddressVerificationEvent",
  "event": {
    "address": ""

This plugin will be used by the mailman-pgp plugin to send events to django-mailman3 which will distribute them to Postorius, HyperKitty and the django app created to enable PGP encrypted mailing lists.


repo @ gitlab

First of all, I think I came up with a name that better fits with mailman-hyperkitty being an archiver “plugin” and in general with how plugins for projects are named in python (flask_ext_… and so on).

As the general functionality and layout of the plugin was already described I will mostly just summarise issues I am currently working on tackling.

low level PGP work#

I originally proposed using python-pgp (github pypi) to use to parse and create OpenPGP packets, as other libraries mostly offer only a high-level API or are not so feature rich. However this library seems quite un-maintained, so either a working fork should be established or a different library used. I am currently looking at both options. Will contact the original author as well as the current pypi package owner and see what’s possible.

public key on subscription#

Since a PGP enabled mailing list needs to know the public key of a subscribed address at the point when moderator approval is required, some mechanism to do so is necessary. This needs to happen before moderator approval, as the moderator / list owner should be able to see / download the provided key to verify it (or at least its fingerprint). Currently I see two options, an extension of the proposed key command or some changes to the current Subscription and Unsubscription workflows.

Extending the proposed key command would let the user use said command to send and confirm his public key, before a subscription to an encrypted list. This is similar to how schleuder does it. They essentially run somewhat of a mailing key-server where the users might submit keys via custom headers in message body.

However the current SubscriptionWorkflow and UnsubscriptionWorkflow are really close to being able to require a custom additional step during which the subscribers key is received and confirmed. Maybe making [Un]SubscriptionWorkflow pluggable components, that can be set on a per-list basis, similar to owner/posting chains or pipelines.

The second solution is definitely a more heavy-handed one, although might be a better one in the long run. As it allows plugins to provide [un]subscription steps which might be a nice use for plugins in Mailman Core. I can imagine for example subscription verification where the plugin verifies that the subscriber is a customer of some company by showing the user a confirmation token in the customer system etc..

require confirmation for all commands#

To address replay attacks where an attacker listens on a user performing a command by sending it to the list_id-confirm address and then sends the same message again in the future to replay the command, all commands will need to require confirmation. This stops the attacker since he cannot obtain the cleartext of the message containing the confirmation token, or even if he could, he cannot forge the signature on the confirmation message (leaving endpoint security out-of-scope). To reuse the confirm command, Workflows and confirmation would have to be refactored out from subscription management.

GSoC 2017 - About a week in

This week has been quite productive. I started laying the groundwork for the encrypted lists plugin by making changes from core_changes to different components of both Mailman Core, Mailman-Client and Postorius. I will describe the MRs and ideas behind them here.

Mailman Core#

Move pipelines to their own package and instantiate dynamically#


This MR makes Pipelines in Mailman Core work in the same dynamic way as Handlers,Rules,Chains and other similar Mailman’s components work.

Add plugin infrastructure#


This MR is the biggest one and certainly the most important one for the pgpmailman plugin and other potential Mailman plugins. It adds a notion of a plugin to Mailman Core. To explain what I mean by a plugin, it’s best to look at its example configuration:

path: example_plugin
class: example_plugin.plugin.ExamplePlugin
enable: yes
configuration: python:example_plugin.config.example

This configuration defines a plugin called example (section name). Which resides in the example_plugin package with the example_plugin.plugin.ExamplePlugin class implementing the IPlugin interface, which I will describe in a moment. This plugin is enabled and can use the path example_plugin.config.example.cfg to load it’s configuration.

Alright now what does this actually do / change / provide in Mailman Core?

  • The plugin’s path package is searched for pluggable components if the plugin is enabled and has a non-empty path value. A new function similar to find_components is introduced and used, find_pluggable_components. The components are searched for in subpackages named after what is being searched, common to Mailman Core:
    • example_plugin.rules -> IRule
    • example_plugin.chains -> IChain
    • example_plugin.commands -> IEmailCommand
    • example_plugin.handlers -> IHandler
    • example_plugin.styles -> IStyle
    • example_plugin.pipelines -> IPipeline (after mailman!287 lands)
  • The plugin’s class is instantiated if the plugin is enabled and has a non-empty class value (and implements the IPlugin interface). The IPlugin interface has several methods:
    • pre_hook() -> Is called after instantiation but before Mailman’s db initialization, really just moved the existing pre_hook configurable callable into a plugin infrastructure. Return value unused.
    • post_hook() -> Is called after pre_hook and after initialization of Mailman’s db and other dynamic components such as commands, rules, handlers… Return value unused.
    • rest_object() -> Is called whenever a REST client calls the /plugins/<>/... route, to obtain an object which will respond to REST queries for the plugin rooted at aforementioned route. This method can return None to signify the plugin doesn’t want to expose any REST routes, in which case any calls by clients return 404. Otherwise the object should use the routing system used by Mailman’s REST api (@child(), on_get and so on).
    • Mailman also sets the name attribute on the plugin object after instantiation, before pre_hook to the plugin’s name in configuration.
  • The plugin can use the path in it’s configuration option to load it’s configuration if it needs a config file. The same path resoultion rules apply as for the archivers configuration.

Add description attribute to styles#


Mailing list styles currently have an attribute for name, which for the default styles is:


Which is not particularly human-readable, well it is, but doesn’t look like something to be shown in a web ui such as Postorius. Mapping these style names to their descriptions in Postorius is possible, however since the styles are dynamically found and instantiated, it’s not something practically doable.

So this MR adds a description attribute to IStyle and adds the appropriate descriptions to the default styles. Also exposes said description to REST clients.


Add bindings for specifying list style#


This MR adds missing access to the lists/styles REST api endpoint and another argument style_name to the Mailman-Client’s create_list call, which is already recognized in Core and will create a new list with selected style. This will then be used by Postorius.

Add plugin bindings#


This MR depends on mailman!288 as it really just exposes the new REST api added with plugins to Mailman-Client. Since this new api gives plugins control over all the requests to paths after their root, the api in Mailman-Client can not be as high-level and abstracted as it is for lists and other core features. It provides access to plugin’s configuration and a, data, method) similar to


Add list style selection to list creation view#


This MR depends on mailmanclient!28 and mailman!289. It adds another field to the list creation form, prompting the user to select a list style, with the default being pre-selected and list style descriptions being shown.

GSoC 2017 - Integrating with Postorius and Hyperkitty

Since a plugin-like out-of-tree approach is required for implementing encrypted lists into Mailman, a straight forward integration into Postorius and HyperKitty (as first proposed) by making them “aware” of the encrypted lists plugin is not possible.

Thus a new approach for providing their functionality and conforming to the project requirements is necessary. I see three possible pathways forward and a middle-ground between them.

Standalone django app#

A new django app will be created, using django-mailman3 as a base, that will implement all the web based functionality for encrypted mailing lists, such as:

  • Displaying the List key for all public encrypted mailing lists.
  • List key management for list admins
  • User key management
  • Encrypted archives, that are server unencrypted (effectively replaces HyperKitty for encrypted lists)

This app will then be run besides Postorius.

A fork/patchset approach#

This approach will create a fork of Postorius and HyperKitty that will integrate changes necessary for the encrypted lists plugin seamlessly. Thus users wanting to use encrypted mailing lists will have to setup Postorius and HyperKitty from this fork.

Wrenching it in#

This approach tries to integrate all of the functionality using configurable options of Postorius and HyperKitty. For example storing messages encrypted could be done via a custom django.db.backend. Receiving messages encrypted could be done via a small custom django app that will receive them, decrypt and pass to HyperKitty decrypted.

A middle-ground#

Somewhat of a middle ground seems to be most sensible. A standalone app will be necessary to provide functionality that is simply not possible to be integrated into Postorius and HyperKitty sensibly. This app will mostly provide key management (user and list), receive the messages encrypted and so on. However Postorius and HyperKitty will work with the least amount of “wrenching it in” as possible.

1   2   3   4