diff --git a/HISTORY/2026-08-16-session-log.md b/HISTORY/2026-08-16-session-log.md
index cf77ea5..7a276f7 100644
--- a/HISTORY/2026-08-16-session-log.md
+++ b/HISTORY/2026-08-16-session-log.md
@@ -1203,3 +1203,21 @@ paho connect() rc=0 даже при CONNACK≠0 (on_connect обязателен
подставляется через {{VERSION}} (без лишней "v").
- v0.1.8 (digest db648725dfe6) задеплоен; проверка: "/" 200, favicon/logo
200 (image/svg+xml), chip v0.1.8.
+
+---
+
+## 34. Лендинг переписан — «понятно, без воды» (v0.1.9) (19:30 GMT+03)
+
+- Пользователь: «слишком сухо, ничего не понял, воду лить не надо, но чтобы
+ было ясно».
+- Переработано содержимое landing.html (дизайн/структура сохранены):
+ - hero: «Это база телеметрии. Устройства шлют показания по MQTT...»;
+ - «Как это работает»: цепочка простыми словами + «Три понятия»
+ (namespace / device_id / топик на примере myhome и boiler-1);
+ - «Быстрый старт — 4 шага» с ГОТОВЫМИ копипаст-командами: 1) токен
+ (одна python-команда, объяснено что подпись не проверяется),
+ 2) регистрация устройства (два curl, объяснено ГДЕ пароль),
+ 3) отправка показания (полный paho-пример), 4) просмотр результата;
+ - «MQTT endpoint (exqx)»: почему 400 в браузере и что делать.
+- v0.1.9 (digest fb488a8666ab) задеплоен, страница проверена в браузере
+ (структура отрисована корректно).
diff --git a/Makefile b/Makefile
index 3729c49..33fb9f1 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# Makefile — монолит iot-service (образ naeel/iot-service).
# Старые k8s-цели — в legacy/Makefile.old.
-VERSION ?= v0.1.8
+VERSION ?= v0.1.9
IMAGE ?= naeel/iot-service
LDFLAGS = -X main.version=$(VERSION)
diff --git a/internal/service/api/ui/landing.html b/internal/service/api/ui/landing.html
index bc0480a..46dd76f 100644
--- a/internal/service/api/ui/landing.html
+++ b/internal/service/api/ui/landing.html
@@ -161,50 +161,52 @@ pre code { background: none; border: none; padding: 0; }
Приём и хранение телеметрии устройств: MQTT → шина SQS → - PostgreSQL → REST API и консоль. Устройства изолированы по namespace, - каждому namespace — отдельная база данных.
+Это база телеметрии. Устройства шлют сюда показания по MQTT — + показания сохраняются и доступны через API и консоль. Ниже — как отправить + первое показание за 2 минуты.
Платформа принимает телеметрию от устройств по протоколу MQTT - (WebSocket с TLS) и доставляет её в базу данных по цепочке:
+Устройство (датчик, бойлер, счётчик) отправляет показания по протоколу + MQTT. Дальше показания проходят по цепочке и попадают в базу данных:
+MQTT (wss) → очередь iot-telemetry
+ → PostgreSQL → API / консоль
+
- устройство → wss://…/mqtt (EMQX) → очередь
- iot-telemetry (shared-sqs) → потребитель → PostgreSQL →
- REST API / консоль.
+ У каждого устройства — свои логин и пароль, поэтому чужое устройство не
+ сможет писать в ваш топик. Данные разных проектов (namespace) лежат в
+ разных базах и не смешиваются.
Три понятия:
- Устройства регистрируются через REST API и получают собственный
- пароль. Каждое устройство публикует только в свой топик
- {namespace}/telemetry/{device_id} — чужие топики
- отклоняются брокером.
+ namespace — имя вашего проекта/группы устройств (например
+ myhome);
+ device_id — идентификатор конкретного устройства (например
+ boiler-1);
+ топик — адрес, в который устройство отправляет показания:
+ namespace/telemetry/device_id.
Устройство создаётся через REST API с Bearer-JWT (структура
- sub + exp). В ответе — mqtt_username;
- пароль возвращается только запросом GET по имени устройства.
Все команды API требуют заголовок Authorization: Bearer
+ <токен>. Для тестов подойдёт самодельный токен: сервис
+ проверяет только два поля (кто и до какого времени), подпись не
+ проверяется. Одна команда — токен готов:
TOKEN="…jwt…"curl -X POST -H "Authorization: Bearer $TOKEN" \
- -H "Content-Type: application/json" \
- -d '{"name":"dev1","device_id":"dev-001"}' \
- https://iot.containerk8s.dev.nubes.ru/v1/namespaces/test/iot/devicescurl -H "Authorization: Bearer $TOKEN" \
- https://iot.containerk8s.dev.nubes.ru/v1/namespaces/test/iot/devices/dev1
+ TOKEN=$(python3 -c "import base64,json,time;h=base64.urlsafe_b64encode(b'{\"alg\":\"none\",\"typ\":\"JWT\"}').rstrip(b'=').decode();p=base64.urlsafe_b64encode(json.dumps({'sub':'admin','exp':int(time.time())+86400}).encode()).rstrip(b'=').decode();print(h+'.'+p+'.sig')")
Endpoint wss://exqx.containerk8s.dev.nubes.ru/mqtt,
- подпротокол WebSocket — mqtt; username
- {namespace}_{device_id}, пароль из шага 1; публикация в
- топик {namespace}/telemetry/{device_id}.
Одна команда — и устройство boiler-1 появляется в
+ проекте myhome. В ответе будет логин для MQTT
+ (myhome_boiler-1). Пароль в ответе НЕ показывается —
+ его отдаёт второй запрос.
import paho.mqtt.client as mqttc = mqtt.Client(transport="websockets")c.ws_set_options(path="/mqtt")c.username_pw_set("test_dev-001", "<пароль>")c.connect("exqx.containerk8s.dev.nubes.ru", 443)c.publish("test/telemetry/dev-001", '{"temp":23.5}')
+ curl -X POST -H "Authorization: Bearer $TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{"name":"boiler","device_id":"boiler-1"}' \
+ https://iot.containerk8s.dev.nubes.ru/v1/namespaces/myhome/iot/devices# пароль — во втором запросе (поле mqtt_password):curl -H "Authorization: Bearer $TOKEN" \
+ https://iot.containerk8s.dev.nubes.ru/v1/namespaces/myhome/iot/devices/boiler
История сообщений — REST API (Bearer-JWT):
+Подключение к MQTT — по адресу
+ wss://exqx.containerk8s.dev.nubes.ru/mqtt. Логин —
+ myhome_boiler-1, пароль — из шага 2. Показание
+ отправляется в топик myhome/telemetry/boiler-1.
curl -H "Authorization: Bearer $TOKEN" \
- "https://iot.containerk8s.dev.nubes.ru/v1/namespaces/test/iot/telemetry?limit=50"
+ import paho.mqtt.client as mqtt, jsonc = mqtt.Client(transport="websockets")c.ws_set_options(path="/mqtt")c.username_pw_set("myhome_boiler-1", "<пароль из шага 2>")c.connect("exqx.containerk8s.dev.nubes.ru", 443)c.publish("myhome/telemetry/boiler-1", json.dumps({"temp": 58.3}))
+ Показание доходит в базу за ~1 секунду. Посмотреть — запросом или + в консоли:
+curl -H "Authorization: Bearer $TOKEN" \
+ "https://iot.containerk8s.dev.nubes.ru/v1/namespaces/myhome/iot/telemetry?limit=50"
- https://exqx.containerk8s.dev.nubes.ru — это MQTT-брокер
- (EMQX), а не веб-страница. Точка подключения — только
- /mqtt через WebSocket. Открытие этого адреса в браузере
- или HTTP-запрос вернёт 400 — это штатное поведение
- (нет WebSocket-подпротокола mqtt).
+ exqx.containerk8s.dev.nubes.ru — это MQTT-брокер, а не
+ веб-страница. Точка подключения — только /mqtt через
+ WebSocket. Если открыть этот адрес в браузере, вернётся
+ 400 — так и должно быть: браузер не использует
+ WebSocket-подпротокол mqtt. Подключаться нужно MQTT-
+ клиентом (paho-mqtt, mqtt.js и т.п.), как в шаге 3.
- Ограничения: размер сообщения — до ~250 КБ (лимит SQS 256 КБ); - платформа может разрывать внешние WebSocket-соединения примерно раз - в 150 секунд — устройства должны автоматически переподключаться - (для QoS 1 повторы выполняются клиентом). + Ограничения: сообщение — до ~250 КБ; платформа может разрывать + внешние WebSocket-соединения примерно раз в 150 секунд — клиент + должен уметь переподключаться (в paho это делается автоматически).