Vervis/FEDERATION.md

315 lines
10 KiB
Markdown
Raw Normal View History

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.
* You can see users' outboxes.
* You can see your inbox.
* If you create a project, all comments on all tickets of the project will be
delivered to your inbox.
* 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.
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.
`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.
## 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.
## 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.
`GET /users/aviva/keys/key1`
```json
{ "@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----- ..."
}
```
`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": "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.
`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"
]
}
```
#### (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
#### (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