HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
fedi-e2ee
public-key-directory-specification
PHP2024–2026
fedi-e2ee / public-key-directory-specification

public-key-directory-specification

Specification for a Fediverse Directory Server for Public Keys

4Distinct people
13Mentions
0Explicit recommenders
183GitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

Specification for a Fediverse Directory Server for Public Keys

GitHub

Where it is used

No documented product use has been verified yet.

What people are saying

No recommendation phrase was found in the indexed comments.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 3:56:07 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by month13 dated mentions

Observed mentions: 2024-08-25 00:20:31 UTC to 2026-01-23 04:17:35 UTC.

012342024-08-01 00:00:00 UTC to 2024-09-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 commentsAug 20242024-09-01 00:00:00 UTC to 2024-10-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2024-10-01 00:00:00 UTC to 2024-11-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2024-11-01 00:00:00 UTC to 2024-12-01 00:00:00 UTC (exclusive): 4 mentions; 0 submissions, 4 comments2024-12-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 1 mentions; 1 submissions, 0 comments2025-01-01 00:00:00 UTC to 2025-02-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-02-01 00:00:00 UTC to 2025-03-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2025-03-01 00:00:00 UTC to 2025-04-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-04-01 00:00:00 UTC to 2025-05-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 commentsApr 20252025-05-01 00:00:00 UTC to 2025-06-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-06-01 00:00:00 UTC to 2025-07-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-07-01 00:00:00 UTC to 2025-08-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-08-01 00:00:00 UTC to 2025-09-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-09-01 00:00:00 UTC to 2025-10-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2025-10-01 00:00:00 UTC to 2025-11-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2025-11-01 00:00:00 UTC to 2025-12-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-12-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2026-01-01 00:00:00 UTC to 2026-02-01 00:00:00 UTC (exclusive): 3 mentions; 1 submissions, 2 commentsJan 2026

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
Aug 20242024-08-01 00:00:00 UTC2024-09-01 00:00:00 UTC101
Sep 20242024-09-01 00:00:00 UTC2024-10-01 00:00:00 UTC000
Oct 20242024-10-01 00:00:00 UTC2024-11-01 00:00:00 UTC000
Nov 20242024-11-01 00:00:00 UTC2024-12-01 00:00:00 UTC404
Dec 20242024-12-01 00:00:00 UTC2025-01-01 00:00:00 UTC110
Jan 20252025-01-01 00:00:00 UTC2025-02-01 00:00:00 UTC000
Feb 20252025-02-01 00:00:00 UTC2025-03-01 00:00:00 UTC101
Mar 20252025-03-01 00:00:00 UTC2025-04-01 00:00:00 UTC000
Apr 20252025-04-01 00:00:00 UTC2025-05-01 00:00:00 UTC000
May 20252025-05-01 00:00:00 UTC2025-06-01 00:00:00 UTC000
Jun 20252025-06-01 00:00:00 UTC2025-07-01 00:00:00 UTC000
Jul 20252025-07-01 00:00:00 UTC2025-08-01 00:00:00 UTC000
Aug 20252025-08-01 00:00:00 UTC2025-09-01 00:00:00 UTC000
Sep 20252025-09-01 00:00:00 UTC2025-10-01 00:00:00 UTC101
Oct 20252025-10-01 00:00:00 UTC2025-11-01 00:00:00 UTC101
Nov 20252025-11-01 00:00:00 UTC2025-12-01 00:00:00 UTC000
Dec 20252025-12-01 00:00:00 UTC2026-01-01 00:00:00 UTC101
Jan 20262026-01-01 00:00:00 UTC2026-02-01 00:00:00 UTC312

What Hacker News says

