Plugin Manifest reference
すべてのプラグインには、ルートに plugin.manifest.json ファイルがあります。 これはプラグインのアイデンティティ、必要な権限、呼び出すことが許可されているネットワーク先、管理ページ、およびビューワーUIに追加されるアクションボタンの真実の源です。
Plugins require Owncast 0.3.0 or later.
マニフェストは、プラグインをインストールする前に管理者が確認するものです。 ホストは、ロード時にこれを解析し、すべての宣言を強制します。 コンパイルされたプラグインのどれも、マニフェストで要求されていない権限を付与することはできません。
マニフェストは、作成したコードの言語に依存せず、ホストにプラグインを説明するプレーンなJSONです。 言語特有の詳細については、JavaScript または Python SDKリファレンスを参照してください。
最小マニフェスト
{
"api": "1",
"name": "My Plugin",
"version": "0.1.0",
"description": "Short description for admins",
"permissions": []
}
api、name、および version は必須です。 他のすべてはオプションで、対応する機能を使用する場合にのみ必要です。
トップレベルフィールド
| フィールド | タイプ | 必須 | 説明 |
|---|---|---|---|
api | 文字列 | はい | マニフェストスキーマのバージョン。 現在は "1"。 |
name | 文字列 | はい | 管理者のリストとレジストリカードに表示される人間が読み取れる表示名。 例: "素晴らしいエコボット"。 |
slug | 文字列 | いいえ | 別名 (URLプレフィックス、設定名前空間、ファイル名)。 省略した場合、自動的に name から派生します。 以下を参照してください。 |
version | 文字列 | はい | あなたのプラグインのバージョン。 SemVer recommended. Informational metadata for admins and the registry: the host doesn't gate loading on it. |
description | 文字列 | いいえ | 管理者がプラグインリストやインストール時に見る1文の要約。 |
category | 文字列 | いいえ | Registry browse category. See category. |
permissions | 文字列[] | いいえ | プラグインが必要とする機能のリスト。 権限を参照してください。 |
config | オブジェクト | いいえ | プラグインがランタイムで読み取る管理者設定可能な設定。 構成を参照してください。 |
bot | オブジェクト | いいえ | チャットボットの構成。 botを参照してください。 |
network | オブジェクト | いいえ | アウトバウンドHTTPの許可リストで、network.fetchが付与される時に必要です。 以下を参照してください。 |
actions | オブジェクト[] | いいえ | ビューワーUIに追加するアクションボタン。 UI: アクションボタンを参照してください。 |
admin | オブジェクト | いいえ | Owncastの管理UIに追加する管理ページ。 UI: 管理ページを参照してください。 |
styles | 文字列[] | いいえ | ビューワーページにインラインされたCSSファイル。 stylesを参照してください。 |
scripts | 文字列[] | いいえ | ビューワーページにインラインされたJavaScriptファイル。 scriptsを参照してください。 |
extraPageContent | オブジェクト | いいえ | スラグとオプションのHTMLファイルを宣言するオブジェクト、ビューワーのエクストラコンテンツブロックの先頭に追加されます。 extraPageContentを参照してください。 |
tabs | object | no | Viewer-page tabs keyed by stable slug. tabsを参照してください。 |
name と slug
nameは人間に読める表示名です。 任意の文字、スペースや句読点を含むことができ、管理者がプラグインリストで見るもの、レジストリブラウズカードに表示されるもの、デフォルトのチャットボットのアイデンティティです。
slugは標準的な識別子です。 制御するのは:
- プラグインのURLプレフィックス:
/plugins/<slug>/... - 設定 (キー-値ストア) 名前空間
- ビルドされたアーティファクトのファイル名 (
<slug>.ocpkg) - プラグインレジストリにおける主キー
スラグは小文字の文字、数字、ハイフンで、文字から始まり、最大64文字です。 SDKは、slugが省略された場合に name から自動的に派生します: スペースと句読点は単一のハイフンに圧縮され、文字は小文字になります。 "素晴らしいエコボット" は awesome-echo-bot になります。 自動派生が望ましくない場合や、表示名にASCII以外の文字を使用する場合は、slugを明示的に設定してください(例: "カフェヘルパー" は caf-helper となります)。
リリース後にスラグを変更するのは避けてください: その名前変更は管理者にとって異なるプラグインのように見え、異なる設定ストアになります。 name(表示のみ)を変更することは安全です。 それはアイデンティティを変えません。
category: registry browse category
An optional label that places your plugin in a browse category on the registry and in the admin UI. The canonical values are chat-bots, chat-filters, moderation, authentication, themes, overlays, notifications, integrations, video, analytics, games, admin-utilities, examples, and other.
The SDK's packaging CLI warns when category isn't one of these, but nothing rejects it: the host and registry tolerate unknown categories, they just won't match any browse filter.
bot: チャットボットのアイデンティティ
チャットに投稿するプラグイン(owncast.chat.sendを使用)はチャットボットユーザーとして表示されます。 デフォルトでは、ボットはプラグインの表示nameの下に表示されます。 それを bot.displayName でオーバーライドする:
{
"name": "Stream Sidekick",
"bot": {
"displayName": "Sidekick"
}
}
チャット内で、ボットは "サイドキック" として投稿され、 "ストリームサイドキック" ではありません。 プラグインが最初にロードされるとき、Owncastはプラグインの slug に基づいて永続的なチャットユーザーを提供します(そのためボットのアイデンティティは再インストールや表示名の変更を経ても生存します)。
bot.displayNameは、chat.send権限を持つプラグインにのみ関連します。 それ以外は無視されます。
config: 管理者が設定可能な設定
ここに型付き設定を宣言し、Owncastは管理者用に編集可能なフォームをレンダリングします。あなたのプラグインはランタイムで owncast.config.get でそれを読み取ります。 各項目には type (string、number、または boolean)、default、および description があります:
{
"config": {
"greeting": { "type": "string", "default": "welcome!", "description": "First-join message" },
"cooldownMs": { "type": "number", "default": 2000, "description": "Per-user command cooldown" },
"modOnly": { "type": "boolean", "default": false, "description": "Restrict to moderators" }
}
}
Config keys starting with __ are reserved: the host uses that prefix to inject per-instance state into the plugin runtime, and a manifest declaring one is rejected at load.
完全なカバレッジ、フォームのレンダリング方法、資格情報のマスキング、検証、およびオーバーライドが保存される場所については、構成を参照してください。
permissions
各項目がホストAPIのスライスを解放します。 ホストは、明示的に宣言していないメソッドへの呼び出しを拒否します。
{
"permissions": ["chat.send", "storage.kv", "network.fetch"]
}
完全な識別子のリストとそれぞれが付与するものについては、権限リファレンスを参照してください。
network: アウトバウンドHTTPの許可リスト
network.fetchはホスト名の明示的な許可リストによって制限されています。 permissionsで network.fetchを宣言した場合、呼び出すホストをリストした network.allowedHosts フィールドも必要です:
{
"permissions": ["network.fetch"],
"network": {
"allowedHosts": ["api.discord.com", "*.weather.com"]
}
}
エントリはホスト名のグロブです。 api.discord.com のような裸名は、正確に一致します。 * はワイルドカードセグメントで、 *.weather.com は api.weather.com および data.weather.com には一致しますが、 weather.com 自体や evil.com には一致しません。
ワイルドカード "*" は任意のホストに一致しますが、明示的に書く必要があります:
{
"network": { "allowedHosts": ["*"] }
}
これは意図的です。 マニフェストをレビューする管理者は、付与している範囲を見ることができます。 ほとんどのプラグインは、呼び出す特定のホストをリストすべきです。
network.fetchが allowedHostsエントリなしで付与されている場合、ホストは読み込みを拒否します。
actions: アクションボタン
アクションボタンは、Owncastがストリームの下に表示するクリック可能なエントリです。 プラグインが有効な間、ホストはそのエントリをOwncastがすでに表示しているリストに統合します。
{
"permissions": ["ui.modify", "http.serve"],
"actions": [
{
"title": "Chat Overlay",
"description": "Open the live chat overlay",
"url": "/",
"icon": "/star.png",
"color": "#3b82f6"
},
{
"title": "Issue tracker",
"url": "https://github.com/example/my-plugin/issues",
"openExternally": true
},
{
"title": "About this stream",
"html": "<p>Live every weekday at 8pm UTC.</p>"
}
]
}
各エントリ:
| フィールド | タイプ | ノート |
|---|---|---|
title | string | 必須。 ボタンラベル。 |
url | string | 絶対的な https://... URL またはパスである必要があります。 htmlと互換性がない。 |
html | 文字列 | インラインモーダルでレンダリングされる生のHTML。 urlと互換性がない。 |
icon | 文字列 | ボタンに表示されるオプションの画像URL。 urlと同じパスルール。 |
color | 文字列 | ボタンの背景色(16進数形式)を任意で指定できます。 |
description | 文字列 | 任意。 URLベースのアクションのために開くモーダルに表示されます。 |
openExternally | ブール値 | trueの場合、URLはインラインモーダルの代わりに新しいタブで開かれます。 |
ホストがロード時に施行するルール:
ui.modifyの権限が必要です。 それがないと、マニフェストは拒否されます。- エントリごとに
urlまたはhtmlのいずれかが必要です。 /で始まる相対URL(およびアイコン)は、自動的にプラグインの名前空間に接頭辞付けされます。"/"は/plugins/my-plugin/に変換されます。"/star.png"は/plugins/my-plugin/star.pngに変換されます。 プラグイン名をハードコーディングする必要がありません。- あなたの名前空間に解決するURL(およびアイコン)は、
http.serveが必要です。あなたがそれを提供しているからです。 - 他のプラグインの名前空間を指すURL(およびアイコン)は拒否されます。 タイプミスを捕捉し、あるプラグインが他のプラグインのUIを宣伝するのを防ぎます。
UI: アクションボタンで完全にカバーされています。
admin: 管理ページ
Plugins can register pages that appear in the Owncast admin UI under Plugins. The pages object is keyed by plugin-relative path glob:
{
"permissions": ["http.serve"],
"admin": {
"pages": {
"/admin": { "title": "Settings", "icon": "gear" }
}
}
}
各エントリには:
| Part | タイプ | ノート |
|---|---|---|
| object key | 文字列 | Required path glob under the plugin's namespace, such as "/admin" or "/admin/*". |
title | 文字列 | 必須。 管理UIに表示されるタブのラベルです。 |
icon | 文字列 | 任意。 短い意味的名称(gear、wrench、userなど)。 |
The host derives each page path from its object key. A key of "/admin" maps to /plugins/<your-slug>/admin. Requests matching any key are auth-gated by the host, so unauthenticated requests get a 401 before your plugin code runs.
JSON object order is not significant. Owncast displays admin pages in lexicographic path order. pages must be an object. Do not add a path member to a page value. The host rejects arrays and page values containing the legacy path member.
UI: 管理ページで完全にカバーされています。
styles: CSS注入
プラグインがビューアページに貢献するCSSファイルのリスト。 各ファイルの内容はOwncastがすでに管理者のカスタムCSSに使用している同じ<style>ブロックにインライン化されるため、プラグインがページのテーマを変更できます。各貢献は独自の<link>タグを必要としません。
{
"permissions": ["ui.modify"],
"styles": ["theme.css", "overrides.css"]
}
パスルールはアクションボタンのURLに一致します:
"theme.css"のような生パスは、あなたのプラグインの名前空間に自動的に接頭辞が付けられます。"/theme.css"のようなシングルスラッシュパスも同様に処理されます。- 完全修飾の
/plugins/<your-slug>/...パスはそのまま通過します。 - 他のプラグインの名前空間のパスは拒否されます。
http://とhttps://のURLは拒否されます。 外部アセット(フォント、画像)をバンドルし、CSS内から@font-faceまたはurl(...)で参照します。そうすれば、マニフェストを確認する管理者はページにどのファイルが含まれるかをすべて見ることができます。- 各エントリは
.cssで終わらなければなりません。
ui.modifyのみが必要です(プラグインはOwncastのクローム内で描画します)。 http.serveは必要ありません:各ファイルのバイトはassets/から読み込まれ、/api/configのcustomStylesにインライン化されますが、URLで提供されません。 The host emits a /* plugin: <your-slug> ... */ comment in front of each contribution so a reader can attribute a rule back to whichever plugin shipped it.
プラグインの状態に依存するCSSの場合、onPageStylesハンドラがリクエスト時に返され、マニフェストフィールドはありません。 その出力は、これらの静的ファイルの後にcustomStylesに追加されます。
UI: ビューアスタイルシートで完全にカバーされています。
scripts: JavaScript注入
プラグインがビューアページに貢献するJavaScriptファイルのリスト。 各ファイルの内容は、管理者のカスタムJavaScriptがすでに持っている応答に追加されるので、プラグインは各貢献が独自の<script>タグを必要とせずにページを拡張できます。
{
"permissions": ["ui.modify"],
"scripts": ["client.js"]
}
パスルールと必要な権限はstylesと一致し、.jsファイルに適用されます(ui.modifyだけが必要で、ホストがassets/から読み取り、/customjavascriptにインライン化されます)。 スクリプトをIIFEでラップして、トップレベルの宣言が管理者のJavaScriptや他のプラグインと衝突しないようにします。 ホストは、各貢献の前に// plugin: <your-slug> ...というコメントを出し、各貢献をtry/catchでラップするため、一つのプラグインのランタイムエラーが他を壊さないようにします。
プラグインの状態に依存するJavaScriptの場合、onPageScriptsハンドラがリクエスト時に返され、マニフェストフィールドはありません。 その出力は、これらの静的ファイルの後に/customjavascriptに追加されます。
UI: ビューアスクリプトで完全にカバーされています。
extraPageContent: HTMLブロック
ビューワーの追加コンテンツエリアにHTMLブロックを貢献するオブジェクトで、/api/configで管理者の記述の上に挿入されます。
{
"permissions": ["ui.modify"],
"extraPageContent": { "slug": "banner", "content": "content.html" }
}
| フィールド | タイプ | ノート |
|---|---|---|
slug | 文字列 | contentが省略された場合のみ必須(ホストがonPageContentに渡す)。 そうでなければオプション。 小文字、数字、ハイフン(文字で始まる)。 |
content | string | 任意。 assets/内の静的HTMLファイルへの相対パス。 存在する場合、そのファイルのバイトは直接インライン化されます。 省略された場合、ホストは代わりにonPageContentを呼び出します。 |
静的(contentがある場合):ホストはリクエスト時にファイルを読み取り、バイトをインライン化します。 stylesとscriptsと同じパスルールが適用され、単一の.htmlエントリに対して実施されます。 プラグインHTMLはマークダウンプロセッサをバイパスするため、タグと属性がそのまま通過します。
Dynamic (without content): implement onPageContent({ slug, user? }) in your plugin to return HTML at request time. 視聴者ごとに内容が異なるべき場合や、ライブデータを使用する場合(例えば、パーソナライズされた挨拶や最新のストリーム統計など)にこれを使用します。 userは視聴者のチャットアイデンティティであり、認証されている場合に存在します。
ui.modifyが必要です。 http.serveは不要です。HTMLは設定応答にインライン化され、URLとして提供されません。 Each contribution is wrapped with an <!-- plugin: <your-slug> ... --> comment so a reader can attribute the markup back.
UI: 追加ページコンテンツで完全にカバーされています。
tabs: ビューアページのタブ
The tabs object contributes tabs to the viewer page's tab row next to the built-in About and Followers tabs. Each object key is the tab's stable slug. Every value requires title, and content is optional.
{
"permissions": ["ui.modify"],
"tabs": {
"music": { "title": "Music", "content": "music.html" },
"schedule": { "title": "Schedule", "content": "schedule.html" }
}
}
Each entry has:
| Part | ノート |
|---|---|
| object key | Required stable slug. 小文字、数字、ハイフン(文字で始まる)。 The host passes this key to onTabContent when content is omitted. |
title | 必須。 タブに表示されるラベル。 プラグインのタブ内でユニークでなければなりません。 |
content | 任意。 assets/内のHTMLファイルへの相対パス。 extraPageContentと同じパスルール(あなたの名前空間に自動的に接頭辞が付けられ、クロスプラグインパスとhttp(s):// URLは拒否され、.htmlで終わらなければなりません)。 When omitted, the host calls onTabContent. |
Within each plugin, Owncast displays tabs in lexicographic slug order. JSON object order is not significant. Ordering between tabs from different plugins is unspecified. tabs must be an object. Do not add a slug member to a tab value. The host rejects arrays and tab values containing the legacy slug member.
ui.modifyが必須です。 http.serve is not required: each static tab's HTML is read from assets/ and inlined into the pluginTabs[] array on /api/config. For a dynamic tab, the host passes the object key to onTabContent as slug and inlines the returned HTML.
UI: ビューアページのタブで完全にカバーされています。
マニフェストとランタイムの契約
プラグインがロードされると、ホストはマニフェストを解析し、ランタイムに自分自身を登録するように依頼します。 It compares the two and rejects the load when:
- the slugs don't match (
slugis the canonical identity on both sides) - the runtime uses a permission that wasn't declared in the manifest
version is intentionally not compared. It's informational metadata the host gates nothing on, and the SDK bakes it into the registration from the same manifest at build time anyway.
登録を自分で書くことはありません:SDKは、あなたが定義するハンドラから生成します(ハンドラがあなたの言語でどのように宣言されるかについては、SDKリファレンスを参照してください)。 この契約が存在することを知っていることは、デバッグ時に便利です。 「マニフェストで宣言されていないランタイムでの権限リクエスト」エラーは、リストに入れ忘れた権限が必要なハンドラを追加したことを意味します。
完全な例
ほとんどの機能を行使する非単純なマニフェスト:
{
"api": "1",
"name": "Stream Sidekick",
"slug": "stream-sidekick",
"version": "0.2.0",
"description": "Posts to Discord on stream start, shows an overlay, and adds a Donate button.",
"permissions": [
"chat.send",
"chat.filter",
"storage.kv",
"http.serve",
"http.sse",
"network.fetch",
"notifications.send",
"ui.modify"
],
"bot": {
"displayName": "Sidekick"
},
"network": {
"allowedHosts": ["api.discord.com", "*.example.com"]
},
"actions": [
{
"title": "Donate",
"url": "https://example.com/donate",
"openExternally": true
}
],
"admin": {
"pages": {
"/admin": { "title": "Sidekick settings", "icon": "gear" }
}
},
"styles": ["sidekick.css"],
"scripts": ["sidekick.js"],
"extraPageContent": { "slug": "intro", "content": "intro.html" },
"tabs": {
"schedule": { "title": "Schedule", "content": "schedule.html" }
}
}
Improve this page
See something missing or incorrect? Edit this page and improve the documentation for everyone.
Gabe Kangas