Команды
Все команды движка, сгруппированные по тому, с чем они работают. Каждая вызывается как 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.
В примерах используется эта функция оболочки:
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 | логическое | Брандмауэр включён для сети, в которой машина сейчас |
networks | string[] | Виды сетей, в которых находится машина: 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.*.
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.
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
На сервере это документ, с которым работает редактор каждого браузера.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
document | Experiment | да | Документ |
Результат: строка — записанный путь.
Ошибки: 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 в папке данных. Каждый экспорт — новый файл.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
document | Experiment | да | Документ |
Результат: строка — записанный путь.
Ошибки: те же, что у experiment_save.
experiment_validate
Проверяет, что документ запустится с его активным профилем (или значениями по умолчанию): граф, каждое поле, параметры и то, что сохранён каждый названный в нём секрет. Блокирующая проблема — это ошибка. При успехе сообщает, какие из других профилей не прошли бы проверку, чтобы вы знали об этом до переключения.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
document | Experiment | да | Документ |
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
Один узел с подставленными шаблонами, как его показывает предпросмотр редактора: значения активного профиля и значения переменных, которые вы передали. Секреты показываются как ••••, но не их значения. Имена, у которых нет значения, остаются как написаны и перечисляются.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
document | Experiment | да | Документ |
nodeId | строка | да | Узел |
vars | объект | да | Значения переменных, которые нужно использовать, по именам; {}, если их нет |
Результат: { "node": node, "missing": string[] }.
Ошибки: node.not_found, template.*, secret.store, secret.unsupported.
experiment_send_node
Кнопка Отправить сейчас: выполняет один узел сам по себе тем же кодом, что и запуск. Действие отправляется; ожидание слушает с этого момента, пока не найдёт совпадение или не истечёт время. Узел Отправка WebSocket или Ожидание WebSocket открывает соединение, которое описывает узел Подключение WebSocket. Ничего не отправляется с cookie, а узлы Сетевые помехи и Эмулятор запуска не открываются.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
document | Experiment | да | Документ; значения параметров берутся из его активного профиля |
nodeId | строка | да | Действие или ожидание |
vars | объект | да | Значения переменных, которые читают шаблоны узла; {}, если их нет |
Результат
| Поле | Тип | Значение |
|---|---|---|
detail | строка | Что произошло, по-английски |
response | HttpResponse или 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).
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
document | Experiment | да | Документ |
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 | Его профиль |
loads | object[] | Каждый шаг нагрузки: 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.
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
У каких из заданных имён есть сохранённое значение.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
names | string[] | да | Имена, которые нужно проверить |
Результат: объект, имя → 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.
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; начинается с / |
args | OscArg[] | да | Аргументы; [], если их нет |
Результат: число — отправленные байты.
Ошибки: 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.*, если отправка не удалась.
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.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
request | HttpRequest | да | Запрос |
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.
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).
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
config | WsConfig | да | Куда и как подключаться |
Результат: 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
Один обмен без задачи: подключиться, отправить сообщение, если оно задано, дождаться ответа, если это нужно, закрыть.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
config | WsConfig | да | Куда и как подключаться |
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, который не разбирается).
invoke ws_exchange '{"config":{"url":"ws://127.0.0.1:9001/"},"message":{"text":"{\"type\":\"ping\"}"},"expect":{"mode":"contains","pattern":"pong"}}' \
| jq .reply.textMQTT
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).
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
config | MqttConfig | да | Брокер и способ подключения |
Результат: 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 | число | да | Задача соединения |
filters | string[] | да | Не меньше одного |
Результат: null. Ответ брокера приходит событием mqtt://ack с kind: "unsubscribed".
Ошибки: mqtt.filter_required, mqtt.not_connected.
mqtt_publish_once
Подключается, публикует одно сообщение, ждёт подтверждения, которого требует его QoS (до 6 с), и отключается. Приносит своё соединение под собственным идентификатором клиента — первые 12 символов client_id, -o и число, — поэтому никогда не вытесняет с брокера живое соединение с этим идентификатором.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
config | MqttConfig | да | Брокер; 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 здесь допустим.
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 |
payload | Payload | — | Что несёт каждая датаграмма |
bind | строка | любой | Локальный IP:port, с которого отправляется; пусто или null: 0.0.0.0:0 ([::]:0, если все цели IPv6) |
ttl | число | 1 | IP TTL или лимит хопов multicast; от 1 до 255 |
multicast_loop | логическое | true | Multicast возвращается и на эту машину |
rate, count, duration_s | число | 0 | Только для broadcast_beacon_start |
mode | target |
|---|---|
list | Записи IP:port или host:port, разделённые запятыми, точками с запятой или переводами строки (но не пробелами); имя разрешается, и если у него есть адрес IPv4, берётся он |
broadcast | 255.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 | число | Датаграммы, которые не удалось отправить |
resolved | string[] | Первые 8 адресатов |
summary | строка | Содержимое в одной строке |
error | EngineError | Почему не ушла первая датаграмма; пропускается, если все ушли |
Ошибки: 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, на котором слушать |
groups | string[] | [] | Группы multicast, к которым присоединиться (IPv4) |
interface | строка | любой | Локальный адрес IPv4, через который присоединяться к группам |
reuse | логическое | true | Делить порт с программой, которая уже его слушает (SO_REUSEADDR) |
respond | логическое | false | Отвечать на то, что приходит |
response | Payload | нет | Ответ; нужен вместе с 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, если сокет больше не может принимать.
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 — имя хоста разрешается один раз, при запуске реле |
profile | ImpairProfile | — | Что делать с трафиком |
seed | число | новый | seed розыгрышей: один seed и тот же трафик дают те же потери |
protocol | строка | udp | udp (датаграммы) или tcp (потоки) |
Результат: JobInfo.
Ошибки: node.range (значение профиля вне допустимого диапазона, с min, max и полем), node.too_long, node.bind_invalid, transport.target_invalid, transport.dns (имя цели не найдено), сбои привязки. Задача завершается с wait.receive_failed, если сокет больше не может принимать.
netsim_set_profile
Работающее реле с этого момента вносит помехи по другому профилю, не закрывая своих сокетов.
| Аргумент | Тип | Обязателен | Значение |
|---|---|---|---|
jobId | число | да | Задача реле |
profile | ImpairProfile | да | Новый профиль |
Результат: null.
Ошибки: netsim.not_running, node.range, node.too_long.
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.
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 (записан только размер).
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 | Поля |
|---|---|
osc | target, address, args (OscArg[]) |
udp | target, payload: { "kind": "text", "text" } или { "kind": "hex", "hex" } |
http | request (HttpRequest) |
mqtt | broker (host:port), topic, payload, qos, retain |
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, пока он отключён; иначе пропускается |
exchanges | object[] | Каждый: 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.
invoke emulator_exchanges '{"jobId":5,"after":0}' | jq '.counts, (.exchanges[] | {request, rule, status})'Общие типы
JobInfo
Что возвращает команда, начинающая задачу, и что перечисляет jobs_list: id, kind, label (по-английски, для журналов), params (значения, которые называет подпись; пропускается, если их нет) и started_ms. См. задачи.
OscArg
Один аргумент OSC — его тип и значение:
type | value | Тег OSC |
|---|---|---|
int | 32-битное целое | i |
float | число, отправляется как 32-битное с плавающей запятой | f |
str | строка | s |
long | 64-битное целое | h |
double | число, 64-битное | d |
bool | true или false | T или F |
blob | массив байтов, [222, 173] | b |
nil | нет: { "type": "nil" } | N |
HttpRequest
| Поле | Тип | По умолчанию | Значение |
|---|---|---|---|
method | строка | — | GET, POST… |
url | строка | — | http:// или https:// |
headers | [name, value][] | [] | Заголовки запроса |
body | строка или null | null | Тело |
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 | объект или null | null | { topic, payload, qos, retain }, публикуется брокером при потере соединения |
subscribe | { filter, qos }[] | [] | Подписка сразу, как только соединение поднято |
WsConfig
| Поле | Тип | По умолчанию | Значение |
|---|---|---|---|
url | строка | — | ws:// или wss:// (wss:// доверяет тому, чему система доверяет для HTTPS) |
headers | [name, value][] | [] | Отправляются с запросом на переход |
protocols | string[] | [] | Подпротоколы, которые предложить, в порядке предпочтения |
timeout_ms | число | 10000 | На соединение, TLS и переход вместе |
Сообщения — не больше 16 МиБ в любую сторону.
ImpairProfile
Каждое поле необязательно; то, что пропущено, ничего не делает. Вероятности — от 0 до 1.
| Поле | Диапазон | Значение | UDP | TCP |
|---|---|---|---|---|
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_kbps | 0 или от 8 до 10 000 000 | Ограничение полосы, килобит в секунду; 0 — без ограничения | да | да |
burst_start | от 0 до 1 | Датаграмма начинает пачку потерь | да | — |
burst_length | от 1 до 1000 | В среднем сколько датаграмм длится пачка (нужно вместе с burst_start) | да | — |
offline | true или false | Ничего не проходит | да | да |
reset | от 0 до 1 | Фрагмент потока сбрасывает его соединение | — | да |
stall | от 0 до 1 | Фрагмент потока оставляет его соединение полуоткрытым | — | да |
Frame и CaptureStats
Frame — один захваченный пакет, запрос или сообщение:
| Поле | Значение |
|---|---|
seq | Его номер, растущий |
ts | Когда, миллисекунды с 1970 года |
proto | osc, udp, tcp, http, mqtt, ws… |
dir | tx (отправлен) или 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 |
hex | Hex-дамп первого 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 МиБ). Сверх любого из лимитов самые старые кадры уступают место новым.