Introduction

Kitsune is a decentralised microblogging platform built on the ActivityPub protocol.
This means that anyone can run their own Kitsune on their own hardware and seamlessly interact with each other.

Of course you can also sign up on an existing instance to try it out, no strings attached.
If you are interested in running your own instance, head over to our installation guide.

Chat

Feel free to join our chat if you have any problems or if you want to contribute.
We have channels on Matrix and Discord which are bridged with each other, so join whichever you are more comfortable with.

Matrix Discord

(The side-panel links to the Matrix room since Matrix is the more open and privacy-respecting solution out of the both)

Installation

At the moment, the only way to install Kitsune is to compile it from source.
Don't worry, that sounds way more scary than it actually is. In this guide we will step you through it.

Dependencies

In order to build Kitsune, need a few dependencies. These are:

  1. The Rust toolchain (recommended installation)
  2. PostgreSQL as a dedicated DBMS
  3. Redis for the job queue
  4. NodeJS v16+
  5. Yarn
  6. Reverse Proxy (recommended: Caddy)

Yes, that's really it. We don't need more. Kitsune is designed to use as few native dependencies as possible to make building from source easy!

Note on C/C++ compiler requirement

Rust needs a C/C++ compiler to invoke the linker and potentially build some native libraries to statically link to, so make sure you have one installed.

To install it under Debian and Ubuntu, run the following command:

sudo apt install build-essential

The name of the package(s) containing these tools might differ between distributions.

Building

First, grab yourself a copy of the source code. You can either download the main branch ZIP archive or clone it via Git. Both ways will work just fine.

The recommended way to download the source is via Git since it makes updating and doing a rollback way easier.

To download a Git repository, just run the following command (this assumes you have Git installed):

git clone https://github.com/kitsune-soc/kitsune.git

If you downloaded the ZIP, just unzip the archive

Next, move into the newly created directory and then into the kitsune-fe directory and build the frontend. To do this run:

yarn install && yarn build

Now move out of the directory and back into the main one. Then build the binaries in release mode.
To do this run the following command:

cargo build --release

After the command finished there should be the following three binaries inside the target/release directory:

  • kitsune: The main Kitsune application. This is the heart of the whole application.
  • kitsune-cli: The Kitsune CLI utility. Used to give users roles and clear the job scheduler table.
  • kitsune-job-runner: The dedicated Kitsune job runner

That's it for the building part but before we can actually run our instance, we need to configure it first.

Basic configuration

Kitsune is using the TOML configuration format to configure the main application and the job runner.
The syntax itself is easy to grasp and is essentially and extended INI format.

The auxiliary services/CLI tools are using environment variables at the moment. Note that this might change in the future.

Example configurations for external programs can be found in kitsune/contrib.

The example config for Kitsune can be found in the root directory titled "config.example.toml", move it wherever you like, and feel free to rename it.

The simplest Kitsune configuration looks like this:

[cache]
type = "in-memory"

[database]
url = "postgres://localhost/kitsune"
max-connections = 20

[instance]
allow-non-ascii-usernames = false
name = "Kitsune"
description = "https://www.youtube.com/watch?v=6lnnPnr_0SU"
character-limit = 5000
registrations-open = true

[instance.federation-filter]
type = "deny"
domains = []

[job-queue]
redis-url = "redis://localhost"
num-workers = 20

[messaging]
type = "in-process"

[server]
frontend-dir = "./kitsune-fe/dist"
max-upload-size = 20242880          # 5MB
media-proxy-enabled = false
port = 5000
request-timeout-secs = 60

[search]
type = "sql"

[storage]
type = "fs"
upload-dir = "./uploads"

[url]
scheme = "https"
domain = "kitsune.example.com"

To successfully deploy the application, make sure you at least change the following sections to your own:

  • domain

    • Domain of your instance. Used to build the URLs of your activities.
    • This is a very important setting and cannot be changed after the first setup.
  • database

    • Specifically the url parameter. Refer to database section page for the expected format.
  • job-queue

    • Specifically the redis-url parameter. Refer to the job scheduler page for the expected format.

To run the application use the following command:

./kitsune -c [path-to-config-file]

In order to read up on all the possible configurations, check out the "Configuration" section.

Cache

Computing things from scratch can be pretty expensive, that's where caching comes in.
To best fit for your specific setup, Kitsune has multiple caching backends:

No Cache

[cache]
type = "none"

This is the simplest of all caching modes. It just doesn't cache anything at all and utilises Kitsune's no-op cache. Pretty much only desirable if you are debugging other caches for invalidation issues (or if you have heavy memory constraints and no way to get your hands on a Redis instance).

