Plugin Manifest reference
Jedes Plugin hat eine plugin.manifest.json-Datei im Root-Verzeichnis. Dies ist die Quelle der Wahrheit für die Identität des Plugins, die Berechtigungen, die es benötigt, die Netzwerkziele, die es anrufen darf, die Verwaltungsseiten, zu denen es beiträgt, und die Aktionsschaltflächen, die es zur Viewer-Oberfläche hinzufügt.
Plugins require Owncast 0.3.0 or later.
Das Manifest ist das, was ein Administrator prüft, bevor er das Plugin installiert. Der Host analysiert es zur Ladezeit und setzt jede Deklaration durch. Nichts im kompilierten Plugin kann eine Fähigkeit gewähren, die das Manifest nicht angefordert hat.
Das Manifest ist einfaches JSON, das das Plugin für den Host beschreibt, unabhängig von der Sprache, in der du den Code geschrieben hast. Für die sprachspezifischen Details siehe die JavaScript oder Python SDK-Referenz.
Minimales Manifest
{
"api": "1",
"name": "My Plugin",
"version": "0.1.0",
"description": "Short description for admins",
"permissions": []
}
api, name und version sind erforderlich. Alles andere ist optional und nur erforderlich, wenn du die entsprechende Funktion verwendest.
Felder auf oberster Ebene
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
api | String | Ja | Version des Manifest-Schemas. Derzeit "1". |
name | String | Ja | Benutzerfreundlicher Anzeigename, der in Verwaltungslisten und Registrierungskarten angezeigt wird. Beispiel: "Awesome Echo Bot". |
slug | String | Nein | Kanonische Kennung (URL-Präfix, Konfigurationsnamespace, Dateiname). Automatisch vom name abgeleitet, wenn weggelassen. Siehe unten. |
version | String | Ja | Die Version deines Plugins. SemVer recommended. Informational metadata for admins and the registry: the host doesn't gate loading on it. |
description | String | Nein | Eine ein Satz lange Zusammenfassung, die der Administrator in der Pluginliste und während der Installation sieht. |
category | String | Nein | Registry browse category. See category. |
permissions | String[] | Nein | Liste der Berechtigungen, die dein Plugin benötigt. Siehe Berechtigungen. |
config | Objekt | Nein | Admin-konfigurierbare Einstellungen, die dein Plugin zur Laufzeit liest. Siehe Konfiguration. |
bot | Objekt | Nein | Chatbot-Konfiguration. Siehe bot. |
network | Objekt | Nein | Ausgehende HTTP-Whitelist, erforderlich, wenn network.fetch gewährt wird. Siehe unten. |
actions | Object[] | Nein | Aktionsschaltflächen, die zur Viewer-Oberfläche hinzugefügt werden sollen. Siehe UI: Aktionsschaltflächen. |
admin | Objekt | Nein | Administrationsseiten, die zur Owncast-Admin-Oberfläche hinzugefügt werden sollen. Siehe UI: Administrationsseiten. |
styles | String[] | Nein | CSS-Dateien, die in die Viewer-Seite eingebettet sind. Siehe styles. |
scripts | String[] | Nein | JavaScript-Dateien, die in die Viewer-Seite eingebettet sind. Siehe scripts. |
extraPageContent | Objekt | Nein | Ein Objekt, das einen Slug und eine optionale HTML-Datei deklariert, die dem Block für zusätzlichen Inhalt des Viewers vorangestellt wird. Siehe extraPageContent. |
tabs | object | no | Viewer-page tabs keyed by stable slug. Siehe tabs. |
name und slug
name ist der benutzerfreundliche Anzeigename. Er kann beliebige Zeichen enthalten, einschließlich Leerzeichen und Satzzeichen, und ist das, was die Administratoren in der Pluginliste sehen, was auf Registrierungsdurchsichtkarten angezeigt wird, und die Standard-Chatbot-Identität.
slug ist die kanonische Kennung. Es steuert:
- Das URL-Präfix des Plugins:
/plugins/<slug>/... - Der Konfigurations- (Schlüssel-Werte-Speicher) Namespace
- Der Dateiname des gebauten Artefakts (
<slug>.ocpkg) - Der Primärschlüssel im Plugin-Register
Slugs sind Kleinbuchstaben, Ziffern und Bindestriche, beginnen mit einem Buchstaben, bis zu 64 Zeichen. Das SDK leitet einen automatisch von name ab, wenn slug weggelassen wird: Leerzeichen und Satzzeichen werden zu einzelnen Bindestrichen zusammengefasst, Buchstaben in Kleinbuchstaben. "Awesome Echo Bot" wird zu awesome-echo-bot. Pinne slug explizit, wenn die automatische Ableitung nicht deinen Wünschen entspricht, oder wenn dein Anzeigename Zeichen außerhalb von ASCII verwendet ("Café Helper" würde ansonsten caf-helper ergeben).
Vermeide es, den Slug nach der Veröffentlichung zu ändern: Die Umbenennung wird für Administratoren wie ein anderes Plugin aussehen, mit einem neuen Konfigurationsspeicher. Das Ändern von name (nur Anzeige) ist sicher. Es ändert nicht die Identität.
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: Chatbot-Identität
Plugins, die in den Chat posten (unter Verwendung von owncast.chat.send), erscheinen unter einem Chatbot-Benutzer. Standardmäßig erscheint der Bot unter dem angezeigten name des Plugins. Überschreibe das mit bot.displayName:
{
"name": "Stream Sidekick",
"bot": {
"displayName": "Sidekick"
}
}
Im Chat postet der Bot als "Sidekick" statt als "Stream Sidekick". Beim ersten Laden des Plugins stellt Owncast einen persistierenden Chat-Benutzer bereit, der auf dem slug des Plugins basiert (damit die Bot-Identität bei Neuinstallationen und Änderungen des Anzeige-Namens erhalten bleibt).
bot.displayName ist nur für Plugins relevant, die die Berechtigung chat.send haben. Es wird andernfalls ignoriert.
config: Admin-konfigurierbare Einstellungen
Deklariere typisierte Einstellungen hier, und Owncast rendert ein bearbeitbares Formular dafür im Admin, das dein Plugin zur Laufzeit mit owncast.config.get liest. Jeder Eintrag hat einen type (string, number oder boolean), einen default und eine 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.
Vollständige Abdeckung, einschließlich wie das Formular gerendert wird, Credential-Masking, Validierung und wo Überschreibungen gespeichert werden, in Konfiguration.
permissions
Jeder Eintrag schaltet einen Teil der Host-APIs frei. Der Host lehnt Aufrufe zu einer Methode ab, deren Berechtigung du nicht deklariert hast.
{
"permissions": ["chat.send", "storage.kv", "network.fetch"]
}
Siehe die Berechtigungsreferenz für die vollständige Liste der Bezeichner und was jeder gewährt.
network: ausgehende HTTP-Whitelist
network.fetch ist durch eine explizite Whitelist von Hostnamen gesperrt. Wenn du network.fetch in permissions deklarierst, benötigst du auch ein Feld network.allowedHosts, das die Hosts auflistet, die du aufrufen wirst:
{
"permissions": ["network.fetch"],
"network": {
"allowedHosts": ["api.discord.com", "*.weather.com"]
}
}
Einträge sind Hostnamen-Globs. Bare Namen wie api.discord.com entsprechen genau. * ist ein Wildcard-Segment, sodass *.weather.com mit api.weather.com und data.weather.com übereinstimmt, aber nicht mit weather.com selbst oder evil.com.
Das Wildcard "*" entspricht jedem Host, aber du musst es explizit schreiben:
{
"network": { "allowedHosts": ["*"] }
}
Das ist beabsichtigt. Administratoren, die das Manifest überprüfen, sehen den Umfang, den sie gewähren. Die meisten Plugins sollten stattdessen die spezifischen Hosts auflisten, die sie aufrufen.
Der Host lehnt das Laden ab, wenn network.fetch gewährt wird, ohne dass ein allowedHosts-Eintrag vorhanden ist.
actions: Aktionsschaltflächen
Aktionsschaltflächen sind klickbare Einträge, die Owncast unter dem Stream anzeigt. Während dein Plugin aktiviert ist, fügt der Host seine Einträge der Liste hinzu, die Owncast bereits anzeigt.
{
"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>"
}
]
}
Jeder Eintrag:
| Feld | Typ | Anmerkungen |
|---|---|---|
title | string | Erforderlich. Die Schaltflächenbeschriftung. |
url | string | Entweder eine absolute https://...-URL oder ein Pfad. Wechselwirkung mit html. |
html | String | Roh-HTML, das in einem Inline-Modul gerendert wird. Wechselwirkung mit url. |
icon | String | Optionale Bild-URL, die auf der Schaltfläche angezeigt wird. Die gleichen Pfadregeln wie url. |
color | Zeichenfolge | Optionale hex-Farbe für den Hintergrund der Schaltfläche. |
Beschreibung | Zeichenfolge | Optional. Im Modalfenster angezeigt, das sich für URL-basierte Aktionen öffnet. |
offenExtern | boolesch | Wenn wahr, öffnet sich die URL in einem neuen Tab anstelle eines Inline-Modals. |
Regeln, die der Host zur Ladezeit durchsetzt:
- Die Berechtigung
ui.modifyist erforderlich. Ohne sie wird das Manifest abgelehnt. - Genau eines von
urloderhtmlpro Eintrag. - Relative URLs (und Icons), die mit
/beginnen, werden automatisch mit dem Namensraum Ihres Plugins vorangestellt."/"wird zu/plugins/my-plugin/."/star.png"wird zu/plugins/my-plugin/star.png. Sichert Ihnen das Hardcoding Ihres Plugin-Namens. - URLs (und Icons), die in Ihren Namensraum aufgelöst werden, benötigen
http.serve, da Sie derjenige sind, der sie bereitstellt. - URLs (und Icons), die auf den Namensraum eines anderen Plugins zeigen, werden abgelehnt. Fängt Tippfehler ab und verhindert, dass ein Plugin die UI eines anderen bewirbt.
Vollständige Abdeckung in UI: Aktionsschaltflächen.
admin: Admin-Seiten
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" }
}
}
}
Jeder Eintrag hat:
| Part | Typ | Hinweise |
|---|---|---|
| object key | Zeichenfolge | Required path glob under the plugin's namespace, such as "/admin" or "/admin/*". |
titel | Zeichenfolge | Erforderlich. Das Tab-Etikett, das in der Admin-UI angezeigt wird. |
icon | Zeichenfolge | Optional. Ein kurzer semantischer Name (gear, wrench, user usw.). |
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.
Vollständige Abdeckung in UI: Admin-Seiten.
styles: CSS-Injektion
Eine Liste von CSS-Dateien, die das Plugin zur Ansichtseite beiträgt. Der Inhalt jeder Datei wird in denselben <style>-Block eingefügt, den Owncast bereits für das benutzerdefinierte CSS der Admins verwendet, sodass Plugins die Seite thematisieren können, ohne dass jeder Beitrag sein eigenes <link>-Tag benötigt.
{
"permissions": ["ui.modify"],
"styles": ["theme.css", "overrides.css"]
}
Pfadregeln entsprechen den URLs von Aktionsschaltflächen:
- Einfache Pfade wie
"theme.css"werden automatisch mit dem Namensraum Ihres Plugins vorangestellt. - Einzel-Schnecken-Pfade wie
"/theme.css"erhalten dasselbe Treatment. - Vollqualifizierte
/plugins/<your-slug>/...-Pfad gibt weiter. - Pfade im Namensraum eines anderen Plugins werden abgelehnt.
http://undhttps://URLs werden abgelehnt. Bündeln Sie externe Assets (Schriften, Bilder) und verweisen Sie darauf mit@font-faceoderurl(...)aus Ihrem CSS, damit ein Admin, der das Manifest überprüft, jede Datei sieht, die auf ihrer Seite landet.- Jeder Eintrag muss mit
.cssenden.
Benötigt nur ui.modify (das Plugin malt innerhalb des Owncast-Chromes). http.serve ist nicht erforderlich: die Bytes jeder Datei werden aus assets/ gelesen und direkt in customStyles auf /api/config eingefügt, nicht unter einer URL bereitgestellt. Der Host erzeugt einen /* plugin: <your-slug> ... */ Kommentar vor jedem Beitrag, sodass ein Leser eine Regel dem Plugin zuordnen kann, das sie bereitgestellt hat.
Für CSS, das von Plugin-Status abhängt, gibt ein onPageStyles Handler es zur Anfragezeit zurück, ohne dass ein Manifestfeld benötigt wird. Seine Ausgabe wird nach diesen statischen Dateien an customStyles angehängt.
Vollständige Abdeckung in UI: Viewer-Stylesheets.
scripts: JavaScript-Injektion
Eine Liste von JavaScript-Dateien, die das Plugin zur Ansichtseite beiträgt. Der Inhalt jeder Datei wird an dieselbe Antwort angehängt, aus der das benutzerdefinierte JavaScript der Admins bereits kommt (/customjavascript), sodass Plugins die Seite erweitern können, ohne dass jeder Beitrag sein eigenes <script>-Tag benötigt.
{
"permissions": ["ui.modify"],
"scripts": ["client.js"]
}
Pfadregeln und erforderliche Berechtigungen entsprechen styles, die auf .js-Dateien angewendet werden (nur ui.modify wird benötigt, und der Host liest von assets/ und fügt in /customjavascript ein). Wickeln Sie Ihr Skript in ein IIFE, sodass keine Deklarationen auf oberster Ebene mit dem JavaScript des Admins oder anderen Plugins in Konflikt stehen. Der Host erzeugt einen // plugin: <your-slug> ... Kommentar vor jedem Beitrag und wickelt jeden Beitrag in einen try/catch, sodass ein Laufzeitfehler eines Plugins nicht die anderen unterbricht.
Für JavaScript, das von Plugin-Status abhängt, gibt ein onPageScripts Handler es zur Anfragezeit zurück, ohne dass ein Manifestfeld benötigt wird. Seine Ausgabe wird nach diesen statischen Dateien an /customjavascript angehängt.
Vollständige Abdeckung in UI: Viewer-Skripte.
extraPageContent: HTML-Block
Ein Objekt, das einen HTML-Block zum Ergänzungsbereich des Betrachters beiträgt, der über den Prosa des Admins auf /api/config vorangestellt wird.
{
"permissions": ["ui.modify"],
"extraPageContent": { "slug": "banner", "content": "content.html" }
}
| Feld | Typ | Hinweise |
|---|---|---|
slug | Zeichenfolge | Erforderlich, wenn content weggelassen wird (der Host übergibt es an onPageContent). Sonst optional. Kleinbuchstaben, Ziffern und Bindestriche, beginnend mit einem Buchstaben. |
content | string | Optional. Relativer Pfad zu einer statischen HTML-Datei in assets/. Wenn vorhanden, werden die Bytes dieser Datei direkt eingefügt. Wenn weggelassen, ruft der Host stattdessen onPageContent auf. |
Statisch (mit content): Der Host liest die Datei zur Anfragezeit und fügt die Bytes ein. Gleiche Pfadregeln wie styles und scripts, die auf einen einzelnen .html Eintrag angewendet werden. Plugin-HTML umgeht den Markdown-Prozessor, sodass Tags und Attribute so durchgelassen werden, wie sie geschrieben wurden.
Dynamisch (ohne content): Implementieren Sie onPageContent({ slug, user? }) in Ihrem Plugin, um HTML zur Anfragezeit zurückzugeben. Verwenden Sie dies, wenn der Inhalt für jeden Ansehen variieren oder auf Live-Daten basieren soll (z. B. personalisierte Begrüßungen oder aktuelle Stream-Statistiken). user ist die Chat-Identität des Zuschauers, sofern authentifiziert.
Benötigt ui.modify. http.serve ist nicht erforderlich, da das HTML in die Konfigurationsantwort eingefügt wird und nicht als URL bereitgestellt wird. Jeder Beitrag wird mit einem <!-- plugin: <your-slug> ... --> Kommentar umwickelt, sodass ein Leser das Markup zuordnen kann.
Vollständige Abdeckung in UI: Zusätzlicher Seiteninhalt.
tabs: Viewer-Seiten-Registerkarten
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 | Hinweise |
|---|---|
| object key | Required stable slug. Kleinbuchstaben, Ziffern und Bindestriche, beginnend mit einem Buchstaben. The host passes this key to onTabContent when content is omitted. |
title | Erforderlich. Das auf der Registerkarte angezeigte Etikett. Muss innerhalb der Registerkarten des Plugins eindeutig sein. |
content | Optional. Relativer Pfad zu einer HTML-Datei unter assets/. Gleiche Pfadregeln wie extraPageContent (automatisch mit Ihrem Namensraum vorangestellt, plattformübergreifende Pfade und http(s):// URLs abgelehnt, muss mit .html enden). 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.
Benötigt 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.
Vollständige Abdeckung in UI: Viewer-Seiten-Registerkarten.
Manifest-zu-Laufzeit-Vertrag
Wenn Ihr Plugin geladen wird, analysiert der Host das Manifest und fordert die Laufzeit dazu auf, sich selbst zu registrieren. 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.
Sie schreiben die Registrierung nicht selbst: das SDK generiert sie aus den Handlern, die Sie definieren (siehe Ihre SDK-Referenz für Informationen dazu, wie Handler in Ihrer Sprache deklariert werden). Zu wissen, dass dieser Vertrag existiert, ist nützlich beim Debuggen. Ein Fehler "Berechtigung zur Laufzeit angefordert, nicht im Manifest deklariert" bedeutet, dass Sie einen Handler hinzugefügt haben, der eine Berechtigung benötigt, die Sie vergessen haben aufzulisten.
Vollständiges Beispiel
Ein nicht triviales Manifest, das die meisten Funktionen nutzt:
{
"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 the English version of this page or help improve translations.
Gabe Kangas