forked from bovine/bovine
Fork 0
Modular ActivityPub/Fediverse Server
Go to file
Helge c31b67674e
ci/woodpecker/push/test Pipeline was successful Details
Repair woodpecker
2023-11-28 18:38:27 +01:00
.woodpecker Finish implementing FEP-8b32 2023-11-28 17:53:48 +01:00
bovine Repair woodpecker 2023-11-28 18:38:27 +01:00
bovine_herd Update dependencies 2023-11-27 21:46:07 +01:00
bovine_process Update dependencies 2023-11-27 21:46:07 +01:00
bovine_pubsub Update dependencies 2023-11-27 21:46:07 +01:00
bovine_store Update dependencies 2023-11-27 21:46:07 +01:00
bovine_tool Update dependencies 2023-11-27 21:46:07 +01:00
bovine_web Update dependencies 2023-11-27 21:46:07 +01:00
ci Migrate propan -> faststream 2023-10-03 19:07:14 +02:00
http_tests Implement FEP-2677 2023-10-20 12:29:47 +02:00
tests Update dependencies 2023-11-27 21:46:07 +01:00
.gitignore Fix test 2023-06-18 10:06:03 +02:00
LICENSE Initial commit 2023-01-17 15:00:46 +01:00 Version 0.5.3 2023-11-19 20:19:15 +01:00 Finish implementing FEP-8b32 2023-11-28 17:53:48 +01:00 Version 0.5.3 2023-11-19 20:19:15 +01:00
docker-compose.yml Add some caching for actors 2023-10-19 09:33:41 +02:00 Update dependencies 2023-11-27 21:46:07 +01:00


Bovine is meant to be a modular FediVerse server that supports ActivityPub. During its history, as how to split the tasks between different modules has become clearer, and the split is not done yet. A lot of the changes are due to the realization that parts can be split away as an ActivityPub Client. Here an ActivityPub Client is an application that communicates with just a single ActivityPub Server.

There are a few more structural comments to make about ActivityPub. One job of a server should be to perform basic validation and sanitation for data it passes to clients (or receives for clients). Doing this for ActivityPub has some consequences:

  • Objects are represented by json-ld with an id.
  • If these objects are sanitized and validated by the server, this means that the object changes.
  • Thus there are two representation: 1. outside (as seen in the Fediverse), 2. inside as passed to clients.

This is not ideal, due to an object with an id now representing two different things. It unfortunately seems unavoidable.

Parts of bovine

When done, these parts should only depend on the parts above them. So bovine_process can be used with only bovine and bovine_store. Furthermore, bovine_store and bovine_process should just provide simple interfaces that can be swapped out.

  • bovine (pypi, docs) provides the basic ActivityPub communication with some form of authorization. Currently, HTTP Signatures and Moo-Auth-1 are supported.

  • bovine_store (pypi, docs)is used to store json-ld by their id and provide a certain level of visibility and access control.

    • Serves requests directly represented by objects
    • Contains the actor storage code, and methods to manage actors
    • Tested with both PostgreSQL and sqlite3 databases
  • bovine_process (pypi) contains the Activity processing described in ActivityPub.

    • Contains tests for the side effects of activities
    • Supported: follow, accept, create, update, delete
  • bovine_pubsub (pypi) allows events to be propagated to subscribers. Two variants exist

    • Using asyncio.Queue in the case of a single worker
    • Using Redis for multiple workers. Enabled through setting the environment variable BOVINE_REDIS to the URI of the redis server.
  • bovine_herd (pypi) the FediVerse server implementation. This contains

    • Webfinger, nodeinfo
    • Requests to endpoints not directly represented by objects, i.e. POST inbox
    • Glue code to make everything work together
  • bovine_tool (pypi) basic tools to administer actors in the bovine_store.

  • tests contains a test suite that ensures that bovine_herd properly implements a FediVerse server

Existing ActivityPub Clients

  • mechanical_bull automatically accepts follow requests. This was part of the evolution of my thinking how to separate out ActivityPub Clients.
  • longhorn is a blog. One can visit mine at
  • calf provides a simple terminal user interface for the inbox. This might get expanded into a full client in the future.

Running tests

Using docker, one can run tests in various environments. For this

docker compose run main ./ 
docker compose run main_sqlite ./ 

one can run tests in various configurations. main runs tests using postgres and python 3.11, main_sqlite runs tests using sqlite3 and python 3.11.

To run tests with rabbitmq, use:

docker compose run --env BOVINE_AMQP=amqp://rabbitmq main ./ 

Note, the processor container reads from postgres, so this is incompatible with the sqlite container.

Running tests against python 3.12

This starts with a python 3.12 alpine image

docker compose run main_12 /bin/sh
pip install poetry
apk update
apk add gcc
apk add musl-dev


Building a new release of bovine requires:

  • Updating the version in the individual pyproject.toml files via
  • Merging the change into the main branch
  • Adding a tag

The rest of the work should be done by the woodpecker CI.