In-Memory Cache

[cache]
type = "in-memory"

This tells Kitsune to cache data directly into its memory. The cache is bounded so it doesn't make you run out of memory.

Redis Cache

[cache]
type = "redis"
redis-url = "redis://[Your Redis instance]"

This tells Kitsune to cache data via expiring keys into the configured Redis instance.
This is the optimal configuration for setups where you have multiple Kitsune nodes running at the same time.

Captcha

Kitsune offers the ability to require captchas on sign-up to protect your service against spam waves.

We offer different implementations to fit your specific needs

hCaptcha

The rather well-known hCaptcha service advertises itself as a more privacy-oriented alternative to Google's reCaptcha.

To use it to protect your instance, add the following to your configuration:

[captcha]
type = "hcaptcha"
verify-url = "[Verify URL]"
site-key = "[Your site key]"
secret-key = "[Your secret key]"

mCaptcha

mCaptcha is a lesser known open-source self-hostable captcha service.
Technically it isn't a "captcha" and more of a "proof-of-work verification system", but it should defend your service against large spam attacks.

To use mCaptcha, add the following to your configuration:

[captcha]
type = "mcaptcha"
widget-link = "[Widget link]"
site-key = "[Your site key]"
secret-key = "[Your secret key]"
verify-url = "[Verify URL]"

Clacks Overhead

Clacks Overhead is a non-standard HTTP header used for as something like a silent memorial.
The header is appended to each response via a middleware.

The header looks something like this:

X-Clacks-Overhead: GNU [Name 1 here], [Name 2 here]

More info about this header


The names for the header can be configured like so:

[server]
clacks-overhead = [
    "Natalie Nguyen",
    "John \"Soap\" MacTavish"
]

Database

Kitsune requires a PostgreSQL installation that it can connect to since we make usage of Postgres-specific features, such as their full-text search.

You can find instructions on creating a database (along with password-protected user) here.

We supported SQLite in the past (before v0.0.1-pre.1), but the support has been dropped due to a high maintenance burden and rather little expected usage.

Database URL structure

postgres://[Username]:[Password]@[DBMS host]:[Port]/[Database name]

Example URL

postgres://database-user:password-here@localhost:5432/db-name-here

Maximum connections

The max-connections setting defines how many connections the globally shared connection pool will open to the database server at maximum.
What you should set this value to depends on many factors.

TLS support

If you want to connect to a database using TLS, set the parameter use-tls to true.
This setting is equivalent to ssl_mode=full-verify if you are looking for a PostgreSQL equivalent.

Example

[database]
url = "postgres://kitsune:verysecure@localhost/kitsune_prod"
max-connections = 25
use-tls = true

Deny Brave Browsers

Brave is a company founded by a queerphobic bigot, funded by people such as Peter Thiel. You can get pretty much all of Brave's "privacy advantages" with a Firefox + uBlock Origin + DuckDuckGo installation.

(Plus, Brave is another Chromium-based browsers. Using Firefox helps diversify the browser landscape at least a little bit)

When this setting is enabled, all browsers with the Brave User-Agent are redirected to an article explaining the hateful and problematic background of Brave.

Since Kitsune is about choice, we give you the ability to simply toggle this functionality off.
While we give you that option, you maybe want to keep this option on.

The reasoning behind this is simple:

  • If people aren't aware and care, they can switch browsers. Switching a browser isn't a herculean task.
  • If people are aware and don't care, I'm not sure if you want them on your service.
[server]
deny-brave-browsers = true

Emailing

Configuring an Email server for Kitsune automatically enables account confirmation via Email.

The mailer currently only supports SMTP, no provider-specific REST APIs.

Example

[email]
from-address = "kitsunemailer@joinkitsune.org"
host = "your.smtp.host"
username = "admin"
password = "password"
starttls = false

There is also an option config you can place in front of "from_address" if your email service provider does not do TLS over 465 and instead uses 587 (STARTTLS).

Here is an example configuration utilizing STARTTLS:

[email]
from_address = "kitsunemailer@joinkitsune.org"
host = "your.smtp-host.example"
username = "admin"
password = "password"
starttls = true

Parameters

starttls :

By default Kitsune users the port 465 to send mail.

Most service providers use this port, but some (for example Postmark) need to have their TLS usage bootstrapped via STARTTLS over port 587.

from-address :

This is the address Kitsune puts into the From field of the Email

host :

This is the domain that your SMTP server is reachable under.

username, password :

The credentials to your SMTP server. Which values to put here vary from provider to provider.

