Draupnir is being supported with a grant from NLnet

Table of Contents

Good news

Our application for a grant from NGI Zero Core has been accepted1. The goals of the work are listed below, we don't expect them to change much but if they do then this post will be updated.

Without this grant, development on Draupnir would have ceased and we would only be able to carry out maintenance. I'm not sure what the future of Draupnir would have been within the context of the upcoming 2.0.0 release, but we don't have to worry about that now.

You can find more information about NGI Zero and NLnet here2.

It's also worth mentioning that the European Commission’s Horizon programme seems to have made moves to drop NLnet's NGI programme from future funding. Within the context of Matrix, NGI has helped fund a number of projects, most notably work on matrix's cryptography and Fractal, although I am unable to find a complete list projects or specific details. If you have some clout to spare please find the open letter3. You can also see the Matrix foundation's own response4.

Initiatives like this ending will negatively impact all software for everyone5, not just the open source ecosystems or bubbles we find ourselves in.

Draupnir's plan

Statement

This statement was written at the beginning of 2024, many of the goals have now been achieved and Draupnir's focus now lies in directly supporting Matrix communities.

Important Trust & Safety work within Matrix7 has proceeded slowly over the course of the last 5+ years due to a lack of resources and split focus. Draupnir is built on this legacy, that is inherited from an older sister project called Mjolnir. Draupnir and Mjolnir are both moderation bots that collectively protect an overwhelming majority of Matrix's public facing communities. Both projects have outstanding issues that are blocked by a significant need for a refactoring and rearchitecting of their core concepts. Draupnir has now almost fulfilled this need with an open source library called the matrix-protection-suite. The plan is to complete this work and finally address the pain points for Matrix room moderators with Draupnir, and make it easier for Draupnir’s behaviour to be modified by developers, system administrators, and in some cases room moderators. This will be done with the aim to create less dependence and pressure on the maintainers of Draupnir itself to meet all the differing requirements of Matrix's communities.

Reading the goals

The goals can be implemented in any order, but they're roughly in the order that they will be tackled in.

For an accurate picture of what is being worked on, please see the planning board on GitHub https://github.com/orgs/the-draupnir-project/projects/5.

I will keep the board up to date, and if I have neglected and failed at that, it shouldn't be too difficult to infer from this what the direction currently is. These goals directly map to a planning story or description within Draupnir's planning tracker https://github.com/the-draupnir-project/planning/issues. And we generally will describe any other big pieces of work there.

The goals certainly look ambitious, but there has already been a lot of work in the background over the past year on the matrix-protection-suite. These features were in mind and the code and architecture was designed somewhat explicitly to allow expansion towards these goals.

The Draupnir blog series

The draupnir blog series, starting here, has been superseded by regular project governance reporting, which can be found on the documentation website.

The goals

Command system refactor

The current API for defining commands depends on too much ambient context that makes unit testing commands difficult. The API will be modified so that commands only have access to their direct dependencies and there is a separate layer of glue to provide these dependencies from a generic common context. The direct dependencies can now be faked on a per command basis in unit testing.

https://github.com/the-draupnir-project/planning/issues/22

Milestones

  • The ban, unban, kick, watch, unwatch commands all have unit tests, not integration tests8.

Completion

Command system library

The current command definition system, including the JSX templating system for rendering Matrix messages, is to be made available as a library and npm package.

https://github.com/the-draupnir-project/planning/issues/22

Milestones

  • The command system is released with a distinct GitHub repository and npm package.

Completion

This was completed at the same time as the command system refactor.

Safe Mode

Problems with runtime settings can be diagnosed and resolved without external tooling or editing.

https://github.com/the-draupnir-project/planning/issues/1

Milestones

  • New pre-release (2.0.0-beta.*) containing the safe mode feature.

Protection Configuration Management

Protection settings can be accessed and modified. Protections can establish subcommands9.

https://github.com/the-draupnir-project/planning/issues/18

Milestones

  • New pre-release (2.0.0-beta.*) containing the configuration management feature.

