FEP-521a: Representing actor's public keys


This is a discussion thread for the proposed FEP-521a: Representing actor’s public keys.
Please use this thread to discuss the proposed FEP and any potential problems
or improvements that can be addressed.


This proposal describes how to represent public keys associated with ActivityPub actors.

cc @silverpill


Known issues:

  • FEP-8b32 compatibility. It is not clear which verification relationship should be used: authentication or assertionMethod.
  • Description of expires property should be added.

Would you consider adding nostr key pairs which use modern, compact Schnorr signatures?

Then all activitypub users will be able to use the nostr eco system. Nostr has 600+ apps on github alone built on nostr, and 1-2 new ones every day.

I’m personally highly disillusioned with the DID/VC eco system as it stands currently, not only is it incredibly complex, but the DID method registry is packed full of illegal and unethical scammy block chains trying to sell tokens to an unsuspecting public. Speaking to the VC group about it, they really dont seem to care, and there are enormous conflicts of interest.

On the contrary, adding nostr would be as simple as adding one field for the pubkey.

Nostr vocab: Nostr Vocabulary
Nostr context: https://w3id.org/nostr/context

I can add terms as needed to comply with the FEP


Could work. Or possibly this is better done as an individual FEP, together with the proxy object.

1 Like

This FEP recommends implementers to use Multikey type for public keys. That means a binary representation of a public key must be encoded according to the multicodec spec and then converted into base58-btc multibase string. So, in theory, this FEP already supports Nostr keys (and many other kinds of keys) – one just needs to choose a prefix. I’m not sure if a prefix for encoding Nostr public keys has been ever registered, but registring a prefix should be as simple as submitting a pull request to the multicodec repository (there’s secp256k1-pub, but apparently BIP-340 public keys are different?).

Note that this FEP is concerned with server-controlled keys. The mechanism for linking user-controlled keys is described in FEP-c390. Yes, it is based on DID standard, but it’s not really that bad. Implementers are free to choose whatever DID method fits their needs, and nobody is forced to use methods submitted by companies with questionable reputation.

Someone even wrote a draft of the did:nostr specification.


Proposal has been updated: #132 - FEP-521a: Update proposal - fep - Codeberg.org

It now states that keys with assertionMethod purpose are suitable for signing activities and objects.

I made several changes to FEP-521a: #189 - FEP-521a: Update proposal - fep - Codeberg.org. Nothing newsworthy - just improved text and updated some links.

@helge You were interested in specifying “revoked” and/or “expired” key statuses. As far as I can tell, the expires property has been added to multikey context: Status of MultiKey · Issue #2 · digitalbazaar/multikey-context · GitHub. Should we proceed with adding relevant definitions to this proposal?

Not sure. I have not been tracking the development closely as I’ve been busy with other things. According to this, there will be a new Multikey release in the future. So one should probably wait for it.

My expectation is that once the new version is released, we will have to use the context

1 Like

Something different, regarding the fragments. Currently, the fragments used are somewhat fixed, e.g. mastodon only uses main-key. This is inconvenient if one things about key rotation. As it means that one key replaces the other, and there can be no period, where two keys are active.

To avoid this type of issues, I would like to suggest using unique fragments for each key, e.g.

    "id": "https://server.example/users/alice#ez6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2",
    "type": "Multikey",
    "controller": "https://server.example/users/alice",
    "publicKeyMultibase": "z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2"

For normal operation key rotation, one can have

  • Old and new key present
  • A document of the old key signing the new one

This makes it easy to check that the new key indeed replaces the old one.

Do you suggest something like “Fragments MUST be unique”?

RSA keys are long, so rather than using encoded key as a fragment, we could use first N characters of it (or a hash).

Maybe :wink: If I suggest it, I suggest it as a consequence. My rule suggestion would be: An URI, including the fragment, MUST always point to the same Multikey. Here the “always” is meant in the strongest possible sense, i.e. forever and the same across the internet.

This has as a consequence that fragments would be unique. I consider this just a consequence of the underlying fact.

