-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathopenapi.yaml
More file actions
557 lines (530 loc) · 20.2 KB
/
Copy pathopenapi.yaml
File metadata and controls
557 lines (530 loc) · 20.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
openapi: 3.1.0
info:
title: Sluice Matcher API
version: 0.1.0
summary: HTTP API exposed by the off-chain Sluice matcher process.
description: |
The matcher serves three concerns over HTTP:
1. **`/snapshot.json`**, read-only, no auth: the matcher's current view of
the contract (subscriptions + recent deliveries). Polled by the dashboard
and the landing page every 5 s.
2. **`/tx/build/*`**, read-only, no auth: build an unsigned `TransactionV1`
JSON ready to be signed by a Casper Wallet against the on-chain
`SubscriptionRegistry`. Wraps `casper-client make-transaction package`
and returns the JSON.
3. **`/tx/submit`** + **`/tx/replay`**, write paths. `submit` accepts a
signed `TransactionV1` and forwards it to the chain via
`casper-client send-transaction`. `replay` re-fires a past delivery's
webhook from the in-memory `recent_events` buffer.
All endpoints return `application/json` with permissive CORS. The matcher
listens on `127.0.0.1:7799` and Caddy reverse-proxies `/api/tx/*` from
`sluice.unitynodes.com`; the snapshot file is served by Caddy directly.
servers:
- url: https://sluice.unitynodes.com/api
description: Live testnet matcher
- url: http://127.0.0.1:7799
description: Local matcher
paths:
/snapshot.json:
get:
summary: Current matcher snapshot
description: The matcher's view of the contract. Written every 30 s.
responses:
'200':
description: Snapshot JSON.
content:
application/json:
schema: { $ref: '#/components/schemas/Snapshot' }
/badge.svg:
get:
summary: Status badge (SVG)
description: 'Shields-style badge, "sluice: N on-chain · M active".'
responses:
'200':
description: Static SVG.
content:
image/svg+xml: {}
/tx/build/create-subscription:
post:
summary: Build an unsigned create_subscription transaction
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [initiator, predicate_json, webhook_url, motes]
properties:
initiator: { type: string, description: 64-hex public key with algo prefix byte (01=ed25519, 02=secp256k1) }
predicate_json: { type: string, description: JSON-stringified predicate (AND-of-fields, six ops) }
webhook_url: { type: string, format: uri }
motes: { type: string, description: CSPR amount to lock, in motes (1 CSPR = 1e9 motes) }
responses:
'200': { description: Unsigned tx ready for the wallet, content: { application/json: { schema: { $ref: '#/components/schemas/UnsignedTxResponse' } } } }
'400': { description: invalid args }
/tx/build/top-up:
post:
summary: Build an unsigned top_up transaction
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [initiator, id, motes]
properties:
initiator: { type: string }
id: { type: integer, minimum: 1 }
motes: { type: string }
responses:
'200': { description: Unsigned tx, content: { application/json: { schema: { $ref: '#/components/schemas/UnsignedTxResponse' } } } }
/tx/build/cancel:
post:
summary: Build an unsigned cancel_subscription transaction
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [initiator, id]
properties:
initiator: { type: string }
id: { type: integer, minimum: 1 }
responses:
'200': { description: Unsigned tx, content: { application/json: { schema: { $ref: '#/components/schemas/UnsignedTxResponse' } } } }
/tx/submit:
post:
summary: Submit a signed transaction
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [signed_tx]
properties:
signed_tx:
type: object
description: Full Version1 transaction JSON with `approvals[]` populated.
responses:
'200':
description: Transaction hash from the node.
content:
application/json:
schema:
type: object
properties: { tx_hash: { type: string } }
'400': { description: build / send failed }
/tx/replay:
post:
summary: Re-fire a past delivery's webhook
description: |
Looks up the event in the matcher's in-memory `recent_events` ring
(capacity 20). Re-POSTs to the original webhook URL with the same
`Idempotency-Key`; the receiver can dedupe.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [event_hash]
properties:
event_hash: { type: string }
responses:
'200':
description: Replay result.
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
statusCode: { type: integer }
attempts: { type: integer }
latency_ms: { type: integer }
'400': { description: event not in buffer }
/tx/test-webhook:
post:
summary: Fire a synthetic Transfer at one subscription's webhook (no on-chain effect)
requestBody:
required: true
content: { application/json: { schema: { type: object, required: [subscription_id], properties: { subscription_id: { type: integer } } } } }
responses:
'200': { description: dispatch result }
'400': { description: subscription not in matcher view }
/predicate/validate:
post:
summary: Dry-run a predicate against the matcher's recent-events ring buffer
requestBody:
required: true
content:
application/json:
schema:
oneOf:
- { type: object, required: [predicate], properties: { predicate: { $ref: '#/components/schemas/Predicate' } } }
- { type: object, required: [predicate_json], properties: { predicate_json: { type: string } } }
responses:
'200':
description: how many recent events would have matched
content:
application/json:
schema:
type: object
properties:
matches: { type: integer }
total_scanned: { type: integer }
sample_matches: { type: array, items: { $ref: '#/components/schemas/TransferEvent' } }
time_window_seconds: { type: integer }
estimated_per_day: { type: number, nullable: true }
source: { type: string, enum: [live, sample, mixed] }
/predicate/from-prompt:
post:
summary: Turn plain English into a predicate
description: |
Deterministic parser, no LLM involved. Turns phrases like
"whales over 100k CSPR to my treasury" into a predicate the engine
accepts, and reports which parts it understood.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [prompt]
properties:
prompt: { type: string, maxLength: 600, description: Plain-English description of the events to match }
responses:
'200':
description: Parsed predicate plus a human-readable explanation of what was understood.
content:
application/json:
schema:
type: object
properties:
predicate: { $ref: '#/components/schemas/Predicate' }
'400': { description: prompt missing, empty, or longer than 600 characters }
'501': { description: parser not wired on this matcher }
/sandbox/dispatch:
post:
summary: Fire test webhooks with no on-chain effect
description: |
POSTs up to 10 events at a URL so you can develop a receiver without
spending CSPR. Real buffered events are used where the optional
predicate matches; the rest are synthetic. Signed with the matcher's
HMAC secret when one is configured. The target passes the same SSRF
guard as live delivery, so loopback and private ranges are rejected.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [webhook]
properties:
webhook: { type: string, format: uri, description: Public http/https URL to POST to }
predicate: { $ref: '#/components/schemas/Predicate' }
count: { type: integer, minimum: 1, maximum: 10, default: 3 }
responses:
'200': { description: per-event dispatch results }
'400': { description: webhook missing or not an http/https URL }
'501': { description: sandbox not wired on this matcher }
/hooks/{slug}:
parameters:
- name: slug
in: path
required: true
schema: { type: string, pattern: '^[a-z0-9-]{4,64}$' }
description: Any slug you choose. The bucket is created on first use.
post:
summary: Throwaway webhook receiver
description: |
A catch-all inbox for testing deliveries without standing up a server.
Point a subscription at it, then read the captured requests back from
the feed endpoint. Buckets are in-memory, capped, and expire.
responses:
'200': { description: request captured }
'400': { description: malformed slug }
/hooks/{slug}/feed:
parameters:
- name: slug
in: path
required: true
schema: { type: string, pattern: '^[a-z0-9-]{4,64}$' }
get:
summary: Read what a throwaway receiver captured
responses:
'200':
description: Captured requests, newest first.
content:
application/json:
schema:
type: object
properties:
id: { type: string }
requests: { type: array, items: { type: object } }
max: { type: integer, description: bucket capacity }
ttl_seconds: { type: integer }
/predicate/explain:
post:
summary: Evaluate every condition independently against one event, return pass/fail trace
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [predicate, event]
properties:
predicate: { $ref: '#/components/schemas/Predicate' }
event: { $ref: '#/components/schemas/TransferEvent' }
responses:
'200':
description: per-condition trace
content:
application/json:
schema:
type: object
properties:
match: { type: boolean }
conditions_total: { type: integer }
conditions_passed: { type: integer }
trace:
type: array
items:
type: object
properties:
index: { type: integer }
field: { type: string }
op: { type: string }
expected: {}
actual: {}
pass: { type: boolean }
reason: { type: string }
/sub/{id}.ics:
get:
summary: iCalendar feed for one subscription (weekly check-in + balance-runout estimate + delivery milestones)
parameters:
- { name: id, in: path, required: true, schema: { type: integer } }
responses:
'200': { description: text/calendar, content: { text/calendar: { schema: { type: string } } } }
'404': { description: subscription not in matcher view }
/sub/{id}/replay-last:
post:
summary: Re-dispatch the last N deliveries for one subscription (no on-chain re-record)
parameters:
- { name: id, in: path, required: true, schema: { type: integer } }
requestBody:
required: false
content:
application/json:
schema:
type: object
properties: { n: { type: integer, minimum: 1, maximum: 20, default: 10 } }
responses:
'200':
description: per-replay outcomes
content:
application/json:
schema:
type: object
properties:
requested: { type: integer }
found: { type: integer }
results:
type: array
items:
type: object
properties:
event_hash: { type: string }
ok: { type: boolean }
statusCode: { type: integer }
attempts: { type: integer }
latency_ms: { type: integer }
timestamp: { type: string, format: date-time }
/og/sub/{id}:
# Served from the site root, not behind the /api prefix the other paths use.
servers:
- url: https://sluice.unitynodes.com
description: Live testnet matcher (root-mounted routes)
- url: http://127.0.0.1:7799
get:
summary: 1200x630 OG card SVG for a subscription (image/svg+xml)
parameters:
- { name: id, in: path, required: true, schema: { type: integer } }
responses:
'200': { description: SVG, content: { image/svg+xml: { schema: { type: string } } } }
'404': { description: subscription not in matcher view }
/embed/sub/{id}:
# Served from the site root, not behind the /api prefix the other paths use.
servers:
- url: https://sluice.unitynodes.com
description: Live testnet matcher (root-mounted routes)
- url: http://127.0.0.1:7799
get:
summary: 320x120 iframe-embed HTML widget for a subscription
parameters:
- { name: id, in: path, required: true, schema: { type: integer } }
responses:
'200': { description: HTML, content: { text/html: { schema: { type: string } } } }
'404': { description: subscription not in matcher view }
/badges/{metric}.svg:
get:
summary: Shields-style badge for a live metric
parameters:
- name: metric
in: path
required: true
schema: { type: string, enum: [subs-active, deliveries, delivery-success, latency-p95, uptime, ws] }
responses:
'200': { description: SVG }
'404': { description: unknown metric (response lists available) }
/metrics:
get:
summary: Prometheus exposition format
responses:
'200':
description: text/plain
content:
text/plain:
schema: { type: string, example: 'sluice_deliveries_total 42\nsluice_ws_connected{stream="transfers"} 1' }
/chain/head:
get:
summary: Cached Casper chain-head info (3s TTL, in-flight collapsed)
responses:
'200':
description: chain head
content:
application/json:
schema:
type: object
properties:
height: { type: integer }
era: { type: integer }
timestamp: { type: string, format: date-time }
chain: { type: string }
fetched_at: { type: string, format: date-time }
/stream:
get:
summary: 'WebSocket fan-out of live matcher events (use ws://, not http://; ?sub=N filter)'
description: |
Upgrade-only endpoint. Emits a hello envelope on connect, then
`{type:'delivery', data:RecentEvent}` for every webhook dispatch and
`{type:'subs.reload', data:{active,total,updated_at}}` on every poll.
Server pings every 25s; clients SHOULD reconnect on close.
responses:
'101': { description: WebSocket upgrade }
/openapi.yaml:
get:
summary: This document (so codegen tools can curl it directly)
responses:
'200':
description: this OpenAPI document
content:
application/yaml:
schema: { type: string }
/health:
get:
summary: Liveness probe
responses:
'200':
description: ok
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
contract: { type: string }
chain: { type: string }
post:
summary: Liveness probe
requestBody:
required: true
content: { application/json: { schema: { type: object } } }
responses:
'200':
description: ok
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
contract: { type: string }
chain: { type: string }
components:
schemas:
Snapshot:
type: object
properties:
contract_hash: { type: string }
chain: { type: string, enum: [casper, casper-test] }
updated_at: { type: string, format: date-time }
subscriptions:
type: array
items: { $ref: '#/components/schemas/Subscription' }
recent_events:
type: array
items: { $ref: '#/components/schemas/RecentEvent' }
Subscription:
type: object
properties:
id: { type: integer }
owner: { type: string }
predicate: { $ref: '#/components/schemas/Predicate' }
webhook_url: { type: string }
balance: { type: string, description: motes }
deliveries: { type: integer }
active: { type: boolean }
created_at: { type: integer }
RecentEvent:
type: object
properties:
subscription_id: { type: integer }
event_hash: { type: string }
description: { type: string }
status: { type: integer }
attempts: { type: integer }
latency_ms: { type: integer }
timestamp: { type: string, format: date-time }
tx_hash: { type: string }
event: { $ref: '#/components/schemas/TransferEvent' }
webhook_url: { type: string }
TransferEvent:
type: object
properties:
id: { type: integer }
deploy_hash: { type: string }
block_height: { type: integer }
transfer_index: { type: integer }
initiator_account_hash: { type: string }
from_purse: { type: string }
to_purse: { type: string }
to_account_hash: { type: string }
amount: { type: string, description: motes }
timestamp: { type: string, format: date-time }
Predicate:
type: object
properties:
and:
type: array
items: { $ref: '#/components/schemas/Condition' }
Condition:
type: object
properties:
field: { type: string, description: dot.notation path into a TransferEvent }
op: { type: string, enum: [eq, neq, gt, gte, lt, lte, contains, starts_with, ends_with, in, not_in, regex] }
value:
oneOf:
- { type: string }
- { type: number }
- { type: boolean }
- { type: array }
UnsignedTxResponse:
type: object
properties:
tx:
type: object
description: Casper Version1 transaction JSON, ready to sign in a wallet.