@nosana/types 2.12.0 → 2.14.1

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,3429 @@
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "Nosana Node API",
5
+ "version": "1.0.0",
6
+ "description": "The HTTP API every Nosana node serves on its own host for the jobs it runs. Job routes are authorized with the standard Nosana authorization header signed by the job's owner; nodes never accept API keys. The node also multiplexes WebSocket routes over one socket, described under `x-websocket`."
7
+ },
8
+ "servers": [
9
+ {
10
+ "url": "https://{node}.{domain}",
11
+ "description": "One node, addressed by its Solana address.",
12
+ "variables": {
13
+ "node": {
14
+ "default": "NODE_ADDRESS",
15
+ "description": "The node's address."
16
+ },
17
+ "domain": {
18
+ "default": "node.k8s.prd.nos.ci",
19
+ "enum": [
20
+ "node.k8s.prd.nos.ci",
21
+ "node.k8s.dev.nos.ci"
22
+ ],
23
+ "description": "Mainnet or devnet nodes."
24
+ }
25
+ }
26
+ }
27
+ ],
28
+ "tags": [
29
+ {
30
+ "name": "Node",
31
+ "description": "About the node itself. Public."
32
+ },
33
+ {
34
+ "name": "Job",
35
+ "description": "A job the node runs, as the node sees it."
36
+ },
37
+ {
38
+ "name": "SSH",
39
+ "description": "Which SSH keys may reach a job."
40
+ }
41
+ ],
42
+ "paths": {
43
+ "/": {
44
+ "get": {
45
+ "operationId": "getNodeAddress",
46
+ "tags": [
47
+ "Node"
48
+ ],
49
+ "summary": "The node's address",
50
+ "description": "Answers with the node's own address, as a liveness check.",
51
+ "parameters": [],
52
+ "responses": {
53
+ "200": {
54
+ "description": "The node's address.",
55
+ "content": {
56
+ "text/plain": {
57
+ "schema": {
58
+ "type": "string"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ },
64
+ "security": []
65
+ }
66
+ },
67
+ "/node/info": {
68
+ "get": {
69
+ "operationId": "getNodeInfo",
70
+ "tags": [
71
+ "Node"
72
+ ],
73
+ "summary": "Node info",
74
+ "description": "What the node is, what it runs on and which images and volumes it holds. Public.",
75
+ "parameters": [],
76
+ "responses": {
77
+ "200": {
78
+ "description": "The node's info.",
79
+ "content": {
80
+ "application/json": {
81
+ "schema": {
82
+ "$ref": "#/components/schemas/NodeInfo"
83
+ }
84
+ }
85
+ }
86
+ }
87
+ },
88
+ "security": []
89
+ }
90
+ },
91
+ "/job/{job}/info": {
92
+ "get": {
93
+ "operationId": "streamJobInfo",
94
+ "tags": [
95
+ "Job"
96
+ ],
97
+ "summary": "Stream job info",
98
+ "description": "Server-sent events: the job's flow without logs plus its task status, sent on open and whenever it changes, until the job ends.",
99
+ "parameters": [
100
+ {
101
+ "name": "job",
102
+ "in": "path",
103
+ "required": true,
104
+ "description": "The job's address.",
105
+ "schema": {
106
+ "$ref": "#/components/schemas/Address"
107
+ }
108
+ }
109
+ ],
110
+ "responses": {
111
+ "200": {
112
+ "description": "A stream of `JobInfo` frames as unnamed `message` events.",
113
+ "content": {
114
+ "text/event-stream": {
115
+ "schema": {
116
+ "type": "string"
117
+ }
118
+ }
119
+ }
120
+ },
121
+ "401": {
122
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
123
+ "content": {
124
+ "application/json": {
125
+ "schema": {
126
+ "$ref": "#/components/schemas/NodeError"
127
+ }
128
+ }
129
+ }
130
+ },
131
+ "404": {
132
+ "description": "No running job with that address, or the id is not part of it.",
133
+ "content": {
134
+ "application/json": {
135
+ "schema": {
136
+ "$ref": "#/components/schemas/NodeError"
137
+ }
138
+ }
139
+ }
140
+ }
141
+ },
142
+ "security": [
143
+ {
144
+ "NosanaAuthorization": []
145
+ }
146
+ ]
147
+ }
148
+ },
149
+ "/job/{job}/job-definition": {
150
+ "get": {
151
+ "operationId": "getJobDefinition",
152
+ "tags": [
153
+ "Job"
154
+ ],
155
+ "summary": "Get the job definition",
156
+ "description": "The job definition. Fails while the job still waits for one. The node labels the body as text, but it is JSON.",
157
+ "parameters": [
158
+ {
159
+ "name": "job",
160
+ "in": "path",
161
+ "required": true,
162
+ "description": "The job's address.",
163
+ "schema": {
164
+ "$ref": "#/components/schemas/Address"
165
+ }
166
+ }
167
+ ],
168
+ "responses": {
169
+ "200": {
170
+ "description": "The job definition.",
171
+ "content": {
172
+ "application/json": {
173
+ "schema": {
174
+ "$ref": "#/components/schemas/JobDefinition"
175
+ }
176
+ }
177
+ }
178
+ },
179
+ "400": {
180
+ "description": "No running job with that address, or the id is not part of it.",
181
+ "content": {
182
+ "application/json": {
183
+ "schema": {
184
+ "$ref": "#/components/schemas/NodeError"
185
+ }
186
+ }
187
+ }
188
+ },
189
+ "401": {
190
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
191
+ "content": {
192
+ "application/json": {
193
+ "schema": {
194
+ "$ref": "#/components/schemas/NodeError"
195
+ }
196
+ }
197
+ }
198
+ }
199
+ },
200
+ "security": [
201
+ {
202
+ "NosanaAuthorization": []
203
+ }
204
+ ]
205
+ },
206
+ "post": {
207
+ "operationId": "setJobDefinition",
208
+ "tags": [
209
+ "Job"
210
+ ],
211
+ "summary": "Send the job definition",
212
+ "description": "Accepted only while the job waits for its definition.",
213
+ "parameters": [
214
+ {
215
+ "name": "job",
216
+ "in": "path",
217
+ "required": true,
218
+ "description": "The job's address.",
219
+ "schema": {
220
+ "$ref": "#/components/schemas/Address"
221
+ }
222
+ }
223
+ ],
224
+ "requestBody": {
225
+ "required": true,
226
+ "content": {
227
+ "application/json": {
228
+ "schema": {
229
+ "$ref": "#/components/schemas/JobDefinition"
230
+ }
231
+ }
232
+ }
233
+ },
234
+ "responses": {
235
+ "200": {
236
+ "description": "Acknowledged.",
237
+ "content": {
238
+ "text/plain": {
239
+ "schema": {
240
+ "type": "string"
241
+ }
242
+ }
243
+ }
244
+ },
245
+ "400": {
246
+ "description": "No running job with that address, or the id is not part of it.",
247
+ "content": {
248
+ "application/json": {
249
+ "schema": {
250
+ "$ref": "#/components/schemas/NodeError"
251
+ }
252
+ }
253
+ }
254
+ },
255
+ "401": {
256
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
257
+ "content": {
258
+ "application/json": {
259
+ "schema": {
260
+ "$ref": "#/components/schemas/NodeError"
261
+ }
262
+ }
263
+ }
264
+ }
265
+ },
266
+ "security": [
267
+ {
268
+ "NosanaAuthorization": []
269
+ }
270
+ ]
271
+ }
272
+ },
273
+ "/job/{job}/results": {
274
+ "get": {
275
+ "operationId": "getJobResults",
276
+ "tags": [
277
+ "Job"
278
+ ],
279
+ "summary": "Collect the job results",
280
+ "description": "The finished flow state. Available once the job waits for its result to be collected; collecting it lets the node finish the job. The node labels the body as text, but it is JSON.",
281
+ "parameters": [
282
+ {
283
+ "name": "job",
284
+ "in": "path",
285
+ "required": true,
286
+ "description": "The job's address.",
287
+ "schema": {
288
+ "$ref": "#/components/schemas/Address"
289
+ }
290
+ }
291
+ ],
292
+ "responses": {
293
+ "200": {
294
+ "description": "The flow state.",
295
+ "content": {
296
+ "application/json": {
297
+ "schema": {
298
+ "$ref": "#/components/schemas/FlowState"
299
+ }
300
+ }
301
+ }
302
+ },
303
+ "400": {
304
+ "description": "No running job with that address, or the id is not part of it.",
305
+ "content": {
306
+ "application/json": {
307
+ "schema": {
308
+ "$ref": "#/components/schemas/NodeError"
309
+ }
310
+ }
311
+ }
312
+ },
313
+ "401": {
314
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
315
+ "content": {
316
+ "application/json": {
317
+ "schema": {
318
+ "$ref": "#/components/schemas/NodeError"
319
+ }
320
+ }
321
+ }
322
+ }
323
+ },
324
+ "security": [
325
+ {
326
+ "NosanaAuthorization": []
327
+ }
328
+ ]
329
+ }
330
+ },
331
+ "/job/{job}/ops": {
332
+ "get": {
333
+ "operationId": "getJobOperations",
334
+ "tags": [
335
+ "Job"
336
+ ],
337
+ "summary": "Operation statuses",
338
+ "description": "Status of every operation of the job.",
339
+ "parameters": [
340
+ {
341
+ "name": "job",
342
+ "in": "path",
343
+ "required": true,
344
+ "description": "The job's address.",
345
+ "schema": {
346
+ "$ref": "#/components/schemas/Address"
347
+ }
348
+ }
349
+ ],
350
+ "responses": {
351
+ "200": {
352
+ "description": "Statuses by operation id.",
353
+ "content": {
354
+ "application/json": {
355
+ "schema": {
356
+ "$ref": "#/components/schemas/OperationStatuses"
357
+ }
358
+ }
359
+ }
360
+ },
361
+ "400": {
362
+ "description": "No running job with that address, or the id is not part of it.",
363
+ "content": {
364
+ "application/json": {
365
+ "schema": {
366
+ "$ref": "#/components/schemas/NodeError"
367
+ }
368
+ }
369
+ }
370
+ },
371
+ "401": {
372
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
373
+ "content": {
374
+ "application/json": {
375
+ "schema": {
376
+ "$ref": "#/components/schemas/NodeError"
377
+ }
378
+ }
379
+ }
380
+ }
381
+ },
382
+ "security": [
383
+ {
384
+ "NosanaAuthorization": []
385
+ }
386
+ ]
387
+ }
388
+ },
389
+ "/job/{job}/ops/{op}": {
390
+ "get": {
391
+ "operationId": "getJobOperation",
392
+ "tags": [
393
+ "Job"
394
+ ],
395
+ "summary": "Operation status",
396
+ "description": "Status of one operation.",
397
+ "parameters": [
398
+ {
399
+ "name": "job",
400
+ "in": "path",
401
+ "required": true,
402
+ "description": "The job's address.",
403
+ "schema": {
404
+ "$ref": "#/components/schemas/Address"
405
+ }
406
+ },
407
+ {
408
+ "name": "op",
409
+ "in": "path",
410
+ "required": true,
411
+ "description": "An operation id within the job definition.",
412
+ "schema": {
413
+ "type": "string"
414
+ }
415
+ }
416
+ ],
417
+ "responses": {
418
+ "200": {
419
+ "description": "The operation's status.",
420
+ "content": {
421
+ "application/json": {
422
+ "schema": {
423
+ "type": "string"
424
+ }
425
+ }
426
+ }
427
+ },
428
+ "400": {
429
+ "description": "No running job with that address, or the id is not part of it.",
430
+ "content": {
431
+ "application/json": {
432
+ "schema": {
433
+ "$ref": "#/components/schemas/NodeError"
434
+ }
435
+ }
436
+ }
437
+ },
438
+ "401": {
439
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
440
+ "content": {
441
+ "application/json": {
442
+ "schema": {
443
+ "$ref": "#/components/schemas/NodeError"
444
+ }
445
+ }
446
+ }
447
+ },
448
+ "404": {
449
+ "description": "No running job with that address, or the id is not part of it.",
450
+ "content": {
451
+ "application/json": {
452
+ "schema": {
453
+ "$ref": "#/components/schemas/NodeError"
454
+ }
455
+ }
456
+ }
457
+ }
458
+ },
459
+ "security": [
460
+ {
461
+ "NosanaAuthorization": []
462
+ }
463
+ ]
464
+ }
465
+ },
466
+ "/job/{job}/group/current": {
467
+ "get": {
468
+ "operationId": "getJobCurrentGroup",
469
+ "tags": [
470
+ "Job"
471
+ ],
472
+ "summary": "Current group statuses",
473
+ "description": "Status of the operations in the group currently running.",
474
+ "parameters": [
475
+ {
476
+ "name": "job",
477
+ "in": "path",
478
+ "required": true,
479
+ "description": "The job's address.",
480
+ "schema": {
481
+ "$ref": "#/components/schemas/Address"
482
+ }
483
+ }
484
+ ],
485
+ "responses": {
486
+ "200": {
487
+ "description": "Statuses by operation id.",
488
+ "content": {
489
+ "application/json": {
490
+ "schema": {
491
+ "$ref": "#/components/schemas/OperationStatuses"
492
+ }
493
+ }
494
+ }
495
+ },
496
+ "400": {
497
+ "description": "No running job with that address, or the id is not part of it.",
498
+ "content": {
499
+ "application/json": {
500
+ "schema": {
501
+ "$ref": "#/components/schemas/NodeError"
502
+ }
503
+ }
504
+ }
505
+ },
506
+ "401": {
507
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
508
+ "content": {
509
+ "application/json": {
510
+ "schema": {
511
+ "$ref": "#/components/schemas/NodeError"
512
+ }
513
+ }
514
+ }
515
+ }
516
+ },
517
+ "security": [
518
+ {
519
+ "NosanaAuthorization": []
520
+ }
521
+ ]
522
+ }
523
+ },
524
+ "/job/{job}/group/{group}": {
525
+ "get": {
526
+ "operationId": "getJobGroup",
527
+ "tags": [
528
+ "Job"
529
+ ],
530
+ "summary": "Group statuses",
531
+ "description": "Status of the operations in one group.",
532
+ "parameters": [
533
+ {
534
+ "name": "job",
535
+ "in": "path",
536
+ "required": true,
537
+ "description": "The job's address.",
538
+ "schema": {
539
+ "$ref": "#/components/schemas/Address"
540
+ }
541
+ },
542
+ {
543
+ "name": "group",
544
+ "in": "path",
545
+ "required": true,
546
+ "description": "A group of the job definition.",
547
+ "schema": {
548
+ "type": "string"
549
+ }
550
+ }
551
+ ],
552
+ "responses": {
553
+ "200": {
554
+ "description": "Statuses by operation id.",
555
+ "content": {
556
+ "application/json": {
557
+ "schema": {
558
+ "$ref": "#/components/schemas/OperationStatuses"
559
+ }
560
+ }
561
+ }
562
+ },
563
+ "400": {
564
+ "description": "No running job with that address, or the id is not part of it.",
565
+ "content": {
566
+ "application/json": {
567
+ "schema": {
568
+ "$ref": "#/components/schemas/NodeError"
569
+ }
570
+ }
571
+ }
572
+ },
573
+ "401": {
574
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
575
+ "content": {
576
+ "application/json": {
577
+ "schema": {
578
+ "$ref": "#/components/schemas/NodeError"
579
+ }
580
+ }
581
+ }
582
+ },
583
+ "404": {
584
+ "description": "No running job with that address, or the id is not part of it.",
585
+ "content": {
586
+ "application/json": {
587
+ "schema": {
588
+ "$ref": "#/components/schemas/NodeError"
589
+ }
590
+ }
591
+ }
592
+ }
593
+ },
594
+ "security": [
595
+ {
596
+ "NosanaAuthorization": []
597
+ }
598
+ ]
599
+ }
600
+ },
601
+ "/job/{job}/group/{group}/restart": {
602
+ "post": {
603
+ "operationId": "restartJobGroup",
604
+ "tags": [
605
+ "Job"
606
+ ],
607
+ "summary": "Restart a group",
608
+ "description": "Restarts every operation of the group.",
609
+ "parameters": [
610
+ {
611
+ "name": "job",
612
+ "in": "path",
613
+ "required": true,
614
+ "description": "The job's address.",
615
+ "schema": {
616
+ "$ref": "#/components/schemas/Address"
617
+ }
618
+ },
619
+ {
620
+ "name": "group",
621
+ "in": "path",
622
+ "required": true,
623
+ "description": "A group of the job definition.",
624
+ "schema": {
625
+ "type": "string"
626
+ }
627
+ }
628
+ ],
629
+ "responses": {
630
+ "202": {
631
+ "description": "Restart initiated.",
632
+ "content": {
633
+ "application/json": {
634
+ "schema": {
635
+ "$ref": "#/components/schemas/ActionResult"
636
+ }
637
+ }
638
+ }
639
+ },
640
+ "400": {
641
+ "description": "No running job with that address, or the id is not part of it.",
642
+ "content": {
643
+ "application/json": {
644
+ "schema": {
645
+ "$ref": "#/components/schemas/NodeError"
646
+ }
647
+ }
648
+ }
649
+ },
650
+ "401": {
651
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
652
+ "content": {
653
+ "application/json": {
654
+ "schema": {
655
+ "$ref": "#/components/schemas/NodeError"
656
+ }
657
+ }
658
+ }
659
+ },
660
+ "500": {
661
+ "description": "The restart failed.",
662
+ "content": {
663
+ "application/json": {
664
+ "schema": {
665
+ "$ref": "#/components/schemas/NodeError"
666
+ }
667
+ }
668
+ }
669
+ }
670
+ },
671
+ "security": [
672
+ {
673
+ "NosanaAuthorization": []
674
+ }
675
+ ]
676
+ }
677
+ },
678
+ "/job/{job}/group/{group}/stop": {
679
+ "post": {
680
+ "operationId": "stopJobGroup",
681
+ "tags": [
682
+ "Job"
683
+ ],
684
+ "summary": "Stop a group",
685
+ "description": "Stops every operation of the group.",
686
+ "parameters": [
687
+ {
688
+ "name": "job",
689
+ "in": "path",
690
+ "required": true,
691
+ "description": "The job's address.",
692
+ "schema": {
693
+ "$ref": "#/components/schemas/Address"
694
+ }
695
+ },
696
+ {
697
+ "name": "group",
698
+ "in": "path",
699
+ "required": true,
700
+ "description": "A group of the job definition.",
701
+ "schema": {
702
+ "type": "string"
703
+ }
704
+ }
705
+ ],
706
+ "responses": {
707
+ "200": {
708
+ "description": "Stopped.",
709
+ "content": {
710
+ "application/json": {
711
+ "schema": {
712
+ "$ref": "#/components/schemas/ActionResult"
713
+ }
714
+ }
715
+ }
716
+ },
717
+ "400": {
718
+ "description": "No running job with that address, or the id is not part of it.",
719
+ "content": {
720
+ "application/json": {
721
+ "schema": {
722
+ "$ref": "#/components/schemas/NodeError"
723
+ }
724
+ }
725
+ }
726
+ },
727
+ "401": {
728
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
729
+ "content": {
730
+ "application/json": {
731
+ "schema": {
732
+ "$ref": "#/components/schemas/NodeError"
733
+ }
734
+ }
735
+ }
736
+ },
737
+ "500": {
738
+ "description": "The stop failed.",
739
+ "content": {
740
+ "application/json": {
741
+ "schema": {
742
+ "$ref": "#/components/schemas/NodeError"
743
+ }
744
+ }
745
+ }
746
+ }
747
+ },
748
+ "security": [
749
+ {
750
+ "NosanaAuthorization": []
751
+ }
752
+ ]
753
+ }
754
+ },
755
+ "/job/{job}/group/{group}/operation/{op}/restart": {
756
+ "post": {
757
+ "operationId": "restartJobOperation",
758
+ "tags": [
759
+ "Job"
760
+ ],
761
+ "summary": "Restart an operation",
762
+ "description": "Restarts one operation of a group.",
763
+ "parameters": [
764
+ {
765
+ "name": "job",
766
+ "in": "path",
767
+ "required": true,
768
+ "description": "The job's address.",
769
+ "schema": {
770
+ "$ref": "#/components/schemas/Address"
771
+ }
772
+ },
773
+ {
774
+ "name": "group",
775
+ "in": "path",
776
+ "required": true,
777
+ "description": "A group of the job definition.",
778
+ "schema": {
779
+ "type": "string"
780
+ }
781
+ },
782
+ {
783
+ "name": "op",
784
+ "in": "path",
785
+ "required": true,
786
+ "description": "An operation id within the job definition.",
787
+ "schema": {
788
+ "type": "string"
789
+ }
790
+ }
791
+ ],
792
+ "responses": {
793
+ "202": {
794
+ "description": "Restart initiated.",
795
+ "content": {
796
+ "application/json": {
797
+ "schema": {
798
+ "$ref": "#/components/schemas/ActionResult"
799
+ }
800
+ }
801
+ }
802
+ },
803
+ "400": {
804
+ "description": "No running job with that address, or the id is not part of it.",
805
+ "content": {
806
+ "application/json": {
807
+ "schema": {
808
+ "$ref": "#/components/schemas/NodeError"
809
+ }
810
+ }
811
+ }
812
+ },
813
+ "401": {
814
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
815
+ "content": {
816
+ "application/json": {
817
+ "schema": {
818
+ "$ref": "#/components/schemas/NodeError"
819
+ }
820
+ }
821
+ }
822
+ },
823
+ "500": {
824
+ "description": "The restart failed.",
825
+ "content": {
826
+ "application/json": {
827
+ "schema": {
828
+ "$ref": "#/components/schemas/NodeError"
829
+ }
830
+ }
831
+ }
832
+ }
833
+ },
834
+ "security": [
835
+ {
836
+ "NosanaAuthorization": []
837
+ }
838
+ ]
839
+ }
840
+ },
841
+ "/job/{job}/group/{group}/operation/{op}/stop": {
842
+ "post": {
843
+ "operationId": "stopJobOperation",
844
+ "tags": [
845
+ "Job"
846
+ ],
847
+ "summary": "Stop an operation",
848
+ "description": "Stops one operation of a group.",
849
+ "parameters": [
850
+ {
851
+ "name": "job",
852
+ "in": "path",
853
+ "required": true,
854
+ "description": "The job's address.",
855
+ "schema": {
856
+ "$ref": "#/components/schemas/Address"
857
+ }
858
+ },
859
+ {
860
+ "name": "group",
861
+ "in": "path",
862
+ "required": true,
863
+ "description": "A group of the job definition.",
864
+ "schema": {
865
+ "type": "string"
866
+ }
867
+ },
868
+ {
869
+ "name": "op",
870
+ "in": "path",
871
+ "required": true,
872
+ "description": "An operation id within the job definition.",
873
+ "schema": {
874
+ "type": "string"
875
+ }
876
+ }
877
+ ],
878
+ "responses": {
879
+ "200": {
880
+ "description": "Stopped.",
881
+ "content": {
882
+ "application/json": {
883
+ "schema": {
884
+ "$ref": "#/components/schemas/ActionResult"
885
+ }
886
+ }
887
+ }
888
+ },
889
+ "400": {
890
+ "description": "No running job with that address, or the id is not part of it.",
891
+ "content": {
892
+ "application/json": {
893
+ "schema": {
894
+ "$ref": "#/components/schemas/NodeError"
895
+ }
896
+ }
897
+ }
898
+ },
899
+ "401": {
900
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
901
+ "content": {
902
+ "application/json": {
903
+ "schema": {
904
+ "$ref": "#/components/schemas/NodeError"
905
+ }
906
+ }
907
+ }
908
+ },
909
+ "500": {
910
+ "description": "The stop failed.",
911
+ "content": {
912
+ "application/json": {
913
+ "schema": {
914
+ "$ref": "#/components/schemas/NodeError"
915
+ }
916
+ }
917
+ }
918
+ }
919
+ },
920
+ "security": [
921
+ {
922
+ "NosanaAuthorization": []
923
+ }
924
+ ]
925
+ }
926
+ },
927
+ "/job/{job}/endpoints": {
928
+ "get": {
929
+ "operationId": "getJobEndpoints",
930
+ "tags": [
931
+ "Job"
932
+ ],
933
+ "summary": "Exposed endpoints",
934
+ "description": "The URLs the job exposes and whether they answer yet.",
935
+ "parameters": [
936
+ {
937
+ "name": "job",
938
+ "in": "path",
939
+ "required": true,
940
+ "description": "The job's address.",
941
+ "schema": {
942
+ "$ref": "#/components/schemas/Address"
943
+ }
944
+ }
945
+ ],
946
+ "responses": {
947
+ "200": {
948
+ "description": "Endpoints and their status.",
949
+ "content": {
950
+ "application/json": {
951
+ "schema": {
952
+ "$ref": "#/components/schemas/JobEndpoints"
953
+ }
954
+ }
955
+ }
956
+ },
957
+ "400": {
958
+ "description": "The job exposes no endpoint.",
959
+ "content": {
960
+ "application/json": {
961
+ "schema": {
962
+ "$ref": "#/components/schemas/NodeError"
963
+ }
964
+ }
965
+ }
966
+ },
967
+ "401": {
968
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
969
+ "content": {
970
+ "application/json": {
971
+ "schema": {
972
+ "$ref": "#/components/schemas/NodeError"
973
+ }
974
+ }
975
+ }
976
+ },
977
+ "500": {
978
+ "description": "The node could not read the endpoints.",
979
+ "content": {
980
+ "application/json": {
981
+ "schema": {
982
+ "$ref": "#/components/schemas/NodeError"
983
+ }
984
+ }
985
+ }
986
+ }
987
+ },
988
+ "security": [
989
+ {
990
+ "NosanaAuthorization": []
991
+ }
992
+ ]
993
+ }
994
+ },
995
+ "/job/{job}/stats": {
996
+ "get": {
997
+ "operationId": "getJobStats",
998
+ "tags": [
999
+ "Job"
1000
+ ],
1001
+ "summary": "Resource stats",
1002
+ "description": "CPU, memory, disk and network samples per operation within a time window.",
1003
+ "parameters": [
1004
+ {
1005
+ "name": "job",
1006
+ "in": "path",
1007
+ "required": true,
1008
+ "description": "The job's address.",
1009
+ "schema": {
1010
+ "$ref": "#/components/schemas/Address"
1011
+ }
1012
+ },
1013
+ {
1014
+ "name": "interval",
1015
+ "in": "query",
1016
+ "description": "Seconds between samples.",
1017
+ "schema": {
1018
+ "type": "integer",
1019
+ "minimum": 1
1020
+ }
1021
+ },
1022
+ {
1023
+ "name": "start",
1024
+ "in": "query",
1025
+ "description": "Start of the window, Unix milliseconds.",
1026
+ "schema": {
1027
+ "type": "integer"
1028
+ }
1029
+ },
1030
+ {
1031
+ "name": "end",
1032
+ "in": "query",
1033
+ "description": "End of the window, Unix milliseconds.",
1034
+ "schema": {
1035
+ "type": "integer"
1036
+ }
1037
+ }
1038
+ ],
1039
+ "responses": {
1040
+ "200": {
1041
+ "description": "Samples, oldest first.",
1042
+ "content": {
1043
+ "application/json": {
1044
+ "schema": {
1045
+ "type": "array",
1046
+ "items": {
1047
+ "$ref": "#/components/schemas/TaskStat"
1048
+ }
1049
+ }
1050
+ }
1051
+ }
1052
+ },
1053
+ "400": {
1054
+ "description": "`start` and `end` must be timestamps in milliseconds.",
1055
+ "content": {
1056
+ "application/json": {
1057
+ "schema": {
1058
+ "$ref": "#/components/schemas/NodeError"
1059
+ }
1060
+ }
1061
+ }
1062
+ },
1063
+ "401": {
1064
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
1065
+ "content": {
1066
+ "application/json": {
1067
+ "schema": {
1068
+ "$ref": "#/components/schemas/NodeError"
1069
+ }
1070
+ }
1071
+ }
1072
+ },
1073
+ "404": {
1074
+ "description": "No running job with that address, or the id is not part of it.",
1075
+ "content": {
1076
+ "application/json": {
1077
+ "schema": {
1078
+ "$ref": "#/components/schemas/NodeError"
1079
+ }
1080
+ }
1081
+ }
1082
+ }
1083
+ },
1084
+ "security": [
1085
+ {
1086
+ "NosanaAuthorization": []
1087
+ }
1088
+ ]
1089
+ }
1090
+ },
1091
+ "/job/{job}/stats/stream": {
1092
+ "get": {
1093
+ "operationId": "streamJobStats",
1094
+ "tags": [
1095
+ "Job"
1096
+ ],
1097
+ "summary": "Stream resource stats",
1098
+ "description": "Server-sent events: the latest sample per operation, every `interval` seconds, until the job ends.",
1099
+ "parameters": [
1100
+ {
1101
+ "name": "job",
1102
+ "in": "path",
1103
+ "required": true,
1104
+ "description": "The job's address.",
1105
+ "schema": {
1106
+ "$ref": "#/components/schemas/Address"
1107
+ }
1108
+ },
1109
+ {
1110
+ "name": "interval",
1111
+ "in": "query",
1112
+ "description": "Seconds between samples.",
1113
+ "schema": {
1114
+ "type": "integer",
1115
+ "minimum": 1
1116
+ }
1117
+ }
1118
+ ],
1119
+ "responses": {
1120
+ "200": {
1121
+ "description": "A stream of `TaskStat[]` frames as unnamed `message` events.",
1122
+ "content": {
1123
+ "text/event-stream": {
1124
+ "schema": {
1125
+ "type": "string"
1126
+ }
1127
+ }
1128
+ }
1129
+ },
1130
+ "401": {
1131
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
1132
+ "content": {
1133
+ "application/json": {
1134
+ "schema": {
1135
+ "$ref": "#/components/schemas/NodeError"
1136
+ }
1137
+ }
1138
+ }
1139
+ },
1140
+ "404": {
1141
+ "description": "No running job with that address, or the id is not part of it.",
1142
+ "content": {
1143
+ "application/json": {
1144
+ "schema": {
1145
+ "$ref": "#/components/schemas/NodeError"
1146
+ }
1147
+ }
1148
+ }
1149
+ }
1150
+ },
1151
+ "security": [
1152
+ {
1153
+ "NosanaAuthorization": []
1154
+ }
1155
+ ]
1156
+ }
1157
+ },
1158
+ "/job/{job}/stop": {
1159
+ "post": {
1160
+ "operationId": "stopJob",
1161
+ "tags": [
1162
+ "Job"
1163
+ ],
1164
+ "summary": "Stop the job",
1165
+ "description": "Asks the node to stop the job.",
1166
+ "parameters": [
1167
+ {
1168
+ "name": "job",
1169
+ "in": "path",
1170
+ "required": true,
1171
+ "description": "The job's address.",
1172
+ "schema": {
1173
+ "$ref": "#/components/schemas/Address"
1174
+ }
1175
+ }
1176
+ ],
1177
+ "responses": {
1178
+ "200": {
1179
+ "description": "Stopped.",
1180
+ "content": {
1181
+ "text/plain": {
1182
+ "schema": {
1183
+ "type": "string"
1184
+ }
1185
+ }
1186
+ }
1187
+ },
1188
+ "400": {
1189
+ "description": "No running job with that address, or the id is not part of it.",
1190
+ "content": {
1191
+ "application/json": {
1192
+ "schema": {
1193
+ "$ref": "#/components/schemas/NodeError"
1194
+ }
1195
+ }
1196
+ }
1197
+ },
1198
+ "401": {
1199
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
1200
+ "content": {
1201
+ "application/json": {
1202
+ "schema": {
1203
+ "$ref": "#/components/schemas/NodeError"
1204
+ }
1205
+ }
1206
+ }
1207
+ },
1208
+ "500": {
1209
+ "description": "The stop failed.",
1210
+ "content": {
1211
+ "application/json": {
1212
+ "schema": {
1213
+ "$ref": "#/components/schemas/NodeError"
1214
+ }
1215
+ }
1216
+ }
1217
+ }
1218
+ },
1219
+ "security": [
1220
+ {
1221
+ "NosanaAuthorization": []
1222
+ }
1223
+ ]
1224
+ }
1225
+ },
1226
+ "/job/{job}/ssh/authorize": {
1227
+ "post": {
1228
+ "operationId": "authorizeJobSshKey",
1229
+ "tags": [
1230
+ "SSH"
1231
+ ],
1232
+ "summary": "Grant an SSH key",
1233
+ "description": "Lets the key reach the job, permanently or until `expiresAt`. The signature must be the job owner's.",
1234
+ "parameters": [
1235
+ {
1236
+ "name": "job",
1237
+ "in": "path",
1238
+ "required": true,
1239
+ "description": "The job's address.",
1240
+ "schema": {
1241
+ "$ref": "#/components/schemas/Address"
1242
+ }
1243
+ }
1244
+ ],
1245
+ "requestBody": {
1246
+ "required": true,
1247
+ "content": {
1248
+ "application/json": {
1249
+ "schema": {
1250
+ "$ref": "#/components/schemas/SshAuthorizationRequest"
1251
+ }
1252
+ }
1253
+ }
1254
+ },
1255
+ "responses": {
1256
+ "201": {
1257
+ "description": "The key is authorized.",
1258
+ "content": {
1259
+ "application/json": {
1260
+ "schema": {
1261
+ "$ref": "#/components/schemas/SshAuthorization"
1262
+ }
1263
+ }
1264
+ }
1265
+ },
1266
+ "400": {
1267
+ "description": "The key is not a valid OpenSSH public key, or the expiry is invalid.",
1268
+ "content": {
1269
+ "application/json": {
1270
+ "schema": {
1271
+ "$ref": "#/components/schemas/NodeError"
1272
+ }
1273
+ }
1274
+ }
1275
+ },
1276
+ "401": {
1277
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
1278
+ "content": {
1279
+ "application/json": {
1280
+ "schema": {
1281
+ "$ref": "#/components/schemas/NodeError"
1282
+ }
1283
+ }
1284
+ }
1285
+ },
1286
+ "404": {
1287
+ "description": "No active job with SSH access.",
1288
+ "content": {
1289
+ "application/json": {
1290
+ "schema": {
1291
+ "$ref": "#/components/schemas/NodeError"
1292
+ }
1293
+ }
1294
+ }
1295
+ },
1296
+ "502": {
1297
+ "description": "The node could not apply the change.",
1298
+ "content": {
1299
+ "application/json": {
1300
+ "schema": {
1301
+ "$ref": "#/components/schemas/NodeError"
1302
+ }
1303
+ }
1304
+ }
1305
+ },
1306
+ "503": {
1307
+ "description": "The SSH provider is not available.",
1308
+ "content": {
1309
+ "application/json": {
1310
+ "schema": {
1311
+ "$ref": "#/components/schemas/NodeError"
1312
+ }
1313
+ }
1314
+ }
1315
+ }
1316
+ },
1317
+ "security": [
1318
+ {
1319
+ "NosanaAuthorization": []
1320
+ }
1321
+ ]
1322
+ }
1323
+ },
1324
+ "/job/{job}/ssh/keys": {
1325
+ "get": {
1326
+ "operationId": "getJobSshKeys",
1327
+ "tags": [
1328
+ "SSH"
1329
+ ],
1330
+ "summary": "List SSH keys",
1331
+ "description": "The keys currently allowed to reach the job, with their expiry when temporary.",
1332
+ "parameters": [
1333
+ {
1334
+ "name": "job",
1335
+ "in": "path",
1336
+ "required": true,
1337
+ "description": "The job's address.",
1338
+ "schema": {
1339
+ "$ref": "#/components/schemas/Address"
1340
+ }
1341
+ }
1342
+ ],
1343
+ "responses": {
1344
+ "200": {
1345
+ "description": "The job's keys.",
1346
+ "content": {
1347
+ "application/json": {
1348
+ "schema": {
1349
+ "$ref": "#/components/schemas/SshKeys"
1350
+ }
1351
+ }
1352
+ }
1353
+ },
1354
+ "401": {
1355
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
1356
+ "content": {
1357
+ "application/json": {
1358
+ "schema": {
1359
+ "$ref": "#/components/schemas/NodeError"
1360
+ }
1361
+ }
1362
+ }
1363
+ },
1364
+ "404": {
1365
+ "description": "No active job with SSH access.",
1366
+ "content": {
1367
+ "application/json": {
1368
+ "schema": {
1369
+ "$ref": "#/components/schemas/NodeError"
1370
+ }
1371
+ }
1372
+ }
1373
+ },
1374
+ "502": {
1375
+ "description": "The node could not read its keys.",
1376
+ "content": {
1377
+ "application/json": {
1378
+ "schema": {
1379
+ "$ref": "#/components/schemas/NodeError"
1380
+ }
1381
+ }
1382
+ }
1383
+ }
1384
+ },
1385
+ "security": [
1386
+ {
1387
+ "NosanaAuthorization": []
1388
+ }
1389
+ ]
1390
+ },
1391
+ "delete": {
1392
+ "operationId": "revokeJobSshKey",
1393
+ "tags": [
1394
+ "SSH"
1395
+ ],
1396
+ "summary": "Revoke an SSH key",
1397
+ "description": "Removes the key whatever its expiry. Open sessions stay until they disconnect.",
1398
+ "parameters": [
1399
+ {
1400
+ "name": "job",
1401
+ "in": "path",
1402
+ "required": true,
1403
+ "description": "The job's address.",
1404
+ "schema": {
1405
+ "$ref": "#/components/schemas/Address"
1406
+ }
1407
+ }
1408
+ ],
1409
+ "requestBody": {
1410
+ "required": true,
1411
+ "content": {
1412
+ "application/json": {
1413
+ "schema": {
1414
+ "$ref": "#/components/schemas/SshRevocationRequest"
1415
+ }
1416
+ }
1417
+ }
1418
+ },
1419
+ "responses": {
1420
+ "200": {
1421
+ "description": "The key is revoked.",
1422
+ "content": {
1423
+ "application/json": {
1424
+ "schema": {
1425
+ "$ref": "#/components/schemas/SshRevocation"
1426
+ }
1427
+ }
1428
+ }
1429
+ },
1430
+ "400": {
1431
+ "description": "The key is not a valid OpenSSH public key.",
1432
+ "content": {
1433
+ "application/json": {
1434
+ "schema": {
1435
+ "$ref": "#/components/schemas/NodeError"
1436
+ }
1437
+ }
1438
+ }
1439
+ },
1440
+ "401": {
1441
+ "description": "The authorization header is missing, invalid, or not signed by the job's owner.",
1442
+ "content": {
1443
+ "application/json": {
1444
+ "schema": {
1445
+ "$ref": "#/components/schemas/NodeError"
1446
+ }
1447
+ }
1448
+ }
1449
+ },
1450
+ "404": {
1451
+ "description": "No active job with SSH access.",
1452
+ "content": {
1453
+ "application/json": {
1454
+ "schema": {
1455
+ "$ref": "#/components/schemas/NodeError"
1456
+ }
1457
+ }
1458
+ }
1459
+ },
1460
+ "502": {
1461
+ "description": "The node could not apply the change.",
1462
+ "content": {
1463
+ "application/json": {
1464
+ "schema": {
1465
+ "$ref": "#/components/schemas/NodeError"
1466
+ }
1467
+ }
1468
+ }
1469
+ }
1470
+ },
1471
+ "security": [
1472
+ {
1473
+ "NosanaAuthorization": []
1474
+ }
1475
+ ]
1476
+ }
1477
+ }
1478
+ },
1479
+ "components": {
1480
+ "schemas": {
1481
+ "JobDefinition": {
1482
+ "type": "object",
1483
+ "properties": {
1484
+ "version": {
1485
+ "const": "0.1"
1486
+ },
1487
+ "type": {
1488
+ "const": "container"
1489
+ },
1490
+ "logistics": {
1491
+ "$ref": "#/components/schemas/Logistics"
1492
+ },
1493
+ "deployment_id": {
1494
+ "type": "string"
1495
+ },
1496
+ "ssh": {
1497
+ "$ref": "#/components/schemas/SSH"
1498
+ },
1499
+ "meta": {
1500
+ "$ref": "#/components/schemas/Meta"
1501
+ },
1502
+ "global": {
1503
+ "$ref": "#/components/schemas/Global"
1504
+ },
1505
+ "ops": {
1506
+ "$ref": "#/components/schemas/Ops"
1507
+ }
1508
+ },
1509
+ "required": [
1510
+ "version",
1511
+ "type",
1512
+ "ops"
1513
+ ]
1514
+ },
1515
+ "Logistics": {
1516
+ "type": "object",
1517
+ "properties": {
1518
+ "send": {
1519
+ "$ref": "#/components/schemas/Logistic"
1520
+ },
1521
+ "receive": {
1522
+ "$ref": "#/components/schemas/Logistic"
1523
+ }
1524
+ },
1525
+ "required": [
1526
+ "send",
1527
+ "receive"
1528
+ ]
1529
+ },
1530
+ "Logistic": {
1531
+ "type": "object",
1532
+ "properties": {
1533
+ "type": {
1534
+ "$ref": "#/components/schemas/LogisticType"
1535
+ },
1536
+ "args": {
1537
+ "$ref": "#/components/schemas/Partial__type"
1538
+ }
1539
+ },
1540
+ "required": [
1541
+ "type",
1542
+ "args"
1543
+ ]
1544
+ },
1545
+ "LogisticType": {
1546
+ "oneOf": [
1547
+ {
1548
+ "const": "api"
1549
+ },
1550
+ {
1551
+ "const": "api-listen"
1552
+ }
1553
+ ],
1554
+ "description": "api - we receive and send via an endpoint endpoint\napi-listen - we create an endpoint to listen for incoming requests"
1555
+ },
1556
+ "Partial__type": {
1557
+ "type": "object",
1558
+ "properties": {
1559
+ "endpoint": {
1560
+ "type": "string"
1561
+ }
1562
+ },
1563
+ "required": [],
1564
+ "description": "Make all properties in T optional"
1565
+ },
1566
+ "SSH": {
1567
+ "type": "object",
1568
+ "properties": {
1569
+ "public_keys": {
1570
+ "type": "array",
1571
+ "items": {
1572
+ "type": "string"
1573
+ }
1574
+ }
1575
+ },
1576
+ "required": []
1577
+ },
1578
+ "Meta": {
1579
+ "type": "object",
1580
+ "properties": {
1581
+ "trigger": {
1582
+ "type": "string"
1583
+ },
1584
+ "system_resources": {
1585
+ "$ref": "#/components/schemas/Recordstringstringnumber"
1586
+ }
1587
+ },
1588
+ "required": [],
1589
+ "additionalProperties": {
1590
+ "oneOf": [
1591
+ {
1592
+ "type": "string"
1593
+ },
1594
+ {
1595
+ "type": "number"
1596
+ },
1597
+ {
1598
+ "type": "array",
1599
+ "items": {
1600
+ "oneOf": [
1601
+ {
1602
+ "type": "string"
1603
+ },
1604
+ {
1605
+ "type": "number"
1606
+ }
1607
+ ]
1608
+ }
1609
+ },
1610
+ {
1611
+ "$ref": "#/components/schemas/Recordstringstringnumber_stringnumber_"
1612
+ }
1613
+ ]
1614
+ }
1615
+ },
1616
+ "Recordstringstringnumber": {
1617
+ "type": "object",
1618
+ "properties": {},
1619
+ "required": [],
1620
+ "description": "Construct a type with a set of properties K of type T",
1621
+ "additionalProperties": {
1622
+ "oneOf": [
1623
+ {
1624
+ "type": "string"
1625
+ },
1626
+ {
1627
+ "type": "number"
1628
+ }
1629
+ ]
1630
+ }
1631
+ },
1632
+ "Recordstringstringnumber_stringnumber_": {
1633
+ "type": "object",
1634
+ "properties": {},
1635
+ "required": [],
1636
+ "description": "Construct a type with a set of properties K of type T",
1637
+ "additionalProperties": {
1638
+ "oneOf": [
1639
+ {
1640
+ "type": "string"
1641
+ },
1642
+ {
1643
+ "type": "number"
1644
+ },
1645
+ {
1646
+ "type": "array",
1647
+ "items": {
1648
+ "oneOf": [
1649
+ {
1650
+ "type": "string"
1651
+ },
1652
+ {
1653
+ "type": "number"
1654
+ }
1655
+ ]
1656
+ }
1657
+ }
1658
+ ]
1659
+ }
1660
+ },
1661
+ "Global": {
1662
+ "type": "object",
1663
+ "properties": {
1664
+ "image": {
1665
+ "type": "string"
1666
+ },
1667
+ "gpu": {
1668
+ "type": "boolean"
1669
+ },
1670
+ "entrypoint": {
1671
+ "oneOf": [
1672
+ {
1673
+ "type": "string"
1674
+ },
1675
+ {
1676
+ "$ref": "#/components/schemas/CMDArray"
1677
+ }
1678
+ ]
1679
+ },
1680
+ "env": {
1681
+ "$ref": "#/components/schemas/Env"
1682
+ },
1683
+ "work_dir": {
1684
+ "type": "string"
1685
+ },
1686
+ "variables": {
1687
+ "$ref": "#/components/schemas/Variables"
1688
+ }
1689
+ },
1690
+ "required": []
1691
+ },
1692
+ "CMDArray": {
1693
+ "type": "array",
1694
+ "items": {
1695
+ "type": "string"
1696
+ }
1697
+ },
1698
+ "Env": {
1699
+ "type": "object",
1700
+ "properties": {},
1701
+ "required": [],
1702
+ "additionalProperties": {
1703
+ "type": "string"
1704
+ }
1705
+ },
1706
+ "Variables": {
1707
+ "type": "object",
1708
+ "properties": {},
1709
+ "required": [],
1710
+ "additionalProperties": {
1711
+ "type": "string"
1712
+ }
1713
+ },
1714
+ "Ops": {
1715
+ "type": "array",
1716
+ "items": {
1717
+ "$ref": "#/components/schemas/OperationkeyofOperationArgsMap"
1718
+ }
1719
+ },
1720
+ "OperationkeyofOperationArgsMap": {
1721
+ "type": "object",
1722
+ "properties": {
1723
+ "type": {
1724
+ "oneOf": [
1725
+ {
1726
+ "const": "container/run"
1727
+ },
1728
+ {
1729
+ "const": "container/create-volume"
1730
+ }
1731
+ ]
1732
+ },
1733
+ "id": {
1734
+ "$ref": "#/components/schemas/OperationId"
1735
+ },
1736
+ "args": {
1737
+ "oneOf": [
1738
+ {
1739
+ "$ref": "#/components/schemas/ContainerRun"
1740
+ },
1741
+ {
1742
+ "$ref": "#/components/schemas/ContainerCreateVolume"
1743
+ }
1744
+ ]
1745
+ },
1746
+ "results": {
1747
+ "$ref": "#/components/schemas/OperationResults"
1748
+ },
1749
+ "execution": {
1750
+ "$ref": "#/components/schemas/Execution"
1751
+ }
1752
+ },
1753
+ "required": [
1754
+ "type",
1755
+ "id",
1756
+ "args"
1757
+ ]
1758
+ },
1759
+ "OperationId": {
1760
+ "type": "string"
1761
+ },
1762
+ "ContainerRun": {
1763
+ "type": "object",
1764
+ "properties": {
1765
+ "image": {
1766
+ "type": "string"
1767
+ },
1768
+ "aliases": {
1769
+ "oneOf": [
1770
+ {
1771
+ "type": "string"
1772
+ },
1773
+ {
1774
+ "$ref": "#/components/schemas/Aliases"
1775
+ }
1776
+ ]
1777
+ },
1778
+ "cmd": {
1779
+ "oneOf": [
1780
+ {
1781
+ "type": "string"
1782
+ },
1783
+ {
1784
+ "$ref": "#/components/schemas/CMDArray"
1785
+ }
1786
+ ]
1787
+ },
1788
+ "volumes": {
1789
+ "type": "array",
1790
+ "items": {
1791
+ "$ref": "#/components/schemas/Volume"
1792
+ }
1793
+ },
1794
+ "expose": {
1795
+ "oneOf": [
1796
+ {
1797
+ "type": "number"
1798
+ },
1799
+ {
1800
+ "$ref": "#/components/schemas/ExposedPort"
1801
+ },
1802
+ {
1803
+ "$ref": "#/components/schemas/LiteralString"
1804
+ },
1805
+ {
1806
+ "type": "object",
1807
+ "properties": {
1808
+ "__spread__": {
1809
+ "$ref": "#/components/schemas/LiteralString"
1810
+ },
1811
+ "chunked": {
1812
+ "type": "boolean"
1813
+ }
1814
+ },
1815
+ "required": [
1816
+ "__spread__"
1817
+ ]
1818
+ },
1819
+ {
1820
+ "$ref": "#/components/schemas/UniqueExposedPorts"
1821
+ }
1822
+ ]
1823
+ },
1824
+ "gpu": {
1825
+ "type": "boolean"
1826
+ },
1827
+ "work_dir": {
1828
+ "type": "string"
1829
+ },
1830
+ "entrypoint": {
1831
+ "oneOf": [
1832
+ {
1833
+ "type": "string"
1834
+ },
1835
+ {
1836
+ "$ref": "#/components/schemas/CMDArray"
1837
+ }
1838
+ ]
1839
+ },
1840
+ "env": {
1841
+ "$ref": "#/components/schemas/Env"
1842
+ },
1843
+ "restart_policy": {
1844
+ "oneOf": [
1845
+ {
1846
+ "const": "no"
1847
+ },
1848
+ {
1849
+ "const": "always"
1850
+ },
1851
+ {
1852
+ "const": "unless-stopped"
1853
+ },
1854
+ {
1855
+ "const": "on-failure"
1856
+ },
1857
+ {
1858
+ "type": "object",
1859
+ "properties": {
1860
+ "policy": {
1861
+ "const": "on-failure"
1862
+ },
1863
+ "restart_tries": {
1864
+ "type": "number"
1865
+ }
1866
+ },
1867
+ "required": [
1868
+ "policy"
1869
+ ]
1870
+ }
1871
+ ]
1872
+ },
1873
+ "private": {
1874
+ "type": "boolean"
1875
+ },
1876
+ "resources": {
1877
+ "$ref": "#/components/schemas/Resources"
1878
+ },
1879
+ "authentication": {
1880
+ "$ref": "#/components/schemas/Authentication"
1881
+ }
1882
+ },
1883
+ "required": [
1884
+ "image"
1885
+ ]
1886
+ },
1887
+ "Aliases": {
1888
+ "type": "array",
1889
+ "items": {
1890
+ "type": "string"
1891
+ }
1892
+ },
1893
+ "Volume": {
1894
+ "type": "object",
1895
+ "properties": {
1896
+ "name": {
1897
+ "type": "string"
1898
+ },
1899
+ "dest": {
1900
+ "type": "string"
1901
+ }
1902
+ },
1903
+ "required": [
1904
+ "name",
1905
+ "dest"
1906
+ ]
1907
+ },
1908
+ "ExposedPort": {
1909
+ "type": "object",
1910
+ "properties": {
1911
+ "port": {
1912
+ "type": "number"
1913
+ },
1914
+ "type": {
1915
+ "oneOf": [
1916
+ {
1917
+ "const": "api"
1918
+ },
1919
+ {
1920
+ "const": "web"
1921
+ },
1922
+ {
1923
+ "const": "webapi"
1924
+ },
1925
+ {
1926
+ "const": "websocket"
1927
+ },
1928
+ {
1929
+ "const": "none"
1930
+ }
1931
+ ]
1932
+ },
1933
+ "health_checks": {
1934
+ "type": "array",
1935
+ "items": {
1936
+ "$ref": "#/components/schemas/HealthCheck"
1937
+ }
1938
+ }
1939
+ },
1940
+ "required": [
1941
+ "port"
1942
+ ]
1943
+ },
1944
+ "HealthCheck": {
1945
+ "oneOf": [
1946
+ {
1947
+ "$ref": "#/components/schemas/HttpHealthCheck"
1948
+ },
1949
+ {
1950
+ "$ref": "#/components/schemas/WebSocketHealthCheck"
1951
+ }
1952
+ ]
1953
+ },
1954
+ "HttpHealthCheck": {
1955
+ "type": "object",
1956
+ "properties": {
1957
+ "type": {
1958
+ "const": "http"
1959
+ },
1960
+ "path": {
1961
+ "type": "string"
1962
+ },
1963
+ "method": {
1964
+ "oneOf": [
1965
+ {
1966
+ "const": "GET"
1967
+ },
1968
+ {
1969
+ "const": "POST"
1970
+ },
1971
+ {
1972
+ "const": "PUT"
1973
+ },
1974
+ {
1975
+ "const": "DELETE"
1976
+ }
1977
+ ]
1978
+ },
1979
+ "expected_status": {
1980
+ "type": "number"
1981
+ },
1982
+ "headers": {
1983
+ "$ref": "#/components/schemas/Recordstringstring"
1984
+ },
1985
+ "body": {},
1986
+ "continuous": {
1987
+ "type": "boolean"
1988
+ }
1989
+ },
1990
+ "required": [
1991
+ "type",
1992
+ "path",
1993
+ "method",
1994
+ "expected_status",
1995
+ "continuous"
1996
+ ]
1997
+ },
1998
+ "Recordstringstring": {
1999
+ "type": "object",
2000
+ "properties": {},
2001
+ "required": [],
2002
+ "description": "Construct a type with a set of properties K of type T",
2003
+ "additionalProperties": {
2004
+ "type": "string"
2005
+ }
2006
+ },
2007
+ "WebSocketHealthCheck": {
2008
+ "type": "object",
2009
+ "properties": {
2010
+ "type": {
2011
+ "const": "websocket"
2012
+ },
2013
+ "expected_response": {
2014
+ "type": "string"
2015
+ },
2016
+ "continuous": {
2017
+ "type": "boolean"
2018
+ }
2019
+ },
2020
+ "required": [
2021
+ "type",
2022
+ "expected_response",
2023
+ "continuous"
2024
+ ]
2025
+ },
2026
+ "LiteralString": {
2027
+ "type": "string"
2028
+ },
2029
+ "UniqueExposedPorts": {
2030
+ "type": "array",
2031
+ "items": {
2032
+ "$ref": "#/components/schemas/ExposeBase"
2033
+ }
2034
+ },
2035
+ "ExposeBase": {
2036
+ "oneOf": [
2037
+ {
2038
+ "type": "number"
2039
+ },
2040
+ {
2041
+ "$ref": "#/components/schemas/ExposedPort"
2042
+ },
2043
+ {
2044
+ "$ref": "#/components/schemas/LiteralString"
2045
+ },
2046
+ {
2047
+ "type": "object",
2048
+ "properties": {
2049
+ "__spread__": {
2050
+ "$ref": "#/components/schemas/LiteralString"
2051
+ },
2052
+ "chunked": {
2053
+ "type": "boolean"
2054
+ }
2055
+ },
2056
+ "required": [
2057
+ "__spread__"
2058
+ ]
2059
+ }
2060
+ ]
2061
+ },
2062
+ "Resources": {
2063
+ "type": "array",
2064
+ "items": {
2065
+ "oneOf": [
2066
+ {
2067
+ "const": "__remove-if-empty__"
2068
+ },
2069
+ {
2070
+ "type": "object",
2071
+ "properties": {
2072
+ "__spread__": {
2073
+ "$ref": "#/components/schemas/LiteralString"
2074
+ },
2075
+ "chunked": {
2076
+ "type": "boolean"
2077
+ }
2078
+ },
2079
+ "required": [
2080
+ "__spread__"
2081
+ ]
2082
+ },
2083
+ {
2084
+ "$ref": "#/components/schemas/S3Base"
2085
+ },
2086
+ {
2087
+ "$ref": "#/components/schemas/S3WithBucket"
2088
+ },
2089
+ {
2090
+ "$ref": "#/components/schemas/S3WithBuckets"
2091
+ },
2092
+ {
2093
+ "$ref": "#/components/schemas/HFResource"
2094
+ },
2095
+ {
2096
+ "$ref": "#/components/schemas/OllamaResource"
2097
+ }
2098
+ ]
2099
+ }
2100
+ },
2101
+ "S3Base": {
2102
+ "type": "object",
2103
+ "properties": {
2104
+ "type": {
2105
+ "const": "S3"
2106
+ },
2107
+ "target": {
2108
+ "type": "string"
2109
+ },
2110
+ "url": {
2111
+ "type": "string"
2112
+ },
2113
+ "allowWrite": {
2114
+ "type": "boolean"
2115
+ },
2116
+ "files": {
2117
+ "type": "array",
2118
+ "items": {
2119
+ "type": "string"
2120
+ }
2121
+ },
2122
+ "IAM": {
2123
+ "$ref": "#/components/schemas/S3Auth"
2124
+ }
2125
+ },
2126
+ "required": [
2127
+ "type",
2128
+ "target"
2129
+ ]
2130
+ },
2131
+ "S3Auth": {
2132
+ "type": "object",
2133
+ "properties": {
2134
+ "REGION": {
2135
+ "type": "string"
2136
+ },
2137
+ "ACCESS_KEY_ID": {
2138
+ "type": "string"
2139
+ },
2140
+ "SECRET_ACCESS_KEY": {
2141
+ "type": "string"
2142
+ }
2143
+ },
2144
+ "required": [
2145
+ "REGION",
2146
+ "ACCESS_KEY_ID",
2147
+ "SECRET_ACCESS_KEY"
2148
+ ]
2149
+ },
2150
+ "S3WithBucket": {
2151
+ "type": "object",
2152
+ "properties": {
2153
+ "type": {
2154
+ "const": "S3"
2155
+ },
2156
+ "target": {
2157
+ "type": "string"
2158
+ },
2159
+ "bucket": {
2160
+ "type": "string"
2161
+ },
2162
+ "url": {
2163
+ "type": "string"
2164
+ },
2165
+ "allowWrite": {
2166
+ "type": "boolean"
2167
+ },
2168
+ "IAM": {
2169
+ "$ref": "#/components/schemas/S3Auth"
2170
+ }
2171
+ },
2172
+ "required": [
2173
+ "type",
2174
+ "target",
2175
+ "bucket"
2176
+ ]
2177
+ },
2178
+ "S3WithBuckets": {
2179
+ "type": "object",
2180
+ "properties": {
2181
+ "type": {
2182
+ "const": "S3"
2183
+ },
2184
+ "target": {
2185
+ "type": "string"
2186
+ },
2187
+ "buckets": {
2188
+ "type": "array",
2189
+ "items": {
2190
+ "type": "object",
2191
+ "properties": {
2192
+ "url": {
2193
+ "type": "string"
2194
+ },
2195
+ "files": {
2196
+ "type": "array",
2197
+ "items": {
2198
+ "type": "string"
2199
+ }
2200
+ }
2201
+ },
2202
+ "required": [
2203
+ "url"
2204
+ ]
2205
+ }
2206
+ },
2207
+ "url": {
2208
+ "type": "string"
2209
+ },
2210
+ "allowWrite": {
2211
+ "type": "boolean"
2212
+ },
2213
+ "IAM": {
2214
+ "$ref": "#/components/schemas/S3Auth"
2215
+ }
2216
+ },
2217
+ "required": [
2218
+ "type",
2219
+ "target",
2220
+ "buckets"
2221
+ ]
2222
+ },
2223
+ "HFResource": {
2224
+ "type": "object",
2225
+ "properties": {
2226
+ "type": {
2227
+ "const": "HF"
2228
+ },
2229
+ "target": {
2230
+ "type": "string"
2231
+ },
2232
+ "repo": {
2233
+ "type": "string"
2234
+ },
2235
+ "revision": {
2236
+ "type": "string"
2237
+ },
2238
+ "files": {
2239
+ "type": "array",
2240
+ "items": {
2241
+ "type": "string"
2242
+ }
2243
+ },
2244
+ "accessToken": {
2245
+ "type": "string"
2246
+ }
2247
+ },
2248
+ "required": [
2249
+ "type",
2250
+ "target",
2251
+ "repo"
2252
+ ]
2253
+ },
2254
+ "OllamaResource": {
2255
+ "type": "object",
2256
+ "properties": {
2257
+ "type": {
2258
+ "const": "Ollama"
2259
+ },
2260
+ "model": {
2261
+ "type": "string"
2262
+ },
2263
+ "target": {
2264
+ "type": "string"
2265
+ }
2266
+ },
2267
+ "required": [
2268
+ "type",
2269
+ "model"
2270
+ ]
2271
+ },
2272
+ "Authentication": {
2273
+ "type": "object",
2274
+ "properties": {
2275
+ "docker": {
2276
+ "$ref": "#/components/schemas/DockerAuth"
2277
+ }
2278
+ },
2279
+ "required": []
2280
+ },
2281
+ "DockerAuth": {
2282
+ "type": "object",
2283
+ "properties": {
2284
+ "username": {
2285
+ "type": "string"
2286
+ },
2287
+ "password": {
2288
+ "type": "string"
2289
+ },
2290
+ "email": {
2291
+ "type": "string"
2292
+ },
2293
+ "server": {
2294
+ "type": "string"
2295
+ }
2296
+ },
2297
+ "required": []
2298
+ },
2299
+ "ContainerCreateVolume": {
2300
+ "type": "object",
2301
+ "properties": {
2302
+ "name": {
2303
+ "type": "string"
2304
+ }
2305
+ },
2306
+ "required": [
2307
+ "name"
2308
+ ]
2309
+ },
2310
+ "OperationResults": {
2311
+ "type": "object",
2312
+ "properties": {},
2313
+ "required": [],
2314
+ "additionalProperties": {
2315
+ "oneOf": [
2316
+ {
2317
+ "type": "string"
2318
+ },
2319
+ {
2320
+ "$ref": "#/components/schemas/OperationResult"
2321
+ }
2322
+ ]
2323
+ }
2324
+ },
2325
+ "OperationResult": {
2326
+ "type": "object",
2327
+ "properties": {
2328
+ "regex": {
2329
+ "type": "string"
2330
+ },
2331
+ "logType": {
2332
+ "$ref": "#/components/schemas/LogTypeTuple"
2333
+ }
2334
+ },
2335
+ "required": [
2336
+ "regex",
2337
+ "logType"
2338
+ ]
2339
+ },
2340
+ "LogTypeTuple": {
2341
+ "type": "array",
2342
+ "items": {
2343
+ "$ref": "#/components/schemas/StdOption"
2344
+ }
2345
+ },
2346
+ "StdOption": {
2347
+ "oneOf": [
2348
+ {
2349
+ "const": "stdin"
2350
+ },
2351
+ {
2352
+ "const": "stdout"
2353
+ },
2354
+ {
2355
+ "const": "stderr"
2356
+ },
2357
+ {
2358
+ "const": "nodeerr"
2359
+ }
2360
+ ]
2361
+ },
2362
+ "Execution": {
2363
+ "type": "object",
2364
+ "properties": {
2365
+ "group": {
2366
+ "type": "string"
2367
+ },
2368
+ "timeout": {
2369
+ "type": "number"
2370
+ },
2371
+ "depends_on": {
2372
+ "type": "array",
2373
+ "items": {
2374
+ "type": "string"
2375
+ }
2376
+ },
2377
+ "stop_if_dependent_stops": {
2378
+ "type": "boolean"
2379
+ }
2380
+ },
2381
+ "required": []
2382
+ },
2383
+ "FlowState": {
2384
+ "type": "object",
2385
+ "properties": {
2386
+ "status": {
2387
+ "type": "string"
2388
+ },
2389
+ "startTime": {
2390
+ "type": "number"
2391
+ },
2392
+ "endTime": {
2393
+ "oneOf": [
2394
+ {
2395
+ "type": "null"
2396
+ },
2397
+ {
2398
+ "type": "number"
2399
+ }
2400
+ ]
2401
+ },
2402
+ "errors": {
2403
+ "type": "array",
2404
+ "items": {}
2405
+ },
2406
+ "opStates": {
2407
+ "type": "array",
2408
+ "items": {
2409
+ "$ref": "#/components/schemas/OpState"
2410
+ }
2411
+ },
2412
+ "secrets": {
2413
+ "$ref": "#/components/schemas/FlowSecrets"
2414
+ }
2415
+ },
2416
+ "required": [
2417
+ "status",
2418
+ "startTime",
2419
+ "endTime",
2420
+ "opStates"
2421
+ ]
2422
+ },
2423
+ "OpState": {
2424
+ "type": "object",
2425
+ "properties": {
2426
+ "providerId": {
2427
+ "oneOf": [
2428
+ {
2429
+ "type": "null"
2430
+ },
2431
+ {
2432
+ "type": "string"
2433
+ }
2434
+ ]
2435
+ },
2436
+ "operationId": {
2437
+ "oneOf": [
2438
+ {
2439
+ "type": "null"
2440
+ },
2441
+ {
2442
+ "type": "string"
2443
+ }
2444
+ ]
2445
+ },
2446
+ "group": {
2447
+ "oneOf": [
2448
+ {
2449
+ "type": "null"
2450
+ },
2451
+ {
2452
+ "type": "string"
2453
+ }
2454
+ ]
2455
+ },
2456
+ "status": {
2457
+ "oneOf": [
2458
+ {
2459
+ "type": "null"
2460
+ },
2461
+ {
2462
+ "type": "string"
2463
+ }
2464
+ ]
2465
+ },
2466
+ "startTime": {
2467
+ "oneOf": [
2468
+ {
2469
+ "type": "null"
2470
+ },
2471
+ {
2472
+ "type": "number"
2473
+ }
2474
+ ]
2475
+ },
2476
+ "endTime": {
2477
+ "oneOf": [
2478
+ {
2479
+ "type": "null"
2480
+ },
2481
+ {
2482
+ "type": "number"
2483
+ }
2484
+ ]
2485
+ },
2486
+ "exitCode": {
2487
+ "oneOf": [
2488
+ {
2489
+ "type": "null"
2490
+ },
2491
+ {
2492
+ "type": "number"
2493
+ }
2494
+ ]
2495
+ },
2496
+ "logs": {
2497
+ "type": "array",
2498
+ "items": {
2499
+ "$ref": "#/components/schemas/Log"
2500
+ }
2501
+ },
2502
+ "results": {
2503
+ "type": "object",
2504
+ "properties": {},
2505
+ "required": [],
2506
+ "additionalProperties": {
2507
+ "oneOf": [
2508
+ {
2509
+ "type": "string"
2510
+ },
2511
+ {
2512
+ "type": "array",
2513
+ "items": {
2514
+ "type": "string"
2515
+ }
2516
+ }
2517
+ ]
2518
+ }
2519
+ },
2520
+ "error": {
2521
+ "type": "object",
2522
+ "properties": {
2523
+ "event": {
2524
+ "type": "string"
2525
+ },
2526
+ "message": {
2527
+ "type": "string"
2528
+ },
2529
+ "code": {
2530
+ "type": "number"
2531
+ }
2532
+ },
2533
+ "required": [
2534
+ "event",
2535
+ "message"
2536
+ ]
2537
+ },
2538
+ "diagnostics": {
2539
+ "type": "object",
2540
+ "properties": {
2541
+ "reason": {
2542
+ "$ref": "#/components/schemas/DiagnosticsReason"
2543
+ },
2544
+ "state": {
2545
+ "$ref": "#/components/schemas/DiagnosticsState"
2546
+ }
2547
+ },
2548
+ "required": [
2549
+ "reason"
2550
+ ]
2551
+ }
2552
+ },
2553
+ "required": [
2554
+ "providerId",
2555
+ "operationId",
2556
+ "group",
2557
+ "status",
2558
+ "startTime",
2559
+ "endTime",
2560
+ "exitCode",
2561
+ "logs",
2562
+ "diagnostics"
2563
+ ]
2564
+ },
2565
+ "Log": {
2566
+ "type": "object",
2567
+ "properties": {
2568
+ "type": {
2569
+ "$ref": "#/components/schemas/StdOption"
2570
+ },
2571
+ "log": {
2572
+ "type": "string"
2573
+ },
2574
+ "timestamp": {
2575
+ "type": "string"
2576
+ }
2577
+ },
2578
+ "required": [
2579
+ "type",
2580
+ "timestamp"
2581
+ ]
2582
+ },
2583
+ "DiagnosticsReason": {
2584
+ "type": "object",
2585
+ "properties": {
2586
+ "hostShutDown": {
2587
+ "type": "boolean"
2588
+ },
2589
+ "jobStopped": {
2590
+ "type": "boolean"
2591
+ },
2592
+ "jobExpired": {
2593
+ "type": "boolean"
2594
+ },
2595
+ "reason": {
2596
+ "type": "string"
2597
+ }
2598
+ },
2599
+ "required": [
2600
+ "hostShutDown",
2601
+ "jobStopped",
2602
+ "jobExpired"
2603
+ ]
2604
+ },
2605
+ "DiagnosticsState": {
2606
+ "type": "object",
2607
+ "properties": {
2608
+ "Status": {
2609
+ "type": "string"
2610
+ },
2611
+ "Running": {
2612
+ "type": "boolean"
2613
+ },
2614
+ "Paused": {
2615
+ "type": "boolean"
2616
+ },
2617
+ "Restarting": {
2618
+ "type": "boolean"
2619
+ },
2620
+ "OOMKilled": {
2621
+ "type": "boolean"
2622
+ },
2623
+ "Dead": {
2624
+ "type": "boolean"
2625
+ },
2626
+ "Pid": {
2627
+ "type": "number"
2628
+ },
2629
+ "ExitCode": {
2630
+ "type": "number"
2631
+ },
2632
+ "Error": {
2633
+ "type": "string"
2634
+ },
2635
+ "StartedAt": {
2636
+ "type": "string"
2637
+ },
2638
+ "FinishedAt": {
2639
+ "type": "string"
2640
+ },
2641
+ "Health": {
2642
+ "type": "object",
2643
+ "properties": {
2644
+ "Status": {
2645
+ "type": "string"
2646
+ },
2647
+ "FailingStreak": {
2648
+ "type": "number"
2649
+ },
2650
+ "Log": {
2651
+ "oneOf": [
2652
+ {
2653
+ "type": "null"
2654
+ },
2655
+ {
2656
+ "type": "array",
2657
+ "items": {
2658
+ "type": "object",
2659
+ "properties": {
2660
+ "Start": {
2661
+ "type": "string"
2662
+ },
2663
+ "End": {
2664
+ "type": "string"
2665
+ },
2666
+ "ExitCode": {
2667
+ "type": "number"
2668
+ },
2669
+ "Output": {
2670
+ "type": "string"
2671
+ }
2672
+ },
2673
+ "required": [
2674
+ "Start",
2675
+ "End",
2676
+ "ExitCode",
2677
+ "Output"
2678
+ ]
2679
+ }
2680
+ }
2681
+ ]
2682
+ }
2683
+ },
2684
+ "required": [
2685
+ "Status",
2686
+ "FailingStreak",
2687
+ "Log"
2688
+ ]
2689
+ },
2690
+ "RestartCount": {
2691
+ "type": "number"
2692
+ }
2693
+ },
2694
+ "required": [
2695
+ "Status",
2696
+ "Running",
2697
+ "Paused",
2698
+ "Restarting",
2699
+ "OOMKilled",
2700
+ "Dead",
2701
+ "Pid",
2702
+ "ExitCode",
2703
+ "Error",
2704
+ "StartedAt",
2705
+ "FinishedAt",
2706
+ "RestartCount"
2707
+ ]
2708
+ },
2709
+ "FlowSecrets": {
2710
+ "type": "object",
2711
+ "properties": {
2712
+ "urlmode": {
2713
+ "oneOf": [
2714
+ {
2715
+ "const": "public"
2716
+ },
2717
+ {
2718
+ "const": "private"
2719
+ }
2720
+ ]
2721
+ }
2722
+ },
2723
+ "required": [],
2724
+ "additionalProperties": {
2725
+ "oneOf": [
2726
+ {
2727
+ "const": "public"
2728
+ },
2729
+ {
2730
+ "const": "private"
2731
+ },
2732
+ {
2733
+ "$ref": "#/components/schemas/JobExposeSecrets"
2734
+ }
2735
+ ]
2736
+ }
2737
+ },
2738
+ "JobExposeSecrets": {
2739
+ "type": "object",
2740
+ "properties": {},
2741
+ "required": [],
2742
+ "additionalProperties": {
2743
+ "$ref": "#/components/schemas/EndpointSecret"
2744
+ }
2745
+ },
2746
+ "EndpointSecret": {
2747
+ "type": "object",
2748
+ "properties": {
2749
+ "opID": {
2750
+ "type": "string"
2751
+ },
2752
+ "port": {
2753
+ "oneOf": [
2754
+ {
2755
+ "type": "string"
2756
+ },
2757
+ {
2758
+ "type": "number"
2759
+ }
2760
+ ]
2761
+ },
2762
+ "url": {
2763
+ "type": "string"
2764
+ },
2765
+ "status": {
2766
+ "$ref": "#/components/schemas/EndpointStatus"
2767
+ }
2768
+ },
2769
+ "required": [
2770
+ "opID",
2771
+ "port",
2772
+ "url",
2773
+ "status"
2774
+ ]
2775
+ },
2776
+ "EndpointStatus": {
2777
+ "oneOf": [
2778
+ {
2779
+ "const": "ONLINE"
2780
+ },
2781
+ {
2782
+ "const": "OFFLINE"
2783
+ },
2784
+ {
2785
+ "const": "UNKNOWN"
2786
+ }
2787
+ ]
2788
+ },
2789
+ "Address": {
2790
+ "type": "string",
2791
+ "pattern": "^[1-9A-HJ-NP-Za-km-z]{32,44}$",
2792
+ "description": "A Solana address in base58."
2793
+ },
2794
+ "NodeError": {
2795
+ "description": "The node answers failures with a sentence or with `{ error }`.",
2796
+ "oneOf": [
2797
+ {
2798
+ "type": "string"
2799
+ },
2800
+ {
2801
+ "type": "object",
2802
+ "required": [
2803
+ "error"
2804
+ ],
2805
+ "properties": {
2806
+ "error": {
2807
+ "type": "string"
2808
+ }
2809
+ }
2810
+ }
2811
+ ]
2812
+ },
2813
+ "NodeInfo": {
2814
+ "type": "object",
2815
+ "required": [
2816
+ "state",
2817
+ "info",
2818
+ "resources"
2819
+ ],
2820
+ "additionalProperties": true,
2821
+ "properties": {
2822
+ "state": {
2823
+ "type": "string",
2824
+ "description": "What the node is doing right now, as classified by its monitor."
2825
+ },
2826
+ "info": {
2827
+ "type": "object",
2828
+ "additionalProperties": true,
2829
+ "properties": {
2830
+ "gpus": {
2831
+ "type": "object",
2832
+ "additionalProperties": true,
2833
+ "properties": {
2834
+ "devices": {
2835
+ "type": "array",
2836
+ "items": {
2837
+ "type": "object",
2838
+ "additionalProperties": true,
2839
+ "properties": {
2840
+ "index": {
2841
+ "type": "integer"
2842
+ },
2843
+ "name": {
2844
+ "type": "string"
2845
+ },
2846
+ "uuid": {
2847
+ "type": "string"
2848
+ },
2849
+ "memory": {},
2850
+ "network_architecture": {}
2851
+ }
2852
+ }
2853
+ }
2854
+ }
2855
+ },
2856
+ "network": {
2857
+ "type": "object",
2858
+ "additionalProperties": true,
2859
+ "description": "Network details with the IP address redacted."
2860
+ }
2861
+ }
2862
+ },
2863
+ "resources": {
2864
+ "type": "object",
2865
+ "required": [
2866
+ "images",
2867
+ "volumes"
2868
+ ],
2869
+ "properties": {
2870
+ "images": {
2871
+ "type": "array",
2872
+ "items": {
2873
+ "type": "string"
2874
+ }
2875
+ },
2876
+ "volumes": {
2877
+ "type": "array",
2878
+ "items": {
2879
+ "type": "string"
2880
+ }
2881
+ }
2882
+ }
2883
+ }
2884
+ }
2885
+ },
2886
+ "OpStateSummary": {
2887
+ "type": "object",
2888
+ "description": "An operation's state as the info stream sends it: `OpState` without its logs and provider handle.",
2889
+ "properties": {
2890
+ "operationId": {
2891
+ "oneOf": [
2892
+ {
2893
+ "type": "string"
2894
+ },
2895
+ {
2896
+ "type": "null"
2897
+ }
2898
+ ]
2899
+ },
2900
+ "group": {
2901
+ "oneOf": [
2902
+ {
2903
+ "type": "string"
2904
+ },
2905
+ {
2906
+ "type": "null"
2907
+ }
2908
+ ]
2909
+ },
2910
+ "status": {
2911
+ "oneOf": [
2912
+ {
2913
+ "type": "string"
2914
+ },
2915
+ {
2916
+ "type": "null"
2917
+ }
2918
+ ]
2919
+ },
2920
+ "startTime": {
2921
+ "oneOf": [
2922
+ {
2923
+ "type": "integer"
2924
+ },
2925
+ {
2926
+ "type": "null"
2927
+ }
2928
+ ]
2929
+ },
2930
+ "endTime": {
2931
+ "oneOf": [
2932
+ {
2933
+ "type": "integer"
2934
+ },
2935
+ {
2936
+ "type": "null"
2937
+ }
2938
+ ]
2939
+ },
2940
+ "exitCode": {
2941
+ "oneOf": [
2942
+ {
2943
+ "type": "integer"
2944
+ },
2945
+ {
2946
+ "type": "null"
2947
+ }
2948
+ ]
2949
+ },
2950
+ "results": {
2951
+ "type": "object",
2952
+ "additionalProperties": {
2953
+ "oneOf": [
2954
+ {
2955
+ "type": "string"
2956
+ },
2957
+ {
2958
+ "type": "array",
2959
+ "items": {
2960
+ "type": "string"
2961
+ }
2962
+ }
2963
+ ]
2964
+ }
2965
+ },
2966
+ "error": {
2967
+ "type": "object",
2968
+ "required": [
2969
+ "event",
2970
+ "message"
2971
+ ],
2972
+ "properties": {
2973
+ "event": {
2974
+ "type": "string"
2975
+ },
2976
+ "message": {
2977
+ "type": "string"
2978
+ },
2979
+ "code": {
2980
+ "type": "integer"
2981
+ }
2982
+ }
2983
+ },
2984
+ "diagnostics": {
2985
+ "type": "object",
2986
+ "required": [
2987
+ "reason"
2988
+ ],
2989
+ "properties": {
2990
+ "reason": {
2991
+ "$ref": "#/components/schemas/DiagnosticsReason"
2992
+ },
2993
+ "state": {
2994
+ "$ref": "#/components/schemas/DiagnosticsState"
2995
+ }
2996
+ }
2997
+ }
2998
+ }
2999
+ },
3000
+ "OperationStatuses": {
3001
+ "type": "object",
3002
+ "description": "Status per operation, keyed by operation id.",
3003
+ "additionalProperties": {
3004
+ "oneOf": [
3005
+ {
3006
+ "type": "string"
3007
+ },
3008
+ {
3009
+ "type": "null"
3010
+ }
3011
+ ]
3012
+ }
3013
+ },
3014
+ "JobInfo": {
3015
+ "type": "object",
3016
+ "description": "One frame of the job info stream: the flow without logs, plus task status.",
3017
+ "required": [
3018
+ "status",
3019
+ "startTime",
3020
+ "endTime",
3021
+ "errors",
3022
+ "opStates",
3023
+ "operations"
3024
+ ],
3025
+ "properties": {
3026
+ "status": {
3027
+ "type": "string"
3028
+ },
3029
+ "startTime": {
3030
+ "type": "integer"
3031
+ },
3032
+ "endTime": {
3033
+ "oneOf": [
3034
+ {
3035
+ "type": "integer"
3036
+ },
3037
+ {
3038
+ "type": "null"
3039
+ }
3040
+ ]
3041
+ },
3042
+ "errors": {
3043
+ "type": "array",
3044
+ "items": {}
3045
+ },
3046
+ "opStates": {
3047
+ "type": "array",
3048
+ "items": {
3049
+ "$ref": "#/components/schemas/OpStateSummary"
3050
+ }
3051
+ },
3052
+ "secrets": {
3053
+ "$ref": "#/components/schemas/FlowSecrets"
3054
+ },
3055
+ "operations": {
3056
+ "oneOf": [
3057
+ {
3058
+ "type": "object",
3059
+ "required": [
3060
+ "all",
3061
+ "currentGroupStatus"
3062
+ ],
3063
+ "properties": {
3064
+ "all": {
3065
+ "oneOf": [
3066
+ {
3067
+ "$ref": "#/components/schemas/OperationStatuses"
3068
+ },
3069
+ {
3070
+ "type": "null"
3071
+ }
3072
+ ]
3073
+ },
3074
+ "currentGroup": {
3075
+ "type": "string"
3076
+ },
3077
+ "currentGroupStatus": {
3078
+ "oneOf": [
3079
+ {
3080
+ "$ref": "#/components/schemas/OperationStatuses"
3081
+ },
3082
+ {
3083
+ "type": "null"
3084
+ }
3085
+ ]
3086
+ }
3087
+ }
3088
+ },
3089
+ {
3090
+ "type": "null"
3091
+ }
3092
+ ]
3093
+ }
3094
+ }
3095
+ },
3096
+ "JobEndpoints": {
3097
+ "type": "object",
3098
+ "required": [
3099
+ "urls",
3100
+ "status"
3101
+ ],
3102
+ "properties": {
3103
+ "urls": {
3104
+ "$ref": "#/components/schemas/JobExposeSecrets"
3105
+ },
3106
+ "status": {
3107
+ "type": "string",
3108
+ "enum": [
3109
+ "ONLINE",
3110
+ "OFFLINE"
3111
+ ]
3112
+ }
3113
+ }
3114
+ },
3115
+ "TaskStat": {
3116
+ "type": "object",
3117
+ "required": [
3118
+ "opId",
3119
+ "timestamp",
3120
+ "cpu",
3121
+ "memory",
3122
+ "disk",
3123
+ "network"
3124
+ ],
3125
+ "properties": {
3126
+ "opId": {
3127
+ "type": "string"
3128
+ },
3129
+ "timestamp": {
3130
+ "type": "integer",
3131
+ "description": "Unix milliseconds."
3132
+ },
3133
+ "cpu": {
3134
+ "type": "object",
3135
+ "required": [
3136
+ "cpu_percent"
3137
+ ],
3138
+ "properties": {
3139
+ "cpu_percent": {
3140
+ "type": "number"
3141
+ }
3142
+ }
3143
+ },
3144
+ "memory": {
3145
+ "type": "object",
3146
+ "required": [
3147
+ "memory_usage",
3148
+ "memory_limit",
3149
+ "memory_percent"
3150
+ ],
3151
+ "properties": {
3152
+ "memory_usage": {
3153
+ "type": "number"
3154
+ },
3155
+ "memory_limit": {
3156
+ "type": "number"
3157
+ },
3158
+ "memory_percent": {
3159
+ "type": "number"
3160
+ }
3161
+ }
3162
+ },
3163
+ "disk": {
3164
+ "type": "object",
3165
+ "required": [
3166
+ "read",
3167
+ "write"
3168
+ ],
3169
+ "properties": {
3170
+ "read": {
3171
+ "type": "number"
3172
+ },
3173
+ "write": {
3174
+ "type": "number"
3175
+ }
3176
+ }
3177
+ },
3178
+ "network": {
3179
+ "type": "object",
3180
+ "required": [
3181
+ "received",
3182
+ "sent"
3183
+ ],
3184
+ "properties": {
3185
+ "received": {
3186
+ "type": "number"
3187
+ },
3188
+ "sent": {
3189
+ "type": "number"
3190
+ }
3191
+ }
3192
+ }
3193
+ }
3194
+ },
3195
+ "TaskLog": {
3196
+ "type": "object",
3197
+ "required": [
3198
+ "opId",
3199
+ "group",
3200
+ "type",
3201
+ "timestamp",
3202
+ "message"
3203
+ ],
3204
+ "properties": {
3205
+ "opId": {
3206
+ "type": "string"
3207
+ },
3208
+ "group": {
3209
+ "type": "string"
3210
+ },
3211
+ "type": {
3212
+ "type": "string"
3213
+ },
3214
+ "timestamp": {
3215
+ "type": "integer"
3216
+ },
3217
+ "message": {}
3218
+ }
3219
+ },
3220
+ "ActionResult": {
3221
+ "type": "object",
3222
+ "required": [
3223
+ "message"
3224
+ ],
3225
+ "properties": {
3226
+ "message": {
3227
+ "type": "string"
3228
+ }
3229
+ }
3230
+ },
3231
+ "SshPublicKey": {
3232
+ "type": "string",
3233
+ "maxLength": 8192,
3234
+ "description": "One OpenSSH `authorized_keys` line: algorithm, base64 key material and an optional comment."
3235
+ },
3236
+ "SshAuthorizationRequest": {
3237
+ "type": "object",
3238
+ "required": [
3239
+ "sshPublicKey"
3240
+ ],
3241
+ "properties": {
3242
+ "sshPublicKey": {
3243
+ "$ref": "#/components/schemas/SshPublicKey"
3244
+ },
3245
+ "expiresAt": {
3246
+ "type": "string",
3247
+ "format": "date-time",
3248
+ "description": "Omit for access until the key is revoked or the job ends."
3249
+ }
3250
+ }
3251
+ },
3252
+ "SshAuthorization": {
3253
+ "type": "object",
3254
+ "required": [
3255
+ "job",
3256
+ "sshUser",
3257
+ "authorized"
3258
+ ],
3259
+ "properties": {
3260
+ "job": {
3261
+ "$ref": "#/components/schemas/Address"
3262
+ },
3263
+ "sshUser": {
3264
+ "type": "string",
3265
+ "description": "Always `nosana`; the hostname identifies the job and operation."
3266
+ },
3267
+ "authorized": {
3268
+ "type": "boolean",
3269
+ "const": true
3270
+ },
3271
+ "expiresAt": {
3272
+ "type": "string",
3273
+ "format": "date-time"
3274
+ }
3275
+ }
3276
+ },
3277
+ "SshAuthorizedKey": {
3278
+ "type": "object",
3279
+ "required": [
3280
+ "sshPublicKey"
3281
+ ],
3282
+ "properties": {
3283
+ "sshPublicKey": {
3284
+ "$ref": "#/components/schemas/SshPublicKey"
3285
+ },
3286
+ "expiresAt": {
3287
+ "type": "string",
3288
+ "format": "date-time",
3289
+ "description": "Absent for permanent keys."
3290
+ }
3291
+ }
3292
+ },
3293
+ "SshKeys": {
3294
+ "type": "object",
3295
+ "required": [
3296
+ "job",
3297
+ "sshUser",
3298
+ "keys"
3299
+ ],
3300
+ "properties": {
3301
+ "job": {
3302
+ "$ref": "#/components/schemas/Address"
3303
+ },
3304
+ "sshUser": {
3305
+ "type": "string"
3306
+ },
3307
+ "keys": {
3308
+ "type": "array",
3309
+ "items": {
3310
+ "$ref": "#/components/schemas/SshAuthorizedKey"
3311
+ }
3312
+ }
3313
+ }
3314
+ },
3315
+ "SshRevocationRequest": {
3316
+ "type": "object",
3317
+ "required": [
3318
+ "sshPublicKey"
3319
+ ],
3320
+ "properties": {
3321
+ "sshPublicKey": {
3322
+ "$ref": "#/components/schemas/SshPublicKey"
3323
+ }
3324
+ }
3325
+ },
3326
+ "SshRevocation": {
3327
+ "type": "object",
3328
+ "required": [
3329
+ "job",
3330
+ "sshUser",
3331
+ "revoked"
3332
+ ],
3333
+ "properties": {
3334
+ "job": {
3335
+ "$ref": "#/components/schemas/Address"
3336
+ },
3337
+ "sshUser": {
3338
+ "type": "string"
3339
+ },
3340
+ "revoked": {
3341
+ "type": "boolean",
3342
+ "const": true
3343
+ }
3344
+ }
3345
+ }
3346
+ },
3347
+ "securitySchemes": {
3348
+ "NosanaAuthorization": {
3349
+ "type": "apiKey",
3350
+ "in": "header",
3351
+ "name": "authorization",
3352
+ "description": "`message:base58-signature[:timestamp]` as produced by `@nosana/authorization`, signed by the wallet that owns the job. Any message is accepted; a timestamp, when present, must be within the job's timeout."
3353
+ }
3354
+ }
3355
+ },
3356
+ "x-websocket": {
3357
+ "description": "One WebSocket endpoint at the node's origin. The first frame is a JSON handshake `{ path, header, body }` where `header` is the same authorization string the HTTP routes take and `body` names the job; every later frame is `{ path, data }`. Only the terminal takes over the socket for raw frames.",
3358
+ "routes": {
3359
+ "/log": {
3360
+ "body": {
3361
+ "jobAddress": "Address"
3362
+ },
3363
+ "frames": {
3364
+ "path": "log",
3365
+ "data": "a log message"
3366
+ },
3367
+ "description": "Node and container logs for basic job posters."
3368
+ },
3369
+ "/flog": {
3370
+ "body": {
3371
+ "jobAddress": "Address",
3372
+ "group?": "string",
3373
+ "opId?": "string",
3374
+ "type?": "string"
3375
+ },
3376
+ "frames": {
3377
+ "path": "flog",
3378
+ "data": "TaskLog, JSON-encoded"
3379
+ },
3380
+ "description": "Task manager logs: history first, then live."
3381
+ },
3382
+ "/status": {
3383
+ "body": {
3384
+ "jobAddress": "Address"
3385
+ },
3386
+ "frames": {
3387
+ "path": "state",
3388
+ "data": "a state message"
3389
+ },
3390
+ "description": "State updates for the job. The node's own signature is accepted as well as the owner's."
3391
+ },
3392
+ "/terminal": {
3393
+ "body": {
3394
+ "jobAddress": "Address",
3395
+ "message": "the signed grant text",
3396
+ "signature": "base64 of the raw 64-byte signature",
3397
+ "op?": "string",
3398
+ "cols?": "integer",
3399
+ "rows?": "integer"
3400
+ },
3401
+ "frames": {
3402
+ "client": [
3403
+ {
3404
+ "type": "stdin",
3405
+ "data": "string"
3406
+ },
3407
+ {
3408
+ "type": "resize",
3409
+ "cols": "integer",
3410
+ "rows": "integer"
3411
+ }
3412
+ ],
3413
+ "node": [
3414
+ "binary frames: raw stdout and stderr",
3415
+ {
3416
+ "type": "exit",
3417
+ "code": "integer or null"
3418
+ },
3419
+ {
3420
+ "type": "error",
3421
+ "message": "string"
3422
+ }
3423
+ ]
3424
+ },
3425
+ "description": "An interactive shell or one operation's tty. The grant is `Nosana Terminal Authorization v1` followed by `job`, `node`, `expiresAt`, optional `op`, `network` and `audience: nosana-web-terminal` lines, valid for at most five minutes, signed by the job's owner."
3426
+ }
3427
+ }
3428
+ }
3429
+ }