Boundary v0: WebSocket with heartbeats, acked commands and mock state

One socket on :7700 carries hello, heartbeats both ways (5 s timeout), 5 Hz
state snapshots of envelopes, and idempotent commands. 'mark' is forwarded to
the recorder's control socket. --mock synthesises degrading state flagged as
mock in every envelope; without it a client gets hello and heartbeats only.
Peers outside loopback, RFC 1918 and the tailnet are refused. Deployed on the
car Orin as yukart-gw.service.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
2026-10-09 20:19:05 +09:00
parent aaeb836f01
commit a1ed8d8fc5
11 changed files with 809 additions and 3 deletions
+19 -1
View File
@@ -1,7 +1,7 @@
---
type: plan
status: active
updated: 2026-09-04
updated: 2026-10-09
---
# Initial plan
@@ -20,6 +20,9 @@ directly.
## Order
0. **Boundary v0** — in place: WebSocket on `:7700`, hello, heartbeats both ways,
`mark` forwarded to the recorder's control socket, mock state behind `--mock`.
Protocol in contracts [`docs/BOUNDARY.md`](gitea:yukart/contracts).
1. Static file serving for the basemap — smallest useful thing, unblocks the map.
2. Nav HTTP proxy to [svc-nav](gitea:yukart/svc-nav).
3. Aggregation and rate limiting, once there is real CAN traffic to aggregate.
@@ -75,3 +78,18 @@ Two rules constrain it:
indistinguishable from real ones, on a display in a moving car, is a safety
problem rather than a tidiness one. The mode is off by default and flagged in the
stream, not merely in a log line at startup.
## Boundary v0, as built
- One WebSocket rather than separate push and request/response transports: the two
channels keep their different guarantees (drop-oldest down, acked-with-idempotency-key
up) on one socket, and the client has one connection to keep alive.
- Threads, not async: a handful of clients at most, and tens of MB matter more
here than connection count.
- **Peers are filtered by address** — loopback, RFC 1918, tailnet 100.64/10. The car
unit has an LTE uplink, and a command endpoint must not answer on it.
- Without `--mock` and with no bus yet, a client sees hello and heartbeats only.
That is the honest state of the car today, and still exercises the link,
reconnection and marking end to end.
- Signal units are read from contracts' `signals.json` at build time from a sibling
checkout (`../contracts`). The build fails without it rather than guess.