docs: sweep after the fleet fixes; approve the 1.0 spec

- README: custody chain and receipts replace epidemic forwarding; Ack
  frame; running on a robot (host crates, current limits); security;
  crate layout, constants and reading list.
- AGENTS.md: current spec list, receipt constraints and review focus
  7-8, first_seq rule, constants, host crates and the ros2 gate.
- THREAT_MODEL.md: re-checked at 89041ea. F1, F2, F5, F17 fixed; F3
  (single configured peer) and F4 partly fixed; new F18 (no scheduled
  flush, stale clock marker).
- CHANGELOG: Unreleased section for the fleet milestone and fixes.
- tines-bt README: single-peer limit; manual BLE check not yet recorded.
- tines service rustdoc: oversized-frame behaviour, JSON tagging, clock.
- 1.0 spec: approved (2026-10-20), including §10 and the adversarial
  fuzz mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 01:03:08 -07:00
co-authored by Claude Opus 5.5
parent 89041ea89a
commit a4cde40e85
8 changed files with 424 additions and 87 deletions
+79
View File
@@ -4,6 +4,85 @@ All notable changes to the tines crates. Versions follow
[Semantic Versioning](https://semver.org/); before 1.0, a minor bump may break
the API.
## Unreleased
The fleet-deployment milestone and the fleet fixes (security assessment
findings F1–F5 and F17; see `THREAT_MODEL.md`). The workspace version is
still 0.3.0.
### Added
- **Handoff receipts** (tines-core). The new `Ack` frame (the last `Frame`
variant) carries up to `ACK_IDS_CAP` (90) ids. Acks are sent as the
exchange runs and are accepted only from a peer the id was sent to during
the current contact.
- An ack for an addressed observation moves custody: the sender releases
its copy (`Store::release`) and never offers it again.
- An ack for a broadcast is confidence only. It surfaces as a
`ConfidenceEvent { peer, id }` from the new `Node::drain_events`.
- New constants: `ACK_IDS_CAP`, `EVENT_QUEUE_CAP`, `BCAST_SENT_CAP`,
`ACK_BUFFER_CAP`.
- `wire::frame_sender(bytes) -> Option<NodeId>`: a frame's claimed sender,
for transports that bind a connection to an identity.
- `Node::store_mut`, so a host can call backend methods such as
`LogStore::flush`.
- **New host crates:**
- `tines-store`: `LogStore`, a persistent append-only backend.
- `tines-bt`: a BLE transport over BlueZ.
- `tines`: a standalone service with Unix-socket JSON IPC.
- `tines-ros2`: a ROS2 node; rclrs is behind the `ros2` feature.
### Changed
- **Addressed delivery is a custody chain.** It used to be epidemic
forwarding. A message has one custodian at a time, which hands it on and
releases it when the next holder acks. A lost ack can leave two
custodians for a while; a message is never dropped by the protocol.
- Bounce records carry a direction, so only records of an actual send
validate an ack.
- **`LogStore` log format v1** (incompatible; there's no migration, so delete
old logs):
- The file starts with a `"TINESLOG"` header and a format version.
- Each record has a sync marker, a length and a CRC-32C.
- Ids are recomputed on load, not stored.
- `LogStore` persists a boot counter, and `first_seq()` returns
`boot_count << 32`. Both hosts seed `Node::new` from it, and the
`first_seq` config field is gone.
- `LogStore::release` writes a `Release` record immediately. `flush` writes
a clock marker, so ages carry across a restart.
- `LogStore::open` takes an exclusive lock and creates the file `0600`.
- tines-bt rewrite:
- L2CAP SeqPacket channels, one frame per packet.
- Every node advertises and scans; the smaller `NodeId` dials and the
larger accepts.
- Connections are bound to the advertised `NodeId`, and frames whose
claimed sender differs are dropped.
- Connections emit `Contact` and `Lost`.
### Fixed
- A receiver whose store returned `Err` (for example `Full`) still acked, so
the sender released custody of a message nobody kept. Now nothing is
acked unless it was stored, addressed or broadcast (F1).
- The standalone service never polled its `Node`, so it never exchanged
anything. It now runs the exchange on a poll loop (F2).
- tines-bt had no listener, lost frame boundaries on a stream socket, and
stamped every frame with the configured peer (F3).
- Sequence numbers repeated after every restart, and released flags were
never persisted (F4).
- One corrupt record in the middle of the log truncated everything after it.
Recovery now skips bad records and truncates only a torn tail, with an
fsync of the file and its directory (F5).
- The service's tick was wall-clock *seconds*. Both hosts now use one
monotonic millisecond clock (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).
- Neither host flushes on a timer, and tines-ros2 never flushes or trims
(F18).
## 0.3.0 — 2026-10-18
### Breaking