# microblog.pub

microblog.pub

Build Status #microblog.pub on Matrix License Code style: black

A self-hosted, single-user, ActivityPub powered microblog.

**Still in early development.** ## /!\ Note to adventurer If you are running an instance with Celery/RabbitMQ, you will need to [perform a migration](https://github.com/tsileo/microblog.pub/tree/drop-celery#perform-the-drop-celery-migration). Getting closer to a stable release, it should be the "last" migration. ## Features - Implements a basic [ActivityPub](https://activitypub.rocks/) server (with federation) - Compatible with [Mastodon](https://github.com/tootsuite/mastodon) and others (Pleroma, Hubzilla...) - Also implements a remote follow compatible with Mastodon instances - Exposes your outbox as a basic microblog - Support all content types from the Fediverse (`Note`, `Article`, `Page`, `Video`, `Image`, `Question`...) - Comes with an admin UI with notifications and the stream of people you follow - Allows you to attach files to your notes - Privacy-aware image upload endpoint that strip EXIF meta data before storing the file - No JavaScript, **that's it**. Even the admin UI is pure HTML/CSS - Easy to customize (the theme is written Sass) - mobile-friendly theme - with dark and light version - Microformats aware (exports `h-feed`, `h-entry`, `h-cards`, ...) - Exports RSS/Atom/[JSON](https://jsonfeed.org/) feeds - You stream/timeline is also available in an (authenticated) JSON feed - Comes with a tiny HTTP API to help posting new content and and read your inbox/notifications - Deployable with Docker (Docker compose for everything: dev, test and deployment) - Implements [IndieAuth](https://indieauth.spec.indieweb.org/) endpoints (authorization and token endpoint) - U2F support - You can use your ActivityPub identity to login to other websites/app - Focused on testing - Tested against the [official ActivityPub test suite](https://test.activitypub.rocks/) ([report submitted](https://github.com/w3c/activitypub/issues/308)) - [CI runs "federation" tests against two instances](https://d.a4.io/tsileo/microblog.pub) - Project is running 2 up-to-date instances ([here](https://microblog.pub) and [there](https://a4.io)) - The core ActivityPub code/tests are in [Little Boxes](https://github.com/tsileo/little-boxes) (but needs some cleanup) - Manually tested against [Mastodon](https://github.com/tootsuite/mastodon) and other platforms ## ActivityPub microblog.pub implements an [ActivityPub](http://activitypub.rocks/) server, it implements both the client to server API and the federated server to server API. Activities are verified using HTTP Signatures or by fetching the content on the remote server directly. ## Running your instance ### Installation ```shell $ git clone https://github.com/tsileo/microblog.pub $ cd microblog.pub $ pip install -r requirements.txt $ cp -r config/me.sample.yml config/me.yml ``` ### Configuration ```shell $ make password Password: $2b$12$iW497g... ``` Edit `config/me.yml` to add the above-generated password, like so: ``` username: 'username' name: 'Your Name' icon_url: 'https://you-avatar-url' domain: 'your-domain.tld' summary: 'your summary' https: true pass: $2b$12$iW497g... ``` ### Deployment ```shell $ make update ``` ## Development The most convenient way to hack on microblog.pub is to run the server locally, and run ```shell # One-time setup $ pip install -r requirements.txt # Start MongoDB and poussetaches $ make poussetaches $ env POUSSETACHES_AUTH_KEY="SetAnyPasswordHere" docker-compose -f docker-compose-dev.yml up -d # Run the server locally $ FLASK_DEBUG=1 MICROBLOGPUB_DEBUG=1 FLASK_APP=app.py POUSSETACHES_AUTH_KEY="SetAnyPasswordHere" flask run -p 5005 --with-threads ``` ## API Your admin API key can be found at `config/admin_api_key.key`. ## ActivityPub API ### GET / Returns the actor profile, with links to all the "standard" collections. ### GET /tags/:tag Special collection that reference notes with the given tag. ### GET /stream Special collection that returns the stream/inbox as displayed in the UI. ## User API The user API is used by the admin UI (and requires a CSRF token when used with a regular user session), but it can also be accessed with an API key. All the examples are using [HTTPie](https://httpie.org/). ### POST /api/note/delete{?id} Deletes the given note `id` (the note must from the instance outbox). Answers a **201** (Created) status code. You can pass the `id` via JSON, form data or query argument. #### Example ```shell $ http POST https://microblog.pub/api/note/delete Authorization:'Bearer ' id=http://microblob.pub/outbox//activity ``` #### Response ```json { "activity": "https://microblog.pub/outbox/" } ``` ### POST /api/note/pin{?id} Adds the given note `id` (the note must from the instance outbox) to the featured collection (and pins it on the homepage). Answers a **201** (Created) status code. You can pass the `id` via JSON, form data or query argument. #### Example ```shell $ http POST https://microblog.pub/api/note/pin Authorization:'Bearer ' id=http://microblob.pub/outbox//activity ``` #### Response ```json { "pinned": true } ``` ### POST /api/note/unpin{?id} Removes the given note `id` (the note must from the instance outbox) from the featured collection (and un-pins it). Answers a **201** (Created) status code. You can pass the `id` via JSON, form data or query argument. #### Example ```shell $ http POST https://microblog.pub/api/note/unpin Authorization:'Bearer ' id=http://microblob.pub/outbox//activity ``` #### Response ```json { "pinned": false } ``` ### POST /api/like{?id} Likes the given activity. Answers a **201** (Created) status code. You can pass the `id` via JSON, form data or query argument. #### Example ```shell $ http POST https://microblog.pub/api/like Authorization:'Bearer ' id=http://activity-iri.tld ``` #### Response ```json { "activity": "https://microblog.pub/outbox/" } ``` ### POST /api/boost{?id} Boosts/Announces the given activity. Answers a **201** (Created) status code. You can pass the `id` via JSON, form data or query argument. #### Example ```shell $ http POST https://microblog.pub/api/boost Authorization:'Bearer ' id=http://activity-iri.tld ``` #### Response ```json { "activity": "https://microblog.pub/outbox/" } ``` ### POST /api/block{?actor} Blocks the given actor, all activities from this actor will be dropped after that. Answers a **201** (Created) status code. You can pass the `id` via JSON, form data or query argument. #### Example ```shell $ http POST https://microblog.pub/api/block Authorization:'Bearer ' actor=http://actor-iri.tld/ ``` #### Response ```json { "activity": "https://microblog.pub/outbox/" } ``` ### POST /api/follow{?actor} Follows the given actor. Answers a **201** (Created) status code. You can pass the `id` via JSON, form data or query argument. #### Example ```shell $ http POST https://microblog.pub/api/follow Authorization:'Bearer ' actor=http://actor-iri.tld/ ``` #### Response ```json { "activity": "https://microblog.pub/outbox/" } ``` ### POST /api/new_note{?content,reply} Creates a new note. `reply` is the IRI of the "replied" note if any. Answers a **201** (Created) status code. You can pass the `content` and `reply` via JSON, form data or query argument. #### Example ```shell $ http POST https://microblog.pub/api/new_note Authorization:'Bearer ' content=hello ``` #### Response ```json { "activity": "https://microblog.pub/outbox/" } ``` ### GET /api/stream #### Example ```shell $ http GET https://microblog.pub/api/stream Authorization:'Bearer ' ``` #### Response ```json ``` ## Contributions PRs are welcome, please open an issue to start a discussion before your start any work.