---
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<string, unknown>;
  ui?: Record<string, unknown>;
}
```

## 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.
