Перейти к содержимому

Команды ​

Все команды движка, сгруппированные по тому, с чем они работают. Каждая вызывается как POST /api/invoke/<command> с объектом JSON из аргументов и отвечает 200 с результатом или 422 с EngineError. Как проходить аутентификацию и что значат статусы — в обзоре API.

Соглашения ​

  • Имена аргументов — в camelCase (jobId, nodeId). Незнакомый команде аргумент или пропущенный обязательный отклоняется с command.args_invalid; этим может завершиться любая команда с аргументами. Команда без аргументов тело не читает.
  • Объекты, переданные как аргумент, — config, request, document, library, emulator, profile — используют собственные имена полей движка, в основном в snake_case (timeout_ms). Поле, которого движок не знает, внутри них игнорируется, поэтому опечатка в необязательном поле молча оставляет значение по умолчанию. Незнакомые поля отклоняет только форма feedback_send.
  • Необязательные аргументы и поля можно пропускать или отправлять как null; значения по умолчанию указаны в таблицах.
  • Результаты — JSON. «null» значит, что команде нечего вернуть.
  • Адреса вида IP:port принимают числовой адрес и порт (127.0.0.1:9000, [::1]:9000); имя хоста там отклоняется. Где в таблице написано IP:port или host:port, имя хоста тоже подходит: оно разрешается при выполнении команды, и если у него есть адрес IPv4, используется он (так localhost:9000 — это 127.0.0.1:9000).
  • Задачи: команда с пометкой Начинает задачу возвращает JobInfo; работа продолжается, пока не закончится сама или не будет остановлена командой job_stop. См. задачи.
  • Пути в результатах относятся к машине, на которой работает движок, — на сервере это путь внутри его папки данных; скачивайте их через /api/files.

В примерах используется эта функция оболочки:

bash
SERVER=http://127.0.0.1:1430
TOKEN=$(cat token.txt)
invoke() {
  curl -sS -X POST "$SERVER/api/invoke/$1" \
    -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
    --data "${2:-}"
}

Приложение ​

app_info ​

Что такое движок и где он работает. Без аргументов.

Результат

ПолеТипЗначение
versionстрокаВерсия Signal Lab, 1.0.0
modeстрокаdesktop или server
secrets_writableлогическоеМогут ли здесь работать secret_set и secret_delete: на сервере false
data_dirстрокаПапка данных на машине, где работает движок
osстрокаwindows, linux…
archстрокаx86_64, aarch64…

get_host_info ​

Имя машины и адрес, с которого она отправляла бы. Без аргументов.

Результат: { "local_ip": string, "hostname": string }. local_ip — адрес IPv4, который система выбирает для трафика в интернет (он определяется без отправки чего-либо), или 127.0.0.1, если такого нет. hostname — имя компьютера или localhost, если система его не сообщает.

firewall_status ​

Пускает ли брандмауэр системы другие машины к этой программе. Брандмауэр для отдельной программы, который можно прочитать, есть только в Windows; в остальных системах applies равно false, а из остального заполнено только program. Без аргументов.

Результат

ПолеТипЗначение
appliesлогическоеЗдесь есть брандмауэр для отдельной программы (Windows)
programстрокаПрограмма, о которой правила
enabledлогическоеБрандмауэр включён для сети, в которой машина сейчас
networksstring[]Виды сетей, в которых находится машина: domain, private, public
allowedлогическоеВходящее правило пропускает UDP к этой программе в текущей сети
blockedлогическоеВходящее правило блокирует эту программу в текущей сети; оно сильнее любого разрешающего
rulesчислоВходящие правила для этой программы любого вида

Ошибки: firewall.failed.

firewall_allow ​

Открывает другим машинам доступ к Signal Lab: система показывает собственный запрос прав администратора, после чего входящие правила программы (включая блокирующее) заменяются одним разрешающим правилом для Signal Lab и одним для командной строки signallab, лежащей рядом. Только для настольного приложения в Windows; сервер отказывает, потому что у его экрана никого нет, чтобы ответить на запрос.

АргументТипОбязателенЗначение
publicлогическоедаРазрешить и в публичных сетях, а не только в частных и доменных

Результат: новый firewall_status.

Ошибки: firewall.server (на сервере), firewall.unsupported (не Windows), firewall.declined (на запрос ответили «Нет»), firewall.failed.

feedback_send ​

Отправляет сообщение разработчикам Signal Lab через узел студии (hub), который пересылает его им по почте.

АргументТипОбязателенЗначение
formобъектдаСообщение, см. ниже. Незнакомые поля отклоняются
Поле formТипПо умолчаниюЗначение
messageстрока—Что произошло; обязательно, не больше 20 000 символов
emailстроканетКуда можно отправить ответ
metaобъект со строками{}Что приложение сообщает о себе (version, os, arch, mode, lang, screen)
screenshots{ name, data }[][]Изображения, data в base64; не больше 6, каждое до 8 МиБ
logs{ name, text }[][]Текстовые файлы; не больше 4, каждый до 2 МиБ

Всё вместе — не больше 15 МиБ.

Результат: { "id": string } — номер, который получают разработчики.

Ошибки: feedback.message_required, feedback.message_too_long, feedback.too_many_files, feedback.file_too_large, feedback.too_large, feedback.invalid, отказы узла студии (feedback.email_invalid, feedback.file_type, feedback.rate_limited, feedback.disabled, feedback.send_failed, feedback.failed) и сетевые transport.*.

bash
invoke app_info
# {"version":"1.0.0","mode":"server","secrets_writable":false,"data_dir":"/data","os":"linux","arch":"x86_64"}

Задачи ​

jobs_list ​

Работающие задачи, самые старые первыми. Без аргументов.

Результат: JobInfo[].

job_stop ​

Немедленно останавливает одну задачу: её сокеты закрываются, её реле, сервер или соединение уходят. Остановленная задача не отправляет job://ended; остановленный запуск не сохраняет отчёт.

АргументТипОбязателенЗначение
idчислодаid задачи

Результат: true, если задача с таким id работала, иначе false.

jobs_stop_all ​

Останавливает все работающие задачи, кто бы их ни начал. Без аргументов.

Результат: null.

bash
invoke jobs_list
# [{"id":3,"kind":"osc-monitor","label":"OSC monitor 0.0.0.0:9000","params":{"bind":"0.0.0.0:9000"},"started_ms":1759600000000}]
invoke job_stop '{"id":3}'
# true

Эксперименты и запуски ​

