@xcitedbs/client 0.1.0 → 0.2.5

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