Vervis/API.md

12 KiB

This document describes the Client-to-Server API of Vervis. If you're developing a frontend application, or a forge search engine, or anything else that wants to interact with Vervis instances, and wondering what language Vervis speaks, this is the document for you.

I suppose in the future it should sit on a Docusaurus website or something like that. For now it's here.

Public Browsing

The /browse page lists the public actors and resource hosted on the server. However there's currently no AP version of that page.

Server Information

NodeInfo isn't implemented yet.

Registration and Authorization

Creating an account on a Vervis instance allows to:

  1. Create and manipulate resources (such as projects, repositories, teams)
  2. View non-public information

Register the application

Register client

Send a POST request to the /oauth/apps endpoint:

curl -X POST \
    -F 'client_name=Anvil' \
    -F 'redirect_uris=urn:ietf:wg:oauth:2.0:oob' \
    -F 'scopes=read' \
    -F 'website=https://anvil.forgefed.org' \
    -F 'repository=https://codeberg.org/Anvil/Anvil' \
    https://vervis.example/oauth/apps
  • redirect_uris currently supports specifying only one URI, and there are 2 options:
    • The special value urn:ietf:wg:oauth:2.0:oob, which means out-of-band: In this case, Vervis will respond with an HTML page containing a token that needs to be manually copied
    • An HTTPS URI: In this case, Vervis will redirect to the given URI
  • The only supported scope is currently read, and despite the misleading name, it allows all API operations

The response, upon success, is a JSON object with 2 text fields:

  • client_id
  • client_secret

Keep these stored for future use.

Obtain an Application Access Token

Send a POST request to the /oauth/token endpoint:

curl -X POST \
    -F 'client_id=your_client_id_here' \
    -F 'client_secret=your_client_secret_here' \
    -F 'redirect_uri=urn:ietf:wg:oauth:2.0:oob' \
    -F 'grant_type=client_credentials' \
    https://vervis.example/oauth/token
  • redirect_uri must be the one defined when registering the application

The response is a JSON object with:

  • access_token: Text, keep for later use
  • token_type: Text, always "Bearer"
  • scope: Text, always "read"
  • created_at: Integer, time as POSIX seconds

Verify the Application Access Token

You can verify the token works by sending a GET request to the /oauth/verify_credentials endpoint:

curl \
    -H 'Authorization: Bearer our_access_token_here' \
    https://vervis.example/oauth/verify_credentials

Register an Account

This section isn't implemented yet. I'm about to implement it. Putting the API here for review while I'm coding.

Check if registration is enabled

Send a GET request to the /register/enabled endpoint. A 2xx response indicates it's enabled, otherwise it's disabled.

Check username availability

Send a GET request to the /register/available endpoint:

curl -X GET \
    -H 'Authorization: Bearer our_application_access_token_here' \
    -F 'username=alice' \
    https://vervis.example/register/available

A 2xx response indicates the username is available.

Create a new account

Send a POST request to the /register endpoint:

curl -X POST \
    -H 'Authorization: Bearer our_application_access_token_here' \
    -F 'username=alice' \
    -F 'passphrase=R6GQJ9HqLtRQ58' \
    -F 'email=alice@email.example' \
    https://vervis.example/register

A 2xx response indicates the account has been created. A JSON object is returned, with a boolean email_sent field. If true, a verification email has been sent to the specified email address. If false, it means email verification is disabled on this server, and the account is ready to be used.

Verify account

The email contains a token, which you can send via a POST request to the /register/verify endpoint, in order to verify and enable the account:

curl -X POST \
    -H 'Authorization: Bearer our_application_access_token_here' \
    -F 'username=alice' \
    -F 'token=pRiW8ayeuN7UBW4qAKg9qRBE0DUVCIof' \
    https://vervis.example/register/verify

A 2xx response indicates successful verification.

Log in as Existing User

Obtain authorization code

In a browser, send a GET request to the /oauth/authorize endpoint:

https://vervis.example/oauth/authorize
?client_id=CLIENT_ID
&scope=read
&redirect_uri=urn:ietf:wg:oauth:2.0:oob
&response_type=code
  • If redirect_uri is the special one as in the example above, the response will be an HTML page containing the authorization code.
  • Otherwise, Vervis will redirect to the redirect_uri, specifying the authorization code as a query parameter named "code":

redirect_uri?code=qDFUEaYrRK5c-HNmTCJbAzazwLRInJ7VHFat0wcMgCU

Obtain a User Access Token

Now that we have the code, send a POST request to the /oauth/token endpoint (which we previously used when registering the application):

curl -X POST \
    -F 'client_id=your_client_id_here' \
    -F 'client_secret=your_client_secret_here' \
    -F 'redirect_uri=urn:ietf:wg:oauth:2.0:oob' \
    -F 'grant_type=authorization_code' \
    -F 'code=user_authzcode_here' \
    -F 'scope=read' \
    https://vervis.example/oauth/token

The response is a JSON object with:

  • access_token: Text, keep for later use
  • token_type: Text, always "Bearer"
  • scope: Text, always "read"
  • created_at: Integer, time as POSIX seconds

Verify the User Access Token & Obtain Actor Object

You can verify the token works by sending a GET request to the /oauth/verify_credentials endpoint:

curl \
    -H 'Authorization: Bearer our_access_token_here' \
    https://vervis.example/oauth/verify_credentials

