I’m pleased to announce the immediate availability of a reference implementation for the Public Key Directory server.
This software implements the Key Transparency specification I’ve been working on since last year, and is an important stepping stone towards secure end-to-end encryption for the Fediverse.
You can find the software publicly available on GitHub:
To get started with the project, start with the pkd-server-php repository (linked above). Some quick commands:
# Clone the source code git clone https://github.com/fedi-e2ee/pkd-server-php.git # Install dependencies cd pkd-server-php composer install --no-dev # Setup and configure cp config/database.php config/local/database.php vim config/local/database.php # or your favorite editor cp config/params.php config/local/params.php vim config/local/params.php # or your favorite editor # Setup SQL tables php cmd/init-database.php # Run locally for dev purposes: php -S localhost:8080 -t public
This represents an important milestone in the overall project!
However, there remains a lot of work left to do.
(We’re only on v0.1.0 for both projects, after all.)
So, I’d like to outline the road between where we are today, and when you can easily use end-to-end encryption to communicate with your friends on Mastodon and other ActivityPub-enabled software.
But before we get into the technical stuff, the most important question to answer is why anyone should care about any of this.
What was released today is still not production-ready. We’re still on v0.x for all of the software in scope.
There will be bugs!
Some features may need to be reconsidered (which will require future revisions to the specification).
Do not count on any of this software being secure or stable until we tag v1.0.0.
As I’ve discussed in a previous blog post, a lot of social media toxicity and online services getting shittier over time share a root cause:
They’re direct consequences of centralized platforms with access to fuckloads of sensitive data about people.
Whenever a techie figures this out, they’re quick to embrace decentralized technologies, but these actually have a pretty awful privacy track record.
I like to pick on PGP and Matrix, but the Fediverse (including Mastodon) is a good example that doesn’t require technical expertise to grok:
On the Fediverse, DMs (“direct messages”) are not end-to-end encrypted. This means that your instance admin can snoop on your messages if they want. (In fairness, this was also true of DMs for Twitter and BlueSky for most of their history.)
Last year, the W3C decided to investigate E2EE for ActivityPub, which would create a standard for solving this privacy foot-gun for Mastodon and other Fediverse software.
However, key management for the Fediverse was still a very difficult problem to solve.
Until today.
To understand that, you need to know a little bit of cryptography concepts.
Don’t worry, I won’t be throwing any crazy math at you today.
There’s a special type of cryptography that involves two different numbers (called “keys”): One public, one secret.
Every “secret key” (which, as its name implies, should be kept secret) has a corresponding “public key” (which can be freely shared with the world). The two have some mathematical relationship that lets us do cool things.
If you’re hoping for an intuitive, easy-to-understand explanation for how any of this “secret/public key” magic works without any mathematics, the best I’ve found online uses colors.
(If you need a deeper explanation, I’ll try to come up with one. But for now, just know that “public keys” are intended to be shared publicly, and can be used for lots of things in cryptography.)
Some things that are nice to know about public keys:
However, these systems are only secure if you know which public key to use, especially if you have an intended recipient in mind.
Knowing which public key is trustworthy turns out to be a much harder problem than even most technologists appreciate.
Key Transparency is a way to ensure that, given a public key to use for cool cryptography purposes, you can be reasonably sure that it’s related to the specific secret key held by the person you want to communicate with.
How Key Transparency works is simple in concept: Build a protocol that lets everyone publish their public keys to an immutable, append-only ledger called a “transparency log”.
If you want to find the public keys that belong to your friend, you can simply query the transparency log for all [non-revoked] public keys that belong to said friend.
If the transparency feature is well-designed, app developers can write software that is reasonably confident it has the right key for the intended recipient. This is more robust than expecting users to manually verify arcane-looking strings (key fingerprints or “safety numbers”).
Finally, you can have the Fediverse instance software (e.g., Mastodon) advertise which transparency log it passes messages onto, so you always know which transparency logs to query for a given instance.
When you package this all together, building end-to-end encryption for the Fediverse becomes much simpler.
Before I get into the meat of today’s discussion, I need to be clear about the service architecture that the Public Key Directory fits in.
If you prefer a visual aid:

