What the heck is an Eludris

Eludris is a FOSS federated, End-To-End-Encrypted Discord x Reddit mesh-like social media platform where the priority is for it to be truly yours.

To that extent Eludris aims to be as easily accessible by everyone, even if they do not understand or care about the unique privacy-related features Eludris offers.

In terms of client Eludris is currently officially providing Pilfer (a lightweight TUI) and Pengin (a web-based cross-platform client).

However the intent has always been and still remains to be for people to make their own clients, toolings, bots, API wrappers and so on.

As such Eludris has tried to convey said support via multiple things, namely the Eludris Community organisation where people can add their own creations and the Eludris Awesome repository, in addition to these docs.

For more info you can check out our Official GitHub organisation or our website.

While you're also at it consider joining our Discord Server since that is currently mainly where we hang out, joke around and work on Eludris and Eludris related stuff.

Reading The Docs

The documentation is straight forward, however here are some additional clarifications oh some of the points where you can get confused.

Omittability And Nullability

While reading the docs you might notice the use of the question marks next to field names or types, here's an example of that.

FieldType
Foo?Integer
BarString?

Now, you may be wondering what these question marks are going there, it depends on where the question mark is, if the question mark is next to the field name like in Foo?, then that field is omittable, meaning that it is not guaranteed to exist, however in a case where it exists it is guaranteed to be an Integer.

On the other hand if the question mark is next to the type like in String? it means that while the field will always be present, its value can be a null.

Oprish

The Eludris REST-api. This is where clients can actually interact in a meaningful way with Eludris, whether it be post messages, request information, etc.

Instance Info

GET /

Get information about the instance you're sending this request to.

Most of this data comes from the instance's configuration.

Returns

The InstanceInfo object for the connected Eludris instance.

Example

curl \
  https://eludris.tooty.xyz
{
  "instance_name": "eludris",
  "description": "The *almost* official Eludris instance - ooliver.\nThis is **not** a testing instance as it is bridged to Eludis. Use your own local instance for testing.",
  "version": "0.3.1",
  "message_limit": 2048,
  "oprish_url": "https://api.eludris.gay",
  "pandemonium_url": "wss://ws.eludris.gay/",
  "effis_url": "https://cdn.eludris.gay",
  "file_size": 20000000,
  "attachment_file_size": 25000000
}

Rate Limits

GET /rate_limits

Get the rate limit info for all of the instance's microservices, those being Oprish, Pandemonium and Effis

Returns

The InstanceRateLimits object for the Eludris instance.

Example

curl \
  https://eludris.tooty.xyz/rate_limits
{
  "oprish": {
    "info": {
      "reset_after": 5,
      "limit": 2
    },
    "message_create": {
      "reset_after": 5,
      "limit": 5
    },
    "rate limits": {
      "reset_after": 5,
      "limit": 2
    }
  },
  "pandemonium": {
    "reset_after": 10,
    "limit": 5
  },
  "effis": {
    "assets": {
      "reset_after": 60,
      "limit": 5,
      "file_size_limit": 30000000
    },
    "attachments": {
      "reset_after": 120,
      "limit": 20,
      "file_size_limit": 100000000
    },
    "fetch_file": {
      "reset_after": 60,
      "limit": 30
    }
  }
}

Create Message

POST /messages

Post a message to your Eludris instance.

Parameters

The Message object to be posted to the Eludris instance.

Note

Message authors must be between 2-32 characters long.

Message content must be at least 1 character long. The upper changes for every instance and hence should be fetched using the instance info / route.

Returns

The Message object that was just sent.

Example

curl \
  --json '{"author":"Not a weeb","content":"Hello, World!"}' \
  https://eludris.tooty.xyz/messages
{
  "author": "Not a weeb",
  "content": "Hello, World!"
}

Pandemonium

The Eludris WebSocket based gateway. This is what clients connect to in order to receive events about what's currently happening on their instance.

Gateway Events

General Structure

Events sent to and received from the gateway should always be of a common format. A field op specifies the opcode of the event, and an optional field d contains that event's data.

FieldTypeDescription
opStringThe opcode of the event. This is a unique identifier in SCREAMING_SNAKE_CASE.
d?ObjectA JSON-serialized object containing further information pertaining the event.

Here're a couple example payloads

/* A payload with no data */
{
  "op": "FOO"
}
/* A couple of payloads with data */
{
  "op": "FOO",
  "d": {
    "bar": "baz"
    }
}
{
  "op": "FOO",
  "d": [
    "bar",
    "baz"
    ]
}

