@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
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "./common.tsp";
|
|
3
|
+
|
|
4
|
+
using TypeSpec.JsonSchema;
|
|
5
|
+
|
|
6
|
+
namespace Osf.Contracts;
|
|
7
|
+
|
|
8
|
+
/** gatekeeper: what a person is asked to approve, and the facts the policy decides on (ADR 0047). */
|
|
9
|
+
model GateRequest {
|
|
10
|
+
/** What waits: a work item ID or a URL. */
|
|
11
|
+
@minLength(1)
|
|
12
|
+
@maxLength(128)
|
|
13
|
+
workItem: string;
|
|
14
|
+
|
|
15
|
+
/** The stage that waits, for example "merge". */
|
|
16
|
+
stage: Name;
|
|
17
|
+
|
|
18
|
+
/** What the approvers decide, shown in the channel. */
|
|
19
|
+
@minLength(1)
|
|
20
|
+
summary: string;
|
|
21
|
+
|
|
22
|
+
links?: Link[];
|
|
23
|
+
|
|
24
|
+
/** Any facts the policy needs, such as changed paths, labels or the author. */
|
|
25
|
+
change?: Record<unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A gate's state: waiting, or how it was decided. */
|
|
29
|
+
enum GateState {
|
|
30
|
+
pending,
|
|
31
|
+
approved,
|
|
32
|
+
rejected,
|
|
33
|
+
expired,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** gatekeeper: a gate as the HTTP API shows it (ADR 0048). */
|
|
37
|
+
model GateView {
|
|
38
|
+
/** "g" and 16 hex digits of the SHA-256 of the work item, the stage and the change. */
|
|
39
|
+
@pattern("^g[0-9a-f]{16}$")
|
|
40
|
+
id: string;
|
|
41
|
+
|
|
42
|
+
workItem: string;
|
|
43
|
+
stage: Name;
|
|
44
|
+
summary: string;
|
|
45
|
+
|
|
46
|
+
/** The risk the policy named, for example "high". */
|
|
47
|
+
risk?: string;
|
|
48
|
+
|
|
49
|
+
/** The groups whose members may decide. Empty when the policy approved without a person. */
|
|
50
|
+
approvers: Name[];
|
|
51
|
+
|
|
52
|
+
/** The groups asked after the escalation time. */
|
|
53
|
+
escalateTo?: Name[];
|
|
54
|
+
|
|
55
|
+
/** Where people decide, for example "slack:C0GATES". */
|
|
56
|
+
channel: string;
|
|
57
|
+
|
|
58
|
+
state: GateState;
|
|
59
|
+
|
|
60
|
+
/** The groups that may decide now. Only while the gate is pending. */
|
|
61
|
+
waitingFor?: Name[];
|
|
62
|
+
|
|
63
|
+
requestedAt: utcDateTime;
|
|
64
|
+
|
|
65
|
+
/** When the gate expires without a decision. */
|
|
66
|
+
expiresAt?: utcDateTime;
|
|
67
|
+
|
|
68
|
+
/** The record, once the gate is decided. */
|
|
69
|
+
record?: GateRecord;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** gatekeeper: who decided what and when, signed with Ed25519 (ADR 0047). */
|
|
73
|
+
model GateRecord {
|
|
74
|
+
@pattern("^g[0-9a-f]{16}$")
|
|
75
|
+
gate: string;
|
|
76
|
+
|
|
77
|
+
workItem: string;
|
|
78
|
+
stage: Name;
|
|
79
|
+
summary: string;
|
|
80
|
+
|
|
81
|
+
/** SHA-256 of the canonical request: what was approved. */
|
|
82
|
+
@pattern("^sha256:[0-9a-f]{64}$")
|
|
83
|
+
request: string;
|
|
84
|
+
|
|
85
|
+
/** The policy file. */
|
|
86
|
+
@minLength(1)
|
|
87
|
+
policy: string;
|
|
88
|
+
|
|
89
|
+
/** SHA-256 of the policy's text: the rules that picked the approvers. */
|
|
90
|
+
@pattern("^sha256:[0-9a-f]{64}$")
|
|
91
|
+
policyDigest: string;
|
|
92
|
+
|
|
93
|
+
risk?: string;
|
|
94
|
+
approvers: Name[];
|
|
95
|
+
channel: string;
|
|
96
|
+
requestedAt: utcDateTime;
|
|
97
|
+
decision: GateDecision;
|
|
98
|
+
|
|
99
|
+
/** Who decided. Absent only when the gate expired. A policy that names no group decides as a trigger. */
|
|
100
|
+
decidedBy?: Actor;
|
|
101
|
+
|
|
102
|
+
decidedAt: utcDateTime;
|
|
103
|
+
|
|
104
|
+
/** The reason of a rejection. */
|
|
105
|
+
reason?: string;
|
|
106
|
+
|
|
107
|
+
/** Ed25519 over the canonical JSON of the rest of the record. Absent when the record is unsigned. */
|
|
108
|
+
signature?: RecordSignature;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
model RecordSignature {
|
|
112
|
+
algorithm: "Ed25519";
|
|
113
|
+
|
|
114
|
+
/** SHA-256 of the public key in DER, like "sha256:…". */
|
|
115
|
+
@pattern("^sha256:")
|
|
116
|
+
key: string;
|
|
117
|
+
|
|
118
|
+
/** The signature in base64. */
|
|
119
|
+
@minLength(1)
|
|
120
|
+
value: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** An error of an HTTP API (RFC 9457). */
|
|
124
|
+
model ApiProblem {
|
|
125
|
+
/** A short summary, the same for every occurrence of the problem. */
|
|
126
|
+
@minLength(1)
|
|
127
|
+
title: string;
|
|
128
|
+
|
|
129
|
+
/** The HTTP status code. */
|
|
130
|
+
status: int32;
|
|
131
|
+
|
|
132
|
+
/** What went wrong this time, for example the field that is missing. */
|
|
133
|
+
detail?: string;
|
|
134
|
+
}
|
package/typespec/main.tsp
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "./common.tsp";
|
|
3
|
+
import "./factory-definition.tsp";
|
|
4
|
+
|
|
5
|
+
using TypeSpec.JsonSchema;
|
|
6
|
+
|
|
7
|
+
namespace Osf.Contracts;
|
|
8
|
+
|
|
9
|
+
// The sandbox spec is written by a program (foreman) for a sandbox adapter, and a sandbox must never get more than
|
|
10
|
+
// it asked for: every object is closed, so an unknown field is an error and not a silent grant.
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The input of a sandbox adapter's `create` (ADR 0054): what one sandbox runs, how much it may use, and what it may
|
|
14
|
+
* reach. Realizes SCN-FACTORY-RUNTIME-004, -006 and -007.
|
|
15
|
+
*/
|
|
16
|
+
@extension("additionalProperties", Json<false>)
|
|
17
|
+
model SandboxSpec {
|
|
18
|
+
/** What the sandbox starts from: an image for the docker adapter, a SandboxTemplate for kubernetes, or a snapshot. */
|
|
19
|
+
@minLength(1)
|
|
20
|
+
template: string;
|
|
21
|
+
|
|
22
|
+
/** CPU cores. The smallest unit is 0.001 (one millicore). */
|
|
23
|
+
@minValue(0.001)
|
|
24
|
+
cpu?: float64;
|
|
25
|
+
|
|
26
|
+
/** Kubernetes quantity, for example "8Gi". */
|
|
27
|
+
@pattern("^[0-9]+(Ki|Mi|Gi|Ti)?$")
|
|
28
|
+
memory?: string;
|
|
29
|
+
|
|
30
|
+
/** Hostnames the sandbox may reach through its egress proxy. Everything else is refused. */
|
|
31
|
+
egress?: Hostname[];
|
|
32
|
+
|
|
33
|
+
/** Variables of the sandbox's environment that hold no secret. */
|
|
34
|
+
env?: Record<string>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Secrets by the name of the variable that holds the placeholder in the sandbox. The egress proxy puts the real
|
|
38
|
+
* value in on the way out, for the hosts of `egress`.
|
|
39
|
+
*/
|
|
40
|
+
secrets?: Record<SecretReference>;
|
|
41
|
+
|
|
42
|
+
/** The MCP servers the sandbox may call. A call to any other MCP server is refused. */
|
|
43
|
+
mcp?: SandboxMcpServer[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** A hostname without scheme or port, for example "api.linear.app". */
|
|
47
|
+
@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])?)*$")
|
|
48
|
+
scalar Hostname extends string;
|
|
49
|
+
|
|
50
|
+
/** An MCP server that a sandbox may call over Streamable HTTP. */
|
|
51
|
+
@extension("additionalProperties", Json<false>)
|
|
52
|
+
model SandboxMcpServer {
|
|
53
|
+
/** The name in the egress log, for example "wiki". */
|
|
54
|
+
name: Name;
|
|
55
|
+
|
|
56
|
+
/** The server's endpoint, for example "https://wiki.example.com/mcp". */
|
|
57
|
+
@pattern("^https?://[^/]+")
|
|
58
|
+
url: string;
|
|
59
|
+
|
|
60
|
+
/** The tools the sandbox may call. A call to any other tool is refused. */
|
|
61
|
+
@minItems(1)
|
|
62
|
+
tools: string[];
|
|
63
|
+
}
|