@stigmer/server 3.23.0 → 3.24.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 (272) hide show
  1. package/dist/authorization/authorizer.d.ts +4 -8
  2. package/dist/authorization/authorizer.d.ts.map +1 -1
  3. package/dist/authorization/authorizer.js +5 -9
  4. package/dist/authorization/authorizer.js.map +1 -1
  5. package/dist/authorization/derived-tuples.d.ts.map +1 -1
  6. package/dist/authorization/derived-tuples.js +10 -25
  7. package/dist/authorization/derived-tuples.js.map +1 -1
  8. package/dist/authorization/evaluator.d.ts +7 -8
  9. package/dist/authorization/evaluator.d.ts.map +1 -1
  10. package/dist/authorization/evaluator.js +4 -20
  11. package/dist/authorization/evaluator.js.map +1 -1
  12. package/dist/authorization/list-read-scope.d.ts.map +1 -1
  13. package/dist/authorization/list-read-scope.js +5 -9
  14. package/dist/authorization/list-read-scope.js.map +1 -1
  15. package/dist/authorization/model/agent.d.ts.map +1 -1
  16. package/dist/authorization/model/agent.js +4 -5
  17. package/dist/authorization/model/agent.js.map +1 -1
  18. package/dist/authorization/model/agent_instance.d.ts.map +1 -1
  19. package/dist/authorization/model/agent_instance.js +2 -2
  20. package/dist/authorization/model/agent_instance.js.map +1 -1
  21. package/dist/authorization/model/mcp_server.d.ts.map +1 -1
  22. package/dist/authorization/model/mcp_server.js +2 -2
  23. package/dist/authorization/model/mcp_server.js.map +1 -1
  24. package/dist/authorization/model/plugin.d.ts.map +1 -1
  25. package/dist/authorization/model/plugin.js +3 -4
  26. package/dist/authorization/model/plugin.js.map +1 -1
  27. package/dist/authorization/model/rewrite.d.ts +8 -10
  28. package/dist/authorization/model/rewrite.d.ts.map +1 -1
  29. package/dist/authorization/model/rewrite.js +0 -3
  30. package/dist/authorization/model/rewrite.js.map +1 -1
  31. package/dist/authorization/model/skill.d.ts.map +1 -1
  32. package/dist/authorization/model/skill.js +2 -2
  33. package/dist/authorization/model/skill.js.map +1 -1
  34. package/dist/authorization/model/workflow.d.ts.map +1 -1
  35. package/dist/authorization/model/workflow.js +2 -2
  36. package/dist/authorization/model/workflow.js.map +1 -1
  37. package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
  38. package/dist/authorization/model/workflow_instance.js +2 -2
  39. package/dist/authorization/model/workflow_instance.js.map +1 -1
  40. package/dist/authorization/organization-directory.d.ts.map +1 -1
  41. package/dist/authorization/organization-directory.js +2 -3
  42. package/dist/authorization/organization-directory.js.map +1 -1
  43. package/dist/authorization/store-test-kit.d.ts +0 -6
  44. package/dist/authorization/store-test-kit.d.ts.map +1 -1
  45. package/dist/authorization/store-test-kit.js +16 -62
  46. package/dist/authorization/store-test-kit.js.map +1 -1
  47. package/dist/authorization/tuples.d.ts +13 -32
  48. package/dist/authorization/tuples.d.ts.map +1 -1
  49. package/dist/authorization/tuples.js +3 -11
  50. package/dist/authorization/tuples.js.map +1 -1
  51. package/dist/domain/agent/controller.d.ts.map +1 -1
  52. package/dist/domain/agent/controller.js +4 -4
  53. package/dist/domain/agent/controller.js.map +1 -1
  54. package/dist/domain/agentchannel/controller.d.ts.map +1 -1
  55. package/dist/domain/agentchannel/controller.js +3 -1
  56. package/dist/domain/agentchannel/controller.js.map +1 -1
  57. package/dist/domain/agentexecution/controller.d.ts.map +1 -1
  58. package/dist/domain/agentexecution/controller.js +3 -1
  59. package/dist/domain/agentexecution/controller.js.map +1 -1
  60. package/dist/domain/agentinstance/controller.d.ts.map +1 -1
  61. package/dist/domain/agentinstance/controller.js +3 -4
  62. package/dist/domain/agentinstance/controller.js.map +1 -1
  63. package/dist/domain/agentshare/constants.d.ts +6 -11
  64. package/dist/domain/agentshare/constants.d.ts.map +1 -1
  65. package/dist/domain/agentshare/constants.js +8 -16
  66. package/dist/domain/agentshare/constants.js.map +1 -1
  67. package/dist/domain/agentshare/controller.d.ts.map +1 -1
  68. package/dist/domain/agentshare/controller.js +3 -1
  69. package/dist/domain/agentshare/controller.js.map +1 -1
  70. package/dist/domain/agentshare/steps.d.ts +18 -25
  71. package/dist/domain/agentshare/steps.d.ts.map +1 -1
  72. package/dist/domain/agentshare/steps.js +41 -142
  73. package/dist/domain/agentshare/steps.js.map +1 -1
  74. package/dist/domain/environment/controller.d.ts.map +1 -1
  75. package/dist/domain/environment/controller.js +2 -3
  76. package/dist/domain/environment/controller.js.map +1 -1
  77. package/dist/domain/executioncontext/controller.d.ts.map +1 -1
  78. package/dist/domain/executioncontext/controller.js +2 -1
  79. package/dist/domain/executioncontext/controller.js.map +1 -1
  80. package/dist/domain/mcpserver/controller.d.ts.map +1 -1
  81. package/dist/domain/mcpserver/controller.js +3 -4
  82. package/dist/domain/mcpserver/controller.js.map +1 -1
  83. package/dist/domain/plugin/controller.d.ts.map +1 -1
  84. package/dist/domain/plugin/controller.js +1 -3
  85. package/dist/domain/plugin/controller.js.map +1 -1
  86. package/dist/domain/plugin/push.d.ts +7 -5
  87. package/dist/domain/plugin/push.d.ts.map +1 -1
  88. package/dist/domain/plugin/push.js +8 -14
  89. package/dist/domain/plugin/push.js.map +1 -1
  90. package/dist/domain/schedule/controller.d.ts.map +1 -1
  91. package/dist/domain/schedule/controller.js +3 -1
  92. package/dist/domain/schedule/controller.js.map +1 -1
  93. package/dist/domain/session/controller.d.ts.map +1 -1
  94. package/dist/domain/session/controller.js +3 -2
  95. package/dist/domain/session/controller.js.map +1 -1
  96. package/dist/domain/skill/controller.d.ts.map +1 -1
  97. package/dist/domain/skill/controller.js +0 -2
  98. package/dist/domain/skill/controller.js.map +1 -1
  99. package/dist/domain/workflow/agent-call-references.d.ts +81 -0
  100. package/dist/domain/workflow/agent-call-references.d.ts.map +1 -0
  101. package/dist/domain/workflow/agent-call-references.js +117 -0
  102. package/dist/domain/workflow/agent-call-references.js.map +1 -0
  103. package/dist/domain/workflow/controller.d.ts.map +1 -1
  104. package/dist/domain/workflow/controller.js +14 -6
  105. package/dist/domain/workflow/controller.js.map +1 -1
  106. package/dist/domain/workflow/registry/data/task-kind-registry.json +3 -3
  107. package/dist/domain/workflow/validation/task-config-constraints.d.ts +10 -1
  108. package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
  109. package/dist/domain/workflow/validation/task-config-constraints.js +3 -2
  110. package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
  111. package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
  112. package/dist/domain/workflowexecution/controller.js +4 -2
  113. package/dist/domain/workflowexecution/controller.js.map +1 -1
  114. package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
  115. package/dist/domain/workflowinstance/controller.js +3 -4
  116. package/dist/domain/workflowinstance/controller.js.map +1 -1
  117. package/dist/extensions/authorization-queries.d.ts +6 -5
  118. package/dist/extensions/authorization-queries.d.ts.map +1 -1
  119. package/dist/extensions/list-read-scope.d.ts +1 -1
  120. package/dist/extensions/resource-authorization.d.ts +3 -2
  121. package/dist/extensions/resource-authorization.d.ts.map +1 -1
  122. package/dist/index.d.ts +1 -1
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js.map +1 -1
  125. package/dist/pipeline/apiresource-labels.d.ts +10 -9
  126. package/dist/pipeline/apiresource-labels.d.ts.map +1 -1
  127. package/dist/pipeline/apiresource-labels.js +10 -9
  128. package/dist/pipeline/apiresource-labels.js.map +1 -1
  129. package/dist/pipeline/apiresource-meta.d.ts +12 -3
  130. package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
  131. package/dist/pipeline/apiresource-meta.js +21 -10
  132. package/dist/pipeline/apiresource-meta.js.map +1 -1
  133. package/dist/pipeline/steps/authorization-tuples.d.ts +9 -3
  134. package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
  135. package/dist/pipeline/steps/authorization-tuples.js +9 -11
  136. package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
  137. package/dist/pipeline/steps/references.d.ts +204 -20
  138. package/dist/pipeline/steps/references.d.ts.map +1 -1
  139. package/dist/pipeline/steps/references.js +356 -43
  140. package/dist/pipeline/steps/references.js.map +1 -1
  141. package/dist/query/search/controller.js +0 -1
  142. package/dist/query/search/controller.js.map +1 -1
  143. package/dist/query/search/criteria.d.ts +1 -5
  144. package/dist/query/search/criteria.d.ts.map +1 -1
  145. package/dist/query/search/criteria.js +3 -13
  146. package/dist/query/search/criteria.js.map +1 -1
  147. package/dist/query/search/handler.d.ts +5 -4
  148. package/dist/query/search/handler.d.ts.map +1 -1
  149. package/dist/query/search/handler.js +7 -6
  150. package/dist/query/search/handler.js.map +1 -1
  151. package/dist/query/search/query-store.d.ts.map +1 -1
  152. package/dist/query/search/query-store.js +0 -2
  153. package/dist/query/search/query-store.js.map +1 -1
  154. package/dist/store/interface.d.ts +3 -6
  155. package/dist/store/interface.d.ts.map +1 -1
  156. package/dist/store/interface.js.map +1 -1
  157. package/dist/store/postgres/migrations.d.ts +3 -1
  158. package/dist/store/postgres/migrations.d.ts.map +1 -1
  159. package/dist/store/postgres/migrations.js +35 -1
  160. package/dist/store/postgres/migrations.js.map +1 -1
  161. package/dist/store/postgres/store.d.ts.map +1 -1
  162. package/dist/store/postgres/store.js +4 -9
  163. package/dist/store/postgres/store.js.map +1 -1
  164. package/dist/store/public-visibility-retired.d.ts +67 -0
  165. package/dist/store/public-visibility-retired.d.ts.map +1 -0
  166. package/dist/store/public-visibility-retired.js +55 -0
  167. package/dist/store/public-visibility-retired.js.map +1 -0
  168. package/dist/store/sqlite/migrations.d.ts +3 -1
  169. package/dist/store/sqlite/migrations.d.ts.map +1 -1
  170. package/dist/store/sqlite/migrations.js +36 -1
  171. package/dist/store/sqlite/migrations.js.map +1 -1
  172. package/dist/store/sqlite/store.d.ts.map +1 -1
  173. package/dist/store/sqlite/store.js +4 -8
  174. package/dist/store/sqlite/store.js.map +1 -1
  175. package/package.json +6 -6
  176. package/src/authorization/README.md +24 -26
  177. package/src/authorization/__tests__/authorizer.test.ts +10 -9
  178. package/src/authorization/__tests__/derived-tuples.test.ts +4 -11
  179. package/src/authorization/__tests__/evaluator.test.ts +14 -73
  180. package/src/authorization/__tests__/facts.test.ts +2 -2
  181. package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +19 -27
  182. package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +6 -98
  183. package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +4 -61
  184. package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +1 -15
  185. package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +1 -21
  186. package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +1 -25
  187. package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +1 -3
  188. package/src/authorization/__tests__/list-read-scope.test.ts +18 -17
  189. package/src/authorization/__tests__/store-tests.test.ts +2 -6
  190. package/src/authorization/__tests__/tuples.test.ts +12 -11
  191. package/src/authorization/authorizer.ts +4 -9
  192. package/src/authorization/derived-tuples.ts +14 -31
  193. package/src/authorization/evaluator.ts +8 -44
  194. package/src/authorization/list-read-scope.ts +5 -11
  195. package/src/authorization/model/agent.ts +2 -5
  196. package/src/authorization/model/agent_instance.ts +0 -2
  197. package/src/authorization/model/mcp_server.ts +0 -2
  198. package/src/authorization/model/plugin.ts +1 -4
  199. package/src/authorization/model/rewrite.ts +8 -13
  200. package/src/authorization/model/skill.ts +0 -2
  201. package/src/authorization/model/workflow.ts +0 -2
  202. package/src/authorization/model/workflow_instance.ts +0 -2
  203. package/src/authorization/organization-directory.ts +1 -3
  204. package/src/authorization/store-test-kit.ts +16 -75
  205. package/src/authorization/tuples.ts +16 -39
  206. package/src/domain/agent/__tests__/agent.test.ts +11 -4
  207. package/src/domain/agent/controller.ts +7 -7
  208. package/src/domain/agentchannel/__tests__/agentchannel.test.ts +223 -48
  209. package/src/domain/agentchannel/controller.ts +6 -1
  210. package/src/domain/agentexecution/controller.ts +6 -1
  211. package/src/domain/agentinstance/controller.ts +6 -12
  212. package/src/domain/agentshare/__tests__/agentshare.test.ts +196 -172
  213. package/src/domain/agentshare/__tests__/create-authorization.test.ts +16 -31
  214. package/src/domain/agentshare/constants.ts +14 -26
  215. package/src/domain/agentshare/controller.ts +6 -1
  216. package/src/domain/agentshare/steps.ts +41 -194
  217. package/src/domain/environment/controller.ts +5 -8
  218. package/src/domain/executioncontext/controller.ts +5 -1
  219. package/src/domain/mcpserver/__tests__/mcpserver.test.ts +115 -28
  220. package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +53 -18
  221. package/src/domain/mcpserver/controller.ts +6 -12
  222. package/src/domain/plugin/controller.ts +1 -8
  223. package/src/domain/plugin/push.ts +8 -16
  224. package/src/domain/schedule/controller.ts +6 -1
  225. package/src/domain/session/controller.ts +12 -4
  226. package/src/domain/skill/controller.ts +0 -7
  227. package/src/domain/workflow/__tests__/agent-call-references.test.ts +373 -0
  228. package/src/domain/workflow/agent-call-references.ts +204 -0
  229. package/src/domain/workflow/controller.ts +28 -10
  230. package/src/domain/workflow/registry/data/task-kind-registry.json +3 -3
  231. package/src/domain/workflow/validation/task-config-constraints.ts +7 -3
  232. package/src/domain/workflowexecution/controller.ts +7 -2
  233. package/src/domain/workflowinstance/controller.ts +6 -12
  234. package/src/extensions/__tests__/built-in-authorization-composed.test.ts +32 -19
  235. package/src/extensions/__tests__/extension-composition.test.ts +4 -4
  236. package/src/extensions/__tests__/list-read-scope-composed.test.ts +9 -6
  237. package/src/extensions/authorization-queries.ts +6 -5
  238. package/src/extensions/list-read-scope.ts +1 -1
  239. package/src/extensions/resource-authorization.ts +3 -5
  240. package/src/index.ts +0 -1
  241. package/src/pipeline/__tests__/supports-visibility.test.ts +112 -0
  242. package/src/pipeline/apiresource-labels.ts +10 -9
  243. package/src/pipeline/apiresource-meta.ts +21 -10
  244. package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +18 -31
  245. package/src/pipeline/steps/__tests__/references.test.ts +737 -0
  246. package/src/pipeline/steps/authorization-tuples.ts +9 -11
  247. package/src/pipeline/steps/references.ts +622 -70
  248. package/src/query/search/__tests__/criteria.test.ts +0 -4
  249. package/src/query/search/__tests__/query-store.test.ts +11 -40
  250. package/src/query/search/controller.ts +0 -1
  251. package/src/query/search/criteria.ts +0 -14
  252. package/src/query/search/handler.ts +6 -7
  253. package/src/query/search/query-store.ts +0 -2
  254. package/src/store/__tests__/public-visibility-retired.test.ts +127 -0
  255. package/src/store/__tests__/store-contract.ts +6 -51
  256. package/src/store/interface.ts +3 -6
  257. package/src/store/postgres/__tests__/migrations.test.ts +187 -6
  258. package/src/store/postgres/__tests__/store-contract.test.ts +0 -2
  259. package/src/store/postgres/migrations.ts +48 -1
  260. package/src/store/postgres/store.ts +4 -11
  261. package/src/store/public-visibility-retired.ts +119 -0
  262. package/src/store/sqlite/__tests__/migrations.test.ts +191 -5
  263. package/src/store/sqlite/migrations.ts +45 -1
  264. package/src/store/sqlite/store.ts +4 -10
  265. package/dist/pipeline/steps/visibility-gates.d.ts +0 -27
  266. package/dist/pipeline/steps/visibility-gates.d.ts.map +0 -1
  267. package/dist/pipeline/steps/visibility-gates.js +0 -139
  268. package/dist/pipeline/steps/visibility-gates.js.map +0 -1
  269. package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +0 -98
  270. package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +0 -52
  271. package/src/pipeline/steps/__tests__/visibility-gates.test.ts +0 -187
  272. package/src/pipeline/steps/visibility-gates.ts +0 -174
