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
|
## Hosts 0.4.0 — Unreleased
|
||||||
|
|
||||||
`tines`, `tines-store`, `tines-bt`, `tines-ros2` and `tines-sim` move to 0.4.0
|
`tines`, `tines-store`, `tines-bt`, `tines-bus`, `tines-ros2` and
|
||||||
and depend on `tines-core = "1"`. Like tines-core 1.0.0, nothing here is
|
`tines-sim` move to 0.4.0 and depend on `tines-core = "1"`. Like tines-core
|
||||||
tagged or published yet.
|
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
|
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
|
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
|
### Known issues
|
||||||
|
|
||||||
- The service talks to exactly one configured `peer`. The multi-peer
|
- The service talks to exactly one configured `peer` on the BT transport.
|
||||||
allowlist the fleet-fixes spec describes isn't wired up (F3).
|
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).
|
- Neither host flushes on a timer or at shutdown (F18).
|
||||||
- The service trims and flushes only after 64 poll rounds that each stored
|
- The service trims and flushes only after 64 poll rounds that each stored
|
||||||
something new.
|
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
|
- Peer time is unauthenticated until 1.1. An adopted time isn't corrected
|
||||||
while the host runs (F19).
|
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
|
## 0.3.0 — 2026-10-18
|
||||||
|
|
||||||
### Breaking
|
### 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
|
- Connections produce `Contact` and `Lost` events. See
|
||||||
[`tines-bt/README.md`](tines-bt/README.md).
|
[`tines-bt/README.md`](tines-bt/README.md).
|
||||||
- **`tines`**: the standalone service, for robots without ROS2.
|
- **`tines`**: the standalone service, for robots without ROS2.
|
||||||
- It runs the exchange on a poll loop and answers `Observe`, `Belief`,
|
- It runs the exchange on a poll loop and answers `Observe` (broadcast or
|
||||||
`Events` and `Status` requests on a Unix socket.
|
addressed), `Belief`, `Events`, `List` and `Status` requests on a Unix
|
||||||
|
socket.
|
||||||
- It's configured by a JSON file (below). Library users call
|
- It's configured by a JSON file (below). Library users call
|
||||||
`tines::serve(node, socket_path, clock, poll_interval_ms,
|
`tines::serve(node, socket_path, clock, poll_interval_ms,
|
||||||
peer_time_window_ms)`.
|
peer_time_window_ms)`.
|
||||||
@@ -578,9 +579,11 @@ typo can't silently fall back to a default.
|
|||||||
| Key | Default | Meaning |
|
| Key | Default | Meaning |
|
||||||
|---------------------------|--------------|---------|
|
|---------------------------|--------------|---------|
|
||||||
| `node_id` | required | this robot's `NodeId`, 32 hex chars |
|
| `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 |
|
| `log_path` | required | the `LogStore` log file |
|
||||||
| `socket_path` | required | the IPC Unix socket |
|
| `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 |
|
| `poll_interval_ms` | 20 | exchange poll cadence |
|
||||||
| `trusted_clock` | unset | `"system"` to trust the system clock; the only accepted value |
|
| `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 |
|
| `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).
|
while the host runs; see F19 in [`THREAT_MODEL.md`](THREAT_MODEL.md).
|
||||||
|
|
||||||
**Current limits** (tracked 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
|
- 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
|
64 productive poll rounds, and both hosts flush when they adopt trusted
|
||||||
time.
|
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 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
|
## Crate layout
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -658,9 +700,14 @@ tines/
|
|||||||
tines-store/ std: LogStore, the persistent append-only backend.
|
tines-store/ std: LogStore, the persistent append-only backend.
|
||||||
tines-bt/ std: the BLE transport (BlueZ via bluer).
|
tines-bt/ std: the BLE transport (BlueZ via bluer).
|
||||||
tines/ std: the standalone service with Unix-socket IPC.
|
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-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
|
The public constants, in `tines_core::consts`, are the wire constants. They
|
||||||
are frozen for wire v1: changing one is a `WIRE_VERSION` bump.
|
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 /
|
- **Assessed:** `master` at `3b1a644` (tines-core / tines-store / tines-bt /
|
||||||
tines-ros2 / tines 0.3.0, fleet-deployment milestone mostly landed)
|
tines-ros2 / tines 0.3.0, fleet-deployment milestone mostly landed)
|
||||||
- **Date:** 2026-10-19
|
- **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
|
tines-core 1.0 review fixes landed. Earlier update: 2026-10-20 at
|
||||||
`89041ea`, after the fleet fixes. The findings keep their original text;
|
`89041ea`, after the fleet fixes. The findings keep their original text;
|
||||||
each fixed one ends with a **Fixed** or **Partly fixed** line. §6 adds
|
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 BLE security level is requested (unchanged; 1.1).
|
||||||
- No two-radio check of the SeqPacket path is recorded yet.
|
- No two-radio check of the SeqPacket path is recorded yet.
|
||||||
tines-bt/README.md gives the procedure but no result.
|
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
|
**Later (1.1):** authenticate the link (spec §2b). Advertised NodeIds are
|
||||||
unauthenticated, so the binding only becomes meaningful once there's a
|
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
|
an idle client blocks everyone. An `accept` error ends the daemon. An
|
||||||
oversized frame (> 1 MiB) gets an error reply without consuming its body,
|
oversized frame (> 1 MiB) gets an error reply without consuming its body,
|
||||||
which desynchronizes the stream (contrary to the module docs).
|
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:**
|
- **Fix:**
|
||||||
- Create the socket in a `0700` runtime directory with mode `0600` (or a
|
- Create the socket in a `0700` runtime directory with mode `0600` (or a
|
||||||
group-restricted mode).
|
group-restricted mode).
|
||||||
@@ -626,6 +640,38 @@ authenticates that estimate.
|
|||||||
`time-sync.target`). Signing and verification in 1.1 close the forged
|
`time-sync.target`). Signing and verification in 1.1 close the forged
|
||||||
case.
|
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
|
## 7. Threats by STRIDE, mapped to findings
|
||||||
|
|
||||||
| | Threat | Status today | Findings | Planned mitigation |
|
| | 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 |
|
| 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 |
|
| 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) |
|
| 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 |
|
| F7, F14, §8 | 1.1 (threat model, link authentication, signing) | 1.0 spec §2a, §2b | Open |
|
||||||
| F15, F16 | Accepted for now | — | — |
|
| F15, F16 | Accepted for now | — | — |
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//! The tines bus transport: exchange wire frames between nodes over a host
|
//! The tines bus transport: exchange wire frames between nodes over a host
|
||||||
//! IPC bus (Unix sockets), identity-preserving (`Addr = NodeId`).
|
//! IPC bus (Unix sockets), identity-preserving (`Addr = NodeId`).
|
||||||
//!
|
//!
|
||||||
//! [`BusTransport`] is the tines [`Transport`] implementation; [`daemon`] is
|
//! [`BusTransport`] is the tines [`tines_core::Transport`] implementation;
|
||||||
//! the hub that routes frames between all connected nodes. The two sides of
|
//! [`daemon`] is the hub that routes frames between all connected nodes. The
|
||||||
//! a fleet talk through one shared daemon socket.
|
//! two sides of a fleet talk through one shared daemon socket.
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user