Capability Management

As a room moderator, I can see the active capability provider for each protection and read about the capability providers that they are compatible with. I should then be able to change the active capability provider for each protection as I see fit.

https://github.com/the-draupnir-project/planning/issues/6

Milestones

  • New pre-release (2.0.0-beta.*) containing the capability management feature.

Completion

This goal is better understood as a stepping stone for the goal to Preview effects of policy list subscription, as simulated capabilities will be required to preview effects. The work does enable protections to use custom capabilities, including the simulated capabilities included for each interface. But there has not been and demonstration of alternative capabilities for protections beyond that yet. It is however relatively straightforward for protections to start to do so.

  • Story:
  • Releases:
    • v2.2.0
      • Compatible capability providers are shown in the !protections show command.
      • The !draupnir protections capability reset <protection name> command is added to restore the default capabilities for a protection.
    • v2.0.0-beta.9
      • Active capability provider set is shown in the !protections show command.
      • !draupnir protections capability <protection name> <capability name> <capability provider name> command is introduced to change the active capability providers for a protection.

Release v2.0.0

Milestones

Completion

Preview effects of policy list subscription

A summary of changes that will be made to Matrix rooms will be presented when watching a list. A prompt is given to confirm or cancel the change. This essentially runs all protections in a dry run mode.

https://github.com/the-draupnir-project/planning/issues/2

Milestones

  • New release (2.*.*) containing the preview effects feature.

Work with and Implement feedback from accessibility and security audits

Draupnir's accessibility will be audited against WCAG where applicable and Draupnir will receive a security quickscan at some point after the v2.0.0 release.

Completion

Acessibility documentation: https://the-draupnir-project.github.io/draupnir-documentation/contributing/accessibility Accessibility report: https://github.com/user-attachments/files/23337837/HAN.Accessibility.Report.Draupnir.pdf Accessibility report response: https://github.com/the-draupnir-project/Draupnir/issues/997 Security documentation: https://the-draupnir-project.github.io/draupnir-documentation/contributing/security

The security report and response will be published in May, we're waiting on matrix.org to review the report as it may have implications for their projects. There were no significant findings in any case.

Approval and disapproval ratings for policies

This goal is an incorporation of goals "Agreement and Disagreement ratings for policies" and "Explicit agreement watch mode for policy lists" (see Abandoned or reworked goals). The goals are still relevant intact, but we break down the milestones to make them more achievable together.

Draupnir moderators will be able to rate policies in other lists with using a binary agree/disagree rating. Draupnir will ignore policies that have a disagree rating. Policies that conflict while unbanning a user that originate from lists that are not controlled by Draupnir will automatically be rated with disagree. https://github.com/the-draupnir- project/planning/issues/3

A new policy list subscription mode is introduced, which allows a lists policies to be applied lazily. This means policies from a list in this mode are only enforced when a policy also has an agreement rating. Any entities marked by a policy from a list that is being watched by Draupnir in explicit agreement mode that are present within the protected rooms set will cause Draupnir to warn the management room. https://github.com/the-draupnir-project/ planning/issues/4

They both cover very similar features and code paths and in order to break down the milestones we need to merge them together. Specifically Draupnir currently treats all policies as "trusted" in the sense that any policy that propagates all the way to a protection has an identical interface regardless of source. And the most important part of the work for both goals is to make policy handling and propagation secure within Draupnir itself. Trusted policies have to be made to have distinct interface that allows them to be applied by protections. And policies have to be introduced into the system as untrusted and later become authorised. Milestones to be delivered together or individually in a Draupnir release.

  • Milestones
    • Draupnir's code base is modified to source all policies with an interface that allows them to be rendered by offering a preview. Infrastructure code that performs matching (for the purpose of showing which policies match which members) use a mirror to access a preview of the entity. Downstream protections may still use the legacy interface at this stage.
    • A new "trusted" interface is introduced that contains the provenance data for the policy and allows direct access to the entity. Untrusted policies become trusted at the existing watched policy rooms abstraction where provenance data is added (at this stage this will most likely be the "direct propagation" policy room subscription method). Protections are modified to use this interface and the legacy policy interface is removed
    • Draupnir can be used to rate policies with "disapprove", this weighs above all other ratings and these policies cannot be made trusted without removing the rating first. Unbanning a user will add "disapproval" ratings to policies sourced from third party lists. Feature is made available in a release of Draupnir.
    • Draupnir can be used to rate policies with "approve". An explicit approval policy room watch mode is available as an alternative to direct propagation. Feature is made available in a release of Draupnir.

