@friggframework/schemas 2.0.0--canary.688.87a9bf3.0 → 2.0.0--canary.690.d043f23.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/package.json +2 -2
- package/schemas/api-authorization.schema.json +46 -7
- package/schemas/api-credentials.schema.json +28 -6
- package/schemas/api-entities.schema.json +43 -7
- package/schemas/api-module-definition.schema.json +47 -0
- package/schemas/api-proxy.schema.json +32 -13
- package/schemas/app-definition.schema.json +328 -44
- package/schemas/integration-definition.schema.json +51 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/schemas",
|
|
3
|
-
"version": "2.0.0--canary.
|
|
3
|
+
"version": "2.0.0--canary.690.d043f23.0",
|
|
4
4
|
"description": "Canonical JSON Schema definitions for Frigg Framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "",
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"middleware/",
|
|
42
42
|
"index.js"
|
|
43
43
|
],
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "d043f234d5a629b6280d399daf0bae6465498ed3"
|
|
45
45
|
}
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
"definitions": {
|
|
7
7
|
"authorizationRequirements": {
|
|
8
8
|
"type": "object",
|
|
9
|
-
"description": "Authorization requirements response from GET /api/authorize",
|
|
9
|
+
"description": "Authorization requirements response from GET /api/v2/authorize (GET /api/authorize in v1 returns the module requirements unwrapped)",
|
|
10
10
|
"required": ["type", "step", "totalSteps", "isMultiStep"],
|
|
11
11
|
"properties": {
|
|
12
12
|
"type": {
|
|
13
13
|
"type": "string",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
14
|
+
"description": "Type of authentication required: oauth2, form, api-key, basic, or a module-defined step type (e.g. email, otp)",
|
|
15
|
+
"examples": ["oauth2", "form", "api-key", "basic", "otp"]
|
|
16
16
|
},
|
|
17
17
|
"step": {
|
|
18
18
|
"type": "integer",
|
|
@@ -112,13 +112,13 @@
|
|
|
112
112
|
},
|
|
113
113
|
"getEntityTypeRequirementsResponse": {
|
|
114
114
|
"type": "object",
|
|
115
|
-
"description": "Authorization requirements response from GET /api/entities/types/:
|
|
115
|
+
"description": "Authorization requirements response from GET /api/v2/entities/types/:entityType/requirements",
|
|
116
116
|
"required": ["type", "step", "totalSteps", "isMultiStep"],
|
|
117
117
|
"properties": {
|
|
118
118
|
"type": {
|
|
119
119
|
"type": "string",
|
|
120
|
-
"
|
|
121
|
-
"
|
|
120
|
+
"description": "Type of authentication required: oauth2, form, api-key, basic, or a module-defined step type (e.g. email, otp)",
|
|
121
|
+
"examples": ["oauth2", "form", "api-key", "basic", "otp"]
|
|
122
122
|
},
|
|
123
123
|
"step": {
|
|
124
124
|
"type": "integer",
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
},
|
|
152
152
|
"authorizationRequest": {
|
|
153
153
|
"type": "object",
|
|
154
|
-
"description": "Authorization request to POST /api/authorize",
|
|
154
|
+
"description": "Authorization request to POST /api/v2/authorize (and v1 POST /api/authorize)",
|
|
155
155
|
"required": ["entityType"],
|
|
156
156
|
"properties": {
|
|
157
157
|
"entityType": {
|
|
@@ -173,6 +173,11 @@
|
|
|
173
173
|
"default": 1,
|
|
174
174
|
"description": "Current step number for multi-step flows"
|
|
175
175
|
},
|
|
176
|
+
"scope": {
|
|
177
|
+
"type": "string",
|
|
178
|
+
"enum": ["individual", "organization"],
|
|
179
|
+
"description": "The scope to connect at, from the entity type's allow list (ADR-056 §3.1). Omit to take the type's default. global is refused."
|
|
180
|
+
},
|
|
176
181
|
"sessionId": {
|
|
177
182
|
"type": "string",
|
|
178
183
|
"description": "Session ID from previous step (required for step > 1)"
|
|
@@ -238,6 +243,40 @@
|
|
|
238
243
|
}
|
|
239
244
|
}
|
|
240
245
|
},
|
|
246
|
+
"authorizeComplete": {
|
|
247
|
+
"type": "object",
|
|
248
|
+
"description": "POST /api/v2/authorize (or /api/v2/credentials/:credentialId/reauthorize) when the flow is complete. The credential is masked.",
|
|
249
|
+
"required": ["status", "entity", "credential"],
|
|
250
|
+
"properties": {
|
|
251
|
+
"status": { "const": "complete" },
|
|
252
|
+
"entity": { "type": "object", "description": "api-entities#/definitions/entity" },
|
|
253
|
+
"credential": { "type": "object", "description": "api-credentials#/definitions/credential, masked" },
|
|
254
|
+
"previousCredentialId": {
|
|
255
|
+
"type": "string",
|
|
256
|
+
"description": "Re-authorization only: set when the provider account changed and a new credential was stored"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"authorizePending": {
|
|
261
|
+
"type": "object",
|
|
262
|
+
"description": "POST /api/v2/authorize when another step is needed",
|
|
263
|
+
"required": ["status", "step", "totalSteps", "sessionId", "requirements"],
|
|
264
|
+
"properties": {
|
|
265
|
+
"status": { "const": "pending" },
|
|
266
|
+
"step": { "type": "integer", "minimum": 2 },
|
|
267
|
+
"totalSteps": { "type": "integer", "minimum": 2 },
|
|
268
|
+
"sessionId": { "type": "string" },
|
|
269
|
+
"requirements": { "$ref": "#/definitions/authorizationRequirements" },
|
|
270
|
+
"message": { "type": "string" }
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
"authorizeV2Response": {
|
|
274
|
+
"description": "Response from POST /api/v2/authorize and POST /api/v2/credentials/:credentialId/reauthorize",
|
|
275
|
+
"oneOf": [
|
|
276
|
+
{ "$ref": "#/definitions/authorizeComplete" },
|
|
277
|
+
{ "$ref": "#/definitions/authorizePending" }
|
|
278
|
+
]
|
|
279
|
+
},
|
|
241
280
|
"authorizationSession": {
|
|
242
281
|
"type": "object",
|
|
243
282
|
"description": "Authorization session stored in database",
|
|
@@ -29,10 +29,32 @@
|
|
|
29
29
|
"type": "boolean",
|
|
30
30
|
"description": "Whether the authentication is currently valid"
|
|
31
31
|
},
|
|
32
|
+
"scope": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"enum": ["individual", "organization"],
|
|
35
|
+
"description": "Whose the credential is (ADR-056 §3.4), from who owns the credential. `global` ones are not listed to tenants."
|
|
36
|
+
},
|
|
37
|
+
"owner": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": {
|
|
40
|
+
"type": { "type": "string", "enum": ["individual", "organization"] },
|
|
41
|
+
"id": { "type": "string" }
|
|
42
|
+
},
|
|
43
|
+
"description": "The user that owns it: the person, or the organization's user"
|
|
44
|
+
},
|
|
45
|
+
"organizationId": {
|
|
46
|
+
"type": ["string", "null"],
|
|
47
|
+
"description": "The organization it belongs to. Null for an individual credential connected outside an organization, or under ownership.individualAcross 'all'."
|
|
48
|
+
},
|
|
49
|
+
"access": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"enum": ["manage", "use", "view"],
|
|
52
|
+
"description": "What the caller may do: manage (reauthorize, delete) or use. A member only uses an organization's credential."
|
|
53
|
+
},
|
|
32
54
|
"entityCount": {
|
|
33
55
|
"type": "integer",
|
|
34
56
|
"minimum": 0,
|
|
35
|
-
"description": "Number of entities using this credential"
|
|
57
|
+
"description": "Number of entities using this credential that the caller may see"
|
|
36
58
|
},
|
|
37
59
|
"createdAt": {
|
|
38
60
|
"type": "string",
|
|
@@ -48,7 +70,7 @@
|
|
|
48
70
|
},
|
|
49
71
|
"listCredentialsResponse": {
|
|
50
72
|
"type": "object",
|
|
51
|
-
"description": "Response from GET /api/credentials - returns list of credentials for the authenticated user",
|
|
73
|
+
"description": "Response from GET /api/v2/credentials - returns list of credentials for the authenticated user",
|
|
52
74
|
"required": ["credentials"],
|
|
53
75
|
"properties": {
|
|
54
76
|
"credentials": {
|
|
@@ -61,7 +83,7 @@
|
|
|
61
83
|
}
|
|
62
84
|
},
|
|
63
85
|
"getCredentialResponse": {
|
|
64
|
-
"description": "Response from GET /api/credentials/:
|
|
86
|
+
"description": "Response from GET /api/v2/credentials/:credentialId - returns a single credential",
|
|
65
87
|
"allOf": [
|
|
66
88
|
{
|
|
67
89
|
"$ref": "#/definitions/credential"
|
|
@@ -70,7 +92,7 @@
|
|
|
70
92
|
},
|
|
71
93
|
"deleteCredentialResponse": {
|
|
72
94
|
"type": "object",
|
|
73
|
-
"description": "
|
|
95
|
+
"description": "Legacy delete result. DELETE /api/v2/credentials/:credentialId answers 204 No Content.",
|
|
74
96
|
"required": ["success"],
|
|
75
97
|
"properties": {
|
|
76
98
|
"success": {
|
|
@@ -85,7 +107,7 @@
|
|
|
85
107
|
},
|
|
86
108
|
"reauthorizeCredentialRequest": {
|
|
87
109
|
"type": "object",
|
|
88
|
-
"description": "Request to POST /api/credentials/:
|
|
110
|
+
"description": "Request to POST /api/v2/credentials/:credentialId/reauthorize - refresh or update credential authentication",
|
|
89
111
|
"required": ["data"],
|
|
90
112
|
"properties": {
|
|
91
113
|
"data": {
|
|
@@ -107,7 +129,7 @@
|
|
|
107
129
|
},
|
|
108
130
|
"reauthorizeCredentialResponse": {
|
|
109
131
|
"type": "object",
|
|
110
|
-
"description": "
|
|
132
|
+
"description": "Legacy re-authorization result. POST /api/v2/credentials/:credentialId/reauthorize answers api-authorization#/definitions/authorizeV2Response.",
|
|
111
133
|
"oneOf": [
|
|
112
134
|
{
|
|
113
135
|
"$ref": "#/definitions/reauthorizeCredentialSuccess"
|
|
@@ -37,6 +37,28 @@
|
|
|
37
37
|
"type": "boolean",
|
|
38
38
|
"description": "Whether authentication is currently valid"
|
|
39
39
|
},
|
|
40
|
+
"scope": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"enum": ["individual", "organization"],
|
|
43
|
+
"description": "Whose the entity is (ADR-056 §3.4). `global` ones are not listed to tenants."
|
|
44
|
+
},
|
|
45
|
+
"owner": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"properties": {
|
|
48
|
+
"type": { "type": "string", "enum": ["individual", "organization"] },
|
|
49
|
+
"id": { "type": "string" }
|
|
50
|
+
},
|
|
51
|
+
"description": "The user that owns it: the person, or the organization's user"
|
|
52
|
+
},
|
|
53
|
+
"organizationId": {
|
|
54
|
+
"type": ["string", "null"],
|
|
55
|
+
"description": "The organization it belongs to. Null for an individual entity connected outside an organization, or under ownership.individualAcross 'all'."
|
|
56
|
+
},
|
|
57
|
+
"access": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"enum": ["manage", "use", "view"],
|
|
60
|
+
"description": "What the caller may do: manage (create, reauthorize, delete), use (bind it, proxy through it) or view (metadata only)"
|
|
61
|
+
},
|
|
40
62
|
"createdAt": {
|
|
41
63
|
"type": "string",
|
|
42
64
|
"format": "date-time",
|
|
@@ -51,7 +73,7 @@
|
|
|
51
73
|
},
|
|
52
74
|
"listEntitiesResponse": {
|
|
53
75
|
"type": "object",
|
|
54
|
-
"description": "Response from GET /api/entities",
|
|
76
|
+
"description": "Response from GET /api/v2/entities",
|
|
55
77
|
"required": ["entities"],
|
|
56
78
|
"properties": {
|
|
57
79
|
"entities": {
|
|
@@ -65,7 +87,7 @@
|
|
|
65
87
|
},
|
|
66
88
|
"createEntityRequest": {
|
|
67
89
|
"type": "object",
|
|
68
|
-
"description": "Request to POST /api/entities",
|
|
90
|
+
"description": "Request to POST /api/entities (v1 POST /api/entity). Not served by v2: POST /api/v2/authorize creates the entity.",
|
|
69
91
|
"required": ["entityType", "data"],
|
|
70
92
|
"properties": {
|
|
71
93
|
"entityType": {
|
|
@@ -88,7 +110,7 @@
|
|
|
88
110
|
},
|
|
89
111
|
"createEntityResponse": {
|
|
90
112
|
"type": "object",
|
|
91
|
-
"description": "Response from POST /api/entities",
|
|
113
|
+
"description": "Response from POST /api/entities (v1 POST /api/entity). Not served by v2.",
|
|
92
114
|
"required": ["entity_id", "credential_id", "type"],
|
|
93
115
|
"properties": {
|
|
94
116
|
"entity_id": {
|
|
@@ -122,6 +144,11 @@
|
|
|
122
144
|
"type": "string",
|
|
123
145
|
"description": "Description of the entity type"
|
|
124
146
|
},
|
|
147
|
+
"usedBy": {
|
|
148
|
+
"type": "array",
|
|
149
|
+
"items": { "type": "string" },
|
|
150
|
+
"description": "Names of the integrations that bind this entity type; empty for a type registered only in appDefinition.entities"
|
|
151
|
+
},
|
|
125
152
|
"authType": {
|
|
126
153
|
"type": "string",
|
|
127
154
|
"enum": ["oauth2", "form", "api-key", "basic"],
|
|
@@ -142,12 +169,21 @@
|
|
|
142
169
|
"type": "string"
|
|
143
170
|
},
|
|
144
171
|
"description": "List of capabilities this entity type supports"
|
|
172
|
+
},
|
|
173
|
+
"scope": {
|
|
174
|
+
"type": "object",
|
|
175
|
+
"description": "The scope a new connection of this type gets and the scopes a request may choose (ADR-056 §3.1). global is never offered.",
|
|
176
|
+
"properties": {
|
|
177
|
+
"default": { "type": "string", "enum": ["individual", "organization", "global"] },
|
|
178
|
+
"allow": { "type": "array", "items": { "type": "string", "enum": ["individual", "organization"] } },
|
|
179
|
+
"source": { "type": "string", "enum": ["binding", "integration", "entityType", "app", "default"] }
|
|
180
|
+
}
|
|
145
181
|
}
|
|
146
182
|
}
|
|
147
183
|
},
|
|
148
184
|
"listEntityTypesResponse": {
|
|
149
185
|
"type": "object",
|
|
150
|
-
"description": "Response from GET /api/entities/types",
|
|
186
|
+
"description": "Response from GET /api/v2/entities/types",
|
|
151
187
|
"required": ["types"],
|
|
152
188
|
"properties": {
|
|
153
189
|
"types": {
|
|
@@ -161,7 +197,7 @@
|
|
|
161
197
|
},
|
|
162
198
|
"getEntityTypeResponse": {
|
|
163
199
|
"type": "object",
|
|
164
|
-
"description": "Response from GET /api/entities/types/:
|
|
200
|
+
"description": "Response from GET /api/v2/entities/types/:entityType",
|
|
165
201
|
"required": ["type", "name"],
|
|
166
202
|
"properties": {
|
|
167
203
|
"type": {
|
|
@@ -201,7 +237,7 @@
|
|
|
201
237
|
},
|
|
202
238
|
"reauthorizeEntityRequest": {
|
|
203
239
|
"type": "object",
|
|
204
|
-
"description": "Request to POST /api/
|
|
240
|
+
"description": "Request to re-authorize an entity. Not served in 2.0.1: use POST /api/v2/credentials/:credentialId/reauthorize.",
|
|
205
241
|
"required": ["data"],
|
|
206
242
|
"properties": {
|
|
207
243
|
"data": {
|
|
@@ -223,7 +259,7 @@
|
|
|
223
259
|
},
|
|
224
260
|
"reauthorizeEntityResponse": {
|
|
225
261
|
"type": "object",
|
|
226
|
-
"description": "
|
|
262
|
+
"description": "Entity re-authorization result. Not served in 2.0.1: POST /api/v2/credentials/:credentialId/reauthorize answers api-authorization#/definitions/authorizeV2Response.",
|
|
227
263
|
"oneOf": [
|
|
228
264
|
{
|
|
229
265
|
"$ref": "#/definitions/reauthorizeEntitySuccess"
|
|
@@ -94,6 +94,53 @@
|
|
|
94
94
|
},
|
|
95
95
|
"additionalProperties": false
|
|
96
96
|
},
|
|
97
|
+
"proxy": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"description": "Opt in to the Management API entity proxy (ADR-052). Without allow, nothing can be proxied.",
|
|
100
|
+
"properties": {
|
|
101
|
+
"allow": {
|
|
102
|
+
"type": "array",
|
|
103
|
+
"description": "Operations that may be proxied. :param matches one segment; a trailing /** matches a subtree. Wildcards (method * or path /**) are ignored here and only accepted in the app definition.",
|
|
104
|
+
"items": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"required": ["method", "path"],
|
|
107
|
+
"properties": {
|
|
108
|
+
"method": { "type": "string", "enum": ["GET", "POST", "PUT", "PATCH", "DELETE"] },
|
|
109
|
+
"path": { "type": "string", "pattern": "^/" },
|
|
110
|
+
"base": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
113
|
+
"description": "Which of the module's named bases (baseUrls) the rule applies to. Default 'default'. Must be declared by the module (checked at load)."
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"additionalProperties": false
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"headers": {
|
|
120
|
+
"type": "array",
|
|
121
|
+
"description": "Provider-specific request headers callers may send, in addition to accept, content-type, accept-language, if-match and if-none-match",
|
|
122
|
+
"items": { "type": "string" }
|
|
123
|
+
},
|
|
124
|
+
"baseUrl": {
|
|
125
|
+
"description": "Proxy base URL (https). A string, or a function of the Api instance for tenant hosts. Means { default: baseUrl }. Defaults to the Api's baseUrl. Declare this or baseUrls, not both.",
|
|
126
|
+
"type": ["string", "object"]
|
|
127
|
+
},
|
|
128
|
+
"baseUrls": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"description": "Named proxy base URLs for a module whose API spans several hosts (ADR-052, amendment 1). The proxy request names one in `base`; omitted means 'default'. Strings only, absolute https URLs with no credentials, query or fragment (checked at load). Declare this or baseUrl, not both.",
|
|
131
|
+
"minProperties": 1,
|
|
132
|
+
"propertyNames": { "pattern": "^[a-z][a-z0-9-]*$" },
|
|
133
|
+
"additionalProperties": { "type": "string", "pattern": "^https://" }
|
|
134
|
+
},
|
|
135
|
+
"binary": {
|
|
136
|
+
"type": "boolean",
|
|
137
|
+
"default": false,
|
|
138
|
+
"description": "Return binary upstream bodies base64-encoded instead of refusing them (415)"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"not": { "required": ["baseUrl", "baseUrls"] },
|
|
142
|
+
"additionalProperties": false
|
|
143
|
+
},
|
|
97
144
|
"env": {
|
|
98
145
|
"type": "object",
|
|
99
146
|
"description": "Environment variable configuration",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"definitions": {
|
|
7
7
|
"proxyRequest": {
|
|
8
8
|
"type": "object",
|
|
9
|
-
"description": "Proxy request to POST /api/entities/:
|
|
9
|
+
"description": "Proxy request to POST /api/v2/entities/:entityId/proxy (ADR-052). Off unless the app sets managementApi.proxy.enable, and only for paths the module (or the app definition) allow-lists.",
|
|
10
10
|
"required": ["method", "path"],
|
|
11
11
|
"properties": {
|
|
12
12
|
"method": {
|
|
@@ -16,10 +16,15 @@
|
|
|
16
16
|
},
|
|
17
17
|
"path": {
|
|
18
18
|
"type": "string",
|
|
19
|
-
"description": "
|
|
19
|
+
"description": "Path relative to the module's base URL (e.g. '/v3/contacts'). Must start with a single /. Rejected: //, backslashes, ? and # (use query), . and .. segments, encoded separators (%2f, %5c), double encoding, control and whitespace characters other than %20. The host always comes from the module.",
|
|
20
20
|
"pattern": "^/",
|
|
21
21
|
"minLength": 1
|
|
22
22
|
},
|
|
23
|
+
"base": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
26
|
+
"description": "Name of one of the module's base URLs (ADR-052 amendment); 'default' when omitted. A name the module declares in proxy.baseUrls, never a URL or host. An unknown name is 400 PROXY_UNKNOWN_BASE; a value that is not shaped like a name is 400 INVALID_PROXY_REQUEST."
|
|
27
|
+
},
|
|
23
28
|
"query": {
|
|
24
29
|
"type": "object",
|
|
25
30
|
"description": "Query parameters to include in the request as key-value pairs",
|
|
@@ -37,7 +42,7 @@
|
|
|
37
42
|
},
|
|
38
43
|
"headers": {
|
|
39
44
|
"type": "object",
|
|
40
|
-
"description": "
|
|
45
|
+
"description": "Request headers. Only accept, content-type, accept-language, if-match, if-none-match and headers the module lists in proxy.headers are forwarded; others are dropped. Authorization, Proxy-Authorization, Cookie, Host, Forwarded, X-Forwarded-*, X-Real-IP, X-API-Key and the module's own auth header are refused (400). Auth headers come only from the module.",
|
|
41
46
|
"additionalProperties": { "type": "string" }
|
|
42
47
|
},
|
|
43
48
|
"body": {
|
|
@@ -84,19 +89,23 @@
|
|
|
84
89
|
},
|
|
85
90
|
"proxyResponse": {
|
|
86
91
|
"type": "object",
|
|
87
|
-
"description": "
|
|
92
|
+
"description": "Upstream answer relayed by the proxy. The HTTP status of the Frigg response equals `status`; `success` is false for upstream 4xx.",
|
|
88
93
|
"required": ["success", "status", "data"],
|
|
89
94
|
"properties": {
|
|
90
95
|
"success": {
|
|
91
96
|
"type": "boolean",
|
|
92
|
-
"
|
|
93
|
-
"description": "Indicates successful proxy operation"
|
|
97
|
+
"description": "True when the upstream status is below 400"
|
|
94
98
|
},
|
|
95
99
|
"status": {
|
|
96
100
|
"type": "integer",
|
|
97
101
|
"minimum": 200,
|
|
98
|
-
"maximum":
|
|
99
|
-
"description": "HTTP status code from the upstream API response"
|
|
102
|
+
"maximum": 499,
|
|
103
|
+
"description": "HTTP status code from the upstream API response (5xx, 401 and 429 are reported as proxy errors)"
|
|
104
|
+
},
|
|
105
|
+
"dataEncoding": {
|
|
106
|
+
"type": "string",
|
|
107
|
+
"enum": ["base64"],
|
|
108
|
+
"description": "Present when data is a base64-encoded binary body (only for modules with proxy.binary: true)"
|
|
100
109
|
},
|
|
101
110
|
"headers": {
|
|
102
111
|
"type": "object",
|
|
@@ -171,16 +180,26 @@
|
|
|
171
180
|
"type": "string",
|
|
172
181
|
"description": "Error code identifying the type of failure",
|
|
173
182
|
"enum": [
|
|
174
|
-
"
|
|
175
|
-
"
|
|
183
|
+
"INVALID_PROXY_REQUEST",
|
|
184
|
+
"PROXY_NOT_ALLOWED",
|
|
185
|
+
"PROXY_UNKNOWN_BASE",
|
|
186
|
+
"PROXY_NOT_CONFIGURED",
|
|
187
|
+
"PAYLOAD_TOO_LARGE",
|
|
188
|
+
"UNSUPPORTED_MEDIA_TYPE",
|
|
189
|
+
"RESPONSE_TOO_LARGE",
|
|
190
|
+
"INVALID_CREDENTIALS",
|
|
191
|
+
"RATE_LIMITED",
|
|
176
192
|
"UPSTREAM_ERROR",
|
|
177
193
|
"TIMEOUT",
|
|
178
194
|
"NETWORK_ERROR",
|
|
179
|
-
"
|
|
180
|
-
"INVALID_REQUEST",
|
|
195
|
+
"ENTITY_NOT_FOUND",
|
|
181
196
|
"NOT_FOUND",
|
|
197
|
+
"UNAUTHORIZED",
|
|
198
|
+
"VALIDATION_ERROR",
|
|
199
|
+
"INVALID_AUTH",
|
|
200
|
+
"EXPIRED_TOKEN",
|
|
201
|
+
"INVALID_REQUEST",
|
|
182
202
|
"PERMISSION_DENIED",
|
|
183
|
-
"INVALID_CREDENTIALS",
|
|
184
203
|
"SERVICE_UNAVAILABLE"
|
|
185
204
|
]
|
|
186
205
|
},
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"provider": {
|
|
18
18
|
"type": "string",
|
|
19
|
-
"description": "
|
|
20
|
-
"enum": ["aws"],
|
|
19
|
+
"description": "Deployment provider. Determines which @friggframework/provider-{name} package handles config generation and deploy. Built-in providers: 'aws' (default, serverless/Lambda), 'netlify' (Netlify Functions).",
|
|
20
|
+
"enum": ["aws", "netlify"],
|
|
21
21
|
"default": "aws"
|
|
22
22
|
},
|
|
23
23
|
"label": {
|
|
@@ -75,39 +75,6 @@
|
|
|
75
75
|
},
|
|
76
76
|
"default": []
|
|
77
77
|
},
|
|
78
|
-
"adminScripts": {
|
|
79
|
-
"type": "array",
|
|
80
|
-
"description": "Admin script classes (extending AdminScriptBase from @friggframework/admin-scripts) served under /admin/scripts",
|
|
81
|
-
"items": {
|
|
82
|
-
"description": "Admin script class"
|
|
83
|
-
},
|
|
84
|
-
"default": []
|
|
85
|
-
},
|
|
86
|
-
"reports": {
|
|
87
|
-
"type": "array",
|
|
88
|
-
"description": "Report classes served by the admin report runner",
|
|
89
|
-
"items": {
|
|
90
|
-
"description": "Report class"
|
|
91
|
-
},
|
|
92
|
-
"default": []
|
|
93
|
-
},
|
|
94
|
-
"admin": {
|
|
95
|
-
"type": "object",
|
|
96
|
-
"description": "Admin script and report runner options",
|
|
97
|
-
"properties": {
|
|
98
|
-
"enableScheduling": {
|
|
99
|
-
"type": "boolean",
|
|
100
|
-
"description": "Provision the EventBridge Scheduler group and role so admin scripts and reports can run on a schedule",
|
|
101
|
-
"default": false
|
|
102
|
-
},
|
|
103
|
-
"includeBuiltinReports": {
|
|
104
|
-
"type": "boolean",
|
|
105
|
-
"description": "Serve the built-in reports alongside the app's own reports",
|
|
106
|
-
"default": false
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
"additionalProperties": false
|
|
110
|
-
},
|
|
111
78
|
"user": {
|
|
112
79
|
"type": "object",
|
|
113
80
|
"description": "User management and authentication configuration",
|
|
@@ -138,6 +105,28 @@
|
|
|
138
105
|
"description": "Require organization user for authentication",
|
|
139
106
|
"default": false
|
|
140
107
|
},
|
|
108
|
+
"strictMembership": {
|
|
109
|
+
"type": "boolean",
|
|
110
|
+
"description": "Reject x-frigg headers that name an organization the person is not already a member of, instead of adding the membership (ADR-056 §3.6). Turn on once entities and integrations are scoped by organization and your app manages membership itself.",
|
|
111
|
+
"default": false
|
|
112
|
+
},
|
|
113
|
+
"strictUserValidation": {
|
|
114
|
+
"type": "boolean",
|
|
115
|
+
"description": "DEPRECATED alias of strictMembership; its meaning changed.",
|
|
116
|
+
"default": false
|
|
117
|
+
},
|
|
118
|
+
"orgAdminRoles": {
|
|
119
|
+
"type": "array",
|
|
120
|
+
"description": "Organization roles that may manage organization-level things: connect or delete an organization-level account, re-authorize or delete its credential, create, change or delete an organization-owned integration (ADR-056 §3). The role is the adopter token's role claim; when the token carries none, the role stored on the caller's membership of the active organization (OrganizationMembership.orgRole); when there is none, MEMBER, never admin. A shared-secret (x-frigg header) caller needs no role, and so does a Frigg bearer token unless orgAdminGateBearerTokens is true. Compared case-insensitively.",
|
|
121
|
+
"items": { "type": "string", "minLength": 1 },
|
|
122
|
+
"minItems": 1,
|
|
123
|
+
"default": ["ADMIN"]
|
|
124
|
+
},
|
|
125
|
+
"orgAdminGateBearerTokens": {
|
|
126
|
+
"type": "boolean",
|
|
127
|
+
"description": "Judge Frigg bearer-token callers (/user/login) by organization role like adopter-token callers. Default false: a bearer token carries no role and keeps its behaviour from before organization roles, so it may manage the organization it acts in.",
|
|
128
|
+
"default": false
|
|
129
|
+
},
|
|
141
130
|
"authModes": {
|
|
142
131
|
"type": "object",
|
|
143
132
|
"description": "Authentication mode configuration - enables multiple authentication strategies. x-frigg-appUserId and x-frigg-appOrgId headers are automatically supported for user identification with any auth mode.",
|
|
@@ -162,30 +151,72 @@
|
|
|
162
151
|
},
|
|
163
152
|
"jwtConfig": {
|
|
164
153
|
"type": "object",
|
|
165
|
-
"description": "JWT
|
|
154
|
+
"description": "Adopter token (JWT) verification for authModes.adopterJwt. Set exactly one of jwksUri (asymmetric keys, rotating) or secret (HMAC). With jwksUri, audience is required and issuer should be set.",
|
|
166
155
|
"properties": {
|
|
156
|
+
"jwksUri": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"format": "uri",
|
|
159
|
+
"description": "URL of the issuer's JWKS (https; http only for localhost, 127.0.0.0/8 or ::1). Keys are fetched, cached and rotated per process."
|
|
160
|
+
},
|
|
167
161
|
"secret": {
|
|
168
162
|
"type": "string",
|
|
169
|
-
"description": "
|
|
163
|
+
"description": "Shared HMAC secret for HS* tokens (set via an environment variable, never in source)"
|
|
164
|
+
},
|
|
165
|
+
"issuer": {
|
|
166
|
+
"type": "string",
|
|
167
|
+
"description": "Required iss claim. Not checked when omitted; with jwksUri a startup warning is logged when it is unset."
|
|
168
|
+
},
|
|
169
|
+
"audience": {
|
|
170
|
+
"description": "Accepted aud claim value(s). Required with jwksUri; optional (not checked when omitted) with secret.",
|
|
171
|
+
"oneOf": [
|
|
172
|
+
{ "type": "string" },
|
|
173
|
+
{ "type": "array", "items": { "type": "string" }, "minItems": 1 }
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
"algorithms": {
|
|
177
|
+
"type": "array",
|
|
178
|
+
"description": "Allowed signing algorithms. Defaults to ES256 and RS256 with jwksUri, HS256 with secret. HS* only with secret, never with jwksUri.",
|
|
179
|
+
"items": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"enum": ["ES256", "ES384", "RS256", "RS384", "RS512", "HS256", "HS384", "HS512"]
|
|
182
|
+
},
|
|
183
|
+
"minItems": 1
|
|
184
|
+
},
|
|
185
|
+
"algorithm": {
|
|
186
|
+
"type": "string",
|
|
187
|
+
"description": "Deprecated alias for a one-item algorithms list. Setting both algorithm and algorithms is an error.",
|
|
188
|
+
"enum": ["ES256", "ES384", "RS256", "RS384", "RS512", "HS256", "HS384", "HS512"]
|
|
189
|
+
},
|
|
190
|
+
"clockToleranceSec": {
|
|
191
|
+
"type": "integer",
|
|
192
|
+
"minimum": 0,
|
|
193
|
+
"description": "Clock skew allowed on exp and nbf, in seconds",
|
|
194
|
+
"default": 30
|
|
170
195
|
},
|
|
171
196
|
"userIdClaim": {
|
|
172
197
|
"type": "string",
|
|
173
|
-
"description": "JWT claim name containing the user ID",
|
|
198
|
+
"description": "JWT claim name containing the app user ID",
|
|
174
199
|
"default": "sub"
|
|
175
200
|
},
|
|
176
201
|
"orgIdClaim": {
|
|
177
202
|
"type": "string",
|
|
178
|
-
"description": "JWT claim name containing the organization ID",
|
|
203
|
+
"description": "JWT claim name containing the app organization ID",
|
|
179
204
|
"default": "org_id"
|
|
180
205
|
},
|
|
181
|
-
"
|
|
206
|
+
"roleClaim": {
|
|
182
207
|
"type": "string",
|
|
183
|
-
"description": "JWT
|
|
184
|
-
"
|
|
185
|
-
"default": "HS256"
|
|
208
|
+
"description": "JWT claim name containing the organization role. Read and exposed on the authenticated user; not stored yet.",
|
|
209
|
+
"default": "org_role"
|
|
186
210
|
}
|
|
187
211
|
},
|
|
188
|
-
"
|
|
212
|
+
"oneOf": [
|
|
213
|
+
{ "required": ["secret"] },
|
|
214
|
+
{ "required": ["jwksUri"] }
|
|
215
|
+
],
|
|
216
|
+
"dependencies": {
|
|
217
|
+
"jwksUri": ["audience"]
|
|
218
|
+
},
|
|
219
|
+
"not": { "required": ["algorithm", "algorithms"] },
|
|
189
220
|
"additionalProperties": false
|
|
190
221
|
},
|
|
191
222
|
"fields": {
|
|
@@ -676,6 +707,204 @@
|
|
|
676
707
|
},
|
|
677
708
|
"additionalProperties": false
|
|
678
709
|
},
|
|
710
|
+
"entities": {
|
|
711
|
+
"type": "object",
|
|
712
|
+
"description": "App-level entity type registry (ADR-059). Keyed by entity type name; each key must equal its definition's moduleName / getName(). Registered types can be connected without any integration. Entity types bound by declared integrations are inferred and merged unless $infer is false.",
|
|
713
|
+
"properties": {
|
|
714
|
+
"$infer": {
|
|
715
|
+
"type": "boolean",
|
|
716
|
+
"default": true,
|
|
717
|
+
"description": "false turns inference off: every module an integration binds must then be registered here, or the app fails to load."
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
"additionalProperties": {
|
|
721
|
+
"type": "object",
|
|
722
|
+
"description": "One entity type: its module definition plus app-level settings.",
|
|
723
|
+
"required": ["definition"],
|
|
724
|
+
"properties": {
|
|
725
|
+
"definition": {
|
|
726
|
+
"type": "object",
|
|
727
|
+
"description": "The API module definition (the object an integration binds as modules.<key>.definition)."
|
|
728
|
+
},
|
|
729
|
+
"scope": {
|
|
730
|
+
"description": "Who owns entities of this type (ADR-056): one scope, or a default plus the scopes callers may choose.",
|
|
731
|
+
"oneOf": [
|
|
732
|
+
{ "type": "string", "enum": ["individual", "organization", "global"] },
|
|
733
|
+
{
|
|
734
|
+
"type": "object",
|
|
735
|
+
"properties": {
|
|
736
|
+
"default": { "type": "string", "enum": ["individual", "organization", "global"] },
|
|
737
|
+
"allow": {
|
|
738
|
+
"type": "array",
|
|
739
|
+
"items": { "type": "string", "enum": ["individual", "organization", "global"] },
|
|
740
|
+
"minItems": 1,
|
|
741
|
+
"uniqueItems": true
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
"additionalProperties": false
|
|
745
|
+
}
|
|
746
|
+
]
|
|
747
|
+
},
|
|
748
|
+
"proxy": {
|
|
749
|
+
"type": "object",
|
|
750
|
+
"description": "Per-type entity proxy allow-list override (ADR-052)."
|
|
751
|
+
},
|
|
752
|
+
"display": {
|
|
753
|
+
"type": "object",
|
|
754
|
+
"description": "Label, icon and description shown by UIs and GET /api/v2/entity-types."
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
"additionalProperties": false
|
|
758
|
+
}
|
|
759
|
+
},
|
|
760
|
+
"ownership": {
|
|
761
|
+
"type": "object",
|
|
762
|
+
"description": "Who owns what a person connects (ADR-056 §3). `scope` is the app-level default for any entity type or integration binding that sets none; the order is request, binding, integration, entity type, app, then `organization` for an organization-primary app and `individual` otherwise.",
|
|
763
|
+
"properties": {
|
|
764
|
+
"scope": {
|
|
765
|
+
"description": "One scope, or a default plus the scopes a request may choose from.",
|
|
766
|
+
"oneOf": [
|
|
767
|
+
{ "type": "string", "enum": ["individual", "organization", "global"] },
|
|
768
|
+
{
|
|
769
|
+
"type": "object",
|
|
770
|
+
"properties": {
|
|
771
|
+
"default": { "type": "string", "enum": ["individual", "organization", "global"] },
|
|
772
|
+
"allow": {
|
|
773
|
+
"type": "array",
|
|
774
|
+
"items": { "type": "string", "enum": ["individual", "organization", "global"] },
|
|
775
|
+
"minItems": 1,
|
|
776
|
+
"uniqueItems": true
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
"additionalProperties": false
|
|
780
|
+
}
|
|
781
|
+
]
|
|
782
|
+
},
|
|
783
|
+
"individualAcross": {
|
|
784
|
+
"type": "string",
|
|
785
|
+
"enum": ["organization", "all"],
|
|
786
|
+
"default": "organization",
|
|
787
|
+
"description": "`organization` (default): an individual entity belongs to the organization it was connected in and is not visible in another. `all`: one connection per person across every organization they belong to, stored with no organizationId."
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
"additionalProperties": false
|
|
791
|
+
},
|
|
792
|
+
"managementApi": {
|
|
793
|
+
"type": "object",
|
|
794
|
+
"description": "Management API settings (ADR-053 versioning, ADR-052 entity proxy).",
|
|
795
|
+
"properties": {
|
|
796
|
+
"v1": {
|
|
797
|
+
"type": "boolean",
|
|
798
|
+
"default": true,
|
|
799
|
+
"description": "Serve the deprecated v1 routes (unprefixed /api/*). false removes them from the router and from the generated API Gateway routes."
|
|
800
|
+
},
|
|
801
|
+
"proxy": {
|
|
802
|
+
"type": "object",
|
|
803
|
+
"description": "Entity proxy, POST /api/v2/entities/:id/proxy (ADR-052). Off by default.",
|
|
804
|
+
"properties": {
|
|
805
|
+
"enable": {
|
|
806
|
+
"type": "boolean",
|
|
807
|
+
"default": false,
|
|
808
|
+
"description": "Mount the entity proxy route and advertise the entityProxy capability."
|
|
809
|
+
},
|
|
810
|
+
"timeoutMs": {
|
|
811
|
+
"type": "integer",
|
|
812
|
+
"minimum": 1,
|
|
813
|
+
"maximum": 25000,
|
|
814
|
+
"default": 25000,
|
|
815
|
+
"description": "Upstream timeout for one proxied call, in milliseconds (can only be lowered)."
|
|
816
|
+
},
|
|
817
|
+
"maxRequestBytes": {
|
|
818
|
+
"type": "integer",
|
|
819
|
+
"minimum": 1,
|
|
820
|
+
"maximum": 10485760,
|
|
821
|
+
"default": 1048576,
|
|
822
|
+
"description": "Largest proxied request body, in bytes."
|
|
823
|
+
},
|
|
824
|
+
"maxResponseBytes": {
|
|
825
|
+
"type": "integer",
|
|
826
|
+
"minimum": 1,
|
|
827
|
+
"maximum": 5242880,
|
|
828
|
+
"default": 5242880,
|
|
829
|
+
"description": "Largest proxied response body, in bytes (can only be lowered)."
|
|
830
|
+
},
|
|
831
|
+
"scopes": {
|
|
832
|
+
"type": "array",
|
|
833
|
+
"minItems": 1,
|
|
834
|
+
"uniqueItems": true,
|
|
835
|
+
"items": {
|
|
836
|
+
"type": "string",
|
|
837
|
+
"enum": [
|
|
838
|
+
"individual",
|
|
839
|
+
"organization"
|
|
840
|
+
]
|
|
841
|
+
},
|
|
842
|
+
"default": [
|
|
843
|
+
"individual"
|
|
844
|
+
],
|
|
845
|
+
"description": "Entity scopes the proxy accepts (ADR-056 §3.5). Narrowest by default: proxying through an organization entity lets any member with use access act on the organization's account, so an app opts in to 'organization' explicitly. 'global' is never accepted."
|
|
846
|
+
},
|
|
847
|
+
"modules": {
|
|
848
|
+
"type": "object",
|
|
849
|
+
"description": "Per-module allow-list override, keyed by module name. 'module' uses the module's own proxy.allow list.",
|
|
850
|
+
"additionalProperties": {
|
|
851
|
+
"type": "object",
|
|
852
|
+
"properties": {
|
|
853
|
+
"allow": {
|
|
854
|
+
"oneOf": [
|
|
855
|
+
{
|
|
856
|
+
"type": "string",
|
|
857
|
+
"enum": [
|
|
858
|
+
"module"
|
|
859
|
+
]
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
"type": "array",
|
|
863
|
+
"items": {
|
|
864
|
+
"type": "object",
|
|
865
|
+
"required": [
|
|
866
|
+
"method",
|
|
867
|
+
"path"
|
|
868
|
+
],
|
|
869
|
+
"properties": {
|
|
870
|
+
"method": {
|
|
871
|
+
"type": "string",
|
|
872
|
+
"enum": [
|
|
873
|
+
"GET",
|
|
874
|
+
"POST",
|
|
875
|
+
"PUT",
|
|
876
|
+
"PATCH",
|
|
877
|
+
"DELETE",
|
|
878
|
+
"*"
|
|
879
|
+
],
|
|
880
|
+
"description": "HTTP method, or * for any (app definition only; frigg validate warns)"
|
|
881
|
+
},
|
|
882
|
+
"path": {
|
|
883
|
+
"type": "string",
|
|
884
|
+
"pattern": "^/",
|
|
885
|
+
"description": "Path pattern relative to the module's base URL. `:param` matches one segment; a trailing `/**` matches a subtree."
|
|
886
|
+
},
|
|
887
|
+
"base": {
|
|
888
|
+
"type": "string",
|
|
889
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
890
|
+
"description": "Which of the module's named bases the rule applies to. Default 'default'. A base the module does not declare stops the app at load."
|
|
891
|
+
}
|
|
892
|
+
},
|
|
893
|
+
"additionalProperties": false
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
]
|
|
897
|
+
}
|
|
898
|
+
},
|
|
899
|
+
"additionalProperties": false
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
},
|
|
903
|
+
"additionalProperties": false
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
"additionalProperties": false
|
|
907
|
+
},
|
|
679
908
|
"custom": {
|
|
680
909
|
"type": "object",
|
|
681
910
|
"description": "Custom application-specific configuration",
|
|
@@ -709,6 +938,61 @@
|
|
|
709
938
|
}
|
|
710
939
|
},
|
|
711
940
|
"additionalProperties": true
|
|
941
|
+
},
|
|
942
|
+
"extensions": {
|
|
943
|
+
"type": "array",
|
|
944
|
+
"description": "Extensions that add custom Prisma models, encrypted fields, admin routes, and bootstrap lifecycle hooks to a Frigg app. Each extension can declare a schema fragment, encryption config, route handler, and bootstrap function. App-level; distinct from an integration's Definition.extensions (Tier 3 integration extensions).",
|
|
945
|
+
"items": {
|
|
946
|
+
"type": "object",
|
|
947
|
+
"required": ["name"],
|
|
948
|
+
"properties": {
|
|
949
|
+
"name": {
|
|
950
|
+
"type": "string",
|
|
951
|
+
"description": "Unique identifier for this extension",
|
|
952
|
+
"minLength": 1,
|
|
953
|
+
"examples": ["db-credentials", "audit-log"]
|
|
954
|
+
},
|
|
955
|
+
"schema": {
|
|
956
|
+
"type": "string",
|
|
957
|
+
"description": "Absolute path to a .prisma schema fragment. Models/enums from this file are composed into the main Prisma schema at build time.",
|
|
958
|
+
"pattern": "\\.prisma$"
|
|
959
|
+
},
|
|
960
|
+
"encryption": {
|
|
961
|
+
"type": "object",
|
|
962
|
+
"description": "Encryption schema additions. Maps model names to arrays of field paths that should be encrypted at rest.",
|
|
963
|
+
"patternProperties": {
|
|
964
|
+
"^[A-Z][a-zA-Z0-9]*$": {
|
|
965
|
+
"type": "object",
|
|
966
|
+
"required": ["fields"],
|
|
967
|
+
"properties": {
|
|
968
|
+
"fields": {
|
|
969
|
+
"type": "array",
|
|
970
|
+
"items": { "type": "string" },
|
|
971
|
+
"minItems": 1
|
|
972
|
+
}
|
|
973
|
+
},
|
|
974
|
+
"additionalProperties": false
|
|
975
|
+
}
|
|
976
|
+
},
|
|
977
|
+
"additionalProperties": false
|
|
978
|
+
},
|
|
979
|
+
"routes": {
|
|
980
|
+
"type": "object",
|
|
981
|
+
"description": "Express router to mount on the app. The handler factory receives (prisma, appDefinition) and returns an Express Router. Routes are protected by admin auth middleware.",
|
|
982
|
+
"required": ["path"],
|
|
983
|
+
"properties": {
|
|
984
|
+
"path": {
|
|
985
|
+
"type": "string",
|
|
986
|
+
"description": "Base URL path for the extension routes",
|
|
987
|
+
"pattern": "^/",
|
|
988
|
+
"examples": ["/api/admin/oauth-credentials"]
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
"additionalProperties": true
|
|
992
|
+
}
|
|
993
|
+
},
|
|
994
|
+
"additionalProperties": true
|
|
995
|
+
}
|
|
712
996
|
}
|
|
713
997
|
},
|
|
714
998
|
"additionalProperties": false,
|
|
@@ -263,11 +263,62 @@
|
|
|
263
263
|
"definition": {
|
|
264
264
|
"type": "object",
|
|
265
265
|
"description": "The API module definition (e.g., from @friggframework/api-module-*)"
|
|
266
|
+
},
|
|
267
|
+
"scope": {
|
|
268
|
+
"description": "The scope this binding accepts (ADR-056 §3.1): an entity bound here must have it. Overrides ownership.scope.",
|
|
269
|
+
"oneOf": [
|
|
270
|
+
{ "type": "string", "enum": ["individual", "organization", "global"] },
|
|
271
|
+
{
|
|
272
|
+
"type": "object",
|
|
273
|
+
"properties": {
|
|
274
|
+
"default": { "type": "string", "enum": ["individual", "organization", "global"] },
|
|
275
|
+
"allow": {
|
|
276
|
+
"type": "array",
|
|
277
|
+
"items": { "type": "string", "enum": ["individual", "organization", "global"] },
|
|
278
|
+
"minItems": 1,
|
|
279
|
+
"uniqueItems": true
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
"additionalProperties": false
|
|
283
|
+
}
|
|
284
|
+
]
|
|
266
285
|
}
|
|
267
286
|
},
|
|
268
287
|
"additionalProperties": true
|
|
269
288
|
}
|
|
270
289
|
},
|
|
290
|
+
"ownership": {
|
|
291
|
+
"type": "object",
|
|
292
|
+
"description": "Who owns this integration and what scope its bindings accept (ADR-056 §3.1, §3.3).",
|
|
293
|
+
"properties": {
|
|
294
|
+
"scope": {
|
|
295
|
+
"description": "One scope, or a default plus the scopes a request may choose from.",
|
|
296
|
+
"oneOf": [
|
|
297
|
+
{ "type": "string", "enum": ["individual", "organization", "global"] },
|
|
298
|
+
{
|
|
299
|
+
"type": "object",
|
|
300
|
+
"properties": {
|
|
301
|
+
"default": { "type": "string", "enum": ["individual", "organization", "global"] },
|
|
302
|
+
"allow": {
|
|
303
|
+
"type": "array",
|
|
304
|
+
"items": { "type": "string", "enum": ["individual", "organization", "global"] },
|
|
305
|
+
"minItems": 1,
|
|
306
|
+
"uniqueItems": true
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
"additionalProperties": false
|
|
310
|
+
}
|
|
311
|
+
]
|
|
312
|
+
},
|
|
313
|
+
"owner": {
|
|
314
|
+
"type": "string",
|
|
315
|
+
"enum": ["derive", "individual", "organization"],
|
|
316
|
+
"default": "derive",
|
|
317
|
+
"description": "`derive` (default): the organization when every entity it uses is organization-scoped, otherwise the person. An organization-owned integration cannot bind an individual entity."
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
"additionalProperties": false
|
|
321
|
+
},
|
|
271
322
|
"hasUserConfig": {
|
|
272
323
|
"type": "boolean",
|
|
273
324
|
"description": "Whether integration requires user-specific configuration",
|