Skip to main content

max / makenotwork

3.2 KB · 80 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. The third arg is the developer-defined SDK key for billing
28 // attribution — typically one key per workspace/org/end-user.
29 let (user_id, app_id) = client.authenticate("user@example.com", "password", "workspace-42").await?;
30
31 // Set up encryption (first device)
32 client.setup_encryption_new("password").await?;
33
34 // Register this device
35 let device = client.register_device("MacBook Pro", "macos").await?;
36
37 // Push encrypted data
38 let cursor = client.push(device.id, vec![
39 ChangeEntry {
40 table: "tasks".into(),
41 op: ChangeOp::Insert,
42 row_id: uuid::Uuid::new_v4().to_string(),
43 timestamp: Utc::now(),
44 data: Some(serde_json::json!({"title": "Buy milk"})),
45 },
46 ]).await?;
47
48 // Pull and auto-decrypt
49 let (changes, cursor, has_more) = client.pull(device.id, 0).await?;
50 ```
51
52 ## Crate Structure
53
54 | File | Role |
55 |------|------|
56 | `lib.rs` | Crate root, re-exports, doc example |
57 | `client.rs` | `SyncKitClient` -- HTTP methods, retry logic, token expiry detection |
58 | `crypto.rs` | Key derivation (Argon2id), key wrapping, per-entry and per-blob encrypt/decrypt |
59 | `error.rs` | `SyncKitError` enum (10 variants: HTTP, server, JSON, crypto, keychain, auth) |
60 | `keystore.rs` | OS keychain read/write/delete, feature-gated with no-op stubs |
61 | `types.rs` | Wire protocol types (`ChangeEntry`, `ChangeOp`, `Device`, `SyncStatus`) |
62
63 ## Feature Flags
64
65 | Flag | Default | Description |
66 |------|---------|-------------|
67 | `keychain` | on | OS keychain storage via the `keyring` crate. Disable with `default-features = false` for headless/CI environments. |
68
69 ## Security Properties
70
71 - **Server-zero-knowledge** -- the server never receives the plaintext master key or user data
72 - **Key zeroization** -- volatile writes clear the master key from memory on drop
73 - **Random salt per wrap** -- re-wrapping with the same password produces a different envelope
74 - **Minimum ciphertext validation** -- decryption rejects inputs shorter than 40 bytes (24-byte nonce + 16-byte tag)
75 - **No key material in logs** -- tracing events never include key bytes or ciphertext
76
77 ## License
78
79 PolyForm Noncommercial 1.0.0
80