sqlite-sync-core 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlite_sync_core-0.2.0/LICENSE +46 -0
- sqlite_sync_core-0.2.0/PKG-INFO +379 -0
- sqlite_sync_core-0.2.0/README.md +341 -0
- sqlite_sync_core-0.2.0/pyproject.toml +66 -0
- sqlite_sync_core-0.2.0/setup.cfg +4 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/__init__.py +59 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/audit/__init__.py +19 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/audit/import_log.py +159 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/bundle/__init__.py +26 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/bundle/format.py +97 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/bundle/generate.py +200 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/bundle/validate.py +205 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/capture/__init__.py +17 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/capture/change_capture.py +90 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/config.py +48 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/crash_safety.py +204 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/db/__init__.py +38 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/db/connection.py +164 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/db/migrations.py +232 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/db/schema.py +183 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/db/triggers.py +362 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/engine.py +541 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/errors.py +176 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/import_apply/__init__.py +43 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/import_apply/apply.py +185 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/import_apply/conflict.py +223 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/import_apply/dedup.py +67 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/import_apply/ordering.py +70 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/invariants.py +193 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/log/__init__.py +47 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/log/operations.py +350 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/log/vector_clock.py +218 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/log_compaction.py +277 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/metrics.py +716 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/network/client.py +79 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/network/peer_discovery.py +498 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/network/protocol.py +22 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/network/server.py +43 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/resolution/__init__.py +215 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/schema_evolution.py +242 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/security.py +758 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/server/__init__.py +10 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/server/auth.py +770 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/server/http_server.py +720 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/sync_loop.py +206 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/transport/__init__.py +15 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/transport/base.py +85 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/transport/http_transport.py +151 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/transport/websocket_transport.py +209 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/utils/__init__.py +4 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/utils/hashing.py +114 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/utils/msgpack_codec.py +158 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync/utils/uuid7.py +117 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync_core.egg-info/PKG-INFO +379 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync_core.egg-info/SOURCES.txt +61 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync_core.egg-info/dependency_links.txt +1 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync_core.egg-info/requires.txt +18 -0
- sqlite_sync_core-0.2.0/src/sqlite_sync_core.egg-info/top_level.txt +1 -0
- sqlite_sync_core-0.2.0/tests/test_conflicts.py +307 -0
- sqlite_sync_core-0.2.0/tests/test_determinism.py +224 -0
- sqlite_sync_core-0.2.0/tests/test_idempotency.py +189 -0
- sqlite_sync_core-0.2.0/tests/test_invariants.py +171 -0
- sqlite_sync_core-0.2.0/tests/test_minimal.py +3 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# SQLite Sync Core License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 VisionQuantech, India
|
|
4
|
+
|
|
5
|
+
## Dual License
|
|
6
|
+
|
|
7
|
+
This software is available under two license options:
|
|
8
|
+
|
|
9
|
+
### 1. Open Source License (AGPL-3.0)
|
|
10
|
+
|
|
11
|
+
For **non-commercial** and **open-source projects**, this software is licensed under the [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.html).
|
|
12
|
+
|
|
13
|
+
You may use, modify, and distribute this software freely, provided that:
|
|
14
|
+
|
|
15
|
+
- Your project is also open source under a compatible license
|
|
16
|
+
- You include this license notice
|
|
17
|
+
- Any modifications are also open sourced under AGPL-3.0
|
|
18
|
+
- Network use counts as distribution (SaaS must open source)
|
|
19
|
+
|
|
20
|
+
### 2. Commercial License
|
|
21
|
+
|
|
22
|
+
For **commercial use**, **proprietary software**, or **closed-source projects**, you must purchase a commercial license.
|
|
23
|
+
|
|
24
|
+
**Commercial use includes:**
|
|
25
|
+
|
|
26
|
+
- Using this library in proprietary/closed-source software
|
|
27
|
+
- Using this library in SaaS products without open-sourcing
|
|
28
|
+
- Any use where you cannot comply with AGPL-3.0 terms
|
|
29
|
+
|
|
30
|
+
**Contact for licensing:**
|
|
31
|
+
|
|
32
|
+
- Email: <shivaysinghrajput@proton.me>
|
|
33
|
+
- Email: <shivaysinghrajput@outlook.com>
|
|
34
|
+
- Email: <vbs.visionquanteh@proton.me>
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
**AGPL-3.0 Summary:**
|
|
39
|
+
|
|
40
|
+
- ✅ Free for personal projects
|
|
41
|
+
- ✅ Free for open-source projects (with AGPL-3.0 compatible license)
|
|
42
|
+
- ✅ Free for educational/research use
|
|
43
|
+
- ❌ NOT free for closed-source commercial products
|
|
44
|
+
- ❌ NOT free for proprietary SaaS without open-sourcing
|
|
45
|
+
|
|
46
|
+
For the full AGPL-3.0 license text, see: <https://www.gnu.org/licenses/agpl-3.0.html>
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sqlite-sync-core
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Universal SQLite Synchronization Core - A dependency-grade, local-first, offline-first SQLite synchronization primitive
|
|
5
|
+
License: AGPL-3.0
|
|
6
|
+
Project-URL: Homepage, https://github.com/shivay00001/sqlite-sync-core
|
|
7
|
+
Project-URL: Documentation, https://github.com/shivay00001/sqlite-sync-core#readme
|
|
8
|
+
Project-URL: Repository, https://github.com/shivay00001/sqlite-sync-core.git
|
|
9
|
+
Project-URL: Issues, https://github.com/shivay00001/sqlite-sync-core/issues
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Database
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: msgpack>=1.0.0
|
|
24
|
+
Requires-Dist: websockets>=12.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
29
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
30
|
+
Provides-Extra: server
|
|
31
|
+
Requires-Dist: flask>=3.0.0; extra == "server"
|
|
32
|
+
Provides-Extra: crypto
|
|
33
|
+
Requires-Dist: cryptography>=41.0.0; extra == "crypto"
|
|
34
|
+
Provides-Extra: all
|
|
35
|
+
Requires-Dist: flask>=3.0.0; extra == "all"
|
|
36
|
+
Requires-Dist: cryptography>=41.0.0; extra == "all"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# sqlite-sync-core
|
|
40
|
+
|
|
41
|
+
[](https://www.python.org/downloads/)
|
|
42
|
+
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
43
|
+
[](https://pypi.org/project/sqlite-sync-core/)
|
|
44
|
+
[](https://github.com/shivay00001/sqlite-sync-core)
|
|
45
|
+
|
|
46
|
+
**A deterministic, infrastructure-free, local-first SQLite synchronization engine designed for offline-critical and privacy-sensitive systems.**
|
|
47
|
+
|
|
48
|
+
Built by [VisionQuantech](https://github.com/shivay00001), India 🇮🇳
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Why sqlite-sync-core?
|
|
53
|
+
|
|
54
|
+
| Feature | sqlite-sync-core | Firebase | Supabase |
|
|
55
|
+
|---------|------------------|----------|----------|
|
|
56
|
+
| **No server required** | ✅ | ❌ | ❌ |
|
|
57
|
+
| **Works offline** | ✅ | ⚠️ Limited | ⚠️ Limited |
|
|
58
|
+
| **Transport agnostic** | ✅ USB, Email, HTTP, WS | Proprietary | WebSocket only |
|
|
59
|
+
| **Explicit conflicts** | ✅ Never auto-overwrites | ❌ LWW | ❌ LWW |
|
|
60
|
+
| **Deterministic replay** | ✅ Git-like correctness | ❌ | ❌ |
|
|
61
|
+
| **No telemetry** | ✅ | ❌ | ❌ |
|
|
62
|
+
| **Self-hosted** | ✅ | ❌ | ✅ |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Basic installation
|
|
70
|
+
pip install sqlite-sync-core
|
|
71
|
+
|
|
72
|
+
# With HTTP server support
|
|
73
|
+
pip install sqlite-sync-core[server]
|
|
74
|
+
|
|
75
|
+
# With encryption support
|
|
76
|
+
pip install sqlite-sync-core[crypto]
|
|
77
|
+
|
|
78
|
+
# Everything
|
|
79
|
+
pip install sqlite-sync-core[all]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
### Initialize a Sync-Enabled Database
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from sqlite_sync import SyncEngine
|
|
90
|
+
|
|
91
|
+
engine = SyncEngine("my_app.db")
|
|
92
|
+
device_id = engine.initialize()
|
|
93
|
+
|
|
94
|
+
# Create your table
|
|
95
|
+
engine.connection.execute("""
|
|
96
|
+
CREATE TABLE todos (
|
|
97
|
+
id INTEGER PRIMARY KEY,
|
|
98
|
+
title TEXT NOT NULL,
|
|
99
|
+
done INTEGER DEFAULT 0
|
|
100
|
+
)
|
|
101
|
+
""")
|
|
102
|
+
|
|
103
|
+
# Enable sync for this table
|
|
104
|
+
engine.enable_sync_for_table("todos")
|
|
105
|
+
|
|
106
|
+
# All changes are now automatically captured!
|
|
107
|
+
engine.connection.execute("INSERT INTO todos (title) VALUES ('Buy milk')")
|
|
108
|
+
engine.connection.commit()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Sync via File Transfer (USB, Email, Cloud Drive)
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
# Device A: Generate bundle
|
|
115
|
+
bundle_path = engine_a.generate_bundle(
|
|
116
|
+
peer_device_id=device_b_id,
|
|
117
|
+
output_path="sync_bundle.db"
|
|
118
|
+
)
|
|
119
|
+
# Send bundle_path via any method: USB, email, Dropbox, etc.
|
|
120
|
+
|
|
121
|
+
# Device B: Import bundle
|
|
122
|
+
result = engine_b.import_bundle("sync_bundle.db")
|
|
123
|
+
print(f"Applied: {result.applied_count}, Conflicts: {result.conflict_count}")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Sync via HTTP (Real-time)
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from sqlite_sync.transport import HTTPTransport
|
|
130
|
+
from sqlite_sync.sync_loop import SyncLoop, SyncLoopConfig
|
|
131
|
+
|
|
132
|
+
# Create transport
|
|
133
|
+
transport = HTTPTransport(
|
|
134
|
+
base_url="http://localhost:8080",
|
|
135
|
+
device_id=engine.device_id
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Create background sync loop
|
|
139
|
+
sync_loop = SyncLoop(
|
|
140
|
+
engine=engine,
|
|
141
|
+
transport=transport,
|
|
142
|
+
config=SyncLoopConfig(interval_seconds=30)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
await sync_loop.start() # Syncs automatically in background
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Sync via WebSocket (Real-time Bidirectional)
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from sqlite_sync.transport import WebSocketTransport
|
|
152
|
+
|
|
153
|
+
transport = WebSocketTransport(
|
|
154
|
+
url="ws://localhost:8765",
|
|
155
|
+
device_id=engine.device_id,
|
|
156
|
+
on_operation_received=lambda op: print(f"Received: {op.op_type}")
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
await transport.connect()
|
|
160
|
+
await transport.send_operations(engine.get_new_operations())
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Conflict Resolution
|
|
166
|
+
|
|
167
|
+
sqlite-sync-core detects conflicts but gives YOU control over resolution.
|
|
168
|
+
|
|
169
|
+
### Built-in Strategies
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from sqlite_sync.resolution import ResolutionStrategy, get_resolver
|
|
173
|
+
|
|
174
|
+
# Last-Write-Wins (simple but may lose data)
|
|
175
|
+
resolver = get_resolver(ResolutionStrategy.LAST_WRITE_WINS)
|
|
176
|
+
|
|
177
|
+
# Field-level merge (preserves non-conflicting fields)
|
|
178
|
+
resolver = get_resolver(ResolutionStrategy.FIELD_MERGE, prefer_local=True)
|
|
179
|
+
|
|
180
|
+
# Manual (keep for user review)
|
|
181
|
+
resolver = get_resolver(ResolutionStrategy.MANUAL)
|
|
182
|
+
|
|
183
|
+
# Custom (your business logic)
|
|
184
|
+
def my_resolver(context):
|
|
185
|
+
# Your logic here
|
|
186
|
+
return ResolutionResult(resolved=True, winning_op=context.local_op, ...)
|
|
187
|
+
|
|
188
|
+
resolver = get_resolver(ResolutionStrategy.CUSTOM, resolver_fn=my_resolver)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Handle Conflicts
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
conflicts = engine.get_unresolved_conflicts()
|
|
195
|
+
for conflict in conflicts:
|
|
196
|
+
print(f"Conflict on {conflict.table_name}, row {conflict.row_pk.hex()}")
|
|
197
|
+
print(f"Local op: {conflict.local_op_id.hex()}")
|
|
198
|
+
print(f"Remote op: {conflict.remote_op_id.hex()}")
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Advanced Features
|
|
204
|
+
|
|
205
|
+
### Log Compaction
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from sqlite_sync.log_compaction import LogCompactor
|
|
209
|
+
|
|
210
|
+
compactor = LogCompactor(engine.connection)
|
|
211
|
+
|
|
212
|
+
# Create snapshot for new devices
|
|
213
|
+
snapshot = compactor.create_snapshot()
|
|
214
|
+
|
|
215
|
+
# Prune old operations
|
|
216
|
+
compactor.prune_acknowledged_ops(safe_op_id)
|
|
217
|
+
|
|
218
|
+
# Full compaction
|
|
219
|
+
result = compactor.compact_log()
|
|
220
|
+
print(f"Removed {result.ops_removed} operations")
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Schema Evolution
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from sqlite_sync.schema_evolution import SchemaManager
|
|
227
|
+
|
|
228
|
+
schema = SchemaManager(engine.connection)
|
|
229
|
+
|
|
230
|
+
# Safe column addition (syncs across devices)
|
|
231
|
+
migration = schema.add_column(
|
|
232
|
+
table_name="todos",
|
|
233
|
+
column_name="priority",
|
|
234
|
+
column_type="INTEGER",
|
|
235
|
+
default_value=1
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Check compatibility
|
|
239
|
+
if schema.check_compatibility(remote_version=1):
|
|
240
|
+
print("Compatible!")
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Security
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
from sqlite_sync.security import SecurityManager
|
|
247
|
+
|
|
248
|
+
security = SecurityManager(
|
|
249
|
+
device_id=engine.device_id,
|
|
250
|
+
signing_key=my_secret_key
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Sign bundles
|
|
254
|
+
signed = security.sign_bundle(bundle_data)
|
|
255
|
+
|
|
256
|
+
# Encrypt bundles (requires cryptography package)
|
|
257
|
+
encrypted = security.encrypt_bundle(bundle_data, password="secret")
|
|
258
|
+
decrypted = security.decrypt_bundle(encrypted, password="secret")
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Crash Safety
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
from sqlite_sync.crash_safety import CrashSafeExecutor
|
|
265
|
+
|
|
266
|
+
executor = CrashSafeExecutor(engine.connection)
|
|
267
|
+
|
|
268
|
+
# Resume after crash
|
|
269
|
+
checkpoint = executor.get_incomplete_checkpoint()
|
|
270
|
+
if checkpoint:
|
|
271
|
+
print(f"Resuming from {checkpoint.last_applied_op_id.hex()}")
|
|
272
|
+
|
|
273
|
+
# Atomic operations
|
|
274
|
+
with executor.atomic_operation():
|
|
275
|
+
engine.apply_operation(op1)
|
|
276
|
+
engine.apply_operation(op2) # Both or neither
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Run the Sync Server
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Start HTTP sync server
|
|
285
|
+
python -m sqlite_sync.server.http_server
|
|
286
|
+
|
|
287
|
+
# Or in code
|
|
288
|
+
from sqlite_sync.server import run_server
|
|
289
|
+
run_server(host="0.0.0.0", port=8080)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Examples
|
|
295
|
+
|
|
296
|
+
See the `examples/` directory:
|
|
297
|
+
|
|
298
|
+
| Example | Description |
|
|
299
|
+
|---------|-------------|
|
|
300
|
+
| `basic_usage.py` | Simple CLI sync demo |
|
|
301
|
+
| `desktop_demo.py` | Local sync between databases |
|
|
302
|
+
| `http_sync_demo.py` | Client/server network sync |
|
|
303
|
+
| `network_sync.py` | WebSocket real-time sync |
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Core Invariants
|
|
308
|
+
|
|
309
|
+
| # | Invariant | Description |
|
|
310
|
+
|---|-----------|-------------|
|
|
311
|
+
| 1 | **Append-only** | Operations never modified |
|
|
312
|
+
| 2 | **Causal consistency** | Vector clocks ensure ordering |
|
|
313
|
+
| 3 | **Deterministic** | Same ops = same result everywhere |
|
|
314
|
+
| 4 | **Explicit conflicts** | Never silently overwrites |
|
|
315
|
+
| 5 | **Idempotent** | Import same bundle N times = same result |
|
|
316
|
+
| 6 | **Transport agnostic** | Bundles work anywhere |
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Architecture
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
┌─────────────────────────────────────────────────────────┐
|
|
324
|
+
│ Your Application │
|
|
325
|
+
├─────────────────────────────────────────────────────────┤
|
|
326
|
+
│ SyncEngine │
|
|
327
|
+
│ ┌─────────┐ ┌─────────────┐ ┌──────────────────────┐ │
|
|
328
|
+
│ │ Capture │ │ Resolution │ │ Schema Evolution │ │
|
|
329
|
+
│ └─────────┘ └─────────────┘ └──────────────────────┘ │
|
|
330
|
+
│ ┌─────────┐ ┌─────────────┐ ┌──────────────────────┐ │
|
|
331
|
+
│ │ Bundle │ │ Compaction │ │ Security │ │
|
|
332
|
+
│ └─────────┘ └─────────────┘ └──────────────────────┘ │
|
|
333
|
+
├─────────────────────────────────────────────────────────┤
|
|
334
|
+
│ Transport Layer (Pluggable) │
|
|
335
|
+
│ ┌──────┐ ┌───────────┐ ┌───────┐ ┌───────────────┐ │
|
|
336
|
+
│ │ HTTP │ │ WebSocket │ │ File │ │ Custom/P2P │ │
|
|
337
|
+
│ └──────┘ └───────────┘ └───────┘ └───────────────┘ │
|
|
338
|
+
└─────────────────────────────────────────────────────────┘
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Use Cases
|
|
344
|
+
|
|
345
|
+
- **Offline-first mobile apps** - Works without internet
|
|
346
|
+
- **Air-gapped systems** - Defense, government, NGOs
|
|
347
|
+
- **Privacy-sensitive applications** - Medical, legal, finance
|
|
348
|
+
- **Field operations** - Works with USB/SD card transfer
|
|
349
|
+
- **Embedded systems** - IoT with intermittent connectivity
|
|
350
|
+
- **Multi-device personal apps** - Notes, todos, journals
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
**Dual License:**
|
|
357
|
+
|
|
358
|
+
| Use Case | License | Cost |
|
|
359
|
+
|----------|---------|------|
|
|
360
|
+
| Personal/Open Source | AGPL-3.0 | **Free** |
|
|
361
|
+
| Commercial/Proprietary | Commercial | **Paid** |
|
|
362
|
+
|
|
363
|
+
Contact for commercial licensing:
|
|
364
|
+
|
|
365
|
+
- <shivaysinghrajput@proton.me>
|
|
366
|
+
- <shivaysinghrajput@outlook.com>
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Contributing
|
|
371
|
+
|
|
372
|
+
1. Fork the repository
|
|
373
|
+
2. Create a feature branch
|
|
374
|
+
3. Run tests: `pytest tests/ -v`
|
|
375
|
+
4. Submit a pull request
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
**Built with ❤️ for offline-first applications**
|