RFCs

RFCs for changes to Revolt. This is a process for adding or changing anything in Revolt, in a formalised and clear manner, so that everyone understands clearly what is needed.

When you need an RFC

You must create an RFC if you intend to make a substantial change to Revolt, or any part of Revolt. This includes:

  • Any new features
  • Changes to existing ones which have a large impact
  • Removing features

Some things don't need an RFC - these include:

  • Rewording, reorganising or refactoring text or code
  • Additions which improve the app in ways which do not add new features (e.g. performance improvements, bug fixes, better user experience in regards to errors)
  • Additions which do not affect users of Revolt but only the developers of Revolt

If you attempt to open a pull request which would require an RFC without opening an RFC first, your pull request will be closed and you will be asked to submit an RFC before the pull request can be looked at.

If applicable, you're allowed to start working on an implementation for a draft or in-progress RFC - however, if the RFC is denied, your pull request will be denied as well.

Before creating an RFC

Before submitting an RFC, it's a good idea to discuss your idea first - if your idea is declined, you won't have wasted any time writing an RFC. You can discuss in issues or discussions in Revolt's GitHub organisation or, preferably, the Revolt API server.

Creating an RFC

To get a feature added to Revolt, you must get your RFC merged into this repository first. At this point, the RFC is "active" and can be discussed.

  • Fork the RFC repo
  • Copy the rfc-0000-template.md file into rfcs/rfc-0000-name-of-feature.md. Don't give it a number yet - this will be assigned once the RFC is opened.
  • Fill in the new RFC file. Make sure to include as much detail as possible - any ambiguties will slow down the RFC process and will likely need to be corrected.
  • Submit the RFC. Make sure to give the PR a relevant name - use the RFC's name or something similar which conveys a similar message.
  • Once the PR is open, you can update the RFC number to match the pull request's number - for example, if the pull request is #36, your RFC will be 0036.
  • The RFC will be labelled by the team.
  • The RFC will then be discussed and commented on with queries or requests for changes by the team and community members. You can make inline edits to incoporate suggestions - do not squash or edit existing commits, as this can lead to confusion.
  • Once the RFC is close to being finalised, a member of the team will assign the RFC the Final Comment Period label. At this point, the RFC should not be edited. This stage will last 10 days; once it ends, the team will be in a position where a final decision on the RFC can be made (i.e. whether to merge it, close it or postpone it). This outcome does not need to match the concensus of all participants of the discussion - however, most (if not all) opinions expressed in the RFC's discussion will be taken into account. During the Final Comment Period, any final comments can be made to lodge any objections before a decision is made.
  • During the Final Comment Period, if any new substantial arguments or ideas are raised, the team or the RFC's author can cancel then Final Comment Period can be cancelled - the RFC will then go back into development.

Active RFC lifecycle

Once the RFC is merged, it will become "active". You can still make updates to the RFC via pull requests; however, the workflow above must be followed.

Now that it is active, a developer can be assigned to implement the RFC. The author does not need to implement it themselves; however, this is the most effective way for the RFC to be implemented. Furthermore, the fact that the RFC is active does not mean it is guaranteed to be implemented or accepted. If, after this stage, it is found that the RFC is no longer deemed necessary or any major flaws are found in it, it can be still denied and closed.

Implementing the RFC

Depending on the RFC, an implemention must be made. Once the RFC is merged and active, a tracking issue can be made to track the status of the implemention and link to any currently open pull requests. This can also serve as a discussion area where the implemention or queries about the RFC can be made. If you would like to implement the RFC, comment on the tracking issue so a member of the team can assign you to it. If you are unsure as to whether someone is already working on implementing the RFC, feel free to leave a comment.

RFC postponement

RFCs can be postponed - if so, they will be labelled with the "postponed" label. This can be for various issues - for example, if the RFC is not being currently worked on, or if it is blocked by other RFCs that are not expected to be finished soon.

  • Feature Name: Webhooks
  • Start Date: 10/03/2023
  • RFC PR: https://github.com/revoltchat/rfcs/issues/0001
  • Tracking Issue: https://github.com/revoltchat/revolt/issues/0000
  • Status: accepted