@@ -6,7 +6,10 @@
6
6
  * predates Go's idempotent ALTERs gains its columns; a legacy pre-v2
7
7
  * database gets its prefix-based audit rows migrated. Rollback safety
8
8
  * (DD-006): a v7 database re-opened by Go-shaped version checks
9
- * (< 6) runs nothing.
9
+ * (< 6) runs nothing. v10, the chain's first row-decoding step, moves
10
+ * every row of the seven kinds that held the retired public level to org
11
+ * and leaves every other row's bytes as they were; a row it cannot decode
12
+ * fails the step and leaves the database at v9.
10
13
  */
11
14
  import { mkdtempSync, rmSync } from "node:fs";
12
15
  import { tmpdir } from "node:os";
@@ -15,12 +18,20 @@ import { DatabaseSync } from "node:sqlite";
15
18
 
16
19
  import { afterEach, describe, expect, it } from "vitest";
17
20
 
21
+ import { create, fromBinary, fromJson, toBinary } from "@bufbuild/protobuf";
22
+ import type { DescMessage } from "@bufbuild/protobuf";
23
+
24
+ import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
25
+ import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
26
+
18
27
  import { SqliteStore } from "../store.js";
19
28
  import {
20
29
  CURRENT_SCHEMA_VERSION,
30
+ SCHEMA_VERSION_10,
21
31
  getSchemaVersion,
22
32
  runMigrations,
23
33
  } from "../migrations.js";
