diff --git a/FEDERATION.md b/FEDERATION.md index c8d8863..934d844 100644 --- a/FEDERATION.md +++ b/FEDERATION.md @@ -194,6 +194,83 @@ all. #### (3) Server-scope actor key +Allows to have actor keys that can be used to sign (and verify) activities of +any actor on the server, not limited to any specific actor. That allows to have +some small constant number of keys on the server, which is very easy to manage +and makes key rotations very cheap. It also saves storage of many local and +remote actor keys. + +In the common Fediverse situation, there's a separate key for each actor, but +all of these actor keys are managed by a single entity, the server. The +signatures aren't made on users' devices using private keys they keep to +themselves. They're made by the server, using private keys the server +generates. + +Server-scope keys are made by the server too. The server makes the signatures, +using a private key it generates and maintains. The server is the owner of the +key, and a part of the signed message is the ID of the actor on whose behalf +the message is being sent. Since the actor isn't specified by the key, the +actor ID is instead placed in a HTTP header. And the actor still has to list +the key under `publicKey` as usual. + +`GET /key1` + +```json +{ "@context": + [ "https://w3id.org/security/v1" + , { "isShared": "https://peers.community/as2-ext#isShared" + } + ] +, "@id": "https://example.dev/key1" +, "@type": "Key" +, "owner": "https://example.dev" +, "isShared": true +, "publicKeyPem": "-----BEGIN PUBLIC KEY----- ..." +} +``` + +`GET /users/aviva` + +```json +{ "@context": + [ "https://www.w3.org/ns/activitystreams" + , "https://w3id.org/security/v1" + ] +, "id": "https://example.dev/users/aviva" +, "type": "Person" +, "preferredUsername": "aviva" +, "name": "Aviva" +, "inbox": "https://example.dev/users/aviva/inbox" +, "outbox": "https://example.dev/users/aviva/outbox" +, "publicKey": + [ { "id": "https://example.dev/users/aviva#main-key" + , "type": "Key" + , "owner": "https://example.dev/users/aviva" + , "publicKeyPem": "-----BEGIN PUBLIC KEY----- ..." + } + , "https://example.dev/users/aviva/extra-keys/extra-key1" + , "https://example.dev/users/aviva/extra-keys/extra-key2" + , "https://example.dev/key1" + ] +} +``` + +Requirements for a server-scope key: + +- Its `owner` is the top-level URI of the server, of the form `https://HOST` +- The `isShared` property is `true` +- The key is in its own document, not embedded in an actor + +Requirements for authentication using a server-scope key: + +- The actor ID is specified in the `ActivityPub-Actor` HTTP header +- The actor and key are on the same server +- That header is included in the HTTP Signature in the `Signature` header +- That actor lists the key (as one of the keys) under `publicKey` +- In the payload, i.e. the activity in the request body, the activity's actor + is the same one specified in the `ActivityPub-Actor` (unless the activity is + forwarded, see proposal B.2 about inbox forwarding) + #### (4) Actor key expiration and revocation #### (5) Ed25519 actor keys