@infinitetoken/lumber-kit 0.7.0 → 0.8.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.
@@ -12,6 +12,39 @@
12
12
  "light"
13
13
  ]
14
14
  },
15
+ "TimeClock": {
16
+ "type": "string",
17
+ "enum": [
18
+ "24h",
19
+ "12h"
20
+ ],
21
+ "description": "A 24-hour clock (`19:15:44`) or a 12-hour one (`7:15:44 PM`)."
22
+ },
23
+ "TimeZoneChoice": {
24
+ "type": "string",
25
+ "enum": [
26
+ "local",
27
+ "utc"
28
+ ],
29
+ "description": "The device's own time zone, or UTC."
30
+ },
31
+ "TimeFormat": {
32
+ "type": "object",
33
+ "properties": {
34
+ "clock": {
35
+ "$ref": "#/definitions/TimeClock"
36
+ },
37
+ "zone": {
38
+ "$ref": "#/definitions/TimeZoneChoice"
39
+ }
40
+ },
41
+ "required": [
42
+ "clock",
43
+ "zone"
44
+ ],
45
+ "additionalProperties": false,
46
+ "description": "How a user reads times, on every device: the clock, and the zone that times, days and hours are in (the log list, the heatmap, pushes)."
47
+ },
15
48
  "PushToken": {
16
49
  "type": "object",
17
50
  "properties": {
@@ -110,6 +143,17 @@
110
143
  "imageUrl": {
111
144
  "type": "string"
112
145
  },
146
+ "timeFormat": {
147
+ "anyOf": [
148
+ {
149
+ "$ref": "#/definitions/TimeFormat"
150
+ },
151
+ {
152
+ "type": "null"
153
+ }
154
+ ],
155
+ "description": "Null until the user picks one (DEFAULT_TIME_FORMAT). Missing from an API before it was kept."
156
+ },
113
157
  "agreedToTermsVersion": {
114
158
  "type": "string"
115
159
  },
@@ -269,98 +313,36 @@
269
313
  "HAS_ACCOUNT"
270
314
  ]
271
315
  },
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": {
316
+ "AppEncryptedKey": {
305
317
  "type": "object",
306
318
  "properties": {
307
- "apps": {
308
- "type": "number"
309
- },
310
- "seats": {
311
- "type": "number"
312
- },
313
- "apiKeys": {
314
- "type": "number"
319
+ "userId": {
320
+ "type": "string"
315
321
  },
316
- "logRetentionDays": {
317
- "type": "number"
322
+ "encryptedKey": {
323
+ "type": "string"
318
324
  },
319
- "monthlyLogs": {
325
+ "keyVersion": {
320
326
  "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
327
  }
330
328
  },
331
329
  "required": [
332
- "apps",
333
- "seats",
334
- "apiKeys",
335
- "logRetentionDays",
336
- "monthlyLogs",
337
- "realtimeLogs",
338
- "minPushLevel"
330
+ "userId",
331
+ "encryptedKey",
332
+ "keyVersion"
339
333
  ],
340
- "additionalProperties": false
334
+ "additionalProperties": false,
335
+ "description": "Wrapped application encryption key for a specific user."
341
336
  },
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"
337
+ "AppTeamAccess": {
338
+ "type": "string",
339
+ "enum": [
340
+ "everyone",
341
+ "owner"
360
342
  ],
361
- "additionalProperties": false
343
+ "description": "Who on an app's team can read it: everyone on the team, or only its owner. Its guests read it either way."
362
344
  },
363
- "TeamRecord": {
345
+ "AppRecord": {
364
346
  "type": "object",
365
347
  "properties": {
366
348
  "id": {
@@ -381,42 +363,32 @@
381
363
  "imageUrl": {
382
364
  "type": "string"
383
365
  },
384
- "subscriptionLevel": {
385
- "$ref": "#/definitions/SubscriptionLevel"
366
+ "teamId": {
367
+ "type": "string"
386
368
  },
387
- "subscriptionEnd": {
369
+ "encryptedKey": {
388
370
  "type": [
389
371
  "string",
390
372
  "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."
373
+ ]
402
374
  },
403
- "memberId": {
404
- "type": "string",
405
- "description": "GET /teams only: the caller's membership id, which leaving the team deletes."
375
+ "teamAccess": {
376
+ "$ref": "#/definitions/AppTeamAccess",
377
+ "description": "Missing means 'everyone'."
406
378
  },
407
- "appCount": {
379
+ "retentionDays": {
408
380
  "type": [
409
381
  "number",
410
382
  "null"
411
383
  ],
412
- "description": "GET /teams only: the team's apps, as creating one counts them. Null unless the caller is its owner or an admin."
384
+ "description": "How many days of its logs the app's team's plan keeps, null for no limit (and when missing)."
413
385
  },
414
- "logsThisMonth": {
386
+ "logsCappedUntil": {
415
387
  "type": [
416
- "number",
388
+ "string",
417
389
  "null"
418
390
  ],
419
- "description": "GET /teams only: logs its apps sent this UTC month, the monthly cap's count. Null unless owner or admin."
391
+ "description": "While the team has sent its plan's monthly logs, when new ones are taken again (the next UTC month); else null."
420
392
  }
421
393
  },
422
394
  "required": [
@@ -424,12 +396,12 @@
424
396
  "createdAt",
425
397
  "updatedAt",
426
398
  "name",
427
- "subscriptionLevel"
399
+ "encryptedKey"
428
400
  ],
429
401
  "additionalProperties": false,
430
- "description": "An organization that owns apps and pays for member usage. Team subscriptionLevel takes precedence over individual member levels."
402
+ "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."
431
403
  },
432
- "TeamMemberRecord": {
404
+ "AppCreatedRecord": {
433
405
  "type": "object",
434
406
  "properties": {
435
407
  "id": {
@@ -441,279 +413,91 @@
441
413
  "updatedAt": {
442
414
  "type": "string"
443
415
  },
444
- "teamId": {
416
+ "name": {
445
417
  "type": "string"
446
418
  },
447
- "userId": {
419
+ "color": {
448
420
  "type": "string"
449
421
  },
450
- "role": {
451
- "type": "string",
452
- "enum": [
453
- "owner",
454
- "admin",
455
- "member"
456
- ]
422
+ "imageUrl": {
423
+ "type": "string"
457
424
  },
458
- "name": {
425
+ "teamId": {
426
+ "type": "string"
427
+ },
428
+ "encryptedKey": {
459
429
  "type": [
460
430
  "string",
461
431
  "null"
462
- ],
463
- "description": "Listed members only: their display name. Never their email address."
432
+ ]
464
433
  },
465
- "handle": {
434
+ "teamAccess": {
435
+ "$ref": "#/definitions/AppTeamAccess",
436
+ "description": "Missing means 'everyone'."
437
+ },
438
+ "retentionDays": {
466
439
  "type": [
467
- "string",
440
+ "number",
468
441
  "null"
469
442
  ],
470
- "description": "Listed members only: the part of their email before the"
443
+ "description": "How many days of its logs the app's team's plan keeps, null for no limit (and when missing)."
471
444
  },
472
- "color": {
445
+ "logsCappedUntil": {
473
446
  "type": [
474
447
  "string",
475
448
  "null"
476
- ]
449
+ ],
450
+ "description": "While the team has sent its plan's monthly logs, when new ones are taken again (the next UTC month); else null."
477
451
  },
478
- "imageUrl": {
479
- "type": [
480
- "string",
481
- "null"
482
- ]
452
+ "apiKey": {
453
+ "type": "string"
483
454
  }
484
455
  },
456
+ "additionalProperties": false,
485
457
  "required": [
486
- "id",
487
458
  "createdAt",
488
- "updatedAt",
489
- "teamId",
490
- "userId",
491
- "role"
459
+ "encryptedKey",
460
+ "id",
461
+ "name",
462
+ "updatedAt"
492
463
  ],
493
- "additionalProperties": false,
494
- "description": "Membership of a user in a team."
464
+ "description": "App creation response. Initial wrapped encryption key may optionally be returned."
495
465
  },
496
- "SubscriptionPlanRecord": {
466
+ "ApiKeyRecord": {
497
467
  "type": "object",
498
468
  "properties": {
499
- "name": {
469
+ "id": {
500
470
  "type": "string"
501
471
  },
502
- "priceId": {
503
- "type": [
504
- "string",
505
- "null"
506
- ]
472
+ "createdAt": {
473
+ "type": "string"
474
+ },
475
+ "updatedAt": {
476
+ "type": "string"
477
+ },
478
+ "appId": {
479
+ "type": "string"
480
+ },
481
+ "name": {
482
+ "type": "string"
507
483
  }
508
484
  },
509
485
  "required": [
510
- "name",
511
- "priceId"
486
+ "id",
487
+ "createdAt",
488
+ "updatedAt",
489
+ "appId"
512
490
  ],
513
491
  "additionalProperties": false,
514
- "description": "A plan GET /subscriptions offers, with its Stripe price (none for Hobby)."
492
+ "description": "Machine authentication credential."
515
493
  },
516
- "TeamsQuery": {
494
+ "GuestRecord": {
517
495
  "type": "object",
518
496
  "properties": {
519
- "sort": {
497
+ "id": {
520
498
  "type": "string"
521
499
  },
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": {
500
+ "createdAt": {
717
501
  "type": "string"
718
502
  },
719
503
  "updatedAt": {
@@ -840,173 +624,225 @@
840
624
  "additionalProperties": false,
841
625
  "description": "Filter params for GET /apps."
842
626
  },
843
- "FlagRecord": {
627
+ "DeviceLinkStatus": {
628
+ "type": "string",
629
+ "enum": [
630
+ "pending",
631
+ "approved",
632
+ "denied",
633
+ "failed"
634
+ ],
635
+ "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."
636
+ },
637
+ "DeviceLinkPlatform": {
638
+ "type": "string",
639
+ "enum": [
640
+ "ios",
641
+ "android",
642
+ "web",
643
+ "desktop",
644
+ "cli"
645
+ ],
646
+ "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."
647
+ },
648
+ "DeviceLinkCreateBody": {
844
649
  "type": "object",
845
650
  "properties": {
846
- "id": {
847
- "type": "string"
848
- },
849
- "createdAt": {
651
+ "deviceName": {
850
652
  "type": "string"
851
653
  },
852
- "updatedAt": {
853
- "type": "string"
654
+ "platform": {
655
+ "$ref": "#/definitions/DeviceLinkPlatform"
854
656
  },
855
- "logId": {
856
- "type": "string"
657
+ "publicKey": {
658
+ "type": "string",
659
+ "description": "The public half of the new device's one-off X25519 keypair, which the account key is wrapped to."
857
660
  },
858
- "userId": {
859
- "type": "string"
661
+ "deviceId": {
662
+ "type": "string",
663
+ "description": "The new device's push device id, so the push asking the account's devices to link it skips it."
860
664
  }
861
665
  },
862
666
  "required": [
863
- "id",
864
- "createdAt",
865
- "updatedAt",
866
- "logId",
867
- "userId"
667
+ "deviceName",
668
+ "platform",
669
+ "publicKey"
868
670
  ],
869
- "additionalProperties": false
671
+ "additionalProperties": false,
672
+ "description": "Body of POST /device-links."
870
673
  },
871
- "FlagsQuery": {
674
+ "DeviceLinkCreatedRecord": {
872
675
  "type": "object",
873
676
  "properties": {
874
- "from": {
875
- "type": "string"
876
- },
877
- "to": {
677
+ "id": {
878
678
  "type": "string"
879
679
  },
880
- "limit": {
881
- "type": "number"
882
- },
883
- "cursor": {
680
+ "expiresAt": {
884
681
  "type": "string"
885
682
  }
886
683
  },
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."
684
+ "required": [
685
+ "id",
686
+ "expiresAt"
687
+ ],
688
+ "additionalProperties": false,
689
+ "description": "Response from POST /device-links."
910
690
  },
911
- "LogEncrypted": {
691
+ "DeviceLinkApproveBody": {
912
692
  "type": "object",
913
693
  "properties": {
914
- "nonce": {
694
+ "encryptedKey": {
915
695
  "type": "string"
916
696
  },
917
- "payload": {
697
+ "deliveryTag": {
918
698
  "type": "string"
919
- },
920
- "keyVersion": {
921
- "type": "number"
922
699
  }
923
700
  },
924
701
  "required": [
925
- "nonce",
926
- "payload",
927
- "keyVersion"
702
+ "encryptedKey",
703
+ "deliveryTag"
928
704
  ],
929
705
  "additionalProperties": false,
930
- "description": "Encrypted envelope stored on the server. payload = base64(authTag + ciphertext) nonce = base64(random IV) keyVersion enables future rotation support."
706
+ "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."
931
707
  },
932
- "LogPayload": {
708
+ "DeviceLinkRecord": {
933
709
  "type": "object",
934
710
  "properties": {
935
- "level": {
936
- "$ref": "#/definitions/LogLevel"
711
+ "id": {
712
+ "type": "string"
937
713
  },
938
- "message": {
714
+ "status": {
715
+ "$ref": "#/definitions/DeviceLinkStatus"
716
+ },
717
+ "encryptedKey": {
939
718
  "type": "string"
940
719
  },
941
- "requestId": {
720
+ "deliveryTag": {
942
721
  "type": "string"
943
722
  },
944
- "traceId": {
723
+ "expiresAt": {
945
724
  "type": "string"
946
725
  },
947
- "context": {
948
- "$ref": "#/definitions/LogContext"
726
+ "sid": {
727
+ "type": [
728
+ "string",
729
+ "null"
730
+ ]
949
731
  },
950
- "metadata": {
951
- "type": "object",
952
- "additionalProperties": {}
732
+ "holderMessage": {
733
+ "type": [
734
+ "string",
735
+ "null"
736
+ ]
737
+ },
738
+ "answeredAt": {
739
+ "type": [
740
+ "string",
741
+ "null"
742
+ ]
953
743
  }
954
744
  },
955
745
  "required": [
956
- "level",
957
- "message"
746
+ "id",
747
+ "status",
748
+ "expiresAt"
958
749
  ],
959
750
  "additionalProperties": false,
960
- "description": "Plaintext shape of log content: a plugin encrypts it into LogEncrypted, and the server never sees it."
751
+ "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."
961
752
  },
962
- "LogData": {
753
+ "PendingDeviceLink": {
963
754
  "type": "object",
964
755
  "properties": {
965
- "requestId": {
756
+ "id": {
966
757
  "type": "string"
967
758
  },
968
- "traceId": {
759
+ "deviceName": {
969
760
  "type": "string"
970
761
  },
971
- "context": {
972
- "$ref": "#/definitions/LogContext"
762
+ "platform": {
763
+ "$ref": "#/definitions/DeviceLinkPlatform"
973
764
  },
974
- "metadata": {
975
- "type": "object",
976
- "additionalProperties": {}
765
+ "publicKey": {
766
+ "type": "string",
767
+ "description": "The new device's one-off public key, which an answer's tag has to cover before anything is wrapped to it."
768
+ },
769
+ "sid": {
770
+ "type": [
771
+ "string",
772
+ "null"
773
+ ]
774
+ },
775
+ "holderMessage": {
776
+ "type": [
777
+ "string",
778
+ "null"
779
+ ]
780
+ },
781
+ "holderState": {
782
+ "type": [
783
+ "string",
784
+ "null"
785
+ ]
786
+ },
787
+ "claimantMessage": {
788
+ "type": [
789
+ "string",
790
+ "null"
791
+ ]
792
+ },
793
+ "tag": {
794
+ "type": [
795
+ "string",
796
+ "null"
797
+ ]
798
+ },
799
+ "answeredAt": {
800
+ "type": [
801
+ "string",
802
+ "null"
803
+ ]
804
+ },
805
+ "expiresAt": {
806
+ "type": "string"
807
+ },
808
+ "createdAt": {
809
+ "type": "string"
977
810
  }
978
811
  },
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."
812
+ "required": [
813
+ "id",
814
+ "deviceName",
815
+ "platform",
816
+ "publicKey",
817
+ "sid",
818
+ "holderMessage",
819
+ "holderState",
820
+ "claimantMessage",
821
+ "tag",
822
+ "answeredAt",
823
+ "expiresAt",
824
+ "createdAt"
825
+ ],
826
+ "additionalProperties": false,
827
+ "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."
981
828
  },
982
- "LogIngestBody": {
829
+ "ApiErrorBody": {
983
830
  "type": "object",
984
831
  "properties": {
985
- "level": {
986
- "$ref": "#/definitions/LogLevel"
832
+ "message": {
833
+ "type": "string"
987
834
  },
988
- "encrypted": {
989
- "$ref": "#/definitions/LogEncrypted"
835
+ "code": {
836
+ "$ref": "#/definitions/ApiErrorCode"
990
837
  }
991
838
  },
992
839
  "required": [
993
- "level",
994
- "encrypted"
840
+ "message"
995
841
  ],
996
842
  "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)."
843
+ "description": "The body of an API error response. `code` is there where the status alone doesn't say what went wrong."
1008
844
  },
1009
- "LogRecord": {
845
+ "FlagRecord": {
1010
846
  "type": "object",
1011
847
  "properties": {
1012
848
  "id": {
@@ -1018,51 +854,31 @@
1018
854
  "updatedAt": {
1019
855
  "type": "string"
1020
856
  },
1021
- "appId": {
857
+ "logId": {
1022
858
  "type": "string"
1023
859
  },
1024
- "level": {
1025
- "$ref": "#/definitions/LogLevel"
1026
- },
1027
- "encrypted": {
1028
- "$ref": "#/definitions/LogEncrypted"
860
+ "userId": {
861
+ "type": "string"
1029
862
  }
1030
863
  },
1031
864
  "required": [
1032
865
  "id",
1033
866
  "createdAt",
1034
867
  "updatedAt",
1035
- "appId",
1036
- "level",
1037
- "encrypted"
868
+ "logId",
869
+ "userId"
1038
870
  ],
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."
871
+ "additionalProperties": false
1041
872
  },
1042
- "LogsQuery": {
873
+ "FlagsQuery": {
1043
874
  "type": "object",
1044
875
  "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
876
  "from": {
1058
877
  "type": "string"
1059
878
  },
1060
879
  "to": {
1061
880
  "type": "string"
1062
881
  },
1063
- "sort": {
1064
- "type": "string"
1065
- },
1066
882
  "limit": {
1067
883
  "type": "number"
1068
884
  },
@@ -1070,548 +886,568 @@
1070
886
  "type": "string"
1071
887
  }
1072
888
  },
1073
- "additionalProperties": false,
1074
- "description": "Sync-protocol params for GET /logs."
889
+ "additionalProperties": false
1075
890
  },
1076
- "NoteEncrypted": {
891
+ "InvitationRecord": {
1077
892
  "type": "object",
1078
893
  "properties": {
1079
- "payload": {
894
+ "id": {
1080
895
  "type": "string"
1081
896
  },
1082
- "nonce": {
897
+ "teamId": {
898
+ "type": [
899
+ "string",
900
+ "null"
901
+ ]
902
+ },
903
+ "appId": {
904
+ "type": [
905
+ "string",
906
+ "null"
907
+ ]
908
+ },
909
+ "email": {
1083
910
  "type": "string"
1084
911
  },
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": {
912
+ "role": {
1101
913
  "type": "string"
1102
914
  },
1103
- "createdAt": {
1104
- "type": "string"
915
+ "sentAt": {
916
+ "type": [
917
+ "string",
918
+ "null"
919
+ ],
920
+ "description": "Null when its email couldn't be sent."
1105
921
  },
1106
- "updatedAt": {
922
+ "expiresAt": {
1107
923
  "type": "string"
1108
924
  },
925
+ "createdAt": {
926
+ "type": "string"
927
+ }
928
+ },
929
+ "required": [
930
+ "id",
931
+ "teamId",
932
+ "appId",
933
+ "email",
934
+ "role",
935
+ "sentAt",
936
+ "expiresAt",
937
+ "createdAt"
938
+ ],
939
+ "additionalProperties": false,
940
+ "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."
941
+ },
942
+ "PendingAppKey": {
943
+ "type": "object",
944
+ "properties": {
1109
945
  "appId": {
1110
946
  "type": "string"
1111
947
  },
1112
948
  "userId": {
1113
949
  "type": "string"
1114
950
  },
1115
- "logId": {
951
+ "publicKey": {
1116
952
  "type": "string"
1117
953
  },
1118
- "encrypted": {
1119
- "$ref": "#/definitions/NoteEncrypted"
954
+ "keyChangedAt": {
955
+ "type": [
956
+ "string",
957
+ "null"
958
+ ]
1120
959
  }
1121
960
  },
1122
961
  "required": [
1123
- "id",
1124
- "createdAt",
1125
- "updatedAt",
1126
962
  "appId",
1127
963
  "userId",
1128
- "encrypted"
964
+ "publicKey"
1129
965
  ],
1130
966
  "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."
967
+ "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."
1132
968
  },
1133
- "NotesQuery": {
969
+ "KeyHolder": {
1134
970
  "type": "object",
1135
971
  "properties": {
1136
- "appId": {
972
+ "userId": {
1137
973
  "type": "string"
1138
974
  },
1139
- "teamId": {
975
+ "publicKey": {
1140
976
  "type": "string"
1141
- },
977
+ }
978
+ },
979
+ "required": [
980
+ "userId",
981
+ "publicKey"
982
+ ],
983
+ "additionalProperties": false,
984
+ "description": "Someone who reads one of the caller's apps and already has a wrapped copy of its key (GET /apps/keys/holders)."
985
+ },
986
+ "AppKeyUpsertBody": {
987
+ "type": "object",
988
+ "properties": {
989
+ "encryptedKey": {
990
+ "type": "string"
991
+ }
992
+ },
993
+ "required": [
994
+ "encryptedKey"
995
+ ],
996
+ "additionalProperties": false,
997
+ "description": "Body of PUT /apps/:id/keys/:userId: the app key wrapped to that user's account key."
998
+ },
999
+ "KeyPin": {
1000
+ "type": "object",
1001
+ "properties": {
1142
1002
  "userId": {
1143
1003
  "type": "string"
1144
1004
  },
1145
- "logId": {
1005
+ "publicKey": {
1146
1006
  "type": "string"
1147
1007
  },
1148
- "from": {
1008
+ "mac": {
1009
+ "type": "string"
1010
+ }
1011
+ },
1012
+ "required": [
1013
+ "userId",
1014
+ "publicKey",
1015
+ "mac"
1016
+ ],
1017
+ "additionalProperties": false,
1018
+ "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."
1019
+ },
1020
+ "KeyCheckResult": {
1021
+ "type": "string",
1022
+ "enum": [
1023
+ "confirmed",
1024
+ "failed",
1025
+ "cancelled"
1026
+ ]
1027
+ },
1028
+ "HolderKeyCheck": {
1029
+ "type": "object",
1030
+ "properties": {
1031
+ "id": {
1149
1032
  "type": "string"
1150
1033
  },
1151
- "to": {
1034
+ "userId": {
1152
1035
  "type": "string"
1153
1036
  },
1154
- "sort": {
1037
+ "sid": {
1155
1038
  "type": "string"
1156
1039
  },
1157
- "limit": {
1158
- "type": "number"
1040
+ "holderMessage": {
1041
+ "type": "string"
1159
1042
  },
1160
- "cursor": {
1043
+ "holderState": {
1044
+ "type": "string",
1045
+ "description": "The holder's half of the exchange, sealed under their account key."
1046
+ },
1047
+ "claimantMessage": {
1048
+ "type": [
1049
+ "string",
1050
+ "null"
1051
+ ]
1052
+ },
1053
+ "publicKey": {
1054
+ "type": [
1055
+ "string",
1056
+ "null"
1057
+ ]
1058
+ },
1059
+ "tag": {
1060
+ "type": [
1061
+ "string",
1062
+ "null"
1063
+ ]
1064
+ },
1065
+ "answeredAt": {
1066
+ "type": [
1067
+ "string",
1068
+ "null"
1069
+ ]
1070
+ },
1071
+ "result": {
1072
+ "anyOf": [
1073
+ {
1074
+ "$ref": "#/definitions/KeyCheckResult"
1075
+ },
1076
+ {
1077
+ "type": "null"
1078
+ }
1079
+ ]
1080
+ },
1081
+ "finishedAt": {
1082
+ "type": [
1083
+ "string",
1084
+ "null"
1085
+ ]
1086
+ },
1087
+ "expiresAt": {
1088
+ "type": "string"
1089
+ },
1090
+ "createdAt": {
1161
1091
  "type": "string"
1162
1092
  }
1163
1093
  },
1094
+ "required": [
1095
+ "id",
1096
+ "userId",
1097
+ "sid",
1098
+ "holderMessage",
1099
+ "holderState",
1100
+ "claimantMessage",
1101
+ "publicKey",
1102
+ "tag",
1103
+ "answeredAt",
1104
+ "result",
1105
+ "finishedAt",
1106
+ "expiresAt",
1107
+ "createdAt"
1108
+ ],
1164
1109
  "additionalProperties": false,
1165
- "description": "Filter params for GET /notes."
1110
+ "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."
1166
1111
  },
1167
- "SocketAppPayload": {
1112
+ "ClaimantKeyCheck": {
1168
1113
  "type": "object",
1169
1114
  "properties": {
1170
1115
  "id": {
1171
1116
  "type": "string"
1172
1117
  },
1173
- "name": {
1118
+ "holderId": {
1174
1119
  "type": "string"
1175
1120
  },
1176
- "teamId": {
1121
+ "holderName": {
1177
1122
  "type": "string"
1178
1123
  },
1179
- "color": {
1124
+ "sid": {
1180
1125
  "type": "string"
1181
1126
  },
1182
- "imageUrl": {
1127
+ "holderMessage": {
1183
1128
  "type": "string"
1184
1129
  },
1185
- "createdAt": {
1130
+ "answeredAt": {
1131
+ "type": [
1132
+ "string",
1133
+ "null"
1134
+ ]
1135
+ },
1136
+ "result": {
1137
+ "anyOf": [
1138
+ {
1139
+ "$ref": "#/definitions/KeyCheckResult"
1140
+ },
1141
+ {
1142
+ "type": "null"
1143
+ }
1144
+ ]
1145
+ },
1146
+ "finishedAt": {
1147
+ "type": [
1148
+ "string",
1149
+ "null"
1150
+ ]
1151
+ },
1152
+ "expiresAt": {
1186
1153
  "type": "string"
1187
1154
  },
1188
- "updatedAt": {
1155
+ "createdAt": {
1189
1156
  "type": "string"
1190
1157
  }
1191
1158
  },
1192
1159
  "required": [
1193
1160
  "id",
1194
- "name",
1195
- "createdAt",
1196
- "updatedAt"
1161
+ "holderId",
1162
+ "holderName",
1163
+ "sid",
1164
+ "holderMessage",
1165
+ "answeredAt",
1166
+ "result",
1167
+ "finishedAt",
1168
+ "expiresAt",
1169
+ "createdAt"
1197
1170
  ],
1198
1171
  "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."
1172
+ "description": "A key check waiting on the caller's answer, or how a recent answer went (GET /key-checks/for-me)."
1200
1173
  },
1201
- "ServerToClientEvents": {
1174
+ "LogContext": {
1202
1175
  "type": "object",
1203
1176
  "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
- }
1177
+ "code": {
1178
+ "type": "string"
1249
1179
  },
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
- }
1180
+ "method": {
1181
+ "type": "string"
1291
1182
  },
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
- }
1183
+ "line": {
1184
+ "type": "number"
1318
1185
  },
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
- }
1186
+ "column": {
1187
+ "type": "number"
1336
1188
  },
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
- }
1189
+ "note": {
1190
+ "type": "string"
1191
+ }
1192
+ },
1193
+ "additionalProperties": {},
1194
+ "description": "Structured log context content."
1195
+ },
1196
+ "LogEncrypted": {
1197
+ "type": "object",
1198
+ "properties": {
1199
+ "nonce": {
1200
+ "type": "string"
1354
1201
  },
1355
- "note:deleted": {
1356
- "$comment": "(note: Pick<NoteRecord, 'id' | 'appId'>) => void",
1202
+ "payload": {
1203
+ "type": "string"
1204
+ },
1205
+ "keyVersion": {
1206
+ "type": "number"
1207
+ }
1208
+ },
1209
+ "required": [
1210
+ "nonce",
1211
+ "payload",
1212
+ "keyVersion"
1213
+ ],
1214
+ "additionalProperties": false,
1215
+ "description": "Encrypted envelope stored on the server. payload = base64(authTag + ciphertext) nonce = base64(random IV) keyVersion enables future rotation support."
1216
+ },
1217
+ "LogPayload": {
1218
+ "type": "object",
1219
+ "properties": {
1220
+ "level": {
1221
+ "$ref": "#/definitions/LogLevel"
1222
+ },
1223
+ "message": {
1224
+ "type": "string"
1225
+ },
1226
+ "requestId": {
1227
+ "type": "string"
1228
+ },
1229
+ "traceId": {
1230
+ "type": "string"
1231
+ },
1232
+ "context": {
1233
+ "$ref": "#/definitions/LogContext"
1234
+ },
1235
+ "metadata": {
1357
1236
  "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
- }
1237
+ "additionalProperties": {}
1238
+ }
1239
+ },
1240
+ "required": [
1241
+ "level",
1242
+ "message"
1243
+ ],
1244
+ "additionalProperties": false,
1245
+ "description": "Plaintext shape of log content: a plugin encrypts it into LogEncrypted, and the server never sees it."
1246
+ },
1247
+ "LogData": {
1248
+ "type": "object",
1249
+ "properties": {
1250
+ "requestId": {
1251
+ "type": "string"
1385
1252
  },
1386
- "app:created": {
1387
- "$comment": "(app: SocketAppPayload) => void",
1253
+ "traceId": {
1254
+ "type": "string"
1255
+ },
1256
+ "context": {
1257
+ "$ref": "#/definitions/LogContext"
1258
+ },
1259
+ "metadata": {
1388
1260
  "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
- }
1261
+ "additionalProperties": {}
1262
+ }
1263
+ },
1264
+ "additionalProperties": {},
1265
+ "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."
1266
+ },
1267
+ "LogIngestBody": {
1268
+ "type": "object",
1269
+ "properties": {
1270
+ "level": {
1271
+ "$ref": "#/definitions/LogLevel"
1272
+ },
1273
+ "encrypted": {
1274
+ "$ref": "#/definitions/LogEncrypted"
1275
+ }
1276
+ },
1277
+ "required": [
1278
+ "level",
1279
+ "encrypted"
1280
+ ],
1281
+ "additionalProperties": false,
1282
+ "description": "POST /logs' body. The level stays plaintext for filtering and pushes; everything else is in `encrypted`."
1283
+ },
1284
+ "LogDeliveryOutcome": {
1285
+ "type": "string",
1286
+ "enum": [
1287
+ "sent",
1288
+ "retry",
1289
+ "drop",
1290
+ "pause"
1291
+ ],
1292
+ "description": "What a plugin does with a log after POST /logs answers (see logDelivery)."
1293
+ },
1294
+ "LogRecord": {
1295
+ "type": "object",
1296
+ "properties": {
1297
+ "id": {
1298
+ "type": "string"
1299
+ },
1300
+ "createdAt": {
1301
+ "type": "string"
1302
+ },
1303
+ "updatedAt": {
1304
+ "type": "string"
1305
+ },
1306
+ "appId": {
1307
+ "type": "string"
1308
+ },
1309
+ "level": {
1310
+ "$ref": "#/definitions/LogLevel"
1311
+ },
1312
+ "encrypted": {
1313
+ "$ref": "#/definitions/LogEncrypted"
1314
+ }
1315
+ },
1316
+ "required": [
1317
+ "id",
1318
+ "createdAt",
1319
+ "updatedAt",
1320
+ "appId",
1321
+ "level",
1322
+ "encrypted"
1323
+ ],
1324
+ "additionalProperties": false,
1325
+ "description": "API response shape for a stored log. level is plaintext for server-side notification routing; all content is inside encrypted."
1326
+ },
1327
+ "LogsQuery": {
1328
+ "type": "object",
1329
+ "properties": {
1330
+ "appIds": {
1331
+ "type": "array",
1332
+ "items": {
1333
+ "type": "string"
1402
1334
  }
1403
1335
  },
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
- }
1336
+ "levels": {
1337
+ "type": "array",
1338
+ "items": {
1339
+ "type": "number"
1420
1340
  }
1421
1341
  },
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."
1342
+ "from": {
1343
+ "type": "string"
1449
1344
  },
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."
1345
+ "to": {
1346
+ "type": "string"
1471
1347
  },
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."
1348
+ "sort": {
1349
+ "type": "string"
1475
1350
  },
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."
1351
+ "limit": {
1352
+ "type": "number"
1519
1353
  },
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
- }
1354
+ "cursor": {
1355
+ "type": "string"
1356
+ }
1357
+ },
1358
+ "additionalProperties": false,
1359
+ "description": "Sync-protocol params for GET /logs."
1360
+ },
1361
+ "NoteEncrypted": {
1362
+ "type": "object",
1363
+ "properties": {
1364
+ "payload": {
1365
+ "type": "string"
1366
+ },
1367
+ "nonce": {
1368
+ "type": "string"
1369
+ },
1370
+ "keyVersion": {
1371
+ "type": "number"
1546
1372
  }
1547
1373
  },
1548
1374
  "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"
1375
+ "payload",
1376
+ "nonce",
1377
+ "keyVersion"
1562
1378
  ],
1563
1379
  "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."
1380
+ "description": "Encrypted note content. payload = base64(authTag + ciphertext) nonce = base64(random IV) keyVersion enables future key rotation."
1565
1381
  },
1566
- "ClientToServerEvents": {
1382
+ "NoteRecord": {
1567
1383
  "type": "object",
1568
1384
  "properties": {
1569
- "listMyApps": {
1570
- "$comment": "() => void"
1385
+ "id": {
1386
+ "type": "string"
1387
+ },
1388
+ "createdAt": {
1389
+ "type": "string"
1390
+ },
1391
+ "updatedAt": {
1392
+ "type": "string"
1393
+ },
1394
+ "appId": {
1395
+ "type": "string"
1396
+ },
1397
+ "userId": {
1398
+ "type": "string"
1399
+ },
1400
+ "logId": {
1401
+ "type": "string"
1402
+ },
1403
+ "encrypted": {
1404
+ "$ref": "#/definitions/NoteEncrypted"
1571
1405
  }
1572
1406
  },
1573
1407
  "required": [
1574
- "listMyApps"
1408
+ "id",
1409
+ "createdAt",
1410
+ "updatedAt",
1411
+ "appId",
1412
+ "userId",
1413
+ "encrypted"
1575
1414
  ],
1576
1415
  "additionalProperties": false,
1577
- "description": "What a client sends Lumber-Socket."
1416
+ "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."
1578
1417
  },
1579
- "ResponseMetadata": {
1418
+ "NotesQuery": {
1580
1419
  "type": "object",
1581
1420
  "properties": {
1582
- "requestId": {
1421
+ "appId": {
1583
1422
  "type": "string"
1584
1423
  },
1585
- "durationMs": {
1586
- "type": "number"
1424
+ "teamId": {
1425
+ "type": "string"
1587
1426
  },
1588
- "warnings": {
1589
- "type": "array",
1590
- "items": {
1591
- "type": "string"
1592
- }
1427
+ "userId": {
1428
+ "type": "string"
1593
1429
  },
1594
- "cursor": {
1595
- "type": [
1596
- "string",
1597
- "null"
1598
- ]
1430
+ "logId": {
1431
+ "type": "string"
1599
1432
  },
1600
- "hasMore": {
1601
- "type": "boolean"
1433
+ "from": {
1434
+ "type": "string"
1602
1435
  },
1603
- "count": {
1604
- "type": "number"
1436
+ "to": {
1437
+ "type": "string"
1605
1438
  },
1606
- "hiddenCount": {
1439
+ "sort": {
1440
+ "type": "string"
1441
+ },
1442
+ "limit": {
1607
1443
  "type": "number"
1444
+ },
1445
+ "cursor": {
1446
+ "type": "string"
1608
1447
  }
1609
1448
  },
1610
- "required": [
1611
- "requestId",
1612
- "durationMs"
1613
- ],
1614
- "additionalProperties": false
1449
+ "additionalProperties": false,
1450
+ "description": "Filter params for GET /notes."
1615
1451
  },
1616
1452
  "PushData": {
1617
1453
  "anyOf": [
@@ -1768,506 +1604,714 @@
1768
1604
  "keys_shared"
1769
1605
  ]
1770
1606
  },
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": {
1607
+ "ResponseMetadata": {
1858
1608
  "type": "object",
1859
1609
  "properties": {
1860
- "id": {
1861
- "type": "string"
1862
- },
1863
- "userId": {
1864
- "type": "string"
1865
- },
1866
- "sid": {
1867
- "type": "string"
1868
- },
1869
- "holderMessage": {
1610
+ "requestId": {
1870
1611
  "type": "string"
1871
1612
  },
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
- ]
1613
+ "durationMs": {
1614
+ "type": "number"
1887
1615
  },
1888
- "tag": {
1889
- "type": [
1890
- "string",
1891
- "null"
1892
- ]
1616
+ "warnings": {
1617
+ "type": "array",
1618
+ "items": {
1619
+ "type": "string"
1620
+ }
1893
1621
  },
1894
- "answeredAt": {
1622
+ "cursor": {
1895
1623
  "type": [
1896
1624
  "string",
1897
1625
  "null"
1898
1626
  ]
1899
1627
  },
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
- ]
1628
+ "hasMore": {
1629
+ "type": "boolean"
1915
1630
  },
1916
- "expiresAt": {
1917
- "type": "string"
1631
+ "count": {
1632
+ "type": "number"
1918
1633
  },
1919
- "createdAt": {
1920
- "type": "string"
1634
+ "hiddenCount": {
1635
+ "type": "number"
1921
1636
  }
1922
1637
  },
1923
1638
  "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"
1639
+ "requestId",
1640
+ "durationMs"
1937
1641
  ],
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."
1642
+ "additionalProperties": false
1940
1643
  },
1941
- "ClaimantKeyCheck": {
1644
+ "SocketAppPayload": {
1942
1645
  "type": "object",
1943
1646
  "properties": {
1944
1647
  "id": {
1945
1648
  "type": "string"
1946
1649
  },
1947
- "holderId": {
1650
+ "name": {
1948
1651
  "type": "string"
1949
1652
  },
1950
- "holderName": {
1653
+ "teamId": {
1951
1654
  "type": "string"
1952
1655
  },
1953
- "sid": {
1656
+ "color": {
1954
1657
  "type": "string"
1955
1658
  },
1956
- "holderMessage": {
1659
+ "imageUrl": {
1957
1660
  "type": "string"
1958
1661
  },
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": {
1662
+ "createdAt": {
1982
1663
  "type": "string"
1983
1664
  },
1984
- "createdAt": {
1665
+ "updatedAt": {
1985
1666
  "type": "string"
1986
1667
  }
1987
1668
  },
1988
1669
  "required": [
1989
1670
  "id",
1990
- "holderId",
1991
- "holderName",
1992
- "sid",
1993
- "holderMessage",
1994
- "answeredAt",
1995
- "result",
1996
- "finishedAt",
1997
- "expiresAt",
1998
- "createdAt"
1671
+ "name",
1672
+ "createdAt",
1673
+ "updatedAt"
1999
1674
  ],
2000
1675
  "additionalProperties": false,
2001
- "description": "A key check waiting on the caller's answer, or how a recent answer went (GET /key-checks/for-me)."
1676
+ "description": "An app as the socket sends it: no wrapped keys, so a client refetches through the API for its own copy."
2002
1677
  },
2003
- "InvitationRecord": {
1678
+ "ServerToClientEvents": {
2004
1679
  "type": "object",
2005
1680
  "properties": {
2006
- "id": {
2007
- "type": "string"
1681
+ "log:created": {
1682
+ "$comment": "(log: Omit<LogRecord, 'updatedAt'> & { updatedAt?: string }) => void",
1683
+ "type": "object",
1684
+ "properties": {
1685
+ "namedArgs": {
1686
+ "type": "object",
1687
+ "properties": {
1688
+ "log": {
1689
+ "type": "object",
1690
+ "additionalProperties": false,
1691
+ "properties": {
1692
+ "updatedAt": {
1693
+ "type": "string"
1694
+ },
1695
+ "id": {
1696
+ "type": "string"
1697
+ },
1698
+ "createdAt": {
1699
+ "type": "string"
1700
+ },
1701
+ "appId": {
1702
+ "type": "string"
1703
+ },
1704
+ "level": {
1705
+ "$ref": "#/definitions/LogLevel"
1706
+ },
1707
+ "encrypted": {
1708
+ "$ref": "#/definitions/LogEncrypted"
1709
+ }
1710
+ },
1711
+ "required": [
1712
+ "appId",
1713
+ "createdAt",
1714
+ "encrypted",
1715
+ "id",
1716
+ "level"
1717
+ ]
1718
+ }
1719
+ },
1720
+ "required": [
1721
+ "log"
1722
+ ],
1723
+ "additionalProperties": false
1724
+ }
1725
+ }
2008
1726
  },
2009
- "teamId": {
2010
- "type": [
2011
- "string",
2012
- "null"
2013
- ]
1727
+ "flag:created": {
1728
+ "$comment": "(flag: Omit<FlagRecord, 'updatedAt'> & { updatedAt?: string }) => void",
1729
+ "type": "object",
1730
+ "properties": {
1731
+ "namedArgs": {
1732
+ "type": "object",
1733
+ "properties": {
1734
+ "flag": {
1735
+ "type": "object",
1736
+ "additionalProperties": false,
1737
+ "properties": {
1738
+ "updatedAt": {
1739
+ "type": "string"
1740
+ },
1741
+ "id": {
1742
+ "type": "string"
1743
+ },
1744
+ "createdAt": {
1745
+ "type": "string"
1746
+ },
1747
+ "logId": {
1748
+ "type": "string"
1749
+ },
1750
+ "userId": {
1751
+ "type": "string"
1752
+ }
1753
+ },
1754
+ "required": [
1755
+ "createdAt",
1756
+ "id",
1757
+ "logId",
1758
+ "userId"
1759
+ ]
1760
+ }
1761
+ },
1762
+ "required": [
1763
+ "flag"
1764
+ ],
1765
+ "additionalProperties": false
1766
+ }
1767
+ }
2014
1768
  },
2015
- "appId": {
2016
- "type": [
2017
- "string",
2018
- "null"
2019
- ]
1769
+ "flag:deleted": {
1770
+ "$comment": "(flag: Pick<FlagRecord, 'id'>) => void",
1771
+ "type": "object",
1772
+ "properties": {
1773
+ "namedArgs": {
1774
+ "type": "object",
1775
+ "properties": {
1776
+ "flag": {
1777
+ "type": "object",
1778
+ "properties": {
1779
+ "id": {
1780
+ "type": "string"
1781
+ }
1782
+ },
1783
+ "required": [
1784
+ "id"
1785
+ ],
1786
+ "additionalProperties": false
1787
+ }
1788
+ },
1789
+ "required": [
1790
+ "flag"
1791
+ ],
1792
+ "additionalProperties": false
1793
+ }
1794
+ }
2020
1795
  },
2021
- "email": {
2022
- "type": "string"
1796
+ "note:created": {
1797
+ "$comment": "(note: NoteRecord) => void",
1798
+ "type": "object",
1799
+ "properties": {
1800
+ "namedArgs": {
1801
+ "type": "object",
1802
+ "properties": {
1803
+ "note": {
1804
+ "$ref": "#/definitions/NoteRecord"
1805
+ }
1806
+ },
1807
+ "required": [
1808
+ "note"
1809
+ ],
1810
+ "additionalProperties": false
1811
+ }
1812
+ }
2023
1813
  },
2024
- "role": {
2025
- "type": "string"
1814
+ "note:updated": {
1815
+ "$comment": "(note: NoteRecord) => void",
1816
+ "type": "object",
1817
+ "properties": {
1818
+ "namedArgs": {
1819
+ "type": "object",
1820
+ "properties": {
1821
+ "note": {
1822
+ "$ref": "#/definitions/NoteRecord"
1823
+ }
1824
+ },
1825
+ "required": [
1826
+ "note"
1827
+ ],
1828
+ "additionalProperties": false
1829
+ }
1830
+ }
1831
+ },
1832
+ "note:deleted": {
1833
+ "$comment": "(note: Pick<NoteRecord, 'id' | 'appId'>) => void",
1834
+ "type": "object",
1835
+ "properties": {
1836
+ "namedArgs": {
1837
+ "type": "object",
1838
+ "properties": {
1839
+ "note": {
1840
+ "type": "object",
1841
+ "properties": {
1842
+ "id": {
1843
+ "type": "string"
1844
+ },
1845
+ "appId": {
1846
+ "type": "string"
1847
+ }
1848
+ },
1849
+ "required": [
1850
+ "id",
1851
+ "appId"
1852
+ ],
1853
+ "additionalProperties": false
1854
+ }
1855
+ },
1856
+ "required": [
1857
+ "note"
1858
+ ],
1859
+ "additionalProperties": false
1860
+ }
1861
+ }
2026
1862
  },
2027
- "sentAt": {
2028
- "type": [
2029
- "string",
2030
- "null"
2031
- ],
2032
- "description": "Null when its email couldn't be sent."
1863
+ "app:created": {
1864
+ "$comment": "(app: SocketAppPayload) => void",
1865
+ "type": "object",
1866
+ "properties": {
1867
+ "namedArgs": {
1868
+ "type": "object",
1869
+ "properties": {
1870
+ "app": {
1871
+ "$ref": "#/definitions/SocketAppPayload"
1872
+ }
1873
+ },
1874
+ "required": [
1875
+ "app"
1876
+ ],
1877
+ "additionalProperties": false
1878
+ }
1879
+ }
2033
1880
  },
2034
- "expiresAt": {
2035
- "type": "string"
1881
+ "app:updated": {
1882
+ "$comment": "(app: SocketAppPayload) => void",
1883
+ "type": "object",
1884
+ "properties": {
1885
+ "namedArgs": {
1886
+ "type": "object",
1887
+ "properties": {
1888
+ "app": {
1889
+ "$ref": "#/definitions/SocketAppPayload"
1890
+ }
1891
+ },
1892
+ "required": [
1893
+ "app"
1894
+ ],
1895
+ "additionalProperties": false
1896
+ }
1897
+ }
2036
1898
  },
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"
1899
+ "app:deleted": {
1900
+ "$comment": "(app: Pick<AppRecord, 'id'>) => void",
1901
+ "type": "object",
1902
+ "properties": {
1903
+ "namedArgs": {
1904
+ "type": "object",
1905
+ "properties": {
1906
+ "app": {
1907
+ "type": "object",
1908
+ "properties": {
1909
+ "id": {
1910
+ "type": "string"
1911
+ }
1912
+ },
1913
+ "required": [
1914
+ "id"
1915
+ ],
1916
+ "additionalProperties": false
1917
+ }
1918
+ },
1919
+ "required": [
1920
+ "app"
1921
+ ],
1922
+ "additionalProperties": false
1923
+ }
1924
+ },
1925
+ "description": "Deleted, or kept to its team's owner and so gone for the rest of the team."
2080
1926
  },
2081
- "platform": {
2082
- "$ref": "#/definitions/DeviceLinkPlatform"
1927
+ "access:changed": {
1928
+ "$comment": "(payload: Record<string, never>) => void",
1929
+ "type": "object",
1930
+ "properties": {
1931
+ "namedArgs": {
1932
+ "type": "object",
1933
+ "properties": {
1934
+ "payload": {
1935
+ "type": "object",
1936
+ "additionalProperties": {
1937
+ "not": {}
1938
+ }
1939
+ }
1940
+ },
1941
+ "required": [
1942
+ "payload"
1943
+ ],
1944
+ "additionalProperties": false
1945
+ }
1946
+ },
1947
+ "description": "Someone joined or left a team or app the user is on. Carries nothing; sent on every plan."
2083
1948
  },
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."
1949
+ "socket:reconnected": {
1950
+ "$comment": "() => void",
1951
+ "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."
2087
1952
  },
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"
1953
+ "myApps": {
1954
+ "$comment": "(payload: { apps: Pick<AppRecord, 'id' | 'name'>[] }) => void",
1955
+ "type": "object",
1956
+ "properties": {
1957
+ "namedArgs": {
1958
+ "type": "object",
1959
+ "properties": {
1960
+ "payload": {
1961
+ "type": "object",
1962
+ "properties": {
1963
+ "apps": {
1964
+ "type": "array",
1965
+ "items": {
1966
+ "type": "object",
1967
+ "properties": {
1968
+ "id": {
1969
+ "type": "string"
1970
+ },
1971
+ "name": {
1972
+ "type": "string"
1973
+ }
1974
+ },
1975
+ "required": [
1976
+ "id",
1977
+ "name"
1978
+ ],
1979
+ "additionalProperties": false
1980
+ }
1981
+ }
1982
+ },
1983
+ "required": [
1984
+ "apps"
1985
+ ],
1986
+ "additionalProperties": false
1987
+ }
1988
+ },
1989
+ "required": [
1990
+ "payload"
1991
+ ],
1992
+ "additionalProperties": false
1993
+ }
1994
+ },
1995
+ "description": "The answer to `listMyApps`: the apps the user gets live updates for."
2106
1996
  },
2107
- "expiresAt": {
2108
- "type": "string"
1997
+ "error": {
1998
+ "$comment": "(payload: { message: string }) => void",
1999
+ "type": "object",
2000
+ "properties": {
2001
+ "namedArgs": {
2002
+ "type": "object",
2003
+ "properties": {
2004
+ "payload": {
2005
+ "type": "object",
2006
+ "properties": {
2007
+ "message": {
2008
+ "type": "string"
2009
+ }
2010
+ },
2011
+ "required": [
2012
+ "message"
2013
+ ],
2014
+ "additionalProperties": false
2015
+ }
2016
+ },
2017
+ "required": [
2018
+ "payload"
2019
+ ],
2020
+ "additionalProperties": false
2021
+ }
2022
+ }
2109
2023
  }
2110
2024
  },
2111
2025
  "required": [
2112
- "id",
2113
- "expiresAt"
2026
+ "log:created",
2027
+ "flag:created",
2028
+ "flag:deleted",
2029
+ "note:created",
2030
+ "note:updated",
2031
+ "note:deleted",
2032
+ "app:created",
2033
+ "app:updated",
2034
+ "app:deleted",
2035
+ "access:changed",
2036
+ "socket:reconnected",
2037
+ "myApps",
2038
+ "error"
2114
2039
  ],
2115
2040
  "additionalProperties": false,
2116
- "description": "Response from POST /device-links."
2041
+ "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."
2117
2042
  },
2118
- "DeviceLinkApproveBody": {
2043
+ "ClientToServerEvents": {
2119
2044
  "type": "object",
2120
2045
  "properties": {
2121
- "encryptedKey": {
2122
- "type": "string"
2123
- },
2124
- "deliveryTag": {
2125
- "type": "string"
2046
+ "listMyApps": {
2047
+ "$comment": "() => void"
2126
2048
  }
2127
2049
  },
2128
2050
  "required": [
2129
- "encryptedKey",
2130
- "deliveryTag"
2051
+ "listMyApps"
2131
2052
  ],
2132
2053
  "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."
2054
+ "description": "What a client sends Lumber-Socket."
2134
2055
  },
2135
- "DeviceLinkRecord": {
2056
+ "TeamRecord": {
2136
2057
  "type": "object",
2137
2058
  "properties": {
2138
2059
  "id": {
2139
2060
  "type": "string"
2140
2061
  },
2141
- "status": {
2142
- "$ref": "#/definitions/DeviceLinkStatus"
2062
+ "createdAt": {
2063
+ "type": "string"
2143
2064
  },
2144
- "encryptedKey": {
2065
+ "updatedAt": {
2145
2066
  "type": "string"
2146
2067
  },
2147
- "deliveryTag": {
2068
+ "name": {
2148
2069
  "type": "string"
2149
2070
  },
2150
- "expiresAt": {
2071
+ "color": {
2151
2072
  "type": "string"
2152
2073
  },
2153
- "sid": {
2074
+ "imageUrl": {
2075
+ "type": "string"
2076
+ },
2077
+ "subscriptionLevel": {
2078
+ "$ref": "#/definitions/SubscriptionLevel"
2079
+ },
2080
+ "subscriptionEnd": {
2154
2081
  "type": [
2155
2082
  "string",
2156
2083
  "null"
2157
- ]
2084
+ ],
2085
+ "description": "When a canceled plan stops; null while it renews."
2158
2086
  },
2159
- "holderMessage": {
2087
+ "role": {
2088
+ "type": "string",
2089
+ "enum": [
2090
+ "owner",
2091
+ "admin",
2092
+ "member"
2093
+ ],
2094
+ "description": "GET /teams only: the caller's role on the team."
2095
+ },
2096
+ "memberId": {
2097
+ "type": "string",
2098
+ "description": "GET /teams only: the caller's membership id, which leaving the team deletes."
2099
+ },
2100
+ "appCount": {
2160
2101
  "type": [
2161
- "string",
2102
+ "number",
2162
2103
  "null"
2163
- ]
2104
+ ],
2105
+ "description": "GET /teams only: the team's apps, as creating one counts them. Null unless the caller is its owner or an admin."
2164
2106
  },
2165
- "answeredAt": {
2107
+ "logsThisMonth": {
2166
2108
  "type": [
2167
- "string",
2109
+ "number",
2168
2110
  "null"
2169
- ]
2111
+ ],
2112
+ "description": "GET /teams only: logs its apps sent this UTC month, the monthly cap's count. Null unless owner or admin."
2170
2113
  }
2171
2114
  },
2172
2115
  "required": [
2173
2116
  "id",
2174
- "status",
2175
- "expiresAt"
2117
+ "createdAt",
2118
+ "updatedAt",
2119
+ "name",
2120
+ "subscriptionLevel"
2176
2121
  ],
2177
2122
  "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."
2123
+ "description": "An organization that owns apps and pays for member usage. Team subscriptionLevel takes precedence over individual member levels."
2179
2124
  },
2180
- "PendingDeviceLink": {
2125
+ "SubscriptionLevel": {
2126
+ "type": "number",
2127
+ "enum": [
2128
+ 0,
2129
+ 1,
2130
+ 2,
2131
+ 3
2132
+ ],
2133
+ "description": "User subscription level. Mirrors User.subscriptionLevel in Lumber-API."
2134
+ },
2135
+ "TeamMemberRecord": {
2181
2136
  "type": "object",
2182
2137
  "properties": {
2183
2138
  "id": {
2184
2139
  "type": "string"
2185
2140
  },
2186
- "deviceName": {
2141
+ "createdAt": {
2187
2142
  "type": "string"
2188
2143
  },
2189
- "platform": {
2190
- "$ref": "#/definitions/DeviceLinkPlatform"
2144
+ "updatedAt": {
2145
+ "type": "string"
2191
2146
  },
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."
2147
+ "teamId": {
2148
+ "type": "string"
2195
2149
  },
2196
- "sid": {
2197
- "type": [
2198
- "string",
2199
- "null"
2150
+ "userId": {
2151
+ "type": "string"
2152
+ },
2153
+ "role": {
2154
+ "type": "string",
2155
+ "enum": [
2156
+ "owner",
2157
+ "admin",
2158
+ "member"
2200
2159
  ]
2201
2160
  },
2202
- "holderMessage": {
2161
+ "name": {
2203
2162
  "type": [
2204
2163
  "string",
2205
2164
  "null"
2206
- ]
2165
+ ],
2166
+ "description": "Listed members only: their display name. Never their email address."
2207
2167
  },
2208
- "holderState": {
2168
+ "handle": {
2209
2169
  "type": [
2210
2170
  "string",
2211
2171
  "null"
2212
- ]
2172
+ ],
2173
+ "description": "Listed members only: the part of their email before the"
2213
2174
  },
2214
- "claimantMessage": {
2175
+ "color": {
2215
2176
  "type": [
2216
2177
  "string",
2217
2178
  "null"
2218
2179
  ]
2219
2180
  },
2220
- "tag": {
2181
+ "imageUrl": {
2221
2182
  "type": [
2222
2183
  "string",
2223
2184
  "null"
2224
2185
  ]
2186
+ }
2187
+ },
2188
+ "required": [
2189
+ "id",
2190
+ "createdAt",
2191
+ "updatedAt",
2192
+ "teamId",
2193
+ "userId",
2194
+ "role"
2195
+ ],
2196
+ "additionalProperties": false,
2197
+ "description": "Membership of a user in a team."
2198
+ },
2199
+ "SubscriptionPlanRecord": {
2200
+ "type": "object",
2201
+ "properties": {
2202
+ "name": {
2203
+ "type": "string"
2225
2204
  },
2226
- "answeredAt": {
2205
+ "priceId": {
2227
2206
  "type": [
2228
2207
  "string",
2229
2208
  "null"
2230
2209
  ]
2231
- },
2232
- "expiresAt": {
2210
+ }
2211
+ },
2212
+ "required": [
2213
+ "name",
2214
+ "priceId"
2215
+ ],
2216
+ "additionalProperties": false,
2217
+ "description": "A plan GET /subscriptions offers, with its Stripe price (none for Hobby)."
2218
+ },
2219
+ "TeamsQuery": {
2220
+ "type": "object",
2221
+ "properties": {
2222
+ "sort": {
2233
2223
  "type": "string"
2234
2224
  },
2235
- "createdAt": {
2225
+ "limit": {
2226
+ "type": "number"
2227
+ },
2228
+ "cursor": {
2236
2229
  "type": "string"
2237
2230
  }
2238
2231
  },
2232
+ "additionalProperties": false,
2233
+ "description": "Filter params for GET /teams."
2234
+ },
2235
+ "PushThreshold": {
2236
+ "anyOf": [
2237
+ {
2238
+ "$ref": "#/definitions/LogLevel"
2239
+ },
2240
+ {
2241
+ "type": "number",
2242
+ "const": 5
2243
+ }
2244
+ ],
2245
+ "description": "The lowest log level that pushes, or PUSH_OFF. Stored as a notification preference's `minimumLevel`."
2246
+ },
2247
+ "SubscriptionLevelName": {
2248
+ "type": "string",
2249
+ "enum": [
2250
+ "hobby",
2251
+ "dev",
2252
+ "pro",
2253
+ "enterprise"
2254
+ ],
2255
+ "description": "String name for each subscription level, suitable for display and Redux state."
2256
+ },
2257
+ "SubscriptionLimits": {
2258
+ "type": "object",
2259
+ "properties": {
2260
+ "apps": {
2261
+ "type": "number"
2262
+ },
2263
+ "seats": {
2264
+ "type": "number"
2265
+ },
2266
+ "apiKeys": {
2267
+ "type": "number"
2268
+ },
2269
+ "logRetentionDays": {
2270
+ "type": "number"
2271
+ },
2272
+ "monthlyLogs": {
2273
+ "type": "number"
2274
+ },
2275
+ "realtimeLogs": {
2276
+ "type": "boolean",
2277
+ "description": "Whether real-time WebSocket log streaming is available."
2278
+ },
2279
+ "minPushLevel": {
2280
+ "type": "number",
2281
+ "description": "Minimum log level that triggers a push notification. Infinity means push is disabled."
2282
+ }
2283
+ },
2239
2284
  "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"
2285
+ "apps",
2286
+ "seats",
2287
+ "apiKeys",
2288
+ "logRetentionDays",
2289
+ "monthlyLogs",
2290
+ "realtimeLogs",
2291
+ "minPushLevel"
2252
2292
  ],
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."
2293
+ "additionalProperties": false
2255
2294
  },
2256
- "ApiErrorBody": {
2295
+ "SubscriptionPlan": {
2257
2296
  "type": "object",
2258
2297
  "properties": {
2259
- "message": {
2260
- "type": "string"
2298
+ "name": {
2299
+ "type": "string",
2300
+ "description": "Display name, as every surface shows it."
2261
2301
  },
2262
- "code": {
2263
- "$ref": "#/definitions/ApiErrorCode"
2302
+ "monthlyPrice": {
2303
+ "type": [
2304
+ "number",
2305
+ "null"
2306
+ ],
2307
+ "description": "Monthly price in US dollars: 0 for free, null when priced per customer."
2264
2308
  }
2265
2309
  },
2266
2310
  "required": [
2267
- "message"
2311
+ "name",
2312
+ "monthlyPrice"
2268
2313
  ],
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."
2314
+ "additionalProperties": false
2271
2315
  }
2272
2316
  }
2273
2317
  }