34
+ import { PUBLIC_ROW_KINDS_AT_RETIREMENT } from "../../public-visibility-retired.js";
24
35
  import { materializeGoFixture } from "./support.js";
25
36
 
26
37
  const cleanups: Array<() => void | Promise<void>> = [];
@@ -81,7 +92,9 @@ describe("fresh database", () => {
81
92
  const rows = db
82
93
  .prepare(`SELECT version FROM schema_version ORDER BY version`)
83
94
  .all() as Array<{ version: number }>;
84
- expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
95
+ expect(rows.map((row) => row.version)).toEqual([
96
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
97
+ ]);
85
98
  });
86
99
 
87
100
  it("re-opening an already-migrated database is a no-op", async () => {
@@ -138,7 +151,9 @@ describe("Go-created v6 database adoption (DD-002 fixture)", () => {
138
151
  expect(org.id).toBe("acme");
139
152
 
140
153
  const audit = db
141
- .prepare(`SELECT version_hash, tag FROM resource_audit WHERE kind = 'organization'`)
154
+ .prepare(
155
+ `SELECT version_hash, tag FROM resource_audit WHERE kind = 'organization'`,
156
+ )
142
157
  .get() as { version_hash: string; tag: string };
143
158
  expect(audit).toEqual({ version_hash: "hash-v1", tag: "stable" });
144
159
 
@@ -283,8 +298,10 @@ describe("legacy pre-v2 database", () => {
283
298
  CREATE TABLE schema_version (version INTEGER PRIMARY KEY, applied_at TEXT NOT NULL DEFAULT (datetime('now')));
284
299
  INSERT INTO schema_version (version) VALUES (1);
285
300
  CREATE TABLE resources (kind TEXT NOT NULL, id TEXT NOT NULL, data BLOB NOT NULL, updated_at TEXT NOT NULL DEFAULT (datetime('now')), PRIMARY KEY (kind, id)) WITHOUT ROWID;
286
- INSERT INTO resources (kind, id, data) VALUES ('skill', 'skl_1', X'0a01aa');
287
- INSERT INTO resources (kind, id, data) VALUES ('skill', 'skill_audit/skl_1/1706123456789', X'0a01bb');
301
+ -- Field 1 (api_version) holding one ASCII byte: bytes v10 can decode
302
+ -- as a Skill, since it walks every live row of that kind.
303
+ INSERT INTO resources (kind, id, data) VALUES ('skill', 'skl_1', X'0a0176');
304
+ INSERT INTO resources (kind, id, data) VALUES ('skill', 'skill_audit/skl_1/1706123456789', X'0a0177');
288
305
  `);
289
306
  setup.close();
290
307
 
@@ -330,3 +347,172 @@ describe("rollback safety (DD-006)", () => {
330
347
  expect(version.v).toBeGreaterThanOrEqual(6);
331
348
  });
332
349
  });
350
+
351
+ describe("v10: the retired public level leaves every row", () => {
352
+ /** A v9 database: the chain replayed, then v10's version row removed. */
353
+ function v9Database(): { dbPath: string; db: DatabaseSync } {
354
+ const dbPath = tempDbPath();
355
+ const setup = new DatabaseSync(dbPath);
356
+ runMigrations(setup);
357
+ setup
358
+ .prepare(`DELETE FROM schema_version WHERE version = ?`)
359
+ .run(SCHEMA_VERSION_10);
360
+ expect(getSchemaVersion(setup)).toBe(SCHEMA_VERSION_10 - 1);
361
+ return { dbPath, db: setup };
362
+ }
363
+
364
+ function agentBytes(
365
+ id: string,
366
+ visibility: ApiResourceVisibility,
367
+ ): Uint8Array {
368
+ return toBinary(
369
+ AgentSchema,
370
+ create(AgentSchema, {
371
+ apiVersion: "agentic.stigmer.ai/v1",
372
+ kind: "Agent",
373
+ metadata: { id, name: id, slug: id, org: "acme", visibility },
374
+ spec: { instructions: "a conformant instruction body" },
375
+ }),
376
+ );
377
+ }
378
+
379
+ /** A row of `schema` carrying only the envelope every kind shares, at `visibility`. */
380
+ function envelopeBytes(
381
+ schema: DescMessage,
382
+ id: string,
383
+ visibility: ApiResourceVisibility,
384
+ ): Uint8Array {
385
+ return toBinary(
386
+ schema,
387
+ fromJson(schema, {
388
+ metadata: {
389
+ id,
390
+ name: id,
391
+ slug: id,
392
+ org: "acme",
393
+ visibility: ApiResourceVisibility[visibility],
394
+ },
395
+ }),
396
+ );
397
+ }
398
+
399
+ function insert(
400
+ db: DatabaseSync,
401
+ kind: string,
402
+ id: string,
403
+ data: Uint8Array,
404
+ ): void {
405
+ db.prepare(
406
+ `INSERT INTO resources (kind, id, data, updated_at) VALUES (?, ?, ?, '2026-09-01 00:00:00')`,
407
+ ).run(kind, id, data);
408
+ }
409
+
410
+ function row(
411
+ db: DatabaseSync,
412
+ kind: string,
413
+ id: string,
414
+ ): { data: Uint8Array; updated_at: string } {
415
+ return db
416
+ .prepare(
417
+ `SELECT data, updated_at FROM resources WHERE kind = ? AND id = ?`,
418
+ )
419
+ .get(kind, id) as { data: Uint8Array; updated_at: string };
420
+ }
421
+
422
+ it("moves a public row of every kind in the frozen table to org, and leaves an org row and a private row byte-for-byte", () => {
423
+ const { dbPath, db: setup } = v9Database();
424
+ // One public row per kind the level could live under, each encoded
425
+ // through its own schema.
426
+ for (const entry of PUBLIC_ROW_KINDS_AT_RETIREMENT) {
427
+ insert(
428
+ setup,
429
+ entry.kind,
430
+ `${entry.kind}_public`,
431
+ envelopeBytes(
432
+ entry.schema,
433
+ `${entry.kind}_public`,
434
+ ApiResourceVisibility.visibility_public,
435
+ ),
436
+ );
437
+ }
438
+ const orgBytes = agentBytes(
439
+ "agt_org",
440
+ ApiResourceVisibility.visibility_org,
441
+ );
442
+ const privateBytes = agentBytes(
443
+ "agt_private",
444
+ ApiResourceVisibility.visibility_private,
445
+ );
446
+ insert(setup, "agent", "agt_org", orgBytes);
447
+ insert(setup, "agent", "agt_private", privateBytes);
448
+ setup.close();
449
+
450
+ const store = SqliteStore.open(dbPath);
451
+ cleanups.push(() => store.close());
452
+ const db = new DatabaseSync(dbPath);
453
+ cleanups.push(() => db.close());
454
+
455
+ expect(getSchemaVersion(db)).toBe(CURRENT_SCHEMA_VERSION);
456
+ for (const entry of PUBLIC_ROW_KINDS_AT_RETIREMENT) {
457
+ const moved = row(db, entry.kind, `${entry.kind}_public`);
458
+ expect(moved.data, entry.kind).toEqual(
459
+ envelopeBytes(
460
+ entry.schema,
461
+ `${entry.kind}_public`,
462
+ ApiResourceVisibility.visibility_org,
463
+ ),
464
+ );
465
+ expect(moved.updated_at, `${entry.kind} updated_at bumped`).not.toBe(
466
+ "2026-09-01 00:00:00",
467
+ );
468
+ }
469
+ expect(row(db, "agent", "agt_org")).toEqual({
470
+ data: orgBytes,
471
+ updated_at: "2026-09-01 00:00:00",
472
+ });
473
+ expect(row(db, "agent", "agt_private")).toEqual({
474
+ data: privateBytes,
475
+ updated_at: "2026-09-01 00:00:00",
476
+ });
477
+ const publicLeft = db
478
+ .prepare(`SELECT count(*) AS n FROM resources WHERE kind = 'agent'`)
479
+ .get() as { n: number };
480
+ expect(publicLeft.n).toBe(3);
481
+ });
482
+
483
+ it("a row of a kind outside the frozen table is never decoded", () => {
484
+ const { dbPath, db: setup } = v9Database();
485
+ // Bytes no schema decodes, under a kind the level never applied to.
486
+ insert(setup, "session", "ses_opaque", new Uint8Array([0xff, 0xff]));
487
+ setup.close();
488
+
489
+ const store = SqliteStore.open(dbPath);
490
+ cleanups.push(() => store.close());
491
+ const db = new DatabaseSync(dbPath);
492
+ cleanups.push(() => db.close());
493
+ expect(getSchemaVersion(db)).toBe(CURRENT_SCHEMA_VERSION);
494
+ expect(row(db, "session", "ses_opaque").data).toEqual(
495
+ new Uint8Array([0xff, 0xff]),
496
+ );
497
+ });
498
+
499
+ it("a row of a table kind that does not decode fails the step, names the row, and leaves the database at v9 with every row untouched", () => {
500
+ const { dbPath, db: setup } = v9Database();
501
+ const publicBytes = agentBytes(
502
+ "agt_public",
503
+ ApiResourceVisibility.visibility_public,
504
+ );
505
+ insert(setup, "agent", "agt_public", publicBytes);
506
+ insert(setup, "agent", "agt_broken", new Uint8Array([0xff, 0xff, 0xff]));
507
+ setup.close();
508
+
509
+ expect(() => SqliteStore.open(dbPath)).toThrow(
510
+ /migrate to v10: .*agent 'agt_broken' cannot be moved off the retired public level/,
511
+ );
512
+
513
+ const db = new DatabaseSync(dbPath);
514
+ cleanups.push(() => db.close());
515
+ expect(getSchemaVersion(db)).toBe(SCHEMA_VERSION_10 - 1);
516
+ expect(row(db, "agent", "agt_public").data).toEqual(publicBytes);
517
+ });
518
+ });
@@ -32,6 +32,11 @@
32
32
  */
33
33
  import type { DatabaseSync } from "node:sqlite";
34
34
 
35
+ import {
36
+ PUBLIC_ROW_KINDS_AT_RETIREMENT,
37
+ movePublicRowToOrg,
38
+ } from "../public-visibility-retired.js";
39
+
35
40
  export const SCHEMA_VERSION_1 = 1;
36
41
  export const SCHEMA_VERSION_2 = 2;
37
42
  export const SCHEMA_VERSION_3 = 3;
@@ -44,9 +49,11 @@ export const SCHEMA_VERSION_7 = 7;
44
49
  export const SCHEMA_VERSION_8 = 8;
45
50
  /** v9: the rows of the removed Project kind deleted. */
46
51
  export const SCHEMA_VERSION_9 = 9;
52
+ /** v10: every row holding the retired public visibility level moved to org. */
53
+ export const SCHEMA_VERSION_10 = 10;
47
54
 
48
55
  /** Target version for new databases. */
49
- export const CURRENT_SCHEMA_VERSION = SCHEMA_VERSION_9;
56
+ export const CURRENT_SCHEMA_VERSION = SCHEMA_VERSION_10;
50
57
 
51
58
  /** Applies all pending migrations in order. */
52
59
  export function runMigrations(db: DatabaseSync): void {
@@ -69,6 +76,7 @@ export function runMigrations(db: DatabaseSync): void {
69
76
  [SCHEMA_VERSION_7, migrateToV7],
70
77
  [SCHEMA_VERSION_8, migrateToV8],
71
78
  [SCHEMA_VERSION_9, migrateToV9],
79
+ [SCHEMA_VERSION_10, migrateToV10],
72
80
  ];
73
81
 
74
82
  for (const [version, migrate] of chain) {
@@ -379,3 +387,39 @@ function migrateToV9(db: DatabaseSync): void {
379
387
  DELETE FROM resource_audit WHERE kind = 'project';
380
388
  `);
381
389
  }
390
+
391
+ /**
392
+ * v10: the public visibility level is retired, so every row that still
393
+ * holds it is moved to org visibility — the chain's first migration that
394
+ * decodes a row (public-visibility-retired.ts says why a migration, why
395
+ * the kind table is frozen there, and why an undecodable row fails the
396
+ * step). This step owns the SQL: one read per kind in the frozen table,
397
+ * one UPDATE per moved row with `updated_at` bumped the way saveResource
398
+ * bumps it, rows that hold any other level left byte-for-byte as they
399
+ * are. Runs inside applyInTransaction's BEGIN, so a throw from the mover
400
+ * rolls the whole step back and the boot stops on the row it names.
401
+ */
402
+ function migrateToV10(db: DatabaseSync): void {
403
+ const update = db.prepare(
404
+ `UPDATE resources SET data = ?, updated_at = datetime('now') WHERE kind = ? AND id = ?`,
405
+ );
406
+ for (const entry of PUBLIC_ROW_KINDS_AT_RETIREMENT) {
407
+ const rows = db
408
+ .prepare(`SELECT id, data FROM resources WHERE kind = ?`)
409
+ .all(entry.kind) as Array<{ id: string; data: Uint8Array }>;
410
+ for (const row of rows) {
411
+ let moved: Uint8Array | undefined;
412
+ try {
413
+ moved = movePublicRowToOrg(entry, row.data);
414
+ } catch (error) {
415
+ throw new Error(
416
+ `${entry.kind} '${row.id}' cannot be moved off the retired public level: ${String(error)}`,
417
+ { cause: error },
418
+ );
419
+ }
420
+ if (moved !== undefined) {
421
+ update.run(moved, entry.kind, row.id);
422
+ }
423
+ }
424
+ }
425
+ }
@@ -820,21 +820,15 @@ export class SqliteStore implements Store {
820
820
  const kindPlaceholders = query.kinds.map(() => "?").join(",");
821
821
  const kindArgs = [...query.kinds];
822
822
 
823
- // The scope-filter fragments, byte-identical to Go's buildScopeFilter:
824
- // org (strict or public-widened) and the independent public subtraction.
823
+ // The scope-filter fragments: the org filter, strict (a row is in the
824
+ // requested org or it is not; visibility never widens or narrows the
825
+ // scope), then the authorized-id allowlist.
825
826
  const scopeClauses: string[] = [];
826
827
  const scopeArgs: string[] = [];
827
828
  if (query.orgFilter !== "") {
828
- scopeClauses.push(
829
- query.crossOrgPublic
830
- ? `AND (org = ? OR visibility = 'visibility_public')`
831
- : `AND org = ?`,
832
- );
829
+ scopeClauses.push(`AND org = ?`);
833
830
  scopeArgs.push(query.orgFilter);
834
831
  }
835
- if (query.excludePublic) {
836
- scopeClauses.push(`AND visibility != 'visibility_public'`);
837
- }
838
832
  if (query.authorizedIdsByKind !== undefined) {
839
833
  // The 20260830.01 scoping arm: per-kind resource_id allowlists.
840
834
  // An empty set contributes NO clause — that kind matches nothing —
@@ -1,27 +0,0 @@
1
- import type { DescMessage } from "@bufbuild/protobuf";
2
- import type { Authorizer } from "../../extensions/authorizer.js";
3
- import type { CallerIdentity } from "../../extensions/identity.js";
4
- import type { PipelineStep } from "../pipeline.js";
5
- /**
6
- * The denial callers see at both doors (the Java policy copy verbatim).
7
- * Deliberately not byte-pinned by the OSS conformance suite; the cloud
8
- * gate itself is recorded via the clientPublicVisibilityWrites flag.
9
- */
10
- export declare const PUBLIC_VISIBILITY_DENY_MESSAGE: string;
11
- /** The CREATE door: a new state declaring PUBLIC needs the operator grant. */
12
- export declare function newGuardPublicVisibilityStep<Desc extends DescMessage>(authorizer: Authorizer): PipelineStep<Desc>;
13
- /**
14
- * The UPDATE door: escalation to PUBLIC (the stored level is anything
15
- * else) needs the operator grant. Reads the loaded target from the
16
- * chain's own context key (every updateVisibility chain stores it —
17
- * the RecordVisibilityBeforeUpdate pattern).
18
- */
19
- export declare function newAuthorizeVisibilityTransitionStep<Desc extends DescMessage>(targetKey: string, authorizer: Authorizer): PipelineStep<Desc>;
20
- /**
21
- * The single owner of "may this caller set public?" — both doors call it,
22
- * and so does the plugin push chain, whose requested level rides the
23
- * request (the level then flows to every member through the members' own
24
- * create and updateVisibility doors, which run this check again).
25
- */
26
- export declare function requireOperatorMaySetPublic(authorizer: Authorizer, caller: CallerIdentity): Promise<void>;
27
- //# sourceMappingURL=visibility-gates.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"visibility-gates.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/visibility-gates.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,gCAAgC,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAInD;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAEU,CAAC;AAEtD,8EAA8E;AAC9E,wBAAgB,4BAA4B,CAAC,IAAI,SAAS,WAAW,EACnE,UAAU,EAAE,UAAU,GACrB,YAAY,CAAC,IAAI,CAAC,CAgBpB;AAED;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAAC,IAAI,SAAS,WAAW,EAC3E,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,UAAU,GACrB,YAAY,CAAC,IAAI,CAAC,CA+BpB;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
@@ -1,139 +0,0 @@
1
- /**
2
- * The PUBLIC-visibility operator gates — the two write doors a resource
3
- * can become public through, both consulting the ONE composed Authorizer
4
- * with the same platform-scoped check (the Java
5
- * PublicVisibilityEscalationPolicy port; C2 Stage 3, 20260827.10).
6
- *
7
- * PUBLIC is the only level that crosses every org boundary (one flip
8
- * lists a resource in every tenant's cross-org catalog), so entering it
9
- * is a curation decision: the owner requests, the platform team grants.
10
- * The answer is `can_set_public_visibility` on `platform:stigmer` — a
11
- * named operator capability, never a bare operator lookup.
12
- *
13
- * - GuardPublicVisibility: the CREATE door, in the create chains of
14
- * the kinds whose VisibilityConfig supports PUBLIC (agent, workflow,
15
- * mcp_server, agent_instance, workflow_instance). After
16
- * BuildNewState — it inspects the state that will persist — which
17
- * keeps the pinned INVALID_ARGUMENT level-support rejections (the
18
- * earlier ValidateVisibility) their precedence over this
19
- * PERMISSION_DENIED.
20
- * - AuthorizeVisibilityTransition: the UPDATE door, in every
21
- * updateVisibility chain after ValidateVisibilityUpdate (the pinned
22
- * FAILED_PRECONDITION default-instance rejection keeps precedence,
23
- * the Java ordering the conformance suite depends on). Gates only
24
- * the ESCALATION to public (stored level is anything else).
25
- *
26
- * Both run LAZILY — only requests that actually involve the PUBLIC level
27
- * reach the Authorizer — and both pass for internal callers (server-side
28
- * flows publish platform resources by design). The OSS permissive
29
- * default allows, so the local posture keeps today's behavior
30
- * byte-identically; the cloud's FGA Authorizer supplies the gate.
31
- *
32
- * DELIBERATE partial port, recorded owner-visibly (Stage-3 execution
33
- * record): Java's transition step additionally lets operators WITHOUT
34
- * resource can_edit publish and take down (its updateVisibility chains
35
- * authorize post-load, transition-aware); this edition's position-1
36
- * Authorize enforces can_edit on updateVisibility before any step here
37
- * runs, so those operator-without-edit lanes require can_edit until the
38
- * Stage-4 chain disposition addresses them. No conformance lane pins
39
- * them.
40
- */
41
- import { Code, ConnectError } from "@connectrpc/connect";
42
- import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
43
- import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
44
- import { IamPermission } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
45
- import { internalError } from "../errors.js";
46
- import { metadataOf } from "./shapes.js";
47
- /**
48
- * The denial callers see at both doors (the Java policy copy verbatim).
49
- * Deliberately not byte-pinned by the OSS conformance suite; the cloud
50
- * gate itself is recorded via the clientPublicVisibilityWrites flag.
51
- */
52
- export const PUBLIC_VISIBILITY_DENY_MESSAGE = "Public visibility is granted by the platform team. " +
53
- "Ask a platform operator to publish this resource.";
54
- /** The CREATE door: a new state declaring PUBLIC needs the operator grant. */
55
- export function newGuardPublicVisibilityStep(authorizer) {
56
- return {
57
- name: "GuardPublicVisibility",
58
- async execute(ctx) {
59
- if (ctx.callerIdentity.callerClass === "internal") {
60
- return;
61
- }
62
- if (metadataOf(ctx.newState)?.visibility !==
63
- ApiResourceVisibility.visibility_public) {
64
- return;
65
- }
66
- await requireOperatorMaySetPublic(authorizer, ctx.callerIdentity);
67
- },
68
- };
69
- }
70
- /**
71
- * The UPDATE door: escalation to PUBLIC (the stored level is anything
72
- * else) needs the operator grant. Reads the loaded target from the
73
- * chain's own context key (every updateVisibility chain stores it —
74
- * the RecordVisibilityBeforeUpdate pattern).
75
- */
76
- export function newAuthorizeVisibilityTransitionStep(targetKey, authorizer) {
77
- return {
78
- name: "AuthorizeVisibilityTransition",
79
- async execute(ctx) {
80
- if (ctx.callerIdentity.callerClass === "internal") {
81
- return;
82
- }
83
- const requested = ctx.input
84
- .visibility;
85
- if (requested !== ApiResourceVisibility.visibility_public) {
86
- return;
87
- }
88
- const target = ctx.get(targetKey);
89
- if (target === undefined) {
90
- // Wiring error, not a user error: without the loaded target there
91
- // is no stored level, and a gate that silently passes un-guards
92
- // the boundary it exists to protect.
93
- throw internalError(new Error("AuthorizeVisibilityTransition ran without a loaded target — step must be placed after the load step"), "visibility authorization requires the loaded resource");
94
- }
95
- const stored = metadataOf(target)?.visibility;
96
- if (stored === ApiResourceVisibility.visibility_public) {
97
- return; // Re-asserting a stored PUBLIC is not an escalation.
98
- }
99
- await requireOperatorMaySetPublic(authorizer, ctx.callerIdentity);
100
- },
101
- };
102
- }
103
- /**
104
- * The single owner of "may this caller set public?" — both doors call it,
105
- * and so does the plugin push chain, whose requested level rides the
106
- * request (the level then flows to every member through the members' own
107
- * create and updateVisibility doors, which run this check again).
108
- */
109
- export async function requireOperatorMaySetPublic(authorizer, caller) {
110
- let decision;
111
- try {
112
- decision = await authorizer.authorize(caller, {
113
- permission: IamPermission.can_set_public_visibility,
114
- resourceKind: ApiResourceKind.platform,
115
- resourceId: "stigmer",
116
- });
117
- }
118
- catch (error) {
119
- throw publicVisibilityCheckFailure(error);
120
- }
121
- switch (decision.kind) {
122
- case "allow":
123
- return;
124
- case "deny":
125
- case "not-found":
126
- throw new ConnectError(PUBLIC_VISIBILITY_DENY_MESSAGE, Code.PermissionDenied);
127
- case "unavailable":
128
- throw publicVisibilityCheckFailure(decision.cause);
129
- default: {
130
- const exhaustive = decision;
131
- throw publicVisibilityCheckFailure(new Error(`unknown decision ${JSON.stringify(exhaustive)}`));
132
- }
133
- }
134
- }
135
- /** Authorization infrastructure failure: fail closed, sanitized (#478). */
136
- function publicVisibilityCheckFailure(error) {
137
- return internalError(error instanceof Error ? error : new Error(String(error)), "public-visibility authorization could not be completed");
138
- }
139
- //# sourceMappingURL=visibility-gates.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"visibility-gates.js","sourceRoot":"","sources":["../../../src/pipeline/steps/visibility-gates.ts"],"names":[],"mappings":"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"}
@@ -1,98 +0,0 @@
1
- # Pinned copy of stigmer-cloud backend/services/stigmer-server/fga/tests/cross-org-share-creation.fga.yaml
2
- # at commit 2ec420161 (the OpenFGA store test the cloud runs against its live model).
3
- # The body below is byte-identical to the source; the cloud re-runs the LIVE
4
- # file against this evaluator at the re-pin, so a stale copy is caught there.
5
- # Cross-org share creation, the FGA half of the decision 013 D2 consent
6
- # bar. Creating a share of ANOTHER org's marketplace-public agent requires:
7
- #
8
- # 1. can_execute on the agent — granted to every authenticated account by
9
- # the allow_public wildcard when the agent is public (checked here),
10
- # and to nobody cross-org when it is not.
11
- # 2. can_create_agent_share on the SHARE's own org — admin-level: a
12
- # public share spends the org's credits on the open internet. The
13
- # per-kind can_create_* pattern, one more row.
14
- #
15
- # The same-org bar (agent can_edit) is untouched and pinned by
16
- # agent-share-owner.fga.yaml; the app-level halves of the contract
17
- # (visibility check, audience rule, dependency sweep, agent-id pin) are
18
- # handler logic outside FGA, pinned by the Java and Go unit tests.
19
-
20
- name: cross-org-share-creation
21
- model_file: ../model/fga.mod
22
-
23
- tuples:
24
- # provider-org publishes a marketplace-public agent (the conditional
25
- # wildcard viewer tuple visibility_public writes).
26
- - user: organization:provider-org
27
- relation: organization
28
- object: agent:public-helper
29
- - user: identity_account:paula
30
- relation: owner
31
- object: agent:public-helper
32
- - user: identity_account:*
33
- relation: viewer
34
- object: agent:public-helper
35
- condition:
36
- name: allow_public
37
- context: {}
38
-
39
- # A private agent in the same provider org — the wildcard is absent.
40
- - user: organization:provider-org
41
- relation: organization
42
- object: agent:private-helper
43
- - user: identity_account:paula
44
- relation: owner
45
- object: agent:private-helper
46
-
47
- # consumer-org: an admin and a plain member.
48
- - user: identity_account:adam
49
- relation: admin
50
- object: organization:consumer-org
51
- - user: identity_account:mia
52
- relation: member
53
- object: organization:consumer-org
54
-
55
- tests:
56
- - name: consumer-org admin holds both halves of the cross-org bar
57
- check:
58
- - user: identity_account:adam
59
- object: agent:public-helper
60
- context:
61
- allow: true
62
- assertions:
63
- can_execute: true
64
- can_edit: false
65
- - user: identity_account:adam
66
- object: organization:consumer-org
67
- assertions:
68
- can_create_agent_share: true
69
-
70
- - name: plain member lacks the org-side bar (spending is admin-level)
71
- check:
72
- - user: identity_account:mia
73
- object: agent:public-helper
74
- context:
75
- allow: true
76
- assertions:
77
- can_execute: true
78
- - user: identity_account:mia
79
- object: organization:consumer-org
80
- assertions:
81
- can_create_agent_share: false
82
-
83
- - name: nobody cross-org can execute a private agent
84
- check:
85
- - user: identity_account:adam
86
- object: agent:private-helper
87
- context:
88
- allow: true
89
- assertions:
90
- can_execute: false
91
- can_view: false
92
-
93
- - name: the org-side bar does not leak across orgs
94
- check:
95
- - user: identity_account:paula
96
- object: organization:consumer-org
97
- assertions:
98
- can_create_agent_share: false
@@ -1,52 +0,0 @@
1
- # Pinned copy of stigmer-cloud backend/services/stigmer-server/fga/tests/public-visibility-setter.fga.yaml
2
- # at commit b87dd1170 (the OpenFGA store test the cloud runs against its live model).
3
- # The body below is byte-identical to the source; the cloud re-runs the LIVE
4
- # file against this evaluator at the re-pin, so a stale copy is caught there.
5
- # Store tests for the public-visibility permission (marketplace curation).
6
- #
7
- # can_set_public_visibility gates who may set a resource's visibility to
8
- # PUBLIC — the only level that crosses every org boundary. It derives
9
- # from the platform operator role: operators (human curators and the
10
- # seeding machine account) hold it; ordinary tenants — even org owners,
11
- # who can otherwise fully manage their resources — never do. Enforced by
12
- # PublicVisibilityEscalationPolicy at both write doors (updateVisibility
13
- # escalation and create-with-public).
14
- #
15
- # Run with:
16
- # fga model test --tests public-visibility-setter.fga.yaml
17
- name: public-visibility-setter
18
- model_file: ../model/fga.mod
19
-
20
- tuples:
21
- # The bootstrap machine account is the platform operator
22
- # (BootstrapIdentitySeeder re-asserts this tuple on every boot).
23
- - user: identity_account:machine
24
- relation: operator
25
- object: platform:stigmer
26
-
27
- # A human operator granted the same platform role.
28
- - user: identity_account:opal
29
- relation: operator
30
- object: platform:stigmer
31
-
32
- # A tenant org owner — owns everything in their org, nothing on the
33
- # platform.
34
- - user: identity_account:mallory
35
- relation: owner
36
- object: organization:acme
37
-
38
- tests:
39
- - name: operators may set public visibility, tenants may not
40
- check:
41
- - user: identity_account:machine
42
- object: platform:stigmer
43
- assertions:
44
- can_set_public_visibility: true
45
- - user: identity_account:opal
46
- object: platform:stigmer
47
- assertions:
48
- can_set_public_visibility: true
49
- - user: identity_account:mallory
50
- object: platform:stigmer
51
- assertions:
52
- can_set_public_visibility: false