@stigmer/server 3.27.1 → 3.28.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.
Files changed (245) hide show
  1. package/README.md +2 -0
  2. package/dist/authorization/authorizer.d.ts +7 -5
  3. package/dist/authorization/authorizer.d.ts.map +1 -1
  4. package/dist/authorization/authorizer.js +7 -5
  5. package/dist/authorization/authorizer.js.map +1 -1
  6. package/dist/authorization/derived-tuples.d.ts +3 -1
  7. package/dist/authorization/derived-tuples.d.ts.map +1 -1
  8. package/dist/authorization/derived-tuples.js +52 -2
  9. package/dist/authorization/derived-tuples.js.map +1 -1
  10. package/dist/authorization/evaluator.d.ts +9 -10
  11. package/dist/authorization/evaluator.d.ts.map +1 -1
  12. package/dist/authorization/evaluator.js.map +1 -1
  13. package/dist/authorization/list-read-scope.d.ts.map +1 -1
  14. package/dist/authorization/list-read-scope.js +29 -18
  15. package/dist/authorization/list-read-scope.js.map +1 -1
  16. package/dist/authorization/model/bindings.d.ts +41 -0
  17. package/dist/authorization/model/bindings.d.ts.map +1 -0
  18. package/dist/authorization/model/bindings.js +89 -0
  19. package/dist/authorization/model/bindings.js.map +1 -0
  20. package/dist/authorization/model/data/authorization-model.json +3114 -0
  21. package/dist/authorization/model/default-of.d.ts +2 -2
  22. package/dist/authorization/model/execution-viewer.d.ts +3 -0
  23. package/dist/authorization/model/execution-viewer.d.ts.map +1 -0
  24. package/dist/authorization/model/execution-viewer.js +50 -0
  25. package/dist/authorization/model/execution-viewer.js.map +1 -0
  26. package/dist/authorization/model/index.d.ts +33 -21
  27. package/dist/authorization/model/index.d.ts.map +1 -1
  28. package/dist/authorization/model/index.js +74 -52
  29. package/dist/authorization/model/index.js.map +1 -1
  30. package/dist/authorization/model/openfga-json.d.ts +18 -0
  31. package/dist/authorization/model/openfga-json.d.ts.map +1 -0
  32. package/dist/authorization/model/openfga-json.js +266 -0
  33. package/dist/authorization/model/openfga-json.js.map +1 -0
  34. package/dist/authorization/model/rewrite.d.ts +39 -67
  35. package/dist/authorization/model/rewrite.d.ts.map +1 -1
  36. package/dist/authorization/model/rewrite.js +1 -117
  37. package/dist/authorization/model/rewrite.js.map +1 -1
  38. package/dist/authorization/strict-json.d.ts +20 -0
  39. package/dist/authorization/strict-json.d.ts.map +1 -0
  40. package/dist/authorization/strict-json.js +45 -0
  41. package/dist/authorization/strict-json.js.map +1 -0
  42. package/dist/boot/compose.d.ts.map +1 -1
  43. package/dist/boot/compose.js +9 -7
  44. package/dist/boot/compose.js.map +1 -1
  45. package/dist/boot/list-indexes.d.ts.map +1 -1
  46. package/dist/boot/list-indexes.js +6 -0
  47. package/dist/boot/list-indexes.js.map +1 -1
  48. package/dist/domain/iampolicy/list-index.d.ts +2 -0
  49. package/dist/domain/iampolicy/list-index.d.ts.map +1 -0
  50. package/dist/domain/iampolicy/list-index.js +32 -0
  51. package/dist/domain/iampolicy/list-index.js.map +1 -0
  52. package/dist/domain/iampolicy/resource-store.d.ts.map +1 -1
  53. package/dist/domain/iampolicy/resource-store.js +24 -11
  54. package/dist/domain/iampolicy/resource-store.js.map +1 -1
  55. package/dist/domain/workflowexecution/list-index.d.ts +1 -1
  56. package/dist/extensions/list-read-scope.d.ts +20 -3
  57. package/dist/extensions/list-read-scope.d.ts.map +1 -1
  58. package/dist/extensions/list-read-scope.js +22 -2
  59. package/dist/extensions/list-read-scope.js.map +1 -1
  60. package/dist/index.d.ts +8 -8
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +20 -5
  63. package/dist/index.js.map +1 -1
  64. package/package.json +7 -6
  65. package/src/authorization/README.md +48 -39
  66. package/src/authorization/__tests__/authorizer.test.ts +5 -14
  67. package/src/authorization/__tests__/derived-tuples.test.ts +192 -17
  68. package/src/authorization/__tests__/evaluator.test.ts +53 -160
  69. package/src/authorization/__tests__/facts.test.ts +5 -11
  70. package/src/authorization/__tests__/list-read-scope.measure.test.ts +424 -62
  71. package/src/authorization/__tests__/list-read-scope.test.ts +15 -14
  72. package/src/authorization/__tests__/organization-directory.test.ts +2 -2
  73. package/src/authorization/__tests__/store-test-kit.ts +246 -0
  74. package/src/authorization/__tests__/store-tests.test.ts +91 -223
  75. package/src/authorization/__tests__/support.ts +30 -2
  76. package/src/authorization/__tests__/throwaway-model.ts +75 -0
  77. package/src/authorization/__tests__/wire-permissions.test.ts +141 -107
  78. package/src/authorization/authorizer.ts +7 -5
  79. package/src/authorization/derived-tuples.ts +105 -16
  80. package/src/authorization/evaluator.ts +8 -10
  81. package/src/authorization/list-read-scope.ts +37 -22
  82. package/src/authorization/model/__tests__/openfga-json.test.ts +353 -0
  83. package/src/authorization/model/__tests__/registry.test.ts +102 -276
  84. package/src/authorization/model/bindings.ts +131 -0
  85. package/src/authorization/model/data/authorization-model.json +3114 -0
  86. package/src/authorization/model/default-of.ts +2 -2
  87. package/src/authorization/model/execution-viewer.ts +56 -0
  88. package/src/authorization/model/index.ts +81 -72
  89. package/src/authorization/model/openfga-json.ts +310 -0
  90. package/src/authorization/model/rewrite.ts +39 -188
  91. package/src/authorization/strict-json.ts +52 -0
  92. package/src/boot/__tests__/list-indexes.test.ts +4 -0
  93. package/src/boot/compose.ts +9 -7
  94. package/src/boot/list-indexes.ts +6 -0
  95. package/src/domain/iampolicy/__tests__/resource-store.test.ts +65 -5
  96. package/src/domain/iampolicy/list-index.ts +33 -0
  97. package/src/domain/iampolicy/resource-store.ts +24 -11
  98. package/src/extensions/__tests__/built-in-authorization-composed.test.ts +3 -6
  99. package/src/extensions/list-read-scope.ts +32 -4
  100. package/src/index.ts +20 -44
  101. package/dist/authorization/model/agent.d.ts +0 -2
  102. package/dist/authorization/model/agent.d.ts.map +0 -1
  103. package/dist/authorization/model/agent.js +0 -43
  104. package/dist/authorization/model/agent.js.map +0 -1
  105. package/dist/authorization/model/agent_channel.d.ts +0 -2
  106. package/dist/authorization/model/agent_channel.d.ts.map +0 -1
  107. package/dist/authorization/model/agent_channel.js +0 -39
  108. package/dist/authorization/model/agent_channel.js.map +0 -1
  109. package/dist/authorization/model/agent_execution.d.ts +0 -2
  110. package/dist/authorization/model/agent_execution.d.ts.map +0 -1
  111. package/dist/authorization/model/agent_execution.js +0 -25
  112. package/dist/authorization/model/agent_execution.js.map +0 -1
  113. package/dist/authorization/model/agent_instance.d.ts +0 -2
  114. package/dist/authorization/model/agent_instance.d.ts.map +0 -1
  115. package/dist/authorization/model/agent_instance.js +0 -48
  116. package/dist/authorization/model/agent_instance.js.map +0 -1
  117. package/dist/authorization/model/agent_share.d.ts +0 -2
  118. package/dist/authorization/model/agent_share.d.ts.map +0 -1
  119. package/dist/authorization/model/agent_share.js +0 -29
  120. package/dist/authorization/model/agent_share.js.map +0 -1
  121. package/dist/authorization/model/api_key.d.ts +0 -2
  122. package/dist/authorization/model/api_key.d.ts.map +0 -1
  123. package/dist/authorization/model/api_key.js +0 -27
  124. package/dist/authorization/model/api_key.js.map +0 -1
  125. package/dist/authorization/model/artifact.d.ts +0 -2
  126. package/dist/authorization/model/artifact.d.ts.map +0 -1
  127. package/dist/authorization/model/artifact.js +0 -27
  128. package/dist/authorization/model/artifact.js.map +0 -1
  129. package/dist/authorization/model/channel_app.d.ts +0 -2
  130. package/dist/authorization/model/channel_app.d.ts.map +0 -1
  131. package/dist/authorization/model/channel_app.js +0 -28
  132. package/dist/authorization/model/channel_app.js.map +0 -1
  133. package/dist/authorization/model/environment.d.ts +0 -2
  134. package/dist/authorization/model/environment.d.ts.map +0 -1
  135. package/dist/authorization/model/environment.js +0 -33
  136. package/dist/authorization/model/environment.js.map +0 -1
  137. package/dist/authorization/model/execution_context.d.ts +0 -2
  138. package/dist/authorization/model/execution_context.d.ts.map +0 -1
  139. package/dist/authorization/model/execution_context.js +0 -19
  140. package/dist/authorization/model/execution_context.js.map +0 -1
  141. package/dist/authorization/model/iam_policy.d.ts +0 -2
  142. package/dist/authorization/model/iam_policy.d.ts.map +0 -1
  143. package/dist/authorization/model/iam_policy.js +0 -31
  144. package/dist/authorization/model/iam_policy.js.map +0 -1
  145. package/dist/authorization/model/identity_account.d.ts +0 -2
  146. package/dist/authorization/model/identity_account.d.ts.map +0 -1
  147. package/dist/authorization/model/identity_account.js +0 -25
  148. package/dist/authorization/model/identity_account.js.map +0 -1
  149. package/dist/authorization/model/mcp_server.d.ts +0 -2
  150. package/dist/authorization/model/mcp_server.d.ts.map +0 -1
  151. package/dist/authorization/model/mcp_server.js +0 -38
  152. package/dist/authorization/model/mcp_server.js.map +0 -1
  153. package/dist/authorization/model/memory.d.ts +0 -2
  154. package/dist/authorization/model/memory.d.ts.map +0 -1
  155. package/dist/authorization/model/memory.js +0 -26
  156. package/dist/authorization/model/memory.js.map +0 -1
  157. package/dist/authorization/model/oauth_app.d.ts +0 -2
  158. package/dist/authorization/model/oauth_app.d.ts.map +0 -1
  159. package/dist/authorization/model/oauth_app.js +0 -31
  160. package/dist/authorization/model/oauth_app.js.map +0 -1
  161. package/dist/authorization/model/organization.d.ts +0 -2
  162. package/dist/authorization/model/organization.d.ts.map +0 -1
  163. package/dist/authorization/model/organization.js +0 -58
  164. package/dist/authorization/model/organization.js.map +0 -1
  165. package/dist/authorization/model/platform_client.d.ts +0 -2
  166. package/dist/authorization/model/platform_client.d.ts.map +0 -1
  167. package/dist/authorization/model/platform_client.js +0 -31
  168. package/dist/authorization/model/platform_client.js.map +0 -1
  169. package/dist/authorization/model/plugin.d.ts +0 -2
  170. package/dist/authorization/model/plugin.d.ts.map +0 -1
  171. package/dist/authorization/model/plugin.js +0 -45
  172. package/dist/authorization/model/plugin.js.map +0 -1
  173. package/dist/authorization/model/schedule.d.ts +0 -2
  174. package/dist/authorization/model/schedule.d.ts.map +0 -1
  175. package/dist/authorization/model/schedule.js +0 -34
  176. package/dist/authorization/model/schedule.js.map +0 -1
  177. package/dist/authorization/model/session.d.ts +0 -2
  178. package/dist/authorization/model/session.d.ts.map +0 -1
  179. package/dist/authorization/model/session.js +0 -38
  180. package/dist/authorization/model/session.js.map +0 -1
  181. package/dist/authorization/model/skill.d.ts +0 -2
  182. package/dist/authorization/model/skill.d.ts.map +0 -1
  183. package/dist/authorization/model/skill.js +0 -39
  184. package/dist/authorization/model/skill.js.map +0 -1
  185. package/dist/authorization/model/workflow.d.ts +0 -2
  186. package/dist/authorization/model/workflow.d.ts.map +0 -1
  187. package/dist/authorization/model/workflow.js +0 -39
  188. package/dist/authorization/model/workflow.js.map +0 -1
  189. package/dist/authorization/model/workflow_execution.d.ts +0 -2
  190. package/dist/authorization/model/workflow_execution.d.ts.map +0 -1
  191. package/dist/authorization/model/workflow_execution.js +0 -33
  192. package/dist/authorization/model/workflow_execution.js.map +0 -1
  193. package/dist/authorization/model/workflow_instance.d.ts +0 -2
  194. package/dist/authorization/model/workflow_instance.d.ts.map +0 -1
  195. package/dist/authorization/model/workflow_instance.js +0 -96
  196. package/dist/authorization/model/workflow_instance.js.map +0 -1
  197. package/dist/authorization/store-test-kit.d.ts +0 -62
  198. package/dist/authorization/store-test-kit.d.ts.map +0 -1
  199. package/dist/authorization/store-test-kit.js +0 -265
  200. package/dist/authorization/store-test-kit.js.map +0 -1
  201. package/src/authorization/__tests__/fixtures/fga/agent-channel-owner.fga.yaml +0 -112
  202. package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +0 -177
  203. package/src/authorization/__tests__/fixtures/fga/agent-share-owner.fga.yaml +0 -109
  204. package/src/authorization/__tests__/fixtures/fga/artifact-org-and-owner.fga.yaml +0 -128
  205. package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +0 -318
  206. package/src/authorization/__tests__/fixtures/fga/channel-app-owner.fga.yaml +0 -125
  207. package/src/authorization/__tests__/fixtures/fga/channel-participation.fga.yaml +0 -201
  208. package/src/authorization/__tests__/fixtures/fga/channel-session-visibility.fga.yaml +0 -187
  209. package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +0 -214
  210. package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +0 -129
  211. package/src/authorization/__tests__/fixtures/fga/memory-subject-only.fga.yaml +0 -72
  212. package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +0 -308
  213. package/src/authorization/__tests__/fixtures/fga/org-shared-environment.fga.yaml +0 -144
  214. package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +0 -199
  215. package/src/authorization/__tests__/fixtures/fga/provider-standing-viewer.fga.yaml +0 -40
  216. package/src/authorization/__tests__/fixtures/fga/reserved-label-writer.fga.yaml +0 -41
  217. package/src/authorization/__tests__/fixtures/fga/schedule-owner.fga.yaml +0 -113
  218. package/src/authorization/__tests__/fixtures/fga/schedule-session-visibility.fga.yaml +0 -214
  219. package/src/authorization/__tests__/fixtures/fga/session-personal-resource.fga.yaml +0 -117
  220. package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +0 -190
  221. package/src/authorization/model/agent.ts +0 -64
  222. package/src/authorization/model/agent_channel.ts +0 -57
  223. package/src/authorization/model/agent_execution.ts +0 -33
  224. package/src/authorization/model/agent_instance.ts +0 -66
  225. package/src/authorization/model/agent_share.ts +0 -40
  226. package/src/authorization/model/api_key.ts +0 -28
  227. package/src/authorization/model/artifact.ts +0 -38
  228. package/src/authorization/model/channel_app.ts +0 -39
  229. package/src/authorization/model/environment.ts +0 -48
  230. package/src/authorization/model/execution_context.ts +0 -20
  231. package/src/authorization/model/iam_policy.ts +0 -42
  232. package/src/authorization/model/identity_account.ts +0 -26
  233. package/src/authorization/model/mcp_server.ts +0 -59
  234. package/src/authorization/model/memory.ts +0 -27
  235. package/src/authorization/model/oauth_app.ts +0 -43
  236. package/src/authorization/model/organization.ts +0 -59
  237. package/src/authorization/model/platform_client.ts +0 -43
  238. package/src/authorization/model/plugin.ts +0 -66
  239. package/src/authorization/model/schedule.ts +0 -49
  240. package/src/authorization/model/session.ts +0 -50
  241. package/src/authorization/model/skill.ts +0 -60
  242. package/src/authorization/model/workflow.ts +0 -60
  243. package/src/authorization/model/workflow_execution.ts +0 -46
  244. package/src/authorization/model/workflow_instance.ts +0 -125
  245. package/src/authorization/store-test-kit.ts +0 -395
