Skip to content

Architecture

PhoenixCord has four parts.

Part Crate Role
Plugin API crates/sdk The WIT definition every plugin is written against, plus the Rust bindings
Discord state crates/discord Discord’s data models and a copy of the client state, built from gateway events
Plugin host crates/host Loads plugins as WebAssembly components and dispatches hooks and events
Adapter not yet written Connects the host to Discord’s native client

The host talks to Discord only through the adapter. The adapter forwards gateway events and outgoing messages to the host, calls the host’s hooks while Discord renders its UI, and performs actions such as sending a message or showing a toast.

Everything that depends on how Discord’s client is implemented lives in the adapter. The plugin API describes intent, for example “a button next to the message input”, so it does not change when Discord’s internals do. A Discord update means an adapter update. Plugins are untouched.

Discord’s gateway protocol is public and stable. PhoenixCord keeps its own copy of guilds, channels, roles, members, messages, presences, voice states and relationships, built from the gateway events the client receives. Plugins read that copy through the client interface, and it powers permission calculations and the old message content in edit and delete events.

Each plugin is a WebAssembly component in its own instance:

  • It can only call the functions in the plugin API. It has no file system, process or network access other than host.fetch.
  • Every call into a plugin has a time limit. A plugin that runs too long or crashes is stopped, marked as failed and recreated the next time it is enabled. The rest of Discord keeps running.
  • Plugin state lives in the plugin’s own memory. Settings and storage are kept per plugin on disk.

A plugin is never called while it is already running. Anything a call triggers for the same plugin, such as setting-changed after host.set-setting, is queued and delivered after the call returns. Asynchronous work, such as HTTP requests, timers and Discord API calls, returns an ID immediately and delivers the result later as an event carrying that ID.