Integrated appservice

he appservice is adapted to provide close integration to Matrix's Spaces feature. We provide a simple, minimalistic web frontend to the service, described as a community management portal. In effect this frontend is a special matrix client that can automatically provision a Draupnir for a community to protect all of the rooms within a space. The portal eventually can become a one-stop shop for managing a community on matrix. Milestones to be delivered together or individually in a Draupnir release.

  • Milestones
    • The appservice is adjusted for the service administrator to monitor on-demand provisioning. Specifically the onboarding workflow is adjusted so that administrators can fallback to a manual approval based system AND the infrastructure exists for Draupnir developers to arbitrarily filter provision requests into an awaiting- approval state.
    • A minimal frontend is developed whereby the user can authenticate using their matrix account and provision a Draupnir from a pre-configured Draupnir service backend. This simply provisions an existing Draupnir for a given Matrix space, with no additional onboarding steps.
    • The frontend is adapted to provide automatic protection of an entire Matrix space and assignment of appropriate power level for the Draupnir service account.
    • Draupnir and the frontend are modified so that it is possible to automatically propagate room level bans to a policy list
    • A migration pathway exists for draupnir deployed without a dedicated homeserver can switch to the appservice version. This is important as draupnir without dedicated deployments will be unable to use policy server capability

Policy server capability

Giving Draupnir policy server capability would allow Draupnir to augment the Matrix room model's access control system proactively https://matrix.org/blog/2025/04/introducing-policy-servers/

  • Milestones
    • The capability for Draupnir to simply respond to the policy server /sign endpoint is developed for Draupnir, either through a deployable proxy or through Draupnir itself. This allows for Draupnir deployed with dedicated homeserver or the appservice to make use of the policy server. To be delivered within a Draupnir release or a dedicated project release.
    • Draupnir provides an internal API to protections similar to the synapse-http-antispam api that would allow a policy-server proxy to forward events to Draupnir. These events are then passed through to protections running in relevant rooms. This includes pass-through to distinct draupnir deployed via the appservice. This is not expected to be deployable
    • Draupnir is able to communicate directly to room participants that are currently being sanctioned by protections at the policy server level. This feature should provide a be generic way for Draupnir to communicate with users, for instance to share updates to the communities code of conduct and is expected to be one way. The most likely way this will be implemented is as a readonly direct message chat that the user cannot reply to. Feature to be delivered within a Draupnir release.

Room upgrade support

  • Draupnir can list all of the rooms that are out of date within a community
  • Draupnir can upgrade individual rooms https://github.com/the-draupnir-project/planning/issues/45 (note acceptance criteria may be updated before work begins.
  • Draupnir can upgrade all eligible protected rooms as part of one process.
  • Draupnir can follow policy room upgrades from watched policy rooms.
  • Draupnir can upgrade policy rooms.

Long-term improvements to maintenance

Draupnir has accumulated a number of small tasks that need to be carried out for the long term health of the project and to reduce complexity for new contributors.

  • Milestones
    • Contributor onboarding. A tutorial exists for creating a new protection that does something simple, it doesn't have to protect the room, it could equally be a welcome of FAQ bot. The tutorial covers how to find relevant protection handles and includes a skeleton protection
    • Migrate npm packages under @the-draupnir-project scope from yarn classic (1.x) to npm.
    • Consolidate as many of our TypeScript repositories into a single mono repository as feasible