@@ -1,33 +1,40 @@
1
1
  /**
2
- * Pins the built-in model's registry: which kinds are declared — every
3
- * kind of the open-source tier, in `fga.mod` order, and nothing else —
4
- * that each declaration names its `.fga` source by its own type, that a
5
- * kind is reached by enum and by FGA type name alike, and that every
6
- * permission the wire can ask about (the IamPermission vocabulary) is
7
- * either a declared relation of the kind or absent from its `.fga` file —
8
- * never a relation the transcript forgot — and that the relations whose
9
- * direct list admits a team (`team#member`) are exactly the roles the
10
- * kind's `team_grantable_roles` names, a subset of what a person can be
11
- * granted and never ownership, so the grant step and the model cannot
12
- * disagree about where a team may be granted.
2
+ * Pins the built-in model against its sources and the contract:
3
+ * - its types are the files `fga.mod` lists, in that order, one type per
4
+ * file named by the file, and every one is bound (bindings.ts);
5
+ * - every binding's schema is the message `kind_meta` spells for the
6
+ * kind (`ai.stigmer.<group>.<name>.<version>.<Name>`), `platform`
7
+ * excepted by name as the one rowless type, so the explicit table
8
+ * cannot bind a kind to the wrong message;
9
+ * - every declaration names a `.fga` file that exists under fga/model;
10
+ * - the model declares every open-source-tier kind, and outside the tier
11
+ * exactly the four kinds the wider editions serve, so a kind the
12
+ * contract moves between tiers is a visible diff here;
13
+ * - a kind is reached by enum and by FGA type name alike;
14
+ * - the relations whose direct list admits a team (`team#member`) are
15
+ * exactly the roles the kind's `team_grantable_roles` names, a subset
16
+ * of what a person can be granted and never ownership, so the grant
17
+ * step and the model cannot disagree about where a team may be
18
+ * granted;
19
+ * - the derived rules are exactly the three relations `kind_meta` cannot
20
+ * derive.
13
21
  *
14
- * The kind list is pinned twice on purpose: as the literal in registry
15
- * order (a reordering or a dropped file is a visible diff) and as
16
- * set-equal to the `kind_meta.tier` open-source members (a kind the
17
- * contract moves into or out of the tier is a visible diff here before
18
- * it is a silent gap in enforcement).
22
+ * Whether the model defines what the wire asks is the whole contract's
23
+ * question, pinned in __tests__/wire-permissions.test.ts.
19
24
  */