The response is a JSON object that has a url field, whose value is the HTTPS URI of the user's Person ActivityPub JSON object.

Perform Authorized Requests

You can now use the access token via the Authorization header, as in the curl example above.

  • For GET requests, it allows to obtain non-public data
  • For POST requests, it allows to publish ActivityPub activities via the user outbox

Getting ActivityPub objects

You can obtain an ActivityPub object by sending GET request to its id URI, with Content-Type being application/ld+json; profile="https://www.w3.org/ns/activitystreams. Unless you've been given such a URI, the starting points for discovering objects are:

  • Public browsing page (which doesn't yet have an AP representation)
  • The user Person object, whose URI can be obtained from /oauth/verify_credentials as described above

The Person Object

{
    "id": "https://fig.fr33domlover.site/people/vDxKn",
    "type": "Person",
    "preferredUsername": "perelev",
    "summary": "Cool person who makes cool stuff",
    "inbox": "https://fig.fr33domlover.site/people/vDxKn/inbox",
    "outbox": "https://fig.fr33domlover.site/people/vDxKn/outbox",
    "followers": "https://fig.fr33domlover.site/people/vDxKn/followers",
    "following": "https://fig.fr33domlover.site/people/vDxKn/following",
    "sshKey": [
        "https://fig.fr33domlover.site/people/vDxKn/ssh-keys/Pn9Yn"
    ]
}

Receiving Messages

Requests and event notifications are received as ActivityPub Activity objects in the Person's inbox collection. Currently push notifications aren't implemented, so client applications need to periodically GET the collection and detect whether new items have appeared at the top. The inbox is a (typically paged) reverse-chronologically OrderedCollection of Activity objects, as described in the ActivityPub specification.

The ForgeFed specification has a relevant detailed section:

https://forgefed.org/spec/#s2s

Common properties

These would appear in every activity:

  • id: ID URI of the activity
  • type: One of the ActivityPub or ForgeFed activity types
  • actor: ID URI of the actor who published this activity

These would appear in some activities:

  • capability: ID URI of a Grant activity serving as authorization
  • fulfills: If the activity was published by an automated process rather than human command, these are ID URI(s) of the activities, to which the automated process was reacting
{
    "id": "https://fig.fr33domlover.site/decks/W058b/outbox/nV34D",
    "type": "Accept",
    "actor": "https://fig.fr33domlover.site/decks/W058b",
    "fulfills": [
        "https://grape.fr33domlover.site/people/WZpnG/outbox/GQvnR"
    ],
    "object": "https://grape.fr33domlover.site/people/WZpnG/outbox/GQvnR"
}

Accept

The actor has accepted/approved some activity.

Always:

  • object: URI of the activity being accepted

Sometimes:

  • result: URI of some new entity created as a result of accepting the object

Add

The actor has requested to add some actor to an authorization-related collection. The cases are everything except adding direct collaborators (which use an Invite activity instead):

  • Add a componet to a project
  • Add a component/project to a team
  • Create a parent-child link between a pair of projects/teams

Always:

  • object: URI of object being added
  • target: URI of the collection
  • instrument: Maximal role (see Role type in ForgeFed spec) of authorizations that will be passed through this newly formed link

To determine the case, grab the target collection's owning actor, pointed via the collection's context field. Now you can example the target and object actor types, as well as which field of the target actor specifies the collection:

  • components of a Project
  • context (i.e. projects) of a component
  • subteams of a Team
  • context (i.e. parents) of a Team
  • subprojects of a Project
  • context (i.e. parents) of a Project
  • teams of a component/Project
  • teamResources of a Team

The Add activity is usually just the first step in a sequence of activities that create the desired authorization link. The activity sequences are described in detail in the specification, e.g. in these sections:

Apply

Create

An actor has published a new object/resource, specified in the object field:

  • A comment on an issue/PR (Note)
  • An issue (Ticket)
  • A component (TicketTracker, PatchTracker, Repository)
  • Team
  • Project

Follow

The actor has requested to follow the actor/object specified by the object field.

Grant

See https://forgefed.org/spec/#managing-access.

Invite

https://forgefed.org/spec/#Invite

Except target specifies the collection, not the resource itself. For a Team, that would be the URI of its members collection. For other actor types, it would be the URI of the collaborators collection.

Join

https://forgefed.org/spec/#Join

Except object specifies the collection, not the resource itself. For a Team, that would be the URI of its members collection. For other actor types, it would be the URI of the collaborators collection.

Offer

Push

https://forgefed.org/spec/#pushing

Reject

Remove

Resolve

Revoke

Undo

Publishing and Manipulating Objects

All object manipulation in Vervis is done using the ActivityPub C2S API, i.e. by POSTing Activity objects to the user's outbox.

To determine the outbox URI, you can HTTP GET the Person object as mentioned above, and grab the URI specified by its outbox field.

Common properties

There are properties you'd often specify in the Activity object, that aren't specific to any activity type.

  • type: Activity
  • actor: URI of the person actor
  • capability: URI of a Grant activity you've received, as authorization for the action you're requesting
  • to, cc, bto, bcc: Audience, i.e. list of URIs of actors and actor collections

Accept

Add

Create

Follow

Invite

Join

Offer

Remove

Resolve

Undo