Metadata-Version: 2.4
Name: zebridge
Version: 0.1.0
Summary: ZeBridge client: a local replica of PostgreSQL through NATS, over libzb's C ABI
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/ndrean/zebridge
Project-URL: Documentation, https://github.com/ndrean/zebridge/blob/main/CLIENTS.md
Project-URL: Issues, https://github.com/ndrean/zebridge/issues
Keywords: postgresql,nats,jetstream,offline-first,sqlite,replica,sync
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Topic :: Database
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# zebridge (Python)

A ZeBridge client for Python: a local replica that follows PostgreSQL through the
bridge, and writes back through it. Pure Python over libzb's C ABI — no ctypes in your
code. Which library for which host: [DISTRIBUTION.md](https://github.com/ndrean/zebridge/blob/main/DISTRIBUTION.md).

```sh
pip install zebridge
```

The wheels carry libzb for Linux (x86_64, aarch64; glibc 2.35+) and macOS (Apple silicon,
Intel; 13+). Python 3.10 or later.

## Use

```python
from zebridge import ZeBridge

with ZeBridge(bridge_url="https://zb.example.com", invite=code, tables=["orders"],
              on_change=lambda r: print("changed:", r.get("changed_tables"))) as zb:
    rows = zb.query("SELECT * FROM orders WHERE status = ?", "open")   # list of dicts
    zb.mutate("orders", "UPDATE", {"id": 7}, {"status": "done"})
```

- **The options** are the ones every ZeBridge client reads ([CLIENTS.md](https://github.com/ndrean/zebridge/blob/main/CLIENTS.md)), in camelCase or
  snake_case (`bridge_url` is `bridgeUrl`). A dict works too: `ZeBridge({"bridgeUrl": …})`.
- **Enrollment.** The first run redeems `invite` at the bridge and keeps the identity next
  to the replica (`identity_path`, default `<db_path>.identity`, mode 0600). Later runs need
  neither the invite nor a NATS URL, and the JWT renews itself before it expires.
- **The thread.** libzb drives a client from one thread; `ZeBridge` owns it. Any thread may
  call it. An idle client polls every `poll_ms` (250 ms); while calls keep coming, the
  thread serves each one at once and still polls every `poll_ms`, so a writer in a loop runs
  at its own pace (measured: 1,700 writes/s, all settled). `on_change` hears every poll
  that applied rows, settled writes or brought requests — on the worker thread (calling
  back into `zb` from it is fine).
- **Errors** raise `ZeBridgeError` with libzb's words. `on_error` hears the loop's.
- **Revocation.** Once revoked, `zb.revoked` is true and the loop stops; `wipe()` removes the
  rows.
- **Services.** `serve({...})`, then answer `on_change`'s `requests` with `reply(id, answer)`;
  `request` and `ingest` for on-demand tables.

## The library

`ZB_LIB` names libzb's shared library; else a copy bundled in the package
(`zebridge/lib/`), else the repository's own build (`cd libzb && zig build
-Doptimize=ReleaseFast`), else the system's: on Debian and Ubuntu, `sudo apt install
./libzb_<version>_<arch>.deb` puts it where the loader finds it. `ZB_ABI` pins the C ABI this package speaks; libzb/python/abi_check.py
checks it with the other bindings.