Regarding just using the first N characters or a hash: Sure. There are lots of implementation details here, that I have no strong opinion on. For example the first 4 characters encode the key type using this multibase / multicodec approach. Do we want this to be part of the id?

My main goal here is to reduce the id-reuse for unrelated things.

1 Like

I’m currently looking through the stuff related to this FEP and I noticed a few relevant things:

1.) In the json-ld @context, the properties authentication and assertionMethod are defined with

"@container": "@set"

This means that

    "assertionMethod":  {
            "id": "https://server.example/users/alice#ed25519-key",
            "type": "Multikey",
            "controller": "https://server.example/users/alice",
            "publicKeyMultibase": "z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2"

would be invalid in difference to the rest of the ActivityPub stuff. One should probably remark on it.

2.) Multikey can now contain an expires field. One should probably remark that one should check that the expiration date is in the future.

3.) I think the pattern

    "verificationMethod":  [{
            "id": "https://server.example/users/alice#ed25519-key",
            "type": "Multikey",
            "controller": "https://server.example/users/alice",
            "publicKeyMultibase": "z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2"
   "assertionMethod": [ "https://server.example/users/alice#ed25519-key"], 
   "authentication": [ "https://server.example/users/alice#ed25519-key"], 

should also be supported. At least that’s how I interpret the examples in Controller documents.

1 Like

This pattern is supported by VCDI, but I think FEP-521a should only recommend one pattern to make implementation process simpler.

AP servers will likely use only assertionMethod, making verificationMethod field redundant.

I agree - while using both is /technically/ correct, I think we should stick to JUST using assertionMethod for Fedi purposes.

1 Like

Updating the proposal: #208 - FEP-521a: Update proposal - fep - Codeberg.org

I added a recommendation to use fragments and a fragment uniqueness requirement (within actor object; I think the requirement to use a globally unique fragment would be too strong).

The requirement to use JSON array was already present:

it MUST be added to the assertionMethod array of the actor object.


The multikey context also contains revoked property, but it feels redundant, because one can set expires to the past date.

Since this FEP encourages the usage of fragments, it needs to specify how to resolve IDs containing fragments.

Related issue in ActivityPub spec repo: Guidance on fragments in ids · Issue #367 · w3c/activitypub · GitHub

1 Like

See RFC 3986 - Section 3.5:

The fragment identifier component of a URI allows indirect
identification of a secondary resource by reference to a primary
resource and additional identifying information.

I would understand this as uri#fragment can be resolved by resolving uri then resolving uri#fragment inside the response. This means that

  • Fragments can never be the top level id.
  • Only fragments of the form primary#fragment are allowed inside a document if primary is used as an @id inside the document.

Another update: #213 - FEP-521a: Update proposal - fediverse/fep - Codeberg.org

I added a short description of uri#fragment resolution and a feature file.

Citation from the FEP:

Implementations MUST use the base-58-btc alphabet

Is there a practical reason for this? In the linked Multikey I wasn’t able to spot a requirement for this.

I personally would prefer Base64 (no pad) since there are SIMD accelerated Base64 implementations which would speed up the process of handling these keys and then storing them into the internal format the database uses (if it differs. Some implementations may choose to represent them as PKCS#8 ASN.1 structures)

This alphabet was chosen to align FEP-521a with FEP-8b32 (these two FEPs complement each other and I think developers will often implement both). FEP-8b32 recommends eddsa-jcs-2022 cryptosuite, which (if I’m reading the spec correctly) requires base-58-btc encoding:

The publicKeyMultibase value of the verification method MUST start with the base-58-btc prefix (z), as defined in the Multibase section of [VC-DATA-INTEGRITY]. A Multibase-encoded Multikey value follows, which MUST consist of a binary value that starts with the two-byte prefix 0xed01, which is the Multikey header for an Ed25519 public key, followed by the 32-byte public key data, all of which is then encoded using base-58-btc. Any other encoding MUST NOT be allowed.

So by requiring base-58-btc here we reduce the amount of work for implementers and make interop easier.

Base58-btc is also required for did:key. Technically, Data Integrity spec allows other encodings of publicKeyMultibase, but base58-btc seems to be what implementers of the spec has converged on.