Skip to content

Mocking Stoat Discover

Or: Create a simple discover platform for your third-party Stoat instance

Amelia
Jul 10, 2026 · 3 min read
1

am in the process of writing a more "comprehensive" guide on self-hosting Stoat, however, I feel like talking about the main instance's "Discover Stoat" feature needs to have it's own standalone article.

Under the hood, Discover is your average NextJS application, rendering servers, bots and themes from a source JSON file that is generated/updated every couple of hours. In this article, I will only cover the IPC protocol that the app uses to communicate to the clients in order to show a join prompt, or in the case of bots, an invite dialog.

The protocol in a nutshell

Messages are passed to and from the frame using the iframe.contentWindow.postMessage and window.postMessage APIs. It communicates using objects, each having a unique type and a "source".

Source MUST be "discover", otherwise the client might outright reject the IPC message. And the client also MUST include a source of "revolt", to avoid discover rejecting your messages.

The frame may send the following messages to the client.

Client <- Frame

Theme

Set the theme of the current frame, usually sent after receiving init to allow the frame to use the current client's theme. The theme key must contain a valid Revite theme, to use the default colours, pass an empty object.

{
  "type": "theme",
  "source": "revolt",
  "theme": {}
} 

Frame -> Client

Init

Discover is ready to send or receive events to and from the client.

{
  "type": "init",
  "source": "discover"
}

Path

User switched tabs. Used for clients restore the last accessed tab.

{
  "type": "path",
  "path": "/discover/bots",
  "source": "discover"
}

Navigate

Tells the client to perform a navigation action, usually starting the "Accept Invite" or "Invite Bot" flow.

{
  "type": "navigate",
  "url" : "url_to_invite_or_bot"
}

Apply Theme

Tells the client to apply the selected Revite theme, only works on the legacy web client.

{
  "type": "applyTheme",
  "theme": {
     "variables": {},
     "css": "css as string"
  }
}

As you can see, it's pretty simple

So why has no one made their own replacement for third-party instances?

Personally, I think it comes down to the fact that there aren't a lot of stoat instances in the wild just yet, and the ones that exist usually only need an admin panel.

But it annoys me that the FOSS people complain that the main discover service isn't open source, because technically it counts as a publicly available component of an AGPLv3 service, even though it's entirely separate, optional, and resides on its own repository.

Anyway, I also wanted to finally do my own replacement with most of the protocol implemented in the simplest way possible, basically, a discover MVP.

It's available on Tangled, licensed under the MIT license. The project is written in Go, using the fantastic labstack/echo library.

bunniesin.space/minidiscover

Minimum viable implementation of Stoat Discover

Feel free to fork the project and change it to your heart's content, or use it as a resource for your own implementation; but please, for the love of god, please don't use it as is in production.

Sources

Protocol reference: https://github.com/stoatchat/for-legacy-web/blob/57e8c0e274a48a913f1a33e406cfbfb68e8269ce/src/pages/discover/Discover.tsx

Cover art: Agnese Rudzīte on Unsplash

Did you enjoy this article?

Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.

Across the AtmosphereDiscussions