Эти команды принимают и возвращают документ эксперимента (Experiment): JSON, который сохраняет и экспортирует редактор, с полями version, name, params, profiles, profile, seed, cookies, nodes и edges. Его узлы описаны в разделе узлы; параметры, профили и шаблоны — в разделе данные. Документ — не больше 4 МиБ (file.too_large). Чтобы выполнить эксперимент и дождаться результата, используйте POST /api/run, а не experiment_start.

experiment_load ​

Рабочий эксперимент: experiment.json в папке данных — на сервере тот, что показывает его интерфейс. Если его нет, возвращается стартовый эксперимент. Документы старых версий переносятся на текущую. Без аргументов.

Результат: Experiment.

Ошибки: file.io, file.json_invalid (с path, line и column файла), file.too_large, doc.version_unsupported и остальные проверки doc.*.

experiment_save ​

Заменяет рабочий эксперимент, experiment.json в папке данных. Сначала он записывается во временный файл, поэтому неудачная запись оставляет предыдущий.

WARNING

На сервере это документ, с которым работает редактор каждого браузера.

АргументТипОбязателенЗначение
documentExperimentдаДокумент

Результат: строка — записанный путь.

Ошибки: doc.*, проверки размера документа (param.*, params.too_many, profile.*, profiles.too_many, seed.range), file.too_large, file.io.

experiment_parse ​

Читает эксперимент из текста JSON, как это делает Открыть JSON…. Версии с 1 по 8 переносятся на версию 9, текущую; файл старше версии 8 открывается с выключенным cookies, чтобы запускаться как раньше. Метка порядка байтов пропускается.

АргументТипОбязателенЗначение
textстрокадаТекст файла

Результат: Experiment.

Ошибки: file.json_invalid (line, column), file.too_large, doc.version_unsupported, doc.*, проверки размера из experiment_save.

experiment_export ​

Записывает снимок документа в exports/experiment-<ms>-<16 hex digits>.json в папке данных. Каждый экспорт — новый файл.

АргументТипОбязателенЗначение
documentExperimentдаДокумент

Результат: строка — записанный путь.

Ошибки: те же, что у experiment_save.

experiment_validate ​

Проверяет, что документ запустится с его активным профилем (или значениями по умолчанию): граф, каждое поле, параметры и то, что сохранён каждый названный в нём секрет. Блокирующая проблема — это ошибка. При успехе сообщает, какие из других профилей не прошли бы проверку, чтобы вы знали об этом до переключения.

АргументТипОбязателенЗначение
documentExperimentдаДокумент
overridesобъект со строкаминетЗначения параметров только для этой проверки, как их задаёт Запустить с…

Результат: { "profile": string or null, "error": EngineError }[] — каждый другой профиль, который не прошёл бы проверку (null: значения по умолчанию, без профиля). Пустой список значит, что все профили в порядке.

Ошибки: любой код проверки (doc.*, graph.*, node.*, param.*, profile.*, template.*, loop.*…), run.override_unknown (переопределение параметра, которого нет в документе), secret.missing, secret.store, secret.unsupported.

experiment_resolve ​

Один узел с подставленными шаблонами, как его показывает предпросмотр редактора: значения активного профиля и значения переменных, которые вы передали. Секреты показываются как ••••, но не их значения. Имена, у которых нет значения, остаются как написаны и перечисляются.

АргументТипОбязателенЗначение
documentExperimentдаДокумент
nodeIdстрокадаУзел
varsобъектдаЗначения переменных, которые нужно использовать, по именам; {}, если их нет

Результат: { "node": node, "missing": string[] }.

Ошибки: node.not_found, template.*, secret.store, secret.unsupported.

experiment_send_node ​

Кнопка Отправить сейчас: выполняет один узел сам по себе тем же кодом, что и запуск. Действие отправляется; ожидание слушает с этого момента, пока не найдёт совпадение или не истечёт время. Узел Отправка WebSocket или Ожидание WebSocket открывает соединение, которое описывает узел Подключение WebSocket. Ничего не отправляется с cookie, а узлы Сетевые помехи и Эмулятор запуска не открываются.

АргументТипОбязателенЗначение
documentExperimentдаДокумент; значения параметров берутся из его активного профиля
nodeIdстрокадаДействие или ожидание
varsобъектдаЗначения переменных, которые читают шаблоны узла; {}, если их нет

Результат

ПолеТипЗначение
detailстрокаЧто произошло, по-английски
responseHttpResponse или nullОтвет узла HTTP
varsобъектЧто задал шаг: ответ ожидания или то, что берут из ответа на запрос узлы Извлечь значение, следующие за ним

Значения секретов во всём этом маскируются.

Ошибки: node.not_found, run.not_an_action (не действие и не ожидание), ws.connection_unknown, secret.missing, template.* и то, чем завершится сам шаг: transport.*, wait.timeout, check.*…

experiment_start ​

Начинает запуск, как кнопка Запустить, и сразу возвращается. Его шаги приходят событиями experiment://step, конец — experiment://ended, а отчёт сохраняется в runs/ в папке данных. Ожидания, эмуляторы, реле помех и подписки MQTT открываются до первого шага, поэтому занятый порт приводит к ошибке здесь. Запуск дольше 300 с завершается ошибкой run.timeout. Начинает задачу (experiment).

АргументТипОбязателенЗначение
documentExperimentдаДокумент
overridesобъект со строкаминетЗначения параметров только для этого запуска
seedчислонетseed запуска, от 0 до 9007199254740991; по умолчанию — seed документа, иначе новый

Результат: JobInfo, в params.name — имя эксперимента.

Ошибки: всё, о чём сообщает experiment_validate, seed.range, transport.address_in_use и другие сбои привязки, emulator.*, impair.*, node.params_only (адрес прослушивания или брокер либо топик ожидания MQTT, не зафиксированные к началу запуска) и ошибки mqtt_connect, если брокер, к которому обращается ожидание MQTT, недоступен.

experiment_runs ​

Запуски, прочитанные из их отчётов в runs/, новые первыми. Отчёт, который не удаётся прочитать, пропускается.

АргументТипОбязателенЗначение
nameстроканетТолько запуски эксперимента с именно этим именем
limitчислонетНе больше стольких; по умолчанию 50, не больше 500

Результат: сводки запусков:

ПолеТипЗначение
nameстрокаИмя файла отчёта, run-<ms>-<job>.json: то, что принимает experiment_compare
experimentстрокаИмя эксперимента
started_ms, ended_msчислоМиллисекунды с 1970 года
outcomeстрокаpassed или failed
seedчислоseed запуска
profileстрока или nullЕго профиль
loadsobject[]Каждый шаг нагрузки: node, sent, rps, p95_ms, error_rate, held (все пороги выдержаны)

Ошибки: file.io.

experiment_compare ​

Два запуска рядом, шаг нагрузки за шагом, как их показывает кнопка Сравнить на ленте запуска. Шаги сопоставляются по id узла.

АргументТипОбязателенЗначение
aстрокадаИмя файла отчёта более раннего запуска
bстрокадаИмя файла отчёта более позднего запуска

Результат: { "a": summary, "b": summary, "steps": [...] }, каждый шаг с node, missing_in (a или b, когда шаг есть только в одном запуске), metrics, sent ([a, b]), thresholds_a и thresholds_b (каждый порог как { metric, op, value, actual, held }). В metrics девять метрик, каждая как { metric, a, b, change, percent, worse }: metric — это p50_ms, p90_ms, p95_ms, p99_ms, mean_ms, max_ms, error_rate, rps или missed; change — это b − a; percent — изменение в % от a (null, если a равно 0); worse — что значение изменилось в плохую сторону (выросло, а для rps — упало) на 5 % или больше либо перешло с 0 на что угодно. Шаг, который есть только в одном запуске, никогда не worse. См. нагрузку.

Ошибки: runs.name_invalid (всё, кроме имени файла отчёта, без папок), runs.not_found, file.json_invalid, file.io.

bash
invoke experiment_validate "$(jq '{document: .}' experiment.json)"
# []

Секреты ​

Значения секретов используются в экспериментах как {{secret.NAME}} и никогда не покидают движок: ни одна команда их не возвращает. Где они хранятся, зависит от того, где работает движок:

ГдеХранилищеЗадание и удаление
Настольное приложение, WindowsДиспетчер учётных данных WindowsДа
Настольное приложение, LinuxНетsecret.unsupported
СерверSIGNALLAB_SECRET_<NAME> или файл <NAME> в --secrets-dir (по умолчанию /run/secrets/signallab)Нет: secret.read_only

Имя начинается с латинской буквы или _, продолжается латинскими буквами, цифрами и _ и содержит не больше 128 символов (secret.name_invalid).

secret_status ​

У каких из заданных имён есть сохранённое значение.

АргументТипОбязателенЗначение
namesstring[]даИмена, которые нужно проверить

Результат: объект, имя → true (сохранён) или false.

Ошибки: secret.name_invalid (на сервере), secret.store, secret.too_large (файл на сервере больше 16 КиБ), secret.unsupported.

secret_set ​

Сохраняет значение под именем, заменяя имеющееся. Только настольное приложение.

АргументТипОбязателенЗначение
nameстрокадаИмя
valueстрокадаНе пустое; не больше 16 КиБ

Результат: null.

Ошибки: secret.read_only (на сервере), secret.unsupported, secret.name_invalid, secret.empty, secret.too_large, secret.store.

secret_delete ​

Удаляет сохранённое значение. Удаление несохранённого не считается ошибкой. Только настольное приложение.

АргументТипОбязателенЗначение
nameстрокадаИмя

Результат: null.

Ошибки: secret.read_only (на сервере), secret.unsupported, secret.name_invalid, secret.store.

bash
invoke secret_status '{"names":["API_TOKEN","MQTT_PASSWORD"]}'
# {"API_TOKEN":true,"MQTT_PASSWORD":false}

OSC ​

Экран, которому служат эти команды, описан в разделе OSC.

osc_send ​

Отправляет одно сообщение OSC в одной датаграмме UDP с нового сокета.

АргументТипОбязателенЗначение
targetстрокадаIP:port или host:port, куда отправить; имя разрешается, и если у него есть адрес IPv4, берётся он
addressстрокадаАдрес OSC, /mixer/fader/1; начинается с /
argsOscArg[]даАргументы; [], если их нет

Результат: число — отправленные байты.

Ошибки: node.osc_address (нет начального /; поле address), transport.target_invalid (нет порта или ни одна из двух форм), transport.dns (имя не разрешается), transport.*.

osc_monitor_start ​

Слушает OSC на порту UDP и расшифровывает каждый пакет. Каждый приходит событием osc://message. Начинает задачу (osc-monitor, params.bind).

АргументТипОбязателенЗначение
bindстрокадаIP:port, на котором слушать: 0.0.0.0:9000 — все сетевые карты, 127.0.0.1:9000 — только эта машина

Результат: JobInfo.

Ошибки: node.bind_invalid, transport.address_in_use, transport.address_unavailable, transport.denied, wait.bind_failed. Задача завершается с wait.receive_failed, если сокет больше не может принимать.

osc_generator_start ​

Отправляет поток сообщений OSC, единственный аргумент которых следует за формой волны. Ход работы приходит событием osc://gen-tick. Начинает задачу (osc-gen, params.target, params.address).

АргументТипОбязателенЗначение
configобъектдаСм. ниже
Поле configТипПо умолчаниюЗначение
targetстрока—IP:port или host:port, куда отправлять; имя разрешается один раз, при старте задачи
addressстрока—Адрес OSC; начинается с /
rateчисло—Сообщений в секунду, ограничено от 0,1 до 5000
waveformстрока—sine, triangle, saw (падающая: от max до min, затем сразу обратно), ramp (растущая: от min до max, затем сразу обратно), square, random или constant (max)
freqчисло—Циклов формы волны в секунду
min, maxчисло—Диапазон значения
as_intлогическоеfalseОкруглить и отправлять int вместо float
duration_sчисло0Остановиться через столько секунд; 0 — работать, пока не остановят

Результат: JobInfo.

Ошибки: node.osc_address, transport.target_invalid, transport.dns, transport.*. Задача завершается с ошибкой transport.*, если отправка не удалась.

bash
invoke osc_send '{"target":"127.0.0.1:9000","address":"/cue/go","args":[{"type":"int","value":1}]}'
# 16
invoke osc_monitor_start '{"bind":"0.0.0.0:9000"}'

HTTP и cookie ​

См. HTTP.

http_request ​

Отправляет один запрос HTTP и возвращает ответ. Запрос, который не получил ответа, — отказ в соединении, истёкшее время, имя, которое не разрешается, сертификат, которому не доверяют, — не ошибка команды: ответ сообщает об этом в error и cause.