13 sources
One of the projects I alluded to in that post makes a technological solution to what you want easy to build, but the harder problem to solve is societal (i.e., getting it adopted). https://github.com/fedi-e2ee/public-key-directory-specificat... My current project aims to bring Key Transparency to the Fediverse for building E2EE on ActivityPub so you can have DMs that are private even against instance moderators. One of the things I added to this design was the idea of "Auxiliary Data" which would be included in the transparency log. Each AuxData has a type identifier (e.g. "ssh-v2", "age-v1", "minisign-v0", but on the client-side, you can have friendly aliases like just "ssh" or "age"). The type identifier tells the server (and other clients) which "extension" to use to validate that the data is valid. (This is to minimize the risk of abu
Read the rest
se.) As this project matures, it will be increasingly easy to do this: // @var pkdClient -- A thin client-side library that queries the Public Key Directory // @var age -- An implementation of age async function forwardSecureEncrypt(file, identity) { const agePKs = await pkdClient.FetchAuxData(identity, "age"); if (agePKs.length === 0) { throw new Error("No age public keys found"); } return age.Encrypt(file, agePKs[0]); } And then you can send the encrypted file in an email without a meaningful subject line and you'll have met your stated requirements. (The degree of "forward secure" here depends on how often your recipient adds a new age key and revokes their old one. Revocation is also published through the transparency log.) However, email encryption is such a mess that most people don't quite appreciate, so I'm blogging about that right now. :) Also, Filippo just created a transparency-based keyserver for age, fwiw: https://words.filippo.io/keyserver-tlog/
In The PGP problem (2019)
Cryptography stuff! ----- COCKTAIL-DKG - A distributed key generation protocol for FROST, based on ChillDKG (but generalized to more elliptic curve groups) -- https://github.com/C2SP/C2SP/pull/164 | https://github.com/C2SP/C2SP/issues/159 ----- A tool for threshold signing software releases that I eventually want to integrate with SigStore, etc. to help folks distribute their code-signing. https://github.com/soatok/freeon ----- Want E2EE for Mastodon (and other ActivityPub-based software), so you can have encrypted Fediverse DMs? I've been working on the public key transparency aspect of this too. Spec: https://github.com/fedi-e2ee/public-key-directory-specificat... Implementation: Coming soon. The empty repository is https://github.com/fedi-e2ee/pkd-server-go but I'll be pushing code in the near future. You can read more about this proje
Read the rest
In Ask HN: What are you working on? (October 2025)
Mostly decentralized/federated crypto stuff. 1. COCKTAIL-DKG - A distributed key generation protocol for FROST, based on ChillDKG (but generalized to more elliptic curve groups) -- https://github.com/C2SP/C2SP/issues/159 2. FREEON (Threshold signing tool for open source development teams) -- https://github.com/soatok/freeon 3. A reference implementation for the specification I wrote last year for federated Key Transparency, so that the Fediverse can build end-to-end encryption (E2EE) with stronger, less-centralized notion of trust than TOFU -- https://github.com/fedi-e2ee/public-key-directory-specificat... I wrote a blog post about a lot of this work (and my other side projects): https://soatok.blog/2025/08/27/its-a-cold-day-in-developer-h... And for the overall ActivityPub E2EE work: https://soatok.blog/category/technology/open-source/fe
Read the rest
In Ask HN: What are you working on? (September 2025)
> If you have some hash function H = sha256 let us say, you could calculate h = H('{"name" : "bob", "pubkey" : "1A1zP1e"}')[:N] and then set record[57] = h (I'll use a 24-bit h = 71b686 for my examples, OP suggests a 48-bit). You're responding to an earlier revision of the blog post, and the design has been updated to not truncate salts this way (nor store them at all), based on feedback on the design. > This seems...tricky, if you can't give the clients the "name" field because when you can't trust the client replicas to "forget" it when they're "supposed" to. The ciphertext is the aspect that's committed to the Merkle tree. The plaintext and the key are not committed to the Merkle tree, and can be freely deleted. That's the important bit. As for your other concern, the replication aspect is specified clearly. https://github.com/fedi-e2e
Read the rest
e/public-key-directory-specificat... If a non-conformant replica persists copies of the plaintext indefinitely, then you can now use the same legal process to request erasure from the replica. If they do not comply, then you have the same amount of legal recourse you had to begin with. This becomes a lawyer problem, not a technology problem. > How can the replicas verify a query result without having access to the redactable data? The thing you can continue to verify, after a record has been forgotten, is all of the other records in the ledger. Before erasure, that includes the record in question. After erasure, you shouldn't have anything to verify, except that history hasn't been altered beyond the decryption key being zeroed out. If you assert your right to be forgotten, the system should forget. You can no longer be verified, because as far as the system is concerned, the record in scope does not exist. But either way, history remains intact. You don't break the Merkle proofs. You can erase records by zeroing out the key, but you cannot alter them in any other way. The plaintext commitment prevents a server that's committed to a ciphertext, Enc(k1, Alice), from responding with a plaintext for Bob. The commitment is a slow hash that's included in the calculation of the authentication tag over the ciphertext (which is what's committed to the Merkle tree). The tree survives. If this still doesn't make sense, check the docs on GitHub. > OP if you're reading, clarification would be most welcome :) Here's a short and oversimplified workflow to help illustrate it: Alice -> Directory: [Encrypted Data], [Keys] Directory -> SigSum: [Encrypted Data] Bob -> Directory: [Alice]? Directory -> Bob: [Encrypted Data], [Plaintext], [Merkle Proofs over Encrypted Data] Is that easier to follow? If Alice later requests her keys deleted, then you end up with: Bob -> Directory: [Alice]? Directory -> Bob: [Encrypted Data], nil, [Merkle Proofs over Encrypted Data]
In Key Transparency and the Right to Be Forgotten
> How does the attacker learn anything about the salt without breaking Argon2id first? It’s not like the salt is included in the Sigsum ledger. Take a close look at https://github.com/fedi-e2ee/public-key-directory-specificat... The commitment (Argon2 salt || Argon2 output) is included in the ciphertext blob. The format of each encrypted attribute is: h || r || Q || t || c Where "h" is the version ID (0x01 currently), "r" is a 256-bit random value for key derivation, "Q" is the plaintext commitment, and "t" is the authentication tag. All of these values are fixed-length. Then "c" is the ciphertext, which is variable length. > If the attacker did somehow learn the salt (truncated HMAC), it seems like that would immediately break security, regardless of truncation. > Due to the truncation, the attacker may get one or more false username
Read the rest
matches in addition to the true username match. But if the attacker has narrowed down the username to two or three possibilities, they’ve already basically won. And they could just check the possibilities against Argon2id to learn which one it is. That's an interesting point. It might be beneficial to exclude the plaintext, then, given that we already have a high-entropy value (the previous Merkle root). We could also just use "r" to calculate the salt, since that's right there. Thank you for this feedback. My primary motivation in writing this blog post (and the ones that follow it which will focus on different design decisions and trade-offs) was to elicit better feedback for the current draft of my proposal. EDIT: https://github.com/fedi-e2ee/public-key-directory-specificat...
In Key Transparency and the Right to Be Forgotten
> Is the intention that E2EE have a separate, parallel account recovery mechanism with more difficult requirements? I'm not sure the wording here is helpful. Let's start with a simple diagram: https://github.com/fedi-e2ee/public-key-directory-specificat... (see the first one) A single Public Key Directory will serve many hosts. (You can further add replication and cross-directory checkpointing between directories, to ensure the whole network is transparent, but that's not our concern right now.) If a host decides to misbehave, there's an independently hosted record of every change to the state machine that maps Actor IDs to public keys. This state machine is backed by a Merkle tree, which includes a proof of inclusion for every change to its state, thereby making it an append-only ledger. In my design, you don't have to trust the hosts' a
Read the rest
dmins to behave, because the directory keeps them honest. You also don't have to trust as single directory to behave, since its primary purpose is to be transparent and independently auditable. If a user wants to revoke their key, they can, but it's an additional protocol message appended to the directory's transparency log. If a user loses all their secret keys, one of two things can happen: 1. Their instance admin issues a BurnDown, which allows the user to enroll a new public key as if they were a new user. 2. If the user has issued a Fireproof message, the instance admin cannot issue a BurnDown, and the user is locked out of the protocol. This is a trade-off. Users can move hosts. Users can publish auxiliary data (e.g., SSH public keys), if the extension for that key type is defined and enabled by the directory. Even an attacker with root access to the host cannot arbitrarily or selectively replace a user's public key with their own. With your suggestion, this substitution is trivial. And that's basically how the system works. But that's not the focus of this blog post. This blog post is how to solve the seemingly incompatible design goals of: 1. Offer userbase consistency verification for users' public keys. 2. Don't cryptographically inhibit an EU citizen's rights under Article 17 of the GDPR. 3. Don't introduce backdoors to the transparency log that allow them to perform the same trivial attack that your suggestion enables.
In Key transparency and the right to be forgotten
I'm building an open source Federated Public Key Directory, so that I can then build end-to-end encryption for the Fediverse. https://github.com/fedi-e2ee/public-key-directory-specificat... Think: Encrypted DMs for Mastodon. I wrote several blog posts about the project and why it matters. https://soatok.blog/2024/08/21/federated-key-transparency-pr... https://soatok.blog/2024/06/06/towards-federated-key-transpa... https://soatok.blog/2022/11/22/towards-end-to-end-encryption... Eventually I plan on doing a "Show HN" post when it's built and close to feature-complete.
In Ask HN: What are you working on (August 2024)?

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.