Summary

This RFC adds the ability to be able to send messages from external applications and services through webhooks, this allows intergration with other services.

Motivation

Webhooks make it easy to intergrate with other services, this allows other services to take advantage of Revolt's to send infomation through revolt. A common example is GitHub Webhooks which allow you to send messages when a repository changes.

Guide-level explanation

This RFC adds the ability to create, edit, delete webhooks and send messages with the webhook, webhooks have a name, id, channel id, avatar and token. The token and id are used to send messages with the webhook.

Fetching the webhook can be done with or without authorization, if you fetch with authorization the token will be returned, as the token is secret it should not be given to people without permissions.

There will be a built-in way to use GitHub webhooks from revolt as this is a very commonly used type of webhook, no other types of webhooks have been considered yet.

Creating a webhook will give it a default name of "Webhook" and default to the first channel in the channel list, users can then edit the name, channel and avatar.

Reference-level explanation

The database will have an additional document to store the webhooks

#![allow(unused)]
fn main() {
struct Webhoook {
  // Unique Id
  id: String,

  // The name of the webhook - 1 to 32 chars
  name: String,

  // The avatar of the webhook, if it has one
  avatar: Option<File>,

  // The ID of the channel which this webhook belongs to
  channel: String,

  // The permissions of the webhook
  permissions: u64,

  // The token of the webhook
  token: String
}
}

Addition routes must be added to the API, this includes:

Channel Routes

POST /channel/<target>/webhooks - Create webhook in the channel
GET /channel/<target>/webhooks - Gets all webhooks in the channel

Webhook Routes

GET /webhooks/<target>/<token> - Gets the webhook with a token, does not require permissions
GET /webhooks/<target> - Gets the wehook, requires permissions
PATCH /webhooks/<target>/<token> - Edits the webhook with a token, does not require permissions
PATCH /webhooks/<target> - Edits the webhooks, requires permissions
DELETE /webhooks/<target>/<token> - Deletes the webhook with a token, does not require permissions
DELETE /webhooks/<target> - Deletes the wehook, requires permissions
POST /webhooks/<target>/<token> - Sends a message with the webhook
POST /webhooks/<target>/<token>/github - GitHub compatible route for sending a message with the webhook

The PATCH routes take a json body with the data for editing the webhook:

#![allow(unused)]
fn main() {
struct WebhookEditBody {
  // The new name for the webhook
  name: Option<String>,

  // The new avatar for the webhook - Autumn ID
  avatar: Option<String>,

  // The new permissions

  permissions: Option<u64>,

  // The fields to remove
  remove: Option<Vec<RemoveFields>>
}

pub enum RemoveFields {
  Avatar
}
}

The POST routes take the same json body as the message send route. The data the GitHub compatible route takes can be seen on the GitHub docs

The message structure must be changed to accommodate these changes, this requires a breaking change of making Message.author either the user ID or the webhook ID, depending on what sent the message, when a webhook sends a message a Message.webhook field will contain the webhook's information.

- author: String  // User ID
+ author: String  // Either user ID or webhook ID
+ webhook: Option<MessageWebhook>

the webhook data inside a message will contain a stripped down version of the Webhook struct to avoid sending unessessary data:

#![allow(unused)]
fn main() {
struct MessageWebhook {
  // The name of the webhook - 1 to 32 chars
  name: String,
  // The id of the avatar of the webhook, if it has one
  avatar: Option<String>
}
}

This will require an update to existing programs which use the API to ensure they do not break with this change.

Information about the webhook which sent the message is included inside the message, you are also able to query GET /webhooks/<target> route which returns the same infomation.

There will be three new events to go along side this, these events do not contain the token as these events are sent to all people who have access to the channel and not just people with permissions

