Skip to main content

max / synckit-client

3.0 KB · 79 lines History Blame Raw
1 # synckit-client
2
3 End-to-end encrypted cloud sync SDK for Rust applications, built for the [MNW SyncKit]https://makenot.work server.
4
5 All row data and binary blobs are encrypted client-side (XChaCha20-Poly1305) before leaving the device. The server only ever stores ciphertext.
6
7 ## Features
8
9 - **E2E encryption** -- XChaCha20-Poly1305 with Argon2id key derivation (64 MB, 3 iterations)
10 - **OS keychain integration** -- master key cached in macOS Keychain, Linux secret-service, or Windows Credential Manager
11 - **Blob encryption** -- binary files encrypted with fixed 40-byte overhead (no base64 expansion)
12 - **Retry with backoff** -- transient failures (network, 5xx, 429) retried up to 3 times with exponential delay
13 - **OAuth2 PKCE** -- browser-based auth flow alongside email/password
14 - **Token expiry detection** -- client-side JWT check with 30-second buffer
15
16 ## Quick Start
17
18 ```rust
19 use synckit_client::{SyncKitClient, SyncKitConfig, ChangeEntry, ChangeOp};
20 use chrono::Utc;
21
22 let client = SyncKitClient::new(SyncKitConfig {
23 server_url: "https://makenot.work".into(),
24 api_key: "your-api-key".into(),
25 });
26
27 // Authenticate
28 let (user_id, app_id) = client.authenticate("user@example.com", "password").await?;
29
30 // Set up encryption (first device)
31 client.setup_encryption_new("password").await?;
32
33 // Register this device
34 let device = client.register_device("MacBook Pro", "macos").await?;
35
36 // Push encrypted data
37 let cursor = client.push(device.id, vec![
38 ChangeEntry {
39 table: "tasks".into(),
40 op: ChangeOp::Insert,
41 row_id: uuid::Uuid::new_v4().to_string(),
42 timestamp: Utc::now(),
43 data: Some(serde_json::json!({"title": "Buy milk"})),
44 },
45 ]).await?;
46
47 // Pull and auto-decrypt
48 let (changes, cursor, has_more) = client.pull(device.id, 0).await?;
49 ```
50
51 ## Crate Structure
52
53 | File | Role |
54 |------|------|
55 | `lib.rs` | Crate root, re-exports, doc example |
56 | `client.rs` | `SyncKitClient` -- HTTP methods, retry logic, token expiry detection |
57 | `crypto.rs` | Key derivation (Argon2id), key wrapping, per-entry and per-blob encrypt/decrypt |
58 | `error.rs` | `SyncKitError` enum (10 variants: HTTP, server, JSON, crypto, keychain, auth) |
59 | `keystore.rs` | OS keychain read/write/delete, feature-gated with no-op stubs |
60 | `types.rs` | Wire protocol types (`ChangeEntry`, `ChangeOp`, `Device`, `SyncStatus`) |
61
62 ## Feature Flags
63
64 | Flag | Default | Description |
65 |------|---------|-------------|
66 | `keychain` | on | OS keychain storage via the `keyring` crate. Disable with `default-features = false` for headless/CI environments. |
67
68 ## Security Properties
69
70 - **Server-zero-knowledge** -- the server never receives the plaintext master key or user data
71 - **Key zeroization** -- volatile writes clear the master key from memory on drop
72 - **Random salt per wrap** -- re-wrapping with the same password produces a different envelope
73 - **Minimum ciphertext validation** -- decryption rejects inputs shorter than 40 bytes (24-byte nonce + 16-byte tag)
74 - **No key material in logs** -- tracing events never include key bytes or ciphertext
75
76 ## License
77
78 PolyForm Noncommercial 1.0.0
79