Plugin Manifest reference
Cada plugin tem um arquivo plugin.manifest.json em sua raiz. Esta é a fonte da verdade para a identidade do plugin, as permissões que ele precisa, os destinos de rede que é permitido chamar, as páginas de administração que ele contribui e os botões de ação que ele adiciona à interface do visualizador.
Plugins require Owncast 0.3.0 or later.
O manifesto é o que um administrador revisa antes de instalar o plugin. O host o analisa na hora do carregamento e aplica cada declaração. Nada no plugin compilado pode conceder uma capacidade que o manifesto não solicitou.
O manifesto é JSON simples que descreve o plugin para o host, independentemente da linguagem que você usou para escrever o código. Para detalhes específicos da linguagem, consulte a referência do SDK JavaScript ou Python.
Manifesto mínimo
{
"api": "1",
"name": "My Plugin",
"version": "0.1.0",
"description": "Short description for admins",
"permissions": []
}
api, name, e version são obrigatórios. Todo o resto é opcional e só necessário quando você utiliza o recurso correspondente.
Campos de nível superior
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
api | string | sim | Versão do esquema do manifesto. Atualmente "1". |
name | string | sim | Nome de exibição legível por humanos exibido nas listas de administradores e nos cartões de registro. Exemplo: "Awesome Echo Bot". |
slug | string | não | Identificador canônico (prefixo da URL, namespace de configuração, nome do arquivo). Derivado automaticamente de name se omitido. Veja abaixo. |
version | string | sim | A versão do seu plugin. SemVer recommended. Informational metadata for admins and the registry: the host doesn't gate loading on it. |
description | string | não | Resumo de uma frase que o administrador vê na lista de plugins e durante a instalação. |
category | string | não | Registry browse category. See category. |
permissions | string[] | não | Lista de capacidades que seu plugin precisa. Veja Permissões. |
config | object | não | Configurações configuráveis pelo administrador que seu plugin lê em tempo de execução. Veja Configuração. |
bot | object | não | Configuração do chatbot. Veja bot. |
network | object | não | Lista de permissão de saída HTTP, necessária quando network.fetch é concedido. Veja abaixo. |
actions | object[] | não | Botões de ação a serem adicionados à interface do visualizador. Veja UI: Botões de ação. |
admin | object | não | Páginas de administração a serem adicionadas à interface administrativa do Owncast. Veja UI: Páginas administrativas. |
styles | string[] | não | Arquivos CSS incorporados na página do visualizador. Veja styles. |
scripts | string[] | não | Arquivos JavaScript incorporados na página do visualizador. Veja scripts. |
extraPageContent | object | não | Um objeto declarando um slug e um arquivo HTML opcional prependido ao bloco de conteúdo extra do visualizador. Veja extraPageContent. |
tabs | object | no | Viewer-page tabs keyed by stable slug. Veja tabs. |
name e slug
name é o nome de exibição legível por humanos. Pode conter quaisquer caracteres, incluindo espaços e pontuação, e é o que os administradores veem na lista de plugins, o que aparece nos cartões de navegação do registro e a identidade padrão do chatbot.
slug é o identificador canônico. Ele controla:
- O prefixo da URL do plugin:
/plugins/<slug>/... - O namespace do armazenamento de configuração (chave-valor)
- O nome do arquivo do artefato construído (
<slug>.ocpkg) - A chave primária no registro de plugins
Slugs são letras minúsculas, dígitos e hifens, começando com uma letra, até 64 caracteres. O SDK automaticamente deriva um de name quando slug é omitido: espaços e pontuações colapsam em hífens simples, letras em minúsculas. "Awesome Echo Bot" se torna awesome-echo-bot. Fixe o slug explicitamente quando a derivação automática não é o que você deseja, ou quando seu nome de exibição usa caracteres fora do ASCII ("Café Helper" resultaria em caf-helper).
Evite mudar o slug após o lançamento: a renomeação parecerá ser um plugin diferente para os administradores, com um novo armazenamento de configuração. Mudar name (apenas exibição) é seguro. Isso não muda a identidade.
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: identidade do chatbot
Plugins que postam no chat (usando owncast.chat.send) aparecem sob um usuário de chatbot. Por padrão, o bot aparece sob o name de exibição do plugin. Substitua isso com bot.displayName:
{
"name": "Stream Sidekick",
"bot": {
"displayName": "Sidekick"
}
}
No chat, o bot posta como "Apoio" ao invés de "Apoio ao Stream". Na primeira vez que o plugin carrega, o Owncast provisiona um usuário de chat persistente chaveado no slug do plugin (então a identidade do bot sobrevive reinstalações e mudanças de nome de exibição).
bot.displayName é apenas relevante para plugins que têm a permissão chat.send. É ignorado de outra forma.
config: configurações configuráveis pelo administrador
Declare as configurações tipadas aqui e o Owncast renderiza um formulário editável para elas no administrador, que seu plugin lê em tempo de execução usando owncast.config.get. Cada entrada tem um type (string, number, ou boolean), um default, e uma 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.
Cobertura total, incluindo como o formulário é renderizado, mascaramento de credenciais, validação, e onde as substituições são armazenadas, em Configuração.
permissions
Cada entrada desbloqueia um segmento das APIs do host. O host rejeita chamadas a um método cuja permissão você não declarou.
{
"permissions": ["chat.send", "storage.kv", "network.fetch"]
}
Veja a referência de permissões para a lista completa de identificadores e o que cada um concede.
network: lista de permissões HTTP de saída
network.fetch é controlado por uma lista explícita de nomes de host permitidos. Se você declarar network.fetch em permissions, você também precisa de um campo network.allowedHosts listando os hosts que você chamará:
{
"permissions": ["network.fetch"],
"network": {
"allowedHosts": ["api.discord.com", "*.weather.com"]
}
}
As entradas são coringas de nome de host. Nomes simples como api.discord.com correspondem exatamente. * é um segmento curinga, então *.weather.com corresponde a api.weather.com e data.weather.com, mas não a weather.com em si ou evil.com.
O coringa "*" corresponde a qualquer host, mas você deve escrevê-lo explicitamente:
{
"network": { "allowedHosts": ["*"] }
}
Isso é intencional. Administradores revisando o manifesto veem o escopo que estão concedendo. A maioria dos plugins deve listar os hosts específicos que eles chamam.
O host rejeita o carregamento se network.fetch for concedido sem uma entrada allowedHosts.
actions: botões de ação
Os botões de ação são entradas clicáveis que o Owncast exibe sob a transmissão. Enquanto seu plugin estiver habilitado, o host mescla suas entradas na lista que o Owncast já exibe.
{
"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>"
}
]
}
Cada entrada:
| Campo | Tipo | Notas |
|---|---|---|
title | string | Obrigatório. O rótulo do botão. |
url | string | Ou uma URL absoluta https://... ou um caminho. Mutuamente exclusivo com html. |
html | string | HTML bruto renderizado em um modal embutido. Mutuamente exclusivo com url. |
icon | string | URL de imagem opcional mostrada no botão. Mesmas regras de caminho como url. |
color | string | Cor hexadecimal opcional para o fundo do botão. |
descrição | string | Opcional. Mostrado no modal que se abre para ações baseadas em URL. |
openExternally | booleano | Se true, a URL se abre em uma nova aba em vez de um modal embutido. |
Regras que o host aplica no momento do carregamento:
- A permissão
ui.modifyé necessária. Sem isso, o manifesto é rejeitado. - Exatamente uma de
urlouhtmlpor entrada. - URLs relativas (e ícones) que começam com
/são prefixadas automaticamente ao namespace do seu plugin."/"se torna/plugins/meu-plugin/."/star.png"se torna/plugins/meu-plugin/star.png. Evita que você tenha que codificar o nome do seu plugin. - URLs (e ícones) que se resolvem em seu namespace requerem
http.serve, pois você é quem os fornece. - URLs (e ícones) apontando para o namespace de outro plugin são rejeitados. Cata erros de digitação e impede que um plugin anuncie a interface de outro.
Cobertura completa em UI: Botões de ação.
admin: páginas administrativas
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" }
}
}
}
Cada entrada tem:
| Part | Tipo | Notas |
|---|---|---|
| object key | string | Required path glob under the plugin's namespace, such as "/admin" or "/admin/*". |
título | string | Obrigatório. O rótulo da aba mostrado na interface administrativa. |
ícone | string | Opcional. Um nome semântico curto (gear, wrench, user e assim por diante). |
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.
Cobertura completa em UI: Páginas administrativas.
styles: injeção de CSS
Uma lista de arquivos CSS que o plugin contribui para a página do visualizador. O conteúdo de cada arquivo é incorporado no mesmo bloco <style> que o Owncast já usa para o CSS personalizado do administrador, permitindo que os plugins temas a página sem que cada contribuição precise de sua própria tag <link>.
{
"permissions": ["ui.modify"],
"styles": ["theme.css", "overrides.css"]
}
As regras de caminho correspondem às URLs dos botões de ação:
- Caminhos simples como
"theme.css"são prefixados automaticamente ao namespace do seu plugin. - Caminhos de barra única como
"/theme.css"recebem o mesmo tratamento. - Caminhos totalmente qualificados
/plugins/<seu-slug>/...são aceitos sem modificações. - Caminhos no namespace de outro plugin são rejeitados.
- URLs
http://ehttps://são rejeitadas. Agrupe ativos externos (fontes, imagens) e referencie-os com@font-faceouurl(...)dentro do seu CSS, para que um administrador que revise o manifesto veja cada arquivo que estará em sua página. - Cada entrada deve terminar com
.css.
Requer apenas ui.modify (o plugin é renderizado dentro do chrome do Owncast). http.serve não é necessário: os bytes de cada arquivo são lidos de assets/ e incorporados em customStyles em /api/config, não fornecidos em uma 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.
Para CSS que depende do estado do plugin, um manipulador onPageStyles retorna isso no momento da requisição, sem campo de manifesto. Sua saída é adicionada a customStyles após estes arquivos estáticos.
Cobertura completa em UI: Folhas de estilo do visualizador.
scripts: injeção de JavaScript
Uma lista de arquivos JavaScript que o plugin contribui para a página do visualizador. O conteúdo de cada arquivo é adicionado à mesma resposta de onde já vem o JavaScript personalizado do administrador (/customjavascript), permitindo que os plugins estendam a página sem que cada contribuição precise de sua própria tag <script>.
{
"permissions": ["ui.modify"],
"scripts": ["client.js"]
}
As regras de caminho e permissões necessárias correspondem a styles, aplicadas a arquivos .js (apenas ui.modify é necessário, e o host lê de assets/ e incorpora em /customjavascript). Encapsule seu script em um IIFE para que declarações de nível superior não colidam com o JavaScript do administrador ou outros plugins. O host emite um comentário // plugin: <seu-slug> ... na frente de cada contribuição e encapsula cada contribuição em um try/catch para que um erro de tempo de execução de um plugin não quebre os outros.
Para JavaScript que depende do estado do plugin, um manipulador onPageScripts retorna isso no momento da requisição, sem campo de manifesto. Sua saída é adicionada a /customjavascript após esses arquivos estáticos.
Cobertura completa em UI: Scripts do visualizador.
extraPageContent: bloco HTML
Um objeto que contribui com um bloco HTML para a área de conteúdo extra do visualizador, inserido acima do texto do administrador em /api/config.
{
"permissions": ["ui.modify"],
"extraPageContent": { "slug": "banner", "content": "content.html" }
}
| Campo | Tipo | Notas |
|---|---|---|
slug | string | Obrigatório apenas quando content está ausente (o host o passa para onPageContent). Opcional caso contrário. Letras minúsculas, dígitos e hífens, começando com uma letra. |
content | string | Opcional. Caminho relativo para um arquivo HTML estático em assets/. Quando presente, os bytes desse arquivo são incorporados diretamente. Quando omitido, o host chama onPageContent em vez disso. |
Estático (com content): o host lê o arquivo no momento da requisição e incorpora os bytes. Mesmas regras de caminho que styles e scripts, aplicadas a uma única entrada .html. O HTML do plugin ignora o processador de markdown, de modo que as tags e atributos passam como escrito.
Dynamic (without content): implement onPageContent({ slug, user? }) in your plugin to return HTML at request time. Use isso quando o conteúdo deve variar por visualizador ou depender de dados ao vivo (por exemplo, cumprimentos personalizados ou estatísticas de stream atuais). user é a identidade de chat do visualizador, presente quando autenticado.
Requer ui.modify. http.serve não é necessário porque o HTML é incorporado na resposta de configuração, não servido como uma URL. Each contribution is wrapped with an <!-- plugin: <your-slug> ... --> comment so a reader can attribute the markup back.
Cobertura completa em UI: Conteúdo extra da página.
tabs: abas da página do visualizador
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 | Notas |
|---|---|
| object key | Required stable slug. Letras minúsculas, dígitos e hífens, começando com uma letra. The host passes this key to onTabContent when content is omitted. |
título | Obrigatório. O rótulo exibido na aba. Deve ser único dentro das abas do plugin. |
content | Opcional. Caminho relativo para um arquivo HTML em assets/. Mesmas regras de caminho que extraPageContent (prefixo automático ao seu namespace, caminhos entre plugins e URLs http(s):// rejeitados, deve terminar em .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.
Requer 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.
Cobertura completa em UI: Abas da página do visualizador.
Contrato manifesto-runtime
Quando seu plugin é carregado, o host analisa o manifesto e pede ao runtime para se registrar. 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.
Você não escreve o registro você mesmo: a SDK o gera a partir dos manipuladores que você define (veja sua referência de SDK sobre como os manipuladores são declarados em sua linguagem). Saber que esse contrato existe é útil ao depurar. Um erro "permissão solicitada em tempo de execução não declarada no manifesto" significa que você adicionou um manipulador que precisa de uma permissão que você esqueceu de listar.
Exemplo completo
Um manifesto não trivial que utiliza a maioria dos recursos:
{
"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