@hearthlog/ledger 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +235 -0
- package/README.md +173 -2
- package/contracts/append-hash.json +30 -0
- package/docs/database-structure.md +176 -0
- package/docs/event-store-service-design.md +124 -0
- package/docs/event-store-service.http +182 -0
- package/docs/event-store-service.openapi.yaml +1651 -0
- package/package.json +43 -3
- package/scripts/logical-backup.ts +656 -0
- package/src/action-batches.ts +65 -0
- package/src/app-registry.ts +479 -0
- package/src/auth.ts +186 -0
- package/src/contracts.ts +251 -0
- package/src/domain.ts +245 -0
- package/src/errors.ts +50 -0
- package/src/event-validation.ts +181 -0
- package/src/fence.ts +76 -0
- package/src/http.ts +656 -0
- package/src/index.ts +3 -0
- package/src/ledger-app.ts +2057 -0
- package/src/rpc.ts +42 -0
- package/src/schema.ts +236 -0
- package/src/schemas.ts +429 -0
- package/src/sqlite-export.ts +136 -0
- package/src/transfer.ts +219 -0
|
@@ -0,0 +1,1651 @@
|
|
|
1
|
+
openapi: 3.1.0
|
|
2
|
+
info:
|
|
3
|
+
title: Event Store Service API
|
|
4
|
+
version: 0.4.0
|
|
5
|
+
license:
|
|
6
|
+
name: Proprietary
|
|
7
|
+
identifier: LicenseRef-Proprietary
|
|
8
|
+
description: |
|
|
9
|
+
Minimal API for managing append-only event logs with entity-level optimistic locking.
|
|
10
|
+
Each event is associated with an entity, and version tracking is maintained per entity,
|
|
11
|
+
allowing concurrent writes to different entities while preventing conflicts on the same entity.
|
|
12
|
+
Bearer tokens are scoped to a single app. App tokens can read and write in that app.
|
|
13
|
+
|
|
14
|
+
Typical client flow:
|
|
15
|
+
1. List/select a log.
|
|
16
|
+
2. Replay events after the local head.
|
|
17
|
+
3. Append events with entity-level version checks (idempotent via `requestId`).
|
|
18
|
+
4. Handle conflicts by replaying missing log events and retrying.
|
|
19
|
+
5. Open a gateway-authenticated `ledger.v1` WebSocket for realtime events.
|
|
20
|
+
|
|
21
|
+
Key concepts:
|
|
22
|
+
- Each entity has its own version counter starting at 1
|
|
23
|
+
- New entity chains use no first base, then `1`, `2`, and so on
|
|
24
|
+
- Existing entity chains at version `N` use `N`, `N+1`, and so on
|
|
25
|
+
- Conflicts return the current entity version for client-side resolution
|
|
26
|
+
- Multiple entities can be updated in a single atomic request
|
|
27
|
+
- Ledger validates event envelopes but leaves domain payload rules to clients
|
|
28
|
+
- `ADMIN_TOKEN` can optionally enable `/admin/v1` for operator-only provisioning
|
|
29
|
+
- Unknown request fields are rejected
|
|
30
|
+
- Log forking and request-level log version checks are not supported
|
|
31
|
+
servers:
|
|
32
|
+
- url: /
|
|
33
|
+
description: The same origin that serves this specification.
|
|
34
|
+
security:
|
|
35
|
+
- bearerAuth: []
|
|
36
|
+
paths:
|
|
37
|
+
/health:
|
|
38
|
+
get:
|
|
39
|
+
operationId: healthCheck
|
|
40
|
+
summary: Check database readiness
|
|
41
|
+
security: []
|
|
42
|
+
responses:
|
|
43
|
+
"204":
|
|
44
|
+
description: The service can query its database.
|
|
45
|
+
"500":
|
|
46
|
+
description: The database readiness check failed.
|
|
47
|
+
/session:
|
|
48
|
+
get:
|
|
49
|
+
operationId: getSession
|
|
50
|
+
summary: Resolve the authenticated app and principal
|
|
51
|
+
responses:
|
|
52
|
+
"200":
|
|
53
|
+
description: Stable tenant and credential-principal identity.
|
|
54
|
+
content:
|
|
55
|
+
application/json:
|
|
56
|
+
schema:
|
|
57
|
+
$ref: "#/components/schemas/SessionResponse"
|
|
58
|
+
example:
|
|
59
|
+
appId: 00000000-0000-0000-0000-000000000001
|
|
60
|
+
principalId: 33333333-3333-4333-8333-333333333333
|
|
61
|
+
"401":
|
|
62
|
+
$ref: "#/components/responses/Unauthorized"
|
|
63
|
+
/logs:
|
|
64
|
+
get:
|
|
65
|
+
operationId: listLogs
|
|
66
|
+
summary: List available logs
|
|
67
|
+
description: Returns every log in the caller's app.
|
|
68
|
+
responses:
|
|
69
|
+
"200":
|
|
70
|
+
description: List of logs.
|
|
71
|
+
content:
|
|
72
|
+
application/json:
|
|
73
|
+
schema:
|
|
74
|
+
$ref: "#/components/schemas/LogListResponse"
|
|
75
|
+
examples:
|
|
76
|
+
default:
|
|
77
|
+
value:
|
|
78
|
+
logs:
|
|
79
|
+
- id: 11111111-1111-1111-1111-111111111111
|
|
80
|
+
name: default
|
|
81
|
+
headVersion: 187
|
|
82
|
+
createdAt: "2024-05-01T08:00:00.000Z"
|
|
83
|
+
lastUpdatedAt: "2024-06-02T09:24:55.612Z"
|
|
84
|
+
- id: 22222222-2222-2222-2222-222222222222
|
|
85
|
+
name: finance-simulation
|
|
86
|
+
headVersion: 42
|
|
87
|
+
createdAt: "2024-05-20T12:00:00.000Z"
|
|
88
|
+
lastUpdatedAt: "2024-05-30T19:02:11.004Z"
|
|
89
|
+
"401":
|
|
90
|
+
$ref: "#/components/responses/Unauthorized"
|
|
91
|
+
post:
|
|
92
|
+
operationId: createLog
|
|
93
|
+
summary: Create a new log
|
|
94
|
+
description: Creates an empty log. The service assigns the log ID and generates a name when omitted.
|
|
95
|
+
requestBody:
|
|
96
|
+
required: true
|
|
97
|
+
content:
|
|
98
|
+
application/json:
|
|
99
|
+
schema:
|
|
100
|
+
$ref: "#/components/schemas/CreateLogRequest"
|
|
101
|
+
example:
|
|
102
|
+
name: elections-archive
|
|
103
|
+
responses:
|
|
104
|
+
"201":
|
|
105
|
+
description: Log created.
|
|
106
|
+
content:
|
|
107
|
+
application/json:
|
|
108
|
+
schema:
|
|
109
|
+
$ref: "#/components/schemas/CreateLogResponse"
|
|
110
|
+
examples:
|
|
111
|
+
default:
|
|
112
|
+
value:
|
|
113
|
+
id: 33333333-3333-3333-3333-333333333333
|
|
114
|
+
name: elections-archive
|
|
115
|
+
headVersion: 0
|
|
116
|
+
createdAt: "2024-06-02T10:18:03.447Z"
|
|
117
|
+
"400":
|
|
118
|
+
$ref: "#/components/responses/ValidationError"
|
|
119
|
+
"401":
|
|
120
|
+
$ref: "#/components/responses/Unauthorized"
|
|
121
|
+
"422":
|
|
122
|
+
description: The JSON body does not match the request schema.
|
|
123
|
+
/logs/{logId}:
|
|
124
|
+
parameters:
|
|
125
|
+
- $ref: "#/components/parameters/LogId"
|
|
126
|
+
get:
|
|
127
|
+
operationId: getLog
|
|
128
|
+
summary: Fetch log metadata
|
|
129
|
+
description: Returns current head information for a log in the caller's app.
|
|
130
|
+
responses:
|
|
131
|
+
"200":
|
|
132
|
+
description: Log metadata.
|
|
133
|
+
content:
|
|
134
|
+
application/json:
|
|
135
|
+
schema:
|
|
136
|
+
$ref: "#/components/schemas/LogMetadata"
|
|
137
|
+
examples:
|
|
138
|
+
default:
|
|
139
|
+
value:
|
|
140
|
+
id: 11111111-1111-1111-1111-111111111111
|
|
141
|
+
name: default
|
|
142
|
+
headVersion: 187
|
|
143
|
+
createdAt: "2024-05-01T08:00:00.000Z"
|
|
144
|
+
lastUpdatedAt: "2024-06-02T09:24:55.612Z"
|
|
145
|
+
"401":
|
|
146
|
+
$ref: "#/components/responses/Unauthorized"
|
|
147
|
+
"404":
|
|
148
|
+
$ref: "#/components/responses/NotFound"
|
|
149
|
+
delete:
|
|
150
|
+
operationId: deleteLog
|
|
151
|
+
summary: Delete a log
|
|
152
|
+
description: Deletes the log and its events. A source log with child forks cannot be deleted.
|
|
153
|
+
responses:
|
|
154
|
+
"204":
|
|
155
|
+
description: Log deleted.
|
|
156
|
+
"401":
|
|
157
|
+
$ref: "#/components/responses/Unauthorized"
|
|
158
|
+
"404":
|
|
159
|
+
$ref: "#/components/responses/NotFound"
|
|
160
|
+
"409":
|
|
161
|
+
description: This log has forks that depend on its provenance.
|
|
162
|
+
/logs/{logId}/forks:
|
|
163
|
+
parameters:
|
|
164
|
+
- $ref: "#/components/parameters/LogId"
|
|
165
|
+
post:
|
|
166
|
+
operationId: forkLog
|
|
167
|
+
summary: Fork an accepted prefix into a new log
|
|
168
|
+
description: Creates a preparing child log with the source events through a complete action boundary. Repeating the same requestId returns the same child. The frontend Worker prepares inherited private files before the child is marked ready.
|
|
169
|
+
requestBody:
|
|
170
|
+
required: true
|
|
171
|
+
content:
|
|
172
|
+
application/json:
|
|
173
|
+
schema:
|
|
174
|
+
$ref: "#/components/schemas/ForkLogRequest"
|
|
175
|
+
responses:
|
|
176
|
+
"201":
|
|
177
|
+
description: Fork created or idempotently found.
|
|
178
|
+
content:
|
|
179
|
+
application/json:
|
|
180
|
+
schema:
|
|
181
|
+
$ref: "#/components/schemas/LogSummary"
|
|
182
|
+
"400":
|
|
183
|
+
$ref: "#/components/responses/ValidationError"
|
|
184
|
+
"409":
|
|
185
|
+
description: Source head changed or requestId was reused with different content.
|
|
186
|
+
/logs/{logId}/gallery-photos/{photoId}/files/{fileVersionId}/reference:
|
|
187
|
+
parameters:
|
|
188
|
+
- $ref: "#/components/parameters/LogId"
|
|
189
|
+
- name: photoId
|
|
190
|
+
in: path
|
|
191
|
+
required: true
|
|
192
|
+
schema:
|
|
193
|
+
type: string
|
|
194
|
+
format: uuid
|
|
195
|
+
- name: fileVersionId
|
|
196
|
+
in: path
|
|
197
|
+
required: true
|
|
198
|
+
schema:
|
|
199
|
+
type: string
|
|
200
|
+
format: uuid
|
|
201
|
+
get:
|
|
202
|
+
operationId: getGalleryPhotoReference
|
|
203
|
+
summary: Check whether an accepted Gallery photo version belongs to a log
|
|
204
|
+
description: The private file gateway calls this with its server-side app token. Preparing forks cannot access inherited photo bytes. A missing reference returns present false.
|
|
205
|
+
responses:
|
|
206
|
+
"200":
|
|
207
|
+
description: Exact per-log photo file reference status.
|
|
208
|
+
content:
|
|
209
|
+
application/json:
|
|
210
|
+
schema:
|
|
211
|
+
type: object
|
|
212
|
+
required: [present]
|
|
213
|
+
additionalProperties: false
|
|
214
|
+
properties:
|
|
215
|
+
present:
|
|
216
|
+
type: boolean
|
|
217
|
+
"400":
|
|
218
|
+
$ref: "#/components/responses/ValidationError"
|
|
219
|
+
"401":
|
|
220
|
+
$ref: "#/components/responses/Unauthorized"
|
|
221
|
+
"404":
|
|
222
|
+
$ref: "#/components/responses/NotFound"
|
|
223
|
+
"409":
|
|
224
|
+
description: The fork is still preparing.
|
|
225
|
+
/logs/{logId}/visibility:
|
|
226
|
+
parameters:
|
|
227
|
+
- $ref: "#/components/parameters/LogId"
|
|
228
|
+
post:
|
|
229
|
+
operationId: setLogVisibility
|
|
230
|
+
summary: Hide or show a log in the log tree
|
|
231
|
+
description: Hiding records when a log was hidden and changes nothing else; the log, its events and its forks stay readable and writable. Hiding an already hidden log keeps the first time. Showing clears it.
|
|
232
|
+
requestBody:
|
|
233
|
+
required: true
|
|
234
|
+
content:
|
|
235
|
+
application/json:
|
|
236
|
+
schema:
|
|
237
|
+
$ref: "#/components/schemas/LogVisibilityRequest"
|
|
238
|
+
responses:
|
|
239
|
+
"200":
|
|
240
|
+
description: Updated log metadata.
|
|
241
|
+
content:
|
|
242
|
+
application/json:
|
|
243
|
+
schema:
|
|
244
|
+
$ref: "#/components/schemas/LogSummary"
|
|
245
|
+
"400":
|
|
246
|
+
$ref: "#/components/responses/ValidationError"
|
|
247
|
+
"404":
|
|
248
|
+
description: Log not found.
|
|
249
|
+
/logs/{logId}/finalize-fork:
|
|
250
|
+
parameters:
|
|
251
|
+
- $ref: "#/components/parameters/LogId"
|
|
252
|
+
post:
|
|
253
|
+
operationId: finalizeFork
|
|
254
|
+
summary: Mark a fork ready after file preparation
|
|
255
|
+
description: Internal frontend Worker operation. The public same-origin Ledger gateway blocks this route.
|
|
256
|
+
responses:
|
|
257
|
+
"200":
|
|
258
|
+
description: Ready fork metadata.
|
|
259
|
+
content:
|
|
260
|
+
application/json:
|
|
261
|
+
schema:
|
|
262
|
+
$ref: "#/components/schemas/LogSummary"
|
|
263
|
+
/logs/{logId}/events:
|
|
264
|
+
parameters:
|
|
265
|
+
- $ref: "#/components/parameters/LogId"
|
|
266
|
+
post:
|
|
267
|
+
operationId: appendEvents
|
|
268
|
+
summary: Append events to a log
|
|
269
|
+
parameters:
|
|
270
|
+
- name: X-Ledger-Actor
|
|
271
|
+
in: header
|
|
272
|
+
required: false
|
|
273
|
+
description: |
|
|
274
|
+
Identity of the person on whose behalf a trusted gateway appends. Ledger stores it as
|
|
275
|
+
each accepted event's `actor`. A value that is empty, longer than 320 characters or
|
|
276
|
+
contains control characters is ignored. Browsers never send it: the frontend Worker
|
|
277
|
+
replaces any incoming value with its verified Access identity.
|
|
278
|
+
schema:
|
|
279
|
+
type: string
|
|
280
|
+
maxLength: 320
|
|
281
|
+
description: |
|
|
282
|
+
Appends one or more events to the log with optimistic locking.
|
|
283
|
+
Re-sending the same `requestId` with canonically equivalent content returns the original
|
|
284
|
+
outcome. Reusing it with different content returns `idempotency_mismatch`.
|
|
285
|
+
A request carries at most 512 events, and the accepted events must serialize to at most
|
|
286
|
+
1,900,000 bytes; larger requests return `400 request_too_large`. Event ids must be unique
|
|
287
|
+
within a request (`400 invalid_event_envelope`) and within the log
|
|
288
|
+
(`409 event_already_accepted`).
|
|
289
|
+
requestBody:
|
|
290
|
+
required: true
|
|
291
|
+
content:
|
|
292
|
+
application/json:
|
|
293
|
+
schema:
|
|
294
|
+
$ref: "#/components/schemas/AppendEventsRequest"
|
|
295
|
+
examples:
|
|
296
|
+
createNewEntity:
|
|
297
|
+
summary: Create a new entity (first event)
|
|
298
|
+
value:
|
|
299
|
+
requestId: b5b2c5b0-64de-4e9f-9c2a-7e69f238d2c1
|
|
300
|
+
events:
|
|
301
|
+
- id: 44444444-4444-4444-4444-444444444444
|
|
302
|
+
type: USER_CREATED
|
|
303
|
+
entityId: user-123
|
|
304
|
+
timestamp: 1717323612903
|
|
305
|
+
payload:
|
|
306
|
+
name: John Doe
|
|
307
|
+
email: john@example.com
|
|
308
|
+
appendToExistingEntity:
|
|
309
|
+
summary: Append to existing entity with version check
|
|
310
|
+
value:
|
|
311
|
+
requestId: a1b2c3d4-5e6f-4788-9190-a1b2c3d4e5f6
|
|
312
|
+
events:
|
|
313
|
+
- id: 55555555-5555-5555-5555-555555555555
|
|
314
|
+
type: USER_UPDATED
|
|
315
|
+
entityId: user-123
|
|
316
|
+
baseEntityVersion: 1
|
|
317
|
+
timestamp: 1717323700000
|
|
318
|
+
payload:
|
|
319
|
+
email: john.doe@example.com
|
|
320
|
+
appendEntityChain:
|
|
321
|
+
summary: Append a sequential chain to one existing entity
|
|
322
|
+
value:
|
|
323
|
+
requestId: 727e32f7-2c5e-48bb-b377-cac25427ac32
|
|
324
|
+
events:
|
|
325
|
+
- type: USER_UPDATED
|
|
326
|
+
entityId: user-123
|
|
327
|
+
baseEntityVersion: 1
|
|
328
|
+
payload:
|
|
329
|
+
email: john.doe@example.com
|
|
330
|
+
- type: USER_VERIFIED
|
|
331
|
+
entityId: user-123
|
|
332
|
+
baseEntityVersion: 2
|
|
333
|
+
payload:
|
|
334
|
+
verified: true
|
|
335
|
+
multiEntityBatch:
|
|
336
|
+
summary: Append events to multiple entities
|
|
337
|
+
value:
|
|
338
|
+
requestId: f1e2d3c4-b5a6-9788-0192-a3b4c5d6e7f8
|
|
339
|
+
events:
|
|
340
|
+
- type: TRANSACTION_ADDED
|
|
341
|
+
entityId: txn-b030
|
|
342
|
+
timestamp: 1717323612903
|
|
343
|
+
payload:
|
|
344
|
+
name: Groceries
|
|
345
|
+
amount: 1249.5
|
|
346
|
+
- type: USER_UPDATED
|
|
347
|
+
entityId: user-456
|
|
348
|
+
baseEntityVersion: 3
|
|
349
|
+
timestamp: 1717323613000
|
|
350
|
+
payload:
|
|
351
|
+
lastActivity: "2024-06-02T09:26:53.000Z"
|
|
352
|
+
responses:
|
|
353
|
+
"200":
|
|
354
|
+
description: Events appended successfully.
|
|
355
|
+
content:
|
|
356
|
+
application/json:
|
|
357
|
+
schema:
|
|
358
|
+
$ref: "#/components/schemas/AppendEventsResponse"
|
|
359
|
+
examples:
|
|
360
|
+
success:
|
|
361
|
+
value:
|
|
362
|
+
newVersion: 188
|
|
363
|
+
events:
|
|
364
|
+
- version: 188
|
|
365
|
+
id: 44444444-4444-4444-4444-444444444444
|
|
366
|
+
type: USER_CREATED
|
|
367
|
+
entityId: user-123
|
|
368
|
+
entityVersion: 1
|
|
369
|
+
timestamp: 1717323612903
|
|
370
|
+
payload:
|
|
371
|
+
name: John Doe
|
|
372
|
+
email: john@example.com
|
|
373
|
+
"400":
|
|
374
|
+
$ref: "#/components/responses/ValidationError"
|
|
375
|
+
"409":
|
|
376
|
+
description: |
|
|
377
|
+
Entity conflict, malformed entity version chain, request ID reuse with different content,
|
|
378
|
+
or an action or event id that the log already accepted.
|
|
379
|
+
content:
|
|
380
|
+
application/json:
|
|
381
|
+
schema:
|
|
382
|
+
oneOf:
|
|
383
|
+
- $ref: "#/components/schemas/ConflictResponse"
|
|
384
|
+
- $ref: "#/components/schemas/EntityVersionChainErrorResponse"
|
|
385
|
+
- $ref: "#/components/schemas/ErrorResponse"
|
|
386
|
+
examples:
|
|
387
|
+
entityConflict:
|
|
388
|
+
summary: Entity version conflict
|
|
389
|
+
value:
|
|
390
|
+
code: entity_conflict
|
|
391
|
+
entityId: user-123
|
|
392
|
+
currentEntityVersion: 3
|
|
393
|
+
currentLogVersion: 190
|
|
394
|
+
description: |
|
|
395
|
+
Client attempted to append with baseEntityVersion=1, but the entity is now at version 3.
|
|
396
|
+
Client should fetch events for user-123 after entity version 1 and retry.
|
|
397
|
+
invalidEntityVersionChain:
|
|
398
|
+
summary: Later event does not continue the batch chain
|
|
399
|
+
value:
|
|
400
|
+
error: ValidationError
|
|
401
|
+
code: invalid_entity_version_chain
|
|
402
|
+
message: baseEntityVersion does not continue the entity version chain
|
|
403
|
+
entityId: user-123
|
|
404
|
+
expectedBaseEntityVersion: 2
|
|
405
|
+
actualBaseEntityVersion: 1
|
|
406
|
+
idempotencyMismatch:
|
|
407
|
+
summary: Request ID reused with different content
|
|
408
|
+
value:
|
|
409
|
+
error: Conflict
|
|
410
|
+
code: idempotency_mismatch
|
|
411
|
+
message: requestId was already used with different request content
|
|
412
|
+
"401":
|
|
413
|
+
$ref: "#/components/responses/Unauthorized"
|
|
414
|
+
"404":
|
|
415
|
+
$ref: "#/components/responses/NotFound"
|
|
416
|
+
"422":
|
|
417
|
+
description: |
|
|
418
|
+
The JSON body does not match the request schema, or the deployment enforces event
|
|
419
|
+
validation (`EVENT_VALIDATION=enforce`) and an event is of an unknown type or has a
|
|
420
|
+
payload key of the wrong kind or a missing required key. The latter returns
|
|
421
|
+
`code: invalid_event_payload` and a `message` naming the event and key.
|
|
422
|
+
get:
|
|
423
|
+
operationId: listEvents
|
|
424
|
+
summary: Fetch events after a version
|
|
425
|
+
parameters:
|
|
426
|
+
- $ref: "#/components/parameters/AfterVersion"
|
|
427
|
+
- $ref: "#/components/parameters/Limit"
|
|
428
|
+
description: |
|
|
429
|
+
Returns at most `limit` events with `version` greater than `afterVersion`.
|
|
430
|
+
Page until fewer than `limit` events are returned.
|
|
431
|
+
Available to app tokens in the same app.
|
|
432
|
+
responses:
|
|
433
|
+
"200":
|
|
434
|
+
description: Events after the requested version.
|
|
435
|
+
content:
|
|
436
|
+
application/json:
|
|
437
|
+
schema:
|
|
438
|
+
$ref: "#/components/schemas/EventBatch"
|
|
439
|
+
examples:
|
|
440
|
+
default:
|
|
441
|
+
value:
|
|
442
|
+
events:
|
|
443
|
+
- version: 181
|
|
444
|
+
id: 66666666-6666-6666-6666-666666666666
|
|
445
|
+
type: TRANSACTION_UPDATED
|
|
446
|
+
entityId: txn-5a00
|
|
447
|
+
entityVersion: 2
|
|
448
|
+
timestamp: 1717319500111
|
|
449
|
+
payload:
|
|
450
|
+
amount: 420.0
|
|
451
|
+
- version: 182
|
|
452
|
+
id: 77777777-7777-7777-7777-777777777777
|
|
453
|
+
type: ASSET_ADDED
|
|
454
|
+
entityId: asset-33a8
|
|
455
|
+
entityVersion: 1
|
|
456
|
+
timestamp: 1717319610786
|
|
457
|
+
payload:
|
|
458
|
+
name: Savings
|
|
459
|
+
amount: 3000
|
|
460
|
+
asset_type: cash
|
|
461
|
+
"401":
|
|
462
|
+
$ref: "#/components/responses/Unauthorized"
|
|
463
|
+
"404":
|
|
464
|
+
$ref: "#/components/responses/NotFound"
|
|
465
|
+
/logs/{logId}/events/socket:
|
|
466
|
+
parameters:
|
|
467
|
+
- $ref: "#/components/parameters/LogId"
|
|
468
|
+
- name: afterVersion
|
|
469
|
+
in: query
|
|
470
|
+
required: false
|
|
471
|
+
schema:
|
|
472
|
+
type: integer
|
|
473
|
+
minimum: 0
|
|
474
|
+
default: 0
|
|
475
|
+
description: Last locally committed log version used for socket catch-up.
|
|
476
|
+
get:
|
|
477
|
+
operationId: connectEventSocket
|
|
478
|
+
summary: Upgrade to the ledger.v1 WebSocket protocol
|
|
479
|
+
description: |
|
|
480
|
+
The browser connects through the same-origin frontend gateway, which
|
|
481
|
+
injects the app bearer credential while forwarding the upgrade through
|
|
482
|
+
a Cloudflare service binding. The server sends `event`, `ready`,
|
|
483
|
+
`resync_required`, and `error` JSON frames. Client messages are not
|
|
484
|
+
supported.
|
|
485
|
+
responses:
|
|
486
|
+
"101":
|
|
487
|
+
description: WebSocket upgrade accepted.
|
|
488
|
+
"400":
|
|
489
|
+
description: The `ledger.v1` subprotocol was not requested.
|
|
490
|
+
"401":
|
|
491
|
+
$ref: "#/components/responses/Unauthorized"
|
|
492
|
+
"404":
|
|
493
|
+
$ref: "#/components/responses/NotFound"
|
|
494
|
+
"409":
|
|
495
|
+
description: |
|
|
496
|
+
The client must upgrade to read this log (`client_upgrade_required`), or the log is a
|
|
497
|
+
fork that is still being prepared (`fork_preparing`).
|
|
498
|
+
"426":
|
|
499
|
+
description: The request is not a WebSocket upgrade.
|
|
500
|
+
/admin/v1/apps:
|
|
501
|
+
get:
|
|
502
|
+
operationId: listApps
|
|
503
|
+
summary: List apps
|
|
504
|
+
description: Returns app lifecycle metadata for operators. Requires `ADMIN_TOKEN`.
|
|
505
|
+
security:
|
|
506
|
+
- adminBearerAuth: []
|
|
507
|
+
responses:
|
|
508
|
+
"200":
|
|
509
|
+
description: List of apps.
|
|
510
|
+
content:
|
|
511
|
+
application/json:
|
|
512
|
+
schema:
|
|
513
|
+
$ref: "#/components/schemas/AdminAppListResponse"
|
|
514
|
+
"401":
|
|
515
|
+
$ref: "#/components/responses/Unauthorized"
|
|
516
|
+
post:
|
|
517
|
+
operationId: createApp
|
|
518
|
+
summary: Create app
|
|
519
|
+
description: Creates an app and returns its newly generated app token exactly once.
|
|
520
|
+
security:
|
|
521
|
+
- adminBearerAuth: []
|
|
522
|
+
requestBody:
|
|
523
|
+
required: true
|
|
524
|
+
content:
|
|
525
|
+
application/json:
|
|
526
|
+
schema:
|
|
527
|
+
$ref: "#/components/schemas/AdminCreateAppRequest"
|
|
528
|
+
responses:
|
|
529
|
+
"201":
|
|
530
|
+
description: App created.
|
|
531
|
+
content:
|
|
532
|
+
application/json:
|
|
533
|
+
schema:
|
|
534
|
+
$ref: "#/components/schemas/AdminAppTokenResponse"
|
|
535
|
+
"400":
|
|
536
|
+
$ref: "#/components/responses/ValidationError"
|
|
537
|
+
"401":
|
|
538
|
+
$ref: "#/components/responses/Unauthorized"
|
|
539
|
+
/admin/v1/apps/{appId}/revoke:
|
|
540
|
+
parameters:
|
|
541
|
+
- $ref: "#/components/parameters/AppId"
|
|
542
|
+
post:
|
|
543
|
+
operationId: revokeApp
|
|
544
|
+
summary: Revoke app
|
|
545
|
+
description: Revokes the app and all active principals in that app.
|
|
546
|
+
security:
|
|
547
|
+
- adminBearerAuth: []
|
|
548
|
+
responses:
|
|
549
|
+
"200":
|
|
550
|
+
description: Revoked app summary.
|
|
551
|
+
content:
|
|
552
|
+
application/json:
|
|
553
|
+
schema:
|
|
554
|
+
$ref: "#/components/schemas/AdminAppSummary"
|
|
555
|
+
"401":
|
|
556
|
+
$ref: "#/components/responses/Unauthorized"
|
|
557
|
+
"404":
|
|
558
|
+
$ref: "#/components/responses/NotFound"
|
|
559
|
+
/admin/v1/apps/{appId}/logs:
|
|
560
|
+
parameters:
|
|
561
|
+
- $ref: "#/components/parameters/AppId"
|
|
562
|
+
get:
|
|
563
|
+
operationId: listAppLogsAsAdmin
|
|
564
|
+
summary: List an app's logs
|
|
565
|
+
description: Returns logical log metadata for an active or revoked app. Requires `ADMIN_TOKEN`.
|
|
566
|
+
security:
|
|
567
|
+
- adminBearerAuth: []
|
|
568
|
+
responses:
|
|
569
|
+
"200":
|
|
570
|
+
description: List of application logs.
|
|
571
|
+
content:
|
|
572
|
+
application/json:
|
|
573
|
+
schema:
|
|
574
|
+
$ref: "#/components/schemas/LogListResponse"
|
|
575
|
+
"401":
|
|
576
|
+
$ref: "#/components/responses/Unauthorized"
|
|
577
|
+
"404":
|
|
578
|
+
$ref: "#/components/responses/NotFound"
|
|
579
|
+
/admin/v1/apps/{appId}/logs/{logId}/lineage:
|
|
580
|
+
parameters:
|
|
581
|
+
- $ref: "#/components/parameters/AppId"
|
|
582
|
+
- $ref: "#/components/parameters/LogId"
|
|
583
|
+
post:
|
|
584
|
+
operationId: setRestoredLogLineage
|
|
585
|
+
summary: Restore a fork's source relationship
|
|
586
|
+
description: Admin-only logical backup restore step. Validates the child prefix against the mapped parent and rejects cycles.
|
|
587
|
+
security:
|
|
588
|
+
- adminBearerAuth: []
|
|
589
|
+
requestBody:
|
|
590
|
+
required: true
|
|
591
|
+
content:
|
|
592
|
+
application/json:
|
|
593
|
+
schema:
|
|
594
|
+
type: object
|
|
595
|
+
required: [sourceLogId, sourceVersion]
|
|
596
|
+
additionalProperties: false
|
|
597
|
+
properties:
|
|
598
|
+
sourceLogId: { type: string, format: uuid }
|
|
599
|
+
sourceVersion: { type: integer, minimum: 0 }
|
|
600
|
+
responses:
|
|
601
|
+
"200":
|
|
602
|
+
description: Updated log metadata.
|
|
603
|
+
content:
|
|
604
|
+
application/json:
|
|
605
|
+
schema:
|
|
606
|
+
$ref: "#/components/schemas/LogSummary"
|
|
607
|
+
"400":
|
|
608
|
+
$ref: "#/components/responses/ValidationError"
|
|
609
|
+
"401":
|
|
610
|
+
$ref: "#/components/responses/Unauthorized"
|
|
611
|
+
/admin/v1/apps/{appId}/connections:
|
|
612
|
+
parameters:
|
|
613
|
+
- $ref: "#/components/parameters/AppId"
|
|
614
|
+
get:
|
|
615
|
+
operationId: listAppConnectionsAsAdmin
|
|
616
|
+
summary: Inspect live and recent WebSocket connections
|
|
617
|
+
description: Samples the Durable Object runtime, including closing sockets. Client handshake metadata is descriptive, not authenticated identity. Cursor is the last event sent, not a client acknowledgement. Legacy metadata may be null. Connection changes between pages are reflected live. Every page also lists recently started sessions that are no longer attached; clients close their stream while hidden, so live sockets alone rarely show who uses an application.
|
|
618
|
+
security:
|
|
619
|
+
- adminBearerAuth: []
|
|
620
|
+
parameters:
|
|
621
|
+
- name: afterId
|
|
622
|
+
in: query
|
|
623
|
+
description: Exclusive connection ID cursor. Pages contain at most 100 connections.
|
|
624
|
+
schema: { type: string, format: uuid }
|
|
625
|
+
responses:
|
|
626
|
+
"200":
|
|
627
|
+
description: Live connection page.
|
|
628
|
+
content:
|
|
629
|
+
application/json:
|
|
630
|
+
schema:
|
|
631
|
+
$ref: "#/components/schemas/AdminConnectionPage"
|
|
632
|
+
"400":
|
|
633
|
+
$ref: "#/components/responses/ValidationError"
|
|
634
|
+
"401":
|
|
635
|
+
$ref: "#/components/responses/Unauthorized"
|
|
636
|
+
"404":
|
|
637
|
+
$ref: "#/components/responses/NotFound"
|
|
638
|
+
/admin/v1/apps/{appId}/export/sqlite:
|
|
639
|
+
parameters:
|
|
640
|
+
- $ref: "#/components/parameters/AppId"
|
|
641
|
+
get:
|
|
642
|
+
operationId: exportAppSqliteAsAdmin
|
|
643
|
+
summary: Export every Ledger-owned SQL table for an application
|
|
644
|
+
description: Streams a SQL dump of schema, metadata, logs, events, entity claims, migration records and idempotency rows. Includes revoked applications. Interleaved Ledger writes or schema changes abort the stream; incomplete downloads must be discarded. No storage mutation is performed.
|
|
645
|
+
security:
|
|
646
|
+
- adminBearerAuth: []
|
|
647
|
+
responses:
|
|
648
|
+
"200":
|
|
649
|
+
$ref: "#/components/responses/SqliteExport"
|
|
650
|
+
"401":
|
|
651
|
+
$ref: "#/components/responses/Unauthorized"
|
|
652
|
+
"404":
|
|
653
|
+
$ref: "#/components/responses/NotFound"
|
|
654
|
+
/admin/v1/registry/export/sqlite:
|
|
655
|
+
get:
|
|
656
|
+
operationId: exportRegistrySqliteAsAdmin
|
|
657
|
+
summary: Export all registry SQL tables
|
|
658
|
+
description: Includes all application registrations, provisioning state, principal metadata, migrations and indexes. The registry and each application are separate databases; separate downloads are not one atomic cross-database snapshot.
|
|
659
|
+
security:
|
|
660
|
+
- adminBearerAuth: []
|
|
661
|
+
responses:
|
|
662
|
+
"200":
|
|
663
|
+
$ref: "#/components/responses/SqliteExport"
|
|
664
|
+
"401":
|
|
665
|
+
$ref: "#/components/responses/Unauthorized"
|
|
666
|
+
/admin/v1/apps/{appId}/metrics:
|
|
667
|
+
parameters:
|
|
668
|
+
- $ref: "#/components/parameters/AppId"
|
|
669
|
+
get:
|
|
670
|
+
operationId: getAppMetricsAsAdmin
|
|
671
|
+
summary: Sample application storage and connection metrics
|
|
672
|
+
description: Includes active and revoked applications. Counts cover current logs only.
|
|
673
|
+
security:
|
|
674
|
+
- adminBearerAuth: []
|
|
675
|
+
responses:
|
|
676
|
+
"200":
|
|
677
|
+
description: A per-application snapshot, not request or billing analytics.
|
|
678
|
+
content:
|
|
679
|
+
application/json:
|
|
680
|
+
schema:
|
|
681
|
+
$ref: "#/components/schemas/AdminMetrics"
|
|
682
|
+
"401":
|
|
683
|
+
$ref: "#/components/responses/Unauthorized"
|
|
684
|
+
"404":
|
|
685
|
+
$ref: "#/components/responses/NotFound"
|
|
686
|
+
/admin/v1/apps/{appId}/logs/{logId}/events:
|
|
687
|
+
parameters:
|
|
688
|
+
- $ref: "#/components/parameters/AppId"
|
|
689
|
+
- $ref: "#/components/parameters/LogId"
|
|
690
|
+
get:
|
|
691
|
+
operationId: listLogEventsAsAdmin
|
|
692
|
+
summary: Browse all stored events in descending version order
|
|
693
|
+
description: Includes revoked applications. Search uses case-insensitive substrings over event type, entity ID and event ID across the selected log; payloads are not searched. Older pages exclude events appended after the first page was read.
|
|
694
|
+
security:
|
|
695
|
+
- adminBearerAuth: []
|
|
696
|
+
parameters:
|
|
697
|
+
- name: beforeVersion
|
|
698
|
+
in: query
|
|
699
|
+
description: Exclusive upper version bound; omit to start at the newest event.
|
|
700
|
+
schema:
|
|
701
|
+
type: integer
|
|
702
|
+
minimum: 1
|
|
703
|
+
maximum: 9007199254740991
|
|
704
|
+
- name: limit
|
|
705
|
+
in: query
|
|
706
|
+
schema:
|
|
707
|
+
type: integer
|
|
708
|
+
minimum: 1
|
|
709
|
+
maximum: 100
|
|
710
|
+
default: 50
|
|
711
|
+
- name: search
|
|
712
|
+
in: query
|
|
713
|
+
schema:
|
|
714
|
+
type: string
|
|
715
|
+
maxLength: 200
|
|
716
|
+
responses:
|
|
717
|
+
"200":
|
|
718
|
+
description: Event page; a null nextBeforeVersion means the end of the matching history.
|
|
719
|
+
content:
|
|
720
|
+
application/json:
|
|
721
|
+
schema:
|
|
722
|
+
$ref: "#/components/schemas/AdminEventPage"
|
|
723
|
+
"400":
|
|
724
|
+
description: Invalid pagination or search parameters.
|
|
725
|
+
"401":
|
|
726
|
+
$ref: "#/components/responses/Unauthorized"
|
|
727
|
+
"404":
|
|
728
|
+
$ref: "#/components/responses/NotFound"
|
|
729
|
+
/admin/v1/apps/{appId}/export:
|
|
730
|
+
parameters:
|
|
731
|
+
- $ref: "#/components/parameters/AppId"
|
|
732
|
+
get:
|
|
733
|
+
operationId: exportAppAsAdmin
|
|
734
|
+
summary: Download all logical app data
|
|
735
|
+
description: |
|
|
736
|
+
Streams a consistent JSON snapshot containing application metadata,
|
|
737
|
+
every log, and every event through each log's captured head version.
|
|
738
|
+
Authentication secrets and internal idempotency rows are not included.
|
|
739
|
+
security:
|
|
740
|
+
- adminBearerAuth: []
|
|
741
|
+
responses:
|
|
742
|
+
"200":
|
|
743
|
+
description: Streamed application export.
|
|
744
|
+
headers:
|
|
745
|
+
Content-Disposition:
|
|
746
|
+
schema:
|
|
747
|
+
type: string
|
|
748
|
+
description: Attachment filename for the JSON export.
|
|
749
|
+
content:
|
|
750
|
+
application/json:
|
|
751
|
+
schema:
|
|
752
|
+
$ref: "#/components/schemas/AdminApplicationExport"
|
|
753
|
+
"401":
|
|
754
|
+
$ref: "#/components/responses/Unauthorized"
|
|
755
|
+
"404":
|
|
756
|
+
$ref: "#/components/responses/NotFound"
|
|
757
|
+
/admin/v1/apps/{appId}/fence:
|
|
758
|
+
parameters:
|
|
759
|
+
- $ref: "#/components/parameters/AppId"
|
|
760
|
+
get:
|
|
761
|
+
operationId: getAppFence
|
|
762
|
+
summary: Get an app's write fence
|
|
763
|
+
security:
|
|
764
|
+
- adminBearerAuth: []
|
|
765
|
+
responses:
|
|
766
|
+
"200":
|
|
767
|
+
description: "`{ fence: null }` or `{ fence: { mode, destination } }`."
|
|
768
|
+
"401":
|
|
769
|
+
$ref: "#/components/responses/Unauthorized"
|
|
770
|
+
"404":
|
|
771
|
+
$ref: "#/components/responses/NotFound"
|
|
772
|
+
put:
|
|
773
|
+
operationId: setAppFence
|
|
774
|
+
summary: Set or lift an app's write fence
|
|
775
|
+
description: "`tenant_migrating` pauses writes (503, `Retry-After`); `moved` ends them (410 with the destination origin); `off` lifts the fence. Reads stay open."
|
|
776
|
+
security:
|
|
777
|
+
- adminBearerAuth: []
|
|
778
|
+
requestBody:
|
|
779
|
+
required: true
|
|
780
|
+
content:
|
|
781
|
+
application/json:
|
|
782
|
+
schema:
|
|
783
|
+
type: object
|
|
784
|
+
properties:
|
|
785
|
+
mode:
|
|
786
|
+
type: string
|
|
787
|
+
enum: [off, tenant_migrating, moved]
|
|
788
|
+
destination:
|
|
789
|
+
type: string
|
|
790
|
+
description: HTTPS origin; only with `moved`.
|
|
791
|
+
required:
|
|
792
|
+
- mode
|
|
793
|
+
additionalProperties: false
|
|
794
|
+
responses:
|
|
795
|
+
"200":
|
|
796
|
+
description: The fence now in force.
|
|
797
|
+
"400":
|
|
798
|
+
$ref: "#/components/responses/ValidationError"
|
|
799
|
+
"401":
|
|
800
|
+
$ref: "#/components/responses/Unauthorized"
|
|
801
|
+
"404":
|
|
802
|
+
$ref: "#/components/responses/NotFound"
|
|
803
|
+
/admin/v1/apps/{appId}/transfer:
|
|
804
|
+
parameters:
|
|
805
|
+
- $ref: "#/components/parameters/AppId"
|
|
806
|
+
get:
|
|
807
|
+
operationId: exportAppTransfer
|
|
808
|
+
summary: Export an app for an identity-preserving import
|
|
809
|
+
description: |
|
|
810
|
+
Streams NDJSON: the application, then for each log its record, its events
|
|
811
|
+
(with authors and action batches), and its idempotency records, then fork
|
|
812
|
+
requests and a totals line. Refused with `409 fork_preparing` while a fork is
|
|
813
|
+
still copying its files.
|
|
814
|
+
security:
|
|
815
|
+
- adminBearerAuth: []
|
|
816
|
+
responses:
|
|
817
|
+
"200":
|
|
818
|
+
description: The transfer stream.
|
|
819
|
+
content:
|
|
820
|
+
application/x-ndjson:
|
|
821
|
+
schema:
|
|
822
|
+
type: string
|
|
823
|
+
"401":
|
|
824
|
+
$ref: "#/components/responses/Unauthorized"
|
|
825
|
+
"404":
|
|
826
|
+
$ref: "#/components/responses/NotFound"
|
|
827
|
+
"409":
|
|
828
|
+
description: A fork is still preparing.
|
|
829
|
+
/admin/v1/apps/{appId}/checksums:
|
|
830
|
+
parameters:
|
|
831
|
+
- $ref: "#/components/parameters/AppId"
|
|
832
|
+
get:
|
|
833
|
+
operationId: getAppChecksums
|
|
834
|
+
summary: Per-log event checksums
|
|
835
|
+
description: One SHA-256 per log over its events in version order, with the head version and event count, for comparing a source with its imported copy.
|
|
836
|
+
security:
|
|
837
|
+
- adminBearerAuth: []
|
|
838
|
+
responses:
|
|
839
|
+
"200":
|
|
840
|
+
description: Checksums per log.
|
|
841
|
+
"401":
|
|
842
|
+
$ref: "#/components/responses/Unauthorized"
|
|
843
|
+
"404":
|
|
844
|
+
$ref: "#/components/responses/NotFound"
|
|
845
|
+
/admin/v1/import:
|
|
846
|
+
post:
|
|
847
|
+
operationId: beginImport
|
|
848
|
+
summary: Begin an identity-preserving import
|
|
849
|
+
description: Creates the application under its original id, empty and unusable (no token exists) until the import finishes. Accepted only by a Ledger with no application (`409 ledger_not_empty`).
|
|
850
|
+
security:
|
|
851
|
+
- adminBearerAuth: []
|
|
852
|
+
responses:
|
|
853
|
+
"201":
|
|
854
|
+
description: The application now awaits its records.
|
|
855
|
+
"401":
|
|
856
|
+
$ref: "#/components/responses/Unauthorized"
|
|
857
|
+
"409":
|
|
858
|
+
description: The Ledger already has an application.
|
|
859
|
+
/admin/v1/apps/{appId}/import:
|
|
860
|
+
parameters:
|
|
861
|
+
- $ref: "#/components/parameters/AppId"
|
|
862
|
+
post:
|
|
863
|
+
operationId: importRecords
|
|
864
|
+
summary: Import a chunk of transfer records
|
|
865
|
+
description: "`{ records: [...] }` taken from the export stream in order. Strictly validated: a gap in versions, an entity version that does not follow, or a log that does not exist yet is refused."
|
|
866
|
+
security:
|
|
867
|
+
- adminBearerAuth: []
|
|
868
|
+
responses:
|
|
869
|
+
"200":
|
|
870
|
+
description: Counts of what was imported.
|
|
871
|
+
"401":
|
|
872
|
+
$ref: "#/components/responses/Unauthorized"
|
|
873
|
+
"404":
|
|
874
|
+
$ref: "#/components/responses/NotFound"
|
|
875
|
+
"409":
|
|
876
|
+
description: Out of order, a gap, or not importing.
|
|
877
|
+
/admin/v1/apps/{appId}/import/abort:
|
|
878
|
+
parameters:
|
|
879
|
+
- $ref: "#/components/parameters/AppId"
|
|
880
|
+
post:
|
|
881
|
+
operationId: abortImport
|
|
882
|
+
summary: Abort an import in progress
|
|
883
|
+
description: Empties an application whose import failed so the Ledger is empty again and the import can start over. Refused once the import has finished.
|
|
884
|
+
security:
|
|
885
|
+
- adminBearerAuth: []
|
|
886
|
+
responses:
|
|
887
|
+
"204":
|
|
888
|
+
description: The half-imported application is gone.
|
|
889
|
+
"401":
|
|
890
|
+
$ref: "#/components/responses/Unauthorized"
|
|
891
|
+
"404":
|
|
892
|
+
$ref: "#/components/responses/NotFound"
|
|
893
|
+
/admin/v1/apps/{appId}/import/finish:
|
|
894
|
+
parameters:
|
|
895
|
+
- $ref: "#/components/parameters/AppId"
|
|
896
|
+
post:
|
|
897
|
+
operationId: finishImport
|
|
898
|
+
summary: Finish an import
|
|
899
|
+
description: "`{ logs: [{ id, headVersion }] }` as the source reported. The application opens only when every log holds exactly that many contiguous events."
|
|
900
|
+
security:
|
|
901
|
+
- adminBearerAuth: []
|
|
902
|
+
responses:
|
|
903
|
+
"200":
|
|
904
|
+
description: The application is active; mint its token with rotate-token.
|
|
905
|
+
"401":
|
|
906
|
+
$ref: "#/components/responses/Unauthorized"
|
|
907
|
+
"404":
|
|
908
|
+
$ref: "#/components/responses/NotFound"
|
|
909
|
+
"409":
|
|
910
|
+
description: The history is incomplete.
|
|
911
|
+
/admin/v1/apps/{appId}/policy:
|
|
912
|
+
parameters:
|
|
913
|
+
- $ref: "#/components/parameters/AppId"
|
|
914
|
+
get:
|
|
915
|
+
operationId: getAppPolicy
|
|
916
|
+
summary: Get an app's event policy
|
|
917
|
+
description: Returns the policy the app's appends are checked against, or `null` for an app created before policies existed (it accepts every module's events and runtime entity types, in the deployment's validation mode).
|
|
918
|
+
security:
|
|
919
|
+
- adminBearerAuth: []
|
|
920
|
+
responses:
|
|
921
|
+
"200":
|
|
922
|
+
description: The app's event policy.
|
|
923
|
+
content:
|
|
924
|
+
application/json:
|
|
925
|
+
schema:
|
|
926
|
+
type: object
|
|
927
|
+
properties:
|
|
928
|
+
policy:
|
|
929
|
+
oneOf:
|
|
930
|
+
- $ref: "#/components/schemas/AdminEventPolicy"
|
|
931
|
+
- type: "null"
|
|
932
|
+
required:
|
|
933
|
+
- policy
|
|
934
|
+
"401":
|
|
935
|
+
$ref: "#/components/responses/Unauthorized"
|
|
936
|
+
"404":
|
|
937
|
+
$ref: "#/components/responses/NotFound"
|
|
938
|
+
put:
|
|
939
|
+
operationId: setAppPolicy
|
|
940
|
+
summary: Set an app's event policy
|
|
941
|
+
description: Replaces the policy. Events already accepted are never re-judged; later appends follow it.
|
|
942
|
+
security:
|
|
943
|
+
- adminBearerAuth: []
|
|
944
|
+
requestBody:
|
|
945
|
+
required: true
|
|
946
|
+
content:
|
|
947
|
+
application/json:
|
|
948
|
+
schema:
|
|
949
|
+
$ref: "#/components/schemas/AdminEventPolicy"
|
|
950
|
+
responses:
|
|
951
|
+
"200":
|
|
952
|
+
description: The policy now in force.
|
|
953
|
+
content:
|
|
954
|
+
application/json:
|
|
955
|
+
schema:
|
|
956
|
+
$ref: "#/components/schemas/AdminEventPolicy"
|
|
957
|
+
"400":
|
|
958
|
+
$ref: "#/components/responses/ValidationError"
|
|
959
|
+
"401":
|
|
960
|
+
$ref: "#/components/responses/Unauthorized"
|
|
961
|
+
"404":
|
|
962
|
+
$ref: "#/components/responses/NotFound"
|
|
963
|
+
/admin/v1/apps/{appId}/rotate-token:
|
|
964
|
+
parameters:
|
|
965
|
+
- $ref: "#/components/parameters/AppId"
|
|
966
|
+
post:
|
|
967
|
+
operationId: rotateAppToken
|
|
968
|
+
summary: Rotate app token
|
|
969
|
+
description: Revokes the current app token and returns a new generated token exactly once.
|
|
970
|
+
security:
|
|
971
|
+
- adminBearerAuth: []
|
|
972
|
+
responses:
|
|
973
|
+
"200":
|
|
974
|
+
description: New app token returned once.
|
|
975
|
+
content:
|
|
976
|
+
application/json:
|
|
977
|
+
schema:
|
|
978
|
+
$ref: "#/components/schemas/AdminAppTokenResponse"
|
|
979
|
+
"400":
|
|
980
|
+
$ref: "#/components/responses/ValidationError"
|
|
981
|
+
"401":
|
|
982
|
+
$ref: "#/components/responses/Unauthorized"
|
|
983
|
+
"404":
|
|
984
|
+
$ref: "#/components/responses/NotFound"
|
|
985
|
+
components:
|
|
986
|
+
securitySchemes:
|
|
987
|
+
bearerAuth:
|
|
988
|
+
type: http
|
|
989
|
+
scheme: bearer
|
|
990
|
+
bearerFormat: opaque
|
|
991
|
+
adminBearerAuth:
|
|
992
|
+
type: http
|
|
993
|
+
scheme: bearer
|
|
994
|
+
bearerFormat: opaque
|
|
995
|
+
parameters:
|
|
996
|
+
LogId:
|
|
997
|
+
name: logId
|
|
998
|
+
in: path
|
|
999
|
+
required: true
|
|
1000
|
+
schema:
|
|
1001
|
+
type: string
|
|
1002
|
+
format: uuid
|
|
1003
|
+
description: Identifier of the log to operate on (as returned by `/logs`).
|
|
1004
|
+
AfterVersion:
|
|
1005
|
+
name: afterVersion
|
|
1006
|
+
in: query
|
|
1007
|
+
required: false
|
|
1008
|
+
schema:
|
|
1009
|
+
type: integer
|
|
1010
|
+
minimum: 0
|
|
1011
|
+
default: 0
|
|
1012
|
+
description: >
|
|
1013
|
+
Only return events with `version` greater than this number.
|
|
1014
|
+
Omit or set to 0 to begin with the first event.
|
|
1015
|
+
Limit:
|
|
1016
|
+
name: limit
|
|
1017
|
+
in: query
|
|
1018
|
+
required: false
|
|
1019
|
+
schema:
|
|
1020
|
+
type: integer
|
|
1021
|
+
minimum: 1
|
|
1022
|
+
default: 512
|
|
1023
|
+
description: Maximum events returned in one page. Values above 512 are clamped.
|
|
1024
|
+
AppId:
|
|
1025
|
+
name: appId
|
|
1026
|
+
in: path
|
|
1027
|
+
required: true
|
|
1028
|
+
schema:
|
|
1029
|
+
type: string
|
|
1030
|
+
format: uuid
|
|
1031
|
+
description: Identifier of the app.
|
|
1032
|
+
responses:
|
|
1033
|
+
SqliteExport:
|
|
1034
|
+
description: A streamed SQL dump, suitable for sqlite3 or incremental reconstruction by the admin UI. Contains statement-boundary comments and ends with the ledger-sqlite-export-complete marker. Excludes Cloudflare private tables, KV, WebSocket attachments, Worker secrets and PITR history. This is not a raw physical SQLite file.
|
|
1035
|
+
headers:
|
|
1036
|
+
Content-Disposition:
|
|
1037
|
+
description: Attachment filename ending in .sql.
|
|
1038
|
+
schema: { type: string }
|
|
1039
|
+
Cache-Control:
|
|
1040
|
+
schema: { type: string, const: no-store }
|
|
1041
|
+
content:
|
|
1042
|
+
application/sql:
|
|
1043
|
+
schema: { type: string }
|
|
1044
|
+
Unauthorized:
|
|
1045
|
+
description: Missing, invalid, or revoked bearer token.
|
|
1046
|
+
content:
|
|
1047
|
+
application/json:
|
|
1048
|
+
schema:
|
|
1049
|
+
$ref: "#/components/schemas/ErrorResponse"
|
|
1050
|
+
example:
|
|
1051
|
+
error: Unauthorized
|
|
1052
|
+
code: unauthorized
|
|
1053
|
+
message: Invalid bearer token
|
|
1054
|
+
NotFound:
|
|
1055
|
+
description: Log not found in the caller's app scope.
|
|
1056
|
+
content:
|
|
1057
|
+
application/json:
|
|
1058
|
+
schema:
|
|
1059
|
+
$ref: "#/components/schemas/ErrorResponse"
|
|
1060
|
+
example:
|
|
1061
|
+
error: NotFound
|
|
1062
|
+
code: log_not_found
|
|
1063
|
+
ValidationError:
|
|
1064
|
+
description: Request validation failed.
|
|
1065
|
+
content:
|
|
1066
|
+
application/json:
|
|
1067
|
+
schema:
|
|
1068
|
+
$ref: "#/components/schemas/ErrorResponse"
|
|
1069
|
+
example:
|
|
1070
|
+
error: ValidationError
|
|
1071
|
+
code: validation_error
|
|
1072
|
+
message: app name must not be empty
|
|
1073
|
+
schemas:
|
|
1074
|
+
AdminConnectionPage:
|
|
1075
|
+
type: object
|
|
1076
|
+
required: [sampledAt, total, unavailable, connections, nextAfterId, recent]
|
|
1077
|
+
properties:
|
|
1078
|
+
sampledAt: { type: string, format: date-time }
|
|
1079
|
+
total: { type: integer, minimum: 0 }
|
|
1080
|
+
unavailable:
|
|
1081
|
+
type: integer
|
|
1082
|
+
minimum: 0
|
|
1083
|
+
description: Attached sockets with unreadable metadata, omitted from the connection list.
|
|
1084
|
+
nextAfterId: { type: [string, "null"], format: uuid }
|
|
1085
|
+
connections:
|
|
1086
|
+
type: array
|
|
1087
|
+
maxItems: 100
|
|
1088
|
+
items:
|
|
1089
|
+
$ref: "#/components/schemas/AdminConnection"
|
|
1090
|
+
recent:
|
|
1091
|
+
type: array
|
|
1092
|
+
maxItems: 50
|
|
1093
|
+
description: Sessions no longer attached, newest connection first. Ledger keeps the 50 most recent sessions per application, including live ones; the list is the same on every page.
|
|
1094
|
+
items:
|
|
1095
|
+
$ref: "#/components/schemas/AdminRecentConnection"
|
|
1096
|
+
AdminConnection:
|
|
1097
|
+
type: object
|
|
1098
|
+
required: [id, logId, logName, principalId, generation, state, cursor, headVersion, connectedAt, lastSentAt, client]
|
|
1099
|
+
properties:
|
|
1100
|
+
id: { type: string, format: uuid }
|
|
1101
|
+
logId: { type: string, format: uuid }
|
|
1102
|
+
logName: { type: [string, "null"] }
|
|
1103
|
+
principalId: { type: string, format: uuid }
|
|
1104
|
+
generation: { type: integer, minimum: 1 }
|
|
1105
|
+
state: { type: string, enum: [connecting, open, closing, closed] }
|
|
1106
|
+
cursor: { type: integer, minimum: 0 }
|
|
1107
|
+
headVersion: { type: [integer, "null"], minimum: 0 }
|
|
1108
|
+
connectedAt: { type: [string, "null"], format: date-time }
|
|
1109
|
+
lastSentAt: { type: [string, "null"], format: date-time }
|
|
1110
|
+
client:
|
|
1111
|
+
type: object
|
|
1112
|
+
required: [userAgent, ip, country, colo]
|
|
1113
|
+
properties:
|
|
1114
|
+
userAgent: { type: [string, "null"] }
|
|
1115
|
+
ip: { type: [string, "null"] }
|
|
1116
|
+
country: { type: [string, "null"] }
|
|
1117
|
+
colo: { type: [string, "null"] }
|
|
1118
|
+
AdminRecentConnection:
|
|
1119
|
+
type: object
|
|
1120
|
+
required: [id, logId, logName, principalId, generation, cursor, connectedAt, disconnectedAt, closeCode, lastSentAt, client]
|
|
1121
|
+
properties:
|
|
1122
|
+
id: { type: string, format: uuid }
|
|
1123
|
+
logId: { type: string, format: uuid }
|
|
1124
|
+
logName: { type: [string, "null"] }
|
|
1125
|
+
principalId: { type: string, format: uuid }
|
|
1126
|
+
generation: { type: integer, minimum: 1 }
|
|
1127
|
+
cursor:
|
|
1128
|
+
type: integer
|
|
1129
|
+
minimum: 0
|
|
1130
|
+
description: Last event sent before the session ended.
|
|
1131
|
+
connectedAt: { type: string, format: date-time }
|
|
1132
|
+
disconnectedAt:
|
|
1133
|
+
type: [string, "null"]
|
|
1134
|
+
format: date-time
|
|
1135
|
+
description: Null when the session ended without a close notification, for example during a deployment.
|
|
1136
|
+
closeCode: { type: [integer, "null"], minimum: 0 }
|
|
1137
|
+
lastSentAt: { type: [string, "null"], format: date-time }
|
|
1138
|
+
client:
|
|
1139
|
+
type: object
|
|
1140
|
+
required: [userAgent, ip, country, colo]
|
|
1141
|
+
properties:
|
|
1142
|
+
userAgent: { type: [string, "null"] }
|
|
1143
|
+
ip: { type: [string, "null"] }
|
|
1144
|
+
country: { type: [string, "null"] }
|
|
1145
|
+
colo: { type: [string, "null"] }
|
|
1146
|
+
ErrorResponse:
|
|
1147
|
+
type: object
|
|
1148
|
+
properties:
|
|
1149
|
+
error:
|
|
1150
|
+
type: string
|
|
1151
|
+
code:
|
|
1152
|
+
type: string
|
|
1153
|
+
message:
|
|
1154
|
+
type:
|
|
1155
|
+
- string
|
|
1156
|
+
- "null"
|
|
1157
|
+
required:
|
|
1158
|
+
- error
|
|
1159
|
+
- code
|
|
1160
|
+
SessionResponse:
|
|
1161
|
+
type: object
|
|
1162
|
+
properties:
|
|
1163
|
+
appId:
|
|
1164
|
+
type: string
|
|
1165
|
+
format: uuid
|
|
1166
|
+
principalId:
|
|
1167
|
+
type: string
|
|
1168
|
+
format: uuid
|
|
1169
|
+
required:
|
|
1170
|
+
- appId
|
|
1171
|
+
- principalId
|
|
1172
|
+
AdminEventPolicy:
|
|
1173
|
+
type: object
|
|
1174
|
+
description: What an app may append. The core's events are always allowed.
|
|
1175
|
+
properties:
|
|
1176
|
+
contracts:
|
|
1177
|
+
oneOf:
|
|
1178
|
+
- type: array
|
|
1179
|
+
items:
|
|
1180
|
+
type: string
|
|
1181
|
+
description: A module id such as `org.stor.finance`, or `core`.
|
|
1182
|
+
maxItems: 64
|
|
1183
|
+
- type: "null"
|
|
1184
|
+
description: Modules whose event types are accepted; `null` accepts every module.
|
|
1185
|
+
dynamicEvents:
|
|
1186
|
+
type: boolean
|
|
1187
|
+
description: Accept runtime-defined entity types (`ENTITY_TYPE_DEFINED` and their events).
|
|
1188
|
+
validation:
|
|
1189
|
+
oneOf:
|
|
1190
|
+
- type: string
|
|
1191
|
+
enum: [report, enforce]
|
|
1192
|
+
- type: "null"
|
|
1193
|
+
description: How violations are handled; `null` follows the deployment's `EVENT_VALIDATION`.
|
|
1194
|
+
required:
|
|
1195
|
+
- contracts
|
|
1196
|
+
- dynamicEvents
|
|
1197
|
+
- validation
|
|
1198
|
+
additionalProperties: false
|
|
1199
|
+
AdminCreateAppRequest:
|
|
1200
|
+
type: object
|
|
1201
|
+
properties:
|
|
1202
|
+
name:
|
|
1203
|
+
type: string
|
|
1204
|
+
policy:
|
|
1205
|
+
$ref: "#/components/schemas/AdminEventPolicy"
|
|
1206
|
+
description: Omitted, the app starts with `{ contracts: null, dynamicEvents: false, validation: enforce }`.
|
|
1207
|
+
required:
|
|
1208
|
+
- name
|
|
1209
|
+
additionalProperties: false
|
|
1210
|
+
AdminAppSummary:
|
|
1211
|
+
type: object
|
|
1212
|
+
properties:
|
|
1213
|
+
id:
|
|
1214
|
+
type: string
|
|
1215
|
+
format: uuid
|
|
1216
|
+
name:
|
|
1217
|
+
type: string
|
|
1218
|
+
createdAt:
|
|
1219
|
+
type: string
|
|
1220
|
+
format: date-time
|
|
1221
|
+
revokedAt:
|
|
1222
|
+
type:
|
|
1223
|
+
- string
|
|
1224
|
+
- "null"
|
|
1225
|
+
format: date-time
|
|
1226
|
+
activeTokenCreatedAt:
|
|
1227
|
+
type:
|
|
1228
|
+
- string
|
|
1229
|
+
- "null"
|
|
1230
|
+
format: date-time
|
|
1231
|
+
required:
|
|
1232
|
+
- id
|
|
1233
|
+
- name
|
|
1234
|
+
- createdAt
|
|
1235
|
+
- revokedAt
|
|
1236
|
+
- activeTokenCreatedAt
|
|
1237
|
+
AdminAppListResponse:
|
|
1238
|
+
type: object
|
|
1239
|
+
properties:
|
|
1240
|
+
apps:
|
|
1241
|
+
type: array
|
|
1242
|
+
items:
|
|
1243
|
+
$ref: "#/components/schemas/AdminAppSummary"
|
|
1244
|
+
required:
|
|
1245
|
+
- apps
|
|
1246
|
+
AdminAppTokenResponse:
|
|
1247
|
+
type: object
|
|
1248
|
+
properties:
|
|
1249
|
+
app:
|
|
1250
|
+
$ref: "#/components/schemas/AdminAppSummary"
|
|
1251
|
+
token:
|
|
1252
|
+
type: string
|
|
1253
|
+
description: Newly generated raw token returned exactly once.
|
|
1254
|
+
required:
|
|
1255
|
+
- app
|
|
1256
|
+
- token
|
|
1257
|
+
AdminApplicationExport:
|
|
1258
|
+
type: object
|
|
1259
|
+
properties:
|
|
1260
|
+
format:
|
|
1261
|
+
type: string
|
|
1262
|
+
enum:
|
|
1263
|
+
- ledger-application-export
|
|
1264
|
+
version:
|
|
1265
|
+
type: integer
|
|
1266
|
+
enum:
|
|
1267
|
+
- 1
|
|
1268
|
+
exportedAt:
|
|
1269
|
+
type: string
|
|
1270
|
+
format: date-time
|
|
1271
|
+
application:
|
|
1272
|
+
$ref: "#/components/schemas/AdminAppSummary"
|
|
1273
|
+
logs:
|
|
1274
|
+
type: array
|
|
1275
|
+
items:
|
|
1276
|
+
$ref: "#/components/schemas/AdminExportedLog"
|
|
1277
|
+
required:
|
|
1278
|
+
- format
|
|
1279
|
+
- version
|
|
1280
|
+
- exportedAt
|
|
1281
|
+
- application
|
|
1282
|
+
- logs
|
|
1283
|
+
AdminExportedLog:
|
|
1284
|
+
allOf:
|
|
1285
|
+
- $ref: "#/components/schemas/LogSummary"
|
|
1286
|
+
- type: object
|
|
1287
|
+
properties:
|
|
1288
|
+
events:
|
|
1289
|
+
type: array
|
|
1290
|
+
items:
|
|
1291
|
+
$ref: "#/components/schemas/AppEvent"
|
|
1292
|
+
required:
|
|
1293
|
+
- events
|
|
1294
|
+
AdminMetrics:
|
|
1295
|
+
type: object
|
|
1296
|
+
additionalProperties: false
|
|
1297
|
+
required: [sampledAt, logCount, eventCount, entityCount, databaseBytes, connectedClients, lastUpdatedAt]
|
|
1298
|
+
properties:
|
|
1299
|
+
sampledAt:
|
|
1300
|
+
type: string
|
|
1301
|
+
format: date-time
|
|
1302
|
+
logCount:
|
|
1303
|
+
type: integer
|
|
1304
|
+
minimum: 0
|
|
1305
|
+
eventCount:
|
|
1306
|
+
type: integer
|
|
1307
|
+
minimum: 0
|
|
1308
|
+
entityCount:
|
|
1309
|
+
type: integer
|
|
1310
|
+
minimum: 0
|
|
1311
|
+
description: Entity streams counted separately in each log.
|
|
1312
|
+
databaseBytes:
|
|
1313
|
+
type: integer
|
|
1314
|
+
minimum: 0
|
|
1315
|
+
description: SQLite storage including indexes and other Ledger tables.
|
|
1316
|
+
connectedClients:
|
|
1317
|
+
type: integer
|
|
1318
|
+
minimum: 0
|
|
1319
|
+
description: Attached WebSocket connections at sampling time.
|
|
1320
|
+
lastUpdatedAt:
|
|
1321
|
+
type: [string, "null"]
|
|
1322
|
+
format: date-time
|
|
1323
|
+
AdminEventPage:
|
|
1324
|
+
type: object
|
|
1325
|
+
additionalProperties: false
|
|
1326
|
+
required: [events, headVersion, nextBeforeVersion]
|
|
1327
|
+
properties:
|
|
1328
|
+
events:
|
|
1329
|
+
type: array
|
|
1330
|
+
maxItems: 100
|
|
1331
|
+
items:
|
|
1332
|
+
$ref: "#/components/schemas/AppEvent"
|
|
1333
|
+
headVersion:
|
|
1334
|
+
type: integer
|
|
1335
|
+
minimum: 0
|
|
1336
|
+
nextBeforeVersion:
|
|
1337
|
+
type: [integer, "null"]
|
|
1338
|
+
minimum: 1
|
|
1339
|
+
LogListResponse:
|
|
1340
|
+
type: object
|
|
1341
|
+
properties:
|
|
1342
|
+
logs:
|
|
1343
|
+
type: array
|
|
1344
|
+
items:
|
|
1345
|
+
$ref: "#/components/schemas/LogSummary"
|
|
1346
|
+
lineage:
|
|
1347
|
+
type: object
|
|
1348
|
+
description: Server-ordered log roots and direct children for rendering fork lineage.
|
|
1349
|
+
properties:
|
|
1350
|
+
rootIds:
|
|
1351
|
+
type: array
|
|
1352
|
+
items: { type: string, format: uuid }
|
|
1353
|
+
childIdsByLogId:
|
|
1354
|
+
type: object
|
|
1355
|
+
additionalProperties:
|
|
1356
|
+
type: array
|
|
1357
|
+
items: { type: string, format: uuid }
|
|
1358
|
+
required: [rootIds, childIdsByLogId]
|
|
1359
|
+
additionalProperties: false
|
|
1360
|
+
required:
|
|
1361
|
+
- logs
|
|
1362
|
+
- lineage
|
|
1363
|
+
additionalProperties: false
|
|
1364
|
+
LogSummary:
|
|
1365
|
+
type: object
|
|
1366
|
+
properties:
|
|
1367
|
+
id:
|
|
1368
|
+
type: string
|
|
1369
|
+
format: uuid
|
|
1370
|
+
description: Server-generated log identifier.
|
|
1371
|
+
name:
|
|
1372
|
+
type: string
|
|
1373
|
+
description: Human-readable log name.
|
|
1374
|
+
headVersion:
|
|
1375
|
+
type: integer
|
|
1376
|
+
minimum: 0
|
|
1377
|
+
description: Current highest version in the log.
|
|
1378
|
+
createdAt:
|
|
1379
|
+
type: string
|
|
1380
|
+
format: date-time
|
|
1381
|
+
description: Timestamp when the log was created.
|
|
1382
|
+
lastUpdatedAt:
|
|
1383
|
+
type:
|
|
1384
|
+
- string
|
|
1385
|
+
- "null"
|
|
1386
|
+
format: date-time
|
|
1387
|
+
description: Timestamp of the most recent update.
|
|
1388
|
+
sourceLogId:
|
|
1389
|
+
type: [string, "null"]
|
|
1390
|
+
format: uuid
|
|
1391
|
+
sourceLogName:
|
|
1392
|
+
type: [string, "null"]
|
|
1393
|
+
sourceVersion:
|
|
1394
|
+
type: [integer, "null"]
|
|
1395
|
+
minimum: 0
|
|
1396
|
+
sourceTimestamp:
|
|
1397
|
+
type: [string, "null"]
|
|
1398
|
+
format: date-time
|
|
1399
|
+
hiddenAt:
|
|
1400
|
+
type: [string, "null"]
|
|
1401
|
+
format: date-time
|
|
1402
|
+
description: When the log was hidden from the log tree, or null.
|
|
1403
|
+
description: Timestamp of the accepted source event at sourceVersion.
|
|
1404
|
+
status:
|
|
1405
|
+
type: string
|
|
1406
|
+
enum: [ready, preparing]
|
|
1407
|
+
required:
|
|
1408
|
+
- id
|
|
1409
|
+
- name
|
|
1410
|
+
- headVersion
|
|
1411
|
+
- createdAt
|
|
1412
|
+
- lastUpdatedAt
|
|
1413
|
+
- sourceLogId
|
|
1414
|
+
- sourceLogName
|
|
1415
|
+
- sourceVersion
|
|
1416
|
+
- sourceTimestamp
|
|
1417
|
+
- status
|
|
1418
|
+
- hiddenAt
|
|
1419
|
+
LogVisibilityRequest:
|
|
1420
|
+
type: object
|
|
1421
|
+
properties:
|
|
1422
|
+
hidden:
|
|
1423
|
+
type: boolean
|
|
1424
|
+
required:
|
|
1425
|
+
- hidden
|
|
1426
|
+
additionalProperties: false
|
|
1427
|
+
ForkLogRequest:
|
|
1428
|
+
type: object
|
|
1429
|
+
properties:
|
|
1430
|
+
name:
|
|
1431
|
+
type: string
|
|
1432
|
+
minLength: 1
|
|
1433
|
+
targetVersion:
|
|
1434
|
+
type: integer
|
|
1435
|
+
minimum: 0
|
|
1436
|
+
expectedSourceHeadVersion:
|
|
1437
|
+
type: integer
|
|
1438
|
+
minimum: 0
|
|
1439
|
+
requestId:
|
|
1440
|
+
type: string
|
|
1441
|
+
format: uuid
|
|
1442
|
+
required: [name, targetVersion, expectedSourceHeadVersion, requestId]
|
|
1443
|
+
additionalProperties: false
|
|
1444
|
+
CreateLogRequest:
|
|
1445
|
+
type: object
|
|
1446
|
+
properties:
|
|
1447
|
+
name:
|
|
1448
|
+
type: string
|
|
1449
|
+
description: Optional display name for the log. If omitted, the service may assign one.
|
|
1450
|
+
additionalProperties: false
|
|
1451
|
+
CreateLogResponse:
|
|
1452
|
+
type: object
|
|
1453
|
+
properties:
|
|
1454
|
+
id:
|
|
1455
|
+
type: string
|
|
1456
|
+
format: uuid
|
|
1457
|
+
description: Newly created log identifier.
|
|
1458
|
+
name:
|
|
1459
|
+
type: string
|
|
1460
|
+
description: Effective log name (may be generated when none provided).
|
|
1461
|
+
headVersion:
|
|
1462
|
+
type: integer
|
|
1463
|
+
minimum: 0
|
|
1464
|
+
createdAt:
|
|
1465
|
+
type: string
|
|
1466
|
+
format: date-time
|
|
1467
|
+
required:
|
|
1468
|
+
- id
|
|
1469
|
+
- name
|
|
1470
|
+
- headVersion
|
|
1471
|
+
- createdAt
|
|
1472
|
+
LogMetadata:
|
|
1473
|
+
allOf:
|
|
1474
|
+
- $ref: "#/components/schemas/LogSummary"
|
|
1475
|
+
AppendEventsRequest:
|
|
1476
|
+
type: object
|
|
1477
|
+
properties:
|
|
1478
|
+
requestId:
|
|
1479
|
+
type: string
|
|
1480
|
+
format: uuid
|
|
1481
|
+
description: Client-generated UUID used for idempotency.
|
|
1482
|
+
events:
|
|
1483
|
+
type: array
|
|
1484
|
+
minItems: 1
|
|
1485
|
+
maxItems: 512
|
|
1486
|
+
description: Events to append (in desired order).
|
|
1487
|
+
items:
|
|
1488
|
+
$ref: "#/components/schemas/IncomingEvent"
|
|
1489
|
+
required:
|
|
1490
|
+
- requestId
|
|
1491
|
+
- events
|
|
1492
|
+
additionalProperties: false
|
|
1493
|
+
IncomingEvent:
|
|
1494
|
+
type: object
|
|
1495
|
+
properties:
|
|
1496
|
+
id:
|
|
1497
|
+
type:
|
|
1498
|
+
- string
|
|
1499
|
+
- "null"
|
|
1500
|
+
format: uuid
|
|
1501
|
+
description: Optional event identifier supplied by the client. If omitted, the service generates one.
|
|
1502
|
+
type:
|
|
1503
|
+
type: string
|
|
1504
|
+
description: Domain event type (e.g., `TRANSACTION_ADDED`).
|
|
1505
|
+
entityId:
|
|
1506
|
+
type: string
|
|
1507
|
+
description: Identifier of the entity affected by the event. Required for all events.
|
|
1508
|
+
baseEntityVersion:
|
|
1509
|
+
type:
|
|
1510
|
+
- integer
|
|
1511
|
+
- "null"
|
|
1512
|
+
format: int64
|
|
1513
|
+
minimum: 0
|
|
1514
|
+
description: |
|
|
1515
|
+
Expected entity version immediately before this event. For an existing entity at version
|
|
1516
|
+
`N`, repeated events in one request use `N`, `N+1`, and so on. For a new entity, omit the
|
|
1517
|
+
first base and use `1`, `2`, and so on for later events in the same request.
|
|
1518
|
+
timestamp:
|
|
1519
|
+
type:
|
|
1520
|
+
- integer
|
|
1521
|
+
- "null"
|
|
1522
|
+
format: int64
|
|
1523
|
+
minimum: 0
|
|
1524
|
+
maximum: 8640000000000000
|
|
1525
|
+
description: Optional client timestamp in milliseconds since Unix epoch.
|
|
1526
|
+
payload:
|
|
1527
|
+
type: object
|
|
1528
|
+
description: Event payload to persist as-is.
|
|
1529
|
+
required:
|
|
1530
|
+
- type
|
|
1531
|
+
- entityId
|
|
1532
|
+
- payload
|
|
1533
|
+
additionalProperties: false
|
|
1534
|
+
AppendEventsResponse:
|
|
1535
|
+
type: object
|
|
1536
|
+
properties:
|
|
1537
|
+
newVersion:
|
|
1538
|
+
type: integer
|
|
1539
|
+
minimum: 0
|
|
1540
|
+
description: Version assigned to the last persisted event.
|
|
1541
|
+
events:
|
|
1542
|
+
type: array
|
|
1543
|
+
description: Events actually stored (with server-assigned ids/timestamps/versions).
|
|
1544
|
+
items:
|
|
1545
|
+
$ref: "#/components/schemas/AppEvent"
|
|
1546
|
+
required:
|
|
1547
|
+
- newVersion
|
|
1548
|
+
- events
|
|
1549
|
+
ConflictResponse:
|
|
1550
|
+
type: object
|
|
1551
|
+
properties:
|
|
1552
|
+
code:
|
|
1553
|
+
type: string
|
|
1554
|
+
const: entity_conflict
|
|
1555
|
+
entityId:
|
|
1556
|
+
type: string
|
|
1557
|
+
description: The entity identifier that caused the conflict.
|
|
1558
|
+
currentEntityVersion:
|
|
1559
|
+
type: integer
|
|
1560
|
+
format: int64
|
|
1561
|
+
minimum: 1
|
|
1562
|
+
description: Current version of the conflicting entity. Client should replay missing log events and retry with the correct baseEntityVersion.
|
|
1563
|
+
currentLogVersion:
|
|
1564
|
+
type: integer
|
|
1565
|
+
minimum: 0
|
|
1566
|
+
description: Current head version of the entire log (for context).
|
|
1567
|
+
required:
|
|
1568
|
+
- code
|
|
1569
|
+
- entityId
|
|
1570
|
+
- currentEntityVersion
|
|
1571
|
+
- currentLogVersion
|
|
1572
|
+
EntityVersionChainErrorResponse:
|
|
1573
|
+
type: object
|
|
1574
|
+
properties:
|
|
1575
|
+
error:
|
|
1576
|
+
type: string
|
|
1577
|
+
const: ValidationError
|
|
1578
|
+
code:
|
|
1579
|
+
type: string
|
|
1580
|
+
const: invalid_entity_version_chain
|
|
1581
|
+
message:
|
|
1582
|
+
type: string
|
|
1583
|
+
entityId:
|
|
1584
|
+
type: string
|
|
1585
|
+
expectedBaseEntityVersion:
|
|
1586
|
+
type:
|
|
1587
|
+
- integer
|
|
1588
|
+
- "null"
|
|
1589
|
+
format: int64
|
|
1590
|
+
actualBaseEntityVersion:
|
|
1591
|
+
type:
|
|
1592
|
+
- integer
|
|
1593
|
+
- "null"
|
|
1594
|
+
format: int64
|
|
1595
|
+
required:
|
|
1596
|
+
- error
|
|
1597
|
+
- code
|
|
1598
|
+
- message
|
|
1599
|
+
- entityId
|
|
1600
|
+
- expectedBaseEntityVersion
|
|
1601
|
+
- actualBaseEntityVersion
|
|
1602
|
+
EventBatch:
|
|
1603
|
+
type: object
|
|
1604
|
+
properties:
|
|
1605
|
+
events:
|
|
1606
|
+
type: array
|
|
1607
|
+
items:
|
|
1608
|
+
$ref: "#/components/schemas/AppEvent"
|
|
1609
|
+
required:
|
|
1610
|
+
- events
|
|
1611
|
+
AppEvent:
|
|
1612
|
+
type: object
|
|
1613
|
+
properties:
|
|
1614
|
+
version:
|
|
1615
|
+
type: integer
|
|
1616
|
+
minimum: 1
|
|
1617
|
+
description: Monotonic sequence number within the log (global log version).
|
|
1618
|
+
id:
|
|
1619
|
+
type: string
|
|
1620
|
+
format: uuid
|
|
1621
|
+
description: Event identifier (typically a UUID).
|
|
1622
|
+
type:
|
|
1623
|
+
type: string
|
|
1624
|
+
description: Domain event type (e.g., `TRANSACTION_ADDED`, `ASSET_UPDATED`).
|
|
1625
|
+
entityId:
|
|
1626
|
+
type: string
|
|
1627
|
+
description: Identifier of the entity associated with the event.
|
|
1628
|
+
entityVersion:
|
|
1629
|
+
type: integer
|
|
1630
|
+
format: int64
|
|
1631
|
+
minimum: 1
|
|
1632
|
+
description: Monotonic sequence number specific to this entity. Starts at 1 for each entity's first event.
|
|
1633
|
+
timestamp:
|
|
1634
|
+
type: integer
|
|
1635
|
+
format: int64
|
|
1636
|
+
description: Milliseconds since Unix epoch when the event was committed.
|
|
1637
|
+
payload:
|
|
1638
|
+
type: object
|
|
1639
|
+
description: Event-specific data; mirrors the client’s expectations.
|
|
1640
|
+
actor:
|
|
1641
|
+
type: string
|
|
1642
|
+
maxLength: 320
|
|
1643
|
+
description: Identity the gateway verified for the request that wrote the event. Absent for events accepted without one.
|
|
1644
|
+
required:
|
|
1645
|
+
- version
|
|
1646
|
+
- id
|
|
1647
|
+
- type
|
|
1648
|
+
- entityId
|
|
1649
|
+
- entityVersion
|
|
1650
|
+
- timestamp
|
|
1651
|
+
- payload
|