#![allow(unused)]
fn main() {
enum Events {
  WebhookCreate(Webhook),  // Contains all the info about the webhook which was created

  WebhookUpdate {
    id: String,  // The ID of the updated webhook
    data: PartialWebhook,  // Contains the data which was updated
    remove: Vec<RemoveFields>  // A vec of the fields which where cleared
  },

  WebhookDelete {
    id: String  // The ID of the webhook which was deleted
  },

  // existing events
  ...
}
}

Permissions

Webhooks are affected by permissions, to for example stop them from uploading files, webhooks will store the full u64 set of permissions to keep consistancy but only a few permissions will affect the webhook, because of this the client will only show a select few permissions, this includes:

  • Send Messages
  • Send Embeds
  • Masquerade
  • React - affects interaction reactions

When the webhook is created it defaults to having all of these permissions enabled.

Prior art

Discord and Slack both have webhooks as well both with similar implementation, both work well and have wide adoption

Unresolved questions

Currently this RFC includes a breaking change, if possible this would like to be avoided however no solution has been found yet.

Security concerns

No security concerns have been found yet, however this does allow third-parties to send messages to revolt, this could be used maliciously however the affected scope is small as it can only send messages.

Future ideas

Currently there is only a Github compatible route for pre-existing formats, in the future this could be expanded to support more formats such as slack for example.

  • Feature Name: Discriminators
  • Start Date: 06/05/2023
  • RFC PR: https://github.com/revoltchat/rfcs/pull/3
  • Tracking Issue: https://github.com/revoltchat/backend/issues/247
  • Status: accepted

Summary

Revolt should introduce discriminators which serve as part of a semi-hidden extension to usernames which allows multiple users to have the same username, the discriminator would not be visible in most circumstances except when viewing a person's profile and if you were to add said person by their username.

Motivation

This RFC aims to move forwards prior discussion on the feature suggestions forum, it stands to solve multiple problems when implemented correctly:

  • Users should in most, if not all, cases be able to pick their chosen username.

  • This should curb any incentive to sell / steal accounts for "rare usernames".

    Certain "rare" discriminators can also be prohibited.

  • Allow users who do not want to be easily discoverable to stay hidden.

    Just a 4-digit discriminator provides 10,000 unique users. Expanding on this, we could for example, let users hide discriminators in servers and similar privacy features.

Guide-level explanation

Revolt would be switching to a new username system made up of three parts:

myusername#1234
^ your chosen name (restricted character set)
          ^ separator between chosen name and discriminator
           ^ discriminator (4-digits in range 0001 to 9999)

Users will also be able to set an optional display name which overrides their username in various parts of the user interface.

Usernames use a restricted character set of any Unicode alphabet excluding some lookalike characters from the Cyrillic alphabet, if you username does not currently fit the specification, then it will be automatically santised upon upgrade and copied to your display name.

Discriminators for new users are generated randomly on sign up while existing users will get a randomly generated discriminators upon the server software being upgraded.

Revolt treats usernames as case-insensitive so a full username such as "Jason#1234" is effectively the same as "jasON#1234" but only "Jason#1234" will display in the UI.

Users may:

  • Change the case of their username at any moment, such as from "Jason" to "jasON".

    This will never affect their discriminator.

  • Change their username, but may have their discriminator regenerated if there is a conflict.

    Such as if you are changing from "Jason#1234" to "Phil", but "Phil#1234" is taken.

User Opinion

Prior discussions on the forums primarily seem to lean in favour of discriminators being implemented into Revolt, the internal team has not raised any complaints against it, and the primary community on Revolt ("Revolt Lounge") appears to be somewhat split on the issue.

As a recent case study, Discord's change to unique usernames only has generated a lot of backlash and criticism from the community, it appears to be overwhemingly negative but these experiences are mostly coming from long-term users of Discord. The true opinion of the average user has not been gauged yet.

Usability

