Signal Lab как сервер
signal-lab-server — это Signal Lab без окна: тот же движок, отдающий браузеру тот же интерфейс. Поставьте его на машину рядом с оборудованием — стоечный ПК, виртуальную машину шоу-контроля, общий лабораторный компьютер — и открывайте из Chrome, Firefox или Edge в любой точке сети: каждый экран работает, как в настольном приложении, а запуски, отчёты и экспорт скачиваются через браузер.
Скрипты и конвейеры работают с тем же сервером через его HTTP API, а signallab --server отправляет на него запуски.
От настольного приложения сервер отличается несколькими вещами:
- Один сервер — один движок. Все вошедшие на него страницы видят одни и те же работающие задачи, одни и те же библиотеки сигналов и эмуляторов и одну и ту же банку cookie экрана HTTP (Хранить cookie). Кто делит сервер, делит и это.
- Секреты принадлежат серверу: он читает их из своего окружения или из файлов, а из браузера задать их нельзя (см. Секреты).
- Брандмауэр принадлежит хосту. Сервер его никогда не меняет; уведомление о брандмауэре из настольного приложения не появляется.
- Он обновляется вместе со своим образом, а не через средство обновления приложения (см. Обновление).
На хосте с Linux — одной командой
На машине с Linux, у которой есть выход в интернет:
curl -fsSL https://raw.githubusercontent.com/ProAnima/SignalLab/main/deploy/install.sh | shСкрипт:
- ставит Docker, если его нет, — предварительно спросив — штатным установщиком Docker (
get.docker.com); - записывает
compose.yamlв/opt/signallab(в~/signallab, если вы не root); - скачивает образ и запускает сервер с сетью хоста, чтобы OSC, UDP, широковещание, multicast и обнаружение выходили в настоящую сеть;
- ждёт, пока сервер ответит на проверку работоспособности (до 90 секунд);
- печатает адреса, которые нужно открыть, токен доступа для входа и команды, чтобы обновить сервер, прочитать журналы и удалить его;
- если включён ufw или firewalld, предлагает открыть порт сервера (см. Брандмауэр хоста).
Параметры передают после sh -s --:
curl -fsSL https://raw.githubusercontent.com/ProAnima/SignalLab/main/deploy/install.sh | sh -s -- --version 1.0.0 --port 8430| Параметр | Что делает | По умолчанию |
|---|---|---|
--version X.Y.Z | Версия образа (latest или X.Y.Z; ведущая v отбрасывается). | latest |
--port N | Порт, на который заходят браузеры. | 1430 |
--listen IP:PORT | Слушать только один адрес. | 0.0.0.0:<port> |
--dir DIR | Куда положить файл compose. | /opt/<name> от root, иначе ~/<name> |
--name NAME | Имя контейнера и его тома данных: строчные буквы, цифры, - и _. Второму серверу на том же хосте нужно собственное имя. | signallab |
--image NAME | Другой образ или реестр; полное NAME:TAG используется как есть. | ghcr.io/proanima/signallab |
--open-udp PORTS | При включённом брандмауэре пропустить также UDP на этих портах — для мониторов и ожиданий: 9000,9100:9110. | |
--no-firewall | Никогда не менять ufw и firewalld. | |
--yes, -y | Отвечать «да»: установить Docker, открыть брандмауэр, удалить данные с --purge. | |
--uninstall | Остановить и удалить сервер; данные остаются. | |
--purge | Вместе с --uninstall: удалить также данные и токен. | |
--help, -h | Вывести параметры. |
Нужен плагин Docker Compose (пакет docker-compose-plugin), который приносит установщик Docker. Образ собран для x86_64 и arm64.
Чтобы обновить сервер, запустите команду снова: та же команда скачивает самый новый образ (или версию, которую вы задали через --version) и перезапускает сервер; данные и токен остаются. Если вы использовали --name, укажите то же имя.
Собственные настройки — секреты экспериментов, SIGNALLAB_ALLOWED_HOSTS, SIGNALLAB_SECURE_COOKIE за HTTPS — записывают в compose.override.yaml рядом с файлом compose. Docker Compose подмешивает его, а скрипт при каждом запуске переписывает compose.yaml, но override не трогает. compose.yaml, написанный не скриптом, сохраняется как compose.yaml.before-install.
# compose.override.yaml
services:
signallab:
environment:
SIGNALLAB_ALLOWED_HOSTS: lab-pc.example.com,192.0.2.10
SIGNALLAB_SECRET_API_TOKEN: ${API_TOKEN}Чтобы удалить сервер:
curl -fsSL https://raw.githubusercontent.com/ProAnima/SignalLab/main/deploy/install.sh | sh -s -- --uninstallДанные остаются в томе Docker, и повторная установка возвращает их вместе с прежним токеном. --uninstall --purge удаляет и данные, и токен — после вопроса.
Брандмауэр хоста
С сетью хоста сервер слушает собственные порты хоста, поэтому о том, кто до него дотянется, решает брандмауэр хоста. Если включён ufw или firewalld, скрипт спрашивает, прежде чем открыть TCP-порт сервера и UDP-порты из --open-udp, записывает, что он открыл (.firewall рядом с файлом compose), и --uninstall закрывает ровно это. Если вы откажетесь, браузеры на других машинах попадут на сервер только после того, как это разрешит брандмауэр, а мониторы и ожидания услышат другие машины только на тех UDP-портах, которые он откроет.
Docker
Образ
ghcr.io/proanima/signallab для linux/amd64 и linux/arm64 публикуется с каждым выпуском:
| Тег | Что это |
|---|---|
X.Y.Z | Этот выпуск. |
X.Y | Самый новый стабильный выпуск этой линейки. |
latest | Самый новый стабильный выпуск. |
В нём лежат signal-lab-server, собранный интерфейс и командная строка signallab; сервер запускается с такими настройками:
| Переменная | Значение в образе |
|---|---|
SIGNALLAB_LISTEN | 0.0.0.0:1430 |
SIGNALLAB_DATA_DIR | /data |
SIGNALLAB_UI_DIR | /usr/share/signal-lab/ui |
SIGNALLAB_GENERATE_TOKEN | true |
Он работает от имени непривилегированного пользователя (uid и gid 10001), пишет только в /data (том), открывает порт 1430 и каждые 30 секунд проверяет собственную работоспособность.
Запуск
docker run -d --name signallab --network host --restart unless-stopped \
-v signallab-data:/data --read-only --cap-drop ALL --security-opt no-new-privileges \
ghcr.io/proanima/signallab:1.0.0
docker logs signallab # при первом запуске: "Sign in with it:" и токенЗатем откройте http://<host>:1430 и войдите с этим токеном. Чтобы позже увидеть токен снова:
docker exec signallab cat /data/token--read-only, --cap-drop ALL и no-new-privileges необязательны и ничего не стоят: серверу не нужны привилегии, и пишет он только в /data.
Docker Compose
deploy/compose.yaml из репозитория — то же самое в виде файла Compose:
name: signallab
services:
signallab:
image: ${SIGNALLAB_IMAGE:-ghcr.io/proanima/signallab:latest}
container_name: signallab
network_mode: host
environment:
SIGNALLAB_GENERATE_TOKEN: "true"
volumes:
- signallab-data:/data
read_only: true
cap_drop: [ALL]
security_opt: ["no-new-privileges:true"]
restart: unless-stopped
stop_grace_period: 15s
volumes:
signallab-data:docker compose up -d
docker exec signallab cat /data/tokenЗадайте SIGNALLAB_IMAGE=ghcr.io/proanima/signallab:X.Y.Z, чтобы закрепить версию.
Сеть
| Сеть Docker | Что работает | Что не работает |
|---|---|---|
--network host (network_mode: host), на хосте с Linux | Всё: OSC, UDP, TCP, HTTP, WebSocket и MQTT в локальную сеть, прослушиваемые порты, широковещание, multicast, обнаружение. | — |
| Bridge (по умолчанию), с опубликованными портами | Unicast к хостам, до которых достаёт контейнер; слушатели на опубликованных портах (-p 1430:1430 -p 9000:9000/udp). | Широковещание и multicast; ответы на порты, которые не опубликованы. |
| Docker Desktop в Windows или macOS | Unicast и опубликованные слушатели. | Сеть хоста до физической сети. В Windows используйте настольное приложение. |
Том данных
/data хранит всё, что хранит сервер: эксперимент, библиотеки сигналов и эмуляторов, отчёты о запусках, экспорт и токен. Именованный том, как выше, изначально принадлежит пользователю образа. Папка хоста, смонтированная туда, должна быть доступна для записи uid 10001:
sudo mkdir -p /srv/signallab && sudo chown 10001:10001 /srv/signallab
docker run -d --name signallab --network host -v /srv/signallab:/data ghcr.io/proanima/signallab:1.0.0Без Docker
Сервер публикуется как образ. Чтобы запустить signal-lab-server напрямую, соберите его из исходников (см. Сборка):
npm install
npm run build # интерфейс, в dist/
cargo run --release -p signal-lab-serverОн отдаёт dist/ по адресу http://127.0.0.1:1430, только для этой машины и без токена. Добавьте --listen и токен, чтобы открыть его для сети.
Параметры
У каждого параметра есть переменная окружения — для контейнеров. Параметры сильнее переменных.
| Параметр | Переменная | По умолчанию | Что делает |
|---|---|---|---|
--listen IP:PORT | SIGNALLAB_LISTEN | 127.0.0.1:1430 | Где слушать. Любому адресу, кроме loopback, нужен токен. |
--token-file PATH | SIGNALLAB_TOKEN_FILE | Файл с токеном доступа (например, секрет Docker). | |
--token TOKEN | SIGNALLAB_TOKEN | Сам токен доступа. Лучше файл: аргументы видны другим пользователям машины. | |
--generate-token | SIGNALLAB_GENERATE_TOKEN | выкл. | Если токен не задан и адрес не loopback: использовать токен, хранящийся в <data folder>/token, создав его при первом запуске. |
--data-dir PATH | SIGNALLAB_DATA_DIR | Documents/SignalLab в домашней папке пользователя | Папка данных. |
--secrets-dir PATH | SIGNALLAB_SECRETS_DIR | /run/secrets/signallab | Папка секретов только для чтения, по файлу на имя. |
--ui-dir PATH | SIGNALLAB_UI_DIR | ui рядом с программой, иначе ./dist | Собранный интерфейс. Без него отдаётся только API. |
--allowed-host NAME | SIGNALLAB_ALLOWED_HOSTS | Имена хоста, по которым можно обращаться к серверу, через запятую. Они и имена loopback принимаются всегда, с токеном и без; если ни одного не задано, сервер с токеном отвечает на любое имя, а сервер без токена — только на имена loopback (см. Имена хоста). | |
--secure-cookie | SIGNALLAB_SECURE_COOKIE | выкл. | Отправлять cookie сессии только по HTTPS. Включайте за прокси с HTTPS. |
--log FILTER | SIGNALLAB_LOG | info | Что писать в журнал: error, warn, info, debug или по модулям (signal_lab_server=debug). |
--log-format text|json | SIGNALLAB_LOG_FORMAT | text | Строки журнала текстом или по одному объекту JSON на строку. |
Переключатели принимают из своей переменной true или false: SIGNALLAB_GENERATE_TOKEN=true.
| Команда | Что делает |
|---|---|
signal-lab-server token | Печатает новый случайный токен: 64 шестнадцатеричных символа. |
signal-lab-server healthcheck | Завершается с кодом 0, если на --listen отвечает сервер (проверка работоспособности образа). |
signal-lab-server --version | Печатает версию. |
signal-lab-server --help | Печатает все параметры. |
Коды завершения: 0 — остановка по Ctrl+C или SIGTERM; 2 — настройки отклонены (нет токена на доступном адресе, слишком короткий токен, токен задан дважды, файл токена не читается, --generate-token без папки данных); 1 — сервер не может слушать на этом адресе или в папку данных нельзя писать. Причина печатается в stderr.
Токен доступа
Без токена сервер слушает только loopback и обслуживает только эту машину. На любом другом адресе токен нужен, и без него сервер не запускается. Задать его можно тремя способами:
| Способ | Когда использовать |
|---|---|
--generate-token (в образе включён) | Ничего настраивать не нужно: при первом запуске сервер создаёт токен, сохраняет его в <data folder>/token — читать его может только собственный пользователь сервера — и один раз печатает в журнал. При следующих запусках он используется снова, поэтому вошедшие браузеры и скрипты продолжают работать после перезапусков и обновлений. Нужна папка данных (--data-dir). |
--token-file PATH | Собственный токен в файле, например секрет Docker. Перевод строки в конце не считается частью токена. |
SIGNALLAB_TOKEN | Токен в окружении. |
В токене не меньше 24 символов, без пробелов и переводов строки; задавайте его только одним способом. Хороший токен делает signal-lab-server token:
docker run --rm ghcr.io/proanima/signallab:1.0.0 token > signallab_token.txtа Compose передаёт его как секрет (файл должен быть доступен для чтения uid 10001):
services:
signallab:
environment:
SIGNALLAB_TOKEN_FILE: /run/secrets/signallab_token
secrets:
- signallab_token
secrets:
signallab_token:
file: ./signallab_token.txtТокен, заданный явно, сильнее --generate-token, и тогда ничего не создаётся. Чтобы сменить созданный токен, остановите сервер, удалите <data folder>/token и запустите его снова: он создаст и напечатает новый. Повреждённый файл токена сервер сообщит, но не заменит.
Вход
Откройте http://<host>:1430. Сервер с токеном сначала направляет браузер на страницу входа — на языке браузера; вставьте токен один раз, и браузер остаётся вошедшим 7 дней. Кнопка Выйти в интерфейсе завершает сессию. Сессии хранятся в памяти сервера: перезапуск выводит из системы всех.
На loopback без токена входа нет.
Скрипты отправляют токен с каждым запросом, как Authorization: Bearer <token>:
curl -fsS http://192.0.2.10:1430/api/invoke/app_info \
-H "Authorization: Bearer $(cat signallab_token.txt)" \
-H "Content-Type: application/json" -d 'null'См. HTTP API и Безопасность сервера, где изложены правила, на которых всё это держится.
Папка данных
Свои файлы сервер хранит в папке данных: эксперимент, библиотеки сигналов и эмуляторов, отчёты о запусках, экспорт и созданный токен. Это --data-dir (SIGNALLAB_DATA_DIR), /data в образе, а в остальных случаях — Documents/SignalLab в домашней папке пользователя, от имени которого он работает. Папка данных, заданная через --data-dir, создаётся, если её нет, и проверяется при запуске: если сервер не может писать в неё, он останавливается и называет эту папку. См. Файлы.
Скачивание (отчёты, экспорт) идёт только изнутри этой папки.
Секреты
Эксперименты читают секреты как {{secret.NAME}}. На сервере они доступны только для чтения — из:
- переменной окружения
SIGNALLAB_SECRET_NAME, иначе - файла
NAMEв папке секретов (--secrets-dir, по умолчанию/run/secrets/signallab— раскладка секретов Docker).
Перевод строки в конце файла не входит в значение; пустое значение считается незаданным; значение — не больше 16 КиБ. Имена состоят из букв, цифр и _ и не начинаются с цифры. Интерфейс показывает, какие секреты заданы на сервере, но задать секрет из браузера нельзя — значения никогда не попадают туда, где защита слабее, и никогда не выходят обратно (см. Секреты).
В Compose — файл секрета на каждое имя:
services:
signallab:
secrets:
- source: api_token
target: /run/secrets/signallab/API_TOKEN
secrets:
api_token:
file: ./api_token.txtФайл должен быть доступен для чтения uid 10001.
За прокси с HTTPS
Сервер говорит по обычному HTTP. Для HTTPS поставьте перед ним обратный прокси (Caddy, nginx, Traefik), который:
- передаёт заголовок
Hostбез изменений; - пропускает переключения на WebSocket (интерфейс держит одно такое соединение открытым — к
/api/events);
и запустите сервер с --secure-cookie, чтобы cookie сессии ходила только по HTTPS, а --allowed-host задайте равным имени, которым пользуются люди.
Обновление
Сервер обновляется своим образом: средство обновления настольного приложения тут ни при чём.
- Установлен скриптом: запустите ту же команду снова.
- С Compose:
docker compose pull && docker compose up -d. - С
docker run: скачайте новый образ, затем удалите контейнер и запустите его снова с тем же томом.
Данные и токен лежат в томе, поэтому остаются на месте.
Проверка работоспособности
GET /api/health отвечает всем, без токена:
curl -s http://127.0.0.1:1430/api/health
# {"auth":true,"status":"ok","version":"1.0.0"}auth говорит, требует ли сервер токен. signal-lab-server healthcheck спрашивает то же на этой машине и завершается с кодом 0, если сервер ответил; образ запускает её каждые 30 секунд (тайм-аут 5 секунд, 3 попытки), поэтому docker ps показывает контейнер как исправный.
Журналы
Сервер пишет журнал в stdout: docker logs -f signallab. На уровне по умолчанию (info) он сообщает, где слушает и нужен ли токен, свои папки данных и интерфейса, каждую запущенную задачу — мониторы, генераторы, штормы, сканирования, запуски, эмуляторы — с адресом клиента, каждый вход, каждый вход с неверным токеном (как предупреждение) и случаи, когда страница отстаёт от событий. --log debug добавляет каждую команду. Строки раскрашиваются только в терминале (и никогда при заданной NO_COLOR); --log-format json пишет по одному объекту JSON на строку — для сборщика журналов.
Остановка
Ctrl+C, docker stop или SIGTERM закрывает соединения всех страниц, останавливает все задачи — идущий запуск завершается как остановленный — и завершает сервер с кодом 0. Файлы compose дают ему на это 15 секунд.