Plugin Permissions
Каждый плагин Owncast работает в песочнице и не имеет неявного доступа к чему-либо за пределами самого плагина. Чтобы выполнять полезные задачи (читать чат, публиковать в fediverse, получать URL-адрес, записывать в хранилище ключ-значение), ваш плагин обращается к хосту через методы owncast.*. Almost every one of those methods is gated by a permission you declare in your manifest. The exceptions are a handful of ambient methods that reach nothing sensitive and need no permission: owncast.log.*, owncast.timer.*, reading your own bundled assets, and owncast.config.get.
Plugins require Owncast 0.3.0 or later.
Когда администратор устанавливает плагин, вкладка Разрешения на странице деталей плагина точно указывает, какие разрешения плагин запрашивал, на простом языке. Это граница доверия: администратор может установить плагин от третьей стороны, не проверяя каждую строку кода, поскольку манифест — это верхняя граница того, что может делать плагин.
Идентификаторы разрешений и модель доверия ниже одинаковы, независимо от того, какой SDK вы используете. Методы owncast.* здесь упоминаются по их каноническим именам. Для точного написания на вашем языке смотрите справку по JavaScript или Python SDK.
Как это работает
-
Вы объявляете разрешения в
plugin.manifest.json:{ "permissions": ["chat.send", "storage.kv"] } -
Администратор проверяет их при включении. Страница деталей плагина Owncast перечисляет каждое разрешение с описанием, доступным для человека.
-
Хост применяет их во время выполнения. Calling
owncast.chat.send(...)withoutchat.sendin your manifest never reaches Owncast: the host logs the denial and the call does nothing. Mutating calls that report an outcome raise an error (moderation,users.register,auth.grantSession,kv.set,videoConfig.write,actions.add,actions.clear, and everysqlmethod), readers return an empty or zero value, and calls that return nothing become silent no-ops.fs.write,fs.delete, andstorage.uploadreport failure in their return value instead of raising. -
Хост отслеживает изменения. Ваш установленный плагин объявляет разрешения, которые он использует во время выполнения. Хост сравнивает это с манифестом и отказывается загружать плагин, если во время выполнения запрашивается больше, чем разрешает манифест. Вы не можете тайком получить дополнительный доступ, заменив файл плагина после факта.
Повторное одобрение, когда разрешения увеличиваются
If you ship an update that asks for more permissions than the admin previously approved, the old approved version keeps running (it holds only the approved permissions) and the new package waits as pending. The plugin list shows a "needs re-approval" badge. The admin reviews the new permissions in the Permissions tab and clicks Approve to accept the expanded set and load the update. Сокращение разрешений проходит безмолвно.
Эффективные возможности установленного плагина никогда не увеличиваются без повторного согласия администратора.
Справочник разрешений
chat.send
Предоставляет:
owncast.chat.send(text): отправить от имени бота плагинаowncast.chat.sendAction(text): отправить сообщение "/me"owncast.chat.sendTo(clientId, text): личное сообщение подключенному клиентуowncast.chat.replyTo(msg, text): whisper a reply back to whoever sent a chat message (sugar oversendTo)owncast.chat.system(body): отправить системное сообщение без пользовательской идентичности, отображенное как серверное уведомление (тело — это HTML)
Сообщения проходят через обычный поток чата Owncast (фильтры, ограничения по времени, сохранение, модерация). Плагины не могут отправлять сообщения от произвольных имен или выдавать себя за реальных пользователей.
chat.history
Предоставляет:
owncast.chat.history(limit?): читать последние сообщения чатаowncast.chat.clients(): список подключенных клиентов чата
Только для чтения.
chat.moderate
Предоставляет:
owncast.chat.deleteMessage(messageId): скрыть сообщение от зрителейowncast.chat.kick(clientId): отключить клиента чата
chat.filter
Предоставляет возможность определить filterChatMessage(msg): видеть каждое сообщение чата перед его трансляцией, с возможностью переписать или удалить его.
Фильтрация происходит в режиме реального времени для каждого сообщения чата, поэтому администратору нужно явно видеть это. Хост отклоняет загрузку, если плагин определяет filterChatMessage, не объявив это разрешение.
users.read
Grants:
owncast.users.list(): читать список пользователей чатаowncast.users.get(id): читать запись одного пользователя
users.moderate
Предоставляет:
owncast.users.setEnabled(id, enabled, reason?): включить или отключить пользователяowncast.users.banIP(ip): заблокировать IP от участия в чате
users.register
Grants owncast.users.register({ authId, displayName?, scopes?, profileUrl?, handle?, public? }): find or create an authenticated Owncast user for an external identity and return its userId. The authId is a stable, provider-scoped identifier such as "github:583231". Pass it raw, without prefixing your slug. The host records the slug separately and scopes every lookup to that pair, so two plugins cannot collide with or spoof each other's users.
The optional profileUrl, handle, and public fields attach a verified external identity. The profile URL must be empty or an absolute HTTP(S) URL. Set public to true only after the viewer opts into public display. These profile fields are captured on the first registration.
Вот так плагин превращает сторонний логин (OAuth, Discord, общий пароль) в реального пользователя Owncast с аутентифицированной идентичностью чата. Сам по себе он не защищает сайт и не выдает сессию: комбинируйте его с auth.gate, чтобы создать входной узел, или используйте его отдельно для создания проверенных идентичностей чата.
auth.gate
Предоставляет входную точку для проверки пользователей:
owncast.auth.grantSession({ userId, ttl? }): issue a signed session for an already-registered user (seeusers.register)owncast.auth.endSession(): очистить текущую сессию зрителя (выход)- опциональный обработчик
onAuthCheck: повторная проверка сессии зрителя при каждой загрузке страницы
Плагин, содержащий auth.gate, является поставщиком идентичности. While it is enabled, viewers must authenticate through it before they can reach the page, chat, or the API. The operator selects one cumulative access mode on the plugin's Authentication tab to decide whether Owncast-hosted video and stream status also require a session. Только один плагин auth.gate может быть активирован одновременно, и входной узел работает в закрытом режиме: если плагин недоступен, зрителей не допускают, а не пропускают. Смотрите Аутентификация для полной модели.
storage.kv
Grants owncast.kv.get(key), owncast.kv.set(key, value), and the JSON helpers owncast.kv.getJSON(key, fallback?) and owncast.kv.setJSON(key, value): a per-plugin namespaced key/value store. Плагины не могут читать ключи друг друга.
Состояние сохраняется между перезагрузками и перезапусками хоста.
storage.upload
Grants owncast.storage.upload(name, data): upload a file to Owncast's public file area and get back a URL. Полезно для значков, динамически сгенерированных изображений, вложений в сообщения fediverse.
storage.fs
Grants owncast.fs.*: a private, sandboxed filesystem at data/plugin-storage/<your-slug>/files/ that your plugin can read, write, list, and delete within. Полезно для кэшей, файлов данных, созданных в виде логов в стиле добавления или всего, что нужно сохранить в виде реальных файлов, а не строк ключ/значение.
В отличие от storage.upload, эти файлы остаются серверной стороной: они никогда не обслуживаются по HTTP. Каждый путь ограничен собственным каталогом плагина: плагин не может читать файлы другого плагина или покинуть свою песочницу (../ и абсолютные пути возвращаются внутрь).
storage.sql
Grants owncast.sql.*: one private SQLite database per plugin, at data/plugin-storage/<your-slug>/db/plugin.db. owncast.sql.exec(sql, params?) runs statements, owncast.sql.query(sql, params?) returns matching rows, and owncast.sql.queryRow(sql, params?) reads a single row. Reach for this instead of storage.kv when you need to sort, filter, or aggregate rather than just remember a value. See owncast.sql.* for the methods in both languages, the per-call limits, and the SQL the host refuses.
The database is private to your plugin and separate from Owncast's own database. The storage.fs sandbox is rooted at files/, so db/ is not a path owncast.fs.* refuses but one it cannot express, and the filesystem quota walk covers files/ only, so the two quotas stay independent: the database has its own 128 MiB cap, and files written through storage.fs count against a separate 256 MiB quota.
Plugin databases are not included in Owncast's database backups, so treat the contents as rebuildable or export what matters yourself. SQL data is retained when a plugin is uninstalled, the same as its config and its storage.fs files, so a reinstall finds its tables where it left them. An admin who wants the space back deletes data/plugin-storage/<your-slug>/.
network.fetch
Предоставляет owncast.http.fetch(url, opts?): синхронный исходящий HTTP.
Требуется список разрешенных хостов network.allowedHosts в манифесте. Хост отклоняет загрузку, если network.fetch предоставляется без списка разрешенных. Каждый вызов проверяется на соответствие списку разрешенных. Хосты, которые не соответствуют, возвращают ошибку, прежде чем какие-либо байты покинут сервер.
{
"permissions": ["network.fetch"],
"network": { "allowedHosts": ["api.discord.com", "*.weather.com"] }
}
Широкий символ "*" разрешен, но должен быть явно записан, чтобы администраторы, проверяющие манифест, видели масштаб. Пользовательский интерфейс администратора отображает полный список allowedHosts на вкладке Разрешения рядом с строкой network.fetch, поэтому оператор сервера, проверяющий плагин, точно видит, к каким хостам он может получить доступ, не распаковывая .ocpkg.
events.emit
Grants owncast.events.emit(eventType, payload). Pass the receiving plugin's
fully qualified <recipient-slug>.<hook> name. The host does not rewrite the
emitted name. Declaring and receiving a plugin-owned custom hook does not
require a permission.
http.serve
Предоставляет разрешение HTTP маршрутизатора хоста для отправки запросов по адресу /plugins/<your-slug>/* в ваш плагин. Это охватывает как статические файлы в вашем каталоге public/, так и динамические запросы, маршрутизируемые к вашему обработчику onHttpRequest.
Без этого разрешения весь URL-адрес /plugins/<your-slug>/ возвращает 404.
http.sse
Предоставляет owncast.sse.send(channel, event, data) и открывает конечную точку, принадлежащую хосту, по адресу /plugins/<your-slug>/_sse/<channel>, к которой браузеры подключаются через EventSource. Независимо от http.serve. Плагин может отправлять события без обслуживания каких-либо других маршрутов.
server.read
Предоставляет API для чтения потоков и состояния сервера:
owncast.stream.current(): состояние живого потокаowncast.stream.broadcaster(): входная телееметрияowncast.server.info(): имя сервера, версия, сводкаowncast.server.socials(): настроенные социальные ссылкиowncast.server.emotes(): custom chat emotes configured on this serverowncast.server.federation(): настройки fediverseowncast.server.tags(): настроенные теги
videoconfig.read
Предоставляет owncast.videoConfig.read(): читать выходные и кодирующие параметры (кодеки, уровень задержки, варианты потоков).
videoconfig.write
Предоставляет owncast.videoConfig.write(partial): изменять параметры конфигурации видеовыхода.
Высокое доверие. Изменения применяются при следующем запуске потока. Хост не перезапускает активную трансляцию. Администраторы должны предоставлять это с осторожностью.
notifications.send
Предоставляет API уведомлений для ведущего:
owncast.notifications.discord(text): через настроенный веб-хук Discord стримераowncast.notifications.browserPush({ title, body, url? }): to subscribed browsersowncast.notifications.fediverse({ type, body, image?, link? }): fediverse-formatted notification
fediverse.inbound
Предоставляет подписку на все семь входящих событий плагина Fediverse:
fediverse.followfediverse.likefediverse.repostfediverse.quotefediverse.mentionfediverse.replyfediverse.activity
fediverse.activity принимает сырый JSON-объект проверенной активности. Это выполняется дополнительно к любому соответствующему специализированному событию. Это разрешение охватывает только получение активности. Публикация от имени учетной записи Owncast требует отдельного разрешения fediverse.post.
fediverse.post
Предоставляет owncast.fediverse.post(text): сделать публичную публикацию в fediverse от имени учетной записи Owncast.
Высокое доверие: публикации выходят под личным именем стримера в fediverse и не могут быть тихо отменены. Администраторы должны предоставлять это с осторожностью.
ui.modify
Предоставляет возможность размещать пользовательский интерфейс внутри собственного хрома Owncast:
- Объявление
manifest.actions(кнопки действий под потоком). - Вызов
owncast.actions.add(...)/.clear()во время выполнения. - Объявление
manifest.styles(CSS встроено в страницу просмотра). - Объявление
manifest.scripts(JavaScript встроено в страницу просмотра). - Объявление
manifest.extraPageContent(HTML-блок, добавленный перед дополнительной областью контента зрителя). - Объявление
manifest.tabs(дополнительные вкладки в строке вкладок страницы зрителя). - Реализация обработчика
onPageStylesилиonPageScripts(CSS или JavaScript, возвращаемые в момент запроса, без поля манифеста).
Без этого разрешения манифесты, которые объявляют любое из этих полей, отклоняются при загрузке. Обработчики onPageStyles и onPageScripts не имеют поля манифеста, поэтому они не отклоняются при загрузке. Хост просто не вызывает их, если плагин не имеет ui.modify. Каждое из этих полей попадает внутрь страницы просмотра, а не остается в пространстве URL плагина, поэтому администратору необходимо видеть разрешение, чтобы понять, что плагин взаимодействует с хост-UI.
Ни одно из четырех полей внедрения не требует http.serve, как и две обработчика. Хост читает каждый файл из каталога assets/ плагина (не из URL) или вызывает обработчик, и встраивает результат в существующие конфигурации / пользовательские ответы JS, так что ui.modify само по себе достаточно.
Сводная таблица
| Разрешение | Предоставляет |
|---|---|
chat.send | owncast.chat.send, .sendAction, .sendTo, .replyTo, .system |
chat.history | owncast.chat.history, .clients |
chat.moderate | owncast.chat.deleteMessage, .kick |
chat.filter | Подписка на filterChatMessage (чтение, изменение или удаление каждого сообщения чата). |
users.read | owncast.users.list, .get |
users.moderate | owncast.users.setEnabled, .banIP |
users.register | owncast.users.register: найти или создать аутентифицированного пользователя для внешней идентичности |
auth.gate | owncast.auth.grantSession, .endSession и обработчик onAuthCheck: быть авторизационными воротами сайта |
storage.kv | Хранилище ключей/значений с пространством имен для каждого плагина |
storage.upload | Загрузить файлы в публичную область файлов Owncast |
storage.fs | Private, sandboxed server-side filesystem at data/plugin-storage/<your-slug>/files/ |
storage.sql | Private per-plugin SQLite database at data/plugin-storage/<your-slug>/db/plugin.db |
network.fetch | Исходящий HTTP. Также требует network.allowedHosts |
events.emit | Создавать пользовательские события для других плагинов |
http.serve | Обслуживать HTTP по адресу /plugins/<your-slug>/* |
http.sse | Отправлять события в реальном времени через owncast.sse.send и конечную точку /_sse/ |
server.read | Считывать состояние потока, конфигурацию сервера, кодировать телеметрию |
videoconfig.read | Читать конфигурацию выхода/трансляции |
videoconfig.write | Изменять конфигурацию видео вывода (применяется при следующем запуске потока) |
notifications.send | Отправлять уведомления Discord, браузера или федиверса |
fediverse.inbound | Подписаться на все семь входящих событий: fediverse.follow, .like, .repost, .quote, .mention, .reply, и .activity |
fediverse.post | Публикация в федиверсе (с ограничением по скорости) |
ui.modify | Добавить кнопки действий или вкладки в интерфейс просмотра Owncast. Встраивание CSS, JavaScript или HTML плагина в страницу просмотра |
Принцип минимальных привилегий
Объявляйте только то, что вы действительно используете. Чем уже ваш манифест, тем проще решение о доверии администратора. Если вы обнаружите, что перечисляете каждое разрешение, отступите и посмотрите, действительно ли ваш плагин должен быть двумя плагинами.
Если вы перестали использовать разрешение во время разработки, уберите его из манифеста. Сжатие молчаливо. Убирая неиспользуемые записи, нет трения.
Improve this page
See something missing or incorrect? Edit this page and improve the documentation for everyone.
Gabe Kangas