АргументТипОбязателенЗначение
requestHttpRequestдаЗапрос
cookiesлогическоенетОтправить банку cookie экрана HTTP и сохранить то, что задаст ответ; по умолчанию false

Результат: HttpResponse.

Ошибки: http.client_failed (запрос не удалось даже подготовить).

http_burst_start ​

Отправляет один запрос много раз, по несколько одновременно, и измеряет его. Без rate каждый исполнитель отправляет снова, как только получил ответ; с ним запросы начинаются по твёрдому расписанию, как бы медленно ни приходили ответы, а запрос, который дольше 50 мс после своего момента ждал свободного исполнителя, пропускается и считается пропущенным. Ход работы приходит событием http://burst-progress десять раз в секунду. Начинает задачу (http-burst, params.method, params.url и params.rate, если темп задан).

АргументТипОбязателенЗначение
configобъектдаПоля HttpRequest и перечисленные ниже, в одном объекте
Поле configТипПо умолчаниюЗначение
concurrencyчисло—Не больше стольких одновременно в пути, ограничено от 1 до 512
totalчисло0Остановиться после стольких запросов; 0 — без счёта
duration_sчисло0Остановиться через столько секунд; 0 — без ограничения по времени
rateчисло0Запросов в секунду, от 0,1 до 100 000; 0 — так быстро, как приходят ответы
cookiesлогическоеfalseИспользовать банку cookie экрана HTTP

Если не заданы ни total, ни duration_s, поток работает, пока его не остановят.

Результат: JobInfo.

Ошибки: http.rate_invalid, http.duration_invalid, http.client_failed.

http_cookies ​

Банка cookie экрана HTTP: все cookie, срок которых не истёк. На сервере она одна для всех страниц и скриптов. Без аргументов.

Результат: cookie, у каждого name, value, domain, host_only (нет атрибута Domain: возвращается только хосту, который его задал), path, expires (секунды Unix, null для cookie сессии), secure, http_only и same_site (строка или null).

http_cookies_clear ​

Очищает банку cookie экрана HTTP. Без аргументов.

Результат: null.

bash
invoke http_request '{"request":{"method":"GET","url":"http://127.0.0.1:8080/health","headers":[["Accept","application/json"]],"body":null,"timeout_ms":5000}}' \
  | jq '{status, latency_ms, body}'

WebSocket ​

См. WebSocket. Соединение, которое открывает ws_connect, — это задача; остальные команды называют его по jobId.

ws_connect ​

Открывает WebSocket и держит его открытым. То, что приходит и что отправлено, поступает событием ws://messages каждые 100 мс; состояние соединения — событием ws://state. Начинает задачу (websocket, params.url).

АргументТипОбязателенЗначение
configWsConfigдаКуда и как подключаться

Результат: JobInfo.

Ошибки: ws.url_invalid, ws.header_invalid, ws.protocol_invalid, ws.handshake_status (сервер ответил на переход другим статусом), ws.subprotocol_refused, ws.handshake_failed, transport.*.

ws_send ​

Отправляет одно сообщение по открытому соединению.

АргументТипОбязателенЗначение
jobIdчислодаЗадача соединения
messageобъектда{ "text": "…" } для текстового сообщения или { "hex": "de ad be ef" } для двоичного; ровно одно из двух

Результат: число — отправленные байты.

Ошибки: ws.not_connected, ws.payload_required (ни одного или оба), hex.invalid (в том числе пустой hex), node.too_long (больше 16 МиБ, поле payload; ничего не отправляется, соединение остаётся открытым), ws.closed, transport.* (transport.timeout, когда сервер перестал читать на 10 с).

ws_close ​

Закрывает соединение рукопожатием закрытия и ждёт ответа сервера до 2 с; после этого задача завершается. Когда соединение закончилось, его задачи уже нет, и закрытие — это ws.not_connected.

АргументТипОбязателенЗначение
jobIdчислодаЗадача соединения
codeчислонет1000 или от 3000 до 4999 для собственных кодов приложения; по умолчанию 1000
reasonстроканетНе больше 123 байт; по умолчанию пусто

Результат: { "code", "reason", "by", "error" } — by равно client, server или lost; code равен 1005, если закрытие его не несло, и 1006, если кадра закрытия не было.

Ошибки: ws.close_code, node.too_long, ws.not_connected.

ws_exchange ​

Один обмен без задачи: подключиться, отправить сообщение, если оно задано, дождаться ответа, если это нужно, закрыть.

АргументТипОбязателенЗначение
configWsConfigдаКуда и как подключаться
messageобъектнет{ "text" } или { "hex" }, как у ws_send
expectобъектнетЧего ждать: mode (any, contains, regex, hex; по умолчанию any), pattern (по умолчанию пусто), timeout_ms (по умолчанию 2000)

Если задан expect, а message нет, засчитывается первое подходящее сообщение после подключения — приветствие.

Результат: { "handshake", "sent", "reply", "closed" } — handshake это { url, peer, local, protocol, ms }; sent — отправленные байты или null; reply — { kind, text, hex, bytes, json, ms } или null (json: разобранный текстовый ответ, иначе null; ms: с момента отправки или с момента подключения, если ничего не отправлялось); closed — то, что возвращает ws_close.

Ошибки: те же, что у ws_connect и ws_send, wait.timeout (с ms, unmatched и target), regex.invalid и hex.invalid (pattern, который не разбирается).

bash
invoke ws_exchange '{"config":{"url":"ws://127.0.0.1:9001/"},"message":{"text":"{\"type\":\"ping\"}"},"expect":{"mode":"contains","pattern":"pong"}}' \
  | jq .reply.text

MQTT ​

MQTT 3.1.1 по обычному TCP, QoS 0, 1 и 2. См. MQTT.

mqtt_connect ​

Подключается к брокеру и держит соединение. Команда возвращается, когда брокер принял соединение (CONNACK), поэтому неверный пароль или закрытый порт — её ошибка. Сообщения приходят событием mqtt://messages каждые 100 мс; изменения состояния — mqtt://state; завершённые публикации и отписки QoS 1/2 — mqtt://ack. Начинает задачу (mqtt, params.broker, params.client).

АргументТипОбязателенЗначение
configMqttConfigдаБрокер и способ подключения

Результат: JobInfo.

Ошибки: mqtt.client_id_required, transport.* (отказ, недоступность, dns, timeout через 6 с), mqtt.no_answer (нет CONNACK за 6 с), mqtt.protocol, mqtt.refused_protocol, mqtt.refused_client_id, mqtt.refused_unavailable, mqtt.refused_credentials, mqtt.refused_not_authorized, mqtt.refused.

