@infinitetoken/lumber-kit 0.6.0 → 0.7.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.
@@ -0,0 +1,2273 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://unpkg.com/@infinitetoken/lumber-kit/contract/schema.json",
4
+ "title": "Lumber-Kit",
5
+ "description": "Every type Lumber-Kit exports, generated from its TypeScript. A log's POST /logs body is LogIngestBody, and the plaintext a plugin encrypts into it is LogPayload.",
6
+ "definitions": {
7
+ "Appearance": {
8
+ "type": "string",
9
+ "enum": [
10
+ "system",
11
+ "dark",
12
+ "light"
13
+ ]
14
+ },
15
+ "PushToken": {
16
+ "type": "object",
17
+ "properties": {
18
+ "token": {
19
+ "type": "string"
20
+ },
21
+ "deviceId": {
22
+ "type": "string"
23
+ },
24
+ "platform": {
25
+ "type": "string",
26
+ "enum": [
27
+ "ios",
28
+ "android",
29
+ "web"
30
+ ]
31
+ },
32
+ "timeZone": {
33
+ "type": "string",
34
+ "description": "The device's IANA time zone, so a push can say a log's time as the device would."
35
+ }
36
+ },
37
+ "required": [
38
+ "token"
39
+ ],
40
+ "additionalProperties": false
41
+ },
42
+ "NotificationPreference": {
43
+ "type": "object",
44
+ "properties": {
45
+ "enabled": {
46
+ "type": "boolean"
47
+ },
48
+ "minimumLevel": {
49
+ "type": "number"
50
+ },
51
+ "notes": {
52
+ "type": "boolean",
53
+ "description": "Whether new notes on this app push, over the account's `notes`; missing follows the account's."
54
+ },
55
+ "lastReadAt": {
56
+ "type": "string"
57
+ }
58
+ },
59
+ "additionalProperties": false
60
+ },
61
+ "NotificationPreferences": {
62
+ "type": "object",
63
+ "properties": {
64
+ "enabled": {
65
+ "type": "boolean"
66
+ },
67
+ "minimumLevel": {
68
+ "type": "number"
69
+ },
70
+ "notes": {
71
+ "type": "boolean",
72
+ "description": "Whether new notes on the user's apps push; on unless false."
73
+ },
74
+ "perApp": {
75
+ "type": "object",
76
+ "additionalProperties": {
77
+ "$ref": "#/definitions/NotificationPreference"
78
+ }
79
+ }
80
+ },
81
+ "additionalProperties": false
82
+ },
83
+ "UserRecord": {
84
+ "type": "object",
85
+ "properties": {
86
+ "id": {
87
+ "type": "string"
88
+ },
89
+ "createdAt": {
90
+ "type": "string"
91
+ },
92
+ "updatedAt": {
93
+ "type": "string"
94
+ },
95
+ "email": {
96
+ "type": "string"
97
+ },
98
+ "name": {
99
+ "type": "string"
100
+ },
101
+ "publicKey": {
102
+ "type": "string"
103
+ },
104
+ "appearance": {
105
+ "$ref": "#/definitions/Appearance"
106
+ },
107
+ "color": {
108
+ "type": "string"
109
+ },
110
+ "imageUrl": {
111
+ "type": "string"
112
+ },
113
+ "agreedToTermsVersion": {
114
+ "type": "string"
115
+ },
116
+ "notificationPreferences": {
117
+ "$ref": "#/definitions/NotificationPreferences"
118
+ },
119
+ "pushTokens": {
120
+ "type": "array",
121
+ "items": {
122
+ "$ref": "#/definitions/PushToken"
123
+ }
124
+ },
125
+ "emailVerifiedAt": {
126
+ "type": [
127
+ "string",
128
+ "null"
129
+ ],
130
+ "description": "When the address was confirmed, null until it is. Missing from an API before email confirmation."
131
+ },
132
+ "notesReadAt": {
133
+ "type": "object",
134
+ "additionalProperties": {
135
+ "type": "string"
136
+ },
137
+ "description": "Per app, when the user last had its notes open, from any device. Missing from an API before it was kept."
138
+ }
139
+ },
140
+ "required": [
141
+ "id",
142
+ "createdAt",
143
+ "updatedAt",
144
+ "email",
145
+ "notificationPreferences",
146
+ "pushTokens"
147
+ ],
148
+ "additionalProperties": false,
149
+ "description": "Public user profile returned from API."
150
+ },
151
+ "TimeseriesBucket": {
152
+ "type": "object",
153
+ "properties": {
154
+ "date": {
155
+ "type": "string"
156
+ },
157
+ "count": {
158
+ "type": "number"
159
+ },
160
+ "byLevel": {
161
+ "type": "object",
162
+ "additionalProperties": {
163
+ "type": "number"
164
+ }
165
+ }
166
+ },
167
+ "required": [
168
+ "date",
169
+ "count"
170
+ ],
171
+ "additionalProperties": false,
172
+ "description": "A single time bucket in a timeseries response. date is an ISO date string (YYYY-MM-DD for 'day', ISO datetime for 'hour'). byLevel optionally breaks down the count per LogLevel for severity-tinted rendering."
173
+ },
174
+ "TimeseriesResponse": {
175
+ "type": "object",
176
+ "properties": {
177
+ "buckets": {
178
+ "type": "array",
179
+ "items": {
180
+ "$ref": "#/definitions/TimeseriesBucket"
181
+ }
182
+ },
183
+ "from": {
184
+ "type": "string"
185
+ },
186
+ "to": {
187
+ "type": "string"
188
+ },
189
+ "interval": {
190
+ "type": "string",
191
+ "enum": [
192
+ "day",
193
+ "hour"
194
+ ]
195
+ }
196
+ },
197
+ "required": [
198
+ "buckets",
199
+ "from",
200
+ "to",
201
+ "interval"
202
+ ],
203
+ "additionalProperties": false,
204
+ "description": "Generic timeseries aggregate response. Reusable across logs, notifications, messages, etc."
205
+ },
206
+ "LogTimeseriesQuery": {
207
+ "type": "object",
208
+ "properties": {
209
+ "appIds": {
210
+ "type": "array",
211
+ "items": {
212
+ "type": "string"
213
+ }
214
+ },
215
+ "levels": {
216
+ "type": "array",
217
+ "items": {
218
+ "type": "number"
219
+ }
220
+ },
221
+ "from": {
222
+ "type": "string"
223
+ },
224
+ "to": {
225
+ "type": "string"
226
+ },
227
+ "interval": {
228
+ "type": "string",
229
+ "enum": [
230
+ "day",
231
+ "hour"
232
+ ]
233
+ }
234
+ },
235
+ "additionalProperties": false,
236
+ "description": "Filter params for GET /logs/timeseries."
237
+ },
238
+ "LogLevel": {
239
+ "type": "number",
240
+ "enum": [
241
+ 0,
242
+ 1,
243
+ 2,
244
+ 3,
245
+ 4
246
+ ],
247
+ "description": "Log severity level. Numeric values match the existing Lumber-API level field."
248
+ },
249
+ "LogLevelName": {
250
+ "type": "string",
251
+ "enum": [
252
+ "debug",
253
+ "info",
254
+ "warn",
255
+ "error",
256
+ "fatal"
257
+ ],
258
+ "description": "String name for each log level."
259
+ },
260
+ "ApiErrorCode": {
261
+ "type": "string",
262
+ "enum": [
263
+ "APP_LIMIT",
264
+ "SEAT_LIMIT",
265
+ "GUEST_LIMIT",
266
+ "ALREADY_MEMBER",
267
+ "ALREADY_READS",
268
+ "ALREADY_INVITED",
269
+ "HAS_ACCOUNT"
270
+ ]
271
+ },
272
+ "PushThreshold": {
273
+ "anyOf": [
274
+ {
275
+ "$ref": "#/definitions/LogLevel"
276
+ },
277
+ {
278
+ "type": "number",
279
+ "const": 5
280
+ }
281
+ ],
282
+ "description": "The lowest log level that pushes, or PUSH_OFF. Stored as a notification preference's `minimumLevel`."
283
+ },
284
+ "SubscriptionLevel": {
285
+ "type": "number",
286
+ "enum": [
287
+ 0,
288
+ 1,
289
+ 2,
290
+ 3
291
+ ],
292
+ "description": "User subscription level. Mirrors User.subscriptionLevel in Lumber-API."
293
+ },
294
+ "SubscriptionLevelName": {
295
+ "type": "string",
296
+ "enum": [
297
+ "hobby",
298
+ "dev",
299
+ "pro",
300
+ "enterprise"
301
+ ],
302
+ "description": "String name for each subscription level, suitable for display and Redux state."
303
+ },
304
+ "SubscriptionLimits": {
305
+ "type": "object",
306
+ "properties": {
307
+ "apps": {
308
+ "type": "number"
309
+ },
310
+ "seats": {
311
+ "type": "number"
312
+ },
313
+ "apiKeys": {
314
+ "type": "number"
315
+ },
316
+ "logRetentionDays": {
317
+ "type": "number"
318
+ },
319
+ "monthlyLogs": {
320
+ "type": "number"
321
+ },
322
+ "realtimeLogs": {
323
+ "type": "boolean",
324
+ "description": "Whether real-time WebSocket log streaming is available."
325
+ },
326
+ "minPushLevel": {
327
+ "type": "number",
328
+ "description": "Minimum log level that triggers a push notification. Infinity means push is disabled."
329
+ }
330
+ },
331
+ "required": [
332
+ "apps",
333
+ "seats",
334
+ "apiKeys",
335
+ "logRetentionDays",
336
+ "monthlyLogs",
337
+ "realtimeLogs",
338
+ "minPushLevel"
339
+ ],
340
+ "additionalProperties": false
341
+ },
342
+ "SubscriptionPlan": {
343
+ "type": "object",
344
+ "properties": {
345
+ "name": {
346
+ "type": "string",
347
+ "description": "Display name, as every surface shows it."
348
+ },
349
+ "monthlyPrice": {
350
+ "type": [
351
+ "number",
352
+ "null"
353
+ ],
354
+ "description": "Monthly price in US dollars: 0 for free, null when priced per customer."
355
+ }
356
+ },
357
+ "required": [
358
+ "name",
359
+ "monthlyPrice"
360
+ ],
361
+ "additionalProperties": false
362
+ },
363
+ "TeamRecord": {
364
+ "type": "object",
365
+ "properties": {
366
+ "id": {
367
+ "type": "string"
368
+ },
369
+ "createdAt": {
370
+ "type": "string"
371
+ },
372
+ "updatedAt": {
373
+ "type": "string"
374
+ },
375
+ "name": {
376
+ "type": "string"
377
+ },
378
+ "color": {
379
+ "type": "string"
380
+ },
381
+ "imageUrl": {
382
+ "type": "string"
383
+ },
384
+ "subscriptionLevel": {
385
+ "$ref": "#/definitions/SubscriptionLevel"
386
+ },
387
+ "subscriptionEnd": {
388
+ "type": [
389
+ "string",
390
+ "null"
391
+ ],
392
+ "description": "When a canceled plan stops; null while it renews."
393
+ },
394
+ "role": {
395
+ "type": "string",
396
+ "enum": [
397
+ "owner",
398
+ "admin",
399
+ "member"
400
+ ],
401
+ "description": "GET /teams only: the caller's role on the team."
402
+ },
403
+ "memberId": {
404
+ "type": "string",
405
+ "description": "GET /teams only: the caller's membership id, which leaving the team deletes."
406
+ },
407
+ "appCount": {
408
+ "type": [
409
+ "number",
410
+ "null"
411
+ ],
412
+ "description": "GET /teams only: the team's apps, as creating one counts them. Null unless the caller is its owner or an admin."
413
+ },
414
+ "logsThisMonth": {
415
+ "type": [
416
+ "number",
417
+ "null"
418
+ ],
419
+ "description": "GET /teams only: logs its apps sent this UTC month, the monthly cap's count. Null unless owner or admin."
420
+ }
421
+ },
422
+ "required": [
423
+ "id",
424
+ "createdAt",
425
+ "updatedAt",
426
+ "name",
427
+ "subscriptionLevel"
428
+ ],
429
+ "additionalProperties": false,
430
+ "description": "An organization that owns apps and pays for member usage. Team subscriptionLevel takes precedence over individual member levels."
431
+ },
432
+ "TeamMemberRecord": {
433
+ "type": "object",
434
+ "properties": {
435
+ "id": {
436
+ "type": "string"
437
+ },
438
+ "createdAt": {
439
+ "type": "string"
440
+ },
441
+ "updatedAt": {
442
+ "type": "string"
443
+ },
444
+ "teamId": {
445
+ "type": "string"
446
+ },
447
+ "userId": {
448
+ "type": "string"
449
+ },
450
+ "role": {
451
+ "type": "string",
452
+ "enum": [
453
+ "owner",
454
+ "admin",
455
+ "member"
456
+ ]
457
+ },
458
+ "name": {
459
+ "type": [
460
+ "string",
461
+ "null"
462
+ ],
463
+ "description": "Listed members only: their display name. Never their email address."
464
+ },
465
+ "handle": {
466
+ "type": [
467
+ "string",
468
+ "null"
469
+ ],
470
+ "description": "Listed members only: the part of their email before the"
471
+ },
472
+ "color": {
473
+ "type": [
474
+ "string",
475
+ "null"
476
+ ]
477
+ },
478
+ "imageUrl": {
479
+ "type": [
480
+ "string",
481
+ "null"
482
+ ]
483
+ }
484
+ },
485
+ "required": [
486
+ "id",
487
+ "createdAt",
488
+ "updatedAt",
489
+ "teamId",
490
+ "userId",
491
+ "role"
492
+ ],
493
+ "additionalProperties": false,
494
+ "description": "Membership of a user in a team."
495
+ },
496
+ "SubscriptionPlanRecord": {
497
+ "type": "object",
498
+ "properties": {
499
+ "name": {
500
+ "type": "string"
501
+ },
502
+ "priceId": {
503
+ "type": [
504
+ "string",
505
+ "null"
506
+ ]
507
+ }
508
+ },
509
+ "required": [
510
+ "name",
511
+ "priceId"
512
+ ],
513
+ "additionalProperties": false,
514
+ "description": "A plan GET /subscriptions offers, with its Stripe price (none for Hobby)."
515
+ },
516
+ "TeamsQuery": {
517
+ "type": "object",
518
+ "properties": {
519
+ "sort": {
520
+ "type": "string"
521
+ },
522
+ "limit": {
523
+ "type": "number"
524
+ },
525
+ "cursor": {
526
+ "type": "string"
527
+ }
528
+ },
529
+ "additionalProperties": false,
530
+ "description": "Filter params for GET /teams."
531
+ },
532
+ "AppEncryptedKey": {
533
+ "type": "object",
534
+ "properties": {
535
+ "userId": {
536
+ "type": "string"
537
+ },
538
+ "encryptedKey": {
539
+ "type": "string"
540
+ },
541
+ "keyVersion": {
542
+ "type": "number"
543
+ }
544
+ },
545
+ "required": [
546
+ "userId",
547
+ "encryptedKey",
548
+ "keyVersion"
549
+ ],
550
+ "additionalProperties": false,
551
+ "description": "Wrapped application encryption key for a specific user."
552
+ },
553
+ "AppTeamAccess": {
554
+ "type": "string",
555
+ "enum": [
556
+ "everyone",
557
+ "owner"
558
+ ],
559
+ "description": "Who on an app's team can read it: everyone on the team, or only its owner. Its guests read it either way."
560
+ },
561
+ "AppRecord": {
562
+ "type": "object",
563
+ "properties": {
564
+ "id": {
565
+ "type": "string"
566
+ },
567
+ "createdAt": {
568
+ "type": "string"
569
+ },
570
+ "updatedAt": {
571
+ "type": "string"
572
+ },
573
+ "name": {
574
+ "type": "string"
575
+ },
576
+ "color": {
577
+ "type": "string"
578
+ },
579
+ "imageUrl": {
580
+ "type": "string"
581
+ },
582
+ "teamId": {
583
+ "type": "string"
584
+ },
585
+ "encryptedKey": {
586
+ "type": [
587
+ "string",
588
+ "null"
589
+ ]
590
+ },
591
+ "teamAccess": {
592
+ "$ref": "#/definitions/AppTeamAccess",
593
+ "description": "Missing means 'everyone'."
594
+ },
595
+ "retentionDays": {
596
+ "type": [
597
+ "number",
598
+ "null"
599
+ ],
600
+ "description": "How many days of its logs the app's team's plan keeps, null for no limit (and when missing)."
601
+ },
602
+ "logsCappedUntil": {
603
+ "type": [
604
+ "string",
605
+ "null"
606
+ ],
607
+ "description": "While the team has sent its plan's monthly logs, when new ones are taken again (the next UTC month); else null."
608
+ }
609
+ },
610
+ "required": [
611
+ "id",
612
+ "createdAt",
613
+ "updatedAt",
614
+ "name",
615
+ "encryptedKey"
616
+ ],
617
+ "additionalProperties": false,
618
+ "description": "API response shape for a listed or fetched app. encryptedKey is the requesting user's wrapped copy of the app key, or null if none exists. The optional fields are missing from an API deployed before them, which clients read as the default."
619
+ },
620
+ "AppCreatedRecord": {
621
+ "type": "object",
622
+ "properties": {
623
+ "id": {
624
+ "type": "string"
625
+ },
626
+ "createdAt": {
627
+ "type": "string"
628
+ },
629
+ "updatedAt": {
630
+ "type": "string"
631
+ },
632
+ "name": {
633
+ "type": "string"
634
+ },
635
+ "color": {
636
+ "type": "string"
637
+ },
638
+ "imageUrl": {
639
+ "type": "string"
640
+ },
641
+ "teamId": {
642
+ "type": "string"
643
+ },
644
+ "encryptedKey": {
645
+ "type": [
646
+ "string",
647
+ "null"
648
+ ]
649
+ },
650
+ "teamAccess": {
651
+ "$ref": "#/definitions/AppTeamAccess",
652
+ "description": "Missing means 'everyone'."
653
+ },
654
+ "retentionDays": {
655
+ "type": [
656
+ "number",
657
+ "null"
658
+ ],
659
+ "description": "How many days of its logs the app's team's plan keeps, null for no limit (and when missing)."
660
+ },
661
+ "logsCappedUntil": {
662
+ "type": [
663
+ "string",
664
+ "null"
665
+ ],
666
+ "description": "While the team has sent its plan's monthly logs, when new ones are taken again (the next UTC month); else null."
667
+ },
668
+ "apiKey": {
669
+ "type": "string"
670
+ }
671
+ },
672
+ "additionalProperties": false,
673
+ "required": [
674
+ "createdAt",
675
+ "encryptedKey",
676
+ "id",
677
+ "name",
678
+ "updatedAt"
679
+ ],
680
+ "description": "App creation response. Initial wrapped encryption key may optionally be returned."
681
+ },
682
+ "ApiKeyRecord": {
683
+ "type": "object",
684
+ "properties": {
685
+ "id": {
686
+ "type": "string"
687
+ },
688
+ "createdAt": {
689
+ "type": "string"
690
+ },
691
+ "updatedAt": {
692
+ "type": "string"
693
+ },
694
+ "appId": {
695
+ "type": "string"
696
+ },
697
+ "name": {
698
+ "type": "string"
699
+ }
700
+ },
701
+ "required": [
702
+ "id",
703
+ "createdAt",
704
+ "updatedAt",
705
+ "appId"
706
+ ],
707
+ "additionalProperties": false,
708
+ "description": "Machine authentication credential."
709
+ },
710
+ "GuestRecord": {
711
+ "type": "object",
712
+ "properties": {
713
+ "id": {
714
+ "type": "string"
715
+ },
716
+ "createdAt": {
717
+ "type": "string"
718
+ },
719
+ "updatedAt": {
720
+ "type": "string"
721
+ },
722
+ "appId": {
723
+ "type": "string"
724
+ },
725
+ "userId": {
726
+ "type": "string"
727
+ },
728
+ "role": {
729
+ "type": "string",
730
+ "enum": [
731
+ "owner",
732
+ "admin",
733
+ "read",
734
+ "write"
735
+ ]
736
+ }
737
+ },
738
+ "required": [
739
+ "id",
740
+ "createdAt",
741
+ "updatedAt",
742
+ "appId",
743
+ "userId",
744
+ "role"
745
+ ],
746
+ "additionalProperties": false,
747
+ "description": "External guest granted access to a specific app without being a team member."
748
+ },
749
+ "AppPermission": {
750
+ "type": "string",
751
+ "enum": [
752
+ "read",
753
+ "write",
754
+ "manage"
755
+ ],
756
+ "description": "What someone may do on an app. manage: the team's owner or admins, or a guest invited as owner or admin. write: a team member or a write guest. read: any other access."
757
+ },
758
+ "AppPersonRecord": {
759
+ "type": "object",
760
+ "properties": {
761
+ "userId": {
762
+ "type": "string"
763
+ },
764
+ "name": {
765
+ "type": [
766
+ "string",
767
+ "null"
768
+ ]
769
+ },
770
+ "handle": {
771
+ "type": [
772
+ "string",
773
+ "null"
774
+ ],
775
+ "description": "The part of their email before the"
776
+ },
777
+ "color": {
778
+ "type": [
779
+ "string",
780
+ "null"
781
+ ]
782
+ },
783
+ "imageUrl": {
784
+ "type": [
785
+ "string",
786
+ "null"
787
+ ],
788
+ "description": "Their picture URL, or null for their initial."
789
+ },
790
+ "role": {
791
+ "type": "string",
792
+ "enum": [
793
+ "owner",
794
+ "admin",
795
+ "member",
796
+ "read",
797
+ "write"
798
+ ]
799
+ },
800
+ "kind": {
801
+ "type": "string",
802
+ "enum": [
803
+ "team",
804
+ "guest"
805
+ ]
806
+ }
807
+ },
808
+ "required": [
809
+ "userId",
810
+ "name",
811
+ "color",
812
+ "role",
813
+ "kind"
814
+ ],
815
+ "additionalProperties": false,
816
+ "description": "Someone who can read an app, as the others see them (GET /apps/:id/people): never their email address."
817
+ },
818
+ "AppsQuery": {
819
+ "type": "object",
820
+ "properties": {
821
+ "teamId": {
822
+ "type": "string"
823
+ },
824
+ "from": {
825
+ "type": "string"
826
+ },
827
+ "to": {
828
+ "type": "string"
829
+ },
830
+ "sort": {
831
+ "type": "string"
832
+ },
833
+ "limit": {
834
+ "type": "number"
835
+ },
836
+ "cursor": {
837
+ "type": "string"
838
+ }
839
+ },
840
+ "additionalProperties": false,
841
+ "description": "Filter params for GET /apps."
842
+ },
843
+ "FlagRecord": {
844
+ "type": "object",
845
+ "properties": {
846
+ "id": {
847
+ "type": "string"
848
+ },
849
+ "createdAt": {
850
+ "type": "string"
851
+ },
852
+ "updatedAt": {
853
+ "type": "string"
854
+ },
855
+ "logId": {
856
+ "type": "string"
857
+ },
858
+ "userId": {
859
+ "type": "string"
860
+ }
861
+ },
862
+ "required": [
863
+ "id",
864
+ "createdAt",
865
+ "updatedAt",
866
+ "logId",
867
+ "userId"
868
+ ],
869
+ "additionalProperties": false
870
+ },
871
+ "FlagsQuery": {
872
+ "type": "object",
873
+ "properties": {
874
+ "from": {
875
+ "type": "string"
876
+ },
877
+ "to": {
878
+ "type": "string"
879
+ },
880
+ "limit": {
881
+ "type": "number"
882
+ },
883
+ "cursor": {
884
+ "type": "string"
885
+ }
886
+ },
887
+ "additionalProperties": false
888
+ },
889
+ "LogContext": {
890
+ "type": "object",
891
+ "properties": {
892
+ "code": {
893
+ "type": "string"
894
+ },
895
+ "method": {
896
+ "type": "string"
897
+ },
898
+ "line": {
899
+ "type": "number"
900
+ },
901
+ "column": {
902
+ "type": "number"
903
+ },
904
+ "note": {
905
+ "type": "string"
906
+ }
907
+ },
908
+ "additionalProperties": {},
909
+ "description": "Structured log context content."
910
+ },
911
+ "LogEncrypted": {
912
+ "type": "object",
913
+ "properties": {
914
+ "nonce": {
915
+ "type": "string"
916
+ },
917
+ "payload": {
918
+ "type": "string"
919
+ },
920
+ "keyVersion": {
921
+ "type": "number"
922
+ }
923
+ },
924
+ "required": [
925
+ "nonce",
926
+ "payload",
927
+ "keyVersion"
928
+ ],
929
+ "additionalProperties": false,
930
+ "description": "Encrypted envelope stored on the server. payload = base64(authTag + ciphertext) nonce = base64(random IV) keyVersion enables future rotation support."
931
+ },
932
+ "LogPayload": {
933
+ "type": "object",
934
+ "properties": {
935
+ "level": {
936
+ "$ref": "#/definitions/LogLevel"
937
+ },
938
+ "message": {
939
+ "type": "string"
940
+ },
941
+ "requestId": {
942
+ "type": "string"
943
+ },
944
+ "traceId": {
945
+ "type": "string"
946
+ },
947
+ "context": {
948
+ "$ref": "#/definitions/LogContext"
949
+ },
950
+ "metadata": {
951
+ "type": "object",
952
+ "additionalProperties": {}
953
+ }
954
+ },
955
+ "required": [
956
+ "level",
957
+ "message"
958
+ ],
959
+ "additionalProperties": false,
960
+ "description": "Plaintext shape of log content: a plugin encrypts it into LogEncrypted, and the server never sees it."
961
+ },
962
+ "LogData": {
963
+ "type": "object",
964
+ "properties": {
965
+ "requestId": {
966
+ "type": "string"
967
+ },
968
+ "traceId": {
969
+ "type": "string"
970
+ },
971
+ "context": {
972
+ "$ref": "#/definitions/LogContext"
973
+ },
974
+ "metadata": {
975
+ "type": "object",
976
+ "additionalProperties": {}
977
+ }
978
+ },
979
+ "additionalProperties": {},
980
+ "description": "What a plugin's log call takes beside its message (Lumber-Node's second argument). requestId and traceId become the payload's own fields, context is merged over the plugin's automatic context, and any other key goes into metadata, where an explicit metadata key wins. A context or metadata that isn't an object is kept as an ordinary metadata key."
981
+ },
982
+ "LogIngestBody": {
983
+ "type": "object",
984
+ "properties": {
985
+ "level": {
986
+ "$ref": "#/definitions/LogLevel"
987
+ },
988
+ "encrypted": {
989
+ "$ref": "#/definitions/LogEncrypted"
990
+ }
991
+ },
992
+ "required": [
993
+ "level",
994
+ "encrypted"
995
+ ],
996
+ "additionalProperties": false,
997
+ "description": "POST /logs' body. The level stays plaintext for filtering and pushes; everything else is in `encrypted`."
998
+ },
999
+ "LogDeliveryOutcome": {
1000
+ "type": "string",
1001
+ "enum": [
1002
+ "sent",
1003
+ "retry",
1004
+ "drop",
1005
+ "pause"
1006
+ ],
1007
+ "description": "What a plugin does with a log after POST /logs answers (see logDelivery)."
1008
+ },
1009
+ "LogRecord": {
1010
+ "type": "object",
1011
+ "properties": {
1012
+ "id": {
1013
+ "type": "string"
1014
+ },
1015
+ "createdAt": {
1016
+ "type": "string"
1017
+ },
1018
+ "updatedAt": {
1019
+ "type": "string"
1020
+ },
1021
+ "appId": {
1022
+ "type": "string"
1023
+ },
1024
+ "level": {
1025
+ "$ref": "#/definitions/LogLevel"
1026
+ },
1027
+ "encrypted": {
1028
+ "$ref": "#/definitions/LogEncrypted"
1029
+ }
1030
+ },
1031
+ "required": [
1032
+ "id",
1033
+ "createdAt",
1034
+ "updatedAt",
1035
+ "appId",
1036
+ "level",
1037
+ "encrypted"
1038
+ ],
1039
+ "additionalProperties": false,
1040
+ "description": "API response shape for a stored log. level is plaintext for server-side notification routing; all content is inside encrypted."
1041
+ },
1042
+ "LogsQuery": {
1043
+ "type": "object",
1044
+ "properties": {
1045
+ "appIds": {
1046
+ "type": "array",
1047
+ "items": {
1048
+ "type": "string"
1049
+ }
1050
+ },
1051
+ "levels": {
1052
+ "type": "array",
1053
+ "items": {
1054
+ "type": "number"
1055
+ }
1056
+ },
1057
+ "from": {
1058
+ "type": "string"
1059
+ },
1060
+ "to": {
1061
+ "type": "string"
1062
+ },
1063
+ "sort": {
1064
+ "type": "string"
1065
+ },
1066
+ "limit": {
1067
+ "type": "number"
1068
+ },
1069
+ "cursor": {
1070
+ "type": "string"
1071
+ }
1072
+ },
1073
+ "additionalProperties": false,
1074
+ "description": "Sync-protocol params for GET /logs."
1075
+ },
1076
+ "NoteEncrypted": {
1077
+ "type": "object",
1078
+ "properties": {
1079
+ "payload": {
1080
+ "type": "string"
1081
+ },
1082
+ "nonce": {
1083
+ "type": "string"
1084
+ },
1085
+ "keyVersion": {
1086
+ "type": "number"
1087
+ }
1088
+ },
1089
+ "required": [
1090
+ "payload",
1091
+ "nonce",
1092
+ "keyVersion"
1093
+ ],
1094
+ "additionalProperties": false,
1095
+ "description": "Encrypted note content. payload = base64(authTag + ciphertext) nonce = base64(random IV) keyVersion enables future key rotation."
1096
+ },
1097
+ "NoteRecord": {
1098
+ "type": "object",
1099
+ "properties": {
1100
+ "id": {
1101
+ "type": "string"
1102
+ },
1103
+ "createdAt": {
1104
+ "type": "string"
1105
+ },
1106
+ "updatedAt": {
1107
+ "type": "string"
1108
+ },
1109
+ "appId": {
1110
+ "type": "string"
1111
+ },
1112
+ "userId": {
1113
+ "type": "string"
1114
+ },
1115
+ "logId": {
1116
+ "type": "string"
1117
+ },
1118
+ "encrypted": {
1119
+ "$ref": "#/definitions/NoteEncrypted"
1120
+ }
1121
+ },
1122
+ "required": [
1123
+ "id",
1124
+ "createdAt",
1125
+ "updatedAt",
1126
+ "appId",
1127
+ "userId",
1128
+ "encrypted"
1129
+ ],
1130
+ "additionalProperties": false,
1131
+ "description": "A short user-authored note, encrypted with the app key. Visible to all members of the associated app. Query by teamId in NotesQuery to build a cross-app team feed."
1132
+ },
1133
+ "NotesQuery": {
1134
+ "type": "object",
1135
+ "properties": {
1136
+ "appId": {
1137
+ "type": "string"
1138
+ },
1139
+ "teamId": {
1140
+ "type": "string"
1141
+ },
1142
+ "userId": {
1143
+ "type": "string"
1144
+ },
1145
+ "logId": {
1146
+ "type": "string"
1147
+ },
1148
+ "from": {
1149
+ "type": "string"
1150
+ },
1151
+ "to": {
1152
+ "type": "string"
1153
+ },
1154
+ "sort": {
1155
+ "type": "string"
1156
+ },
1157
+ "limit": {
1158
+ "type": "number"
1159
+ },
1160
+ "cursor": {
1161
+ "type": "string"
1162
+ }
1163
+ },
1164
+ "additionalProperties": false,
1165
+ "description": "Filter params for GET /notes."
1166
+ },
1167
+ "SocketAppPayload": {
1168
+ "type": "object",
1169
+ "properties": {
1170
+ "id": {
1171
+ "type": "string"
1172
+ },
1173
+ "name": {
1174
+ "type": "string"
1175
+ },
1176
+ "teamId": {
1177
+ "type": "string"
1178
+ },
1179
+ "color": {
1180
+ "type": "string"
1181
+ },
1182
+ "imageUrl": {
1183
+ "type": "string"
1184
+ },
1185
+ "createdAt": {
1186
+ "type": "string"
1187
+ },
1188
+ "updatedAt": {
1189
+ "type": "string"
1190
+ }
1191
+ },
1192
+ "required": [
1193
+ "id",
1194
+ "name",
1195
+ "createdAt",
1196
+ "updatedAt"
1197
+ ],
1198
+ "additionalProperties": false,
1199
+ "description": "An app as the socket sends it: no wrapped keys, so a client refetches through the API for its own copy."
1200
+ },
1201
+ "ServerToClientEvents": {
1202
+ "type": "object",
1203
+ "properties": {
1204
+ "log:created": {
1205
+ "$comment": "(log: Omit<LogRecord, 'updatedAt'> & { updatedAt?: string }) => void",
1206
+ "type": "object",
1207
+ "properties": {
1208
+ "namedArgs": {
1209
+ "type": "object",
1210
+ "properties": {
1211
+ "log": {
1212
+ "type": "object",
1213
+ "additionalProperties": false,
1214
+ "properties": {
1215
+ "updatedAt": {
1216
+ "type": "string"
1217
+ },
1218
+ "id": {
1219
+ "type": "string"
1220
+ },
1221
+ "createdAt": {
1222
+ "type": "string"
1223
+ },
1224
+ "appId": {
1225
+ "type": "string"
1226
+ },
1227
+ "level": {
1228
+ "$ref": "#/definitions/LogLevel"
1229
+ },
1230
+ "encrypted": {
1231
+ "$ref": "#/definitions/LogEncrypted"
1232
+ }
1233
+ },
1234
+ "required": [
1235
+ "appId",
1236
+ "createdAt",
1237
+ "encrypted",
1238
+ "id",
1239
+ "level"
1240
+ ]
1241
+ }
1242
+ },
1243
+ "required": [
1244
+ "log"
1245
+ ],
1246
+ "additionalProperties": false
1247
+ }
1248
+ }
1249
+ },
1250
+ "flag:created": {
1251
+ "$comment": "(flag: Omit<FlagRecord, 'updatedAt'> & { updatedAt?: string }) => void",
1252
+ "type": "object",
1253
+ "properties": {
1254
+ "namedArgs": {
1255
+ "type": "object",
1256
+ "properties": {
1257
+ "flag": {
1258
+ "type": "object",
1259
+ "additionalProperties": false,
1260
+ "properties": {
1261
+ "updatedAt": {
1262
+ "type": "string"
1263
+ },
1264
+ "id": {
1265
+ "type": "string"
1266
+ },
1267
+ "createdAt": {
1268
+ "type": "string"
1269
+ },
1270
+ "logId": {
1271
+ "type": "string"
1272
+ },
1273
+ "userId": {
1274
+ "type": "string"
1275
+ }
1276
+ },
1277
+ "required": [
1278
+ "createdAt",
1279
+ "id",
1280
+ "logId",
1281
+ "userId"
1282
+ ]
1283
+ }
1284
+ },
1285
+ "required": [
1286
+ "flag"
1287
+ ],
1288
+ "additionalProperties": false
1289
+ }
1290
+ }
1291
+ },
1292
+ "flag:deleted": {
1293
+ "$comment": "(flag: Pick<FlagRecord, 'id'>) => void",
1294
+ "type": "object",
1295
+ "properties": {
1296
+ "namedArgs": {
1297
+ "type": "object",
1298
+ "properties": {
1299
+ "flag": {
1300
+ "type": "object",
1301
+ "properties": {
1302
+ "id": {
1303
+ "type": "string"
1304
+ }
1305
+ },
1306
+ "required": [
1307
+ "id"
1308
+ ],
1309
+ "additionalProperties": false
1310
+ }
1311
+ },
1312
+ "required": [
1313
+ "flag"
1314
+ ],
1315
+ "additionalProperties": false
1316
+ }
1317
+ }
1318
+ },
1319
+ "note:created": {
1320
+ "$comment": "(note: NoteRecord) => void",
1321
+ "type": "object",
1322
+ "properties": {
1323
+ "namedArgs": {
1324
+ "type": "object",
1325
+ "properties": {
1326
+ "note": {
1327
+ "$ref": "#/definitions/NoteRecord"
1328
+ }
1329
+ },
1330
+ "required": [
1331
+ "note"
1332
+ ],
1333
+ "additionalProperties": false
1334
+ }
1335
+ }
1336
+ },
1337
+ "note:updated": {
1338
+ "$comment": "(note: NoteRecord) => void",
1339
+ "type": "object",
1340
+ "properties": {
1341
+ "namedArgs": {
1342
+ "type": "object",
1343
+ "properties": {
1344
+ "note": {
1345
+ "$ref": "#/definitions/NoteRecord"
1346
+ }
1347
+ },
1348
+ "required": [
1349
+ "note"
1350
+ ],
1351
+ "additionalProperties": false
1352
+ }
1353
+ }
1354
+ },
1355
+ "note:deleted": {
1356
+ "$comment": "(note: Pick<NoteRecord, 'id' | 'appId'>) => void",
1357
+ "type": "object",
1358
+ "properties": {
1359
+ "namedArgs": {
1360
+ "type": "object",
1361
+ "properties": {
1362
+ "note": {
1363
+ "type": "object",
1364
+ "properties": {
1365
+ "id": {
1366
+ "type": "string"
1367
+ },
1368
+ "appId": {
1369
+ "type": "string"
1370
+ }
1371
+ },
1372
+ "required": [
1373
+ "id",
1374
+ "appId"
1375
+ ],
1376
+ "additionalProperties": false
1377
+ }
1378
+ },
1379
+ "required": [
1380
+ "note"
1381
+ ],
1382
+ "additionalProperties": false
1383
+ }
1384
+ }
1385
+ },
1386
+ "app:created": {
1387
+ "$comment": "(app: SocketAppPayload) => void",
1388
+ "type": "object",
1389
+ "properties": {
1390
+ "namedArgs": {
1391
+ "type": "object",
1392
+ "properties": {
1393
+ "app": {
1394
+ "$ref": "#/definitions/SocketAppPayload"
1395
+ }
1396
+ },
1397
+ "required": [
1398
+ "app"
1399
+ ],
1400
+ "additionalProperties": false
1401
+ }
1402
+ }
1403
+ },
1404
+ "app:updated": {
1405
+ "$comment": "(app: SocketAppPayload) => void",
1406
+ "type": "object",
1407
+ "properties": {
1408
+ "namedArgs": {
1409
+ "type": "object",
1410
+ "properties": {
1411
+ "app": {
1412
+ "$ref": "#/definitions/SocketAppPayload"
1413
+ }
1414
+ },
1415
+ "required": [
1416
+ "app"
1417
+ ],
1418
+ "additionalProperties": false
1419
+ }
1420
+ }
1421
+ },
1422
+ "app:deleted": {
1423
+ "$comment": "(app: Pick<AppRecord, 'id'>) => void",
1424
+ "type": "object",
1425
+ "properties": {
1426
+ "namedArgs": {
1427
+ "type": "object",
1428
+ "properties": {
1429
+ "app": {
1430
+ "type": "object",
1431
+ "properties": {
1432
+ "id": {
1433
+ "type": "string"
1434
+ }
1435
+ },
1436
+ "required": [
1437
+ "id"
1438
+ ],
1439
+ "additionalProperties": false
1440
+ }
1441
+ },
1442
+ "required": [
1443
+ "app"
1444
+ ],
1445
+ "additionalProperties": false
1446
+ }
1447
+ },
1448
+ "description": "Deleted, or kept to its team's owner and so gone for the rest of the team."
1449
+ },
1450
+ "access:changed": {
1451
+ "$comment": "(payload: Record<string, never>) => void",
1452
+ "type": "object",
1453
+ "properties": {
1454
+ "namedArgs": {
1455
+ "type": "object",
1456
+ "properties": {
1457
+ "payload": {
1458
+ "type": "object",
1459
+ "additionalProperties": {
1460
+ "not": {}
1461
+ }
1462
+ }
1463
+ },
1464
+ "required": [
1465
+ "payload"
1466
+ ],
1467
+ "additionalProperties": false
1468
+ }
1469
+ },
1470
+ "description": "Someone joined or left a team or app the user is on. Carries nothing; sent on every plan."
1471
+ },
1472
+ "socket:reconnected": {
1473
+ "$comment": "() => void",
1474
+ "description": "The socket's database connection came back after a gap and has replayed what it could. Deletes, and anything older or past the replay's cap, need the client's own resync."
1475
+ },
1476
+ "myApps": {
1477
+ "$comment": "(payload: { apps: Pick<AppRecord, 'id' | 'name'>[] }) => void",
1478
+ "type": "object",
1479
+ "properties": {
1480
+ "namedArgs": {
1481
+ "type": "object",
1482
+ "properties": {
1483
+ "payload": {
1484
+ "type": "object",
1485
+ "properties": {
1486
+ "apps": {
1487
+ "type": "array",
1488
+ "items": {
1489
+ "type": "object",
1490
+ "properties": {
1491
+ "id": {
1492
+ "type": "string"
1493
+ },
1494
+ "name": {
1495
+ "type": "string"
1496
+ }
1497
+ },
1498
+ "required": [
1499
+ "id",
1500
+ "name"
1501
+ ],
1502
+ "additionalProperties": false
1503
+ }
1504
+ }
1505
+ },
1506
+ "required": [
1507
+ "apps"
1508
+ ],
1509
+ "additionalProperties": false
1510
+ }
1511
+ },
1512
+ "required": [
1513
+ "payload"
1514
+ ],
1515
+ "additionalProperties": false
1516
+ }
1517
+ },
1518
+ "description": "The answer to `listMyApps`: the apps the user gets live updates for."
1519
+ },
1520
+ "error": {
1521
+ "$comment": "(payload: { message: string }) => void",
1522
+ "type": "object",
1523
+ "properties": {
1524
+ "namedArgs": {
1525
+ "type": "object",
1526
+ "properties": {
1527
+ "payload": {
1528
+ "type": "object",
1529
+ "properties": {
1530
+ "message": {
1531
+ "type": "string"
1532
+ }
1533
+ },
1534
+ "required": [
1535
+ "message"
1536
+ ],
1537
+ "additionalProperties": false
1538
+ }
1539
+ },
1540
+ "required": [
1541
+ "payload"
1542
+ ],
1543
+ "additionalProperties": false
1544
+ }
1545
+ }
1546
+ }
1547
+ },
1548
+ "required": [
1549
+ "log:created",
1550
+ "flag:created",
1551
+ "flag:deleted",
1552
+ "note:created",
1553
+ "note:updated",
1554
+ "note:deleted",
1555
+ "app:created",
1556
+ "app:updated",
1557
+ "app:deleted",
1558
+ "access:changed",
1559
+ "socket:reconnected",
1560
+ "myApps",
1561
+ "error"
1562
+ ],
1563
+ "additionalProperties": false,
1564
+ "description": "What Lumber-Socket sends a client, for socket.io's typed `Server` and `Socket`. Each goes to the users who can read the app it's about, live updates permitting (the app's team's plan); flags go to the user who set them."
1565
+ },
1566
+ "ClientToServerEvents": {
1567
+ "type": "object",
1568
+ "properties": {
1569
+ "listMyApps": {
1570
+ "$comment": "() => void"
1571
+ }
1572
+ },
1573
+ "required": [
1574
+ "listMyApps"
1575
+ ],
1576
+ "additionalProperties": false,
1577
+ "description": "What a client sends Lumber-Socket."
1578
+ },
1579
+ "ResponseMetadata": {
1580
+ "type": "object",
1581
+ "properties": {
1582
+ "requestId": {
1583
+ "type": "string"
1584
+ },
1585
+ "durationMs": {
1586
+ "type": "number"
1587
+ },
1588
+ "warnings": {
1589
+ "type": "array",
1590
+ "items": {
1591
+ "type": "string"
1592
+ }
1593
+ },
1594
+ "cursor": {
1595
+ "type": [
1596
+ "string",
1597
+ "null"
1598
+ ]
1599
+ },
1600
+ "hasMore": {
1601
+ "type": "boolean"
1602
+ },
1603
+ "count": {
1604
+ "type": "number"
1605
+ },
1606
+ "hiddenCount": {
1607
+ "type": "number"
1608
+ }
1609
+ },
1610
+ "required": [
1611
+ "requestId",
1612
+ "durationMs"
1613
+ ],
1614
+ "additionalProperties": false
1615
+ },
1616
+ "PushData": {
1617
+ "anyOf": [
1618
+ {
1619
+ "type": "object",
1620
+ "properties": {
1621
+ "type": {
1622
+ "type": "string",
1623
+ "const": "log"
1624
+ },
1625
+ "appId": {
1626
+ "type": "string"
1627
+ },
1628
+ "logId": {
1629
+ "type": "string"
1630
+ },
1631
+ "level": {
1632
+ "type": "string"
1633
+ }
1634
+ },
1635
+ "required": [
1636
+ "type",
1637
+ "appId",
1638
+ "logId",
1639
+ "level"
1640
+ ],
1641
+ "additionalProperties": false
1642
+ },
1643
+ {
1644
+ "type": "object",
1645
+ "properties": {
1646
+ "type": {
1647
+ "type": "string",
1648
+ "const": "note"
1649
+ },
1650
+ "appId": {
1651
+ "type": "string"
1652
+ },
1653
+ "noteId": {
1654
+ "type": "string"
1655
+ }
1656
+ },
1657
+ "required": [
1658
+ "type",
1659
+ "appId",
1660
+ "noteId"
1661
+ ],
1662
+ "additionalProperties": false
1663
+ },
1664
+ {
1665
+ "type": "object",
1666
+ "properties": {
1667
+ "type": {
1668
+ "type": "string",
1669
+ "const": "device_link"
1670
+ },
1671
+ "deviceLinkId": {
1672
+ "type": "string"
1673
+ },
1674
+ "deviceName": {
1675
+ "type": "string"
1676
+ }
1677
+ },
1678
+ "required": [
1679
+ "type",
1680
+ "deviceLinkId",
1681
+ "deviceName"
1682
+ ],
1683
+ "additionalProperties": false
1684
+ },
1685
+ {
1686
+ "type": "object",
1687
+ "properties": {
1688
+ "type": {
1689
+ "type": "string",
1690
+ "const": "device_link_answered"
1691
+ },
1692
+ "deviceLinkId": {
1693
+ "type": "string"
1694
+ },
1695
+ "deviceName": {
1696
+ "type": "string"
1697
+ }
1698
+ },
1699
+ "required": [
1700
+ "type",
1701
+ "deviceLinkId",
1702
+ "deviceName"
1703
+ ],
1704
+ "additionalProperties": false
1705
+ },
1706
+ {
1707
+ "type": "object",
1708
+ "properties": {
1709
+ "type": {
1710
+ "type": "string",
1711
+ "const": "key_check"
1712
+ },
1713
+ "checkId": {
1714
+ "type": "string"
1715
+ }
1716
+ },
1717
+ "required": [
1718
+ "type",
1719
+ "checkId"
1720
+ ],
1721
+ "additionalProperties": false
1722
+ },
1723
+ {
1724
+ "type": "object",
1725
+ "properties": {
1726
+ "type": {
1727
+ "type": "string",
1728
+ "const": "key_waiting"
1729
+ },
1730
+ "userId": {
1731
+ "type": "string"
1732
+ }
1733
+ },
1734
+ "required": [
1735
+ "type",
1736
+ "userId"
1737
+ ],
1738
+ "additionalProperties": false
1739
+ },
1740
+ {
1741
+ "type": "object",
1742
+ "properties": {
1743
+ "type": {
1744
+ "type": "string",
1745
+ "const": "keys_shared"
1746
+ },
1747
+ "appId": {
1748
+ "type": "string"
1749
+ }
1750
+ },
1751
+ "required": [
1752
+ "type"
1753
+ ],
1754
+ "additionalProperties": false
1755
+ }
1756
+ ],
1757
+ "description": "The `data` a push notification carries, which the app reads to open the screen it's about. Expo delivers data as JSON, but some platforms want strings, so every value is one."
1758
+ },
1759
+ "PushType": {
1760
+ "type": "string",
1761
+ "enum": [
1762
+ "log",
1763
+ "note",
1764
+ "device_link",
1765
+ "device_link_answered",
1766
+ "key_check",
1767
+ "key_waiting",
1768
+ "keys_shared"
1769
+ ]
1770
+ },
1771
+ "PendingAppKey": {
1772
+ "type": "object",
1773
+ "properties": {
1774
+ "appId": {
1775
+ "type": "string"
1776
+ },
1777
+ "userId": {
1778
+ "type": "string"
1779
+ },
1780
+ "publicKey": {
1781
+ "type": "string"
1782
+ },
1783
+ "keyChangedAt": {
1784
+ "type": [
1785
+ "string",
1786
+ "null"
1787
+ ]
1788
+ }
1789
+ },
1790
+ "required": [
1791
+ "appId",
1792
+ "userId",
1793
+ "publicKey"
1794
+ ],
1795
+ "additionalProperties": false,
1796
+ "description": "Someone with access to an app who has no wrapped copy of its key yet (GET /apps/keys/pending), with when they last replaced their account key: null if never, missing from an API before key pins."
1797
+ },
1798
+ "KeyHolder": {
1799
+ "type": "object",
1800
+ "properties": {
1801
+ "userId": {
1802
+ "type": "string"
1803
+ },
1804
+ "publicKey": {
1805
+ "type": "string"
1806
+ }
1807
+ },
1808
+ "required": [
1809
+ "userId",
1810
+ "publicKey"
1811
+ ],
1812
+ "additionalProperties": false,
1813
+ "description": "Someone who reads one of the caller's apps and already has a wrapped copy of its key (GET /apps/keys/holders)."
1814
+ },
1815
+ "AppKeyUpsertBody": {
1816
+ "type": "object",
1817
+ "properties": {
1818
+ "encryptedKey": {
1819
+ "type": "string"
1820
+ }
1821
+ },
1822
+ "required": [
1823
+ "encryptedKey"
1824
+ ],
1825
+ "additionalProperties": false,
1826
+ "description": "Body of PUT /apps/:id/keys/:userId: the app key wrapped to that user's account key."
1827
+ },
1828
+ "KeyPin": {
1829
+ "type": "object",
1830
+ "properties": {
1831
+ "userId": {
1832
+ "type": "string"
1833
+ },
1834
+ "publicKey": {
1835
+ "type": "string"
1836
+ },
1837
+ "mac": {
1838
+ "type": "string"
1839
+ }
1840
+ },
1841
+ "required": [
1842
+ "userId",
1843
+ "publicKey",
1844
+ "mac"
1845
+ ],
1846
+ "additionalProperties": false,
1847
+ "description": "An account key a user has accepted for someone they share apps with (GET /key-pins). `mac` is an HMAC by the user's own devices, under a key derived from their account key, over the user, the person and the key: the server can drop a pin but not forge or move one."
1848
+ },
1849
+ "KeyCheckResult": {
1850
+ "type": "string",
1851
+ "enum": [
1852
+ "confirmed",
1853
+ "failed",
1854
+ "cancelled"
1855
+ ]
1856
+ },
1857
+ "HolderKeyCheck": {
1858
+ "type": "object",
1859
+ "properties": {
1860
+ "id": {
1861
+ "type": "string"
1862
+ },
1863
+ "userId": {
1864
+ "type": "string"
1865
+ },
1866
+ "sid": {
1867
+ "type": "string"
1868
+ },
1869
+ "holderMessage": {
1870
+ "type": "string"
1871
+ },
1872
+ "holderState": {
1873
+ "type": "string",
1874
+ "description": "The holder's half of the exchange, sealed under their account key."
1875
+ },
1876
+ "claimantMessage": {
1877
+ "type": [
1878
+ "string",
1879
+ "null"
1880
+ ]
1881
+ },
1882
+ "publicKey": {
1883
+ "type": [
1884
+ "string",
1885
+ "null"
1886
+ ]
1887
+ },
1888
+ "tag": {
1889
+ "type": [
1890
+ "string",
1891
+ "null"
1892
+ ]
1893
+ },
1894
+ "answeredAt": {
1895
+ "type": [
1896
+ "string",
1897
+ "null"
1898
+ ]
1899
+ },
1900
+ "result": {
1901
+ "anyOf": [
1902
+ {
1903
+ "$ref": "#/definitions/KeyCheckResult"
1904
+ },
1905
+ {
1906
+ "type": "null"
1907
+ }
1908
+ ]
1909
+ },
1910
+ "finishedAt": {
1911
+ "type": [
1912
+ "string",
1913
+ "null"
1914
+ ]
1915
+ },
1916
+ "expiresAt": {
1917
+ "type": "string"
1918
+ },
1919
+ "createdAt": {
1920
+ "type": "string"
1921
+ }
1922
+ },
1923
+ "required": [
1924
+ "id",
1925
+ "userId",
1926
+ "sid",
1927
+ "holderMessage",
1928
+ "holderState",
1929
+ "claimantMessage",
1930
+ "publicKey",
1931
+ "tag",
1932
+ "answeredAt",
1933
+ "result",
1934
+ "finishedAt",
1935
+ "expiresAt",
1936
+ "createdAt"
1937
+ ],
1938
+ "additionalProperties": false,
1939
+ "description": "A key check the caller started, as their devices read it (GET /key-checks/mine): enough to show the code again or finish the check. The messages are a password-authenticated exchange the server can't read or test codes against."
1940
+ },
1941
+ "ClaimantKeyCheck": {
1942
+ "type": "object",
1943
+ "properties": {
1944
+ "id": {
1945
+ "type": "string"
1946
+ },
1947
+ "holderId": {
1948
+ "type": "string"
1949
+ },
1950
+ "holderName": {
1951
+ "type": "string"
1952
+ },
1953
+ "sid": {
1954
+ "type": "string"
1955
+ },
1956
+ "holderMessage": {
1957
+ "type": "string"
1958
+ },
1959
+ "answeredAt": {
1960
+ "type": [
1961
+ "string",
1962
+ "null"
1963
+ ]
1964
+ },
1965
+ "result": {
1966
+ "anyOf": [
1967
+ {
1968
+ "$ref": "#/definitions/KeyCheckResult"
1969
+ },
1970
+ {
1971
+ "type": "null"
1972
+ }
1973
+ ]
1974
+ },
1975
+ "finishedAt": {
1976
+ "type": [
1977
+ "string",
1978
+ "null"
1979
+ ]
1980
+ },
1981
+ "expiresAt": {
1982
+ "type": "string"
1983
+ },
1984
+ "createdAt": {
1985
+ "type": "string"
1986
+ }
1987
+ },
1988
+ "required": [
1989
+ "id",
1990
+ "holderId",
1991
+ "holderName",
1992
+ "sid",
1993
+ "holderMessage",
1994
+ "answeredAt",
1995
+ "result",
1996
+ "finishedAt",
1997
+ "expiresAt",
1998
+ "createdAt"
1999
+ ],
2000
+ "additionalProperties": false,
2001
+ "description": "A key check waiting on the caller's answer, or how a recent answer went (GET /key-checks/for-me)."
2002
+ },
2003
+ "InvitationRecord": {
2004
+ "type": "object",
2005
+ "properties": {
2006
+ "id": {
2007
+ "type": "string"
2008
+ },
2009
+ "teamId": {
2010
+ "type": [
2011
+ "string",
2012
+ "null"
2013
+ ]
2014
+ },
2015
+ "appId": {
2016
+ "type": [
2017
+ "string",
2018
+ "null"
2019
+ ]
2020
+ },
2021
+ "email": {
2022
+ "type": "string"
2023
+ },
2024
+ "role": {
2025
+ "type": "string"
2026
+ },
2027
+ "sentAt": {
2028
+ "type": [
2029
+ "string",
2030
+ "null"
2031
+ ],
2032
+ "description": "Null when its email couldn't be sent."
2033
+ },
2034
+ "expiresAt": {
2035
+ "type": "string"
2036
+ },
2037
+ "createdAt": {
2038
+ "type": "string"
2039
+ }
2040
+ },
2041
+ "required": [
2042
+ "id",
2043
+ "teamId",
2044
+ "appId",
2045
+ "email",
2046
+ "role",
2047
+ "sentAt",
2048
+ "expiresAt",
2049
+ "createdAt"
2050
+ ],
2051
+ "additionalProperties": false,
2052
+ "description": "An invitation to an address with no account yet: to a team (teamId, as a member or admin) or to one app as its guest (appId). The address joins when it signs up and confirms itself. A live one (unexpired) holds its seat."
2053
+ },
2054
+ "DeviceLinkStatus": {
2055
+ "type": "string",
2056
+ "enum": [
2057
+ "pending",
2058
+ "approved",
2059
+ "denied",
2060
+ "failed"
2061
+ ],
2062
+ "description": "A device link request: a new device asking for the account key from a device that has it. `failed` is a code that didn't match, which ends the request."
2063
+ },
2064
+ "DeviceLinkPlatform": {
2065
+ "type": "string",
2066
+ "enum": [
2067
+ "ios",
2068
+ "android",
2069
+ "web",
2070
+ "desktop",
2071
+ "cli"
2072
+ ],
2073
+ "description": "What a request comes from, so the device answering it can say so and offer a QR code only to a phone's camera."
2074
+ },
2075
+ "DeviceLinkCreateBody": {
2076
+ "type": "object",
2077
+ "properties": {
2078
+ "deviceName": {
2079
+ "type": "string"
2080
+ },
2081
+ "platform": {
2082
+ "$ref": "#/definitions/DeviceLinkPlatform"
2083
+ },
2084
+ "publicKey": {
2085
+ "type": "string",
2086
+ "description": "The public half of the new device's one-off X25519 keypair, which the account key is wrapped to."
2087
+ },
2088
+ "deviceId": {
2089
+ "type": "string",
2090
+ "description": "The new device's push device id, so the push asking the account's devices to link it skips it."
2091
+ }
2092
+ },
2093
+ "required": [
2094
+ "deviceName",
2095
+ "platform",
2096
+ "publicKey"
2097
+ ],
2098
+ "additionalProperties": false,
2099
+ "description": "Body of POST /device-links."
2100
+ },
2101
+ "DeviceLinkCreatedRecord": {
2102
+ "type": "object",
2103
+ "properties": {
2104
+ "id": {
2105
+ "type": "string"
2106
+ },
2107
+ "expiresAt": {
2108
+ "type": "string"
2109
+ }
2110
+ },
2111
+ "required": [
2112
+ "id",
2113
+ "expiresAt"
2114
+ ],
2115
+ "additionalProperties": false,
2116
+ "description": "Response from POST /device-links."
2117
+ },
2118
+ "DeviceLinkApproveBody": {
2119
+ "type": "object",
2120
+ "properties": {
2121
+ "encryptedKey": {
2122
+ "type": "string"
2123
+ },
2124
+ "deliveryTag": {
2125
+ "type": "string"
2126
+ }
2127
+ },
2128
+ "required": [
2129
+ "encryptedKey",
2130
+ "deliveryTag"
2131
+ ],
2132
+ "additionalProperties": false,
2133
+ "description": "Body of POST /device-links/:id/approve: the account key wrapped to the new device, and a tag over it made from the code's secret, which the new device checks before storing the key, so the server can't hand it a key of its own."
2134
+ },
2135
+ "DeviceLinkRecord": {
2136
+ "type": "object",
2137
+ "properties": {
2138
+ "id": {
2139
+ "type": "string"
2140
+ },
2141
+ "status": {
2142
+ "$ref": "#/definitions/DeviceLinkStatus"
2143
+ },
2144
+ "encryptedKey": {
2145
+ "type": "string"
2146
+ },
2147
+ "deliveryTag": {
2148
+ "type": "string"
2149
+ },
2150
+ "expiresAt": {
2151
+ "type": "string"
2152
+ },
2153
+ "sid": {
2154
+ "type": [
2155
+ "string",
2156
+ "null"
2157
+ ]
2158
+ },
2159
+ "holderMessage": {
2160
+ "type": [
2161
+ "string",
2162
+ "null"
2163
+ ]
2164
+ },
2165
+ "answeredAt": {
2166
+ "type": [
2167
+ "string",
2168
+ "null"
2169
+ ]
2170
+ }
2171
+ },
2172
+ "required": [
2173
+ "id",
2174
+ "status",
2175
+ "expiresAt"
2176
+ ],
2177
+ "additionalProperties": false,
2178
+ "description": "Response from GET /device-links/:id, as the new device reads it. encryptedKey and deliveryTag are present only when status is 'approved'. sid and holderMessage are set once a device holding the account key shows the request's code, and answeredAt once the new device has answered it."
2179
+ },
2180
+ "PendingDeviceLink": {
2181
+ "type": "object",
2182
+ "properties": {
2183
+ "id": {
2184
+ "type": "string"
2185
+ },
2186
+ "deviceName": {
2187
+ "type": "string"
2188
+ },
2189
+ "platform": {
2190
+ "$ref": "#/definitions/DeviceLinkPlatform"
2191
+ },
2192
+ "publicKey": {
2193
+ "type": "string",
2194
+ "description": "The new device's one-off public key, which an answer's tag has to cover before anything is wrapped to it."
2195
+ },
2196
+ "sid": {
2197
+ "type": [
2198
+ "string",
2199
+ "null"
2200
+ ]
2201
+ },
2202
+ "holderMessage": {
2203
+ "type": [
2204
+ "string",
2205
+ "null"
2206
+ ]
2207
+ },
2208
+ "holderState": {
2209
+ "type": [
2210
+ "string",
2211
+ "null"
2212
+ ]
2213
+ },
2214
+ "claimantMessage": {
2215
+ "type": [
2216
+ "string",
2217
+ "null"
2218
+ ]
2219
+ },
2220
+ "tag": {
2221
+ "type": [
2222
+ "string",
2223
+ "null"
2224
+ ]
2225
+ },
2226
+ "answeredAt": {
2227
+ "type": [
2228
+ "string",
2229
+ "null"
2230
+ ]
2231
+ },
2232
+ "expiresAt": {
2233
+ "type": "string"
2234
+ },
2235
+ "createdAt": {
2236
+ "type": "string"
2237
+ }
2238
+ },
2239
+ "required": [
2240
+ "id",
2241
+ "deviceName",
2242
+ "platform",
2243
+ "publicKey",
2244
+ "sid",
2245
+ "holderMessage",
2246
+ "holderState",
2247
+ "claimantMessage",
2248
+ "tag",
2249
+ "answeredAt",
2250
+ "expiresAt",
2251
+ "createdAt"
2252
+ ],
2253
+ "additionalProperties": false,
2254
+ "description": "A request waiting on the caller's account key (GET /device-links/pending), as a device holding it reads it: enough to show its code, and to finish it once answered. The messages are the same password-authenticated exchange as a key check's, so the server can't read or test codes against them. holderState is the code's side of the exchange, sealed under the account key, so any of the account's devices can show the same code or finish the request."
2255
+ },
2256
+ "ApiErrorBody": {
2257
+ "type": "object",
2258
+ "properties": {
2259
+ "message": {
2260
+ "type": "string"
2261
+ },
2262
+ "code": {
2263
+ "$ref": "#/definitions/ApiErrorCode"
2264
+ }
2265
+ },
2266
+ "required": [
2267
+ "message"
2268
+ ],
2269
+ "additionalProperties": false,
2270
+ "description": "The body of an API error response. `code` is there where the status alone doesn't say what went wrong."
2271
+ }
2272
+ }
2273
+ }