This may make it more difficult to understand how to add users on Revolt, however this shouldn't be an issue as long as it's clearly communicated through the user interface. Revolt already allows a wide range of Unicode in usernames so we are definitely not making a significant UX hit towards the usability of the friend system.

There are further considerations listed at the end of this document which must be addressed if this RFC were to be implemented.

Reference-level explanation

A new discriminator string field shall be added to the User object, as such the UNIQUE IGNORE-CASE USERNAME index will be replaced with a UNIQUE (IGNORE-CASE USERNAME + DISCRIMINATOR) index.

A new display_name string field shall be added to the User object, this will be subject to all existing username restrictions.

Existing clients may continue to display usernames but will no longer have the guarantee any one username is unique and must implement the discriminator field to distinguish them to end users, and they must also show the display name instead of username in the chat interface and include it in profiles.

Discriminators shall be 4-digit identifiers (this may be expanded in the future if we reach a point at which they are no longer sustainable, or otherwise at least one username is becoming saturated).

The friend add route, POST /users/friend will be updated to support parsing a discriminator, this will provide backwards compatibility for older clients to some limited extent.

{
  "username": "abc#1234"
}

Drawbacks

The main drawbacks are:

  • Additional information to remember about your username

    Although this works both ways in the sense that, you can have a much simpler username.

  • Users may no longer have a completely 'unique' username on the platform

    But we're approaching this from the standpoint that usernames on platforms such as these should not have to compete for 'uniqueness'.

I want to take this section to also discuss the case study of Discord's removal of the discriminator system and their cited drawbacks, it is worth noting however that Discord has not really specified how they've generated these statistics or this information, although for the sake of the following they are taken at face value:

  • More than 40% of you either don’t remember your discriminator or don’t even know what a discriminator is. That’s a big problem when discriminators are required to add a new friend.

    This RFC cannot address this issue however further work can be done to ease these interactions:

    • R&D to determine how to build the UI to intuitively tell the user how to share their username with their discriminator

    • Implementation of friend links / codes to ease connecting users to each other

      This is not something that Discord has solved and to this day there is no way to create a friend request link.

    • Implementation of nearby find (prior art: Discord) / QR friend codes (prior art: Snapchat) / add by contact book (prior art: Signal, WhatsApp)

      These may help streamline IRL interactions by providing users with a simple flow to follow.

    • Implementation of add by connections

      Allow adding other people by common social media connections.

    • Global user search (prior art: Steam)

      Allow users to opt-in to a global username search / or otherwise also search through mutual members on servers, to avoid needing the discriminator altogether.

  • Across Discord, almost half of all friend requests fail to connect the user with the person they wanted to match with, mostly because users enter an incorrect or invalid username due to a combination of missing discriminator and incorrect casing.

    • Issues with ease of adding are addressed in the point above, however incorrect casing is irrelevant to Revolt as-is because Revolt's usernames are already case-insensitive and this will not change.

      If we look at the given example:

      You meet someone IRL that you want to talk to on Discord, and they say “I’m Phibi Eight Nine Three Six!” You go home and add “phibi#8936” only to find out you added the wrong “Phibi” because your new friend’s username is actually “PhIBI#8936”.

      Revolt does not permit a registration of both "PhIBI" and "phibi".

  • You want to use a common name like “Mike” or “Jane” but there are already 9,999 Mikes or Janes so you’re blocked from that name altogether.

    We are not restricted to just 4-digit discriminators in our implementation.

  • You like to change your username a lot and get rate limited.

    If we were also to add more stringent rate limits, this may be solved by also including display names.

  • Your friend says they changed their name to “vernacular” but actually it’s “𝖛𝖊𝖗𝖓𝖆𝖈𝖚𝖑𝖆𝖗” and you have trouble finding them.

    While this is a valid concern, I would personally put this down as the user's own issue.

Rationale and alternatives

Discriminators (with display names and a restricted character set) appear to show the least disadvantages out of all of the solutions discussed so far.