mqtt_publish ​

Публикует по открытому соединению.

АргументТипОбязателенЗначение
jobIdчислодаЗадача соединения
topicстрокадаТопик: не пустой и без + и #
payloadстрокадаСодержимое, отправляется в UTF-8
qosчислода0, 1 или 2 (больше 2 отправляется как 2)
retainлогическоедаПопросить брокер сохранить сообщение; пустое содержимое с retain очищает retained-значение

Результат: null. Публикацию QoS 1 или 2 позже подтверждает mqtt://ack.

Ошибки: node.topic_wildcard (поле topic) и mqtt.topic_required (поле topic), как у mqtt_publish_once — команда отклоняет их до поиска соединения; mqtt.not_connected.

mqtt_subscribe ​

Подписывает открытое соединение на фильтры. То, что выдал брокер, приходит событием mqtt://state с state: "subscribed".

АргументТипОбязателенЗначение
jobIdчислодаЗадача соединения
filters{ filter, qos }[]даНе меньше одного; qos по умолчанию 0. + и # — подстановочные знаки

Результат: null.

Ошибки: mqtt.filter_required, mqtt.not_connected.

mqtt_unsubscribe ​

Отписывает открытое соединение от фильтров.

АргументТипОбязателенЗначение
jobIdчислодаЗадача соединения
filtersstring[]даНе меньше одного

Результат: null. Ответ брокера приходит событием mqtt://ack с kind: "unsubscribed".

Ошибки: mqtt.filter_required, mqtt.not_connected.

mqtt_publish_once ​

Подключается, публикует одно сообщение, ждёт подтверждения, которого требует его QoS (до 6 с), и отключается. Приносит своё соединение под собственным идентификатором клиента — первые 12 символов client_id, -o и число, — поэтому никогда не вытесняет с брокера живое соединение с этим идентификатором.

АргументТипОбязателенЗначение
configMqttConfigдаБрокер; subscribe не используется
topicстрокадаНе пустой и без + и #
payloadстрокадаОтправляется в UTF-8
qosчислода0, 1 или 2 (больше 2 отправляется как 2)
retainлогическоедаПопросить брокер сохранить сообщение

Результат: строка — сводка, написанная Signal Lab: <topic> → <broker> · <bytes> B · qos<n>, с retained для retained-сообщения.

Ошибки: mqtt.topic_required, node.topic_wildcard и ошибки mqtt_connect, кроме mqtt.client_id_required: пустой client_id здесь допустим.

bash
invoke mqtt_publish_once '{"config":{"host":"127.0.0.1","port":1883,"client_id":"lab"},"topic":"lab/lamp/set","payload":"ON","qos":1,"retain":false}'
# "lab/lamp/set → 127.0.0.1:1883 · 2 B · qos1"

Широковещание, multicast и обнаружение ​

См. широковещание и обнаружение.

DANGER

Широковещание и обход подсети достигают каждого хоста сегмента сети. Отправляйте только в сетях, за которые вы отвечаете.

broadcast_send ​

Отправляет по одной датаграмме каждой цели, один раз.

АргументТипОбязателенЗначение
configобъектдаСм. ниже
Поле configТипПо умолчаниюЗначение
modeстрока—list, broadcast, multicast или sweep
targetстрока—Зависит от режима, см. ниже
portчисло0Порт, только для sweep
payloadPayload—Что несёт каждая датаграмма
bindстрокалюбойЛокальный IP:port, с которого отправляется; пусто или null: 0.0.0.0:0 ([::]:0, если все цели IPv6)
ttlчисло1IP TTL или лимит хопов multicast; от 1 до 255
multicast_loopлогическоеtrueMulticast возвращается и на эту машину
rate, count, duration_sчисло0Только для broadcast_beacon_start
modetarget
listЗаписи IP:port или host:port, разделённые запятыми, точками с запятой или переводами строки (но не пробелами); имя разрешается, и если у него есть адрес IPv4, берётся он
broadcast255.255.255.255:port или адрес, оканчивающийся на .255, с его портом
multicastГруппа от 224.0.0.0 до 239.255.255.255 с её портом
sweepБлок CIDR, 192.0.2.0/24: каждый пригодный хост на port; не больше 1024 хостов, то есть /22 или уже

Результат

ПолеТипЗначение
targetsчислоАдресаты
packets, bytesчислоЧто ушло
errorsчислоДатаграммы, которые не удалось отправить
resolvedstring[]Первые 8 адресатов
summaryстрокаСодержимое в одной строке
errorEngineErrorПочему не ушла первая датаграмма; пропускается, если все ушли

Ошибки: broadcast.target_required, broadcast.not_broadcast, broadcast.ipv6, broadcast.not_multicast, broadcast.sweep_port, broadcast.cidr_invalid, broadcast.prefix_invalid, broadcast.sweep_too_large, node.osc_address (адрес OSC должен начинаться с /), hex.empty, hex.invalid, node.bind_invalid, socket.option_failed, transport.target_invalid, transport.dns, сбои привязки.

broadcast_beacon_start ​

Снова и снова отправляет один и тот же круг — по одной датаграмме на цель. Его счётчики приходят событием broadcast://emit-stat каждые 250 мс. Если неудачных отправок больше 32 и ни одной удачной, он останавливается с указанием причины. Начинает задачу (beacon, params.mode, params.target, params.targets, params.rate).

АргументТипОбязателенЗначение
configобъектдаКак у broadcast_send, с тремя полями ниже
Поле configТипПо умолчаниюЗначение
rateчисло—Кругов в секунду; больше 0, а кругов × целей — не больше 50 000 датаграмм в секунду
countчисло0Остановиться после стольких кругов; 0 — без счёта
duration_sчисло0Остановиться через столько секунд; 0 — пока не остановят

Результат: JobInfo.

Ошибки: те же, что у broadcast_send, broadcast.rate_invalid, broadcast.rate_limit.

discovery_start ​

Слушает порт UDP, ведёт список всех пиров, которые что-то отправляют, и может отвечать на запросы, как это делало бы устройство. Пиры приходят событием broadcast://peers каждые 400 мс. Начинает задачу (discovery, params.bind, params.groups, params.joined).

