Files
tines/CHANGELOG.md
T

174 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
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
`NodeEvent<A>::Delivered { peer, id }` from the new `Node::drain_events`.
- Broadcast confirmations and counted drops both come through
`Node::drain_events`; a confirmed broadcast arrives as
`NodeEvent::Delivered`, and a frame the peer dropped arrives as
`NodeEvent::Dropped { reason, count }` (§3).
- **Counted drop reporting** (tines-core). A frame the peer could not use is no
dropped silently: the exchange counts it under a `DropReason` (`VersionMismatch`
or `Malformed`) keyed by the peer's transport address and reports the count
through `Node::drain_events` as `NodeEvent::Dropped`. Counters are held in a
bounded table (`consts::DROP_TABLE_CAP`) with a single overflow slot for the
case where the table is full, so an adversarial peer can't spin forever — it
just reports `addr: None`. Expired addressed observations dropped by policy are
*not* counted.
- 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
- `Store` now requires `release(&mut self, id)` and
`is_released(&self, id) -> bool`. Implementations of `Store` — including test
doubles and future backends — must provide them.
### Added
- `Store::release` marks an observation as sent, so it is no longer offered for
exchange. `MemStore` tracks a per-entry `released` flag, and its
`for_each_addressed_id` skips released ids as an optimization (the exchange
predicate remains the load-bearing check). Releasing an id that is not stored
is a no-op.
## 0.2.1 — 2026-10-18
### Changed
- Package metadata only: `repository`, `documentation`, `readme`,
`keywords` and `categories`, shared through `[workspace.package]`. The
README now appears on crates.io.
- Each crate now ships the Apache-2.0 `LICENSE` text.
## 0.2.0 — 2026-10-18
Hardening from a full-codebase review. Wire-compatible with 0.1.0 except
where noted.
### Added
- `Exchange::on_send_error`: rolls back the last `next_frame` after a failed
send. `Node::poll` calls it, so a `Busy` or `Unreachable` frame is retried
instead of being lost.
- `Store::for_each_addressed_id`, with a default that visits every id.
`MemStore` overrides it, so custody no longer reads every broadcast
observation on each poll.
- `Bloom::fold`, and `Bloom::from_parts` now accepts any power-of-two length up
to `SUMMARY_BYTES`. Summaries are folded to fit the transport's MTU; a
0.1.0 node rejects a folded summary, so mixed-version links only sync when
the MTU allows a full 512-byte filter.
- `InFlight`, describing a fragment stream in progress.
### Changed
- **Breaking:** `Peer::in_flight` is now `Option<InFlight>` (was
`Option<(ObservationId, u8)>`).
- Custody (addressed) and broadcast sending alternate per peer, and addressed
candidates are taken in rotation.
- Bounce suppression lasts one contact, as the addressed-delivery spec says.
### Fixed
- A fragment stream could panic, or send corrupt fragments, when the
observation's age changed encoded length mid-stream or the tick went
backwards.
- Summaries larger than the MTU stopped all broadcast sync on narrow links.
- A full bounce table silently stopped recording, so one addressed message
was re-sent on every poll and starved broadcast.
- An addressed send interrupted another peer's fragment stream, which was
then never finished.
- An addressed message that couldn't fit the MTU blocked every other
addressed message for that peer.
- A duplicate addressed message was forwarded back to the carrier that sent
it.
### CI
- Tests, clippy and docs also run without the `alloc` feature; doc warnings
fail the build; `--locked` everywhere; toolchain pinned to 1.99.0; actions
pinned to commit SHAs.
## 0.1.0
Initial release: `tines-core` and `tines-sim`.