Federation filter

Kitsune has a basic federation filter. It has two main modes of operation:

  • Allowlist-based
  • Denylist-based

The domain list supports globbing, so can, among other things, define wildcard blocks.

Allowlist

As the name might suggests, this mode allows instance administrators to define a set list of domains the server is allowed to interact with.
Federation attempts from any other server are rejected.

Configuration example

[instance.federation-filter]
type = "allow" 
domains = ["*.myfriends.com", "cool-instan.ce"]

Denylist

This is the opposite of the allowlist-based federation. In this mode, Kitsune generally accepts federation attempts from any instance except the ones defined in the domain list.

Configuration example

[instance.federation-filter]
type = "deny"
domains = ["*.badstuff.com", "mean-people.biz"]

Instance

Kitsune has a number of configurations that change how your instance works.

[instance]
allow-non-ascii-usernames = false
name = "Kitsune"
description = "https://www.youtube.com/watch?v=6lnnPnr_0SU"
character-limit = 5000
registrations-open = true

allow-non-ascii-usernames

Kitsune's database schema allows us to store usernames in a way that:

  1. Ignores the case
  2. Ignores the accent markers, etc.

That way we can store usernames in the database that aren't strictly ASCII letters, meaning you could sign up using the username äumeträ.
And it would also automatically reserve any mutations of your username.

For example, getting the username äumeträ would automatically reserve these usernames, too:

  • AUMETRA
  • Äumetra
  • áumetrà

and so on..

Note: This doesn't allow you to sign up with emoji, such as 🎈. It will still require your name to be alphanumeric, just in any alphabet that unicode supports!

This is opt-in, since we aren't quite sure yet how other fediverse software, such as Mastodon, handles non-ASCII usernames.

name

This changes the name of the instance displayed on the landing page and returned via instance metadata endpoints (such as Mastodon's /api/v1/instance).

description

Similar to name, this setting adjusts the description on the landing page and the one returned via the metadata endpoints.

Note: This field is interpreted as raw HTML

character-limit

This setting sets the character limit specific to your instance.

registrations-open

Determines whether your instance accepts new users or not. When set to false, the registration APIs will return a failure code.

webfinger-domain

This enables you to host your .well-known/webfinger resource on your main domain (i.e. example.com) and the web UI and inboxes on a subdomain (i.e. kitsune.example.com).
The advantage of this configuration is that your handle can be @me@example.com, while the account is hosted on fedi.example.com.

Example value

webfinger-domain = "example.com"

Job Scheduler

Kitsune uses the database to store and retrieve jobs that have to be run. There are options to tune the job scheduler to your specific needs.

[job-queue]
redis-url = "redis://localhost"
num-workers = 20

redis-url

This option configures the Redis instance that the jobs are put on.

We utilize Redis streams and transactional Lua scripting to ensure no jobs are lost even in the case of a crash.

job-workers

This option configures how many jobs are run concurrently at the same time.

Each job is a lightweight task inside Kitsune's async runtime, so you can raise this well above what you'd raise a thread limit to.

Caution: Each activity delivery is a job, and each of the delivery jobs run multiple connections concurrently. Raising this job worker limit too high without also increasing the maximum file descriptors might lead to weird issues.

Language detection

In order to classify posts better, Kitsune attempts to automatically guess the language a post is written in to improve the search experience by using language-specific tokenization.

It can do that through a number of language detection backends.
The currently supported backends are:

  • none: Use no detection and just return the default language
  • whatlang: Use the whatlang library (recommended)
  • whichlang: Use the whichlang library

Note on the backends

In general you should prefer the whatlang backend as it offers reliability calculations and covers a wide range of languages.

whichlang is generally faster than whatlang but has less supported languages and doesn't offer any reliability calculations, meaning the classifications might be way off and won't ever fall back on the default language.

You probably don't want to use the none backend unless you are 100% confident that the language detection is too resource intensive for your installation (which is extremely unlikely!)

Configuration

backend

In order to set the backend, choose one of the above mentioned supported backends.
It is configured like so:

[language-detection]
backend = "whatlang" # Use "whatlang" to detect the language
default-language = "en"

default-language

This setting sets the default language Kitsune falls back onto.
If the language couldn't be guessed for whatever reason (be it an internal failure of the backend or because the reliability of the guess wasn't given), this is the language Kitsune returns.

You might want to adjust this if you know the language that will be predominantly posted in on your instance to improve search quality, especially on shorter posts.

The setting accepts any valid ISO 639-1 or 639-3 code.

ISO 639-1:

[language-detection]
backend = "whatlang"
default-language = "de" # Use German to fall back on by referencing its ISO 639-1 value

ISO 639-3:

[language-detection]
backend = "whatlang"
default-language = "kor" # Use Korean to fall back on by referencing its ISO 639-3 value

Link embedding

Kitsune has the ability to show link previews as so-called "embed cards".
We use Lantern Chat's embed-service to provide this functionality.

To use it with Kitsune, run the embed-service and add the following configuration to Kitsune's configuration file:

[embed]
service-url = "[URL to the embed service]"

MRF (Message Rewrite Facility)

The idea of a message rewrite facility was originally popularized by Pleroma/Akkoma.
Essentially it enables you to add small filters/transformers into your ActivityPub federation.

The MRF module sits at the very beginning of processing an incoming activity.
In this position, the MRF can transform and reject activities based on criteria defined by the developers of the module.

For example, you can use it to:

  • detect spam
  • mark media attachments as sensitive
  • nya-ify every incoming post

Configuration

module-dir

This configuration option tells Kitsune where to scan for WASM modules to load and compile.

[mrf]
module-dir = "mrf-modules"

artifact-cache

This configuration option tells Kitsune to cache compiled versions of the WASM components on-disk to improve startup times by not having to recompile the components every time Kitsune starts up.

Simply omitting this configuration disables the caching.

[mrf.artifact-cache]
path = "./artifact-cache"

Note: DO NOT modify the files inside the artifact cache. You can delete the cache to reclaim space, but DO NOT modify the files. The files inside the cache are treated by Kitsune as trusted executables. Modifying them may lead to unexpected behaviour and crashes.

storage

Kitsune provides MRF modules with scoped key-value storages to allow them to persist data across runs for things like counters or spam lists.

We provide multiple backends here:

Redis

[mrf.storage]
type = "redis"
url = "redis://localhost"
pool-size = 5

Filesystem

[mrf.storage]
type = "fs"
path = "./mrf-storage"

module-config

WASM MRFs can have configuration passed to them upon invocation. In Kitsune you configure them via key-value pairs inside your configuration as follows:

[mrf.module-config]
module-name = "my configuration"

The names that are used to select the configuration are sourced from the module manifest.

Messaging

Kitsune uses messaging services to exchange events for cache invalidation, notification delivery, etc.
To offer flexibility and to make self-hosting as easy as possible, we have multiple such backends.

Redis backend

This is backend you should choose when running multiple nodes in parallel. This then uses Redis' PubSub feature to exchange messages.

Configuration example

[messaging]
type = "redis"
redis-url = "redis://localhost:6379"

In-process

This backend is optimal for small self-contained installations. It uses Tokio's broadcast channel internally.

Configuration example

[messaging]
type = "in-process"

OIDC (OpenID Connect)

This feature is gated behind the oidc compile-time feature

OpenID Connect (OIDC) is a technology to provide Single Sign-On (SSO) that it shared across multiple services. This is useful if you, for example, want to run Kitsune together with a bunch of other services and don't want to maintain multiple logins.

In order to enable OIDC for your Kitsune instance, find the oidc parameter inside the server configuration section. Set this parameter to the following value:

[server.oidc]
server-url = "[Issuer URL]"
client-id = "[Kitsune's Client ID]"
client-secret = "[Kitsune's Client Secret]"

[server.oidc.store]
type = "in-memory" # "in-memory" or "redis"
#url = "redis://localhost" # If the configured type is "redis"

Server URL

This is the URL of the issuer; this setting differs between OIDC solutions. Don't worry, Kitsune won't start up if this value is invalid.

Client ID and Secret

These values are created on the dashboard of the OIDC solution you are using. Kitsune needs these to obtain an access token from the OIDC server to do introspection and obtain some information about the user.

Store

The store is where the login state is stored in temporarily until the sign-in has completed.

Supported backends

  • in-memory: The state is stored in an in-memory LRU structure
  • redis: The state is stored in a Redis instance

If you run multiple nodes, you should use Redis. Otherwise sign-ins can randomly fail.

Kitsune-specific OIDC requirements

The OIDC server must return the following values in the claim:

  • preferred_username
  • email

Keep the preferred username field unique. The username is used to identify the user inside Kitsune's database that's getting registered.
This might change in the future.

OpenTelemetry

Kitsune can export its traces via the OpenTelemetry Protocol (or OTLP, for short).

To push the data to an endpoint, add the following to your configuration:

