@xcitedbs/client 0.1.0 → 0.2.0

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.
Files changed (3) hide show
  1. package/llms-full.txt +1158 -0
  2. package/llms.txt +251 -0
  3. package/package.json +4 -2
package/llms-full.txt ADDED
@@ -0,0 +1,1158 @@
1
+ # XciteDB — Complete Reference for AI Assistants
2
+
3
+ > This file contains the full XciteDB documentation and SDK reference, intended to be provided as context to AI assistants for accurate code generation. It covers the product overview, every HTTP API surface, the JavaScript/TypeScript SDK, the Python SDK, and the C++ SDK.
4
+
5
+ ---
6
+
7
+ ## IMPORTANT: Non-Standard Conventions
8
+
9
+ Before reading the full reference, note these critical differences from typical databases:
10
+
11
+ 1. **The default branch is the empty string `""`, not `"main"`.** When no `X-Branch` header is sent (or `context.branch` is omitted/empty), the server operates on the root timeline. A branch named `"main"` may exist as a user-created branch, but it is not required or special.
12
+
13
+ 2. **Identifiers are hierarchical, path-like strings** — e.g. `/us/bills/hr1`, `/manual/v2/chapter3`. They are NOT auto-generated UUIDs. The leading `/` is part of the identifier. Parent/child relationships are derived from the path structure.
14
+
15
+ 3. **Documents are XML or JSON, not arbitrary blobs.** XML documents are the primary document type. JSON documents are a parallel store. Both are fully versioned.
16
+
17
+ 4. **Context (branch + date) travels as HTTP headers** (`X-Branch`, `X-Date`), not URL path segments.
18
+
19
+ 5. **XML documents carry their identifier inside the XML** via a `db:identifier` attribute on the root element.
20
+
21
+ 6. **Authentication has two tiers.** Platform operators use `/api/v1/platform/auth/*`; Application end-users use `/api/v1/app/auth/*`. API keys bypass login for server-to-server use.
22
+
23
+ 7. **The query model is NOT SQL.** Use REST query parameters (`match`, `match_start`, `contains`, `regex`) or the **Unquery** JSON DSL.
24
+
25
+ ---
26
+
27
+ # Part 1: Product Overview
28
+
29
+ # XciteDB: Executive Summary
30
+
31
+ **Audience:** Technical stakeholders (CTOs, architects, senior engineers, and product managers) evaluating XciteDB. This document provides a high-level understanding of XciteDB's purpose, target workloads, architecture, and unique differentiators.
32
+
33
+ ---
34
+
35
+ ## 1. Executive Overview
36
+
37
+ ### 1.1 What is XciteDB?
38
+ XciteDB is an enterprise-grade **Backend-as-a-Service (BaaS)** built on top of a highly optimized **embedded LMDB** database engine. Powered by a high-performance C++17 HTTP/WebSocket API, XciteDB exposes a multi-tenant, secure platform for managing complex, versioned, structured documents.
39
+
40
+ **Both XML and JSON are first-class citizens.** XciteDB can operate as a pure **XML document store**, a pure **JSON document database**, or any combination of the two — including the common pattern of **XML documents enriched with structured JSON metadata**. In every mode, data is deeply shredded into its structural components (elements, attributes, objects, fields, array items), versioned, branched, indexed by path, and fully queryable through the same Unquery analytics engine.
41
+
42
+ ### 1.2 The Problem It Solves
43
+ Standard relational databases and generic NoSQL stores struggle with highly structured, hierarchical, and deeply versioned content. Building collaborative authoring tools, legislative drafting systems, or complex content management platforms usually requires bolting a Git-like versioning layer and document-aware parsers onto an ill-fitting database.
44
+
45
+ XciteDB bridges this gap. It provides **Git-like versioning for structured XML and JSON data** out of the box, delivered as a modern, API-first cloud platform with multi-tenancy, Attribute-Based Access Control (ABAC), and S3-compatible backups.
46
+
47
+ ### 1.3 Target Workloads
48
+ XciteDB shines in domains requiring strict auditability, collaborative authoring, and complex document hierarchies:
49
+ - **Legal and Legislative Drafting:** Tracking amendments, clauses, and exact historical states of laws.
50
+ - **Structured Technical Documentation:** Managing manuals, specifications, and compliance documents where every change must be versioned and attributable.
51
+ - **Collaborative Content Management:** Systems requiring branching, merging, and cooperative locking to prevent editor conflicts.
52
+ - **Application State and Configuration:** Storing deeply structured JSON configuration, feature flags, or workflow state that benefits from versioning and path-level querying.
53
+
54
+ ### 1.4 Unique Differentiators
55
+ - **First-Class XML & JSON:** Both formats are shredded, indexed by path, versioned, branched, and fully queryable through the same engine.
56
+ - **Embedded LMDB Engine:** A memory-mapped, ACID-compliant storage core purpose-built for structured document workloads.
57
+ - **Bare-Metal Speed:** Reads hit memory-mapped pages with no network round-trip. Microsecond-class read latency on warm data.
58
+ - **"Git for Data" Versioning:** Branches, commits, tags, diffs, and time-travel are first-class database primitives.
59
+ - **Unquery DSL:** A purpose-built declarative query language for navigating, filtering, and aggregating data across both XML trees and JSON structures.
60
+ - **Native Office Ingestion:** Directly convert and ingest DOCX, ODF, RTF, and PDF formats into clean, versioned XML documents.
61
+ - **Transparent Tenant Routing:** Scale out horizontally using a coordinator/worker topology without breaking the client API contract.
62
+ - **Optional encryption at rest (per project):** AES-256-GCM protection for document values.
63
+
64
+ ---
65
+
66
+ ## 2. Architecture at a Glance
67
+
68
+ ### 2.1 Logical Layers
69
+ - **Edge:** HTTPS traffic terminates at NGINX, which proxies to the C++ API.
70
+ - **Security & Routing:** Drogon filters enforce rate limits, validate JWT/API keys, and isolate requests to specific tenant projects.
71
+ - **Execution:** Business logic services interact with the `xcitelib` library for ACID-compliant LMDB transactions.
72
+ - **Response:** Data is returned as structured JSON or XML.
73
+
74
+ ### 2.2 Storage & Document Model
75
+ - **LMDB Backed:** Memory-mapped key-value store organized into specialized sub-databases (`nodes`, `ids`, `meta`, `json_docs`).
76
+ - **XML Documents:** Stored natively via pugixml and shredded into elements, attributes, and text nodes. Addressed by hierarchical, path-like identifiers (e.g., `/manual/v1/chapter1`).
77
+ - **JSON Documents:** Stored as standalone structured documents keyed by string. Shredded into objects, fields, and array elements.
78
+ - **JSON Metadata on XML:** JSON metadata can be attached to any XML document or path.
79
+ - **Hierarchical Identifiers:** Both XML and JSON documents are addressed via logical, path-like strings. The engine natively indexes parent/child relationships.
80
+
81
+ ### 2.3 Git-Like Document Versioning
82
+ - **Branches & Commits:** Isolate collaborative edits into branches. Create atomic commits with descriptive messages.
83
+ - **Merge & Cherry-Pick:** Merge branches or cherry-pick specific commits.
84
+ - **Time Travel:** Read historical state at any point in time using the `X-Date` header.
85
+ - **Cooperative Locking:** TTL locks prevent conflicting writes (HTTP 409 on conflict).
86
+
87
+ ### 2.4 Unquery: Declarative Query DSL
88
+ Clients submit structured query documents via `POST /api/v1/unquery`; the engine evaluates them against matching identifiers and returns structured JSON results. Supports hierarchical navigation, XPath queries, JSON path navigation, aggregation, and conditional branching.
89
+
90
+ ---
91
+
92
+ # Part 2: HTTP API Reference
93
+
94
+ # Getting started
95
+
96
+ Welcome to the **XciteDB HTTP API**. This reference describes REST endpoints under the `/api/v1` prefix, plus a few discovery URLs.
97
+
98
+ ## Base URL and versioning
99
+
100
+ - All documented JSON APIs use the prefix **`/api/v1`**.
101
+ - Replace `https://your-server` with your deployment origin (scheme + host + optional port).
102
+
103
+ ## Content types
104
+
105
+ | Usage | Header |
106
+ |--------|--------|
107
+ | JSON request bodies | `Content-Type: application/json` |
108
+ | XML document bodies | `Content-Type: application/xml` or `text/xml` |
109
+ | Multipart uploads | `multipart/form-data` (e.g. document import) |
110
+
111
+ ## Common request headers
112
+
113
+ | Header | When | Description |
114
+ |--------|------|-------------|
115
+ | `Authorization` | Usually required | `Bearer <JWT>` or `Bearer <api_key>` |
116
+ | `X-Project-Id` | Platform console / multi-project JWT | Selects the **tenant project** when the token is not already bound to one tenant |
117
+ | `X-Branch` | Optional | Active branch name for document and versioning operations |
118
+ | `X-Date` | Optional | Point-in-time / revision context (ISO-like string as used by your deployment) |
119
+
120
+ ## Errors
121
+
122
+ Errors are usually JSON objects with `error` or `message` and optional `code` fields.
123
+
124
+ HTTP status codes: **`401`** unauthenticated, **`403`** forbidden by role or policy, **`404`** not found, **`409`** conflict, **`422`** validation, **`423`** encrypted and locked, **`429`** rate limited, **`500`** server error.
125
+
126
+ ## Pagination and limits
127
+
128
+ List endpoints often accept **`limit`** and **`offset`** query parameters.
129
+
130
+ ---
131
+
132
+ # Authentication
133
+
134
+ XciteDB supports several credentials depending on **who** is calling: **platform operators**, **tenant administrators**, **project members** (JWT or API keys), and **end-user app accounts**.
135
+
136
+ ## Bearer tokens
137
+
138
+ ```http
139
+ Authorization: Bearer <token>
140
+ ```
141
+
142
+ ## Platform vs project context
143
+
144
+ | Mode | Typical token | Project selection |
145
+ |------|----------------|-------------------|
146
+ | **Platform** (site operator) | Platform JWT from `/api/v1/platform/auth/login` | Use **`X-Project-Id`** for tenant data APIs when JWT is system-scoped |
147
+ | **Tenant / project member** | Tenant JWT or API key from project keys | Tenant is implied by the key or login |
148
+ | **App user** (end user) | App JWT from `/api/v1/app/auth/login` | Scoped to the **tenant_id** / project used at login |
149
+
150
+ ## API keys (project)
151
+
152
+ Project administrators can create **public** and **secret** API keys. Public keys are suited to read-only or limited clients; secret keys can perform privileged operations.
153
+
154
+ ## OAuth (app users)
155
+
156
+ Browser and mobile apps can use OAuth2-style flows against `/api/v1/app/auth/oauth/...`.
157
+
158
+ ---
159
+
160
+ # Health, version & discovery
161
+
162
+ ## Health
163
+
164
+ **`GET /api/v1/health`** — Returns `{"status":"ok"}`. No authentication required.
165
+
166
+ ## Version
167
+
168
+ **`GET /api/v1/version`** — Build version and metadata. No authentication.
169
+
170
+ ## JWKS
171
+
172
+ **`GET /.well-known/jwks.json`** — JSON Web Key Set for JWT validation.
173
+
174
+ ## OpenID configuration
175
+
176
+ **`GET /.well-known/openid-configuration`** — OIDC discovery document.
177
+
178
+ ---
179
+
180
+ # Documents (XML)
181
+
182
+ **Base path:** `/api/v1/documents`
183
+
184
+ ## List / query documents
185
+
186
+ **`GET /api/v1/documents`**
187
+
188
+ | Parameter | Type | Required | Description |
189
+ |-----------|------|----------|-------------|
190
+ | `limit` | integer | No | Page size (clamped by server) |
191
+ | `offset` | integer | No | Skip rows |
192
+ | `prefix` | string | No | Identifier prefix filter |
193
+ | `match` | string | No | Match expression / pattern |
194
+ | `match_start` | string | No | Identifier starts with |
195
+ | `match_end` | string | No | Identifier ends with |
196
+ | `contains` | string | No | Identifier contains substring |
197
+ | `regex` | string | No | Regex filter |
198
+
199
+ ## Write document
200
+
201
+ **`POST /api/v1/documents`**
202
+
203
+ With `Content-Type: application/json`:
204
+ ```json
205
+ {
206
+ "xml": "<chapter db:identifier=\"/book/ch1\">...</chapter>",
207
+ "is_top": true
208
+ }
209
+ ```
210
+
211
+ With `Content-Type: application/xml`: send raw XML body.
212
+
213
+ The identifier is extracted from the `db:identifier` attribute in the root XML element.
214
+
215
+ ## Get document by identifier
216
+
217
+ **`GET /api/v1/documents/by-id?identifier=/book/ch1`**
218
+
219
+ Query parameters: `identifier` (required), `flags` (optional: `FirstMatch`, `IncludeChildren`, `NoChildren`, `KeepIndexNodes`), `filter`, `path_filter`.
220
+
221
+ ## Delete document
222
+
223
+ **`DELETE /api/v1/documents/by-id?identifier=/book/ch1`**
224
+
225
+ ## List identifiers
226
+
227
+ **`GET /api/v1/documents/identifiers`** — Returns `{ identifiers: string[], total, offset, limit }`.
228
+
229
+ Same query filters as document listing (`match`, `match_start`, `contains`, `regex`, `limit`, `offset`).
230
+
231
+ ## Identifier children (hierarchy)
232
+
233
+ **`GET /api/v1/documents/identifier-children?parent_path=/book`**
234
+
235
+ Returns `{ parent_path, parent_is_identifier, children: [{ segment, full_path, is_identifier, has_children }] }`.
236
+
237
+ ## Add identifier
238
+
239
+ **`POST /api/v1/documents/identifiers`** — `{ "identifier": "/book/ch2" }`
240
+
241
+ ## Add alias
242
+
243
+ **`POST /api/v1/documents/identifiers/alias`** — `{ "original": "/book/ch1", "alias": "/book/intro" }`
244
+
245
+ ## Changed documents
246
+
247
+ **`GET /api/v1/documents/changed?branch=...&from_date=...&to_date=...`**
248
+
249
+ ## Document change log
250
+
251
+ **`GET /api/v1/documents/log?match_start=...&from_date=...&to_date=...`**
252
+
253
+ ## Change date query
254
+
255
+ **`GET /api/v1/documents/change-date?identifier=...`**
256
+
257
+ ## Resolve xcitepath
258
+
259
+ **`GET /api/v1/documents/xcitepath?identifier=...`**
260
+
261
+ ---
262
+
263
+ # JSON documents
264
+
265
+ **Base path:** `/api/v1/json-documents`
266
+
267
+ ## Write JSON document
268
+
269
+ **`POST /api/v1/json-documents`**
270
+
271
+ ```json
272
+ {
273
+ "identifier": "app.settings",
274
+ "data": { "theme": "dark", "maxUploadMb": 25 }
275
+ }
276
+ ```
277
+
278
+ Note: The SDK method uses `identifier` and `data` fields. Some older documentation shows `key` and `value`.
279
+
280
+ ## Read JSON document
281
+
282
+ **`GET /api/v1/json-documents?identifier=app.settings`**
283
+
284
+ ## Delete JSON document
285
+
286
+ **`DELETE /api/v1/json-documents?identifier=app.settings`**
287
+
288
+ ## List JSON documents
289
+
290
+ **`GET /api/v1/json-documents/list?match=...&limit=...&offset=...`**
291
+
292
+ Returns `{ identifiers: string[], total, offset, limit }`.
293
+
294
+ ---
295
+
296
+ # Document import & export
297
+
298
+ ## Import document
299
+
300
+ **`POST /api/v1/documents/import`** — `Content-Type: multipart/form-data`, field `file`. Supported: DOCX, ODF, RTF, PDF, Markdown, plain text.
301
+
302
+ ## Export document
303
+
304
+ **`GET /api/v1/documents/export?id=/book/ch1&format=...`**
305
+
306
+ ## Export provision
307
+
308
+ **`GET /api/v1/documents/export/provision`** — Migration / mirror workflows.
309
+
310
+ ---
311
+
312
+ # Metadata
313
+
314
+ Attach structured **JSON metadata** to documents or nodes.
315
+
316
+ **Base path:** `/api/v1/meta`
317
+
318
+ ## Append / set metadata
319
+
320
+ **`POST /api/v1/meta`**
321
+
322
+ ```json
323
+ {
324
+ "identifier": "/book/ch1",
325
+ "value": { "status": "review", "owner": "alice" },
326
+ "path": ""
327
+ }
328
+ ```
329
+
330
+ Optional `"mode": "append"` to append rather than overwrite.
331
+
332
+ Can also use `"query"` instead of `"identifier"` to target multiple documents by query filter.
333
+
334
+ ## Query metadata (GET)
335
+
336
+ **`GET /api/v1/meta?identifier=/book/ch1&path=`**
337
+
338
+ ## Query metadata (POST body)
339
+
340
+ **`POST /api/v1/meta/query`** — `{ "query": {...}, "path": "" }`
341
+
342
+ ## Clear metadata
343
+
344
+ **`DELETE /api/v1/meta`** — `{ "query": {...} }`
345
+
346
+ ---
347
+
348
+ # Search
349
+
350
+ Full-text search (backed by Meilisearch or Elasticsearch).
351
+
352
+ **Base path:** `/api/v1/search`
353
+
354
+ ## Search
355
+
356
+ **`POST /api/v1/search`**
357
+
358
+ ```json
359
+ {
360
+ "query": "installation guide",
361
+ "doc_types": ["xml", "json"],
362
+ "branch": "",
363
+ "limit": 20,
364
+ "offset": 0
365
+ }
366
+ ```
367
+
368
+ Response: `{ hits: [{ identifier, path, doc_type, branch, snippet, score, xcitepath? }], total, query }`.
369
+
370
+ ## Reindex
371
+
372
+ **`POST /api/v1/search/reindex`** — Rebuilds the search index.
373
+
374
+ ---
375
+
376
+ # Unquery
377
+
378
+ **`POST /api/v1/unquery`**
379
+
380
+ Executes a structured query document (JSON DSL) for advanced analytics, bulk exports, and cross-document operations.
381
+
382
+ ```json
383
+ {
384
+ "query": { "match_start": "/manual/" },
385
+ "unquery": { /* Unquery DSL operations */ }
386
+ }
387
+ ```
388
+
389
+ Unquery supports: hierarchical navigation (self, parents, ancestors, children, descendants), XPath queries on XML, JSON path navigation, counts, sums, string operations, type casting, and conditional branching. Results are always structured JSON.
390
+
391
+ ---
392
+
393
+ # Branches
394
+
395
+ **Base path:** `/api/v1/branches`
396
+
397
+ **IMPORTANT: The default branch is the empty string `""`.** When no branch is specified, the server uses the root timeline.
398
+
399
+ ## List branches
400
+
401
+ **`GET /api/v1/branches`** — Returns `{ branches: [{ name, from_branch, from_date, from_date_key, tip_commit }] }`.
402
+
403
+ ## Create branch
404
+
405
+ **`POST /api/v1/branches`** — `{ "name": "feature-x", "from_branch": "", "from_date": "" }`
406
+
407
+ ## Get branch
408
+
409
+ **`GET /api/v1/branches/{name}`** — Returns `{ branch: {...} }`.
410
+
411
+ ## Delete branch
412
+
413
+ **`DELETE /api/v1/branches/{name}`**
414
+
415
+ ## Merge branch
416
+
417
+ **`POST /api/v1/branches/{target}/merge`**
418
+
419
+ ```json
420
+ {
421
+ "source_branch": "feature-x",
422
+ "message": "Merge feature",
423
+ "auto_resolve": "none"
424
+ }
425
+ ```
426
+
427
+ `auto_resolve`: `"none"` | `"source"` | `"target"`.
428
+
429
+ Returns `{ status: "completed"|"conflicts", commit?, merged_identifiers?, conflicts? }`.
430
+
431
+ ## Delete revision on branch
432
+
433
+ **`DELETE /api/v1/branches/{name}/revisions/{date}`**
434
+
435
+ ---
436
+
437
+ # Commits, tags & diff
438
+
439
+ ## Create commit
440
+
441
+ **`POST /api/v1/commits`** — `{ "message": "...", "author": "..." }`
442
+
443
+ Returns `{ commit: { id, branch, date_key, message, author, ... } }`.
444
+
445
+ ## List commits
446
+
447
+ **`GET /api/v1/commits?branch=...&limit=...&offset=...`**
448
+
449
+ Returns `{ commits: [...], total, branch }`.
450
+
451
+ ## Get commit
452
+
453
+ **`GET /api/v1/commits/{id}`** — Returns `{ commit: {...} }`.
454
+
455
+ ## Rollback to commit
456
+
457
+ **`POST /api/v1/commits/{id}/rollback`** — `{ "confirm": true }`
458
+
459
+ ## Cherry-pick commit
460
+
461
+ **`POST /api/v1/commits/{id}/cherry-pick`** — `{ "message": "...", "author": "..." }`
462
+
463
+ ## Create tag
464
+
465
+ **`POST /api/v1/tags`** — `{ "name": "v1.0", "commit_id": "...", "message": "..." }`
466
+
467
+ ## List tags
468
+
469
+ **`GET /api/v1/tags?limit=...&offset=...`** — Returns `{ tags: [...], total }`.
470
+
471
+ ## Get / Delete tag
472
+
473
+ **`GET /api/v1/tags/{name}`** / **`DELETE /api/v1/tags/{name}`**
474
+
475
+ ## Diff
476
+
477
+ **`POST /api/v1/diff`**
478
+
479
+ ```json
480
+ {
481
+ "from": { "branch": "", "date_key": "..." },
482
+ "to": { "branch": "feature-x" },
483
+ "include_content": true
484
+ }
485
+ ```
486
+
487
+ Returns `{ changes: [{ identifier, action: "added"|"modified"|"deleted", from_content?, to_content? }], total_changes }`.
488
+
489
+ ---
490
+
491
+ # Locks
492
+
493
+ **Base path:** `/api/v1/locks`
494
+
495
+ ## Acquire lock
496
+
497
+ **`POST /api/v1/locks`** — `{ "identifier": "/book/ch1", "expires": 600 }`
498
+
499
+ Returns lock info. **`409`** if already locked.
500
+
501
+ ## Release lock
502
+
503
+ **`DELETE /api/v1/locks`** — `{ "identifier": "/book/ch1", "lock_id": "..." }`
504
+
505
+ ## Query locks
506
+
507
+ **`GET /api/v1/locks?identifier=...`** — Lists active locks.
508
+
509
+ ---
510
+
511
+ # Project configuration
512
+
513
+ **Base path:** `/api/v1/config`
514
+
515
+ **`GET /api/v1/config`** — Returns `{ settings: {...} }`.
516
+
517
+ **`PUT /api/v1/config`** — `{ "settings": {...} }`.
518
+
519
+ ---
520
+
521
+ # Triggers
522
+
523
+ **Base path:** `/api/v1/triggers`
524
+
525
+ ## Create or update trigger
526
+
527
+ **`POST /api/v1/triggers`**
528
+
529
+ ```json
530
+ {
531
+ "trigger_id": "on_status_review",
532
+ "trigger": {
533
+ "match": { "meta_key": "status", "meta_value": "review" },
534
+ "action": { "type": "webhook", "url": "https://hooks.example.com/xcite" }
535
+ }
536
+ }
537
+ ```
538
+
539
+ ## List triggers
540
+
541
+ **`GET /api/v1/triggers`** — Returns map of `{ trigger_id: trigger_definition }`.
542
+
543
+ ## Delete trigger
544
+
545
+ **`DELETE /api/v1/triggers?name=on_status_review`**
546
+
547
+ ---
548
+
549
+ # Security policies
550
+
551
+ **Base path:** `/api/v1/security`
552
+
553
+ ## Create policy
554
+
555
+ **`POST /api/v1/security/policies`**
556
+
557
+ ```json
558
+ {
559
+ "policy_id": "deny-viewers-admin",
560
+ "policy": {
561
+ "effect": "deny",
562
+ "priority": 10,
563
+ "subjects": { "type": "app_user", "groups": ["viewers"] },
564
+ "resources": { "identifiers": [{ "match_start": "/admin/" }] },
565
+ "actions": ["document:write", "document:delete"],
566
+ "conditions": {}
567
+ }
568
+ }
569
+ ```
570
+
571
+ ## List / Get / Update / Delete policies
572
+
573
+ **`GET /api/v1/security/policies`** — Map of all policies.
574
+ **`GET /api/v1/security/policies/{id}`**
575
+ **`PUT /api/v1/security/policies/{id}`**
576
+ **`DELETE /api/v1/security/policies/{id}`**
577
+
578
+ ## Check access (dry run)
579
+
580
+ **`POST /api/v1/security/check`**
581
+
582
+ ```json
583
+ {
584
+ "subject": { "type": "app_user", "groups": ["editors"] },
585
+ "identifier": "/admin/config",
586
+ "action": "document:write"
587
+ }
588
+ ```
589
+
590
+ Returns `{ effect: "allow"|"deny", matched_policy_id? }`.
591
+
592
+ ## Security config
593
+
594
+ **`GET /api/v1/security/config`** / **`PUT /api/v1/security/config`**
595
+
596
+ Fields: `default_effect`, `app_user_default_effect`, `developer_bypass`.
597
+
598
+ ---
599
+
600
+ # App authentication
601
+
602
+ End-user authentication for application accounts.
603
+
604
+ **Base path:** `/api/v1/app/auth`
605
+
606
+ ## Register
607
+
608
+ **`POST /api/v1/app/auth/register`** — `{ "tenant_id": "default", "email": "...", "password": "..." }`
609
+
610
+ ## Login
611
+
612
+ **`POST /api/v1/app/auth/login`** — `{ "tenant_id": "default", "email": "...", "password": "..." }`
613
+
614
+ Returns `{ access_token, refresh_token, expires_in, user? }`.
615
+
616
+ ## Refresh
617
+
618
+ **`POST /api/v1/app/auth/refresh`** — `{ "refresh_token": "...", "tenant_id": "..." }`
619
+
620
+ ## Logout
621
+
622
+ **`POST /api/v1/app/auth/logout`**
623
+
624
+ ## Profile
625
+
626
+ **`GET /api/v1/app/auth/me`** / **`PUT /api/v1/app/auth/me`**
627
+
628
+ ## Password flows
629
+
630
+ - `POST /api/v1/app/auth/change-password` — `{ current_password, new_password }`
631
+ - `POST /api/v1/app/auth/forgot-password` — `{ email }`
632
+ - `POST /api/v1/app/auth/reset-password` — `{ token, new_password, tenant_id }`
633
+
634
+ ## Email verification
635
+
636
+ - `POST /api/v1/app/auth/verify-email` — `{ token, tenant_id }`
637
+ - `POST /api/v1/app/auth/send-verification` — `{ user_id }`
638
+
639
+ ## Custom token
640
+
641
+ **`POST /api/v1/app/auth/custom-token`** — Exchange or mint a custom scoped token.
642
+
643
+ ---
644
+
645
+ # OAuth (app users)
646
+
647
+ **Base path:** `/api/v1/app/auth/oauth`
648
+
649
+ ## List providers
650
+
651
+ **`GET /api/v1/app/auth/oauth/providers?tenant_id=default`** — Returns `{ providers: [{ id, display_name }] }`.
652
+
653
+ ## Start authorization
654
+
655
+ **`GET /api/v1/app/auth/oauth/{provider}/authorize?tenant_id=default`** — Redirects browser to IdP.
656
+
657
+ ## Callback
658
+
659
+ **`GET /api/v1/app/auth/oauth/{provider}/callback`** — Handles IdP redirect.
660
+
661
+ ## Exchange code (API)
662
+
663
+ **`POST /api/v1/app/auth/oauth/exchange`** — `{ "provider": "google", "code": "...", "tenant_id": "default" }`
664
+
665
+ ---
666
+
667
+ # App users (administration)
668
+
669
+ **Base path:** `/api/v1/app/users`
670
+
671
+ **`GET /api/v1/app/users`** — List with pagination.
672
+ **`POST /api/v1/app/users`** — Create user.
673
+ **`GET /api/v1/app/users/{id}`** — Get user.
674
+ **`DELETE /api/v1/app/users/{id}`** — Delete user.
675
+ **`PUT /api/v1/app/users/{id}/groups`** — `{ "groups": ["editors"] }`
676
+ **`PUT /api/v1/app/users/{id}/status`** — `{ "status": "active"|"disabled"|"pending_verification" }`
677
+
678
+ ---
679
+
680
+ # Email configuration
681
+
682
+ **Base path:** `/api/v1/app/email`
683
+
684
+ **`GET /api/v1/app/email/config`** / **`PUT /api/v1/app/email/config`** — SMTP settings.
685
+ **`GET /api/v1/app/email/templates`** / **`PUT /api/v1/app/email/templates`** — Email templates.
686
+ **`POST /api/v1/app/email/test`** — `{ "to": "admin@example.com" }`
687
+
688
+ ---
689
+
690
+ # Project & API keys
691
+
692
+ **Base path:** `/api/v1/project`
693
+
694
+ **`GET /api/v1/project`** / **`PUT /api/v1/project`** / **`DELETE /api/v1/project`** — Project CRUD.
695
+ **`POST /api/v1/project/reset`** — Wipe document data.
696
+
697
+ ## Members
698
+
699
+ **`GET /api/v1/project/members`** — List.
700
+ **`POST /api/v1/project/members`** — `{ "email": "...", "role": "editor" }`
701
+ **`PUT /api/v1/project/members/{member_id}/role`** — `{ "role": "admin" }`
702
+ **`DELETE /api/v1/project/members/{member_id}`**
703
+
704
+ ## API keys
705
+
706
+ **`GET /api/v1/project/keys`** — List key metadata.
707
+ **`POST /api/v1/project/keys`** — `{ "name": "CI key", "key_type": "public"|"secret" }`. Raw secret returned **once**.
708
+ **`DELETE /api/v1/project/keys/{key_id}`** — Revoke.
709
+
710
+ ---
711
+
712
+ # Project CORS & rate limits
713
+
714
+ **`GET /api/v1/project/settings/cors`** / **`PUT /api/v1/project/settings/cors`**
715
+
716
+ ```json
717
+ {
718
+ "allowed_origins": ["https://app.example.com"],
719
+ "allowed_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
720
+ "allow_credentials": true
721
+ }
722
+ ```
723
+
724
+ **`GET /api/v1/project/settings/rate-limits`** / **`PUT /api/v1/project/settings/rate-limits`**
725
+
726
+ ```json
727
+ { "read_rpm": 1200, "write_rpm": 120, "auth_rpm": 30 }
728
+ ```
729
+
730
+ ---
731
+
732
+ # Project backups
733
+
734
+ **Base path:** `/api/v1/project/settings/backup`
735
+
736
+ **`GET .../config`** / **`PUT .../config`** — S3 destination, schedule, retention.
737
+ **`POST .../config/test`** — Test credentials.
738
+ **`GET .../list`** — List backups.
739
+ **`POST .../run`** — Run backup now.
740
+ **`GET .../status`** — Job state.
741
+ **`POST .../restore`** — `{ "backup_id": "2025-01-15T10-00-00Z" }`
742
+
743
+ ---
744
+
745
+ # Mirror sync
746
+
747
+ **Base path:** `/api/v1/project/mirror`
748
+
749
+ **`GET .../config`** / **`PUT .../config`** — Upstream URI, sync mode, credentials.
750
+ **`POST .../test`** — Test connectivity.
751
+ **`GET .../status`** — Last sync time, lag, errors.
752
+ **`POST .../sync`** — Trigger immediate sync.
753
+
754
+ ---
755
+
756
+ # Activity log & notifications
757
+
758
+ **`GET /api/v1/project/logs`** — Paginated audit events.
759
+ **`DELETE /api/v1/project/logs`** — Truncate logs (admin).
760
+ **`GET /api/v1/project/logs/config`** / **`PUT .../config`** — Log settings.
761
+ **`GET /api/v1/project/notifications`** — Unread/read notifications.
762
+ **`POST /api/v1/project/notifications/{id}/read`** — Mark one read.
763
+ **`POST /api/v1/project/notifications/read-all`** — Mark all read.
764
+ **`GET /api/v1/project/notifications/config`** / **`PUT .../config`** — Notification settings.
765
+
766
+ ---
767
+
768
+ # Tenant admin API
769
+
770
+ **Base path:** `/api/v1/admin`
771
+
772
+ **`POST /api/v1/admin/tenants`** — `{ "tenant_id": "acme-docs", "name": "Acme Docs" }`
773
+ **`GET /api/v1/admin/tenants`** — List tenants.
774
+ **`GET /api/v1/admin/tenants/{id}`** / **`PUT .../{id}`** / **`DELETE .../{id}`**
775
+ **`POST /api/v1/admin/tenants/reset`** — `{ "tenant_id": "acme-docs" }`
776
+ **`POST /api/v1/admin/users`** — Create tenant user.
777
+ **`GET /api/v1/admin/users`** — List users.
778
+ **`DELETE /api/v1/admin/users/{id}`**
779
+ **`PUT /api/v1/admin/users/{id}/role`** — `{ "role": "editor" }`
780
+
781
+ ---
782
+
783
+ # Platform authentication
784
+
785
+ **Base path:** `/api/v1/platform/auth`
786
+
787
+ **`GET .../registration-config`** — Public: registration policy.
788
+ **`POST .../login`** — `{ "email": "...", "password": "..." }`
789
+ **`POST .../register`** — Create platform user.
790
+ **`POST .../refresh`** — Refresh JWT.
791
+ **`POST .../logout`** — Invalidate session.
792
+ **`GET .../me`** / **`PUT .../me`** — Profile.
793
+ **`POST .../change-password`** — `{ current_password, new_password }`
794
+ **`POST .../forgot-password`** / **`POST .../reset-password`** / **`POST .../verify-email`** — Email flows.
795
+ **`GET .../workspaces`** — List orgs/projects.
796
+
797
+ ---
798
+
799
+ # Platform admin
800
+
801
+ **Base path:** `/api/v1/platform/admin`
802
+
803
+ ## Users
804
+ **`GET .../users`** / **`POST .../users`** / **`GET .../users/{id}`** / **`PUT .../users/{id}`** / **`DELETE .../users/{id}`**
805
+ Moderation: **`POST .../users/{id}/approve|reject|suspend|activate`**
806
+
807
+ ## Invites
808
+ **`GET .../invites`** / **`POST .../invites`** / **`DELETE .../invites/{code}`**
809
+
810
+ ## Config
811
+ **`GET .../config`** / **`PUT .../config`**
812
+
813
+ ## IP access control
814
+ **`GET .../ip-access`** / **`PUT .../ip-access`**
815
+
816
+ ## Rate limits
817
+ **`GET .../rate-limits`** / **`PUT .../rate-limits`**
818
+
819
+ ---
820
+
821
+ # Platform organizations
822
+
823
+ **Base path:** `/api/v1/platform/orgs`
824
+
825
+ **`GET .../`** / **`POST .../`** — `{ "name": "Acme Corp", "slug": "acme" }`
826
+ **`GET .../{org_id}`** / **`PUT .../{org_id}`** / **`DELETE .../{org_id}`**
827
+ **Members:** **`GET .../{org_id}/members`** / **`POST`** / **`PUT .../{member_id}`** / **`DELETE .../{member_id}`**
828
+ **Projects:** **`GET .../{org_id}/projects`** / **`POST .../{org_id}/projects`**
829
+
830
+ ## Member-scoped org API (`/api/v1/orgs`)
831
+ For logged-in users (not only platform admins):
832
+ **`GET /api/v1/orgs`** / **`GET .../{org_id}`** / **`GET .../{org_id}/members`** / **`POST .../{org_id}/members`** / **`PUT .../{org_id}/members/{id}`** / **`DELETE .../{org_id}/members/{id}`** / **`GET .../{org_id}/projects`** / **`POST .../{org_id}/projects`**
833
+
834
+ ---
835
+
836
+ # Platform projects
837
+
838
+ **Base path:** `/api/v1/platform/projects`
839
+
840
+ **`GET .../{project_id}`** / **`PUT .../{project_id}`** / **`DELETE .../{project_id}`**
841
+ **Members:** **`GET .../{project_id}/members`** / **`POST`** / **`PUT .../{member_id}`** / **`DELETE .../{member_id}`**
842
+
843
+ ---
844
+
845
+ # Platform backups
846
+
847
+ **Base path:** `/api/v1/platform/admin/backup`
848
+
849
+ **`GET .../config`** / **`PUT .../config`**
850
+ **`POST .../config/test`** — Test destination.
851
+ **`GET .../list`** — List backups.
852
+ **`POST .../run`** — Start backup.
853
+ **`GET .../status`** — Job progress.
854
+ **`POST .../restore`** — `{ "backup_id": "platform-2025-01-20T00-00-00Z" }`
855
+
856
+ ---
857
+
858
+ # Part 3: SDK Reference
859
+
860
+ # JavaScript/TypeScript SDK (`@xcitedbs/client`)
861
+
862
+ Install: `npm install @xcitedbs/client`
863
+
864
+ ## Quick Start
865
+
866
+ ```typescript
867
+ import { XCiteDBClient } from '@xcitedbs/client';
868
+
869
+ const client = new XCiteDBClient({
870
+ baseUrl: 'http://localhost:8080',
871
+ apiKey: 'your-api-key',
872
+ context: { branch: '', date: '' },
873
+ });
874
+
875
+ // Health check
876
+ await client.health();
877
+
878
+ // Write XML document
879
+ await client.writeDocumentJson(
880
+ '<chapter db:identifier="/manual/v1/intro"><title>Introduction</title></chapter>'
881
+ );
882
+
883
+ // Read document by identifier
884
+ const xml = await client.queryByIdentifier('/manual/v1/intro', 'FirstMatch');
885
+
886
+ // Write JSON document
887
+ await client.writeJsonDocument('app.settings', { theme: 'dark' });
888
+
889
+ // Read JSON document
890
+ const settings = await client.readJsonDocument('app.settings');
891
+
892
+ // Branch, edit, commit, merge
893
+ await client.createBranch('feature-x');
894
+ client.setContext({ branch: 'feature-x' });
895
+ await client.writeJsonDocument('app.settings', { theme: 'light' });
896
+ await client.createCommit('Switch to light theme');
897
+ await client.mergeBranch('', 'feature-x');
898
+ ```
899
+
900
+ ## Constructor Options
901
+
902
+ ```typescript
903
+ interface XCiteDBClientOptions {
904
+ baseUrl: string;
905
+ apiKey?: string;
906
+ accessToken?: string;
907
+ appUserAccessToken?: string;
908
+ appUserRefreshToken?: string;
909
+ context?: DatabaseContext;
910
+ platformConsole?: boolean;
911
+ projectId?: string;
912
+ onSessionTokensUpdated?: (pair: TokenPair) => void;
913
+ onAppUserTokensUpdated?: (pair: AppUserTokenPair) => void;
914
+ onSessionInvalid?: () => void;
915
+ }
916
+
917
+ interface DatabaseContext {
918
+ branch?: string; // '' = default (root timeline)
919
+ date?: string; // Point-in-time
920
+ prefix?: string; // Identifier prefix filter
921
+ tenant_id?: string; // For app-user auth
922
+ }
923
+ ```
924
+
925
+ ## Complete Method List
926
+
927
+ ### Health & Version
928
+ - `health()` → `{ status, timestamp? }`
929
+ - `version()` → `{ version, server, api_version }`
930
+
931
+ ### Platform Auth
932
+ - `platformLogin(email, password)` → `TokenPair`
933
+ - `login(email, password)` → `TokenPair` (alias for platformLogin)
934
+ - `refresh()` → `TokenPair`
935
+ - `logout()` → `void`
936
+ - `me()` → `UserInfo`
937
+ - `platformRegister(body)` → `unknown`
938
+ - `platformRegistrationConfig()` → `PlatformRegistrationConfig`
939
+ - `platformWorkspaces()` → `PlatformWorkspacesResponse`
940
+ - `listMyTenants()` / `listMyProjects()` → `OwnedTenantInfo[]`
941
+ - `switchTenant(tenantId)` / `switchProject(projectId)` → `void` (platform console only)
942
+ - `changePassword(current, new)` → `void`
943
+
944
+ ### Context & Configuration
945
+ - `setContext(ctx: DatabaseContext)` → `void`
946
+ - `setProjectId(projectId)` → `void`
947
+ - `setTokens(access, refresh?)` → `void`
948
+ - `setAppUserTokens(access, refresh?)` → `void`
949
+ - `clearAppUserTokens()` → `void`
950
+
951
+ ### XML Documents
952
+ - `writeDocumentJson(xml, options?)` → `void` — JSON wrapper (recommended)
953
+ - `writeXML(xml)` → `void` — Raw XML body
954
+ - `queryByIdentifier(identifier, flags?, filter?, pathFilter?)` → `string[]`
955
+ - `queryDocuments(query: XCiteQuery, flags?, filter?, pathFilter?)` → `string[]`
956
+ - `deleteDocument(identifier)` → `void`
957
+ - `listIdentifiers(query: XCiteQuery)` → `ListIdentifiersResult`
958
+ - `listIdentifierChildren(parentPath?)` → `ListIdentifierChildrenResult`
959
+ - `addIdentifier(identifier)` → `boolean`
960
+ - `addAlias(original, alias)` → `boolean`
961
+ - `queryChangeDate(identifier)` → `string`
962
+ - `getXcitepath(identifier)` → `string`
963
+ - `changedIdentifiers(branch, fromDate?, toDate?)` → `string[]`
964
+ - `queryLog(query, fromDate, toDate)` → `LogEntry[]`
965
+
966
+ ### JSON Documents
967
+ - `writeJsonDocument(identifier, data)` → `void`
968
+ - `readJsonDocument(identifier)` → `unknown`
969
+ - `deleteJsonDocument(identifier)` → `void`
970
+ - `listJsonDocuments(match?, limit?, offset?)` → `ListIdentifiersResult`
971
+
972
+ ### Metadata
973
+ - `addMeta(identifier, value, path?, opts?)` → `boolean`
974
+ - `addMetaByQuery(query, value, path?, firstMatch?, opts?)` → `boolean`
975
+ - `appendMeta(identifier, value, path?)` → `boolean`
976
+ - `appendMetaByQuery(query, value, path?, firstMatch?)` → `boolean`
977
+ - `queryMeta(identifier, path?)` → `unknown`
978
+ - `queryMetaByQuery(query, path?)` → `unknown`
979
+ - `clearMeta(query)` → `boolean`
980
+
981
+ ### Branches
982
+ - `createBranch(name, fromBranch?, fromDate?)` → `void`
983
+ - `listBranches()` → `BranchInfo[]`
984
+ - `getBranch(name)` → `BranchInfo`
985
+ - `deleteBranch(name)` → `void`
986
+ - `deleteRevision(branch, date)` → `void`
987
+ - `mergeBranch(targetBranch, sourceBranch, options?)` → `MergeResult`
988
+
989
+ ### Commits
990
+ - `createCommit(message, author?)` → `CommitRecord`
991
+ - `listCommits(options?)` → `{ commits, total, branch }`
992
+ - `getCommit(commitId)` → `CommitRecord`
993
+ - `rollbackToCommit(commitId)` → `{ rolled_back_commits, current_tip }`
994
+ - `cherryPick(commitId, message?, author?)` → `CommitRecord`
995
+
996
+ ### Tags
997
+ - `createTag(name, commitId, message?, author?)` → `TagRecord`
998
+ - `listTags(options?)` → `{ tags, total }`
999
+ - `getTag(name)` → `TagRecord`
1000
+ - `deleteTag(name)` → `void`
1001
+
1002
+ ### Diff
1003
+ - `diff(from: DiffRef, to: DiffRef, includeContent?)` → `DiffResult`
1004
+
1005
+ ### Locks
1006
+ - `acquireLock(identifier, expires?)` → `LockInfo`
1007
+ - `releaseLock(identifier, lockId)` → `boolean`
1008
+ - `findLocks(identifier)` → `LockInfo[]`
1009
+
1010
+ ### Search
1011
+ - `search(query: TextSearchQuery)` → `TextSearchResult`
1012
+ - `reindex()` → `{ status, message }`
1013
+
1014
+ ### Unquery
1015
+ - `unquery(query: XCiteQuery, unqueryDoc)` → `unknown`
1016
+
1017
+ ### Security Policies
1018
+ - `createPolicy(policyId, policy: SecurityPolicy)` → `StoredPolicyResponse`
1019
+ - `listPolicies()` → `Record<string, SecurityPolicy>`
1020
+ - `getPolicy(policyId)` → `StoredPolicyResponse`
1021
+ - `updatePolicy(policyId, policy)` → `PolicyUpdateResponse`
1022
+ - `deletePolicy(policyId)` → `void`
1023
+ - `checkAccess(subject, identifier, action, metaPath?, branch?)` → `AccessCheckResult`
1024
+ - `getSecurityConfig()` → `SecurityConfig`
1025
+ - `updateSecurityConfig(config)` → `void`
1026
+
1027
+ ### Triggers
1028
+ - `upsertTrigger(triggerId, trigger)` → `StoredTriggerResponse`
1029
+ - `listTriggers()` → `Record<string, TriggerDefinition>`
1030
+ - `getTrigger(name)` → `StoredTriggerResponse`
1031
+ - `deleteTrigger(name)` → `void`
1032
+
1033
+ ### App User Auth
1034
+ - `registerAppUser(email, password, displayName?, groups?, attributes?)` → `AppUser`
1035
+ - `loginAppUser(email, password)` → `AppUserTokenPair`
1036
+ - `refreshAppUser()` → `AppUserTokenPair`
1037
+ - `logoutAppUser()` → `void`
1038
+ - `appUserMe()` → `AppUser`
1039
+ - `updateAppUserProfile(displayName?, attributes?)` → `AppUser`
1040
+ - `changeAppUserPassword(current, new)` → `void`
1041
+ - `forgotAppUserPassword(email)` → `ForgotPasswordResponse`
1042
+ - `resetAppUserPassword(token, newPassword)` → `void`
1043
+ - `sendAppUserVerification(userId)` → `SendVerificationResponse`
1044
+ - `verifyAppUserEmail(token)` → `void`
1045
+ - `exchangeCustomToken(token)` → `AppUserTokenPair`
1046
+ - `getOAuthProviders()` → `OAuthProvidersResponse`
1047
+ - `oauthAuthorizePath(provider)` → `string`
1048
+ - `exchangeOAuthCode(code)` → `AppUserTokenPair`
1049
+ - `getAppAuthConfig()` → `AppAuthConfig`
1050
+
1051
+ ### App User Management (admin)
1052
+ - `listAppUsers()` → `AppUser[]`
1053
+ - `getAppUser(userId)` → `AppUser`
1054
+ - `createAppUser(email, password, displayName?, groups?, attributes?)` → `AppUser`
1055
+ - `deleteAppUser(userId)` → `void`
1056
+ - `updateAppUserGroups(userId, groups)` → `void`
1057
+ - `updateAppUserStatus(userId, status)` → `void`
1058
+
1059
+ ### Email Config
1060
+ - `getEmailConfig()` → `AppEmailConfig`
1061
+ - `updateEmailConfig(config)` → `AppEmailConfig`
1062
+ - `getEmailTemplates()` → `AppEmailTemplates`
1063
+ - `updateEmailTemplates(templates)` → `AppEmailTemplates`
1064
+ - `sendTestEmail(to)` → `EmailTestResponse`
1065
+
1066
+ ### API Keys
1067
+ - `listApiKeys()` → `ApiKeyInfo[]`
1068
+ - `createApiKey(name, expiresAt?, keyType?)` → `unknown`
1069
+ - `revokeApiKey(keyId)` → `void`
1070
+
1071
+ ### WebSocket
1072
+ - `subscribe(options: SubscriptionOptions, callback, onError?)` → `WebSocketSubscription`
1073
+
1074
+ ## Key Types
1075
+
1076
+ ```typescript
1077
+ interface XCiteQuery {
1078
+ match?: string;
1079
+ match_start?: string;
1080
+ match_end?: string;
1081
+ contains?: string | string[];
1082
+ regex?: string;
1083
+ required_meta_paths?: string[];
1084
+ filter_any_meta?: boolean;
1085
+ limit?: number;
1086
+ offset?: number;
1087
+ }
1088
+
1089
+ type Flags = 'None' | 'FirstMatch' | 'IncludeChildren' | 'NoChildren' | 'KeepIndexNodes' | string;
1090
+
1091
+ interface WriteDocumentOptions {
1092
+ is_top?: boolean;
1093
+ compare_attributes?: boolean;
1094
+ }
1095
+
1096
+ interface SubscriptionOptions {
1097
+ pattern: string; // e.g. '/manual/*'
1098
+ event_type?: string; // e.g. '*'
1099
+ }
1100
+ ```
1101
+
1102
+ ## Errors
1103
+
1104
+ ```typescript
1105
+ class XCiteDBError extends Error {
1106
+ status: number; // HTTP status code
1107
+ body?: unknown; // Parsed response body
1108
+ }
1109
+ ```
1110
+
1111
+ ---
1112
+
1113
+ # Python SDK (`xcitedb`)
1114
+
1115
+ Install: `pip install xcitedb`
1116
+
1117
+ ```python
1118
+ import asyncio
1119
+ from xcitedb import XCiteDBClient, XCiteQuery, DatabaseContext
1120
+
1121
+ async def main():
1122
+ async with XCiteDBClient(
1123
+ "http://localhost:8080",
1124
+ api_key="your-key",
1125
+ context=DatabaseContext(branch="", date=""),
1126
+ ) as client:
1127
+ print(await client.health())
1128
+ ids = await client.query_documents(XCiteQuery(match_start="/manual/"))
1129
+ print(ids)
1130
+
1131
+ asyncio.run(main())
1132
+ ```
1133
+
1134
+ Methods mirror the JavaScript SDK with Python naming conventions (e.g. `query_documents`, `write_json_document`).
1135
+
1136
+ ---
1137
+
1138
+ # C++ SDK (`xcitedb`)
1139
+
1140
+ ```cpp
1141
+ #include <xcitedb/xcitedb.hpp>
1142
+
1143
+ xcitedb::XCiteDBClientOptions opt;
1144
+ opt.base_url = "http://127.0.0.1:8080";
1145
+ opt.api_key = "your-key";
1146
+ opt.context.branch = ""; // root timeline
1147
+
1148
+ xcitedb::XCiteDBClient client(opt);
1149
+ auto health = client.health();
1150
+
1151
+ xcitedb::XCiteQuery q;
1152
+ q.match_start = "/manual/";
1153
+ auto ids = client.query_documents(q);
1154
+ ```
1155
+
1156
+ Synchronous (blocking) HTTP client. Methods mirror the JavaScript SDK with C++ naming. Errors throw `xcitedb::XCiteDBError` with `.status()` and `.body()`.
1157
+
1158
+ Includes optional `xcitevcs` CLI for command-line operations (branches, commits, documents, search, import/export).