Skip to content

Releases: bothy-sync/bothy

v0.4.0

Choose a tag to compare

@alitalhq alitalhq released this 24 Oct 10:47
3bc6bd4

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 (p2p feature, 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.com by 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) with P2pStatus::discovering and
      per-peer PeerState.
    • 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
      online file could not be written retries after 15 s.
  • Signaling files: codec::keys::{KeyKind::Signal, SignalKind}
    (<db_id>/signaling/<device_id>/{online-<hlc>,offer-<peer>-<hlc>,answer-<peer>-<hlc>}.json)
    and codec::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 --stun or --no-stun, prints peer state changes.
    • The hidden bench propagate (--peer for a second local database, or bench echo on
      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::KeyKind is #[non_exhaustive].
  • AutoSync has a new field; struct literals need ..AutoSync::default().
  • The p2p feature builds aws-lc-sys (C) for str0m's aws-lc-rs crypto backend.

Security

  • No crypto-bigint 0.5 (RUSTSEC-2026-0387): P2P uses str0m's aws-lc-rs backend rather than
    its RustCrypto one.

Packages

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

Choose a tag to compare

@alitalhq alitalhq released this 23 Oct 09:48
95e0eec

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 refreshing AccessTokenProvider), 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

  • ctutils 0.4.5 in the lockfile (RUSTSEC-2026-0385), which cargo install --locked bothy-cli uses.

Packages

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

Choose a tag to compare

@alitalhq alitalhq released this 22 Oct 19:14
ebba5c6

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 (crypto feature, on by default): XChaCha20-Poly1305 for segment
    bodies, snapshot chunks and device.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::key and Config::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_changed reports it. Cached data keys keep working.
  • ErrorKind::KeyRequired for syncing a locked database.
  • Conflict log across the database: Database::conflicts(since_ms) and clear_conflicts(),
    Config::conflict_log(limit) (default 1,000 records, None turns logging off), and the
    CLI command bothy conflicts [--since <duration|date>] [--clear]. Conflict now carries
    the collection, document id and the time it was recorded, and is #[non_exhaustive].
  • CLI: init --encrypt, unlock [--remember], lock, passwd, --remember on init and
    join, the global --password-stdin flag, and BOTHY_PASSWORD / BOTHY_NEW_PASSWORD.
    status shows 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 KeyRequired instead of
    FormatTooNew (vector segment/invalid.json, case encrypted_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

Full changelog: v0.1.0...v0.2.0