АргументТипОбязателенЗначение
configобъектдаСм. ниже
Поле configТипПо умолчаниюЗначение
bindстрока—IP:port, на котором слушать
groupsstring[][]Группы multicast, к которым присоединиться (IPv4)
interfaceстрокалюбойЛокальный адрес IPv4, через который присоединяться к группам
reuseлогическоеtrueДелить порт с программой, которая уже его слушает (SO_REUSEADDR)
respondлогическоеfalseОтвечать на то, что приходит
responsePayloadнетОтвет; нужен вместе с respond
respond_delay_msчисло0Ждать столько перед ответом
match_containsстроканетОтвечать только на датаграммы, текст которых содержит это

В списке не больше 512 пиров; последующие не добавляются.

Результат: JobInfo.

Ошибки: node.bind_invalid, broadcast.port_shared (порт занят, а reuse выключен), broadcast.interface_invalid, broadcast.not_multicast, broadcast.join_failed, broadcast.reply_missing, node.osc_address, hex.*, сбои привязки. Задача завершается с wait.receive_failed, если сокет больше не может принимать.

bash
invoke broadcast_send '{"config":{"mode":"list","target":"127.0.0.1:9000, 127.0.0.1:9001","payload":{"kind":"text","text":"PING"}}}' \
  | jq '{packets, errors}'

Помехи ​

Реле между клиентом и его сервером, которое задерживает, теряет, дублирует, искажает, переставляет или ограничивает то, что через него проходит, по UDP или TCP. См. помехи.

netsim_start ​

Запускает реле: то, что приходит на listen, идёт дальше на target, а ответы возвращаются тем же путём, причём в обе стороны с помехами по профилю. Его счётчики приходят событием netsim://stat каждые 250 мс. Начинает задачу (netsim, params.listen, params.target и params.protocol для TCP).

АргументТипОбязателенЗначение
configобъектдаСм. ниже
Поле configТипПо умолчаниюЗначение
listenстрока—IP:port, на котором слушает реле; направьте клиента сюда
targetстрока—IP:port настоящего сервера или host:port — имя хоста разрешается один раз, при запуске реле
profileImpairProfile—Что делать с трафиком
seedчислоновыйseed розыгрышей: один seed и тот же трафик дают те же потери
protocolстрокаudpudp (датаграммы) или tcp (потоки)

Результат: JobInfo.

Ошибки: node.range (значение профиля вне допустимого диапазона, с min, max и полем), node.too_long, node.bind_invalid, transport.target_invalid, transport.dns (имя цели не найдено), сбои привязки. Задача завершается с wait.receive_failed, если сокет больше не может принимать.

netsim_set_profile ​

Работающее реле с этого момента вносит помехи по другому профилю, не закрывая своих сокетов.

АргументТипОбязателенЗначение
jobIdчислодаЗадача реле
profileImpairProfileдаНовый профиль

Результат: null.

Ошибки: netsim.not_running, node.range, node.too_long.

bash
invoke netsim_start '{"config":{"listen":"127.0.0.1:9010","target":"127.0.0.1:9000","profile":{"latency_ms":80,"jitter_ms":20,"loss":0.02}}}'

Шторм и сканер ​

DANGER

Шторм нагружает цель так сильно, как вы попросите, а сканирование проверяет каждый порт диапазона. Направляйте их только на хосты, за которые вы отвечаете.

storm_start ​

Отправляет на одну цель ровный поток датаграмм UDP или соединений TCP. Его счётчики приходят событием storm://stat каждые 250 мс. Начинает задачу (storm, params.protocol, params.target, params.rate).

АргументТипОбязателенЗначение
configобъектдаСм. ниже
Поле configТипПо умолчаниюЗначение
targetстрока—IP:port или host:port; имя разрешается один раз, при старте задачи
protocolстрока—udp: датаграммы; tcp: на каждую единицу — соединение, которое записывает содержимое и закрывается (каждое подключение может занять 500 мс)
sizeчисло—Байт содержимого, ограничено от 1 до 65 507
rateчисло—Единиц в секунду, по расписанию: единица n должна уйти через n / rate секунд после старта, и при каждом пробуждении отправляется всё, что подошло (не больше 256; если расписание отстало дальше, более старые единицы пропускаются); 0 — отправлять так быстро, как получится
duration_sчисло0Остановиться через столько секунд; 0 — пока не остановят

Результат: JobInfo.

Ошибки: transport.target_invalid, transport.dns. Неудавшиеся отправки учитываются в событиях, а не сообщаются как ошибки.

scan_start ​

Пробует подключиться по TCP к каждому порту диапазона и сообщает об открытых, а также о том, что служба говорит первой, если об этом попросили. Открытые порты приходят событием scan://open, ход работы — событием scan://progress. Начинает задачу (scan, params.host, params.from, params.to).

АргументТипОбязателенЗначение
configобъектдаСм. ниже
Поле configТипПо умолчаниюЗначение
hostстрока—Имя хоста или адрес
port_start, port_endчисло—Диапазон, оба конца включены; если заданы наоборот, меняются местами
concurrencyчисло256Попыток одновременно, от 1 до 1024
timeout_msчисло600На порт, от 50 до 10 000
grab_bannerлогическоеfalseПрочитать до 256 байт, которые служба отправит в течение 400 мс после подключения

Результат: JobInfo.

Ошибки: scan.host_required.

bash
invoke scan_start '{"config":{"host":"127.0.0.1","port_start":8000,"port_end":9100,"grab_banner":true}}'

Инспектор ​

Инспектор записывает в виде кадров то, что инструменты отправляют и принимают, пока захват включён. На сервере один Инспектор на все страницы и скрипты. См. Инспектор.

inspect_set_enabled ​

Включает или выключает захват. Пока он выключен, ничего не записывается.

АргументТипОбязателенЗначение
enabledлогическоедаВключить (true) или выключить

Результат: CaptureStats.

inspect_stats ​

Счётчики захвата. Без аргументов.

Результат: CaptureStats.

inspect_snapshot ​

Самые новые кадры, самые старые первыми.

АргументТипОбязателенЗначение
limitчислодаСколько, от 1 до 8192

Результат: Frame[] без их байтов (см. inspect_payload).

inspect_clear ​

Очищает захват и его счётчики. Без аргументов.

Результат: CaptureStats.

inspect_export ​

Записывает все хранимые кадры в capture-<ms>.jsonl или capture-<ms>.txt в папке данных. В jsonl каждая строка — кадр с байтами, которые он хранит, в data, в base64; txt предназначен для чтения, с hex-дампом каждого кадра.

АргументТипОбязателенЗначение
formatстрокадаtxt; всё остальное записывает jsonl

Результат: строка — записанный путь.

