release(core): tines-core 1.0.0
This commit is contained in:
@@ -19,7 +19,7 @@ architecture in the **spec first**:
|
|||||||
- **Addressed delivery (authoritative):** `docs/superpowers/specs/2026-10-18-tines-addressed-delivery.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 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.
|
- **Fleet fixes (done):** `docs/superpowers/specs/2026-10-19-tines-fleet-fixes.md` — security-assessment findings F1–F5 and F17.
|
||||||
- **tines-core 1.0 (next, core):** `docs/superpowers/specs/2026-10-18-tines-1-0-design.md` — wire version, public API, signature slot, core hardening (§10).
|
- **tines-core 1.0 (done — shipped as tines-core 1.0.0, 2026-10-21):** `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.
|
- **Host hardening (next, hosts):** `docs/superpowers/specs/2026-10-19-tines-host-hardening.md` — findings F10–F12.
|
||||||
- **Implementation plans:** `docs/superpowers/plans/`
|
- **Implementation plans:** `docs/superpowers/plans/`
|
||||||
|
|
||||||
|
|||||||
+42
-4
@@ -4,11 +4,49 @@ All notable changes to the tines crates. Versions follow
|
|||||||
[Semantic Versioning](https://semver.org/); before 1.0, a minor bump may break
|
[Semantic Versioning](https://semver.org/); before 1.0, a minor bump may break
|
||||||
the API.
|
the API.
|
||||||
|
|
||||||
## Unreleased
|
## 1.0.0 — 2026-10-21
|
||||||
|
|
||||||
The fleet-deployment milestone and the fleet fixes (security assessment
|
tines-core 1.0: the wire format is frozen (wire v1), the public API is
|
||||||
findings F1–F5 and F17; see `THREAT_MODEL.md`). The workspace version is
|
narrowed and documented, and the core hardening from the 1.0 spec is in.
|
||||||
still 0.3.0.
|
This release ships the fleet-deployment milestone (handoff receipts) and the
|
||||||
|
remaining fleet fixes (F1–F5, F6, F8, F9, F13, F17; see `THREAT_MODEL.md`).
|
||||||
|
|
||||||
|
### Breaking (from 0.3.0)
|
||||||
|
|
||||||
|
- **Wire v1** (tines-core). Every frame now leads with a `WIRE_VERSION` byte
|
||||||
|
(4). 0.x-shaped frames are rejected as `WireError::VersionMismatch`.
|
||||||
|
- **`Dest` replaces `to`/`lifetime_ms`** (tines-core). Addressed delivery now
|
||||||
|
takes `dest: Option<Dest>` where `Dest { to, lifetime_ms }`; the lifetime is
|
||||||
|
excluded from observation identity.
|
||||||
|
- **Signature slot** (tines-core). `Observation` carries a reserved
|
||||||
|
`sig: Option<Signature>` (algorithm + up to 64 bytes). 1.0 enforces the
|
||||||
|
wire shape but does not sign or verify (1.1).
|
||||||
|
- **Public API narrowing** (tines-core). Nine items moved `pub(crate)`,
|
||||||
|
eleven types are `#[non_exhaustive]` (build via constructors, not struct
|
||||||
|
literals). `Frame` and `WireError` are now *public*: the wire format is the
|
||||||
|
1.x compatibility contract.
|
||||||
|
- **`Store` trait settled** (tines-core). `flush(&mut self, now)` and
|
||||||
|
`is_empty` joined the trait; `Node::store_mut` is gone. A new opt-in
|
||||||
|
`WallClockStore` trait carries the trusted-time operations
|
||||||
|
(`set_wall_clock`, `peer_time_estimate`) that only `LogStore` implements.
|
||||||
|
- **`NodeEvent` + counted drops** (tines-core). Exchange outcomes surface as
|
||||||
|
`NodeEvent` (`Delivered`, `Dropped { reason, count }`) through
|
||||||
|
`Node::drain_events`; `DropReason` is `VersionMismatch`, `Malformed`, or
|
||||||
|
`IdentityChanged`.
|
||||||
|
- **`Node::poll` is the only pump** (tines-core). The separate custody
|
||||||
|
re-offer pass folded into `poll`.
|
||||||
|
- **Log format v2** (tines-store). `FORMAT_VERSION` bumped for the new
|
||||||
|
`Observation` encoding; opening an older log is a hard error that names
|
||||||
|
the fix (delete the log).
|
||||||
|
- **Quarantine + wall-clock fold** (tines-store). `LogStore::open(path, now)`
|
||||||
|
takes no wall time; observations held across a power-off are quarantined
|
||||||
|
until the host feeds trusted time via `set_wall_clock`.
|
||||||
|
- **Trusted wall time in the hosts** (tines). A `trusted_clock = "system"`
|
||||||
|
config flag or a peer-derived estimate (smallest `observed_at + age_ms`)
|
||||||
|
anchors the wall clock once; observations carry `observed_at: None` until
|
||||||
|
then.
|
||||||
|
- **MSRV** (tines-core). `rust-version = "1.82"`; CI builds and tests
|
||||||
|
tines-core alone on it.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
|||||||
Generated
+1
-1
@@ -1486,7 +1486,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tines-core"
|
name = "tines-core"
|
||||||
version = "0.3.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"heapless 0.8.0",
|
"heapless 0.8.0",
|
||||||
|
|||||||
@@ -351,14 +351,11 @@ for event in node.drain_events() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Keep the store bounded.
|
// Keep the store bounded.
|
||||||
node.trim(
|
let mut policy = TrimPolicy::default();
|
||||||
&TrimPolicy {
|
policy.keep_per_topic = Some(4);
|
||||||
keep_per_topic: Some(4),
|
policy.max_age_ms = Some(3_600_000);
|
||||||
max_age_ms: Some(3_600_000),
|
policy.max_count = Some(10_000);
|
||||||
max_count: Some(10_000),
|
node.trim(&policy, 500);
|
||||||
},
|
|
||||||
500,
|
|
||||||
);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`observe` rejects an oversized topic or payload with `ObserveError::TooLarge`;
|
`observe` rejects an oversized topic or payload with `ObserveError::TooLarge`;
|
||||||
@@ -598,6 +595,27 @@ sh scripts/ci.sh
|
|||||||
GitHub Actions runs the same script on every push to `master` and on every
|
GitHub Actions runs the same script on every push to `master` and on every
|
||||||
pull request.
|
pull request.
|
||||||
|
|
||||||
|
## Stability
|
||||||
|
|
||||||
|
tines-core 1.0 guarantees, within the 1.x line:
|
||||||
|
|
||||||
|
- **Wire:** every 1.x release speaks wire v1. Only new optional `Frame`
|
||||||
|
variants may be added, plus new `SigAlgo` ids and signature versions within
|
||||||
|
the 64-byte `MAX_SIG` budget. A wire-v2 change, including any post-quantum
|
||||||
|
signature or a `MAX_FRAME` above 1500, ships as tines-core 2.0.
|
||||||
|
- **Identity:** `Observation::id` never changes within a major version, and
|
||||||
|
should not change across majors unless the hash itself is broken.
|
||||||
|
- **API:** standard semver; MSRV bumps are minor. Public types are
|
||||||
|
`#[non_exhaustive]` where new fields are plausible; a public trait may only
|
||||||
|
gain methods with a default implementation (or a new opt-in trait — e.g.
|
||||||
|
`WallClockStore` for wall-clock folds, which `Store` itself never carries).
|
||||||
|
- **Local behaviour** (capacities, timings, scheduling order, which candidate
|
||||||
|
goes first) can change in a minor release as long as the protocol
|
||||||
|
properties hold: custody always keeps ≥ 1 holder, receipts never shorten
|
||||||
|
world state, and every frame fits `mtu()`.
|
||||||
|
- **Signatures:** 1.0 carries them; 1.1 signs and verifies them, without
|
||||||
|
breaking the wire or the API.
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
tines has **no trust layer yet**, by design. Anyone in radio range can read
|
tines has **no trust layer yet**, by design. Anyone in radio range can read
|
||||||
|
|||||||
+43
-6
@@ -57,11 +57,22 @@ threat questions (§8) before designing signing.
|
|||||||
| F5 | Fixed | `4718b6f`, `24b5a78` |
|
| F5 | Fixed | `4718b6f`, `24b5a78` |
|
||||||
| F17 | Fixed | `6e2dc6f` |
|
| F17 | Fixed | `6e2dc6f` |
|
||||||
|
|
||||||
|
**1.0 fixes, 2026-10-21** (through `941a7ce`):
|
||||||
|
|
||||||
|
| # | Status | Commit |
|
||||||
|
|---|---|---|
|
||||||
|
| F6 | Fixed | `fb71810` |
|
||||||
|
| F8 | Fixed | `fb71810` |
|
||||||
|
| F9 | Fixed | `fb71810`, `a180ec2` |
|
||||||
|
| F13 | Fixed | `fb71810` |
|
||||||
|
| F18 | **Partly fixed**: the stale clock-marker tick is fixed (`Store::flush(now)` takes the caller's `now`); hosts still don't flush on a schedule | `c42f5de` |
|
||||||
|
| F19 | **Open** (new, 2026-10-21): peer-derived trusted time is unauthenticated | — |
|
||||||
|
|
||||||
Still open:
|
Still open:
|
||||||
- F6, F8, F9 and F13: the 1.0 spec §10.
|
|
||||||
- F10–F12: the host-hardening spec.
|
- F10–F12: the host-hardening spec.
|
||||||
- F7 and F14: 1.1.
|
- F7 and F14: 1.1.
|
||||||
- F18 (new) and the rest of F3: host work, not yet specced (§10).
|
- F18 (remaining): scheduled flushes, host work (§10).
|
||||||
|
- F19: 1.1 (signatures), or a local clock source before peers (§6).
|
||||||
|
|
||||||
## 2. System and trust boundaries
|
## 2. System and trust boundaries
|
||||||
|
|
||||||
@@ -482,10 +493,12 @@ inserts and trims (log.rs module docs, "Durability contract").
|
|||||||
advances only while observations arrive, so a robot that is mostly
|
advances only while observations arrive, so a robot that is mostly
|
||||||
offline, the normal case, may never flush.
|
offline, the normal case, may never flush.
|
||||||
- tines-ros2 never calls `flush` or `trim`.
|
- tines-ros2 never calls `flush` or `trim`.
|
||||||
- Even when it runs, `flush` writes `LogStore`'s own tick as the clock
|
- Even when it runs, `flush` wrote `LogStore`'s own tick as the clock
|
||||||
marker, and that tick advances only when something new is inserted
|
marker, and that tick advanced only when something new was inserted
|
||||||
(tines-store/src/log.rs:698). On a quiet robot the marker is stale, and
|
(tines-store/src/log.rs:698). On a quiet robot the marker was stale, and
|
||||||
the time since the last insert is lost.
|
the time since the last insert was lost. **Fixed in 1.0** (`c42f5de`):
|
||||||
|
`Store::flush(now)` takes the caller's tick, so the marker is the true
|
||||||
|
current time at every flush.
|
||||||
- **Effect:**
|
- **Effect:**
|
||||||
- After a restart, every stored observation's age is folded only up to the
|
- After a restart, every stored observation's age is folded only up to the
|
||||||
last clock marker. Time since that marker is lost, so facts look fresher
|
last clock marker. Time since that marker is lost, so facts look fresher
|
||||||
@@ -502,6 +515,30 @@ inserts and trims (log.rs module docs, "Durability contract").
|
|||||||
on clean shutdown. The fleet-fixes spec asked for "a scheduled flush". The
|
on clean shutdown. The fleet-fixes spec asked for "a scheduled flush". The
|
||||||
trim defaults belong to host hardening (F10).
|
trim defaults belong to host hardening (F10).
|
||||||
|
|
||||||
|
### F19 — Peer-derived trusted time is unauthenticated (Medium, A0)
|
||||||
|
|
||||||
|
*Added 2026-10-21, when the hosts gained the peer-time path (Task 10b).*
|
||||||
|
When a host has no `trusted_clock = "system"` source, it estimates wall time
|
||||||
|
from a peer's observation: `observed_at + age_ms` from the youngest such
|
||||||
|
observation, then folds it into the store once. Nothing authenticates that
|
||||||
|
estimate.
|
||||||
|
|
||||||
|
- **Effect:**
|
||||||
|
- A forged time **ahead** of the true time only makes observations expire
|
||||||
|
early: the fold ages everything faster, addressed messages die sooner,
|
||||||
|
and `observed_at` stamps run fast. Availability loss, not integrity loss.
|
||||||
|
- A forged time **behind** the true time takes the "downtime unknown"
|
||||||
|
path (or under-folds), leaving the store quarantined longer or ages
|
||||||
|
younger — the same effect as F17/F18 by another route.
|
||||||
|
- Worst case: the robot's own `observed_at` stamps are wrong until a
|
||||||
|
local source corrects them, so every observation it publishes between
|
||||||
|
the forged fold and the correction carries a bad wall time.
|
||||||
|
- **Mitigation:** prefer a local source whenever one exists (the config
|
||||||
|
flag is exactly that); otherwise accept that a peer's time is a
|
||||||
|
lower-bound hint, not a trusted clock. Signing and verification (1.1)
|
||||||
|
closes it; a monotonic-host sanity check (reject a wall time behind the
|
||||||
|
last fold) would contain the "behind" case today.
|
||||||
|
|
||||||
## 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 |
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ keywords = ["robotics", "gossip", "delay-tolerant", "ble", "bluer"]
|
|||||||
categories = ["science::robotics", "network-programming"]
|
categories = ["science::robotics", "network-programming"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tines-core = { path = "../tines-core", version = "0.3.0" }
|
tines-core = { path = "../tines-core", version = "1" }
|
||||||
bluer = { version = "0.17", features = ["bluetoothd", "l2cap"] }
|
bluer = { version = "0.17", features = ["bluetoothd", "l2cap"] }
|
||||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "sync", "net", "time", "signal"] }
|
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "sync", "net", "time", "signal"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tines-core"
|
name = "tines-core"
|
||||||
version.workspace = true
|
version = "1.0.0"
|
||||||
rust-version = "1.82.0"
|
rust-version = "1.82.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use crate::observation::{NodeId, Observation};
|
|||||||
|
|
||||||
/// Reduces an ordered stream of observations to a single accumulated belief.
|
/// Reduces an ordered stream of observations to a single accumulated belief.
|
||||||
pub trait BeliefPolicy {
|
pub trait BeliefPolicy {
|
||||||
|
/// The accumulated belief this policy folds into.
|
||||||
type Belief;
|
type Belief;
|
||||||
/// Streaming fold: called once per matching observation (already
|
/// Streaming fold: called once per matching observation (already
|
||||||
/// age-adjusted to `now`), in no particular order. `acc` is `None` before
|
/// age-adjusted to `now`), in no particular order. `acc` is `None` before
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
//! tines-core: content-defined Observation identity and shared-memory core.
|
//! tines-core: content-defined Observation identity and shared-memory core.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ pub struct Dest {
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Observation {
|
pub struct Observation {
|
||||||
|
/// The node that published this observation.
|
||||||
pub observer: NodeId,
|
pub observer: NodeId,
|
||||||
|
/// The observer's per-observation sequence number.
|
||||||
pub seq: u64,
|
pub seq: u64,
|
||||||
/// The observer's elapsed time for this observation, in ms.
|
/// The observer's elapsed time for this observation, in ms.
|
||||||
///
|
///
|
||||||
@@ -56,7 +58,9 @@ pub struct Observation {
|
|||||||
/// this observation was published, or `None` if the observer has no
|
/// this observation was published, or `None` if the observer has no
|
||||||
/// trusted clock. Set once at construction and never changed by a relay.
|
/// trusted clock. Set once at construction and never changed by a relay.
|
||||||
pub observed_at: Option<u64>,
|
pub observed_at: Option<u64>,
|
||||||
|
/// The topic this observation is published under.
|
||||||
pub topic: Topic,
|
pub topic: Topic,
|
||||||
|
/// The observation's content.
|
||||||
pub payload: Payload,
|
pub payload: Payload,
|
||||||
/// Addressing: `None` is a broadcast, `Some(dest)` is addressed to
|
/// Addressing: `None` is a broadcast, `Some(dest)` is addressed to
|
||||||
/// `dest.to` and expires per `dest.lifetime_ms`.
|
/// `dest.to` and expires per `dest.lifetime_ms`.
|
||||||
|
|||||||
+20
-1
@@ -21,24 +21,40 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum Frame {
|
pub enum Frame {
|
||||||
|
/// A broadcast bloom summary of a node's observation-id set.
|
||||||
Summary {
|
Summary {
|
||||||
|
/// The sending node.
|
||||||
from: NodeId,
|
from: NodeId,
|
||||||
|
/// The bloom salt the bits were hashed with.
|
||||||
salt: u32,
|
salt: u32,
|
||||||
|
/// The broadcast bloom bits (the summary body).
|
||||||
bits: Vec<u8, { consts::SUMMARY_BYTES }>,
|
bits: Vec<u8, { consts::SUMMARY_BYTES }>,
|
||||||
},
|
},
|
||||||
|
/// A single observation, broadcast or addressed (see [`Observation::dest`]).
|
||||||
Obs {
|
Obs {
|
||||||
|
/// The sending (relaying or originating) node.
|
||||||
from: NodeId,
|
from: NodeId,
|
||||||
|
/// The carried observation.
|
||||||
obs: Observation,
|
obs: Observation,
|
||||||
},
|
},
|
||||||
|
/// One fragment of a large addressed observation (reassembly by `id`).
|
||||||
Frag {
|
Frag {
|
||||||
|
/// The sending node.
|
||||||
from: NodeId,
|
from: NodeId,
|
||||||
|
/// The observation this fragment belongs to.
|
||||||
id: ObservationId,
|
id: ObservationId,
|
||||||
|
/// This fragment's 0-based position in the reassembly.
|
||||||
index: u8,
|
index: u8,
|
||||||
|
/// The total number of fragments for `id`.
|
||||||
count: u8,
|
count: u8,
|
||||||
|
/// The fragment payload bytes.
|
||||||
data: Vec<u8, { consts::MAX_FRAME }>,
|
data: Vec<u8, { consts::MAX_FRAME }>,
|
||||||
},
|
},
|
||||||
|
/// An acknowledgement of stored observation ids (custody-chain release).
|
||||||
Ack {
|
Ack {
|
||||||
|
/// The acknowledging node.
|
||||||
from: NodeId,
|
from: NodeId,
|
||||||
|
/// The observation ids being acknowledged.
|
||||||
ids: Vec<ObservationId, { consts::ACK_IDS_CAP }>,
|
ids: Vec<ObservationId, { consts::ACK_IDS_CAP }>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -139,7 +155,10 @@ pub enum WireError {
|
|||||||
/// The input bytes were malformed, truncated, or not a valid `Frame`.
|
/// The input bytes were malformed, truncated, or not a valid `Frame`.
|
||||||
Malformed,
|
Malformed,
|
||||||
/// The frame's leading `WIRE_VERSION` byte did not match `consts::WIRE_VERSION`.
|
/// The frame's leading `WIRE_VERSION` byte did not match `consts::WIRE_VERSION`.
|
||||||
VersionMismatch { version: u8 },
|
VersionMismatch {
|
||||||
|
/// The version byte read off the wire.
|
||||||
|
version: u8,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ default = []
|
|||||||
ros2 = ["dep:rclrs", "dep:ros-env", "dep:tines"]
|
ros2 = ["dep:rclrs", "dep:ros-env", "dep:tines"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tines-core = { path = "../tines-core", version = "0.3.0" }
|
tines-core = { path = "../tines-core", version = "1" }
|
||||||
tines-store = { path = "../tines-store", version = "0.3.0" }
|
tines-store = { path = "../tines-store", version = "0.3.0" }
|
||||||
tines-bt = { path = "../tines-bt", version = "0.3.0" }
|
tines-bt = { path = "../tines-bt", version = "0.3.0" }
|
||||||
# The host's wall-clock abstraction (`HostClock`) and the trusted-time state
|
# The host's wall-clock abstraction (`HostClock`) and the trusted-time state
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ keywords = ["robotics", "gossip", "simulation", "delay-tolerant"]
|
|||||||
categories = ["simulation", "science::robotics"]
|
categories = ["simulation", "science::robotics"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tines-core = { path = "../tines-core", version = "0.3.0" }
|
tines-core = { path = "../tines-core", version = "1" }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ keywords = ["robotics", "gossip", "delay-tolerant", "store", "persistent"]
|
|||||||
categories = ["science::robotics"]
|
categories = ["science::robotics"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tines-core = { path = "../tines-core", version = "0.3.0" }
|
tines-core = { path = "../tines-core", version = "1" }
|
||||||
postcard = { version = "1", features = ["use-std"] }
|
postcard = { version = "1", features = ["use-std"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ keywords = ["robotics", "delay-tolerant", "science::robotics", "ipc", "ble"]
|
|||||||
categories = ["science::robotics", "network-programming"]
|
categories = ["science::robotics", "network-programming"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tines-core = { path = "../tines-core", version = "0.3.0" }
|
tines-core = { path = "../tines-core", version = "1" }
|
||||||
tines-store = { path = "../tines-store", version = "0.3.0" }
|
tines-store = { path = "../tines-store", version = "0.3.0" }
|
||||||
tines-bt = { path = "../tines-bt", version = "0.3.0" }
|
tines-bt = { path = "../tines-bt", version = "0.3.0" }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|||||||
Reference in New Issue
Block a user