Files
tines/AGENTS.md
T
kyle 76263ccbf0
CI / msrv (push) Successful in 1m5s
CI / ci (push) Successful in 2m55s
docs: AGENTS.md — bus crate layout, standalone gui, decision C in core constraints
2026-10-25 17:21:31 -07:00

174 lines
9.0 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.
# AGENTS.md — tines
Instructions for AI agents working on **tines**: opportunistic shared memory
for mostly-offline robots. Read this before touching code.
## What tines is
Small robots collect facts about the world while mostly offline. When two of
them happen to meet in range, they swap what they know so each one's picture of
the world improves. No robot depends on receiving anything, but there is no
reason not to share.
`tines` is the library that does this: a store of timestamped-by-age
observations, a belief layer derived from them, and a delay-tolerant exchange
protocol over any datagram link. Read the full intent, goals, anti-goals, and
architecture in the **spec first**:
- **Core design (authoritative):** `docs/superpowers/specs/2026-10-16-tines-design.md`
- **Addressed delivery (authoritative):** `docs/superpowers/specs/2026-10-18-tines-addressed-delivery.md`
- **Fleet deployment v1 (authoritative):** `docs/superpowers/specs/2026-10-18-tines-fleet-deployment-design.md` — supersedes the addressed-delivery spec's epidemic forwarding (custody chain with handoff receipts).
- **Fleet fixes (done):** `docs/superpowers/specs/2026-10-19-tines-fleet-fixes.md` — security-assessment findings F1–F5 and F17.
- **tines-core 1.0 (implemented on `feat/tines-core-1-0`; publish pending):** `docs/superpowers/specs/2026-10-18-tines-1-0-design.md` — wire version, public API, signature slot, core hardening (§10).
- **Host hardening (next, hosts):** `docs/superpowers/specs/2026-10-19-tines-host-hardening.md` — findings F10–F12.
- **Implementation plans:** `docs/superpowers/plans/`
The plans argue from the specs. Where a plan is silent, the **spec rules**.
Do not edit the specs or the plans on your own initiative — they are design
history, not source. Change one only to record something Kyle has discussed
and agreed, and record it as a dated decision in that document. Anything
else that needs changing in a spec goes to Kyle first.
`README.md` is the user-facing overview and should track the code.
`THREAT_MODEL.md` is the living security assessment: when a change fixes or
changes a finding, update its status there (§1 and §10) with the commit.
`CHANGELOG.md` records each release.
## Architecture (three layers)
```
memory — "my priors": beliefs per topic, derived from storage under a policy
storage — append-only log of raw observations; local trimming policy
comms — contact-driven exchange of observations; transport trait + adapters
```
Only observations cross the wire. Memory is a pure function of storage plus a
policy and is never transmitted. See the spec's "Architecture" section for the
`Observation`, `Store`, `BeliefPolicy`, `Transport`, and exchange protocol
details.
## Workflow
Changes are TDD: write the failing test → see it fail → implement → see it
pass → commit.
1. Keep the public API small. New public names in `tines-core` (types, trait
methods, functions) need a reason; call them out when you add them.
2. Before committing, run the full gate, which is what CI runs:
`sh scripts/ci.sh` (fmt, clippy and rustdoc with warnings as errors, tests
with and without `alloc`, and the `thumbv7em-none-eabihf` build).
3. Use conventional commit messages (`fix(core): …`, `ci: …`, `docs: …`).
The `Node` facade is core, not application glue: an application on any target
should only need to construct a `Node` with its `NodeId`, store, and transport,
call `poll()` in its loop, and `observe()` when it sees something.
## Hard constraints (non-negotiable in `tines-core`)
- `tines-core` is `#![no_std]`; `alloc` is a cargo feature (default on). With
`--no-default-features` it must build for `thumbv7em-none-eabihf`. Only
`MemStore` may require `alloc`.
- Decision C (fleet console, approved 2026-10-24): the only sanctioned core
change — the additive `NodeEvent::Contact` / `NodeEvent::Lost` variants,
so hosts can see peers come and go. `Contact` fires only for a newly
added peer; idle eviction stays silent. Everything else in core stays
frozen for 1.0.
- No async, no threads, no I/O, and no panic on untrusted input in core. Every
frame from the wire is untrusted: malformed bytes are dropped, never
unwrapped.
- Core never maps a transport `Addr` to a `NodeId`; every frame carries the
sender's `NodeId`.
- Identity hash covers `(observer, seq, topic, payload, to)` **only**.
`age_ms`, `observed_at` and `lifetime_ms` are excluded.
- Addressed observations (`to = Some`) never appear in a summary Bloom
filter, and an addressed observation past its `lifetime_ms` is never
stored or forwarded.
- `first_seq` passed to `Node::new` must not repeat across reboots
(`boot_count << 32`, with a persisted boot counter; `LogStore::first_seq`
provides it). A repeated seq after reboot collides in the ID hash and must
dedupe silently (`insert` returns `Ok(false)`).
- Receipts: a receive the store rejects (`Err`, e.g. `Full`) is never acked,
addressed or broadcast; an addressed duplicate whose local copy is released
is never acked; an `Ack` is accepted only from a peer the id was sent to
this contact.
- `NodeId = [u8; 16]`, `ObservationId = [u8; 16]`, `Tick = u64` (local
monotonic ms, supplied on every call).
- 1.0 trait extension rule: any method added to `Store`, `Transport` or
`BeliefPolicy` after 1.0 must have a default whose behaviour is safe — it
may lose performance or telemetry but never data or custody. A method that
can't have such a default is a 2.0 change (the rule `release` violated in
0.3.0).
### Review Focus (each covered by a test; keep it that way)
1. Reused `seq` after reboot → store dedupes, `insert` returns `Ok(false)`.
2. Oversized `topic`/`payload` at `observe()` → `Err(TooLarge)`, never truncate/panic.
3. Garbage frames → dropped, no panic, no store change.
4. Frame from an address with no prior `Contact` → treated as implicit contact.
5. Tick going backwards → age saturates at stored value, never underflow.
6. Every emitted frame fits the transport's `mtu()`, including summaries.
7. Store-full receive → no `Ack`; custody stays with the sender (unit test and
the full-store sim property).
8. Released addressed duplicate → no `Ack`.
### Compile-time constants (`tines-core/src/consts.rs`)
Split into **wire** (frozen for wire v1 — changing one changes how a v1 peer
decodes or interprets a frame) and **local** (never crosses the wire; may
change in a minor release):
- **Wire** (public): `WIRE_VERSION=4`, `MAX_TOPIC=64`, `MAX_PAYLOAD=1024`,
`MAX_FRAME=1500`, `SUMMARY_BYTES=512`, `ACK_IDS_CAP=90`, `MAX_SIG=64`,
`BLOOM_K=3`. Also wire, but private: `MAX_FRAGMENTS=64`, which lives in
`tines-core/src/frag.rs`, not `consts.rs`.
- **Local** (`pub(crate)`, spec decision K: defaults that a later 1.x makes
configurable): `MAX_PEERS=8`, `SUMMARY_INTERVAL_MS=2000`,
`REASSEMBLY_TIMEOUT_MS=5000`, `SEND_QUEUE_CAP=256`, `EVENT_QUEUE_CAP=64`,
`DROP_TABLE_CAP=32`, `BCAST_SENT_CAP=256`, `ACK_BUFFER_CAP=256`,
`PEER_IDLE_MS=30_000`, and `MIN_MTU=23`. `MIN_MTU` is the lower bound of the
fragmentation MTU sweep in tests (BLE ATT's default), not the smallest
usable MTU; `Transport::mtu`'s rustdoc states those floors (85 bytes for
every valid observation).
Match the exact values above. A crate outside core that needs a matching
number keeps its own constant (as `tines-bt` does for `MAX_PEERS` and
`MIN_MTU`).
The `conformance` feature also compiles a `#[doc(hidden)]`
`tines_core::conformance` module: byte-level frame encoders and decoders, and
mirrors of the local limits, for the fuzz harness and tests outside core. It
isn't supported public API and isn't covered by semver. Don't build on it
outside the workspace and `fuzz/`.
## Crate layout
```
tines/
tines-core/ no_std core: Observation, Store/Transport/BeliefPolicy traits,
exchange state machine, Node facade, in-memory store, reference
policies
tines-sim/ std: simulator, contact traces, convergence, relay and custody
property tests
tines-store/ std: LogStore, the persistent append-only backend
tines-bt/ std: BLE transport over BlueZ (bluer)
tines/ std: standalone service, Unix-socket JSON IPC
tines-bus/ std: host bus transport — hub-and-spoke Unix-socket daemon
(`tines-busd`) for simulated fleets
tines-ros2/ std: ROS2 node; rclrs behind the `ros2` feature
```
`tines-gui/` sits outside the workspace (like `fuzz/`): a standalone crate
with its own manifest and lockfile — the desktop fleet console. It's gated
by `scripts/ci.sh` separately from the workspace crates.
Only `tines-core` is headed for 1.0; the host crates stay 0.x (1.0 spec, D4).
They're std-only and may use threads and I/O, but they're still the trust
boundary for radio, IPC and disk input (see `THREAT_MODEL.md`).
`tines-ros2` with `--features ros2` is outside the default gate: check it
with the ROS2 Docker job (`Dockerfile.ros2`, `scripts/check-ros2.sh`).
## Not started
`tines-udp`, `tines-sqlite`, `tines-cli`, and `tines-serial` (from the core
spec's crate layout) belong to a future plan. Don't start them unless asked.