SolutionDescriptionUsers have desired usernameSelling disincentivizedPrivacyUsabilityDifficulty
DiscriminatorsProposed in this RFC⚠️Low
Discriminators
w/ display names
Show display names with greater priority to username / discriminator combinations.
* May be proposed in this RFC
⚠️Low
Discriminators
w/ display names
w/ restricted character set
Also restrict the characters that you can use in the username itself.
* May be proposed in this RFC
Medium †
Unique usernamesCurrent system on Revolt
(but any unicode username is allowed)
⚠️Low
Unique usernames
w/ restricted character set
Alphanumeric unique global usernamesMedium †
Unique usernames
w/ display names
Either unique username solution but with added display names that show instead of the username⚠️⚠️Low
Remove usernames altogether
(only display names)
Resort to using friend codes, invite links, and the like exclusively.⚠️High ¶
Unique cryptographic user identifiers with display namesResort to using friend codes, invite links, and the like exclusively.⚠️⚠️⚠️Medium

In regards to this table,

  • Privacy means whether users can avoid being discovered based on their common names.
  • Usability is whether users can practically and quickly add each other.
  • † Restricting existing usernames further would potentially require some users to change username.
  • ¶ Removing usernames altogether would require a complete redesign of how friends work on Revolt.

Discriminators also help solve a couple of issues with regards to username abandonment, users may sign up and:

  1. Forget their account credentials or otherwise get locked out, and never recover their account.
  2. Forget that their account even exists.
  3. Decide they don't want to keep using their account but never delete it.

Revolt does not currently automatically delete old accounts with no activity which is why this poses an issue.

Given Revolt's current size, 4-digit numeric discriminators currently pose the least issues.

SolutionExampleDescriptionUsabilityQuantitySafe
4-digit#1234Any 4 digitsLow
N-digit#123456Any N digits⚠️Medium⚠️
Variable Digit#123 ... #1234Scale between n and N digits depending on use⚠️Infinite⚠️
4-hex#12AFAny 4 hex charactersMedium⚠️
4-char#1ABZAny 4 alphanumeric characters❌ †High

In regards to this table:

  • Usability is whether the solution is practical, i.e. reasonably sized and simple.
  • Quantity is how many possible discriminators may be housed under one username.
  • Safe is whether the solution is not susceptible to generating undesired combinations and phrases.
  • † Allowing any alphanumeric characters may cause confusion between similar charactres using certain fonts, e.g. O and 0.

We also choose to restrict usernames to any Unicode alphabet rather than the full range given this has the best compromise between reasonable usernames, users affected, and localisation:

Permissible FormatRegexUsers affected by changeSupports regional dialectsPotential for abuse
Alphanumeric^[a-zA-Z\d]+$18%Low
Any alphabet ¶ or digit^(\p{L} \| \d)+$17%Medium ¶
Alphanumeric with special characters †^([a-zA-Z\d_.-])+$11%Low
Any alphabet ¶ or digit with special characters †^(\p{L} \| [\d_.-])+$9%Medium ¶
Current format^[^\u200BА-Яа-яΑ-Ωα-ω@#:\n\r\[\]]+$0%High
  • † Special characters include underscore, period and dash.
  • ¶ Certain lookalike characters will continue to be blocked, such as those from the cyrillic alphabet.

Prior art

This has been implemented before on other platforms:

Unresolved questions

No currently unresolved questions.

Security concerns

This should not impact security, since this is almost entirely a cosmetic change to usernames.

This should not have any adverse effects for functionality such as blocking users as this is entirely handled using internal IDs.

Future ideas

As discussed previously, we may look into implementing:

  • Friend links / codes
  • Nearby find / QR friend codes / add by contact book
  • Add users by social connections
  • Global user search
  • Better friend flow UX

We may also want to look into implementing additional privacy settings for adding users.

As per discussion comment, we may want to warn users that their username contains weird unicode or other characters that may look similar or weird, that may prevent them from adding other people on the platform.