docs: fleet console, bus transport, widened IPC
This commit is contained in:
+44
-5
@@ -190,9 +190,9 @@ fixed in core; see `THREAT_MODEL.md`.
|
||||
|
||||
## Hosts 0.4.0 — Unreleased
|
||||
|
||||
`tines`, `tines-store`, `tines-bt`, `tines-ros2` and `tines-sim` move to 0.4.0
|
||||
and depend on `tines-core = "1"`. Like tines-core 1.0.0, nothing here is
|
||||
tagged or published yet.
|
||||
`tines`, `tines-store`, `tines-bt`, `tines-bus`, `tines-ros2` and
|
||||
`tines-sim` move to 0.4.0 and depend on `tines-core = "1"`. Like tines-core
|
||||
1.0.0, nothing here is tagged or published yet.
|
||||
|
||||
The 0.3.0 release shipped only `tines-core` and `tines-sim`. The four other
|
||||
host crates were added afterwards, during the fleet milestone, and carried the
|
||||
@@ -339,8 +339,9 @@ milestone's host work (F2–F5, F17).
|
||||
|
||||
### Known issues
|
||||
|
||||
- The service talks to exactly one configured `peer`. The multi-peer
|
||||
allowlist the fleet-fixes spec describes isn't wired up (F3).
|
||||
- The service talks to exactly one configured `peer` on the BT transport.
|
||||
The multi-peer allowlist the fleet-fixes spec describes isn't wired up
|
||||
(F3). The new bus transport drops the limit, as a test medium only.
|
||||
- Neither host flushes on a timer or at shutdown (F18).
|
||||
- The service trims and flushes only after 64 poll rounds that each stored
|
||||
something new.
|
||||
@@ -348,6 +349,44 @@ milestone's host work (F2–F5, F17).
|
||||
- Peer time is unauthenticated until 1.1. An adopted time isn't corrected
|
||||
while the host runs (F19).
|
||||
|
||||
### Fleet console (unreleased, on `feat/fleet-console`)
|
||||
|
||||
The desktop console for driving a containerized fleet, and the host pieces
|
||||
it rides on:
|
||||
|
||||
- **`tines-bus`** (new workspace crate): a host-only transport over a
|
||||
hub-and-spoke Unix-socket daemon (`tines-busd`). `Addr = NodeId`, like
|
||||
the BT transport. A client registers its `NodeId`, then sends data
|
||||
envelopes carrying a 32-byte routing header (`from`, `to`); the daemon
|
||||
drops any envelope whose `from` isn't that socket's registered id or
|
||||
whose `to` isn't registered, and forwards the frame verbatim. Envelopes
|
||||
are length-capped. A bus peer loses the BT transport's single-peer
|
||||
limit — any registered peer is reachable. It is a test medium for
|
||||
simulated fleets, not a deployment transport.
|
||||
- **Transport choice in the service** (tines): config `transport` is
|
||||
`"bt"` (default) or `"bus"`, with `bus_socket` naming the daemon's
|
||||
socket. `HostTransport` wraps both, so the service runs BLE or the bus
|
||||
from the same config file.
|
||||
- **Widened IPC** (tines): `Observe` gains `to` / `lifetime_ms` for
|
||||
addressed observations (both set together or neither); new `List`
|
||||
(observation ids, with a `truncated` flag at the cap) and `Events`
|
||||
(contacts, drops and drop counts) requests. The new request kinds keep
|
||||
the existing error posture: a bad request gets an `Error` response,
|
||||
never a panic or a partial write.
|
||||
- **Decision C** (core): the additive `NodeEvent::Contact` /
|
||||
`NodeEvent::Lost` variants, so hosts see peers come and go. `Contact`
|
||||
fires only for a newly added peer; idle eviction stays silent. The only
|
||||
sanctioned core change for the console.
|
||||
- **Fleet console** (`tines-gui`, standalone crate outside the workspace):
|
||||
a launcher (create a fleet, add nodes, start/stop containers, open node
|
||||
windows) and a per-container node window (status strip, observation
|
||||
send, belief read, events feed) driving the service IPC. BT mode runs a
|
||||
privileged container with the host adapter passed through; sim mode runs
|
||||
N containers over the bus daemon. Fleet state lives under
|
||||
`$XDG_STATE_HOME/tines-console/fleets/<fleet>/`, created `0700`.
|
||||
- **Runtime image** (`Dockerfile.tines`): the two-stage container for the
|
||||
service plus `tines-busd`; the GUI drives docker as a CLI subprocess.
|
||||
|
||||
## 0.3.0 — 2026-10-18
|
||||
|
||||
### Breaking
|
||||
|
||||
@@ -560,8 +560,9 @@ A robot runs either the service or the ROS2 node, not both: each owns its
|
||||
- Connections produce `Contact` and `Lost` events. See
|
||||
[`tines-bt/README.md`](tines-bt/README.md).
|
||||
- **`tines`**: the standalone service, for robots without ROS2.
|
||||
- It runs the exchange on a poll loop and answers `Observe`, `Belief`,
|
||||
`Events` and `Status` requests on a Unix socket.
|
||||
- It runs the exchange on a poll loop and answers `Observe` (broadcast or
|
||||
addressed), `Belief`, `Events`, `List` and `Status` requests on a Unix
|
||||
socket.
|
||||
- It's configured by a JSON file (below). Library users call
|
||||
`tines::serve(node, socket_path, clock, poll_interval_ms,
|
||||
peer_time_window_ms)`.
|
||||
@@ -578,9 +579,11 @@ typo can't silently fall back to a default.
|
||||
| Key | Default | Meaning |
|
||||
|---------------------------|--------------|---------|
|
||||
| `node_id` | required | this robot's `NodeId`, 32 hex chars |
|
||||
| `peer` | required | the one peer's `NodeId`, 32 hex chars |
|
||||
| `peer` | bt only | the one peer's `NodeId`, 32 hex chars |
|
||||
| `log_path` | required | the `LogStore` log file |
|
||||
| `socket_path` | required | the IPC Unix socket |
|
||||
| `transport` | `"bt"` | `"bt"` for BLE, or `"bus"` for the host bus transport |
|
||||
| `bus_socket` | bus only | the bus daemon's Unix socket |
|
||||
| `poll_interval_ms` | 20 | exchange poll cadence |
|
||||
| `trusted_clock` | unset | `"system"` to trust the system clock; the only accepted value |
|
||||
| `peer_time_window_ms` | 10000 | how long to collect peer time estimates before adopting the latest |
|
||||
@@ -635,7 +638,8 @@ Peer time is unauthenticated until 1.1, and an adopted time isn't corrected
|
||||
while the host runs; see F19 in [`THREAT_MODEL.md`](THREAT_MODEL.md).
|
||||
|
||||
**Current limits** (tracked in [`THREAT_MODEL.md`](THREAT_MODEL.md)):
|
||||
- The service talks to one configured `peer`.
|
||||
- On the BT transport the service talks to one configured `peer`; the bus
|
||||
transport (simulated fleets) reaches any peer registered with the daemon.
|
||||
- The hosts don't flush on a timer or at shutdown. The service flushes after
|
||||
64 productive poll rounds, and both hosts flush when they adopt trusted
|
||||
time.
|
||||
@@ -644,6 +648,44 @@ while the host runs; see F19 in [`THREAT_MODEL.md`](THREAT_MODEL.md).
|
||||
|
||||
The two-device BLE check in `tines-bt/README.md` hasn't been recorded yet.
|
||||
|
||||
## The fleet console (lab desktop)
|
||||
|
||||
`tines-gui/` (a standalone crate outside the workspace, like `fuzz/`) is
|
||||
the desktop console: an orchestrator that runs a whole fleet in containers.
|
||||
It is never a node host — the GUI embeds no `Node` and no radio runs in the
|
||||
GUI process; every node lives in a container the GUI drives over the
|
||||
service IPC.
|
||||
|
||||
- **Launcher** (`tines-gui` with no arguments): create a fleet, add nodes,
|
||||
start/stop containers, open a node window.
|
||||
- **Node window** (`tines-gui --node <dir> <container-id> bt|sim`): one
|
||||
window per container — a status strip, observation send (broadcast or
|
||||
addressed, with a lifetime), belief read, and a live events feed
|
||||
(contacts, drops). It speaks the service IPC; nothing else.
|
||||
- **Modes.** BT mode runs one privileged container with the host's
|
||||
Bluetooth adapter passed through, so that node reaches real robots. Sim
|
||||
mode runs N containers over the bus transport (below), with no radio.
|
||||
- Fleet state lives under
|
||||
`$XDG_STATE_HOME/tines-console/fleets/<fleet>/`, created `0700`: one
|
||||
directory per node with its config, log and IPC socket.
|
||||
|
||||
### The bus transport
|
||||
|
||||
`tines-bus` (a workspace member) is a host-only transport for simulated
|
||||
fleets: a hub-and-spoke daemon (`tines-busd`) that containers dial over
|
||||
Unix sockets. Its `Addr` is the `NodeId`, like the BT transport.
|
||||
|
||||
- A client registers its `NodeId` with the daemon, then sends data
|
||||
envelopes carrying a 32-byte routing header (`from`, `to`). The daemon
|
||||
drops any envelope whose `from` isn't that socket's registered id or
|
||||
whose `to` isn't registered, and forwards the frame verbatim — it never
|
||||
decodes frame contents.
|
||||
- Envelopes are length-capped; an oversized one is a protocol violation.
|
||||
- The bus is a **test medium**, not a deployment transport: no encryption,
|
||||
no authentication beyond the registration check. The service picks it
|
||||
with `transport = "bus"` plus `bus_socket`, which also drops the BT
|
||||
transport's single-peer limit — any registered peer is reachable.
|
||||
|
||||
## Crate layout
|
||||
|
||||
```
|
||||
@@ -658,9 +700,14 @@ tines/
|
||||
tines-store/ std: LogStore, the persistent append-only backend.
|
||||
tines-bt/ std: the BLE transport (BlueZ via bluer).
|
||||
tines/ std: the standalone service with Unix-socket IPC.
|
||||
tines-bus/ std: the host bus transport — a hub-and-spoke Unix-socket
|
||||
daemon (`tines-busd`) for simulated fleets.
|
||||
tines-ros2/ std: the ROS2 node (rclrs behind the `ros2` feature).
|
||||
```
|
||||
|
||||
`tines-gui/` sits outside the workspace (like `fuzz/`): the desktop fleet
|
||||
console. Build it with `cargo test --manifest-path tines-gui/Cargo.toml`.
|
||||
|
||||
The public constants, in `tines_core::consts`, are the wire constants. They
|
||||
are frozen for wire v1: changing one is a `WIRE_VERSION` bump.
|
||||
|
||||
|
||||
+48
-1
@@ -3,7 +3,11 @@
|
||||
- **Assessed:** `master` at `3b1a644` (tines-core / tines-store / tines-bt /
|
||||
tines-ros2 / tines 0.3.0, fleet-deployment milestone mostly landed)
|
||||
- **Date:** 2026-10-19
|
||||
- **Updated:** 2026-10-21 at `411daa0` (`feat/tines-core-1-0`), after the
|
||||
- **Updated:** 2026-10-25, at `2a917088` (`feat/fleet-console`), after the
|
||||
fleet console work: F3's single-peer limit is now BT-only (the new bus
|
||||
transport drops it, as a test medium); F11 gains the widened IPC; F20
|
||||
(bus daemon) added. Earlier updates: 2026-10-21 at
|
||||
`411daa0` (`feat/tines-core-1-0`), after the
|
||||
tines-core 1.0 review fixes landed. Earlier update: 2026-10-20 at
|
||||
`89041ea`, after the fleet fixes. The findings keep their original text;
|
||||
each fixed one ends with a **Fixed** or **Partly fixed** line. §6 adds
|
||||
@@ -240,6 +244,11 @@ for a poll loop, `docs/superpowers/plans/2026-10-18-tines-fleet-deployment.md:20
|
||||
- No BLE security level is requested (unchanged; 1.1).
|
||||
- No two-radio check of the SeqPacket path is recorded yet.
|
||||
tines-bt/README.md gives the procedure but no result.
|
||||
- *At `2a917088` (fleet console):* the new bus transport (tines-bus)
|
||||
drops the single-peer limit — bus peers reach any id registered with
|
||||
the daemon. That is a **test medium** for simulated fleets over host
|
||||
Unix sockets, not a radio deployment; on real radios the single-peer
|
||||
limit stands until the allowlist work lands.
|
||||
|
||||
**Later (1.1):** authenticate the link (spec §2b). Advertised NodeIds are
|
||||
unauthenticated, so the binding only becomes meaningful once there's a
|
||||
@@ -420,6 +429,11 @@ re-queues what was already sent.
|
||||
an idle client blocks everyone. An `accept` error ends the daemon. An
|
||||
oversized frame (> 1 MiB) gets an error reply without consuming its body,
|
||||
which desynchronizes the stream (contrary to the module docs).
|
||||
- *At `2a917088` (fleet console):* the IPC widened — `Observe` gained
|
||||
`to`/`lifetime_ms`, and `List` and `Events` were added — without
|
||||
changing this posture. The new request kinds validate their inputs, cap
|
||||
their outputs (`List` truncates with a flag), and answer a bad request
|
||||
with an `Error` response, never a panic or a partial write.
|
||||
- **Fix:**
|
||||
- Create the socket in a `0700` runtime directory with mode `0600` (or a
|
||||
group-restricted mode).
|
||||
@@ -626,6 +640,38 @@ authenticates that estimate.
|
||||
`time-sync.target`). Signing and verification in 1.1 close the forged
|
||||
case.
|
||||
|
||||
### F20 — The bus daemon is a host attack surface (Medium, A3)
|
||||
|
||||
*Added 2026-10-25, with the fleet console's bus transport (tines-bus).*
|
||||
The bus daemon (`tines-busd`) stands in for the radio in simulated fleets:
|
||||
N service containers dial one daemon over host Unix sockets and exchange
|
||||
tines frames through it. It is host-local infrastructure and was built
|
||||
with the same distrust of input as the IPC server:
|
||||
|
||||
- **Registration is explicit.** A client registers its `NodeId` before
|
||||
sending data, and the daemon drops (and counts) any envelope whose
|
||||
`from` isn't that socket's registered id, or whose `to` isn't a
|
||||
registered id.
|
||||
- **Envelopes are length-capped** at `MAX_FRAME + 32` header bytes
|
||||
(`MAX_PAYLOAD`); an oversized length header is a protocol violation that
|
||||
closes the connection.
|
||||
- **The daemon decodes nothing beyond the 32-byte routing header.** Frame
|
||||
contents are forwarded verbatim, so a malformed frame is the receiving
|
||||
node's problem, exactly as on the radio.
|
||||
- **The fleet directory is `0700`** (tines-gui `config::fleet_dir` and
|
||||
per-node dirs), so node sockets, logs and configs under it are private
|
||||
to the owning user.
|
||||
|
||||
- **Effect (A3):** another local user can neither read nor join a fleet
|
||||
(directory permissions), nor spoof frames through the daemon (the
|
||||
registration check). Root can do anything on the host, as always. The
|
||||
daemon is bench equipment: it carries no encryption and authenticates
|
||||
nothing, and must not become a deployment transport.
|
||||
- **Open:** the daemon rate-limits nothing. A buggy or hostile local
|
||||
client can flood envelopes at the fan-out rate. Accepted for the test
|
||||
medium; F10-style quotas would be the fix if the daemon ever left the
|
||||
bench.
|
||||
|
||||
## 7. Threats by STRIDE, mapped to findings
|
||||
|
||||
| | Threat | Status today | Findings | Planned mitigation |
|
||||
@@ -708,6 +754,7 @@ agreed fuzz budget hasn't been run and recorded yet.
|
||||
| F10–F12 | Host hardening, before a robot leaves the lab | `docs/superpowers/specs/2026-10-19-tines-host-hardening.md` | Open |
|
||||
| F6, F8, F9, F13 | tines-core 1.0 | `docs/superpowers/specs/2026-10-18-tines-1-0-design.md` §10 | Fixed (§1); F13's Reassembler displacement accepted |
|
||||
| F19 | Hosts and tines-store (decision J, plausibility bounds); authentication in 1.1 | 1.0 spec §9, decision J; §2a | Partly mitigated (§1) |
|
||||
| F20 | Fleet console: the bus daemon's posture (registration, caps, 0700 dirs) is built in; per-peer rate limits are open | fleet console design | Mitigated by design (§6) |
|
||||
| F7, F14, §8 | 1.1 (threat model, link authentication, signing) | 1.0 spec §2a, §2b | Open |
|
||||
| F15, F16 | Accepted for now | — | — |
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! The tines bus transport: exchange wire frames between nodes over a host
|
||||
//! IPC bus (Unix sockets), identity-preserving (`Addr = NodeId`).
|
||||
//!
|
||||
//! [`BusTransport`] is the tines [`Transport`] implementation; [`daemon`] is
|
||||
//! the hub that routes frames between all connected nodes. The two sides of
|
||||
//! a fleet talk through one shared daemon socket.
|
||||
//! [`BusTransport`] is the tines [`tines_core::Transport`] implementation;
|
||||
//! [`daemon`] is the hub that routes frames between all connected nodes. The
|
||||
//! two sides of a fleet talk through one shared daemon socket.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user