Define display boundary protocol v0
What crosses between svc-gw and the app: hello, heartbeats both ways, state snapshots of envelopes with mock flagged per message, and commands carrying an idempotency key. TTS utterances are left for when a TTS exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
---
|
||||
type: reference
|
||||
---
|
||||
# Display boundary protocol — v0
|
||||
|
||||
Between a display client (`app-flutter`) and [svc-gw](gitea:yukart/svc-gw). The
|
||||
bus topics in `schema/` stay inside the Orin; this is what crosses to the tablet.
|
||||
|
||||
## Transport
|
||||
|
||||
One WebSocket, `ws://<orin>:7700/`, JSON text messages, each with a `"t"` type.
|
||||
The tablet reaches the car Orin at `192.168.55.1` over USB NCM; the app binds to
|
||||
that network itself (see `app-flutter`'s initial plan).
|
||||
|
||||
The client always connects and owns reconnection. svc-gw refuses peers outside
|
||||
loopback, RFC 1918 and the tailnet's 100.64.0.0/10.
|
||||
|
||||
The two channels from decision 24 share the socket but keep different guarantees:
|
||||
|
||||
| | Down (state) | Up (commands) |
|
||||
|---|---|---|
|
||||
| Loss | tolerated; svc-gw drops the oldest when a client lags (64-message queue) | not tolerated; retry until acked |
|
||||
| On reconnect | `hello`, then current state on the next tick | resend anything unacked, same `id` |
|
||||
|
||||
## Down: svc-gw → client
|
||||
|
||||
| `t` | When | Fields |
|
||||
|---|---|---|
|
||||
| `hello` | first message | `proto` (0), `server`, `host`, `source` (`mock` \| `none`), `mock`, `hb_ms`, `timeout_ms` |
|
||||
| `hb` | every `hb_ms` | `ts` (Orin epoch ms), `clients` |
|
||||
| `state` | 5 Hz, when there is a source | `seq`, `mock`, `signals`: name → envelope (`schema/envelope.json`) |
|
||||
| `ack` | reply to a `cmd` | `id`, `ok`, `detail`, `dup` (true when answered from the cache) |
|
||||
|
||||
`signals` holds only what is currently known. A missing signal is unknown, a
|
||||
`null` value is known-without-value, `quality: "stale"` is an old value. All three
|
||||
are normal and a display must render each distinctly.
|
||||
|
||||
⚠ **`mock: true` must be visible on the display surface** whenever it appears in
|
||||
`hello` or in any envelope. Not in a settings page, not in a log.
|
||||
|
||||
## Up: client → svc-gw
|
||||
|
||||
| `t` | When | Fields |
|
||||
|---|---|---|
|
||||
| `hb` | every `hb_ms` | — |
|
||||
| `cmd` | on user action | `id` (unique, the idempotency key), `cmd`, `args` |
|
||||
|
||||
svc-gw drops a client silent for `timeout_ms` (5 s). The client should treat
|
||||
`timeout_ms` without any downstream message the same way and reconnect: an asleep
|
||||
tablet, or an Orin behind a replugged hub, looks connected at the link layer.
|
||||
|
||||
### Commands
|
||||
|
||||
| `cmd` | `args` | Effect |
|
||||
|---|---|---|
|
||||
| `ping` | — | `ack` with `detail: "pong"` |
|
||||
| `mark` | `text`, `delay_ms` (optional: how long the command waited before this send) | stamped into the open recording; fails with no session open |
|
||||
|
||||
A repeated `id` returns the original `ack` (last 256 kept, shared across
|
||||
connections) and never re-runs the command.
|
||||
|
||||
## Not yet
|
||||
|
||||
- TTS utterances (id, chunk sequence, terminator, expiry) — when there is a TTS.
|
||||
- `gnss` and decoded vehicle state from the bus — when the bus exists.
|
||||
- Any version beyond `proto: 0`. A breaking change bumps it; the client checks it.
|
||||
@@ -20,20 +20,10 @@ observed traffic. They arrive as DBC entries plus registry rows with
|
||||
`system/<service>` have their fields named in `topics.json` but no JSON Schema
|
||||
yet. They get one when a publisher exists to constrain.
|
||||
|
||||
**The app ↔ `svc-gw` boundary protocol.** Bus topics are defined here, but the
|
||||
two channels across the boundary (latest-wins state down, acknowledged commands
|
||||
up) have no wire format yet. Project-yukart decision 24 (proposed) already
|
||||
fixes what it must carry, because the USB link drops for ~3 s at every power
|
||||
change and a sleeping tablet looks alive at the link layer:
|
||||
|
||||
- a **heartbeat** in both directions, with an agreed interval and timeout, so the
|
||||
Orin reaps a silent client instead of trusting TCP or link state;
|
||||
- an **idempotency key** on every upstream command, so retries across a
|
||||
reconnect never act twice;
|
||||
- for TTS, **utterance id + chunk sequence + terminator + an expiry**, so a
|
||||
reconnect can resume or drop an utterance rather than replay it blindly.
|
||||
|
||||
Defined when `svc-gw` and `app-flutter` first talk, not before.
|
||||
**The app ↔ `svc-gw` boundary protocol** is at v0 in [BOUNDARY.md](../../BOUNDARY.md):
|
||||
heartbeat both ways, idempotency key on every command, mock flagged per message.
|
||||
Still missing from it: the TTS utterance shape (id, chunk sequence, terminator,
|
||||
expiry), defined when a TTS exists to send one.
|
||||
|
||||
## Order
|
||||
|
||||
|
||||
Reference in New Issue
Block a user