Ping

The gateway expects to receive a ping every 45 seconds. A ping is a simple payload consisting of only an opcode. In response to a ping, the gateway will send back a pong.

FieldTypeDescription
opStringAlways "PING".

Example

{
  "op": "PING"
}

Pong

The response to a connected client's ping payload.

FieldTypeDescription
opStringAlways "PONG".

Example

{
  "op": "PONG"
}

Message Create

An event that is dispatched to all connected clients when a message is received. This event contains the message that triggered the event.

FieldTypeDescription
opStringAlways "MESSAGE_CREATE".
dMessageThe message object that triggered the event.

Example

{
  "op": "MESSAGE_CREATE",
  "d": {
    "author": "A certain woo",
    "content": "Woo!"
  }
}

Pandemonium close codes

All Pandemonium intentional closes have a close code of 1011 the reasons however can provide us with some additional info, here's a list of all the supported reasons:

  • Client connection dead

The client did not ping pandemonium for more than 45 seconds which resulted in a disconnection.

  • Client hit rate limit

The client surpassed their allowed connection and payload send rate limits which lead to them getting disconnected.

  • Server error

A server error has occurred A shrugging pepe

Effis

The Eludris file server & soon to be embed proxy. This is where clients can store and fetch attachments. Furthermore, static asset files set by the instance owner can be fetched.

Uploading files to Effis

Upload file to bucket

POST /<bucket>

Upload a file to Effis under a specific bucket. At the moment, only the attachments bucket is supported.

Parameters

A multipart/form-data body containing the file to be uploaded and whether or not it should be spoiler tagged.

FieldTypeDescriptionDefault
fileFileThe file to be uploaded.
spoiler?BooleanWhether or not this file should be spoiler tagged.false

Returns

A FileData object containing information about the file as processed by Effis. This includes helpful parameters such as the file's id, which can then be used to fetch the file from Effis.

Upload attachment

POST /

This route acts as a shorthand for POST /attachments/, the same logic applies.

Example

curl \
  -F "file=@kaas.png" \
  -F "spoiler=true" \
  https://effis.tooty.xyz/
{
  "id": "370617076959762069670199317",
  "name": "kaas",
  "bucket": "attachments",
  "spoiler": true,
  "metadata": {
    "type": "image",
    "width": 128,
    "height": 128
  }
}

Fetching files from Effis

Fetch file from bucket

GET /<bucket>/<id>

Fetch the file with the provided id from the provided bucket.

Returns

The file with the provided id from the provided bucket. This is returned with header Content-Disposition: inline set.

Fetch attachment

GET /<id>

This route acts as a shorthand for GET /attachments/<id>, the same logic applies.

Fetching file data from Effis

Fetch file data from bucket

GET /<bucket>/<id>/data

Fetch the file data of the file with the provided id from the provided bucket.

Returns

The FileData object of the file with the provided id from the provided bucket.

Fetch attachment file data

GET /<id>/data

This route acts as a shorthand for GET /attachments/<id>/data, the same logic applies.

Example

curl \
  https://effis.tooty.xyz/370617076959762069670199317/data
{
  "id": "370617076959762069670199317",
  "name": "kaas",
  "bucket": "attachments",
  "spoiler": true,
  "metadata": {
    "type": "image",
    "width": 128,
    "height": 128
  }
}

Downloading an Effis file

Download file from bucket

GET /<bucket>/<id>/download

Download the file with the provided id from the provided bucket.

Returns

The file with the provided id from the provided bucket. This is returned with header Content-Disposition: attachment set.

Download attachment

GET /<id>/download

This route acts as a shorthand for GET /attachments/<id>/download, the same logic applies.

Dealing with static Effis files

Fetch static file

GET /static/<name>

Fetch the static file with the provided name. Static files are added by the instance owner and cannot be externally modified.

Returns

The static file with the provided name. This is returned with header Content-Disposition: inline set.

Download static file

GET /static/<name>/download

Download the static file with the provided name.

Returns

The static file with the provided name. This is returned with header Content-Disposition: attachment set.

Models

Documentation for all the models present in the Eludris API.

Errors

All error messages in Eludris come in one format as follows:

FieldTypeDescription
statusNumberThe status code of the error.
messageStringA message which explains the error.
data?ObjectThe error's associated data.

Example

/* An error without data */
{
  "status": 404,
  "message": "The requested resource cannot be found"
}
/* An error with data */
{
  "status": 429,
  "message": "You have been rate limited",
  "data": {
      "retry_after": 42069,
    }
}

