--- sidebar_position: 2 --- # Manifest Model The manifest is the source of truth for plugin identity, compatibility, runtime shape, capabilities, data, events, UI hints, and distribution metadata. ```ts type TojuPluginInstallScope = 'client' | 'server'; type PluginEventDirection = 'clientToServer' | 'serverRelay' | 'p2pHint'; type PluginEventScope = 'server' | 'channel' | 'user' | 'plugin'; type PluginCapabilityId = | 'profile.read' | 'profile.write' | 'users.read' | 'users.manage' | 'roles.read' | 'roles.manage' | 'messages.read' | 'messages.send' | 'messages.editOwn' | 'messages.deleteOwn' | 'messages.moderate' | 'messages.sync' | 'channels.read' | 'channels.manage' | 'server.read' | 'server.manage' | 'p2p.data' | 'p2p.media' | 'media.playAudio' | 'media.addAudioStream' | 'media.addVideoStream' | 'audio.volume' | 'audio.effects' | 'ui.settings' | 'ui.pages' | 'ui.sidePanel' | 'ui.channelsSection' | 'ui.embeds' | 'ui.dom' | 'storage.local' | 'storage.serverData.read' | 'storage.serverData.write' | 'events.server.publish' | 'events.server.subscribe' | 'events.p2p.publish' | 'events.p2p.subscribe'; interface TojuPluginManifest { schemaVersion: 1; id: string; title: string; description: string; version: string; kind: 'client' | 'library'; scope?: TojuPluginInstallScope; apiVersion: string; compatibility: { minimumTojuVersion: string; maximumTojuVersion?: string; verifiedTojuVersion?: string; }; entrypoint?: string; bundle?: { url: string; entrypoint?: string; }; readme?: string; homepage?: string; bugs?: string; changelog?: string; license?: string; authors?: { name: string; email?: string; url?: string; }[]; capabilities?: PluginCapabilityId[]; events?: { eventName: string; direction: PluginEventDirection; scope: PluginEventScope; maxPayloadBytes?: number; schema?: string; }[]; data?: { key: string; schema?: string; scope: string; storage: 'local' | 'serverData'; }[]; relationships?: { after?: string[]; before?: string[]; conflicts?: string[]; optional?: { id: string; versionRange?: string }[]; requires?: { id: string; versionRange?: string }[]; }; load?: { priority?: 'bootstrap' | 'high' | 'default' | 'low'; }; pluginUser?: { avatar?: string; displayName: string; label?: string; }; settings?: Record; ui?: Record; } ``` ## Required Fields | Field | Meaning | | --- | --- | | `schemaVersion` | Manifest schema version. Currently `1`. | | `id` | Stable plugin id. Use a reverse-DNS or package-style id. | | `title` | Human-readable plugin name. | | `description` | Short explanation shown in plugin UI. | | `version` | Plugin version. | | `kind` | `client` for runtime plugins, `library` for shared dependency-style entries. | | `apiVersion` | Plugin API version expected by the plugin. | | `compatibility.minimumTojuVersion` | Oldest app version the plugin supports. | ## Scope `scope: "client"` installs the plugin for the current client. Omit `scope` for the same behavior. `scope: "server"` marks a plugin as server-scoped. Server-scoped store entries can be installed to a chat server as requirements. Required server plugins are auto-installed for members when that server opens; optional requirements stay listed but do not auto-install. ## Entrypoint and Bundle Use `entrypoint` for a browser-resolvable module relative to the manifest. Use `bundle.url` when publishing a cached browser bundle through a plugin source manifest. Desktop installs cache bundle files into app data and load the cached manifest afterward. ## Events Every server or P2P plugin event should be declared before it is published or subscribed to. ```json { "events": [ { "eventName": "poll:vote", "direction": "p2pHint", "scope": "channel", "maxPayloadBytes": 2048 } ] } ``` ## Data Declarations Use `data` to document plugin-owned data keys and intended storage. - `local` maps to client-local plugin data. - `serverData` maps to local per-user/per-server plugin data. Signal server HTTP persistence for arbitrary plugin data is disabled by design.