Rationale of abandoned and reworked goals

Objectives of the rework

It has become evident that the project should now pursue the following objectives as a priority:

  • Better onboarding / service architecture for communities that do not have a systems admin or infrastructure of their own
  • Some kind of web frontend for setting up Draupnir for a new or existing Matrix community. This would be as simple as possible (which can be a base to later can be extended to do more tasks, although not a priority).
  • In the wake of the Matrix foundation's project hydra security disclosure, specific tooling within Draupnir to support matrix room upgrades may be required.
  • Focus on what we are confident we can and want to complete before May. Drop anything out of scope.

Commentary on significantly reworked or abandoned goals

Timeline introspection (visible events cache)

This goal was designed specifically because the Matrix room model required Draupnir to play "catch up" with abuse. It could only react to what has already happened. Since April this year a proposal has been developed that would allow Draupnir to effectively dynamically augment the access control system of the matrix room model. This proposal is called "Policy Servers" https://matrix.org/blog/2025/04/introducing-policy-servers/.

The resources from this goal should really be catered to implementing and providing policy server capability for draupnir so that we can proactively manage events via a policy server rather than retroactively react with the timeline cache.

Simple participation metric

This is the ultimate jackpot for the project. Especially when combined with policy server capability. https://github.com/the-draupnir-project/planning/issues/2,%C2%A0https://marewolf.me/posts/draupnir/25/02.html#priorities-a-focus-on-on-boarding-users (written before policy servers were proposed). We have done some exploratory development work here but it is clear to me that we need to spend some time thinking about how Draupnir deals with persistent storage (which for the most part, so far we have managed to evade thinking about with all together).

Unfortunately without some thought and a conclusive decision about persistent storage for the project in general, progress cannot currently be made here without significant technical debt. Exploration here at this stage is particularly risky and uncertain at this stage, and so is not a priority. However, the discussion for how to solve the associated problems is ongoing.

Abandoned or reworked goals

The original goals were written at the very start of 2024. By the end of 2025 naturally some of the goals had changed or needed refining. These goals are listed below

Baseline documentation for Draupnir’s matrix-protection-suite

Conceptual documentation exists to explain policy rooms, policy lists, revisions, protections, capabilities and protected rooms sets.

https://github.com/the-draupnir-project/planning/issues/12

  • Milestones
    • Conceptual documentation is available to read and linked from matrix-protection-suite’s README.
    • A how to guide exists for scanning timeline events.
    • A how to guide exists for scanning new members.
    • A tutorial exists for creating a new protection that does something simple, it doesn't have to protect the room, it could equally be a welcome or FAQ bot.

Agreement and Disagreement ratings for policies

Draupnir moderators will be able to rate policies in other lists with using a binary agree/disagree rating. Draupnir will ignore policies that have a disagree rating. Policies that conflict while unbanning a user that originate from lists that are not controlled by Draupnir will automatically be rated with disagree.

https://github.com/the-draupnir-project/planning/issues/3

  • Milestones
    • New release (2.*.*) containing an ability to disagree with policies and to unban users.

Explicit Agreement watch mode for policy lists

A new policy list subscription mode is introduced, which allows a lists policies to be applied lazily. This means policies from a list in this mode are only enforced when a policy also has an agreement rating. Any entities marked by a policy from a list that is being watched by Draupnir in explicit agreement mode that are present within the protected rooms set will cause Draupnir to warn the management room.

https://github.com/the-draupnir-project/planning/issues/4

  • Milestones
    • New release contained explicit agreement mode.

Integrated appservice (multi-tenancy V2)

The appservice is redesigned such that Draupnir operates much more closely to the homeserver, providing an almost transparent integration to Matrix’s Spaces feature. When users create spaces or add rooms to them, the appservice automatically provisions a Draupnir for the space and protects all of the rooms, without any manual intervention.

https://github.com/the-draupnir-project/planning/issues/20

  • Milestones
    • New release containing the integrated appservice.