RateLimitedError

The error raised when the client is rate limited.

FieldTypeDescription
retry_afterNumberThe number of milliseconds the client should wait before making new requests.

Example

{
  "status": 429,
  "message": "You have been rate limited",
  "data": {
    "retry_after": 42069
  }
}

FileSizeRateLimitedError

The error raised when the client surpasses the maximum amount of bytes and is rate limited.

FieldTypeDescription
retry_afterNumberThe number of milliseconds the client should wait before making new requests.
bytes_leftNumberThe number of bytes the client has available until the rate limit resets.

Example

{
  "status": 429,
  "message": "You have surpassed your file size limit",
  "data": {
    "retry_after": 42069,
    "bytes_left": 1337
  }
}

ValidationError

The error raised when the supplied request body is invalid.

FieldTypeDescription
field_nameStringThe name of the field that raised the error.
errorStringThe error that occurred.

Example

{
  "status": 422,
  "message": "Invalid request body",
  "data": {
    "field_name": "content",
    "error": "I disagree with your opinion"
  }
}

NotFoundError

The error raised when the requested resource could not be found.

This error has no special data.

Example

{
  "status": 404,
  "message": "The requested resource cannot be found"
}

ServerError

The error raised when an internal server error occurs.

FieldTypeDescription
errorStringThe name of the field that raised the error.

Example

{
  "status": 500,
  "message": "The server encountered an error while performing the requested action",
  "data": {
    "error": "Shine didn't confirm ;-;"
  }
}

File related models

FileMetadata object

Holds file type-dependent information of a file stored on Effis. This can be one of four variants:

Text

Text metadata does not contain any special parameters.

FieldTypeDescription
typeStringThe type of file as a lowercase string, always "text".

Image

Image metadata contains the width and height of the image.

FieldTypeDescription
typeStringThe type of file as a lowercase string, always "image".
widthNumberThe width of the image in pixels.
heightNumberThe height of the image in pixels.

Video

Video metadata contains the width and height of the video.

FieldTypeDescription
typeStringThe type of file as a lowercase string, always "video".
widthNumberThe width of the video in pixels.
heightNumberThe height of the video in pixels.

Other

Metadata for other files only include the file type.

FieldTypeDescription
typeStringThe type of file as a lowercase string.

Examples

{
    "type": "text"
}
{
    "type": "image",
    "width": 5120,
    "height": 1440
}
{
    "type": "video",
    "width": 1920,
    "height": 1080
}
{
    "type": "other"
}

FileData object

Represents a file stored on Effis.

FieldTypeDescription
idStringThe id of the file.
nameStringThe name of the file.
bucketStringThe bucket to which the file belongs.
spoiler?BooleanWhether this file is spoiler tagged. This is not provided if false.
metadataObjectA FileMetadata object containing meta-information about the file.

Example

{
    "id": 12345678,
    "name": "kaas",
    "bucket": "attachments",
    "spoiler": true,
    "metadata": {
        "type": "image",
        "width": 7680,
        "height": 4320,
    }
}
{
    "id": 12345678,
    "name": "japanese goblin",
    "bucket": "attachments",
    "metadata": {
        "type": "other"
    }
}

Relevant Endpoints

MethodEndpoint
POSTEffis /
POSTEffis /<bucket>

InstanceInfo object

Represents info about your instance.

FieldTypeDescription
instance_nameStringThe name of the connected Eludris instance.
descriptionString?The description of the connected Eludris instance. If provided, this must be within 1 and 2048 characters long.
versionStringThe version of the connected Eludris instance.
message_limitNumberThe maximum allowed message content length.
oprish_urlStringThe url to this instance's REST api.
pandemonium_urlStringThe url to this instance's gateway.
effis_urlStringThe url to this instance's CDN.
file_sizeNumberThe maximum number of bytes for an asset.
attachment_file_sizeNumberThe maximum number of bytes for an attachment.

Example

{
  "instance_name": "WooChat",
  "description": "The most Woo place to Woo", // This field *can* be `null`, but it will always exist
  "message_limit": 2048,
  "oprish_url": "https://eludris.tooty.xyz/",
  "pandemonium_url": "wss://eludris.tooty.xyz/ws/",
  "effis_url": "https://effis.tooty.xyz/",
  "file_size": 20000000,
  "attachment_file_size": 25000000
}

Relevant Endpoints

MethodEndpoint
GETOprish /

Message object

