Set up stoat.chat for local development
And things to look out for when contributing upstream
years ago, I joined stoat.chat after learning about the project via Github's "Recommended for you" feature, to try it out and get away from the mess that is modern discord.
2 years later, my first commit to stoat was upstreamed, after being encouraged to contribute to the then frontend rewrite that would eventually become "Stoat for Web", the recommended stoat.chat client promoted on their website.
That's to say that, well, I think I have some semblance of knowledge about the project. My contributions to the project former have since died down as I got busier with life, but my passion still remains.
I've had an older version of this article back on my older website, when I was still using NekoWeb (A Neocities-like static hosting website), and was still in high school. But, of course, it's very outdated and a lot of things have changed since then, so I wanted to sort of "modernize" it, because a lot has changed since 2023.
I'll also give some guidance on how to mock certain closed-source components of the stack, such as the discover service and the health endpoints, as I think it would be very helpful, not only to individuals wanting to contribute, but to third-party instances that want similar functionality as well.
Requirements
Hardware
As of 2023, to host the backend you'll need the following minimum specifications:
- Any reasonably modern multi-core CPU
- At least 30GB of Storage
- At least 4GB of RAM, but 16GB is recommended if you use
rust-analyzer
Software
- Rust 1.86 or later
- Mise-en-place, this ensures that you have the correct versions of the dependencies on both the backend and frontend side.
- Docker & Docker Compose
(You could replace Docker with
podman, but this setup is unsupported and may require additional thinkering) - Git, to clone the repository
- And optionally, to have faster linking times,
mold
Other requirements
If you are using Docker Compose, feel free to skip to the next section. Otherwise, you'll need the following services up and running:
- MongoDB
- Redis, or a Redis-Compatible KV Cache
- MinIO, or a service compatible with the AWS S3 API
- RabbitMQ, or a AMPQ compatible service
- MailDev (to mock email verification)
Initial setup
First, fork the backend and web app repositories on GitHub, and locally clone both git repositories. You can do this by opening a terminal session and running the following commands:
git clone https://github.com/stoatchat/stoatchat stoat-backend
git clone --recursive https://github.com/stoatchat/for-web stoat-frontendNote that when cloning the web app, we pass the --recursive flag.
This is because Stoat vendors some dependencies. If you forget to recursively clone the repository, you can always go into the folder Git just created and run git submodule update --init— this will make sure that all the submodules are initialised and cloned.
Backend
Go inside the stoat-backend directory and run mise install.
This commands installs the correct rust version, among other things, such as the git town command, the gh command, and both node and pnpm.
Next, build the backend by using the mise build command. If you are using mold, you can tell mise to use it by prepending BUILDER="mold --run cargo", like so:
BUILDER="mold --run cargo" mise buildThen copy the example livekit configuration (which is made for development) into livekit.yml and finally, start the services using docker compose alongside the backend!
cp livekit.example.yml livekit.yml
mise startThis will run the proxy service, CDN, GIFBox (tenor-compatible API proxy), events server and API in the following ports:
- Delta (API): 14702
- Bonfire (Events): 14703
- Autumn (CDN): 14704
- January (Proxy/Embed): 14705
- GIFBox (Tenor proxy): 14706
If you with to override these ports, you can take a look at the included Revolt.toml file. You can find which values do what by checking crates/core/config/Revolt.toml.
Web App
Configuring the web app is a bit simpler than the backend, it just requires editing a environment file and running a few commands to get it running.
First, let's install the dependencies by running mise install; Like before, it will install pnpm and node, but also some extra dependencies needed for E2E testing and generating the documentation site (which is available on https://stoatchat.github.io/for-web).
Next, copy the packages/client/.env.example file into packages/client/.env in order to tell the app to use our backend. This isn't necesary if you aren't planning on hosting it on your machine, because by default it will use the public stoat.chat API.
Now, let's install and build the required dependencies:
mise install:frozen
mise build:depsThis will install the external dependencies, as well as build the vendored ones, alongside the translation strings and panda-css (the CSS-in-JS engine that the project uses).
You can finally run the app by running mise dev, which will open a Vite server on port 5173.
Miscellaneous services you might want
Discover
If you are going to be working on a part of the discover integration, you are going to need a proper discover implementation.
Sadly, this service is closed source due to potential abuse of ranking algorithms and hardcoded production strings. But recently I made a minimum viable implementation of the service called MiniDiscover, which is meant to be used by external contributors wanting to fix bugs related to Discover from the web app side.
You can find instructions on how to quickly run the service on your system in the tangled repository below:
bunniesin.space/minidiscover
Minimum viable implementation of Stoat Discover
Alternatively, you can implement your own by reading this article on the undocumented Discover IPC protocol:
Mocking Stoat Discover | Offprint
How to create a simple discover platform for your third-party Stoat instance
To point the web app to our service, you need to go to the packages/client/src/interface/Discover.tsx file and replace any occurrences to https://stt.gg with your discover domain, or in the case of MiniDiscover, with http://localhost:8008.
--- a/packages/client/src/interface/Discover.tsx
+++ b/packages/client/src/interface/Discover.tsx
@@ -24,7 +24,7 @@ export function Discover() {
if (!frame) return;
const url = new URL(message.origin);
- if (url.origin !== "https://stt.gg") return;
+ if (url.origin !== "http://localhost:8008") return;
const data = JSON.parse(message.data);
console.info(data);
@@ -88,7 +88,7 @@ export function Discover() {
// Render the URL once, update path in browser through messaging
const query = new URLSearchParams(location.query as Record<string, string>);
query.set("embedded", "true");
- const src = `https://stt.gg/${location.pathname}?${query}`;
+ const src = `http://localhost:8008/${location.pathname}?${query}`;
return <Base ref={setRef} src={src} />;
}Admin Panel/Platform Moderation
At the time of writing this article, I haven't come across any open-source admin panels that support Stoat. There is an admin API in the works, but progress is slow.
I'll update this section if a reader sends me a working implementation, you can find my contact information on my site, bunniesin.space.
Contributing
Let's say that you successfully set up your development environment, have worked on a bug fix, and plan to contribute upstream. You need to keep in mind the following things before opening a pull request:
Be concise
You don't need to describe every little detail of your changes, this should instead be done through the commit messages.
Commit messages are short, atomic (that means, only edits the parts that it needs), and to the point, which makes them perfect candidates to describe the nature of the change.
Use conventional naming scheme on pull requests
Pull requests that are feature additions should be marked with the feat: prefix, fix: are fixes, and chore: are general housekeeping actions, such as updating a lockfile, editing surrounding code to suit your change, running tools like formatters and linters, etc., and refactor: for code restructuring.
This goes hand in hand with being concise and keeping your commits "atomic".
Sign your commits
Stoat is licensed under the AGPLv3 license, which is only compatible with GPLv3-compatible licenses and GPLv3-compatible licensed code. Which is why they have a DCO (Developer Certificate of Origin) Github Action to ensure that, by contributing your changes, you make it clear that the change comes from you, and that it doesn't use, or rely upon, any non GPLv3-compatible code.
You can sign your commits by adding the -s flag in your git commit command, which adds a Signed-off-by trailer containing your git username (which is usually set up before creating a commit) and your git email.
Many developers forget to do this, and then wonder why their changes don't get merged.
It's usually because you forgot to sign a commit.
You can learn more about DCO here.
Please, don't use AI or upstream entirely AI-generated code
As of the time of writing this article, Stoat is not accepting any AI-generated pull requests, that be with AI-generated code, or AI-generated PR descriptions, or both.
Also, while writing the description of a PR, you are required to disclose any other use of AI in your contribution.
You can also read the contribution guidelines on your own here:
Contribution Guide | Stoat Developers
This is the contribution guide for developers wanting to help out with Stoat.
They apply to every project under the stoatchat GitHub organization.
Closing remarks
I am very grateful for the Stoat team and their project. It's what gave me the confidence boost to keep going and to grow as a person, despite any previous beef or bitter interactions I might have had during my stay on the platform.
If it weren't for Stoat, I probably wouldn't be here, writing a blog for all to read, or making more (though, smaller) contributions to other open source software.
So, take this as my "thank you" letter, for changing my life.
(also thanks for having patience with me, i am somewhat unstable sometimes and it's quite nice that you lot understand that.)
(also also, sorry insert for showing you solidjs on the stoat server one day and inadvertently starting what would become the web app rewrite)
Did you enjoy this article?
Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.