@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/README.md +2 -1
- package/dist/client.d.ts +47 -14
- package/dist/client.js +71 -16
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +37 -5
- package/llms-full.txt +1181 -0
- package/llms.txt +274 -0
- package/package.json +4 -2
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).
|