Repository navigation
Releases: bothy-sync/bothy
Release list
v0.4.0
A fast path between online devices. On-remote format unchanged (bothy_format = 1); the new
signaling files are optional and 0.3 readers ignore them. The Python and Flutter packages
have their own changelogs.
Added
- Direct channels between online devices (
p2pfeature, on by default but inactive until
Config::p2p(P2pConfig)). While background sync runs, devices find each other through
signaling files on the remote and open a WebRTC data channel
(str0m; ICE with host and STUN candidates,
stun.l.google.comby default, no TURN).- After uploading a segment, a device sends it to its peers (up to 64 KiB, otherwise an
announcement), and they apply it at once. The remote stays the durable record: nothing
is sent before it is uploaded, and without a channel sync works as before. - Measured p50 from a write to the other device: 1.1 to 1.7 s, against about 30 s by
polling. Database::p2p_status()(also in the blocking API) withP2pStatus::discoveringand
per-peerPeerState.AutoSync::p2p_debounce(100 ms) is the write debounce while a channel is open.
Background sync polls every 3 s while peers are being found.- A failed attempt is retried after 15 s, doubling up to 10 minutes. A device whose
onlinefile could not be written retries after 15 s.
- After uploading a segment, a device sends it to its peers (up to 64 KiB, otherwise an
- Signaling files:
codec::keys::{KeyKind::Signal, SignalKind}
(<db_id>/signaling/<device_id>/{online-<hlc>,offer-<peer>-<hlc>,answer-<peer>-<hlc>}.json)
andcodec::signal. They are sealed in encrypted databases, so SDPs and IP addresses never
reach the remote in plain text. Conformance vectors:spec/vectors/signaling.json. - CLI:
sync --watch --p2p, with--stunor--no-stun, prints peer state changes.- The hidden
bench propagate(--peerfor a second local database, orbench echoon
another device) measures how long a write takes to reach another device.
Changed
- Drive: file ids are reserved 20 at a time, so most uploads need one request instead of two.
codec::keys::KeyKindis#[non_exhaustive].AutoSynchas a new field; struct literals need..AutoSync::default().- The
p2pfeature buildsaws-lc-sys(C) for str0m'saws-lc-rscrypto backend.
Security
- No
crypto-bigint0.5 (RUSTSEC-2026-0387): P2P uses str0m'saws-lc-rsbackend rather than
its RustCrypto one.
Packages
- crates.io:
bothy,bothy-cli,bothy-oauth - PyPI:
bothy0.4.0, abi3 wheels for CPython 3.9+ on Linux (x86_64, aarch64, armv7), macOS (universal2) and Windows (x64); seebindings/python/CHANGELOG.md - pub.dev:
bothy0.4.0; seebindings/flutter/CHANGELOG.md
Accepted live between the macOS CLI and the Flutter demo on an Android emulator, on one encrypted database (#71):
- 50 writes from the Mac reached the phone with p50 1.12 s, against about 30 s by polling.
- With the network cut, sync went on through Drive.
- The channel came back about 1.5 minutes after the network did.
- No plain-text addresses or SDPs reached the remote.
v0.3.0
Python and embedded Linux. On-remote format unchanged (bothy_format = 1). The Python
package (bothy on PyPI) and the Flutter package have their own changelogs.
Added
bothy-oauth: the Google OAuth helpers (loopback sign-in with PKCE, device flow, token
refresh, revoke, a keyring/file token store and a refreshingAccessTokenProvider), moved
out of the CLI so other front ends can use them. The CLI uses it; its behaviour is unchanged.crates/bothy-ffi(not published): the start of a C ABI with a cbindgen header, for
embedding the engine in C and C++ programs.
Changed
- The default Argon2id cost for new key files is 64 MiB, 2 passes, 1 lane (was 3 passes),
bothy::crypto::DEFAULT_KDF_COST. Three passes took 1.09 s per unlock on a Cortex-A53
board. Existing key files keep the parameters stored in them.
Fixed
- The blocking API's runtime had no IO driver, so Drive requests from it (the Python
binding) panicked.
Security
ctutils0.4.5 in the lockfile (RUSTSEC-2026-0385), whichcargo install --locked bothy-cliuses.
Packages
- crates.io:
bothy,bothy-cli,bothy-oauth - PyPI:
bothy0.3.0, abi3 wheels for CPython 3.9+ on Linux (x86_64, aarch64, armv7), macOS (universal2) and Windows (x64); seebindings/python/CHANGELOG.md - pub.dev:
bothy0.3.0; seebindings/flutter/CHANGELOG.md
Tested end to end on an arm64 board (4x Cortex-A53, Ubuntu 22.04, ROS 2 Humble): the robot agent in examples/pi-agent took a mission from the Flutter demo, synced 48 hours of offline telemetry (2,880 records) without loss, peaked at 28 MiB while syncing, and bridged missions and telemetry to ROS 2 topics.
bothy 0.2.0: encryption, conflict log, Flutter
Encryption, the conflict log and the Flutter binding. On-remote format still
bothy_format = 1: encrypted files use the encrypted flag that format 1 reserved, and
key files are new files that 0.1 readers ignore.
Added
- Client-side encryption (
cryptofeature, on by default): XChaCha20-Poly1305 for segment
bodies, snapshot chunks anddevice.json, with a random data key wrapped under an
Argon2id-derived key in per-device key files (keys/<device>/key-<hlc>.json). Config::encrypt,Config::password,Config::keyandConfig::kdf_cost;
Database::{is_encrypted, is_locked, unlock, unlock_with_key, lock, export_key, change_password}, mirrored in the blocking API. A password unlocks offline from the cached
key file.- Password change re-wraps the data key; the old password stops working on every device and
SyncReport::password_changedreports it. Cached data keys keep working. ErrorKind::KeyRequiredfor syncing a locked database.- Conflict log across the database:
Database::conflicts(since_ms)andclear_conflicts(),
Config::conflict_log(limit)(default 1,000 records,Noneturns logging off), and the
CLI commandbothy conflicts [--since <duration|date>] [--clear].Conflictnow carries
the collection, document id and the time it was recorded, and is#[non_exhaustive]. - CLI:
init --encrypt,unlock [--remember],lock,passwd,--rememberoninitand
join, the global--password-stdinflag, andBOTHY_PASSWORD/BOTHY_NEW_PASSWORD.
statusshows whether the database is encrypted and locked. - CLI:
bothy inspect scan <text>...downloads every remote file of the database and fails
if any contains the text, to check that an encrypted database leaks nothing. - Conformance vectors for the crypto primitives and encrypted segments and snapshots; a fuzz
target for key files and sealed JSON; encrypted seeds in the simulation.
Changed
- A segment with the encrypted flag and no key now fails with
KeyRequiredinstead of
FormatTooNew(vectorsegment/invalid.json, caseencrypted_without_key).
Fixed
- On Android the Drive backend verifies TLS against the Mozilla root store (
webpki-roots);
the platform verifier needs JNI setup there and panicked on the first request.
Packages
- crates.io:
bothy0.2.0,bothy-cli0.2.0 - pub.dev:
bothy0.2.0 (Flutter; building an app needs a Rust toolchain) - Demo app:
examples/flutter-notes
Full changelog: v0.1.0...v0.2.0