Deploy MQTT test backend with private viewer and Gitea CI/CD
Test and deploy MQTT backend / test (push) Successful in 2m8s
Test and deploy MQTT backend / deploy (push) Successful in 18s

This commit is contained in:
2026-10-08 14:48:53 +03:30
commit 71ef622b4f
70 changed files with 7123 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
# Test message contracts
Sprint 1 implements structural validation and safe inspection previews. Sprint 3 implements network exchange, native MQTT properties, request tracking and duplicate execution handling. These contracts are independent of the ESP32 connection configuration.
## Topics and common fields
Topics: `robot-market/test/v1/{device_id}/{telemetry|status|commands|acks}`. The namespace may have additional safe lab segments under `robot-market/test/v1`.
Every payload is a JSON object with `schema_version: 1` and a `device_id` matching the topic. IDs contain 1–64 letters/digits/underscores/hyphens, beginning with a letter/digit. Timestamps use RFC3339 UTC ending in `Z`. Maximum payload is 16 KiB. Unknown fields, duplicate keys (including case aliases), nulls, arrays, invalid UTF-8, trailing JSON and excessive nesting are rejected. Validation errors exclude raw values.
| Category | Additional required fields | Rules |
| --- | --- | --- |
| telemetry | message_id, boot_id, sequence, timestamp, readings | Sequence is an unsigned integer, including zero. Readings has 1–16 safe-name finite numeric values. |
| status | boot_id, state and one time field | State is online/offline. Use timestamp for explicit publication, or will_configured_at for a preconfigured offline Will. Never both. |
| commands | request_id, issued_at, expires_at, command | Only ping/get_version. Lifetime is 1–60 seconds. |
| acks | request_id, timestamp, stage, ok | ok must be an explicit boolean. Stage rules below. |
Status optionally reports implementation and reported_protocol (`5` or `3.1.1`). Reported protocol is self-reported, not compatibility proof.
## Examples
Telemetry:
```json
{"schema_version":1,"device_id":"sim-001","message_id":"evt-001","boot_id":"boot-001","sequence":0,"timestamp":"2026-10-06T10:00:00Z","readings":{"uptime_s":12}}
```
Status:
```json
{"schema_version":1,"device_id":"sim-001","boot_id":"boot-001","state":"online","timestamp":"2026-10-06T10:00:00Z","implementation":"go-simulator","reported_protocol":"5"}
```
Command:
```json
{"schema_version":1,"device_id":"sim-001","request_id":"req-001","issued_at":"2026-10-06T10:00:00Z","expires_at":"2026-10-06T10:00:15Z","command":"ping"}
```
Acceptance:
```json
{"schema_version":1,"device_id":"sim-001","request_id":"req-001","timestamp":"2026-10-06T10:00:01Z","stage":"accepted","ok":true}
```
Completion:
```json
{"schema_version":1,"device_id":"sim-001","request_id":"req-001","timestamp":"2026-10-06T10:00:01Z","stage":"completed","ok":true,"result":{"pong":true}}
```
Failure:
```json
{"schema_version":1,"device_id":"sim-001","request_id":"req-001","timestamp":"2026-10-06T10:00:15Z","stage":"rejected","ok":false,"error":{"code":"expired","message":"command deadline passed"}}
```
Accepted requires ok=true with no result/error. Completed requires ok=true and exactly one result: pong=true or a bounded version identifier. Rejected/failed requires ok=false, a safe error code and a 1–256-byte error message, without result. Error messages are always redacted in inspection previews because they can contain arbitrary data.
## Deadline and acknowledgement semantics
`Command.Validate` checks structure and lifetime. `CheckDeadline(now)` additionally rejects commands before issued_at and at/after expires_at. Historical examples remain structurally valid even after their deadline. Device clocks must be synchronized for execution checks.
Transport acknowledgement, application acceptance and device completion are separate. The structural decoder validates ACK shape. The Sprint 3 backend additionally matches active own requests, command-specific results and native correlation; simulator deduplication and transport properties are covered by local/remote tests. Broader expiry/fault and physical-device verification remain later sprints. JSON request_id is not a native MQTT property.
## Safe inspection
`events.SafePreview` decodes and validates before rendering known fields. Unknown/sensitive fields, invalid JSON and binary payloads cause complete preview omission; payload length remains visible. Free-form ACK error messages become `[redacted]`. Preview output is at most the requested limit, capped at 4 KiB, and has an explicit truncation flag. Truncated text is display text, not necessarily valid JSON.
The event model provides direction, topic, identities, QoS, retain/DUP, properties, lifecycle kind, request ID and attributable latency. The implemented transport bounds and sanitizes property metadata before creating events; do not attach raw broker user properties or credentials.
## Native MQTT 5 transport (Sprint 3)
Commands use non-retained QoS 1 with Response Topic equal to the exact device ACK topic, bounded Correlation Data and MQTT Message Expiry alongside the JSON application deadline. Backend command mode uses the request ID bytes as native correlation. The simulator accepts opaque nonempty correlation values up to 64 bytes and echoes them unchanged on every ACK. ACK publications are non-retained QoS 1 and have no Response Topic. Pending completion requires matching device, topic, JSON request ID, native correlation and command-specific result; retained ACKs are rejected. Completed may arrive before accepted without regressing state.
Dedup identity includes canonical command JSON and native correlation. In-progress duplicates replay acceptance, completed duplicates replay the cached terminal ACK, and conflicting reuse is rejected without execution. Cache retention is bounded and memory-only. MQTT transport acknowledgement is separate from application admission and execution; dropped input, missing completion and connection loss cannot be reported as execution success.