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:
- Create and manipulate resources (such as projects, repositories, teams)
- 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 special value
- 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 usetoken_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 usetoken_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 activitytype
: One of the ActivityPub or ForgeFed activity typesactor
: ID URI of the actor who published this activity
These would appear in some activities:
capability
: ID URI of aGrant
activity serving as authorizationfulfills
: 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 theobject
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 addedtarget
: URI of the collectioninstrument
: Maximal role (seeRole
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 aProject
context
(i.e. projects) of a componentsubteams
of aTeam
context
(i.e. parents) of aTeam
subprojects
of aProject
context
(i.e. parents) of aProject
teams
of a component/Project
teamResources
of aTeam
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:
- https://forgefed.org/spec/#associating-projects-and-components
- https://forgefed.org/spec/#adding-and-removing-children-and-parents-to-projects-and-teams
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 actorcapability
: URI of aGrant
activity you've received, as authorization for the action you're requestingto
,cc
,bto
,bcc
: Audience, i.e. list of URIs of actors and actor collections