The above diagram is taken from the current version of the Architecture page of the Specification repository.
Of course, this is only concerned with writing to the Public Key Directory.
There is, additionally, an HTTP JSON REST API for read-only access. This HTTP API allows SDK software to make an incredibly simple but powerful user experience for fetching public keys and other data from the Public Key Directory.
For example, if someone were to write JS SDK tomorrow, the API their users would need to know is quite simple:
// Example config
const keyDir = new PublicKeyDir('https://example.com');
// Fetch public keys to use for E2EE
const publicKeys = await keyDir.getKeys("@[email protected]");
// AuxData type: ssh-v2
const sshPubKeys = await keyDir.getAuxData("@[email protected]", "ssh-v2");
The Auxiliary Data feature also allows other people to build atop this work to help provide key transparency to other protocols. See fedi-pkd-extensions for more information.
There are, indeed, many projects that aim to provide some cryptographic notion for transparency. Famously, Facebook / Meta has an open source “auditable key directories” project.
There are a few things the Fediverse Public Key Directory project does that other incumbent designs, such as SigSum, do not:
If you wanted to build with AKD or another key transparency solution, you still need to figure out your own architecture and storage.
In contrast, the Fediverse Public Key Directory project is an opinionated complete solution.
Now that we’ve covered the preliminaries, let’s take a quick look at how we got here, where we’re at, and then where we’re going next.
I’ve talked about this at length in earlier blog posts in this category, if you want more details.
At the end of 2022, I decided to use my applied cryptography experience to help the Fediverse encrypt direct messages between users. I started laying out a specification for E2EE overall, but realized that Key Transparency was a harder problem to solve, and therefore the one I should focus on first. In 2024, I shifted my focus to solely tackle Key Transparency.
The public key directory specification project was open source from its inception, but I didn’t want to release a reference implementation of the server software until I was certain about the design decisions made in the specification.
Thus, the actual implementation work was a solo undertaking. Lessons learned from trying to build it were used as a feedback mechanism to strengthen, simplify, and clarify the specification.
Earlier this year, I started writing a Public Key Directory server in Go, which was a sensible choice since I was planning to build atop SigSum (and all the developer tooling was written in Go). However, this proved to be a grueling experience, so I decided to change direction and instead implement my own Merkle tree-based transparency log.
In the weeks following this experience, I’ve been hammering out the PHP server implementation I’m releasing today.
Here’s a quick visual aid for understanding the architecture:
Once the full specification was implemented, and I have good CI tests to ensure it works well on multiple RDBMS backends, I fleshed out a PHP client SDK.
As soon as both software components were ready for public feedback, I made them both open source and published this blog post.
The ActivityPub authors are actively figuring out how to implement E2EE in the protocol. I filed an issue in June recommending Key Transparency over manual actions (i.e., manual key fingerprint validation by the end-users).
Let’s talk about what needs to happen in order for your Direct Messages to be encrypted on the Fediverse.
Now that the specification is implemented (and isn’t sparkling vaporware), we can start to advocate for Fediverse software developers to consider it.
Therefore, the immediate next step (in my mind, anyway) is to write a FASP (Fediverse Auxiliary Service Provider) specification for key transparency.
In parallel, writing more client SDKs will make it easier for Fediverse software written in TypeScript, Ruby, Elixir, Python, Rust, and Go to communicate with the Public Key Directory.
Maybe some of those can FFI the Rust implementation?
Either way, the goal for these SDK libraries is to allow both end-user applications and Fediverse servers speak the protocol (even if, in practice, most end users will only use it from a browser extension).
As more of the community gets involved with the project, we may need to update the specification and implementation to make adoption easier for all parties involved. Once we’re happy with both, we will begin tagging the v1 major version and proceed to the roll-out phase.
While I’ve been developing this project, I’ve been looking for ways to ensure that we meet an extremely high bar for software assurance (security and correctness).
Before v1.0.0 is tagged for any project in the fedi-e2ee organization on GitHub, each of these requirements will either be met, or I’ll commit a statement about why it’s not an appropriate mechanism for that specific repository.
One thing you’ll notice is absent from the above list, but common for cryptography projects, is a paid third-party assessment by other cryptography and software security experts.
As of this writing, I simply do not have the disposable income to fund such an audit, and I have no plans to generate revenue off the work I’m doing, so it’s unlikely that I ever will.
Unless a third party steps up and pays for an audit, this will remain an unchecked box.
Once we tag v1.0.0 of the various specifications and implementations, it will be time to write patches for the various Fediverse instance software and client applications.
Patching instance software will generally be easy: All instances really need to do is advertise a list of Public Key Directory servers. Everything else will be handled by the existing ActivityPub plumbing (since the Public Key Directory only accepts most Protocol Messages via ActivityPub, not generic HTTP).
However, if the instance software doesn’t already support RFC 9421 and FEP-521a, those will remain blockers for that project. (Also, they MUST support Ed25519 for it to work.)
I’ve already volunteered to help Mastodon get with the program. However, the Fediverse is much bigger than just Mastodon, so some coordination is necessary.
Once Key Transparency exists across the Fediverse, the next two phases can be performed in parallel.
If the W3C SWICG’s ActivityPub E2EE specification makes excellent technical decisions about applied cryptography, I intend to focus my time and energy on that project.
If they commit to an extremely stupid mistake (e.g., being backwards compatible with some legacy protocol that requires, I dunno, RC4?), I will dust off my own early specification and proceed to build that out.
(With the Public Key Directory project already deployed, I don’t really need to boil the ocean on the “Federated PKI” step in my original spec, after all.)
At the end of this effort, we should have open source desktop apps, mobile apps, and browser extensions that implement E2EE with public keys vended from the Public Key Directory.
As far as MLS implementations go, the ts-mls project seems like a reasonable TypeScript implementation to build upon. At some point in 2026, I hope to find time to review it thoroughly.
Key Transparency is a powerful security tool, and there’s no sense keeping it all to ourselves.
To that end, I want to build proposals, specifications, and proofs-of-concept for using the Public Key Directory to fetch other application-specific public key material (dubbed “AuxData” in the PKD spec).
Some use cases that come to mind are:
The sky is the limit, really. I’ve already outlined the projects I want to work on next, in a previous blog post.
I can’t give you a realistic timeline for when all this work will be complete. I’m actually very bad at accurately predicting how long it will take to build software.
What I can say is, I’ve already put a lot of necessary work in, and most of the remaining work doesn’t actually require much of my particular skillset, so maybe it can be sooner than later?
Ultimately, that decision comes down to the Fediverse and the greater open source community.