25
+ import { existsSync, readFileSync } from "node:fs";
26
+ import { fileURLToPath } from "node:url";
27
+
20
28
  import { describe, expect, it } from "vitest";
21
29
 
30
+ import { ApiResourceGroup } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_group_pb";
22
31
  import {
23
32
  ApiResourceKind,
24
33
  ApiResourceKindSchema,
34
+ ApiResourceVersion,
25
35
  ResourceTier,
26
36
  } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
27
- import {
28
- IamPermissionSchema,
29
- IamRole,
30
- } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
37
+ import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
31
38
 
32
39
  import {
33
40
  getKindMeta,
@@ -35,221 +42,40 @@ import {
35
42
  kindEnumName,
36
43
  teamGrantableRolesFor,
37
44
  } from "../../../pipeline/apiresource-meta.js";
45
+ import { KIND_BINDINGS, ROWLESS } from "../bindings.js";
38
46
  import { builtInModel, declarationFor } from "../index.js";
39
47
  import type { Rewrite, SubjectType } from "../rewrite.js";
40
48
 
41
- /** The open-source tier, in registry order (the `fga.mod` order of their files). */
42
- const DECLARED_KINDS = [
43
- ApiResourceKind.identity_account,
44
- ApiResourceKind.iam_policy,
45
- ApiResourceKind.api_key,
46
- ApiResourceKind.oauth_app,
47
- ApiResourceKind.platform_client,
48
- ApiResourceKind.organization,
49
- ApiResourceKind.agent,
50
- ApiResourceKind.agent_channel,
51
- ApiResourceKind.agent_share,
52
- ApiResourceKind.channel_app,
53
- ApiResourceKind.agent_instance,
54
- ApiResourceKind.agent_execution,
55
- ApiResourceKind.artifact,
56
- ApiResourceKind.environment,
57
- ApiResourceKind.execution_context,
58
- ApiResourceKind.mcp_server,
59
- ApiResourceKind.memory,
60
- ApiResourceKind.schedule,
61
- ApiResourceKind.session,
62
- ApiResourceKind.skill,
63
- ApiResourceKind.workflow,
64
- ApiResourceKind.workflow_instance,
65
- ApiResourceKind.workflow_execution,
66
- ApiResourceKind.plugin,
67
- ] as const;
49
+ /** The server package root, where fga/model lives. */
50
+ const PACKAGE_ROOT = new URL("../../../../", import.meta.url);
68
51
 
69
- /**
70
- * The permissions each `.fga` file defines that the wire vocabulary also
71
- * names, in the file's order — derived by hand from the files at the
72
- * pinned commit. A relation in the file and not here, or here and not in
73
- * the transcript, fails. Verbs the files define outside the vocabulary
74
- * (`can_use`, `can_clone`, `can_rotate`, `can_revoke`) are the model's
75
- * own and are not listed.
76
- */
77
- const WIRE_PERMISSIONS_BY_TYPE: Readonly<
78
- Record<string, ReadonlyArray<string>>
79
- > = {
80
- identity_account: [
81
- "can_view",
82
- "can_edit",
83
- "can_delete",
84
- "can_grant_access",
85
- "can_view_access",
86
- ],
87
- iam_policy: [
88
- "can_view",
89
- "can_edit",
90
- "can_delete",
91
- "can_grant_access",
92
- "can_view_access",
93
- ],
94
- api_key: [
95
- "can_view",
96
- "can_edit",
97
- "can_delete",
98
- "can_grant_access",
99
- "can_view_access",
100
- ],
101
- oauth_app: [
102
- "can_view",
103
- "can_edit",
104
- "can_delete",
105
- "can_grant_access",
106
- "can_view_access",
107
- ],
108
- platform_client: [
109
- "can_view",
110
- "can_edit",
111
- "can_delete",
112
- "can_grant_access",
113
- "can_view_access",
114
- ],
115
- organization: [
116
- "can_view",
117
- "can_edit",
118
- "can_delete",
119
- "can_create_agent",
120
- "can_create_workflow",
121
- "can_create_session",
122
- "can_create_environment",
123
- "can_create_skill",
124
- "can_create_plugin",
125
- "can_create_mcp_server",
126
- "can_create_idp",
127
- "can_create_identity_account",
128
- "can_create_oauth_app",
129
- "can_create_platform_client",
130
- "can_create_channel_app",
131
- "can_create_execution_in",
132
- "can_create_agent_share",
133
- "can_create_agent_instance",
134
- "can_create_team",
135
- "can_grant_access",
136
- "can_view_access",
137
- "can_view_billing",
138
- "can_manage_billing",
139
- ],
140
- agent: [
141
- "can_view",
142
- "can_edit",
143
- "can_delete",
144
- "can_execute",
145
- "can_create_instance",
146
- "can_grant_access",
147
- "can_view_access",
148
- ],
149
- agent_channel: [
150
- "can_view",
151
- "can_edit",
152
- "can_delete",
153
- "can_participate",
154
- "can_grant_access",
155
- "can_view_access",
156
- ],
157
- agent_share: [
158
- "can_view",
159
- "can_edit",
160
- "can_delete",
161
- "can_grant_access",
162
- "can_view_access",
163
- ],
164
- channel_app: [
165
- "can_view",
166
- "can_edit",
167
- "can_delete",
168
- "can_grant_access",
169
- "can_view_access",
170
- ],
171
- agent_instance: [
172
- "can_view",
173
- "can_execute",
174
- "can_edit",
175
- "can_delete",
176
- "can_grant_access",
177
- "can_view_access",
178
- ],
179
- agent_execution: ["can_view", "can_edit"],
180
- artifact: ["can_view", "can_edit"],
181
- environment: [
182
- "can_view",
183
- "can_edit",
184
- "can_delete",
185
- "can_read_secrets",
186
- "can_grant_access",
187
- "can_view_access",
188
- ],
189
- execution_context: ["can_view", "can_edit"],
190
- mcp_server: [
191
- "can_view",
192
- "can_edit",
193
- "can_delete",
194
- "can_connect",
195
- "can_grant_access",
196
- "can_view_access",
197
- ],
198
- memory: ["can_view", "can_edit", "can_delete"],
199
- schedule: [
200
- "can_view",
201
- "can_edit",
202
- "can_delete",
203
- "can_grant_access",
204
- "can_view_access",
205
- ],
206
- session: [
207
- "can_view",
208
- "can_edit",
209
- "can_delete",
210
- "can_create_execution_in",
211
- "can_grant_access",
212
- "can_view_access",
213
- ],
214
- skill: [
215
- "can_view",
216
- "can_edit",
217
- "can_delete",
218
- "can_grant_access",
219
- "can_view_access",
220
- ],
221
- workflow: [
222
- "can_view",
223
- "can_edit",
224
- "can_delete",
225
- "can_execute",
226
- "can_grant_access",
227
- "can_view_access",
228
- ],
229
- plugin: [
230
- "can_view",
231
- "can_edit",
232
- "can_delete",
233
- "can_grant_access",
234
- "can_view_access",
235
- ],
236
- workflow_instance: [
237
- "can_view",
238
- "can_execute",
239
- "can_edit",
240
- "can_delete",
241
- "can_grant_access",
242
- "can_view_access",
243
- ],
244
- workflow_execution: [
245
- "can_view",
246
- "can_edit",
247
- "can_grant_access",
248
- "can_view_access",
249
- ],
250
- };
52
+ /** The kinds the model defines outside the open-source tier: the ones the wider editions serve. */
53
+ const WIDER_EDITION_KINDS: ReadonlyArray<ApiResourceKind> = [
54
+ ApiResourceKind.platform,
55
+ ApiResourceKind.identity_provider,
56
+ ApiResourceKind.invitation,
57
+ ApiResourceKind.team,
58
+ ];
59
+
60
+ /** The files `fga.mod` lists, in its order. */
61
+ function modFiles(): ReadonlyArray<string> {
62
+ const text = readFileSync(fileURLToPath(new URL("fga/model/fga.mod", PACKAGE_ROOT)), "utf8");
63
+ return [...text.matchAll(/^\s*-\s*(\S+\.fga)\s*$/gm)].map((match) => match[1] ?? "");
64
+ }
65
+
66
+ /** The message `kind_meta` spells for a kind. */
67
+ function kindMetaMessageName(kind: ApiResourceKind): string {
68
+ const meta = getKindMeta(kind);
69
+ return [
70
+ "ai",
71
+ "stigmer",
72
+ ApiResourceGroup[meta.group],
73
+ meta.name.toLowerCase(),
74
+ ApiResourceVersion[meta.version],
75
+ meta.name,
76
+ ].join(".");
77
+ }
251
78
 
252
- /** The subject types a relation's direct lists name, wherever they sit in its rewrite. */
253
79
  function directSubjects(rewrite: Rewrite): ReadonlyArray<SubjectType> {
254
80
  switch (rewrite.node) {
255
81
  case "this":
@@ -268,14 +94,10 @@ function directSubjects(rewrite: Rewrite): ReadonlyArray<SubjectType> {
268
94
  }
269
95
 
270
96
  function isTeamMembers(subject: SubjectType): boolean {
271
- return (
272
- subject.form === "userset" &&
273
- subject.type === "team" &&
274
- subject.relation === "member"
275
- );
97
+ return subject.form === "userset" && subject.type === "team" && subject.relation === "member";
276
98
  }
277
99
 
278
- /** Every kind whose `kind_meta.tier` is the open-source tier, by enum number. */
100
+ /** Every kind whose `kind_meta.tier` is the open-source tier. */
279
101
  function openSourceTierKinds(): ReadonlySet<ApiResourceKind> {
280
102
  const kinds = new Set<ApiResourceKind>();
281
103
  for (const value of ApiResourceKindSchema.values) {
@@ -290,59 +112,63 @@ function openSourceTierKinds(): ReadonlySet<ApiResourceKind> {
290
112
  return kinds;
291
113
  }
292
114
 
293
- describe("the built-in model's registry", () => {
294
- it("declares exactly the open-source tier's kinds, in fga.mod order", () => {
295
- expect(builtInModel.declarations.map((d) => d.kind)).toEqual([
296
- ...DECLARED_KINDS,
297
- ]);
298
- expect(new Set(DECLARED_KINDS)).toEqual(openSourceTierKinds());
115
+ describe("the built-in model", () => {
116
+ it("defines one type per file fga.mod lists, named by the file, in fga.mod order, every one bound", () => {
117
+ const files = modFiles();
118
+ expect(files.length).toBeGreaterThan(0);
119
+ expect(builtInModel.declarations.map((d) => d.type)).toEqual(
120
+ files.map((file) => file.replace(/^.*\//, "").replace(/\.fga$/, "")),
121
+ );
122
+ expect(builtInModel.declarations.map((d) => d.source)).toEqual(
123
+ files.map((file) => `fga/model/${file}`),
124
+ );
125
+ expect([...KIND_BINDINGS.keys()].map(kindEnumName)).toEqual(
126
+ builtInModel.declarations.map((d) => d.type),
127
+ );
299
128
  });
300
129
 
301
- it("reaches a declaration by kind and by FGA type name, and answers undefined for the kinds this edition does not serve", () => {
302
- for (const kind of DECLARED_KINDS) {
303
- const byKind = declarationFor(kind);
304
- expect(byKind?.kind).toBe(kind);
305
- expect(builtInModel.byType(kindEnumName(kind))).toBe(byKind);
306
- }
307
- for (const unserved of [
308
- ApiResourceKind.platform,
309
- ApiResourceKind.identity_provider,
310
- ApiResourceKind.invitation,
311
- ApiResourceKind.team,
312
- ]) {
313
- expect(declarationFor(unserved), kindEnumName(unserved)).toBeUndefined();
130
+ it("binds every kind to the message kind_meta spells, and platform alone to ROWLESS", () => {
131
+ for (const declaration of builtInModel.declarations) {
132
+ if (declaration.kind === ApiResourceKind.platform) {
133
+ expect(declaration.schema).toBe(ROWLESS);
134
+ continue;
135
+ }
136
+ expect(declaration.schema?.typeName, declaration.type).toBe(
137
+ kindMetaMessageName(declaration.kind),
138
+ );
314
139
  }
315
- expect(builtInModel.byType("")).toBeUndefined();
316
140
  });
317
141
 
318
- it("names its `.fga` source by the kind's own type, under the module the fga.mod files it in", () => {
142
+ it("names a .fga source that exists", () => {
319
143
  for (const declaration of builtInModel.declarations) {
320
- expect(declaration.source).toMatch(
321
- new RegExp(
322
- `^fga/model/(tenancy|agentic|iam)/${declaration.type}\\.fga$`,
323
- ),
324
- );
144
+ expect(
145
+ existsSync(fileURLToPath(new URL(declaration.source, PACKAGE_ROOT))),
146
+ declaration.source,
147
+ ).toBe(true);
325
148
  }
326
149
  });
327
150
 
328
- it("declares, for every kind, exactly the wire permissions its `.fga` file defines", () => {
329
- const wireVocabulary = new Set(
330
- IamPermissionSchema.values.map((value) => value.name),
331
- );
332
- expect(Object.keys(WIRE_PERMISSIONS_BY_TYPE).sort()).toEqual(
333
- builtInModel.declarations.map((d) => d.type).sort(),
334
- );
151
+ it("defines every open-source-tier kind, and outside the tier exactly the kinds the wider editions serve", () => {
152
+ const declared = new Set(builtInModel.declarations.map((d) => d.kind));
153
+ const tier = openSourceTierKinds();
154
+ for (const kind of tier) {
155
+ expect(declared.has(kind), kindEnumName(kind)).toBe(true);
156
+ }
157
+ expect(
158
+ [...declared].filter((kind) => !tier.has(kind)).map(kindEnumName).sort(),
159
+ ).toEqual(WIDER_EDITION_KINDS.map(kindEnumName).sort());
160
+ });
161
+
162
+ it("reaches a declaration by kind and by FGA type name", () => {
335
163
  for (const declaration of builtInModel.declarations) {
336
- const declaredWirePermissions = [...declaration.relations.keys()].filter(
337
- (relation) => wireVocabulary.has(relation),
338
- );
339
- expect(declaredWirePermissions, declaration.type).toEqual(
340
- WIRE_PERMISSIONS_BY_TYPE[declaration.type],
341
- );
164
+ expect(declarationFor(declaration.kind)).toBe(declaration);
165
+ expect(builtInModel.byType(kindEnumName(declaration.kind))).toBe(declaration);
342
166
  }
167
+ expect(declarationFor(ApiResourceKind.api_resource_kind_unknown)).toBeUndefined();
168
+ expect(builtInModel.byType("")).toBeUndefined();
343
169
  });
344
170
 
345
- it("admits a team exactly on the roles each kind's team_grantable_roles lists — the transcript and the contract say one thing", () => {
171
+ it("admits a team exactly on the roles each kind's team_grantable_roles lists — the model and the contract say one thing", () => {
346
172
  for (const declaration of builtInModel.declarations) {
347
173
  const admitsTeam = [...declaration.relations]
348
174
  .filter(([, rewrite]) => directSubjects(rewrite).some(isTeamMembers))
@@ -0,0 +1,131 @@
1
+ /**
2
+ * What the compiled model cannot say about a type, and the evaluator needs:
3
+ * the message schema a stored row of the kind decodes with, and the
4
+ * relations `kind_meta.authorization` cannot derive from the row alone.
5
+ * One row per type of the model, in `fga.mod` order; model/index.ts joins
6
+ * this table with the compiled JSON and refuses, at load, a type with no
7
+ * binding, a binding with no type, and a derived rule for a relation the
8
+ * type does not define.
9
+ *
10
+ * The table is explicit rather than computed from `kind_meta`'s naming
11
+ * convention (`ai.stigmer.<group>.<name>.<version>.<Name>`): a lookup by
12
+ * name would need a registry holding every message and would hide a
13
+ * mistake behind a rule, and an explicit import is what the tree-shaken
14
+ * slim bundle wants. The convention holds for every row but `platform`,
15
+ * and model/__tests__/registry.test.ts pins the table against it.
16
+ *
17
+ * `platform` has no stored resource: it is the singleton the platform's
18
+ * own permissions hang off (`platform:stigmer`), and a check on it
19
+ * resolves over tuples alone. Its row is ROWLESS, so the table says so
20
+ * rather than leaving a hole; the tuple source never loads a row for it
21
+ * and the list scope never lists it.
22
+ *
23
+ * The three derived rules: `default_of` on both instance kinds, which the
24
+ * blueprint's default-instance pointer decides (default-of.ts), and a
25
+ * workflow instance's `execution_viewer`, which its execution visibility
26
+ * decides (execution-viewer.ts).
27
+ */
28
+ import type { DescMessage } from "@bufbuild/protobuf";
29
+
30
+ import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
31
+ import { AgentChannelSchema } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/api_pb";
32
+ import { AgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
33
+ import { AgentInstanceSchema } from "@stigmer/protos/ai/stigmer/agentic/agentinstance/v1/api_pb";
34
+ import { AgentShareSchema } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/api_pb";
35
+ import { ArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/artifact/v1/api_pb";
36
+ import { ChannelAppSchema } from "@stigmer/protos/ai/stigmer/agentic/channelapp/v1/api_pb";
37
+ import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
38
+ import { ExecutionContextSchema } from "@stigmer/protos/ai/stigmer/agentic/executioncontext/v1/api_pb";
39
+ import { McpServerSchema } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
40
+ import { MemorySchema } from "@stigmer/protos/ai/stigmer/agentic/memory/v1/api_pb";
41
+ import { PluginSchema } from "@stigmer/protos/ai/stigmer/agentic/plugin/v1/api_pb";
42
+ import { ScheduleSchema } from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/api_pb";
43
+ import { SessionSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
44
+ import { SkillSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/api_pb";
45
+ import { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
46
+ import { WorkflowExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
47
+ import { WorkflowInstanceSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowinstance/v1/api_pb";
48
+ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
49
+ import { ApiKeySchema } from "@stigmer/protos/ai/stigmer/iam/apikey/v1/api_pb";
50
+ import { IamPolicySchema } from "@stigmer/protos/ai/stigmer/iam/iampolicy/v1/api_pb";
51
+ import { IdentityAccountSchema } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/api_pb";
52
+ import { IdentityProviderSchema } from "@stigmer/protos/ai/stigmer/iam/identityprovider/v1/api_pb";
53
+ import { InvitationSchema } from "@stigmer/protos/ai/stigmer/iam/invitation/v1/api_pb";
54
+ import { OAuthAppSchema } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/api_pb";
55
+ import { PlatformClientSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
56
+ import { TeamSchema } from "@stigmer/protos/ai/stigmer/iam/team/v1/api_pb";
57
+ import { OrganizationSchema } from "@stigmer/protos/ai/stigmer/tenancy/organization/v1/api_pb";
58
+
59
+ import { defaultOfBlueprint } from "./default-of.js";
60
+ import { executionViewer } from "./execution-viewer.js";
61
+ import type { DerivedRelation } from "./rewrite.js";
62
+
63
+ /** A type with no stored resource: it resolves over tuples alone (the module header). */
64
+ export const ROWLESS = undefined;
65
+
66
+ export interface KindBinding {
67
+ /** What a stored row decodes with, or ROWLESS. */
68
+ readonly schema: DescMessage | typeof ROWLESS;
69
+ /** The relations `kind_meta` cannot derive, by relation name. */
70
+ readonly derived?: Readonly<Record<string, DerivedRelation>>;
71
+ }
72
+
73
+ /** Every type of the model, bound, in `fga.mod` order. */
74
+ export const KIND_BINDINGS: ReadonlyMap<ApiResourceKind, KindBinding> = new Map<
75
+ ApiResourceKind,
76
+ KindBinding
77
+ >([
78
+ [ApiResourceKind.platform, { schema: ROWLESS }],
79
+ [ApiResourceKind.identity_account, { schema: IdentityAccountSchema }],
80
+ [ApiResourceKind.identity_provider, { schema: IdentityProviderSchema }],
81
+ [ApiResourceKind.iam_policy, { schema: IamPolicySchema }],
82
+ [ApiResourceKind.api_key, { schema: ApiKeySchema }],
83
+ [ApiResourceKind.oauth_app, { schema: OAuthAppSchema }],
84
+ [ApiResourceKind.platform_client, { schema: PlatformClientSchema }],
85
+ [ApiResourceKind.invitation, { schema: InvitationSchema }],
86
+ [ApiResourceKind.team, { schema: TeamSchema }],
87
+ [ApiResourceKind.organization, { schema: OrganizationSchema }],
88
+ [ApiResourceKind.agent, { schema: AgentSchema }],
89
+ [ApiResourceKind.agent_channel, { schema: AgentChannelSchema }],
90
+ [ApiResourceKind.agent_share, { schema: AgentShareSchema }],
91
+ [ApiResourceKind.channel_app, { schema: ChannelAppSchema }],
92
+ [
93
+ ApiResourceKind.agent_instance,
94
+ {
95
+ schema: AgentInstanceSchema,
96
+ derived: {
97
+ default_of: defaultOfBlueprint({
98
+ kind: ApiResourceKind.agent,
99
+ schema: AgentSchema,
100
+ defaultInstanceIdOf: (agent) => agent.status?.defaultInstanceId ?? "",
101
+ }),
102
+ },
103
+ },
104
+ ],
105
+ [ApiResourceKind.agent_execution, { schema: AgentExecutionSchema }],
106
+ [ApiResourceKind.artifact, { schema: ArtifactSchema }],
107
+ [ApiResourceKind.environment, { schema: EnvironmentSchema }],
108
+ [ApiResourceKind.execution_context, { schema: ExecutionContextSchema }],
109
+ [ApiResourceKind.mcp_server, { schema: McpServerSchema }],
110
+ [ApiResourceKind.memory, { schema: MemorySchema }],
111
+ [ApiResourceKind.plugin, { schema: PluginSchema }],
112
+ [ApiResourceKind.schedule, { schema: ScheduleSchema }],
113
+ [ApiResourceKind.session, { schema: SessionSchema }],
114
+ [ApiResourceKind.skill, { schema: SkillSchema }],
115
+ [ApiResourceKind.workflow, { schema: WorkflowSchema }],
116
+ [
117
+ ApiResourceKind.workflow_instance,
118
+ {
119
+ schema: WorkflowInstanceSchema,
120
+ derived: {
121
+ default_of: defaultOfBlueprint({
122
+ kind: ApiResourceKind.workflow,
123
+ schema: WorkflowSchema,
124
+ defaultInstanceIdOf: (workflow) => workflow.status?.defaultInstanceId ?? "",
125
+ }),
126
+ execution_viewer: executionViewer,
127
+ },
128
+ },
129
+ ],
130
+ [ApiResourceKind.workflow_execution, { schema: WorkflowExecutionSchema }],
131
+ ]);