Represents a message sent to or received from Eludris. The message contains its author and content.

FieldTypeDescription
authorStringThe author of the message, between 2-32 characters long.
contentStringThe content of the message. At least one character; the upper bound is set per-instance inside Eludris.toml. This upper bound can be requested as part of the instance's InstanceInfo.

Example

{
  "author": "Frynus Mctronalds",
  "content": "dragon"
}

Relevant Endpoints

MethodEndpoint
GETOprish /
POSTOprish /messages/

Rate Limits

Rate limits in Eludris fully depend on the instance's configuration. You can always get a specific instance's rate limit info by using the /rate_limit route or dynamically using response headers.

RateLimitConf

Represents a rate limit for a specific part of the Eludris spec.

FieldTypeDescription
reset_afterNumberThe number of seconds the client should wait before making new requests.
limitNumberThe number of requests that can be made within the time frame denoted by reset_after.

Example

{
  "reset_after": 10,
  "limit": 5
}

InstanceRateLimits

Represents all rate limits that apply to the connected Eludris instance. This includes individual rate limit information for Oprish (REST-api), Pandemonium (Gateway), and Effis (CDN).

FieldTypeDescription
oprishOprishRateLimitsThe rate limits that apply to the connected Eludris instance's REST api.
pandemoniumRateLimitConfThe rate limits that apply to the connected Eludris instance's gateway.
effisEffisRateLimitsThe rate limits that apply to the connected Eludris instance's CDN.

OprishRateLimits

Represents the rate limits for Oprish (REST-api). This denotes the rate limits on each individual route.

FieldTypeDescription
infoRateLimitConfThe rate limit information for the / route.
message_createRateLimitConfThe rate limit information for the /messages route.
rate limitsRateLimitConfThe rate limit information for the /rate_limits route.

EffisRateLimitConf Object

Represents a singular rate limit for Effis (CDN). Unlike normal rate limits, these also include a file size limit.

FieldTypeDescription
reset_afterNumberThe number of seconds the client should wait before making new requests.
limitNumberThe number of requests that can be made within the time frame denoted by reset_after.
file_size_limitNumberThe maximum number of bytes that can be uploaded in the time frame denoted by reset_after.

Example

{
  "reset_after": 10,
  "limit": 5,
  "file_size_limit": 50000000 // 50MB
}

EffisRateLimits Object

Represents the rate limits for Effis. These rate limits denote how often files can be requested from the CDN, as well as maximum file size limits.

FieldTypeDescription
assetsEffisRateLimitConfThe rate limit information for the handling of Assets.
attachmentsEffisRateLimitConfThe rate limit information for the handling of Attachments.

Headers

Every request to a rate limited route returns some rate limit related headers, you're supposed to use these to properly rate limit any clients or API wrapper you make.

HeaderTypeDescription
X-RateLimit-ResetNumberThe reset interval length of a bucket in milliseconds.
X-RateLimit-MaxNumberThe maximum number of requests that can be sent per bucket interval.
x-RateLimit-Last-resetNumberThe UNIX timestamp of the last time this bucket interval was reset in milliseconds.
x-RateLimit-Request-CountNumberThe amount of requests done by the client for this bucket.



X-RateLimit-Byte-MaxNumberThe maximum number of bytes that can be sent per bucket interval
X-RateLimit-Sent-BytesNumberThe number of sent bytes by the client for this bucket interval.

Notes

These headers also exist on 429 responses.


The UNIX timestamps here all use the actual UNIX epoch and not the Eludris one.

How To Implement Rate Limiting

Ideally how rate limiting is implemented is that you check the response headers on every request and check if you've ran out of requests for this interval.

If so, you prevent any other requests on the same bucket for Now - (X-RateLimit-Last-Reset + X-RateLimit-Reset) milliseconds.

Additionally - when dealing with Effis - you should also check if the uploaded files abide by the limits of file sizes in the InstanceInfo payload.

You should also keep track of how many more bytes you can send and limit requests according to that.

Relevant Endpoints

MethodEndpoint
GETOprish /rate_limits
GETOprish /

Hosting your own instance

One of our goals ever since the start is to make hosting Eludris instances as simple and as friction free as possible. To that extent Eludris has provided an Eludris.toml where you can configure your instance as much as you wish and also official docker support to help you get started scoff free.

Deploying a Production Instance With Docker

We really recommend using Docker for production instances, it helps keep all the microservices and the external tools they rely on all in one neat group of containers without conflicting with anything else related to the host system, and without you having to go around OS-dependant installing C libraries.

