@friggframework/schemas 2.0.0--canary.679.a2bcc2f.0 → 2.0.0--canary.686.87e9875.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@friggframework/schemas",
3
- "version": "2.0.0--canary.679.a2bcc2f.0",
3
+ "version": "2.0.0--canary.686.87e9875.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": "a2bcc2f124b0574c0d868beff77b4da848fd7849"
44
+ "gitHead": "87e9875d4dba36bf3bb90400e862d5fb9db17ae1"
45
45
  }
@@ -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)"
@@ -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",
@@ -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",
@@ -147,6 +169,15 @@
147
169
  "type": "string"
148
170
  },
149
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
+ }
150
181
  }
151
182
  }
152
183
  },
@@ -16,8 +16,8 @@
16
16
  },
17
17
  "provider": {
18
18
  "type": "string",
19
- "description": "Cloud provider for deployment",
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": {
@@ -105,6 +105,28 @@
105
105
  "description": "Require organization user for authentication",
106
106
  "default": false
107
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
+ },
108
130
  "authModes": {
109
131
  "type": "object",
110
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.",
@@ -129,30 +151,72 @@
129
151
  },
130
152
  "jwtConfig": {
131
153
  "type": "object",
132
- "description": "JWT configuration for adopter JWT authentication mode (when authModes.adopterJwt is true)",
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.",
133
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
+ },
134
161
  "secret": {
135
162
  "type": "string",
136
- "description": "JWT secret key for signature validation (should be set via environment variable)"
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
137
195
  },
138
196
  "userIdClaim": {
139
197
  "type": "string",
140
- "description": "JWT claim name containing the user ID",
198
+ "description": "JWT claim name containing the app user ID",
141
199
  "default": "sub"
142
200
  },
143
201
  "orgIdClaim": {
144
202
  "type": "string",
145
- "description": "JWT claim name containing the organization ID",
203
+ "description": "JWT claim name containing the app organization ID",
146
204
  "default": "org_id"
147
205
  },
148
- "algorithm": {
206
+ "roleClaim": {
149
207
  "type": "string",
150
- "description": "JWT signing algorithm",
151
- "enum": ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512"],
152
- "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"
153
210
  }
154
211
  },
155
- "required": ["secret"],
212
+ "oneOf": [
213
+ { "required": ["secret"] },
214
+ { "required": ["jwksUri"] }
215
+ ],
216
+ "dependencies": {
217
+ "jwksUri": ["audience"]
218
+ },
219
+ "not": { "required": ["algorithm", "algorithms"] },
156
220
  "additionalProperties": false
157
221
  },
158
222
  "fields": {
@@ -693,6 +757,38 @@
693
757
  "additionalProperties": false
694
758
  }
695
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
+ },
696
792
  "managementApi": {
697
793
  "type": "object",
698
794
  "description": "Management API settings (ADR-053 versioning, ADR-052 entity proxy).",
@@ -732,6 +828,22 @@
732
828
  "default": 5242880,
733
829
  "description": "Largest proxied response body, in bytes (can only be lowered)."
734
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
+ },
735
847
  "modules": {
736
848
  "type": "object",
737
849
  "description": "Per-module allow-list override, keyed by module name. 'module' uses the module's own proxy.allow list.",
@@ -821,6 +933,61 @@
821
933
  }
822
934
  },
823
935
  "additionalProperties": true
936
+ },
937
+ "extensions": {
938
+ "type": "array",
939
+ "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).",
940
+ "items": {
941
+ "type": "object",
942
+ "required": ["name"],
943
+ "properties": {
944
+ "name": {
945
+ "type": "string",
946
+ "description": "Unique identifier for this extension",
947
+ "minLength": 1,
948
+ "examples": ["db-credentials", "audit-log"]
949
+ },
950
+ "schema": {
951
+ "type": "string",
952
+ "description": "Absolute path to a .prisma schema fragment. Models/enums from this file are composed into the main Prisma schema at build time.",
953
+ "pattern": "\\.prisma$"
954
+ },
955
+ "encryption": {
956
+ "type": "object",
957
+ "description": "Encryption schema additions. Maps model names to arrays of field paths that should be encrypted at rest.",
958
+ "patternProperties": {
959
+ "^[A-Z][a-zA-Z0-9]*$": {
960
+ "type": "object",
961
+ "required": ["fields"],
962
+ "properties": {
963
+ "fields": {
964
+ "type": "array",
965
+ "items": { "type": "string" },
966
+ "minItems": 1
967
+ }
968
+ },
969
+ "additionalProperties": false
970
+ }
971
+ },
972
+ "additionalProperties": false
973
+ },
974
+ "routes": {
975
+ "type": "object",
976
+ "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.",
977
+ "required": ["path"],
978
+ "properties": {
979
+ "path": {
980
+ "type": "string",
981
+ "description": "Base URL path for the extension routes",
982
+ "pattern": "^/",
983
+ "examples": ["/api/admin/oauth-credentials"]
984
+ }
985
+ },
986
+ "additionalProperties": true
987
+ }
988
+ },
989
+ "additionalProperties": true
990
+ }
824
991
  }
825
992
  },
826
993
  "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",