@stigmer/server 3.24.0 → 3.26.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 (341) hide show
  1. package/dist/authorization/authorizer.d.ts +10 -4
  2. package/dist/authorization/authorizer.d.ts.map +1 -1
  3. package/dist/authorization/authorizer.js +11 -4
  4. package/dist/authorization/authorizer.js.map +1 -1
  5. package/dist/authorization/model/index.d.ts +4 -4
  6. package/dist/authorization/model/index.d.ts.map +1 -1
  7. package/dist/authorization/model/index.js +2 -0
  8. package/dist/authorization/model/index.js.map +1 -1
  9. package/dist/authorization/model/oauth_app.d.ts.map +1 -1
  10. package/dist/authorization/model/oauth_app.js +3 -2
  11. package/dist/authorization/model/oauth_app.js.map +1 -1
  12. package/dist/authorization/model/platform_client.d.ts +2 -0
  13. package/dist/authorization/model/platform_client.d.ts.map +1 -0
  14. package/dist/authorization/model/platform_client.js +31 -0
  15. package/dist/authorization/model/platform_client.js.map +1 -0
  16. package/dist/boot/compose.d.ts.map +1 -1
  17. package/dist/boot/compose.js +99 -13
  18. package/dist/boot/compose.js.map +1 -1
  19. package/dist/boot/list-indexes.d.ts +3 -0
  20. package/dist/boot/list-indexes.d.ts.map +1 -0
  21. package/dist/boot/list-indexes.js +24 -0
  22. package/dist/boot/list-indexes.js.map +1 -0
  23. package/dist/boot/route-shadowing.d.ts +25 -0
  24. package/dist/boot/route-shadowing.d.ts.map +1 -0
  25. package/dist/boot/route-shadowing.js +17 -0
  26. package/dist/boot/route-shadowing.js.map +1 -0
  27. package/dist/domain/agentexecution/controller.d.ts.map +1 -1
  28. package/dist/domain/agentexecution/controller.js +6 -8
  29. package/dist/domain/agentexecution/controller.js.map +1 -1
  30. package/dist/domain/agentexecution/list-index.d.ts +2 -0
  31. package/dist/domain/agentexecution/list-index.d.ts.map +1 -0
  32. package/dist/domain/agentexecution/list-index.js +23 -0
  33. package/dist/domain/agentexecution/list-index.js.map +1 -0
  34. package/dist/domain/agentexecution/steps.d.ts +17 -33
  35. package/dist/domain/agentexecution/steps.d.ts.map +1 -1
  36. package/dist/domain/agentexecution/steps.js +94 -95
  37. package/dist/domain/agentexecution/steps.js.map +1 -1
  38. package/dist/domain/agentexecution/usage.d.ts.map +1 -1
  39. package/dist/domain/agentexecution/usage.js +37 -14
  40. package/dist/domain/agentexecution/usage.js.map +1 -1
  41. package/dist/domain/artifact/controller.d.ts.map +1 -1
  42. package/dist/domain/artifact/controller.js +27 -15
  43. package/dist/domain/artifact/controller.js.map +1 -1
  44. package/dist/domain/artifact/list-index.d.ts +2 -0
  45. package/dist/domain/artifact/list-index.d.ts.map +1 -0
  46. package/dist/domain/artifact/list-index.js +21 -0
  47. package/dist/domain/artifact/list-index.js.map +1 -0
  48. package/dist/domain/iampolicy/membership.d.ts +6 -1
  49. package/dist/domain/iampolicy/membership.d.ts.map +1 -1
  50. package/dist/domain/iampolicy/membership.js +8 -2
  51. package/dist/domain/iampolicy/membership.js.map +1 -1
  52. package/dist/domain/identityaccount/constants.d.ts +13 -0
  53. package/dist/domain/identityaccount/constants.d.ts.map +1 -1
  54. package/dist/domain/identityaccount/constants.js +17 -0
  55. package/dist/domain/identityaccount/constants.js.map +1 -1
  56. package/dist/domain/identityaccount/controller.d.ts +3 -1
  57. package/dist/domain/identityaccount/controller.d.ts.map +1 -1
  58. package/dist/domain/identityaccount/controller.js +15 -6
  59. package/dist/domain/identityaccount/controller.js.map +1 -1
  60. package/dist/domain/identityaccount/operator.d.ts.map +1 -1
  61. package/dist/domain/identityaccount/operator.js +1 -0
  62. package/dist/domain/identityaccount/operator.js.map +1 -1
  63. package/dist/domain/identityaccount/provisioning.d.ts +18 -0
  64. package/dist/domain/identityaccount/provisioning.d.ts.map +1 -1
  65. package/dist/domain/identityaccount/provisioning.js +1 -0
  66. package/dist/domain/identityaccount/provisioning.js.map +1 -1
  67. package/dist/domain/identityaccount/resource-store.js +9 -5
  68. package/dist/domain/identityaccount/resource-store.js.map +1 -1
  69. package/dist/domain/identityaccount/steps.d.ts +11 -2
  70. package/dist/domain/identityaccount/steps.d.ts.map +1 -1
  71. package/dist/domain/identityaccount/steps.js +42 -4
  72. package/dist/domain/identityaccount/steps.js.map +1 -1
  73. package/dist/domain/identityaccount/store-contract.d.ts.map +1 -1
  74. package/dist/domain/identityaccount/store-contract.js +33 -0
  75. package/dist/domain/identityaccount/store-contract.js.map +1 -1
  76. package/dist/domain/identityaccount/store.d.ts +7 -5
  77. package/dist/domain/identityaccount/store.d.ts.map +1 -1
  78. package/dist/domain/identityaccount/store.js.map +1 -1
  79. package/dist/domain/platform/controller.d.ts +10 -0
  80. package/dist/domain/platform/controller.d.ts.map +1 -1
  81. package/dist/domain/platform/controller.js +2 -1
  82. package/dist/domain/platform/controller.js.map +1 -1
  83. package/dist/domain/platformclient/constants.d.ts +60 -0
  84. package/dist/domain/platformclient/constants.d.ts.map +1 -0
  85. package/dist/domain/platformclient/constants.js +78 -0
  86. package/dist/domain/platformclient/constants.js.map +1 -0
  87. package/dist/domain/platformclient/controller.d.ts +59 -0
  88. package/dist/domain/platformclient/controller.d.ts.map +1 -0
  89. package/dist/domain/platformclient/controller.js +191 -0
  90. package/dist/domain/platformclient/controller.js.map +1 -0
  91. package/dist/domain/platformclient/credentials.d.ts +13 -0
  92. package/dist/domain/platformclient/credentials.d.ts.map +1 -0
  93. package/dist/domain/platformclient/credentials.js +56 -0
  94. package/dist/domain/platformclient/credentials.js.map +1 -0
  95. package/dist/domain/platformclient/mint.d.ts +23 -0
  96. package/dist/domain/platformclient/mint.d.ts.map +1 -0
  97. package/dist/domain/platformclient/mint.js +254 -0
  98. package/dist/domain/platformclient/mint.js.map +1 -0
  99. package/dist/domain/platformclient/origin-guard.d.ts +9 -0
  100. package/dist/domain/platformclient/origin-guard.d.ts.map +1 -0
  101. package/dist/domain/platformclient/origin-guard.js +81 -0
  102. package/dist/domain/platformclient/origin-guard.js.map +1 -0
  103. package/dist/domain/platformclient/resource-store.d.ts +4 -0
  104. package/dist/domain/platformclient/resource-store.d.ts.map +1 -0
  105. package/dist/domain/platformclient/resource-store.js +117 -0
  106. package/dist/domain/platformclient/resource-store.js.map +1 -0
  107. package/dist/domain/platformclient/steps.d.ts +51 -0
  108. package/dist/domain/platformclient/steps.d.ts.map +1 -0
  109. package/dist/domain/platformclient/steps.js +339 -0
  110. package/dist/domain/platformclient/steps.js.map +1 -0
  111. package/dist/domain/platformclient/store-contract.d.ts +8 -0
  112. package/dist/domain/platformclient/store-contract.d.ts.map +1 -0
  113. package/dist/domain/platformclient/store-contract.js +208 -0
  114. package/dist/domain/platformclient/store-contract.js.map +1 -0
  115. package/dist/domain/platformclient/store.d.ts +58 -0
  116. package/dist/domain/platformclient/store.d.ts.map +1 -0
  117. package/dist/domain/platformclient/store.js +13 -0
  118. package/dist/domain/platformclient/store.js.map +1 -0
  119. package/dist/domain/platformclient/system-managed.d.ts +18 -0
  120. package/dist/domain/platformclient/system-managed.d.ts.map +1 -0
  121. package/dist/domain/platformclient/system-managed.js +80 -0
  122. package/dist/domain/platformclient/system-managed.js.map +1 -0
  123. package/dist/domain/platformclient/token-controller.d.ts +11 -0
  124. package/dist/domain/platformclient/token-controller.d.ts.map +1 -0
  125. package/dist/domain/platformclient/token-controller.js +32 -0
  126. package/dist/domain/platformclient/token-controller.js.map +1 -0
  127. package/dist/domain/platformclient/verifier.d.ts +10 -0
  128. package/dist/domain/platformclient/verifier.d.ts.map +1 -0
  129. package/dist/domain/platformclient/verifier.js +76 -0
  130. package/dist/domain/platformclient/verifier.js.map +1 -0
  131. package/dist/domain/session/list-index.d.ts +4 -0
  132. package/dist/domain/session/list-index.d.ts.map +1 -0
  133. package/dist/domain/session/list-index.js +26 -0
  134. package/dist/domain/session/list-index.js.map +1 -0
  135. package/dist/domain/session/steps.d.ts +7 -8
  136. package/dist/domain/session/steps.d.ts.map +1 -1
  137. package/dist/domain/session/steps.js +76 -78
  138. package/dist/domain/session/steps.js.map +1 -1
  139. package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
  140. package/dist/domain/workflowexecution/controller.js +53 -55
  141. package/dist/domain/workflowexecution/controller.js.map +1 -1
  142. package/dist/domain/workflowexecution/execution-filter.d.ts +4 -0
  143. package/dist/domain/workflowexecution/execution-filter.d.ts.map +1 -1
  144. package/dist/domain/workflowexecution/execution-filter.js +11 -5
  145. package/dist/domain/workflowexecution/execution-filter.js.map +1 -1
  146. package/dist/domain/workflowexecution/get-execution-summary.d.ts.map +1 -1
  147. package/dist/domain/workflowexecution/get-execution-summary.js +23 -17
  148. package/dist/domain/workflowexecution/get-execution-summary.js.map +1 -1
  149. package/dist/domain/workflowexecution/list-index.d.ts +2 -0
  150. package/dist/domain/workflowexecution/list-index.d.ts.map +1 -0
  151. package/dist/domain/workflowexecution/list-index.js +21 -0
  152. package/dist/domain/workflowexecution/list-index.js.map +1 -0
  153. package/dist/domain/workflowexecution/list-pending-approvals.d.ts.map +1 -1
  154. package/dist/domain/workflowexecution/list-pending-approvals.js +69 -32
  155. package/dist/domain/workflowexecution/list-pending-approvals.js.map +1 -1
  156. package/dist/domain/workflowexecution/queries.d.ts +30 -4
  157. package/dist/domain/workflowexecution/queries.d.ts.map +1 -1
  158. package/dist/domain/workflowexecution/queries.js +76 -22
  159. package/dist/domain/workflowexecution/queries.js.map +1 -1
  160. package/dist/encryption/key-manager.d.ts +20 -3
  161. package/dist/encryption/key-manager.d.ts.map +1 -1
  162. package/dist/encryption/key-manager.js +64 -42
  163. package/dist/encryption/key-manager.js.map +1 -1
  164. package/dist/extensions/caller-guards.d.ts +8 -3
  165. package/dist/extensions/caller-guards.d.ts.map +1 -1
  166. package/dist/extensions/drivers.d.ts +37 -0
  167. package/dist/extensions/drivers.d.ts.map +1 -1
  168. package/dist/extensions/guest-token-minting.d.ts +26 -0
  169. package/dist/extensions/guest-token-minting.d.ts.map +1 -0
  170. package/dist/extensions/guest-token-minting.js +2 -0
  171. package/dist/extensions/guest-token-minting.js.map +1 -0
  172. package/dist/extensions/identity.d.ts +6 -4
  173. package/dist/extensions/identity.d.ts.map +1 -1
  174. package/dist/extensions/identity.js.map +1 -1
  175. package/dist/extensions/list-read-scope.d.ts +9 -12
  176. package/dist/extensions/list-read-scope.d.ts.map +1 -1
  177. package/dist/extensions/list-read-scope.js.map +1 -1
  178. package/dist/extensions/registry.d.ts +30 -1
  179. package/dist/extensions/registry.d.ts.map +1 -1
  180. package/dist/extensions/registry.js +34 -1
  181. package/dist/extensions/registry.js.map +1 -1
  182. package/dist/index.d.ts +15 -2
  183. package/dist/index.d.ts.map +1 -1
  184. package/dist/index.js +6 -0
  185. package/dist/index.js.map +1 -1
  186. package/dist/pipeline/interceptors/auth.d.ts +14 -0
  187. package/dist/pipeline/interceptors/auth.d.ts.map +1 -1
  188. package/dist/pipeline/interceptors/auth.js +24 -0
  189. package/dist/pipeline/interceptors/auth.js.map +1 -1
  190. package/dist/pipeline/steps/build-update-state.d.ts +11 -1
  191. package/dist/pipeline/steps/build-update-state.d.ts.map +1 -1
  192. package/dist/pipeline/steps/build-update-state.js +5 -2
  193. package/dist/pipeline/steps/build-update-state.js.map +1 -1
  194. package/dist/pipeline/steps/list-page.d.ts +63 -0
  195. package/dist/pipeline/steps/list-page.d.ts.map +1 -0
  196. package/dist/pipeline/steps/list-page.js +187 -0
  197. package/dist/pipeline/steps/list-page.js.map +1 -0
  198. package/dist/platformtoken/envelope.d.ts +100 -0
  199. package/dist/platformtoken/envelope.d.ts.map +1 -0
  200. package/dist/platformtoken/envelope.js +184 -0
  201. package/dist/platformtoken/envelope.js.map +1 -0
  202. package/dist/platformtoken/key-ring.d.ts +85 -0
  203. package/dist/platformtoken/key-ring.d.ts.map +1 -0
  204. package/dist/platformtoken/key-ring.js +164 -0
  205. package/dist/platformtoken/key-ring.js.map +1 -0
  206. package/dist/query/activity/handler.d.ts +6 -7
  207. package/dist/query/activity/handler.d.ts.map +1 -1
  208. package/dist/query/activity/handler.js +42 -59
  209. package/dist/query/activity/handler.js.map +1 -1
  210. package/dist/store/interface.d.ts +29 -0
  211. package/dist/store/interface.d.ts.map +1 -1
  212. package/dist/store/interface.js.map +1 -1
  213. package/dist/store/list-index.d.ts +123 -0
  214. package/dist/store/list-index.d.ts.map +1 -0
  215. package/dist/store/list-index.js +277 -0
  216. package/dist/store/list-index.js.map +1 -0
  217. package/dist/store/logger.d.ts +3 -0
  218. package/dist/store/logger.d.ts.map +1 -1
  219. package/dist/store/logger.js +5 -1
  220. package/dist/store/logger.js.map +1 -1
  221. package/dist/store/postgres/migrations.d.ts +10 -6
  222. package/dist/store/postgres/migrations.d.ts.map +1 -1
  223. package/dist/store/postgres/migrations.js +64 -6
  224. package/dist/store/postgres/migrations.js.map +1 -1
  225. package/dist/store/postgres/store.d.ts +26 -5
  226. package/dist/store/postgres/store.d.ts.map +1 -1
  227. package/dist/store/postgres/store.js +377 -22
  228. package/dist/store/postgres/store.js.map +1 -1
  229. package/dist/store/sqlite/migrations.d.ts +11 -5
  230. package/dist/store/sqlite/migrations.d.ts.map +1 -1
  231. package/dist/store/sqlite/migrations.js +50 -4
  232. package/dist/store/sqlite/migrations.js.map +1 -1
  233. package/dist/store/sqlite/store.d.ts +18 -3
  234. package/dist/store/sqlite/store.d.ts.map +1 -1
  235. package/dist/store/sqlite/store.js +321 -15
  236. package/dist/store/sqlite/store.js.map +1 -1
  237. package/package.json +6 -6
  238. package/src/authorization/README.md +1 -1
  239. package/src/authorization/__tests__/authorizer.test.ts +6 -2
  240. package/src/authorization/__tests__/drivers.ts +6 -1
  241. package/src/authorization/__tests__/wire-permissions.test.ts +1 -0
  242. package/src/authorization/authorizer.ts +11 -4
  243. package/src/authorization/model/__tests__/registry.test.ts +8 -1
  244. package/src/authorization/model/index.ts +6 -4
  245. package/src/authorization/model/oauth_app.ts +3 -2
  246. package/src/authorization/model/platform_client.ts +43 -0
  247. package/src/boot/__tests__/auth-enabled-composition.test.ts +2 -1
  248. package/src/boot/__tests__/list-indexes.test.ts +65 -0
  249. package/src/boot/__tests__/route-shadowing.test.ts +67 -0
  250. package/src/boot/compose.ts +113 -18
  251. package/src/boot/list-indexes.ts +25 -0
  252. package/src/boot/route-shadowing.ts +47 -0
  253. package/src/domain/agentexecution/__tests__/agentexecution.test.ts +85 -3
  254. package/src/domain/agentexecution/controller.ts +8 -10
  255. package/src/domain/agentexecution/list-index.ts +24 -0
  256. package/src/domain/agentexecution/steps.ts +118 -115
  257. package/src/domain/agentexecution/usage.ts +38 -17
  258. package/src/domain/artifact/controller.ts +28 -18
  259. package/src/domain/artifact/list-index.ts +22 -0
  260. package/src/domain/iampolicy/__tests__/membership.test.ts +7 -0
  261. package/src/domain/iampolicy/membership.ts +8 -2
  262. package/src/domain/identityaccount/__tests__/platform-client-accounts.test.ts +124 -0
  263. package/src/domain/identityaccount/__tests__/resource-store.test.ts +1 -0
  264. package/src/domain/identityaccount/constants.ts +20 -0
  265. package/src/domain/identityaccount/controller.ts +16 -4
  266. package/src/domain/identityaccount/operator.ts +1 -0
  267. package/src/domain/identityaccount/provisioning.ts +17 -0
  268. package/src/domain/identityaccount/resource-store.ts +9 -5
  269. package/src/domain/identityaccount/steps.ts +58 -3
  270. package/src/domain/identityaccount/store-contract.ts +51 -0
  271. package/src/domain/identityaccount/store.ts +7 -5
  272. package/src/domain/platform/__tests__/platform.test.ts +7 -1
  273. package/src/domain/platform/controller.ts +12 -1
  274. package/src/domain/platformclient/__tests__/controller.test.ts +397 -0
  275. package/src/domain/platformclient/__tests__/credentials.test.ts +46 -0
  276. package/src/domain/platformclient/__tests__/mint.test.ts +415 -0
  277. package/src/domain/platformclient/__tests__/resource-store.test.ts +140 -0
  278. package/src/domain/platformclient/__tests__/system-managed.test.ts +101 -0
  279. package/src/domain/platformclient/__tests__/verifier-and-guard.test.ts +207 -0
  280. package/src/domain/platformclient/constants.ts +120 -0
  281. package/src/domain/platformclient/controller.ts +449 -0
  282. package/src/domain/platformclient/credentials.ts +68 -0
  283. package/src/domain/platformclient/mint.ts +412 -0
  284. package/src/domain/platformclient/origin-guard.ts +117 -0
  285. package/src/domain/platformclient/resource-store.ts +150 -0
  286. package/src/domain/platformclient/steps.ts +472 -0
  287. package/src/domain/platformclient/store-contract.ts +318 -0
  288. package/src/domain/platformclient/store.ts +63 -0
  289. package/src/domain/platformclient/system-managed.ts +115 -0
  290. package/src/domain/platformclient/token-controller.ts +49 -0
  291. package/src/domain/platformclient/verifier.ts +106 -0
  292. package/src/domain/session/list-index.ts +28 -0
  293. package/src/domain/session/steps.ts +108 -123
  294. package/src/domain/workflowexecution/__tests__/list-read-scope-summaries.test.ts +155 -99
  295. package/src/domain/workflowexecution/__tests__/workflowexecution.test.ts +193 -31
  296. package/src/domain/workflowexecution/controller.ts +54 -82
  297. package/src/domain/workflowexecution/execution-filter.ts +20 -7
  298. package/src/domain/workflowexecution/get-execution-summary.ts +31 -24
  299. package/src/domain/workflowexecution/list-index.ts +22 -0
  300. package/src/domain/workflowexecution/list-pending-approvals.ts +82 -25
  301. package/src/domain/workflowexecution/queries.ts +134 -23
  302. package/src/encryption/key-manager.ts +87 -44
  303. package/src/extensions/__tests__/extension-composition.test.ts +3 -2
  304. package/src/extensions/__tests__/platform-client-composed.test.ts +288 -0
  305. package/src/extensions/__tests__/registry.test.ts +4 -1
  306. package/src/extensions/__tests__/tier-truthfulness.test.ts +8 -0
  307. package/src/extensions/caller-guards.ts +8 -3
  308. package/src/extensions/drivers.ts +37 -0
  309. package/src/extensions/guest-token-minting.ts +32 -0
  310. package/src/extensions/identity.ts +6 -4
  311. package/src/extensions/list-read-scope.ts +9 -12
  312. package/src/extensions/registry.ts +77 -3
  313. package/src/index.ts +65 -1
  314. package/src/pipeline/interceptors/auth.ts +27 -0
  315. package/src/pipeline/steps/__tests__/list-page.test.ts +186 -0
  316. package/src/pipeline/steps/__tests__/references.test.ts +2 -0
  317. package/src/pipeline/steps/build-update-state.ts +5 -2
  318. package/src/pipeline/steps/list-page.ts +284 -0
  319. package/src/platformtoken/__tests__/envelope.test.ts +277 -0
  320. package/src/platformtoken/__tests__/key-ring.test.ts +205 -0
  321. package/src/platformtoken/envelope.ts +276 -0
  322. package/src/platformtoken/key-ring.ts +266 -0
  323. package/src/query/activity/handler.ts +57 -78
  324. package/src/runnerauth/__tests__/runner-subject-composed.test.ts +9 -3
  325. package/src/store/README.md +51 -32
  326. package/src/store/__tests__/list-index.measure.test.ts +293 -0
  327. package/src/store/__tests__/list-index.test.ts +328 -0
  328. package/src/store/__tests__/store-contract.ts +346 -3
  329. package/src/store/interface.ts +39 -0
  330. package/src/store/list-index.ts +465 -0
  331. package/src/store/logger.ts +8 -1
  332. package/src/store/postgres/__tests__/list-index-repair.test.ts +133 -0
  333. package/src/store/postgres/__tests__/migrations.test.ts +27 -19
  334. package/src/store/postgres/__tests__/store-contract.test.ts +50 -3
  335. package/src/store/postgres/migrations.ts +69 -7
  336. package/src/store/postgres/store.ts +485 -29
  337. package/src/store/sqlite/__tests__/migrations.test.ts +51 -7
  338. package/src/store/sqlite/__tests__/store-contract.test.ts +70 -16
  339. package/src/store/sqlite/__tests__/support.ts +12 -3
  340. package/src/store/sqlite/migrations.ts +57 -6
  341. package/src/store/sqlite/store.ts +462 -21