Deploying a production-ready Eludris instance with docker is only 2 simple steps:

  1. Install the Eludris CLI You can find more info about this in the CLI docs.

  2. Run eludris deploy This will take you through the process of setting up your own Eludris instance step-by-step without you having to worry about screwing up anywhere.

And it is as simple as that.

If all goes right, you can find Oprish at 0.0.0.0:7159, Pandemonium at 0.0.0.0:7160 and Effis at 0.0.0.0:7161.

Deploying on Bare Metal / Running a Development Instance

Now this is just a little bit more complicated, here's a quick rundown of what you have to do:

  1. Get the required dependencies You will need KeyDB and MariaDB, the latest version of both should work.

  2. Get the latest version of eludris/eludris.

git clone https://github.com/eludris/eludris && cd eludris

You may also want update the submodules to use the next branch if you're trying to run a development instance or test a new feature, for that run this instead:

git clone https://github.com/eludris/eludris --branch=next && cd eludris
  1. Configure the Eludris.toml file & .env.

.env is mostly optional thanks to somewhat sane defaults but you'll need to have an eludris database for MariaDB and a root user with the password as root, you can also just change the database URI from the DATABASE_URL environment variable.

Additionally, I'd recommend throwing a RUST_LOG="debug" in there for debugging & logging purposes.

For more info check out the configuration page of the docs.

  1. Start the dependencies.

  2. Start the microservices.

Now what I'd recommend doing here first is building all the microservices at once, to do that, just run this command:

cargo build --release

After which you can just run these commands to start the microservices.

cargo run --bin oprish --release
cargo run --bin pandemonium --release
cargo run --bin effis --release

If you want faster build time for the price of slightly worse performance when you're for example testing, you can run the microservices in debug mode like so:

cargo build
cargo run --bin oprish
cargo run --bin pandemonium
cargo run --bin effis

You will need to run each of these commands in it's own terminal buffer for.

Depending on your hardware and whether you're building in release mode, this may take a bit :D

Congratulations, now you're self-hosting a development instance of Eludris on bare metal!

If you did everything like in this documentation, you should have Oprish at 127.0.0.1:7159, Pandemonium at 127.0.0.1:7160 and Effis at 127.0.0.1:7161.

The Eludris CLI

The Eludris CLI is a tool that's meant to handle all the heavy lifting that comes with initially starting an Eludris instance and managing it until the end of time.

Note

There are no plans to support Windows in the near future.

Installation

You can install the Eludris CLI in a couple of ways, mainly you can either:

  • Install it from the releases page.
  • Install it using Cargo.
    cargo install eludris
    
  • Install it from the AUR on Arch based Linux distributions:
    <your preferred aur helper> -S eludris
    

You can also clone the repository and build it from source.

Usage

If you need any help with using the Eludris CLI at any time you should always check the help command by running eludris --help. This will almost always tell you what you want to know.

Commands

Here's a list of the commands the Eludris CLI has along with a few extra notes.

Deploy

sudo eludris deploy [--next]

This command will start up your Eludris instance using our pre-built Docker images.

Additionally if no instance is already found on your machine it will take you on a step by step process to create one at /usr/eludris.

Using the --next flag will make you use the latest development version of Eludris for your instance instead.

Note

When prompted to choose an editor do not chose VSCode or any of it's forks as this command is ran as root which will really mess up your root partition.

Although you can still pass in a full command to use it you have been warned.

Stop

sudo eludris stop

This command will stop all the microservices in your instance along with all the other databases and such.

Update

sudo eludris update [--next]

This command will update your instance's version of Eludris to the latest available version on GitHub.

Using the --next flag makes you update to the latest development version of ELudris.

Logs

sudo eludris logs

This command will show you your instance's logs and wait for new ones.

Static

This is a command group which is meant to help deal with static assets.

Add

sudo eludris static add <path-to-file>

This command will add a new static asset to your instance's Effis.

Remove

sudo eludris static remove <name>

This command will remove a static asset from your instance's Effis.

Attachments

This is a command group which is meant to help deal with Effis files in the attachments bucket.

Remove

sudo eludris attachments remove <id>

This command will remove an attachment's file from your instance along with it's database entry to prevent 500 errors.

Clean

sudo eludris clean

This command will remove your Eludris instance along with all the database files.

Note

This will not clean up the docker images, you can remove those using docker image rm <image names>*

Configuring your instance

