@opensoftware/contracts 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/dist/formats.js +2 -0
- package/dist/formats.js.map +1 -1
- package/dist/generated/types.d.ts +258 -2
- package/dist/index.d.ts +6 -1
- package/dist/index.js.map +1 -1
- package/gate-record/gate-record.schema.json +140 -0
- package/openapi/openapi.json +466 -3
- package/package.json +5 -2
- package/sandbox-spec/sandbox-spec.schema.json +90 -0
- package/typespec/gatekeeper-api.tsp +39 -0
- package/typespec/gatekeeper.tsp +134 -0
- package/typespec/main.tsp +3 -0
- package/typespec/sandbox.tsp +63 -0
package/openapi/openapi.json
CHANGED
|
@@ -2,14 +2,127 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Open Software Factory contracts",
|
|
5
|
-
"version": "1.
|
|
6
|
-
"description": "The shared formats as named models
|
|
5
|
+
"version": "1.6.0",
|
|
6
|
+
"description": "The shared formats as named models, and the HTTP APIs of the modules. Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
7
7
|
"license": {
|
|
8
8
|
"name": "Apache-2.0",
|
|
9
9
|
"identifier": "Apache-2.0"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
-
"paths": {
|
|
12
|
+
"paths": {
|
|
13
|
+
"/v1/gates": {
|
|
14
|
+
"post": {
|
|
15
|
+
"operationId": "GatekeeperApi_requestGate",
|
|
16
|
+
"description": "Ask the policy's approver groups for a decision. The same request again answers the gate it made before.",
|
|
17
|
+
"parameters": [],
|
|
18
|
+
"responses": {
|
|
19
|
+
"200": {
|
|
20
|
+
"description": "The request has succeeded.",
|
|
21
|
+
"content": {
|
|
22
|
+
"application/json": {
|
|
23
|
+
"schema": {
|
|
24
|
+
"$ref": "#/components/schemas/GateView"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"201": {
|
|
30
|
+
"description": "The request has succeeded and a new resource has been created as a result.",
|
|
31
|
+
"content": {
|
|
32
|
+
"application/json": {
|
|
33
|
+
"schema": {
|
|
34
|
+
"$ref": "#/components/schemas/GateView"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"400": {
|
|
40
|
+
"description": "The server could not understand the request due to invalid syntax.",
|
|
41
|
+
"content": {
|
|
42
|
+
"application/problem+json": {
|
|
43
|
+
"schema": {
|
|
44
|
+
"$ref": "#/components/schemas/ApiProblem"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"401": {
|
|
50
|
+
"description": "Access is unauthorized.",
|
|
51
|
+
"content": {
|
|
52
|
+
"application/problem+json": {
|
|
53
|
+
"schema": {
|
|
54
|
+
"$ref": "#/components/schemas/ApiProblem"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"requestBody": {
|
|
61
|
+
"required": true,
|
|
62
|
+
"content": {
|
|
63
|
+
"application/json": {
|
|
64
|
+
"schema": {
|
|
65
|
+
"$ref": "#/components/schemas/GateRequest"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"/v1/gates/{gateId}": {
|
|
73
|
+
"get": {
|
|
74
|
+
"operationId": "GatekeeperApi_getGate",
|
|
75
|
+
"description": "A gate: its state, and its record once it is decided.",
|
|
76
|
+
"parameters": [
|
|
77
|
+
{
|
|
78
|
+
"name": "gateId",
|
|
79
|
+
"in": "path",
|
|
80
|
+
"required": true,
|
|
81
|
+
"schema": {
|
|
82
|
+
"type": "string"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"responses": {
|
|
87
|
+
"200": {
|
|
88
|
+
"description": "The request has succeeded.",
|
|
89
|
+
"content": {
|
|
90
|
+
"application/json": {
|
|
91
|
+
"schema": {
|
|
92
|
+
"$ref": "#/components/schemas/GateView"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"401": {
|
|
98
|
+
"description": "Access is unauthorized.",
|
|
99
|
+
"content": {
|
|
100
|
+
"application/problem+json": {
|
|
101
|
+
"schema": {
|
|
102
|
+
"$ref": "#/components/schemas/ApiProblem"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"404": {
|
|
108
|
+
"description": "The server cannot find the requested resource.",
|
|
109
|
+
"content": {
|
|
110
|
+
"application/problem+json": {
|
|
111
|
+
"schema": {
|
|
112
|
+
"$ref": "#/components/schemas/ApiProblem"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"security": [
|
|
122
|
+
{
|
|
123
|
+
"BearerAuth": []
|
|
124
|
+
}
|
|
125
|
+
],
|
|
13
126
|
"components": {
|
|
14
127
|
"schemas": {
|
|
15
128
|
"Actor": {
|
|
@@ -101,6 +214,31 @@
|
|
|
101
214
|
],
|
|
102
215
|
"additionalProperties": false
|
|
103
216
|
},
|
|
217
|
+
"ApiProblem": {
|
|
218
|
+
"type": "object",
|
|
219
|
+
"properties": {
|
|
220
|
+
"title": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"minLength": 1,
|
|
223
|
+
"description": "A short summary, the same for every occurrence of the problem."
|
|
224
|
+
},
|
|
225
|
+
"status": {
|
|
226
|
+
"type": "integer",
|
|
227
|
+
"minimum": -2147483648,
|
|
228
|
+
"maximum": 2147483647,
|
|
229
|
+
"description": "The HTTP status code."
|
|
230
|
+
},
|
|
231
|
+
"detail": {
|
|
232
|
+
"type": "string",
|
|
233
|
+
"description": "What went wrong this time, for example the field that is missing."
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
"required": [
|
|
237
|
+
"title",
|
|
238
|
+
"status"
|
|
239
|
+
],
|
|
240
|
+
"description": "An error of an HTTP API (RFC 9457)."
|
|
241
|
+
},
|
|
104
242
|
"Artifact": {
|
|
105
243
|
"type": "object",
|
|
106
244
|
"properties": {
|
|
@@ -489,6 +627,127 @@
|
|
|
489
627
|
"expired"
|
|
490
628
|
]
|
|
491
629
|
},
|
|
630
|
+
"GateRecord": {
|
|
631
|
+
"type": "object",
|
|
632
|
+
"properties": {
|
|
633
|
+
"gate": {
|
|
634
|
+
"type": "string",
|
|
635
|
+
"pattern": "^g[0-9a-f]{16}$"
|
|
636
|
+
},
|
|
637
|
+
"workItem": {
|
|
638
|
+
"type": "string"
|
|
639
|
+
},
|
|
640
|
+
"stage": {
|
|
641
|
+
"$ref": "#/components/schemas/Name"
|
|
642
|
+
},
|
|
643
|
+
"summary": {
|
|
644
|
+
"type": "string"
|
|
645
|
+
},
|
|
646
|
+
"request": {
|
|
647
|
+
"type": "string",
|
|
648
|
+
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
649
|
+
"description": "SHA-256 of the canonical request: what was approved."
|
|
650
|
+
},
|
|
651
|
+
"policy": {
|
|
652
|
+
"type": "string",
|
|
653
|
+
"minLength": 1,
|
|
654
|
+
"description": "The policy file."
|
|
655
|
+
},
|
|
656
|
+
"policyDigest": {
|
|
657
|
+
"type": "string",
|
|
658
|
+
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
659
|
+
"description": "SHA-256 of the policy's text: the rules that picked the approvers."
|
|
660
|
+
},
|
|
661
|
+
"risk": {
|
|
662
|
+
"type": "string"
|
|
663
|
+
},
|
|
664
|
+
"approvers": {
|
|
665
|
+
"type": "array",
|
|
666
|
+
"items": {
|
|
667
|
+
"$ref": "#/components/schemas/Name"
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
"channel": {
|
|
671
|
+
"type": "string"
|
|
672
|
+
},
|
|
673
|
+
"requestedAt": {
|
|
674
|
+
"type": "string",
|
|
675
|
+
"format": "date-time"
|
|
676
|
+
},
|
|
677
|
+
"decision": {
|
|
678
|
+
"$ref": "#/components/schemas/GateDecision"
|
|
679
|
+
},
|
|
680
|
+
"decidedBy": {
|
|
681
|
+
"$ref": "#/components/schemas/Actor",
|
|
682
|
+
"description": "Who decided. Absent only when the gate expired. A policy that names no group decides as a trigger."
|
|
683
|
+
},
|
|
684
|
+
"decidedAt": {
|
|
685
|
+
"type": "string",
|
|
686
|
+
"format": "date-time"
|
|
687
|
+
},
|
|
688
|
+
"reason": {
|
|
689
|
+
"type": "string",
|
|
690
|
+
"description": "The reason of a rejection."
|
|
691
|
+
},
|
|
692
|
+
"signature": {
|
|
693
|
+
"$ref": "#/components/schemas/RecordSignature",
|
|
694
|
+
"description": "Ed25519 over the canonical JSON of the rest of the record. Absent when the record is unsigned."
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
"required": [
|
|
698
|
+
"gate",
|
|
699
|
+
"workItem",
|
|
700
|
+
"stage",
|
|
701
|
+
"summary",
|
|
702
|
+
"request",
|
|
703
|
+
"policy",
|
|
704
|
+
"policyDigest",
|
|
705
|
+
"approvers",
|
|
706
|
+
"channel",
|
|
707
|
+
"requestedAt",
|
|
708
|
+
"decision",
|
|
709
|
+
"decidedAt"
|
|
710
|
+
],
|
|
711
|
+
"description": "gatekeeper: who decided what and when, signed with Ed25519 (ADR 0047)."
|
|
712
|
+
},
|
|
713
|
+
"GateRequest": {
|
|
714
|
+
"type": "object",
|
|
715
|
+
"properties": {
|
|
716
|
+
"workItem": {
|
|
717
|
+
"type": "string",
|
|
718
|
+
"minLength": 1,
|
|
719
|
+
"maxLength": 128,
|
|
720
|
+
"description": "What waits: a work item ID or a URL."
|
|
721
|
+
},
|
|
722
|
+
"stage": {
|
|
723
|
+
"$ref": "#/components/schemas/Name",
|
|
724
|
+
"description": "The stage that waits, for example \"merge\"."
|
|
725
|
+
},
|
|
726
|
+
"summary": {
|
|
727
|
+
"type": "string",
|
|
728
|
+
"minLength": 1,
|
|
729
|
+
"description": "What the approvers decide, shown in the channel."
|
|
730
|
+
},
|
|
731
|
+
"links": {
|
|
732
|
+
"type": "array",
|
|
733
|
+
"items": {
|
|
734
|
+
"$ref": "#/components/schemas/Link"
|
|
735
|
+
}
|
|
736
|
+
},
|
|
737
|
+
"change": {
|
|
738
|
+
"type": "object",
|
|
739
|
+
"properties": {},
|
|
740
|
+
"unevaluatedProperties": {},
|
|
741
|
+
"description": "Any facts the policy needs, such as changed paths, labels or the author."
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
"required": [
|
|
745
|
+
"workItem",
|
|
746
|
+
"stage",
|
|
747
|
+
"summary"
|
|
748
|
+
],
|
|
749
|
+
"description": "gatekeeper: what a person is asked to approve, and the facts the policy decides on (ADR 0047)."
|
|
750
|
+
},
|
|
492
751
|
"GateRequested": {
|
|
493
752
|
"type": "object",
|
|
494
753
|
"properties": {
|
|
@@ -597,6 +856,96 @@
|
|
|
597
856
|
"summary"
|
|
598
857
|
]
|
|
599
858
|
},
|
|
859
|
+
"GateState": {
|
|
860
|
+
"type": "string",
|
|
861
|
+
"enum": [
|
|
862
|
+
"pending",
|
|
863
|
+
"approved",
|
|
864
|
+
"rejected",
|
|
865
|
+
"expired"
|
|
866
|
+
],
|
|
867
|
+
"description": "A gate's state: waiting, or how it was decided."
|
|
868
|
+
},
|
|
869
|
+
"GateView": {
|
|
870
|
+
"type": "object",
|
|
871
|
+
"properties": {
|
|
872
|
+
"id": {
|
|
873
|
+
"type": "string",
|
|
874
|
+
"pattern": "^g[0-9a-f]{16}$",
|
|
875
|
+
"description": "\"g\" and 16 hex digits of the SHA-256 of the work item, the stage and the change."
|
|
876
|
+
},
|
|
877
|
+
"workItem": {
|
|
878
|
+
"type": "string"
|
|
879
|
+
},
|
|
880
|
+
"stage": {
|
|
881
|
+
"$ref": "#/components/schemas/Name"
|
|
882
|
+
},
|
|
883
|
+
"summary": {
|
|
884
|
+
"type": "string"
|
|
885
|
+
},
|
|
886
|
+
"risk": {
|
|
887
|
+
"type": "string",
|
|
888
|
+
"description": "The risk the policy named, for example \"high\"."
|
|
889
|
+
},
|
|
890
|
+
"approvers": {
|
|
891
|
+
"type": "array",
|
|
892
|
+
"items": {
|
|
893
|
+
"$ref": "#/components/schemas/Name"
|
|
894
|
+
},
|
|
895
|
+
"description": "The groups whose members may decide. Empty when the policy approved without a person."
|
|
896
|
+
},
|
|
897
|
+
"escalateTo": {
|
|
898
|
+
"type": "array",
|
|
899
|
+
"items": {
|
|
900
|
+
"$ref": "#/components/schemas/Name"
|
|
901
|
+
},
|
|
902
|
+
"description": "The groups asked after the escalation time."
|
|
903
|
+
},
|
|
904
|
+
"channel": {
|
|
905
|
+
"type": "string",
|
|
906
|
+
"description": "Where people decide, for example \"slack:C0GATES\"."
|
|
907
|
+
},
|
|
908
|
+
"state": {
|
|
909
|
+
"$ref": "#/components/schemas/GateState"
|
|
910
|
+
},
|
|
911
|
+
"waitingFor": {
|
|
912
|
+
"type": "array",
|
|
913
|
+
"items": {
|
|
914
|
+
"$ref": "#/components/schemas/Name"
|
|
915
|
+
},
|
|
916
|
+
"description": "The groups that may decide now. Only while the gate is pending."
|
|
917
|
+
},
|
|
918
|
+
"requestedAt": {
|
|
919
|
+
"type": "string",
|
|
920
|
+
"format": "date-time"
|
|
921
|
+
},
|
|
922
|
+
"expiresAt": {
|
|
923
|
+
"type": "string",
|
|
924
|
+
"format": "date-time",
|
|
925
|
+
"description": "When the gate expires without a decision."
|
|
926
|
+
},
|
|
927
|
+
"record": {
|
|
928
|
+
"$ref": "#/components/schemas/GateRecord",
|
|
929
|
+
"description": "The record, once the gate is decided."
|
|
930
|
+
}
|
|
931
|
+
},
|
|
932
|
+
"required": [
|
|
933
|
+
"id",
|
|
934
|
+
"workItem",
|
|
935
|
+
"stage",
|
|
936
|
+
"summary",
|
|
937
|
+
"approvers",
|
|
938
|
+
"channel",
|
|
939
|
+
"state",
|
|
940
|
+
"requestedAt"
|
|
941
|
+
],
|
|
942
|
+
"description": "gatekeeper: a gate as the HTTP API shows it (ADR 0048)."
|
|
943
|
+
},
|
|
944
|
+
"Hostname": {
|
|
945
|
+
"type": "string",
|
|
946
|
+
"pattern": "^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$",
|
|
947
|
+
"description": "A hostname without scheme or port, for example \"api.linear.app\"."
|
|
948
|
+
},
|
|
600
949
|
"Identifier": {
|
|
601
950
|
"type": "string",
|
|
602
951
|
"minLength": 1,
|
|
@@ -997,6 +1346,30 @@
|
|
|
997
1346
|
"description": "The adapters a factory uses, per port (docs/architecture/internals.md). Keys are adapter names.",
|
|
998
1347
|
"additionalProperties": false
|
|
999
1348
|
},
|
|
1349
|
+
"RecordSignature": {
|
|
1350
|
+
"type": "object",
|
|
1351
|
+
"properties": {
|
|
1352
|
+
"algorithm": {
|
|
1353
|
+
"type": "string",
|
|
1354
|
+
"const": "Ed25519"
|
|
1355
|
+
},
|
|
1356
|
+
"key": {
|
|
1357
|
+
"type": "string",
|
|
1358
|
+
"pattern": "^sha256:",
|
|
1359
|
+
"description": "SHA-256 of the public key in DER, like \"sha256:…\"."
|
|
1360
|
+
},
|
|
1361
|
+
"value": {
|
|
1362
|
+
"type": "string",
|
|
1363
|
+
"minLength": 1,
|
|
1364
|
+
"description": "The signature in base64."
|
|
1365
|
+
}
|
|
1366
|
+
},
|
|
1367
|
+
"required": [
|
|
1368
|
+
"algorithm",
|
|
1369
|
+
"key",
|
|
1370
|
+
"value"
|
|
1371
|
+
]
|
|
1372
|
+
},
|
|
1000
1373
|
"ReviewState": {
|
|
1001
1374
|
"type": "string",
|
|
1002
1375
|
"enum": [
|
|
@@ -1320,6 +1693,90 @@
|
|
|
1320
1693
|
],
|
|
1321
1694
|
"additionalProperties": false
|
|
1322
1695
|
},
|
|
1696
|
+
"SandboxMcpServer": {
|
|
1697
|
+
"type": "object",
|
|
1698
|
+
"properties": {
|
|
1699
|
+
"name": {
|
|
1700
|
+
"$ref": "#/components/schemas/Name",
|
|
1701
|
+
"description": "The name in the egress log, for example \"wiki\"."
|
|
1702
|
+
},
|
|
1703
|
+
"url": {
|
|
1704
|
+
"type": "string",
|
|
1705
|
+
"pattern": "^https?://[^/]+",
|
|
1706
|
+
"description": "The server's endpoint, for example \"https://wiki.example.com/mcp\"."
|
|
1707
|
+
},
|
|
1708
|
+
"tools": {
|
|
1709
|
+
"type": "array",
|
|
1710
|
+
"items": {
|
|
1711
|
+
"type": "string"
|
|
1712
|
+
},
|
|
1713
|
+
"minItems": 1,
|
|
1714
|
+
"description": "The tools the sandbox may call. A call to any other tool is refused."
|
|
1715
|
+
}
|
|
1716
|
+
},
|
|
1717
|
+
"required": [
|
|
1718
|
+
"name",
|
|
1719
|
+
"url",
|
|
1720
|
+
"tools"
|
|
1721
|
+
],
|
|
1722
|
+
"description": "An MCP server that a sandbox may call over Streamable HTTP.",
|
|
1723
|
+
"additionalProperties": false
|
|
1724
|
+
},
|
|
1725
|
+
"SandboxSpec": {
|
|
1726
|
+
"type": "object",
|
|
1727
|
+
"properties": {
|
|
1728
|
+
"template": {
|
|
1729
|
+
"type": "string",
|
|
1730
|
+
"minLength": 1,
|
|
1731
|
+
"description": "What the sandbox starts from: an image for the docker adapter, a SandboxTemplate for kubernetes, or a snapshot."
|
|
1732
|
+
},
|
|
1733
|
+
"cpu": {
|
|
1734
|
+
"type": "number",
|
|
1735
|
+
"minimum": 0.001,
|
|
1736
|
+
"description": "CPU cores. The smallest unit is 0.001 (one millicore)."
|
|
1737
|
+
},
|
|
1738
|
+
"memory": {
|
|
1739
|
+
"type": "string",
|
|
1740
|
+
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti)?$",
|
|
1741
|
+
"description": "Kubernetes quantity, for example \"8Gi\"."
|
|
1742
|
+
},
|
|
1743
|
+
"egress": {
|
|
1744
|
+
"type": "array",
|
|
1745
|
+
"items": {
|
|
1746
|
+
"$ref": "#/components/schemas/Hostname"
|
|
1747
|
+
},
|
|
1748
|
+
"description": "Hostnames the sandbox may reach through its egress proxy. Everything else is refused."
|
|
1749
|
+
},
|
|
1750
|
+
"env": {
|
|
1751
|
+
"type": "object",
|
|
1752
|
+
"properties": {},
|
|
1753
|
+
"unevaluatedProperties": {
|
|
1754
|
+
"type": "string"
|
|
1755
|
+
},
|
|
1756
|
+
"description": "Variables of the sandbox's environment that hold no secret."
|
|
1757
|
+
},
|
|
1758
|
+
"secrets": {
|
|
1759
|
+
"type": "object",
|
|
1760
|
+
"properties": {},
|
|
1761
|
+
"unevaluatedProperties": {
|
|
1762
|
+
"$ref": "#/components/schemas/SecretReference"
|
|
1763
|
+
},
|
|
1764
|
+
"description": "Secrets by the name of the variable that holds the placeholder in the sandbox. The egress proxy puts the real\nvalue in on the way out, for the hosts of `egress`."
|
|
1765
|
+
},
|
|
1766
|
+
"mcp": {
|
|
1767
|
+
"type": "array",
|
|
1768
|
+
"items": {
|
|
1769
|
+
"$ref": "#/components/schemas/SandboxMcpServer"
|
|
1770
|
+
},
|
|
1771
|
+
"description": "The MCP servers the sandbox may call. A call to any other MCP server is refused."
|
|
1772
|
+
}
|
|
1773
|
+
},
|
|
1774
|
+
"required": [
|
|
1775
|
+
"template"
|
|
1776
|
+
],
|
|
1777
|
+
"description": "The input of a sandbox adapter's `create` (ADR 0054): what one sandbox runs, how much it may use, and what it may\nreach. Realizes SCN-FACTORY-RUNTIME-004, -006 and -007.",
|
|
1778
|
+
"additionalProperties": false
|
|
1779
|
+
},
|
|
1323
1780
|
"ScenarioEvidence": {
|
|
1324
1781
|
"type": "object",
|
|
1325
1782
|
"properties": {
|
|
@@ -1710,6 +2167,12 @@
|
|
|
1710
2167
|
],
|
|
1711
2168
|
"description": "intake: a work item was accepted."
|
|
1712
2169
|
}
|
|
2170
|
+
},
|
|
2171
|
+
"securitySchemes": {
|
|
2172
|
+
"BearerAuth": {
|
|
2173
|
+
"type": "http",
|
|
2174
|
+
"scheme": "Bearer"
|
|
2175
|
+
}
|
|
1713
2176
|
}
|
|
1714
2177
|
}
|
|
1715
2178
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensoftware/contracts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "The shared formats of the open software factory: JSON Schemas, TypeScript types and the osf-contracts CLI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -39,9 +39,12 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@asyncapi/parser": "3.6.6",
|
|
41
41
|
"@typespec/compiler": "1.17.0",
|
|
42
|
+
"@typespec/http": "1.17.0",
|
|
42
43
|
"@typespec/json-schema": "1.17.0",
|
|
44
|
+
"@typespec/openapi": "1.17.0",
|
|
45
|
+
"@typespec/openapi3": "1.17.0",
|
|
43
46
|
"openapi-typescript": "7.13.0",
|
|
44
|
-
"@opensoftware/testkit": "0.
|
|
47
|
+
"@opensoftware/testkit": "0.16.0"
|
|
45
48
|
},
|
|
46
49
|
"scripts": {
|
|
47
50
|
"build": "node scripts/build.ts",
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/sesigl/open-software-factory/main/contracts/sandbox-spec/sandbox-spec.schema.json",
|
|
4
|
+
"title": "SandboxSpec",
|
|
5
|
+
"$comment": "Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"template": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"minLength": 1,
|
|
11
|
+
"description": "What the sandbox starts from: an image for the docker adapter, a SandboxTemplate for kubernetes, or a snapshot."
|
|
12
|
+
},
|
|
13
|
+
"cpu": {
|
|
14
|
+
"type": "number",
|
|
15
|
+
"minimum": 0.001,
|
|
16
|
+
"description": "CPU cores. The smallest unit is 0.001 (one millicore)."
|
|
17
|
+
},
|
|
18
|
+
"memory": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti)?$",
|
|
21
|
+
"description": "Kubernetes quantity, for example \"8Gi\"."
|
|
22
|
+
},
|
|
23
|
+
"egress": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$",
|
|
28
|
+
"description": "A hostname without scheme or port, for example \"api.linear.app\"."
|
|
29
|
+
},
|
|
30
|
+
"description": "Hostnames the sandbox may reach through its egress proxy. Everything else is refused."
|
|
31
|
+
},
|
|
32
|
+
"env": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"properties": {},
|
|
35
|
+
"unevaluatedProperties": {
|
|
36
|
+
"type": "string"
|
|
37
|
+
},
|
|
38
|
+
"description": "Variables of the sandbox's environment that hold no secret."
|
|
39
|
+
},
|
|
40
|
+
"secrets": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {},
|
|
43
|
+
"unevaluatedProperties": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"pattern": "^ref:[a-z][a-z0-9-]*/[A-Za-z0-9_./-]+$",
|
|
46
|
+
"description": "A reference to a secret, resolved at the egress proxy, never in the sandbox: \"ref:<provider>/<name>\"."
|
|
47
|
+
},
|
|
48
|
+
"description": "Secrets by the name of the variable that holds the placeholder in the sandbox. The egress proxy puts the real\nvalue in on the way out, for the hosts of `egress`."
|
|
49
|
+
},
|
|
50
|
+
"mcp": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"properties": {
|
|
55
|
+
"name": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"pattern": "^[a-z][a-z0-9-]{0,62}$",
|
|
58
|
+
"description": "The name in the egress log, for example \"wiki\"."
|
|
59
|
+
},
|
|
60
|
+
"url": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"pattern": "^https?://[^/]+",
|
|
63
|
+
"description": "The server's endpoint, for example \"https://wiki.example.com/mcp\"."
|
|
64
|
+
},
|
|
65
|
+
"tools": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"items": {
|
|
68
|
+
"type": "string"
|
|
69
|
+
},
|
|
70
|
+
"minItems": 1,
|
|
71
|
+
"description": "The tools the sandbox may call. A call to any other tool is refused."
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"required": [
|
|
75
|
+
"name",
|
|
76
|
+
"url",
|
|
77
|
+
"tools"
|
|
78
|
+
],
|
|
79
|
+
"description": "An MCP server that a sandbox may call over Streamable HTTP.",
|
|
80
|
+
"additionalProperties": false
|
|
81
|
+
},
|
|
82
|
+
"description": "The MCP servers the sandbox may call. A call to any other MCP server is refused."
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"required": [
|
|
86
|
+
"template"
|
|
87
|
+
],
|
|
88
|
+
"description": "The input of a sandbox adapter's `create` (ADR 0054): what one sandbox runs, how much it may use, and what it may\nreach. Realizes SCN-FACTORY-RUNTIME-004, -006 and -007.",
|
|
89
|
+
"additionalProperties": false
|
|
90
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// The HTTP API of `gatekeeper serve` (ADR 0048). `pnpm build` compiles it with the OpenAPI 3 emitter and copies its
|
|
2
|
+
// paths into openapi/openapi.json; the models are the named models of the JSON Schema bundle.
|
|
3
|
+
import "@typespec/http";
|
|
4
|
+
import "./gatekeeper.tsp";
|
|
5
|
+
|
|
6
|
+
using TypeSpec.Http;
|
|
7
|
+
|
|
8
|
+
@service(#{ title: "Open Software Factory contracts" })
|
|
9
|
+
@useAuth(BearerAuth)
|
|
10
|
+
namespace Osf.Contracts;
|
|
11
|
+
|
|
12
|
+
namespace GatekeeperApi {
|
|
13
|
+
@error
|
|
14
|
+
model Failure<Code extends int32> {
|
|
15
|
+
@statusCode statusCode: Code;
|
|
16
|
+
@header contentType: "application/problem+json";
|
|
17
|
+
@body problem: ApiProblem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Ask the policy's approver groups for a decision. The same request again answers the gate it made before. */
|
|
21
|
+
@route("/v1/gates")
|
|
22
|
+
@post
|
|
23
|
+
op requestGate(@body request: GateRequest):
|
|
24
|
+
| {
|
|
25
|
+
@statusCode statusCode: 201;
|
|
26
|
+
@body gate: GateView;
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
@statusCode statusCode: 200;
|
|
30
|
+
@body gate: GateView;
|
|
31
|
+
}
|
|
32
|
+
| Failure<400>
|
|
33
|
+
| Failure<401>;
|
|
34
|
+
|
|
35
|
+
/** A gate: its state, and its record once it is decided. */
|
|
36
|
+
@route("/v1/gates/{gateId}")
|
|
37
|
+
@get
|
|
38
|
+
op getGate(@path gateId: string): GateView | Failure<401> | Failure<404>;
|
|
39
|
+
}
|