Dynamic protection loading

Allow protections defined externally to be loaded at startup from an arbitrary directory. Which will allow standalone protections to be developed and maintained for Draupnir by third parties without conflicting with the upstream.

https://github.com/the-draupnir-project/planning/issues/21

  • Milestones
    • New release containing the dynamic protection loading feature.

Timeline introspection (visible events cache)

When searching for events from a given user, Draupnir has to call /messages in every single protected room. This is a very expensive operation, and on homeservers with less resources can prevent Draupnir from redacting harmful messages when the operation takes too long. A timeline cache of visible events would allow Draupnir and protections to cheaply scan and locate events matching arbitrary criteria. We also need an API to be able to extract all media that has been included within an event so that it can be accessed by protections.

https://github.com/the-draupnir-project/planning/issues/9

  • Milestones
    • New release of the matrix-protection-suite containing the timeline introspection feature.
    • New release of Draupnir whereby the RedactionSynchronisationProtection is rewritten to use the timeline introspection feature.

Simple participation metric

Draupnir and MPS have no standard way of determining how long a user has been participating in a community. Draupnir’s protections each have an adhoc way of doing this and then discriminating against newer users without considering all of the rooms in the entire community (leading to false positives). A metric needs to be derived representing how likely a moderator is likely to have observed a user’s behaviour, probably from participation.

https://github.com/the-draupnir-project/planning/issues/24

  • Milestones
    • A new release of Draupnir where all the protections that discriminate against new users are rewritten to use the participation metric.

Policy expiration and temporary bans

There are many reasons why policies may no longer be relevant. A room moderator often issues bans in the heat of the moment or when tensions are high. Things can change. There may no longer be any context for why a ban is in place. As a room moderator I should be notified when policies expire or are approaching expiry and be given options for how to proceed.

https://github.com/the-draupnir-project/planning/issues/25

  • Milestones
    • New release of the matrix-protection-suite containing the policy expiry feature.
    • New release of Draupnir that uses the policy expiry feature.

Internationalisation

Draupnir is made ready for internationalisation by translators.

  • Milestones
    • Infrastructure is setup for translators to contribute to Draupnir and announced as part of a release.
    • All Draupnir commands are eligible for translation (it may also be possible to offer localisation too, but no promises).
    • Languages can be listed and changed at runtime.

Footnotes:

1

On a personal note, I did not expect this application to be successful and it hasn't really set in yet. Trying not to think about it too hard, I'm just going to focus on delivering. It took about 5-6 months to get through the process, which was pretty smooth, the limiting factor was time if anything. Of course waiting that long with uncertainty is not great and I wish I had managed myself better.

2

Honestly I'm confused about what and how I'm supposed to refer to them if you can't tell. I'm pretty sure it's just "NGI Zero" but idk.

3

I don't really know who wrote this or the context or how to write this properly, but it's more important that you just see it.

5

The consequences people focus on are usually immediate, like the the open source ecosystem as a whole becoming weaker. Maybe you will even go as far to worry about how software development is tilting towards some kind of gig/grift economy. But what I worry about is how taking software for granted will change how software is produced structurally from the ground up. And that's important, because I get the feeling that the way people program is a reflection of how they think. If the essentials become "corrupted", such as programming languages and environments. Then this means we end up unable to express ourselves properly6. To an extent, this has already happened, and it is exactly why programming is currently so terrible.

6

I don't mean expression like, some funky lisper writing a tonne of macros, or something like that. I mean more that structurally programming itself becomes tilted to serve the needs of corporations, to sell products. Rather than the needs of people who can be helped by software.

7

If you are unfamiliar with what Matrix is, it is an open source instant messaging protocol that was created in 2014. See https://matrix.org

8

Integration tests will still exist, the point is that we want it to to be a lot easier to create tests for commands. Please see https://marewolf.me/posts/draupnir/2402.html#development-testing-commands.

9

We broke this at some point either during the original command refactor back in 2023 or during the migration to MPS.