There are two layers of configuration for Eludris instances, .env which is used for mostly deployment logic such as ports, database URIs and such, or Eludris.toml which is used to actually customise the behaviour of your instance's outward-facing APIs like rate limits and whatnot.

The eludris/eludris meta repository comes with examples for both .env and Eludris.toml in the form of .env.example and ExampleELudris.toml, all you have to do is rename them to their respective names and add whatever configuration is required in Eludris.toml.

.env

Here's the example .env file found in the repository.

ELUDRIS_CONF = Eludris.toml # the path to your configuration file
REDIS_URL = redis://127.0.0.1:6379
DATABASE_URL = mysql://root:root@localhost:3306/eludris

# Don't forget to also change the ports in the docker-compose.yml

# oprish
OPRISH_PORT = 7159

# pandemonium
PANDEMONIUM_PORT = 7160

# effis
EFFIS_PORT = 7161

Eludris.toml

Here's the example Eludris.toml file found in the repository.

# Make sure to copy this file into `Eludris.toml`

# You can uncomment the sections that you want and change their config values,
# this is just the defaults.

instance_name = "" # This is required, has to be over 0 characters long.

# Optional instance description, can be from 1 to 2048 characters long.
#description = ""

# With rate limits, reset_after is the number of seconds between every bucket reset.

[oprish]
#message_limit = 2048 # The maximum message content length.
url = "" # This instance's Oprish url

#[oprish.rate limits]
#info = { reset_after = 5, limit = 2}
#message_create = { reset_after = 5, limit = 10}
#rate limits = { reset_after = 5, limit = 2 }

[pandemonium]
url = "" # This instance's Pandemonium url
#rate limit = { reset_after = 10, limit = 5}

[effis]
#file_size = "20MB" # The maximum file size for all the assets
#attachment_file_size = "100MB" # The maximum file size for the attachment bucket
url = "" # This instance's Effis url

# Effis rate limits are special, you're not only limited by how many requests per
# bucket reset, but also by how big the files you upload are, so assuming a rate limit
# with a limit of 5 and a file_size_limit of 10MB, I can either upload 1 10MB file
# (if the effis.file_size allows so) or for example 5 2MB files, after either of
# which I get rate limited.

#[effis.rate limits]
# The rate limit for all buckets besides the attachments one, these buckets are
# stuff like avatars, guild icons, etc.
#assets = { reset_after = 60, limit = 5, file_size_limit = "30MB"}
#attachments = { reset_after = 180, limit = 20 file_size_limit = "500MB" }
# This is a normal rate limit
#fetch_file = { reset_after = 60, limit = 30 }

Eludris Detailed

A detailed rundown and initial spec of how we plan Eludris to eventually function.

Overview

The main goal with Eludris to provide a uniquely fresh but not entirely new experience. One where anyone can find (or create) a place focused around their interests while having a couple of fundamental policies that greatly improve users' experience.

These policies include being secure, 100% FOSS, privacy respecting and decentralised, while also not inconveniencing users who don't understand what these things entail, but still enhancing their experience.

In addition -- and as with anything Eludris-related -- modifying the source code or making your own clients, libraries, or tooling around Eludris is more than welcome and is in fact encouraged. We have an entire GitHub organisation focused on this.

Takeaways

You can make Communities.

Communities can be either message-based, post-based, or both.

Message-based communities work like how a Discord server does, having many different channels secluded to their own types. Members can -- as you can guess -- send messages within these channels.

Post-based communities work like how a Subreddit does with members being able to create different types of posts, vote on them and leave comments.

Both community types have shared features however, like roles, nicknames and so on.

Communities can be either public or private.

Communities can usually (depends on the instance) get manually reviewed by an instance admin to get verified if requested by the community's owner.

Verified communities can claim a namespace, getting their own URL invite and are indexed onto list to be easily discoverable. However, doing so adds more restrictions upon them, such as no End-To-End-Encryption, stricter moderation, and so on.

Accounts are unique.

Accounts for a single instance are unique, but when federated, this is broken.

You can follow people or send them friend requests.

There's also a Reddit karma-like point system creatively called Social Credit by the Eludris Team. You can gain Social Credit by getting more up-votes on your posts, spending time interacting with people, getting rewarded by instance moderators or through events.

Bots are done right.

Bots will be user accounts like you'd see on Reddit or Twitter. They won't have any bot-specific API limitations, however you have to explicitly mark your bot as one for it to get verified.

Verification for bots only means that the bot and its owner will be given a little neat badge of honour.

