mirror of
https://github.com/logseq/logseq.git
synced 2026-05-14 16:02:31 +00:00
7.4 KiB
7.4 KiB
DB-Sync Client-Server Protocol
Transport
- WebSocket
ws(s)to/sync/:graph-id. - Client builds URL from config and appends
?token=...when available. - Encoding: JSON objects;
txpayloads are Transit strings. - Note: keep this document in sync with the current implementation.
Client -> Server
{"type":"hello","client":"<repo-id>"}- Initial handshake from client.
{"type":"presence","editing-block-uuid":"<uuid|null>"}- Update current editing block for presence (omit or null to clear).
{"type":"pull","since":<t>}- Request txs after
since(defaults to 0).
- Request txs after
{"type":"tx/batch","t-before":<t>,"txs":[{"tx":"<tx-transit>","tx-id":"<uuid?>","outliner-op":"<keyword?>"}, ...]}- Upload a batch of txs based on
t-before(required). tx-idis optional but recommended for per-entry ack/reject mapping.
- Upload a batch of txs based on
{"type":"ping"}- Optional keepalive; server replies
pong.
- Optional keepalive; server replies
Server -> Client
{"type":"hello","t":<t>,"checksum":"<hex>"}- Server hello with current t and entity checksum.
{"type":"online-users","online-users":[{"user-id":"...","email":"...","username":"...","name":"..."}...]}- Presence update
- Optional
editing-block-uuidindicates the block the user is editing.
{"type":"pull/ok","t":<t>,"checksum":"<hex>","txs":[{"t":<t>,"tx":"<tx-transit>","outliner-op":"<keyword?>"}...]}- Pull response with txs and post-apply entity checksum.
{"type":"tx/batch/ok","t":<t>,"checksum":"<hex>"}- Batch accepted; server advanced to t and returns the resulting entity checksum.
{"type":"changed","t":<t>}- Broadcast once after a handled
tx/batchthat advanced server state (tincreased); client should pull.
- Broadcast once after a handled
{"type":"tx/reject","reason":"stale","t":<t>}- Client tx is based on stale t.
{"type":"tx/reject","reason":"db transact failed","t":<t>,"success-tx-ids":["<uuid>",...],"failed-tx-id":"<uuid>"}- Server-side transact/validation failed for one tx entry in the batch.
success-tx-idsare entries already applied before the failure.failed-tx-idis the entry that failed.- Legacy servers may return
datawith rejected tx payload for debugging.
{"type":"tx/reject","reason":"empty tx data"|"invalid tx"|"invalid t-before"|"snapshot upload in progress"}- Invalid batch.
{"type":"pong"}- Keepalive response.
{"type":"error","message":"..."}- Invalid/unknown message. Current messages:
"unknown type","invalid request","server error","invalid since".
- Invalid/unknown message. Current messages:
HTTP API
- Auth: Bearer token via
Authorization: Bearer <token>or?token=.... - JSON body/response unless noted.
- Auth required for
/graphs,/sync/:graph-id/*, and/assets/*. Expect401(unauthorized) or403(forbidden) on access failure.
Worker Health
GET /health- Worker health check. Response:
{"ok":true}.
- Worker health check. Response:
Graphs (index DO)
GET /graphs- List graphs the user owns. Response:
{"graphs":[{graph-id, graph-name, schema-version?, graph-ready-for-use?, created-at, updated-at}...]}.
- List graphs the user owns. Response:
POST /graphs- Create graph. Body:
{"graph-name":"...","schema-version":"<major>"}(schema-version optional). Response:{"graph-id":"...","graph-ready-for-use?":false}. graph-ready-for-use?is persisted in the D1graphsrow. Existing graphs default totrue; bootstrap uploads flip it tofalseuntil the final snapshot upload request completes.
- Create graph. Body:
GET /graphs/:graph-id/access- Access check. Response:
{"ok":true},401(unauthorized),403(forbidden), or404(not found).
- Access check. Response:
GET /graphs/:graph-id/members- Graph members list. Response:
{"members":[{user-id, graph-id, role, invited-by, created-at, email?, username?}...]}.
- Graph members list. Response:
DELETE /graphs/:graph-id- Delete graph and reset data. Response:
{"graph-id":"...","deleted":true}or400(missing graph id).
- Delete graph and reset data. Response:
E2EE (index DO)
GET /e2ee/user-keys- Fetch current user's RSA key pair. Response:
{"public-key":"<transit>","encrypted-private-key":"<transit>"}or{}when missing.
- Fetch current user's RSA key pair. Response:
POST /e2ee/user-keys- Upsert current user's RSA key pair. Body:
{"public-key":"<transit>","encrypted-private-key":"<transit>","reset-private-key":false?}. - Response mirrors the stored keys:
{"public-key":"<transit>","encrypted-private-key":"<transit>"}.
- Upsert current user's RSA key pair. Body:
GET /e2ee/user-public-key?email=<email>- Fetch a user's RSA public key by email. Response:
{"public-key":"<transit>"}or{}when missing.
- Fetch a user's RSA public key by email. Response:
GET /e2ee/graphs/:graph-id/aes-key- Fetch current user's encrypted graph AES key. Response:
{"encrypted-aes-key":"<transit>"}or{}when missing.
- Fetch current user's encrypted graph AES key. Response:
POST /e2ee/graphs/:graph-id/aes-key- Upsert current user's encrypted graph AES key. Body:
{"encrypted-aes-key":"<transit>"}. - Response:
{"encrypted-aes-key":"<transit>"}.
- Upsert current user's encrypted graph AES key. Body:
POST /e2ee/graphs/:graph-id/grant-access- Manager-only. Upsert encrypted graph AES keys for members.
- Body:
{"target-user-email+encrypted-aes-key-coll":[{"user/email":"<email>","encrypted-aes-key":"<transit>"}...]}. - Response:
{"ok":true,"missing-users":["<email>", ...]?}.
Sync (per-graph DO, via /sync/:graph-id/...)
GET /sync/:graph-id/health- Health check. Response:
{"ok":true}.
- Health check. Response:
GET /sync/:graph-id/pull?since=<t>- Same as WS pull. Response:
{"type":"pull/ok","t":<t>,"checksum":"<hex>","txs":[{"t":<t>,"tx":"<tx-transit>","outliner-op":"<keyword?>"}...]}. - Error response (400):
{"error":"invalid since"}. - Error response (409):
{"error":"graph not ready"}when bootstrap upload/import has not finished.
- Same as WS pull. Response:
POST /sync/:graph-id/tx/batch- Same as WS tx/batch. Body:
{"t-before":<t>,"txs":[{"tx":"<tx-transit>","tx-id":"<uuid?>","outliner-op":"<keyword?>"}, ...]}. - Response:
{"type":"tx/batch/ok","t":<t>,"checksum":"<hex>"}or{"type":"tx/reject","reason":...}. - Error response (400):
{"error":"missing body"|"invalid tx"}. - Error response (409):
{"error":"graph not ready"}when bootstrap upload/import has not finished.
- Same as WS tx/batch. Body:
GET /sync/:graph-id/snapshot/download- Build a snapshot file in R2 and return a download URL.
- Response:
{"ok":true,"key":"<graph-id>/<uuid>.snapshot","url":"<origin>/assets/:graph-id/<uuid>.snapshot","content-encoding":"gzip"}. - Error response (409):
{"error":"graph not ready"}when bootstrap upload/import has not finished. - The snapshot file stored in R2 is a framed Transit stream of sqlite
kvsrows ([addr, content, addresses]), optionally gzip-compressed.
POST /sync/:graph-id/snapshot/upload?reset=true|false- Upload a snapshot stream for bootstrap import. Current upload format remains framed Transit JSON kvs rows, optionally gzip-compressed.
- Request body: binary stream; headers should include
content-type: application/transit+jsonandcontent-encoding: gzipwhen compressed. - Response:
{"ok":true,"count":<n>,"key":"<graph-id>/<uuid>.snapshot"}. - Error response (400):
{"error":"missing body"|"missing graph id"}.
DELETE /sync/:graph-id/admin/reset- Drop/recreate per-graph tables. Response:
{"ok":true}.
- Drop/recreate per-graph tables. Response:
Assets
GET /assets/:graph-id/:asset-uuid.:ext- Download asset (binary response,
content-typeset,x-asset-typeheader included).
- Download asset (binary response,
PUT /assets/:graph-id/:asset-uuid.:ext- Upload asset (binary body). Size limit ~100MB. Response:
{"ok":true}.
- Upload asset (binary body). Size limit ~100MB. Response:
DELETE /assets/:graph-id/:asset-uuid.:ext- Delete asset. Response:
{"ok":true}.
- Delete asset. Response:
- Asset error responses:
{"error":"invalid asset path"}(400),{"error":"not found"}(404),{"error":"asset too large"}(413),{"error":"method not allowed"}(405),{"error":"missing assets bucket"}(500).