Ошибки: inspect.empty, file.io.

inspect_payload ​

Байты, которые хранит кадр, сверх 1 КиБ предпросмотра, который несла его пачка.

АргументТипОбязателенЗначение
seqчислодаНомер кадра

Результат: { "seq", "bytes", "kept", "dump", "hex" } — bytes это размер кадра, kept — сколько из них хранится (до 256 КиБ), dump — каждая строка в виде offset hex |ascii|, hex — простой hex, который отправляет повтор.

Ошибки: inspect.frame_gone (его место заняли более новые кадры), inspect.no_payload (записан только размер).

bash
invoke inspect_set_enabled '{"enabled":true}'
invoke inspect_snapshot '{"limit":20}' | jq '.[] | {seq, proto, dir, summary}'

Библиотека сигналов ​

Библиотека — это signals.json в папке данных. Это только хранилище: сигнал отправляется командой своего транспорта (osc_send, broadcast_send, http_request, mqtt_publish или mqtt_publish_once). См. сигналы и файлы.

signals_load ​

Читает библиотеку. Если файла нет, сначала записывается стартовый набор. Без аргументов.

Результат: { "path": string, "library": library, "seeded": boolean } — seeded истинно, если стартовый набор только что записан. Библиотека — это { "version", "signals": [...], "folders": [...] }: version равна 2 (файл версии 1 возвращается как есть), folders пропускается, если папок нет. У каждого сигнала есть id, name, group (его папка, "A/B"; пусто, если папки нет), note и body.

Ошибки: signals.json_invalid (с path, line, column; файл никогда не заменяется), file.io.

signals_save ​

Заменяет весь файл библиотеки через временный файл в той же папке. Существующий файл, который не читается как библиотека, остаётся как есть.

АргументТипОбязателенЗначение
libraryобъектда{ version, signals, folders }, как его возвращает signals_load

Результат: строка — записанный путь.

Ошибки: signals.json_invalid (файл, который сейчас на диске, не читается, с path, line, column; ничего не записывается), signals.encode, file.io.

body сигнала зависит от его transport:

transportПоля
osctarget, address, args (OscArg[])
udptarget, payload: { "kind": "text", "text" } или { "kind": "hex", "hex" }
httprequest (HttpRequest)
mqttbroker (host:port), topic, payload, qos, retain
bash
invoke signals_load | jq '.library.signals[] | {name, transport: .body.transport}'

Эмуляторы ​

Эмулятор — это Signal Lab, играющий другую сторону: API HTTP, устройство OSC, UDP или TCP, брокер MQTT. Его документ — name, bind, protocol, правила протокола и необязательный outage — описан в разделе эмуляторы. Библиотека — это emulators.json в папке данных.

emulators_load ​

Читает библиотеку эмуляторов. Если файла нет, сначала записывается стартовый набор. Без аргументов.

Результат: { "path", "library": { "version": 1, "emulators": [{ "id", "note", "emulator" }] }, "seeded" }.

Ошибки: emulators.json_invalid (с path, line, column; никогда не заменяется), file.io.

emulators_save ​

Заменяет всю библиотеку эмуляторов через временный файл.

АргументТипОбязателенЗначение
libraryобъектда{ version, emulators }, как его возвращает emulators_load

Результат: строка — записанный путь.

Ошибки: emulators.encode, file.io.

emulator_check ​

Запустится ли эмулятор: всё, что emulator_start проверяет до привязки.

АргументТипОбязателенЗначение
emulatorобъектдаДокумент эмулятора
paramsобъект со строкаминетЗначения, которые его шаблоны читают как параметры

Результат: null, если он запустится.

Ошибки: emulator.*; node.* для поля, которого нет, которое вне диапазона, слишком длинное или неправильно составлено (node.required, node.range, node.too_long, node.bind_invalid, node.target_invalid, node.method_invalid…); param.unknown, template.*, osc.pattern_*, regex.invalid, hex.invalid. В params у каждой есть rule, retained или response, когда проблема в одном из них.

emulator_start ​

Запускает эмулятор как отдельную задачу. Его сокет открыт к моменту, когда команда возвращается. То, что он принимает и на что отвечает, приходит событием emulator://activity каждые 200 мс, если что-то изменилось. Начинает задачу (emulator, params.name, params.protocol, params.local и params.source, если задан source).

АргументТипОбязателенЗначение
emulatorобъектдаДокумент эмулятора
paramsобъект со строкаминетЗначения, которые его шаблоны читают как параметры
seedчислонетЕго seed, от 0 до 9007199254740991; по умолчанию — новый
sourceстроканетЗапись библиотеки, из которой он взят, хранится в задаче как params.source

Результат: JobInfo.

Ошибки: те же, что у emulator_check, seed.range, transport.address_in_use и другие сбои привязки.

emulator_exchanges ​

Что работающий эмулятор принял и на что ответил. Хранит последние 500 обменов.

АргументТипОбязателенЗначение
jobIdчислодаЗадача эмулятора
afterчислонетТолько обмены с номером больше этого; по умолчанию 0
limitчислонетНе больше стольких, от 1 до 500; по умолчанию 500

Результат

ПолеТипЗначение
job_idчислоЗадача
name, protocol, localстрокаЭмулятор, его протокол и адрес, на котором он слушает
countsобъектtotal, unmatched (ни одно правило не сработало), failed, down (пришло, пока он был отключён: его отключение по расписанию или emulator_down), hits (по правилам) и missed (MQTT: сообщения, которые не получил слишком отставший клиент; пропускается, пока 0)
forcedстрокаunavailable, reset или timeout, пока он отключён; иначе пропускается
exchangesobject[]Каждый: seq, ts, from, request, rule (с 1; пропускается, если ни одно правило не сработало), reply, status, fault, ms, error, frame, down и data (запрос в том виде, как его читают шаблоны)

Ошибки: emulator.not_running.

emulator_down ​

Отключает работающий эмулятор, пока его не включат, что бы ни говорило расписание его отключений, или возвращает его. Пока он отключён, эмулятор HTTP отвечает на каждый запрос по fault, устройство TCP и брокер MQTT разрывают соединения и отказывают новым, а устройства OSC и UDP ничего не отвечают.

АргументТипОбязателенЗначение
jobIdчислодаЗадача эмулятора
downлогическоедаОтключить (true) или включить
faultстроканетЧто получают запросы HTTP: unavailable (503 без Retry-After: когда он вернётся, неизвестно), reset (соединение закрывается), timeout (нет ответа); по умолчанию unavailable