Discord-styled application commands will be available. However -- unlike Discord -- they will not be forced upon people and will have more cool features, uses and will be more flexible. Additionally buttons and more message components will be available to give bot developers more room and tools to make cool stuff.

Miscellaneous Info

IDs

A Eludris ID is a 128 bit (16 byte) number, structured like so:

 12345678  12345678  12345678  12345678  12345678  12345678  12345678  12345678  12345678  12345678  12345678  12345678  12345678  12345678  12345678  12345678
 TTTTTTTT  TTTTTTTT  TTTTTTTT  TTTTTTTT  TTTTTTTT  TTTTTTTT  TTTTTTTT  TTTTTTTT  IIIIIIII  IIIIIIII  IIIIIIII  IIIIIIII  IIIIIIII  IIIIIIII  SSSSSSSS  SSSSSSSS
╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────╯╰──────────────────╯
                                       │                                                                     │                                       │
                                       │                                                                     │                          16 bit (2 byte) sequence.
                                       │                                                       48 bits (6 byte) Instance ID.
                        64 bit (8 byte) Unix timestamp.

T: A Unix timestamp with the Eludris epoch (1,650,000,000)─.

I: The id of the instance that generated this ID.

S: The sequence number of this ID

An instance ID is a 48 bit (6 byte) number, structured like so:

 12345678  12345678  12345678  12345678  12345678  12345678  12345678 12345678
 TTTTTTTT  TTTTTTTT  TTTTTTTT  TTTTTTTT  TTTTTTTT  TTTTTTTT  TTTTTTTT TTTTTTTT
╰─────────────────────────────────────────────────────────────────────────────╯
                                       │
                                       │
                        48 bit (6 byte) Unix timestamp.

T: 48 bits of the current Unix timestamp (also with the Eludris) epoch.

KeyDB

Eludris uses a non persistent KeyDB instance to store data that should be fetched with low latency, and is ephemeral such as rate-limit info.

Here's the structure of currently used keys:

  • rate_limit:<user-id>:<method>:<route>

How It Works

Eludris is split into four main parts, most of which are microservices. These services are:

  • Oprish: The Eludris RESTful API.
  • Pandemonium: The Eludris websocket-based gateway.
  • Effis: The Eludris file server, proxy and CDN.
  • Todel: The Eludris model and shared logic crate.

All of the microservices' source code can be found in the Eludris meta-repository.

Authentication & Encryption

This page details how Authentication & Encryption may work for Eludris.

The Token

Eludris uses JWT tokens to authenticate users. These tokens are required for nearly every interaction. Trying to connect to the Gateway or interact with the API? You'll need a token! The JWT tokens used within our services use a cryptographically secure pseudo-random string with a randomly generated HC128 string as a secret.

If you wish to get a new token, send an HTTP request to /auth with your email and password.

Tokens work on a per-session basis. What this means is that you'll have to generate a new token for every client you use. This is done to make it easy to invalidate any session without impacting the others.

Changing your password automatically invalidates all your tokens.

End-To-End-Encryption

End-To-End-Encryption (or E2EE for short) will be available to private communities, private GDMs (group direct messages) and direct messages (DMs) between friends.

E2EE Implementation

First off, every user is provided a personal and unique pair of a public key and a private key.

Payloads with encrypted data (message, post, etc.) have an extra field in their payload, the pubkey field, which contains the public key the user used to encrypt the payload's content. This is done so that the corresponding private key could be fetched from the user's public-private key pairs and requested if the current one is invalid.

