2019-05-05 13:15:51 +02:00
|
|
|
ForgeFed/ActivityPub Federation in Vervis
|
|
|
|
=========================================
|
|
|
|
|
|
|
|
At the time of writing, here's the current status of federation implemented in
|
|
|
|
Vervis.
|
|
|
|
|
|
|
|
Summary:
|
|
|
|
|
|
|
|
* To post a comment on a local ticket (same server), log in and browse
|
|
|
|
to the ticket's page and use the ticket reply form, the regular way it's been
|
|
|
|
on Vervis.
|
|
|
|
* To post a comment on a remote ticket (other server), log in and browse to the
|
|
|
|
/publish page, fill the form and the comment will be delivered to the right
|
|
|
|
place
|
|
|
|
|
|
|
|
For more details, read below.
|
|
|
|
|
|
|
|
## Federation triggered by regular UI
|
|
|
|
|
|
|
|
* Ticket comments are federated. If you submit a ticket comment, local and
|
|
|
|
remote users who previously commented on the same ticket will get your
|
|
|
|
comment delivered to their inboxes. The user who created the ticket's project
|
|
|
|
will have it delivered to them too.
|
|
|
|
* If you comment on a ticket, you automatically become a ticket follower, and
|
|
|
|
all future comments on the ticket will be delivered to your inbox.
|
2019-05-26 13:33:41 +02:00
|
|
|
* You can see users' outboxes.
|
|
|
|
* You can see your inbox.
|
2019-05-05 13:15:51 +02:00
|
|
|
* If you create a project, all comments on all tickets of the project will be
|
|
|
|
delivered to your inbox.
|
2019-05-26 13:33:41 +02:00
|
|
|
* There is UI for notifications about comments on tickets you commented on or
|
|
|
|
whose projects you created. However there's no JS to display them in
|
|
|
|
real-time and no email integration.
|
2019-05-05 13:15:51 +02:00
|
|
|
|
|
|
|
The ticket comment UI allows to see tickets and comments, and if you're logged
|
|
|
|
in, you can post new comments. If you wish to post a comment on a ticket hosted
|
|
|
|
on another server, not the one on which your account is hosted, see the
|
|
|
|
dedicated federation pages listed below.
|
|
|
|
|
|
|
|
## GET endpoints
|
|
|
|
|
|
|
|
`GET /publish`
|
|
|
|
|
|
|
|
A page where you can write and publish a ticket comment, either on a local
|
|
|
|
ticket (i.e. a ticket on a project hosted on the same server as your account)
|
|
|
|
or on a remote ticket (i.e. a ticket on a project hosted on some other server).
|
|
|
|
|
|
|
|
`GET /inbox`
|
|
|
|
|
|
|
|
A test page that displays received activities and the result of their
|
|
|
|
processing.
|
|
|
|
|
|
|
|
`GET /s/joe/inbox`
|
|
|
|
|
|
|
|
A page that displays your personal inbox. It should list all ticket comments on
|
|
|
|
projects you've created and and ticket comments on tickets you previously
|
|
|
|
commented on.
|
2019-05-26 13:33:41 +02:00
|
|
|
|
|
|
|
`GET /s/joe/outbox`
|
|
|
|
|
|
|
|
A page that displays your personal outbox. It should list all the activities
|
|
|
|
you're published, all ticket comments you've made.
|
2019-05-05 13:15:51 +02:00
|
|
|
|
|
|
|
## POST endpoints
|
|
|
|
|
|
|
|
`POST /s/joe/outbox`
|
|
|
|
|
|
|
|
Personal endpoint for publishing ticket comments. When you submit the form in
|
|
|
|
the /publish page, this is where it is sent. In the future you'll be able to
|
|
|
|
see the content of your outbox, and other people will be able to see the public
|
|
|
|
items in your outbox.
|
|
|
|
|
|
|
|
You can access this endpoint without using the /publish page, but Vervis
|
|
|
|
doesn't have OAuth2 support yet, so you'll need to log in first and grab the
|
|
|
|
cookie, and send it along with the request.
|
|
|
|
|
|
|
|
`POST /s/joe/inbox`
|
|
|
|
|
|
|
|
Personal endpoint to which other servers deliver ticket comments for you to
|
|
|
|
see. These are comments on tickets on which you previously commented, and thus
|
|
|
|
automatically became a follower of thosr tickets.
|
|
|
|
|
|
|
|
`POST /s/joe/p/proj/inbox`
|
|
|
|
|
|
|
|
Per-project inbox, to which projects receive ticket comments from other
|
|
|
|
servers. If someone on another server publishes a comment on your project, then
|
|
|
|
your project will receive the comment at this endpoint and the comment will be
|
|
|
|
displayed when you visit the ticket page.
|
2019-05-27 11:06:29 +02:00
|
|
|
|
|
|
|
## Spec
|
|
|
|
|
|
|
|
Federation in Vervis is done using ActivityPub. Below comes a description of
|
|
|
|
the details that aren't already common on the Fediverse. The details are
|
|
|
|
written informally in the form of short simple proposals.
|
|
|
|
|
|
|
|
### (A) Authentication
|
|
|
|
|
|
|
|
Vervis uses HTTP Signatures to authenticate messages received in inboxes. The
|
|
|
|
Host, (request-target), Date and Digest headers are required to be present and
|
|
|
|
used in the signature, and the Digest header must be verified by computing the
|
|
|
|
hash of the request body. Other headers may need signing too, as specified in
|
|
|
|
the proposals below.
|
|
|
|
|
|
|
|
The `publicKeyPem` field maps to the PEM encoding of the key. The PEM encoding
|
|
|
|
contains not just the key itself, but also a code specifying the key type. The
|
|
|
|
Fediverse de-facto standard is RSA, more precisely PKCS#1 v1.5, and used with
|
|
|
|
the SHA-256 hash algorithm. This is often referred to as RSA-SHA256.
|
|
|
|
|
|
|
|
If the `algorithm` is specified in the Signature header, it must match the key
|
|
|
|
type in the PEM. But `algorithm` isn't required, and we should probably stop
|
|
|
|
using it.
|
|
|
|
|
|
|
|
#### (1) Actor key(s) in a separate document
|
|
|
|
|
|
|
|
Allow an actor's signing key to be a separate document, rather than embedded in
|
|
|
|
the actor document. In Vervis, the use of that is for server-scope keys (see
|
|
|
|
proposal below), but otherwise, an embedded key is just as good.
|
|
|
|
|
2019-05-27 13:17:56 +02:00
|
|
|
`GET /users/aviva/keys/key1`
|
2019-05-27 11:06:29 +02:00
|
|
|
|
2019-05-27 13:17:56 +02:00
|
|
|
```json
|
2019-05-27 11:06:29 +02:00
|
|
|
{ "@context": "https://w3id.org/security/v1"
|
|
|
|
, "@id": "https://example.dev/users/aviva/keys/key1"
|
|
|
|
, "@type": "Key"
|
|
|
|
, "owner": "https://example.dev/users/aviva"
|
|
|
|
, "publicKeyPem": "-----BEGIN PUBLIC KEY----- ..."
|
|
|
|
}
|
2019-05-27 13:17:56 +02:00
|
|
|
```
|
2019-05-27 11:06:29 +02:00
|
|
|
|
2019-05-27 13:17:56 +02:00
|
|
|
`GET /users/aviva`
|
2019-05-27 11:06:29 +02:00
|
|
|
|
2019-05-27 13:17:56 +02:00
|
|
|
```json
|
2019-05-27 11:06:29 +02:00
|
|
|
{ "@context":
|
|
|
|
[ "https://www.w3.org/ns/activitystreams"
|
|
|
|
, "https://w3id.org/security/v1"
|
|
|
|
]
|
2019-05-27 15:46:38 +02:00
|
|
|
, "id": "https://example.dev/users/aviva"
|
2019-05-27 11:06:29 +02:00
|
|
|
, "type": "Person"
|
|
|
|
, "preferredUsername": "aviva"
|
|
|
|
, "name": "Aviva"
|
|
|
|
, "inbox": "https://example.dev/users/aviva/inbox"
|
|
|
|
, "outbox": "https://example.dev/users/aviva/outbox"
|
|
|
|
, "publicKey": "https://example.dev/users/aviva/keys/key1"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Authentication requirements:
|
|
|
|
|
|
|
|
- The `keyId` from the signature header matches the `@id` in the document you
|
|
|
|
receive
|
|
|
|
- The and key and the owner actor IDs are on the same host
|
|
|
|
- They key specifies the `owner`, and the owner actor's `publicKey` links back
|
|
|
|
to the key
|
|
|
|
|
|
|
|
#### (2) Multiple actor keys
|
|
|
|
|
|
|
|
Allow an actor to specify more than one key, or no key at all. This means that
|
|
|
|
when you examine the owner actor of the key, you verify the actor links back to
|
|
|
|
the key by checking that the key is listed among the actor's keys (instead of
|
|
|
|
requiring/expecting only a single key to be specified by the actor).
|
|
|
|
|
|
|
|
The reason this is used in Vervis is for key rotation using a pair of
|
|
|
|
server-cope keys (see proposal below).
|
|
|
|
|
|
|
|
When used along with proposal A.1, each key may be either embedded in the
|
|
|
|
document, or a URI specifying the ID of a key defined in a separate document.
|
|
|
|
|
|
|
|
Actors that never need to post activities can simply not specify any keys at
|
|
|
|
all.
|
|
|
|
|
2019-05-27 13:17:56 +02:00
|
|
|
`GET /users/aviva`
|
2019-05-27 11:06:29 +02:00
|
|
|
|
2019-05-27 13:17:56 +02:00
|
|
|
```json
|
2019-05-27 11:06:29 +02:00
|
|
|
{ "@context":
|
|
|
|
[ "https://www.w3.org/ns/activitystreams"
|
|
|
|
, "https://w3id.org/security/v1"
|
|
|
|
]
|
2019-05-27 15:46:38 +02:00
|
|
|
, "id": "https://example.dev/users/aviva"
|
2019-05-27 11:06:29 +02:00
|
|
|
, "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"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
#### (3) Server-scope actor key
|
|
|
|
|
2019-05-28 00:22:52 +02:00
|
|
|
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)
|
|
|
|
|
2019-05-27 11:06:29 +02:00
|
|
|
#### (4) Actor key expiration and revocation
|
|
|
|
|
|
|
|
#### (5) Ed25519 actor keys
|
|
|
|
|
|
|
|
#### (6) Key rotation using a pair of server-scope keys
|
|
|
|
|
|
|
|
### (B) ActivityPub
|
|
|
|
|
|
|
|
#### (1) Non-actor audience
|
|
|
|
|
|
|
|
#### (2) Authenticated inbox forwarding
|
|
|
|
|
|
|
|
#### (3) Non-announced following
|
|
|
|
|
|
|
|
#### (4) Object nesting depth
|
|
|
|
|
|
|
|
#### (5) Object capability authorization tokens
|
|
|
|
|
|
|
|
### (C) ForgeFed
|
|
|
|
|
|
|
|
#### (1) Actors
|
|
|
|
|
|
|
|
#### (2) Authorization and roles
|
|
|
|
|
|
|
|
#### (3) Comments
|
|
|
|
|
|
|
|
#### (4) Tickets
|
|
|
|
|
|
|
|
#### (5) Patches
|
|
|
|
|
|
|
|
#### (6) Merge requests
|
|
|
|
|
|
|
|
#### (7) Commits
|
|
|
|
|
|
|
|
#### (8) Forks
|
|
|
|
|
|
|
|
#### (9) SSH keys
|
|
|
|
|
|
|
|
#### (10) Pushes
|
|
|
|
|
|
|
|
#### (11) Avatars
|