A todo and personal organisation app
Sync API#
Base URL: /api/sync
All endpoints require authentication.
Overview#
The sync protocol uses checksums to efficiently synchronize noots between client and server.
Endpoints#
POST /#
Full sync operation.
Request:
{
"noots": [
{
"id": "uuid",
"version": 2,
"checksum": "sha256...",
"activities": [...]
}
],
"knownNoots": [
{"id": "uuid-1", "checksum": "sha256..."},
{"id": "uuid-2", "checksum": "sha256..."}
]
}
Response (200):
{
"serverNoots": [
{
"id": "uuid",
"version": 3,
"checksum": "sha256...",
"activities": [...]
}
],
"deletedIds": ["uuid-3", "uuid-4"],
"accepted": ["uuid-1"],
"conflicts": []
}
Request Fields#
| Field | Description |
|---|---|
| noots | Local noots with pending changes |
| knownNoots | Checksums of all local noots |
Response Fields#
| Field | Description |
|---|---|
| serverNoots | Noots that differ from client |
| deletedIds | Noots deleted on server |
| accepted | Client noots accepted by server |
| conflicts | Noots with version conflicts |
GET /checksums#
Get server checksums for comparison.
Response (200):
{
"noots": [
{"id": "uuid-1", "checksum": "sha256..."},
{"id": "uuid-2", "checksum": "sha256..."}
]
}
POST /fetch#
Fetch specific noots by ID.
Request:
{
"nootIds": ["uuid-1", "uuid-2", "uuid-3"]
}
Response (200):
{
"noots": [
{
"id": "uuid-1",
"version": 1,
"activities": [...]
}
]
}
Sync Algorithm#
Client-Side#
- Get local checksums
- Send to
/syncwith pending changes - Apply server changes
- Mark conflicts for resolution
// Simplified sync flow
final localChecksums = nootPool.getChecksums();
final pendingNoots = nootPool.syncQueue
.map((e) => e.noot.toJson())
.toList();
final result = await apiClient.sync(
noots: pendingNoots,
knownNoots: localChecksums,
);
// Apply server changes
nootPool.mergeFromServer(
result.serverNoots,
result.deletedIds,
);
// Clear synced items
nootPool.clearSyncQueue();
Conflict Resolution#
Strategy: Last-Write-Wins
- Higher
versionnumber takes precedence - If versions equal, server wins
- Conflicts array returned for manual resolution
Checksum Format#
SHA-256 hash of normalized JSON:
String generateChecksum(Noot noot) {
final normalized = jsonEncode({
'activities': noot.activities.map((a) => a.toJson()).toList(),
});
return 'sha256:${sha256.convert(utf8.encode(normalized))}';
}