As for storing public-private key pairs, storing them locally (on the client's machine) causes a lot of extra complexity, especially with sharing and syncing keys.

For example, issues with a client being offline when it's given a key, multiple clients, and so on.

To combat that, Eludris' E2EE is designed so that each user has a super private-public key pair that their other private keys are encrypted with.

The instance does not know the user's super private key. The instance gives the user all the unencrypted-public keys and encrypted-private keys when connecting to Pandemonium.

The private keys are encrypted with the user's super public key.

For example, let's say a user creates an account. They create themselves a pair of keys, one public (A) and one private key (B). They give the instance their public key (A) and store the private key (B).

They then join an encrypted DM and the other user generates a pair of keys for the DM, one public key (C) and one private key (D). They send the instance the DM's private key (D) encrypted with the first user's public key (A), the instance stores this and gives it to the first user when requested and when they connect to pandemonium.

This ensures that every user can always have their keys without any risks of the server being able to decrypt the payloads.

The instance never gets access to the non-encrypted private keys of any key pair at any point in time.

To further increase the security each instance marks all sessions (besides the first) as untrusted and essentially rats it out to everyone, a user can verify their session from their original session in which they securely pass on the super key pair to the new instance.

Direct Messages

This one is quite simple, upon a friend request getting accepted and two users becoming friends, the user who accepted the friend request sends a payload with a public key and a private key for the DM, both encrypted using the other user's super public key.

After that all messages sent in this DM is encrypted using the DM's public key and are decrypted with the DM's private key which is stored on the instance twice, once encrypted with the first user's super public key, and another encrypted with the second user's super public key.

A user can also request they get a new key from the other end which will entirely scrap the old pair of keys and generate new ones in case the old ones get compromised.

Group DMs

Group DMs can be encrypted too. They work in a similar fashion, the host sends the room's public and private keys to all the starting participants on room creation encrypted with their public keys.

When a new user joins any other user will send the instance the keys they need whenever they're online.

The room's keys can also be re-generated by the GDM's host.

Private Communities

Private communities work similarly to how Group DMs work with the addition that the posts may also be encrypted but follow the same foundations.

Federation

Eludris will be federated, meaning anyone can host their own instance and instances can communicate with each other so that any user on one instance can interact with others on any other instance.

Federation Implementation

All routes where other instances can request or submit data will have an additional /external path (like /external/this/channels/:channel_id/).

For info about how IDs are created read this.

/external routes will follow specific rules, these being:

A new instance (one the home instance has never seen before) will have to first send an identify payload. This payload is simple as it just provides a shared secret token key that both instances can use to identify each other (in case an instance's domain gets compromised or changed) and the instance's id.

/external routes will take both Oprish payloads and Pandemonium payloads in the form of HTTP requests.

For example, let's say an instance A has a community with a channel that has users from other instances, one of which is B. When a user from instance B sends a message to B's domain/external/A's ID/channels/:channel_id/messages, B will send the Oprish message payload to A's domain/external/this/channels/:channel_id/message.

When a user from instance A sends a message the opposite will happen with A sending a request to B's external route in form of a Pandemonium payload.

Version 0.3.2 Changes

This version addresses some issues related to deploying production-ready instances of Eludris by providing pre-built docker images and a simple CLI.

API changes

  • All mentions of ratelimit have become rate_limit, this should not break anything.

Hosting Changes

  • Pre-built Docker images now exist, long are gone the days of sitting around for 20 minutes waiting for oprish to build, and welcome to the age of deployments taking less than a minute.
  • A CLI has been created which overhauls the process of creating and managing your own Eludris instance, for more info, read the new CLI docs!

PR

Version 0.3.1 Changes

This version mainly tries to address some developer-side frustrations with Eludris, namely issues with having to make a branches in at least 2 repositories, make 2 pull requests, merge the pull requests, update lockfiles, making a meta-repository branch update the meta-repository's submodules, making a pull request then merging that for every single change, also issues with syncing up versions.

Additionally Docker deployments have been greatly enhanced thanks to buildx powered caching and much more (thanks @ooliver1).

API Changes

Oprish

The InstanceInfo payload now has an extra version field to make it easier to make clients and API wrappers that will not break.

Hosting Changes

Everything has been merged into one meta-repository using a cargo workspace

Dockerfiles have also been greatly improved to use caching and other optimisations, kudos to @ooliver1 (and docker buildx) which helped make this possible!

PR

Version 0.3 Changes

API Changes

Oprish API Changes

  • Due to rate limits being a per instance configuration thing, a new /rate_limits route has been added besides the already pre-existing response headers.
  • Rate limit headers now also exist when you encounter a 429 response.
  • The instance info route now has a rate-limit.

Pandemonium API Changes

  • Changed the websocket CLOSE frame messages.
  • The websocket connection now expects you to send a PING every 45 seconds as opposed to 20 seconds.
  • Switch to OPCode payloads instead of PING and PONG frames, also switched to using a MESSAGE_CREATE OPCode for new messages, refer to the Pandemonium docs for more information.

Effis API Changes

Re-wrote the entirety of effis :D

Find out more in Effis' documentation

Hosting

A new Eludris.toml file has been added which allows you to customise your instance, from per route rate limit settings to the maximum message content length, all from the convenience of one TOML file, check out the docs page about configuring your instance and the Eludris.toml example in the github repository

Oprish Hosting Changes

  • The REDIS_URL environment variable has been introduced as a direct way to supply the url of your Redis/KeyDB instance as opposed to ROCKET_DATABASES.

PR