prism-sync keeps Prism Plural devices in sync without giving the server their content. Each device records changes locally, encrypts them, and exchanges them through a relay. Devices merge the changes when they reconnect.
This repository contains the Rust engine, its Dart/Flutter integration, and the relay server. We develop them for Prism, a plural system management app, and keep them separate so the protocol can be inspected and the engine can be used outside the app.
- Run a relay: follow the self-hosting guide.
- Understand the protocol: read ARCHITECTURE.md and the pairing contract.
- Work on the code: start with CONTRIBUTING.md.
- Use Prism: visit the app's download page.
The engine records field-level changes with a hybrid logical clock. When devices edit different fields, both changes can survive. When they edit the same field, a deterministic last-write-wins order chooses the result: clock, then device ID, then operation ID. It doesn't combine two competing edits to the same text field. Deletes have tombstone handling to prevent stale edits from bringing records back.
Content is encrypted on the client. The protocol uses XChaCha20-Poly1305 for content encryption, hybrid Ed25519/ML-DSA-65 batch signatures, and X-Wing for key exchange. Architecture explains the key lifecycle and what gets verified before a change is applied.
The relay stores encrypted payloads, but also sees device membership, public keys, epochs, and transfer sizes and timing. Encryption doesn't hide that metadata or guarantee delivery. A relay can withhold valid batches; the current protocol does not detect all resulting forks or selective withholding. Read the documented security limits when evaluating whether it fits your application.
| Component | Role |
|---|---|
| prism-sync-core | Schema, change tracking, merge, storage, device pairing, key lifecycle, and relay client |
| prism-sync-crypto | Cryptographic primitives without app or sync state |
| prism-sync-ffi | Rust API exposed through flutter_rust_bridge |
| prism-sync-relay | Axum/SQLite relay with WebSocket notifications; runs without the app or core engine |
| Dart packages | Generated bindings, Drift adapter, and Flutter storage/provider integration |
| prism-sync-bench | Local relay benchmarks |
Prism is currently the only app we know of using prism-sync. If you'd like to use it in another app, open an issue. We're happy to work with you one-on-one and improve the library for other uses.
Rust consumers start with PrismSync in prism-sync-core. You supply the entity schema, storage integration, secure key storage, and relay configuration. The engine's sync_now() operation pulls, merges, and pushes changes; your app is responsible for applying them to its own data model and updating its interface. See the core guide for the module map and storage interfaces.
Flutter consumers use the three packages under dart/packages/: prism_sync, prism_sync_drift, and prism_sync_flutter. Keep them on the same Git revision. Prism's dependency declarations and local development guide show both pinned Git dependencies and path overrides.
You need Rust 1.88 or newer, Cargo, Git, and a native C/C++ build toolchain. The workspace uses Rust 2021.
git clone https://github.com/prismplural/prism-sync.git
cd prism-sync
cargo build --workspace --locked
cargo test --workspace --lockedDart/Flutter setup, test lanes, code generation, and compatibility checks are in CONTRIBUTING.md.
Bug reports, reproducible sync failures, protocol questions, and documentation fixes are welcome in the issue tracker. Discuss protocol, cryptography, or pairing changes before implementing them; we need to consider devices already running older versions.
Report vulnerabilities privately through SECURITY.md. Don't attach real keys, tokens, recovery phrases, or user records to public reports.
We use local and hosted AI tools extensively in development. AI-assisted contributions are welcome under our AI policy, which covers scope, repository conventions, verification, and communication.
Dual-licensed under MIT or Apache 2.0, at your option. Contributions are subject to the existing CLA; see Contributing for the sign-off requirement.