max / synckit
| 1 | //! SyncKit Client SDK, end-to-end encrypted cloud sync. |
| 2 | //! |
| 3 | //! All row data is encrypted client-side before leaving the device. |
| 4 | //! The server only ever sees ciphertext. |
| 5 | //! |
| 6 | //! # Design |
| 7 | //! |
| 8 | //! The `SyncStore` engine, the wire/crypto model, and the per-app manifests are |
| 9 | //! in `docs/architecture.md`. The strategy, competitive positioning, and HLC |
| 10 | //! conflict-resolution design live in the maintainer wiki. |
| 11 | //! <!-- wiki: synckit-overview --> |
| 12 | //! |
| 13 | //! # Consumer requirement: `serde_json` insertion order must stay OFF |
| 14 | //! |
| 15 | //! Conflict resolution breaks an exact-HLC tie on the canonical bytes of the row |
| 16 | //! payload (see [`conflict::resolve_field_merge`]). Those bytes are identical on |
| 17 | //! every device **only** while `serde_json` serializes map keys in sorted order, |
| 18 | //! i.e. while its `preserve_order` feature is NOT enabled anywhere in your final |
| 19 | //! dependency tree. Cargo features are additive and global, so a *different* crate |
| 20 | //! in your build turning on `serde_json/preserve_order` would silently make two |
| 21 | //! devices compute different tiebreak bytes for the same value and diverge, with |
| 22 | //! no error. There is no compile-time guard the SDK can enforce for you: if you |
| 23 | //! depend on `serde_json` with `preserve_order`, do not use SyncKit conflict |
| 24 | //! resolution. The `canonical_payload_sorts_map_keys` unit test pins this |
| 25 | //! invariant for this crate's own build. |
| 26 | //! |
| 27 | //! # Quick start |
| 28 | //! |
| 29 | //! ```no_run |
| 30 | //! use synckit_client::{SyncKitClient, SyncKitConfig, ChangeEntry, ChangeOp}; |
| 31 | //! use chrono::Utc; |
| 32 | //! |
| 33 | //! # async fn example() -> synckit_client::Result<()> { |
| 34 | //! let client = SyncKitClient::new(SyncKitConfig { |
| 35 | //! server_url: "https://makenot.work".into(), |
| 36 | //! api_key: "your-api-key".into(), |
| 37 | //! }); |
| 38 | //! |
| 39 | //! // Authenticate (the third arg is the developer-defined SDK key for |
| 40 | //! // billing attribution, typically one per workspace/org/end-user). |
| 41 | //! let (user_id, app_id) = client.authenticate("user@example.com", "password", "workspace-42").await?; |
| 42 | //! |
| 43 | //! // Set up encryption (first device) |
| 44 | //! client.setup_encryption_new("password").await?; |
| 45 | //! |
| 46 | //! // Register this device |
| 47 | //! let device = client.register_device("MacBook Pro", "macos").await?; |
| 48 | //! |
| 49 | //! // Push encrypted data |
| 50 | //! let cursor = client.push(device.id, vec![ |
| 51 | //! ChangeEntry { |
| 52 | //! table: "tasks".into(), |
| 53 | //! op: ChangeOp::Insert, |
| 54 | //! row_id: uuid::Uuid::new_v4().to_string(), |
| 55 | //! timestamp: Utc::now(), |
| 56 | //! // Minted from the app's persistent clock in real code (see `Hlc::tick`); |
| 57 | //! // the nil-node floor stands in here so the example stays self-contained. |
| 58 | //! hlc: synckit_client::Hlc::zero(device.id), |
| 59 | //! data: Some(serde_json::json!({"title": "Buy milk"})), |
| 60 | //! extra: serde_json::Map::default(), |
| 61 | //! }, |
| 62 | //! ]).await?; |
| 63 | //! |
| 64 | //! // Pull and auto-decrypt |
| 65 | //! let (changes, cursor, has_more) = client.pull(device.id, 0).await?; |
| 66 | //! # Ok(()) |
| 67 | //! # } |
| 68 | //! ``` |
| 69 | |
| 70 | |
| 71 | |
| 72 | |
| 73 | |
| 74 | |
| 75 | |
| 76 | |
| 77 | |
| 78 | |
| 79 | |
| 80 | |
| 81 | |
| 82 | // Re-exports for convenience |
| 83 | pub use |
| 84 | AccountInfo, AppPricing, BillingInterval, Cents, CheckoutResponse, PriceQuote, |
| 85 | SubscriptionStatus, |
| 86 | ; |
| 87 | pub use ; |
| 88 | pub use |
| 89 | SecretToken, SessionInfo, SyncKitClient, SyncKitConfig, SyncNotifyStream, validate_api_key, |
| 90 | ; |
| 91 | pub use |
| 92 | CleanChanges, ConflictPair, ConflictResolver, Resolution, detect_conflicts, |
| 93 | resolve_field_merge, resolve_lww, |
| 94 | ; |
| 95 | pub use ; |
| 96 | pub use |
| 97 | IdentityKeypair, IdentityPublicKey, generate_group_key, open_gck_grant, seal_gck_to_member, |
| 98 | ; |
| 99 | pub use ; |
| 100 | pub use ; |
| 101 | |
| 102 | pub use |
| 103 | BlobPolicy, BlobRef, ConflictStrategy, DbSource, DeleteMode, RowIdScheme, SyncConfig, SyncMode, |
| 104 | SyncObserver, SyncOutcome, SyncSchema, SyncState, SyncStore, SyncTable, |
| 105 | ; |
| 106 | pub use ; |
| 107 |