Appearance
Concepts
What is a plugin?
A Dissent plugin is an HTML page that runs inside a sandboxed <iframe> within the Dissent client. The plugin communicates with the host application exclusively through the SDK — there is no direct DOM or network access to the surrounding app. The sandbox boundary is the security boundary.
The plugin contexts
| Context | Where it renders | Typical use |
|---|---|---|
channel | Replaces the entire chat area | Rich apps: polls, event cards, game dashboards |
sidebar | Compact card in the right panel | Live stats, countdowns, leaderboards |
profile | Card on a user's profile | Now playing, game rank, activity graph |
embed | Inline in the chat feed (ephemeral) | Slash command results: /weather, /rank |
personal | A user's own My plugins page — and, on desktop, panels in the game overlay | A game wiki, a run tracker, anything for yourself. See Personal Plugins |
A plugin can support multiple contexts. The context field in manifest.json is an array.
The tier and permission model
A plugin lists the permissions it needs in declared_permissions; that list is all a user can grant. If it declares any, a channel or sidebar plugin shows a consent card before it loads: Join grants every declared permission, View Without Joining grants none.
The manifest tier is only a label — 1 Passive, 2 Interactive, 3 Activity. It grants nothing and does not decide whether the card appears. The full list of permissions is in Permissions.
ctx.permissions in your plugin always reflects exactly what the current user has granted. Check it before calling methods that require a permission.
The sandbox
Every plugin runs in:
html
<iframe sandbox="allow-scripts">This means:
- The plugin's origin is
null— it cannot read cookies,localStorage, or make credentialed requests to any domain. - The plugin cannot access the Dissent session token, E2EE keys, or any data from the surrounding app.
- All data flows through the SDK. Everything a plugin reads or sends —
Dissent.fetch(),Dissent.getUser(),Dissent.storage— goes through the host, which checks the user's grants on every call. There is no way for a plugin to read channel messages.
Two ways to load the SDK
Script tag (no toolchain needed):
html
<script src="https://app.dissent.chat/sdk/v1/dissent-plugin-sdk.iife.js"></script>
<script>
Dissent.init().then(ctx => { /* your plugin */ });
</script>ES module:
html
<script type="module">
import Dissent from 'https://app.dissent.chat/sdk/v1/dissent-plugin-sdk.js';
const ctx = await Dissent.init();
</script>Both are built from the same source and expose the identical API. There is no npm package; the SDK API Reference lists the TypeScript types to copy into your project.