Архитектура
0.4 NEW — обзор того, как устроен WASM-runtime и его жизненный цикл.
Из чего состоит
lampac-go/
├── internal/wasmmodules/ ← хост: wazero + ABI + permissions + studio
├── wasm_sdk/
│ ├── tinygo/ ← Go SDK для плагинов (импортируется через replace)
│ └── rust/ ← Rust SDK (cargo path-зависимость)
├── wasm_modules/ ← живые плагины (manifest.json + plugin.wasm)
│ ├── echo/ ← server-плагин с полным host API
│ ├── quality_filter/ ← middleware: фильтрует результаты по качеству
│ └── welcome_toast/ ← client: Lampa.Noty.show один раз
├── wasm_dumps/ ← crash-dumps (ts.json на каждое падение)
├── wasm_studio/ ← staging для Browser Studio (build artefacts)
└── wwwroot/
├── plugins/wasm_loader.js ← runtime для client-плагинов в Lampa
└── wasm_studio.html ← Monaco-редактор для админаЖизненный цикл серверного плагина
- Scan — на старте
wasmmodules.Manager.Scan()обходитwasm_modules/, парсит каждыйmanifest.json, читаетplugin.wasmв память. - Lazy-instantiate — на первый запрос
/lite/{id}создаётсяwazero.Runtime, регистрируются хост-импорты (модульlampac), вызывается_initialize(если есть), плагин остаётся загруженным. - Invoke — на каждый запрос: маршалим
Invocationв JSON, кладём в guest memory через экспортalloc(size), вызываемhandle(ptr, len), читаем ответ. - Hot-reload —
fsnotifywatcher наwasm_modules/ловит change/create наmanifest.jsonи*.wasm, через 300 мс debounce делаетScan(). Старая инстанция закрывается, новая создастся при следующем запросе. - Crash — любая ошибка
handle()пишет дамп вwasm_dumps/{id}/{unix_ns}.json: invocation, путь, headers, размер памяти, последние 50 строк лога плагина. Хранится 20 последних на модуль.
Manifest
{
"id": "my_plugin", // [a-z][a-z0-9_]{1,31}
"name": "Display name",
"version": "0.1.0",
"target": "server", // server|client|both|middleware
"entry_server": "plugin.wasm",
"language": "tinygo", // info-only, для админки
"abi_version": 1, // host ABI rev — несовместимый ребусит
"upstreams": ["collaps"], // только для middleware
"permissions": ["http:example.com", "cache"],// см. /docs/wasm/permissions
"config_schema": [
{"key": "host", "label": "API host", "type": "string", "default": "https://api.example.com"}
]
}Все поля опциональны кроме id и target. entry_server по умолчанию — plugin.wasm.
Reactor-mode
TinyGo с -target wasi по умолчанию вызывает _start, после которого модуль закрывается. Хост обходит это:
cfg := wazero.NewModuleConfig().WithStartFunctions() // пустой список
inst, _ := wz.InstantiateWithConfig(ctx, wasmBytes, cfg)
if init := inst.ExportedFunction("_initialize"); init != nil {
init.Call(ctx) // запускаем reactor-режим вручную
}Это значит плагин должен экспортировать alloc(size i32) i32 и handle(ptr i32, len i32) i64, и желательно не вызывать os.Exit / panic.
Telemetry
Manager.Stats(id) возвращает:
| Поле | Что считает |
|---|---|
invocations / errors |
Счётчики handle()-вызовов |
p50_us / p95_us / p99_us |
Latency-перцентили (последние 256 вызовов) |
http_requests / http_bytes_in/out |
Что плагин выкачал через host_http |
cache_hits / cache_misses / cache_hit_rate |
Эффективность host_cache_* |
last_error / last_error_at / last_ok_at |
Для столбца “что сломалось” в админке |
Что дальше
- ABI / Host imports — точный контракт между плагином и хостом
- TinyGo SDK / Rust SDK — пишем балансер с нуля
- Permissions — capability-based ограничения
- Browser Studio — Monaco в админке