@@ -106,6 +106,16 @@ export interface PlatformControllerDeps {
106
106
  * states its installed release and answers exactly as a bundle would.
107
107
  */
108
108
  readonly version: string;
109
+ /**
110
+ * The resolved require-authentication posture (compose.ts: the OSS
111
+ * issuer OR a unit's declaration), reported so a console can tell a
112
+ * server that verifies its callers from one that trusts every request —
113
+ * the features that hand out credentials only a verifying server
114
+ * honours (PlatformClient token minting) are unavailable on the latter.
115
+ * Always set on the wire, true or false: the field's absence is how a
116
+ * client recognises a server that predates it.
117
+ */
118
+ readonly authenticationRequired: boolean;
109
119
  /**
110
120
  * What license this server holds (extensions/license-status.ts). The
111
121
  * composition root installs the built-in `absent` provider when no unit
@@ -136,11 +146,12 @@ export function registerPlatformServices(
136
146
  });
137
147
  }
138
148
 
139
- /** Go GetServerInfo: the server edition and release version. */
149
+ /** Go GetServerInfo: the server edition, release version and authentication posture. */
140
150
  function getServerInfo(deps: PlatformControllerDeps): GetServerInfoOutput {
141
151
  return create(GetServerInfoOutputSchema, {
142
152
  edition: deps.edition,
143
153
  version: deps.version,
154
+ authenticationRequired: deps.authenticationRequired,
144
155
  });
145
156
  }
146
157
 
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Pins the PlatformClient chains over the OSS adapter on sqlite, through the
3
+ * package's own interceptor chain (validation included):
4
+ * - create answers the secret once and a client whose stored hash never
5
+ * leaves: not on create, get, getByReference, listByOrg, update,
6
+ * rotateSecret or delete — while the ROW keeps the hash the mint needs;
7
+ * - update replaces the spec and keeps the credentials, addressed by id
8
+ * or by org-scoped slug;
9
+ * - rotateSecret replaces the secret and fingerprint under the same
10
+ * client_id, and the row's hash follows;
11
+ * - the reserved slug is refused, the two auto-grant rules are refused on
12
+ * the contract, and a system-managed client refuses update, delete and
13
+ * rotate with the cloud's copy;
14
+ * - create and update run the reference rule on `environment_refs`: a
15
+ * bare slug is stored under the client's organization, a missing
16
+ * environment is refused on either chain before anything is stored, and
17
+ * another organization's environment is refused with the rule's one
18
+ * sentence;
19
+ * - listByOrg answers the organization's clients, newest first.
20
+ * Who may read a client is the enforcing lane's conformance suite's.
21
+ */
22
+ import {
23
+ Code,
24
+ ConnectError,
25
+ createClient,
26
+ createRouterTransport,
27
+ } from "@connectrpc/connect";
28
+ import type { Client } from "@connectrpc/connect";
29
+ import { clone, create } from "@bufbuild/protobuf";
30
+ import type { MessageInitShape } from "@bufbuild/protobuf";
31
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
32
+
33
+ import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
34
+ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
35
+ import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
36
+ import { ApiResourceReferenceSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
37
+ import { PlatformClientSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
38
+ import { PlatformClientCommandController } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/command_pb";
39
+ import { PlatformClientQueryController } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/query_pb";
40
+ import { PlatformClientSpecSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/spec_pb";
41
+ import type { PlatformClientSpec } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/spec_pb";
42
+ import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
43
+
44
+ import { silentLogger } from "../../../extensions/__tests__/composed-support.js";
45
+ import {
46
+ SYSTEM_MANAGED_LABEL,
47
+ RESERVED_LABEL_TRUE,
48
+ } from "../../../pipeline/apiresource-labels.js";
49
+ import { buildInterceptorChain } from "../../../pipeline/chain.js";
50
+ import { createInProcessCallerInterceptor } from "../../../pipeline/interceptors/auth.js";
51
+ import { newPermissiveSingleTeamAuthorizer } from "../../../pipeline/steps/authorize.js";
52
+ import {
53
+ missingReferencesMessage,
54
+ notAvailableReferenceMessage,
55
+ referenceTargetKind,
56
+ } from "../../../pipeline/steps/references.js";
57
+ import type { Store } from "../../../store/interface.js";
58
+ import { tempStore } from "../../../store/sqlite/__tests__/support.js";
59
+ import { reservedSlugMessage, systemManagedMessage } from "../constants.js";
60
+ import { registerPlatformClientServices } from "../controller.js";
61
+ import { hashClientSecret } from "../credentials.js";
62
+ import { newResourcePlatformClientStore } from "../resource-store.js";
63
+ import type { PlatformClientStore } from "../store.js";
64
+
65
+ let cleanup: () => void;
66
+ let store: Store;
67
+ let clients: PlatformClientStore;
68
+ let command: Client<typeof PlatformClientCommandController>;
69
+ let query: Client<typeof PlatformClientQueryController>;
70
+
71
+ beforeEach(() => {
72
+ const temp = tempStore();
73
+ cleanup = () => temp.cleanup();
74
+ store = temp.store;
75
+ clients = newResourcePlatformClientStore(temp.store);
76
+ const transport = createRouterTransport(
77
+ (router) =>
78
+ registerPlatformClientServices(router, {
79
+ clients,
80
+ store,
81
+ logger: silentLogger,
82
+ authorizer: newPermissiveSingleTeamAuthorizer(),
83
+ authorizationLifecycle: undefined,
84
+ listReadScope: undefined,
85
+ }),
86
+ {
87
+ router: {
88
+ interceptors: buildInterceptorChain(
89
+ silentLogger,
90
+ createInProcessCallerInterceptor(),
91
+ ),
92
+ },
93
+ },
94
+ );
95
+ command = createClient(PlatformClientCommandController, transport);
96
+ query = createClient(PlatformClientQueryController, transport);
97
+ });
98
+
99
+ afterEach(() => cleanup());
100
+
101
+ function input(
102
+ name: string,
103
+ spec: MessageInitShape<typeof PlatformClientSpecSchema> = {},
104
+ ) {
105
+ return create(PlatformClientSchema, {
106
+ apiVersion: "iam.stigmer.ai/v1",
107
+ kind: "PlatformClient",
108
+ metadata: { name, org: "acme" },
109
+ spec: { autoProvisionAccounts: true, ...spec },
110
+ });
111
+ }
112
+
113
+ async function refusal(promise: Promise<unknown>): Promise<ConnectError> {
114
+ try {
115
+ await promise;
116
+ } catch (error) {
117
+ if (error instanceof ConnectError) return error;
118
+ throw error;
119
+ }
120
+ throw new Error("expected a ConnectError refusal");
121
+ }
122
+
123
+ async function seedEnvironment(org: string, slug: string): Promise<void> {
124
+ const id = `env_${org}_${slug}`;
125
+ await store.saveResource(
126
+ ApiResourceKind.environment,
127
+ id,
128
+ EnvironmentSchema,
129
+ create(EnvironmentSchema, {
130
+ apiVersion: "agentic.stigmer.ai/v1",
131
+ kind: "Environment",
132
+ metadata: {
133
+ id,
134
+ name: slug,
135
+ slug,
136
+ org,
137
+ visibility: ApiResourceVisibility.visibility_org,
138
+ },
139
+ }),
140
+ );
141
+ }
142
+
143
+ function environmentRef(slug: string, org = "") {
144
+ return { org, slug, kind: ApiResourceKind.environment };
145
+ }
146
+
147
+ function environmentTarget() {
148
+ const entry = referenceTargetKind(ApiResourceKind.environment);
149
+ if (entry === undefined)
150
+ throw new Error("environment is not a reference target kind");
151
+ return entry;
152
+ }
153
+
154
+ describe("PlatformClient chains", () => {
155
+ it("shows the secret once and never returns the stored hash, which the row keeps", async () => {
156
+ const created = await command.create(input("Dashboard"));
157
+ const id = created.platformClient?.metadata?.id ?? "";
158
+ expect(id).toMatch(/^pcl_/);
159
+ expect(created.clientSecret).toMatch(/^stgm_cs_/);
160
+ expect(created.platformClient?.spec?.clientSecretHash).toBe("");
161
+ expect(created.platformClient?.spec?.secretFingerprint).toBe(
162
+ created.clientSecret.slice(-6),
163
+ );
164
+
165
+ expect((await clients.findById(id))?.spec?.clientSecretHash).toBe(
166
+ hashClientSecret(created.clientSecret),
167
+ );
168
+ expect((await query.get({ value: id })).spec?.clientSecretHash).toBe("");
169
+ expect(
170
+ (
171
+ await query.getByReference({
172
+ org: "acme",
173
+ slug: "dashboard",
174
+ kind: ApiResourceKind.platform_client,
175
+ })
176
+ ).spec?.clientSecretHash,
177
+ ).toBe("");
178
+ const listed = await query.listByOrg({ org: "acme" });
179
+ expect(listed.entries.map((entry) => entry.spec?.clientSecretHash)).toEqual(
180
+ [""],
181
+ );
182
+ });
183
+
184
+ it("update keeps the credentials, addressed by id or by slug", async () => {
185
+ const created = await command.create(input("Dashboard"));
186
+ const stored = created.platformClient;
187
+ const clientId = stored?.spec?.clientId ?? "";
188
+
189
+ if (stored?.spec === undefined) throw new Error("create answered no spec");
190
+ const byId = clone(PlatformClientSchema, stored);
191
+ byId.spec = clone(PlatformClientSpecSchema, stored.spec);
192
+ byId.spec.clientId = "stgm_cid_forged";
193
+ byId.spec.allowedOrigins = ["https://a.example"];
194
+ const updated = await command.update(byId);
195
+ expect(updated.spec?.clientId).toBe(clientId);
196
+ expect(updated.spec?.allowedOrigins).toEqual(["https://a.example"]);
197
+ expect(updated.spec?.clientSecretHash).toBe("");
198
+
199
+ const bySlug = create(PlatformClientSchema, {
200
+ apiVersion: "iam.stigmer.ai/v1",
201
+ kind: "PlatformClient",
202
+ metadata: { name: "Dashboard", org: "acme", slug: "dashboard" },
203
+ spec: { autoProvisionAccounts: false },
204
+ });
205
+ const viaSlug = await command.update(bySlug);
206
+ expect(viaSlug.metadata?.id).toBe(stored?.metadata?.id);
207
+ expect(viaSlug.spec?.clientId).toBe(clientId);
208
+ expect(
209
+ (await clients.findById(stored?.metadata?.id ?? ""))?.spec
210
+ ?.clientSecretHash,
211
+ ).toBe(hashClientSecret(created.clientSecret));
212
+ });
213
+
214
+ it("rotateSecret replaces the secret under the same client_id, and the row follows", async () => {
215
+ const created = await command.create(input("Dashboard"));
216
+ const id = created.platformClient?.metadata?.id ?? "";
217
+ const rotated = await command.rotateSecret({ value: id });
218
+
219
+ expect(rotated.clientSecret).not.toBe(created.clientSecret);
220
+ expect(rotated.platformClient?.spec?.clientId).toBe(
221
+ created.platformClient?.spec?.clientId,
222
+ );
223
+ expect(rotated.platformClient?.spec?.secretFingerprint).toBe(
224
+ rotated.clientSecret.slice(-6),
225
+ );
226
+ expect(rotated.platformClient?.spec?.clientSecretHash).toBe("");
227
+ expect((await clients.findById(id))?.spec?.clientSecretHash).toBe(
228
+ hashClientSecret(rotated.clientSecret),
229
+ );
230
+ });
231
+
232
+ it("refuses the reserved slug and the two auto-grant rules", async () => {
233
+ const reserved = await refusal(
234
+ command.create(
235
+ create(PlatformClientSchema, {
236
+ apiVersion: "iam.stigmer.ai/v1",
237
+ kind: "PlatformClient",
238
+ metadata: { name: "System Share Client", org: "acme" },
239
+ }),
240
+ ),
241
+ );
242
+ expect(reserved.code).toBe(Code.InvalidArgument);
243
+ expect(reserved.rawMessage).toBe(
244
+ reservedSlugMessage("system-share-client"),
245
+ );
246
+
247
+ const owner = await refusal(
248
+ command.create(
249
+ input("Owner grant", {
250
+ autoGrantOnOrg: true,
251
+ autoGrantRole: IamRole.owner,
252
+ }),
253
+ ),
254
+ );
255
+ expect(owner.code).toBe(Code.InvalidArgument);
256
+
257
+ const noProvision = await refusal(
258
+ command.create(
259
+ input("No provision", {
260
+ autoProvisionAccounts: false,
261
+ autoGrantOnOrg: true,
262
+ }),
263
+ ),
264
+ );
265
+ expect(noProvision.code).toBe(Code.InvalidArgument);
266
+ });
267
+
268
+ it("stores a bare environment slug under the client's organization", async () => {
269
+ await seedEnvironment("acme", "support-secrets");
270
+ const created = await command.create(
271
+ input("Dashboard", {
272
+ environmentRefs: [environmentRef("support-secrets")],
273
+ }),
274
+ );
275
+ const refsOf = (spec: PlatformClientSpec | undefined) =>
276
+ (spec?.environmentRefs ?? []).map((ref) => `${ref.org}/${ref.slug}`);
277
+ expect(refsOf(created.platformClient?.spec)).toEqual([
278
+ "acme/support-secrets",
279
+ ]);
280
+ const id = created.platformClient?.metadata?.id ?? "";
281
+ expect(refsOf((await clients.findById(id))?.spec)).toEqual([
282
+ "acme/support-secrets",
283
+ ]);
284
+ });
285
+
286
+ it("refuses a missing environment on create and on update, storing nothing", async () => {
287
+ const missing = missingReferencesMessage(environmentTarget(), [
288
+ { slug: "ghost", org: "acme" },
289
+ ]);
290
+
291
+ const onCreate = await refusal(
292
+ command.create(
293
+ input("Dashboard", { environmentRefs: [environmentRef("ghost")] }),
294
+ ),
295
+ );
296
+ expect(onCreate.code).toBe(Code.FailedPrecondition);
297
+ expect(onCreate.rawMessage).toBe(missing);
298
+ expect((await query.listByOrg({ org: "acme" })).entries).toEqual([]);
299
+
300
+ const created = await command.create(input("Dashboard"));
301
+ const stored = created.platformClient;
302
+ if (stored?.spec === undefined) throw new Error("create answered no spec");
303
+ const withGhost = clone(PlatformClientSchema, stored);
304
+ withGhost.spec = clone(PlatformClientSpecSchema, stored.spec);
305
+ withGhost.spec.environmentRefs = [
306
+ create(ApiResourceReferenceSchema, environmentRef("ghost")),
307
+ ];
308
+ const onUpdate = await refusal(command.update(withGhost));
309
+ expect(onUpdate.code).toBe(Code.FailedPrecondition);
310
+ expect(onUpdate.rawMessage).toBe(missing);
311
+ expect(
312
+ (await clients.findById(stored.metadata?.id ?? ""))?.spec
313
+ ?.environmentRefs,
314
+ ).toEqual([]);
315
+ });
316
+
317
+ it("refuses another organization's environment with the rule's one sentence", async () => {
318
+ await seedEnvironment("globex", "shared");
319
+ const foreign = await refusal(
320
+ command.create(
321
+ input("Dashboard", {
322
+ environmentRefs: [environmentRef("shared", "globex")],
323
+ }),
324
+ ),
325
+ );
326
+ expect(foreign.code).toBe(Code.FailedPrecondition);
327
+ expect(foreign.rawMessage).toBe(
328
+ notAvailableReferenceMessage(environmentTarget(), {
329
+ kind: ApiResourceKind.environment,
330
+ org: "globex",
331
+ slug: "shared",
332
+ }),
333
+ );
334
+ });
335
+
336
+ it("refuses to update, delete or rotate a system-managed client", async () => {
337
+ await clients.save(
338
+ create(PlatformClientSchema, {
339
+ apiVersion: "iam.stigmer.ai/v1",
340
+ kind: "PlatformClient",
341
+ metadata: {
342
+ id: "pcl_system",
343
+ name: "System Share Client",
344
+ org: "acme",
345
+ slug: "system-share-client",
346
+ labels: { [SYSTEM_MANAGED_LABEL]: RESERVED_LABEL_TRUE },
347
+ },
348
+ spec: { clientId: "stgm_cid_system" },
349
+ }),
350
+ );
351
+ const stored = await clients.findById("pcl_system");
352
+ if (stored === undefined) throw new Error("seeded client missing");
353
+
354
+ const update = await refusal(command.update(stored));
355
+ expect(update.code).toBe(Code.FailedPrecondition);
356
+ expect(update.rawMessage).toBe(systemManagedMessage("updated"));
357
+
358
+ const rotate = await refusal(command.rotateSecret({ value: "pcl_system" }));
359
+ expect(rotate.rawMessage).toBe(systemManagedMessage("rotated"));
360
+
361
+ const remove = await refusal(command.delete({ resourceId: "pcl_system" }));
362
+ expect(remove.rawMessage).toBe(systemManagedMessage("deleted"));
363
+ expect(await clients.findById("pcl_system")).toBeDefined();
364
+ });
365
+
366
+ it("listByOrg answers the organization's clients newest first", async () => {
367
+ for (const [id, seconds] of [
368
+ ["pcl_older", 1_000],
369
+ ["pcl_newer", 2_000],
370
+ ] as const) {
371
+ await clients.save(
372
+ create(PlatformClientSchema, {
373
+ metadata: { id, name: id, org: "acme", slug: id },
374
+ spec: { clientId: `stgm_cid_${id}` },
375
+ status: {
376
+ audit: { specAudit: { createdAt: { seconds: BigInt(seconds) } } },
377
+ },
378
+ }),
379
+ );
380
+ }
381
+ const listed = await query.listByOrg({ org: "acme" });
382
+ expect(listed.entries.map((entry) => entry.metadata?.id)).toEqual([
383
+ "pcl_newer",
384
+ "pcl_older",
385
+ ]);
386
+ expect((await query.listByOrg({ org: "globex" })).entries).toEqual([]);
387
+ });
388
+
389
+ it("delete answers the client without its hash and removes it", async () => {
390
+ const created = await command.create(input("Dashboard"));
391
+ const id = created.platformClient?.metadata?.id ?? "";
392
+
393
+ const deleted = await command.delete({ resourceId: id });
394
+ expect(deleted.spec?.clientSecretHash).toBe("");
395
+ expect(await clients.findById(id)).toBeUndefined();
396
+ });
397
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Pins the PlatformClient credential material byte for byte with what
3
+ * Stigmer Cloud has issued (the secrets integrators hold must keep
4
+ * matching): the prefixes and lengths, the base64url SHA-256 over the whole
5
+ * prefixed secret, the last-six fingerprint, and a constant-time match that
6
+ * refuses a wrong secret and an empty stored hash.
7
+ */
8
+ import { createHash } from "node:crypto";
9
+
10
+ import { describe, expect, it } from "vitest";
11
+
12
+ import {
13
+ generateClientId,
14
+ generateClientSecret,
15
+ hashClientSecret,
16
+ secretFingerprint,
17
+ secretMatchesHash,
18
+ } from "../credentials.js";
19
+
20
+ describe("PlatformClient credentials", () => {
21
+ it("issues a stgm_cid_ id and a stgm_cs_ secret of the cloud's lengths", () => {
22
+ expect(generateClientId()).toMatch(/^stgm_cid_[A-Za-z0-9_-]{43}$/);
23
+ expect(generateClientSecret()).toMatch(/^stgm_cs_[A-Za-z0-9_-]{64}$/);
24
+ });
25
+
26
+ it("hashes the whole prefixed secret with SHA-256, base64url", () => {
27
+ const secret = "stgm_cs_example";
28
+ expect(hashClientSecret(secret)).toBe(
29
+ createHash("sha256").update(secret, "utf8").digest("base64url"),
30
+ );
31
+ expect(hashClientSecret(secret)).toHaveLength(43);
32
+ });
33
+
34
+ it("fingerprints the last six characters, or the whole secret when shorter", () => {
35
+ expect(secretFingerprint("stgm_cs_abcdefgh")).toBe("cdefgh");
36
+ expect(secretFingerprint("tiny")).toBe("tiny");
37
+ });
38
+
39
+ it("matches only the secret that produced the hash, and never an empty stored hash", () => {
40
+ const secret = generateClientSecret();
41
+ const hash = hashClientSecret(secret);
42
+ expect(secretMatchesHash(secret, hash)).toBe(true);
43
+ expect(secretMatchesHash(generateClientSecret(), hash)).toBe(false);
44
+ expect(secretMatchesHash(secret, "")).toBe(false);
45
+ });
46
+ });