Permissions
Зачем
Когда permissions не задан в манифесте, плагину разрешено всё (legacy/permissive default — для приставочных JS-модулей и встроенных плагинов из wasm_modules/). Как только админ добавляет хоть одно правило — режим переключается на explicit allowlist: всё, что не разрешено, запрещено.
Это критически важно для hub.alcopa.cc — маркетплейс плагинов, где автор плагина не равен админу lampac-go.
Грамматика
{
"permissions": [
"http:*", // любой HTTP/HTTPS
"http:example.com", // только этот хост (включая порт)
"http:*.example.com", // wildcard subdomain — НЕ матчит сам apex
"http:api.example.com", // exact subdomain
"cache", // host_cache_get / host_cache_set
"proxy", // host_proxy_url (signed /proxy/ URL)
"log", // host_log — всегда разрешён, для документации
"config" // host_config — всегда разрешён
]
}| Правило | Что разрешает |
|---|---|
http:* |
Любой HTTP-запрос через host_http |
http:example.com |
Точное совпадение Hostname() |
http:*.foo.com |
a.foo.com, a.b.foo.com — но не foo.com |
cache |
host_cache_get + host_cache_set |
proxy |
host_proxy_url |
log, config |
Всегда разрешены — указывайте для документации |
Hostname() нечувствителен к регистру и игнорирует порт: правило http:example.com пропустит https://example.com:8443/x.
Пример: balanced плагин
{
"id": "my_balancer",
"permissions": [
"http:api.example.com",
"http:*.cdn.example.com",
"cache",
"proxy"
]
}Плагин может:
- ходить только на API-домен и CDN с любым subdomain,
- кешировать ответы,
- подписывать стрим-URL через
/proxy/.
Всё остальное (другие домены, отправка email через host_http куда попало) — заблокировано на уровне runtime.
Пример: пассивный middleware
{
"id": "voice_dedup",
"target": "middleware",
"upstreams": ["collaps", "filmix"],
"permissions": ["log"]
}Middleware читает upstream-ответы из inv.Config.__upstreams — это не host_http. Сам в сеть не ходит, кеш не нужен. Минимальные права.
Пример: client-target ratings widget
{
"id": "ratings",
"target": "client",
"permissions": ["log"]
}Client-плагины не используют host_http (ходить через сеть в Lampa-плагине = fetch() через JS, не через WASM). Permissions для client-target проверяются только если плагин both и часть запросов идёт через server-сторону.
Что происходит при отказе
host_http с заблокированным URL:
{ "error": "permission denied: host evil.com not in allowlist" }Плагин получает это как ошибку HTTP-вызова, а не аборт — может среагировать (например, fallback на разрешённый источник).
host_proxy_url с отсутствующим proxy:
{ "error": "permission denied: proxy not granted" }host_cache_get/set без cache:
Getвсегда возвращает0(cache miss),Setтихо ничего не делает.
Это сделано осознанно — плагин не может отличить “кеш отключён” от “key не найден”, что не даёт ему oracle по защите.
Production checklist для маркетплейса
При публикации плагина на hub.alcopa.cc:
-
permissionsявно перечислен — нетhttp:*без обоснования. - Если плагин ходит на 2-3 домена — перечисли поимённо.
-
cacheуказан, только если плагин действительно кеширует. -
proxyуказан, только если плагин выдаёт стрим-URL. - Plugin reviewer проверяет, что список совпадает с поведением.
Почему не WASI capabilities
WASI имеет свою модель capability (preopens, fd-permissions). Мы её не используем потому что:
- наш host API — не файлы, а
host_http/host_cache_*/host_proxy_url, - WASI capabilities не покрывают допустимые HTTP-домены (только сам факт наличия сети).
Текущая модель — application-level capabilities, проверяемые в Go перед каждым host-вызовом.
Roadmap
- Глобальная политика “запрещать
http:*для не-builtin плагинов” - Time-budget per request (сколько мс плагин может бегать) — сейчас защищает только timeout context’а, а не CPU
- Memory cap per module (linear memory size limit) — wazero это умеет, нужно прокинуть в Manager