Результат: null.

Ошибки: emulator.not_running.

bash
invoke emulator_exchanges '{"jobId":5,"after":0}' | jq '.counts, (.exchanges[] | {request, rule, status})'

Общие типы ​

JobInfo ​

Что возвращает команда, начинающая задачу, и что перечисляет jobs_list: id, kind, label (по-английски, для журналов), params (значения, которые называет подпись; пропускается, если их нет) и started_ms. См. задачи.

OscArg ​

Один аргумент OSC — его тип и значение:

typevalueТег OSC
int32-битное целоеi
floatчисло, отправляется как 32-битное с плавающей запятойf
strстрокаs
long64-битное целоеh
doubleчисло, 64-битноеd
booltrue или falseT или F
blobмассив байтов, [222, 173]b
nilнет: { "type": "nil" }N

HttpRequest ​

ПолеТипПо умолчаниюЗначение
methodстрока—GET, POST…
urlстрока—http:// или https://
headers[name, value][][]Заголовки запроса
bodyстрока или nullnullТело
timeout_msчисло10000На весь обмен
authобъектнет{ "scheme": "basic", "username", "password" }, { "scheme": "digest", "username", "password" } или { "scheme": "bearer", "token" }

Выполняется до 10 перенаправлений. Учётные данные и cookie, введённые для одного хоста, никогда не передаются другому. Запрос Digest отвечает на вызов 401 от сервера и отправляется снова.

HttpResponse ​

ПолеТипЗначение
okлогическоеСтатус 2xx
status, status_textчисло, строкаСтатус; 0 и пусто, если ответа не было
latency_msчислоДо прихода всего тела
headers[name, value][]Заголовки ответа
bodyстрокаТело как текст, не больше 256 КиБ
body_bytesчислоПолный размер тела
truncatedлогическоеbody обрезано на 256 КиБ
errorстрока или nullПочему не было ответа, каждый слой причины
causeстрока или nullКакого рода сбой: refused, timeout, dns, unreachable, reset, address_in_use, address_unavailable, denied, tls, target_invalid, failed — то же, что коды transport.*
digestобъектТолько для запроса Digest, получившего 401; иначе пропускается. challenged: на вызов ответили, и запрос отправлен снова. error: почему этого не удалось сделать, EngineError (http.digest_not_offered, http.digest_unsupported, http.digest_invalid, http.digest_other_origin) или null

Payload ​

Что несёт датаграмма широковещания или обнаружения: { "kind": "osc", "address", "args" }, { "kind": "text", "text" } (отправляется как есть, без завершающего нуля) или { "kind": "hex", "hex" } (de ad be ef, deadbeef, 0xDE,0xAD — всё, кроме шестнадцатеричных цифр, игнорируется).

MqttConfig ​

ПолеТипПо умолчаниюЗначение
hostстрока—Имя или адрес брокера
portчисло—Обычно 1883
client_idстрока—Не пустой; другое соединение с тем же идентификатором брокер вытесняет
username, passwordстрокапустоusername отправляется, если не пуст; password — только вместе с username
keep_alive_sчисло60Пинги идут вдвое чаще; 0 — без них
clean_sessionлогическоеtrueФлаг CONNECT
willобъект или nullnull{ topic, payload, qos, retain }, публикуется брокером при потере соединения
subscribe{ filter, qos }[][]Подписка сразу, как только соединение поднято

WsConfig ​

ПолеТипПо умолчаниюЗначение
urlстрока—ws:// или wss:// (wss:// доверяет тому, чему система доверяет для HTTPS)
headers[name, value][][]Отправляются с запросом на переход
protocolsstring[][]Подпротоколы, которые предложить, в порядке предпочтения
timeout_msчисло10000На соединение, TLS и переход вместе

Сообщения — не больше 16 МиБ в любую сторону.

ImpairProfile ​

Каждое поле необязательно; то, что пропущено, ничего не делает. Вероятности — от 0 до 1.

ПолеДиапазонЗначениеUDPTCP
nameне больше 60 символовПодпись для ленты запуска и отчётадада
latency_msот 0 до 60 000Задержка, добавляемая ко всемудада
jitter_msот 0 до 60 000Добавка до этого значения, разыгрывается каждый раздада
lossот 0 до 1Датаграмма теряетсяда—
duplicateот 0 до 1Датаграмма отправляется дваждыда—
corruptот 0 до 1В датаграмме переворачивается один битда—
reorderот 0 до 1Датаграмма придерживается, чтобы более поздние её обогналида—
rate_kbps0 или от 8 до 10 000 000Ограничение полосы, килобит в секунду; 0 — без ограничениядада
burst_startот 0 до 1Датаграмма начинает пачку потерьда—
burst_lengthот 1 до 1000В среднем сколько датаграмм длится пачка (нужно вместе с burst_start)да—
offlinetrue или falseНичего не проходитдада
resetот 0 до 1Фрагмент потока сбрасывает его соединение—да
stallот 0 до 1Фрагмент потока оставляет его соединение полуоткрытым—да

Frame и CaptureStats ​

Frame — один захваченный пакет, запрос или сообщение:

ПолеЗначение
seqЕго номер, растущий
tsКогда, миллисекунды с 1970 года
protoosc, udp, tcp, http, mqtt, ws…
dirtx (отправлен) или rx (принят)
sourceИнструмент, который его захватил: osc-monitor, broadcast, netsim…
job_idЕго задача или null
local, remoteАдреса: IP:port этой стороны и другой (URL или брокер для HTTP, WebSocket и MQTT). У кадра, прошедшего через реле, local — адрес, на котором слушает реле, а remote — куда шёл кадр; направление завершает его verdict (· client→target, · target→client)
bytesЕго размер
summaryОдна строка
detailРасшифровка в несколько строк или null
hexHex-дамп первого 1 КиБ или null
verdictЧто с ним стало — dropped, sampled, статус — или null
keptСколько из bytes хранится (до 256 КиБ); 0, если записан только размер
publishТолько для публикации MQTT: { broker, topic, qos, retain, text } — брокер как host:port и то, является ли хранимое, то есть содержимое сообщения, текстом UTF-8. Отсутствует у всех остальных кадров

CaptureStats: enabled, total (записанные кадры), bytes, skipped (записанные, но никогда не отправленные в интерфейс), buffered (хранимые кадры), capacity (8192), held (хранимые байты содержимого) и held_limit (64 МиБ). Сверх любого из лимитов самые старые кадры уступают место новым.