bothy (Python)
Serverless, local-first sync engine that uses your own Google Drive as the transport.
⚠️ Early release. The API may still change between minor versions.
Bothy lets programs sync structured data between devices without running a server. The devices can be desktops, phones (through the Flutter package) or embedded Linux boards such as a Raspberry Pi. Each device keeps a local SQLite database and works offline; devices exchange immutable files through the user's own Google Drive or a shared folder.
Concurrent edits to different fields merge. Optionally, everything stored on Drive is end-to-end encrypted.
A typical embedded setup: a robot gets missions from a phone app and sends logs back, with no server or MQTT broker in between.
import bothy
db = bothy.Database.open("robot.db", app_id="com.example.robot",
remote=bothy.FolderRemote("/mnt/shared/robot"))
db.init_or_join()
missions = db.collection("missions")
missions.put("m1", {"goal": "dock", "speed": 0.5})
missions.patch("m1", {"state": "accepted"})
print(missions.get("m1"))
db.start_auto_sync() # or db.sync() when you choose
for change in db.changes(timeout=30): # local and remote changes
print(change.collection, change.id, "from another device" if change.remote else "")
db.close()
The API is synchronous and thread-safe, and calls release the GIL. Wheels are abi3, for CPython 3.9 or newer on Linux (x86_64, aarch64, armv7), macOS and Windows.
Google Drive
Bothy never signs in by itself. Use OAuth clients of your own Google Cloud project; every client of one project sees the same Drive files.
import bothy, bothy.auth
# A board without a browser: the device flow (a "TVs and Limited Input devices" client).
grant = bothy.auth.device_login("client_tv.json") # prints a URL and a code to enter on a phone
grant.save("token.json") # readable only by you
tokens = bothy.auth.TokenProvider("client_tv.json", bothy.auth.Grant.load("token.json"))
db = bothy.Database.open("robot.db", app_id="com.example.robot",
remote=bothy.DriveRemote.folder("robot", tokens))
- Remote type: the device flow only grants the
drive.filescope, so headless devices useDriveRemote.folder(a visibleBothy/<name>folder). - Desktops can use
bothy.auth.loopback_loginwith a "Desktop app" client, andDriveRemote.appdata(the hidden app folder). - Your own tokens: any callable
(force_refresh: bool) -> strworks astokens.
Encryption
db = bothy.Database.open(..., encrypt=True, password=pw) # the device that creates it
db = bothy.Database.open(..., password=pw) # devices that join
key = db.export_key() # keep it to skip the password
db = bothy.Database.open(..., key=key)
- Wrong password: raises
bothy.WrongPasswordError. - Locked database: opened without a secret, an encrypted database is locked. Local reads
and writes work;
sync()raisesbothy.KeyRequiredErroruntilunlock(). - Embedded boards: keep the key instead of the password. Unlocking with a password costs 64 MiB of memory once; syncing with the key does not. On a 4-core Cortex-A53 board, syncing two days of per-minute telemetry with the key peaked at 28 MiB of resident memory.
A complete example, a robot agent that takes missions from the Flutter demo and reports
telemetry (with an optional ROS 2 bridge), is in
examples/pi-agent.
Direct channels (P2P)
db = bothy.Database.open(..., p2p=True) # stun=[] keeps it on the local network
db.start_auto_sync()
print(db.p2p_status()) # {'running': True, 'discovering': False, 'peers': [{'device_id': ..., 'state': 'connected'}]}
- What it does: while background sync runs, devices of the same database that are online at the same time open a direct WebRTC data channel. Writes then arrive within about a second instead of at the next poll.
- Durability: Drive stays the durable record. Segments go over the channel only after they are uploaded, and losing the channel just means waiting for the next poll.
- Finding each other: peers find each other through small files on the remote; that usually takes 10–30 seconds after both are up.
- Outside the local network: a STUN server (Google's public one by default) tells each device its public address. There is no relay, so a strict NAT falls back to polling.
Errors
Every engine error is a bothy.BothyError. Each error kind has its own subclass, for
example OfflineError, AuthRequiredError, WrongPasswordError, KeyRequiredError and
InvalidInputError.
Building from source
pip install maturin
maturin develop # needs a Rust toolchain
python -m unittest discover -s tests
- Repository: https://github.com/bothy-sync/bothy
- License: MIT OR Apache-2.0
Metadata
Release files for bothy 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| bothy-0.4.0.tar.gz | 195.6 kB | Without provenance | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| bothy-0.4.0-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| bothy-0.4.0-cp39-abi3-manylinux_2_28_armv7l.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ ARMv7l | Details |
| bothy-0.4.0-cp39-abi3-manylinux_2_28_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| bothy-0.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| bothy-0.4.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64, macOS 10.12+ universal2 (ARM64, x86-64), macOS 11.0+ ARM64 | Details |
Total release size: 46.7 MB
Release files / bothy-0.4.0.tar.gz
| Download URL | bothy-0.4.0.tar.gz |
|---|---|
| Size | 195.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8748e25273d728da6d1a49e27e99381bfa482ce0163640fed18069f82e3c62c1
|
|
BLAKE2b-256 checksum How to use checksums |
9a60ac170eb2d0a6da0237ad245b618b568a03f09654c7e277ce1b5697c222bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / bothy-0.4.0-cp39-abi3-win_amd64.whl
| Download URL | bothy-0.4.0-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 6.9 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
867a86db1b0cbf93d519b939e400d446787384aaefb692612cefd751dfbcdf4e
|
|
BLAKE2b-256 checksum How to use checksums |
2df00a5cf56aace680f4b24c8c0a89988ae06e59a4bb43d627aa37db67225b0a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / bothy-0.4.0-cp39-abi3-manylinux_2_28_armv7l.whl
| Download URL | bothy-0.4.0-cp39-abi3-manylinux_2_28_armv7l.whl |
|---|---|
| Size | 8.1 MB |
| Tags | CPython 3.9 Linux glibc 2.28+ ARMv7l abi3 |
|
SHA-256 checksum How to use checksums |
a8eb4338fba5e04b0a8a697b1e29154c3d6b14820a9dc74117db6b480a485f54
|
|
BLAKE2b-256 checksum How to use checksums |
9b289d85a4c116eb50210b0d35bc3b5ff6952955864dd02860f1016b309ac538
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / bothy-0.4.0-cp39-abi3-manylinux_2_28_aarch64.whl
| Download URL | bothy-0.4.0-cp39-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 8.7 MB |
| Tags | CPython 3.9 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
7c0f0f782e8c92d386aa8f60dfe480d5b00134048ec49327446e37ce2826854d
|
|
BLAKE2b-256 checksum How to use checksums |
9d1f28e11e69605d3a7e369a739639ed41b4c1ef0d4162dc433d6ccad0fe1e73
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / bothy-0.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | bothy-0.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 8.9 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
3e18c443b2badb70debe159f462322abb79727f958803bbebc72990adefdd77d
|
|
BLAKE2b-256 checksum How to use checksums |
d5a9a1d7ff2ef2373c4fcf6cdbf105b20224950a053ef366fde08ed738aa353e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / bothy-0.4.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
| Download URL | bothy-0.4.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl |
|---|---|
| Size | 13.9 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
7dd12be5b23fabd5c3592a7210cd35994a6419325872533e28e6668b3a9c84af
|
|
BLAKE2b-256 checksum How to use checksums |
823bbf756d661971595f01ae172fa0feed07c0f13256dc750adb7e90ecd0ef1b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Security for bothy 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.