[opentelemetry]
# Where Kitsune pushes traces (eg. Jaeger)
tracing-transport = "http" # "http" or "grpc"
tracing-endpoint = "https://localhost:5050/tracing-endpoint"

Search

Kitsune has a number of search backends, each different from the other, to best fit your specific needs. We want to give you a brief overview over the available backends.

[search]
type = "none"

This completely disables the search on your instance. Finding posts and accounts is now only possible via direct links and handles.

[search]
type = "sql"

This runs searches on your database directly. The quality is actually not too bad and uses automatic language detection to make full-text searches relevant.

Meilisearch

[search]
type = "meilisearch"
instance-url = "[URL of your Meilisearch instance]"
api-key = "[API key to access your Meilisearch instance]"

This instructs Kitsune to use Meilisearch as the search engine. Meilisearch provides incredibly fast, high-quality full-text search.
Meilisearch also has a cloud offering, making this the easiest high-quality search to use with Kitsune.

Storage

As a microblogging platform, Kitsune also offers users the ability to attach images, videos, and audio to their posts. We offer multiple different storage backends to store the attachments to.

Note: You might want to increase the upload limit by tweaking the max-upload-size parameter in your configuration; the number is the upload limit in bytes. The default set by the example configuration is 5MiB.

File System

This is recommended for small instances.
Kitsune simply stores the media inside a user-defined directory on the local file system.

In order to enable this, add this to your configuration:

[storage]
type = "fs"
upload-dir = "path/to/upload/directory"

This will then place all the uploads into the specified directory.

S3-compatible storage

When your user count increases (or your requirements change), you might want to consider using an S3-compatible storage solution to store all your media attachments.

In order to make this happen, add this to your configuration:

[storage]
type = "s3"
bucket-name = "[Name of your bucket]"
endpoint-url = "[Name of the endpoint]"
region = "[Region of your S3 storage]"
force-path-style = false
access-key = "[Access key]"
secret-access-key = "[Secret access key]"

Bucket Name

This setting is pretty self-explanatory. It's the name of the bucket you created and want your attachments to be put in.

Endpoint URL

The URL of the S3 endpoint. You can either get this from your storage provider's dashboard or documentation; really depends on the provider.

Region

The S3 region in which you created the bucket. The value of this might be different from provider to provider.

Force Path Style

Some S3 storage providers don't support the virtual-hosted request style. If your provider is one of those, set this setting to true to instruct Kitsune to use the legacy path style.

(Secret) Access Key

These keys are given to you when creating your bucket. Make sure you keep these private!

Notes on "get_object" requests

Currently Kitsune proxies all the S3 accesses, meaning each media access results in a "get object" request.
For example, Cloudflare R2 has a "no egress fee policy" which, due to this implementation detail, doesn't apply to Kitsune.

This is not final, we might change how S3 uploads are handled

Migrating from file-system storage to S3

The migration is pretty simple. Upload all the files from your upload directory into the S3 bucket (while preserving the same file hierarchy) and change the configuration.
Kitsune should then serve the files without any problems.

HTTP signatures

HTTP signatures are used by Kitsune to validate that the Activity/Object actually originates from the user it claims to.
We implement a subset of draft-cavage-http-signatures-12 to do this.

Only asymmetric cryptographic algorithms are implemented since the symmetric ones:

  1. Could lead to potential key-confusion attacks
  2. Aren't useful in the context of ActivityPub

We make use of the keyId field by looking up the public key material via this ID. The ID is sourced from the ActivityPub actor.
The signature scheme used is inferred by the OID embedded in the public key material. The material is expected to be an X.509 SPKI structure.

At the moment, Kitsune uses RSA keys but has support for implementations that use Ed25519 for signatures.

We use RSA because Mastodon doesn't support anything else. So if you want compatibility with Mastodon, you have to use RSA.
As soon as a mainline Mastodon release gets support for Ed25519 signatures, we will release an update that allows to rekey all the users.

But if you are happy to just federate with Kitsune users, you can use Ed25519!

Webfinger

Kitsune uses Webfinger for resolving mentions and such. For example, when mentioning a user in a post in the form @user@instance.org, the mention is interpreted as a Webfinger acct query.
We then connect to the remote server and send an HTTP GET request on the path /.well-known/webfinger with the query parameter resource set to acct:user@instance.org.

The server is then expected to return a Webfinger resource containing a link with the rel property set to self and the href attribute pointing to the ActivityPub actor.

Example Webfinger resource:

{
    "subject": "acct:user@instance.org",
    "aliases": [],
    "links": [
        {
            "rel": "self",
            "href": "https://instance.org/users/user"
        }
    ]
}