@stigmer/server 3.23.1-dev.20260923075205 → 3.25.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 (226) 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 +88 -5
  18. package/dist/boot/compose.js.map +1 -1
  19. package/dist/boot/route-shadowing.d.ts +25 -0
  20. package/dist/boot/route-shadowing.d.ts.map +1 -0
  21. package/dist/boot/route-shadowing.js +17 -0
  22. package/dist/boot/route-shadowing.js.map +1 -0
  23. package/dist/domain/iampolicy/membership.d.ts +6 -1
  24. package/dist/domain/iampolicy/membership.d.ts.map +1 -1
  25. package/dist/domain/iampolicy/membership.js +8 -2
  26. package/dist/domain/iampolicy/membership.js.map +1 -1
  27. package/dist/domain/identityaccount/constants.d.ts +13 -0
  28. package/dist/domain/identityaccount/constants.d.ts.map +1 -1
  29. package/dist/domain/identityaccount/constants.js +17 -0
  30. package/dist/domain/identityaccount/constants.js.map +1 -1
  31. package/dist/domain/identityaccount/controller.d.ts +3 -1
  32. package/dist/domain/identityaccount/controller.d.ts.map +1 -1
  33. package/dist/domain/identityaccount/controller.js +15 -6
  34. package/dist/domain/identityaccount/controller.js.map +1 -1
  35. package/dist/domain/identityaccount/operator.d.ts.map +1 -1
  36. package/dist/domain/identityaccount/operator.js +1 -0
  37. package/dist/domain/identityaccount/operator.js.map +1 -1
  38. package/dist/domain/identityaccount/provisioning.d.ts +18 -0
  39. package/dist/domain/identityaccount/provisioning.d.ts.map +1 -1
  40. package/dist/domain/identityaccount/provisioning.js +1 -0
  41. package/dist/domain/identityaccount/provisioning.js.map +1 -1
  42. package/dist/domain/identityaccount/resource-store.js +9 -5
  43. package/dist/domain/identityaccount/resource-store.js.map +1 -1
  44. package/dist/domain/identityaccount/steps.d.ts +11 -2
  45. package/dist/domain/identityaccount/steps.d.ts.map +1 -1
  46. package/dist/domain/identityaccount/steps.js +42 -4
  47. package/dist/domain/identityaccount/steps.js.map +1 -1
  48. package/dist/domain/identityaccount/store-contract.d.ts.map +1 -1
  49. package/dist/domain/identityaccount/store-contract.js +33 -0
  50. package/dist/domain/identityaccount/store-contract.js.map +1 -1
  51. package/dist/domain/identityaccount/store.d.ts +7 -5
  52. package/dist/domain/identityaccount/store.d.ts.map +1 -1
  53. package/dist/domain/identityaccount/store.js.map +1 -1
  54. package/dist/domain/platform/controller.d.ts +10 -0
  55. package/dist/domain/platform/controller.d.ts.map +1 -1
  56. package/dist/domain/platform/controller.js +2 -1
  57. package/dist/domain/platform/controller.js.map +1 -1
  58. package/dist/domain/platformclient/constants.d.ts +60 -0
  59. package/dist/domain/platformclient/constants.d.ts.map +1 -0
  60. package/dist/domain/platformclient/constants.js +78 -0
  61. package/dist/domain/platformclient/constants.js.map +1 -0
  62. package/dist/domain/platformclient/controller.d.ts +59 -0
  63. package/dist/domain/platformclient/controller.d.ts.map +1 -0
  64. package/dist/domain/platformclient/controller.js +191 -0
  65. package/dist/domain/platformclient/controller.js.map +1 -0
  66. package/dist/domain/platformclient/credentials.d.ts +13 -0
  67. package/dist/domain/platformclient/credentials.d.ts.map +1 -0
  68. package/dist/domain/platformclient/credentials.js +56 -0
  69. package/dist/domain/platformclient/credentials.js.map +1 -0
  70. package/dist/domain/platformclient/mint.d.ts +23 -0
  71. package/dist/domain/platformclient/mint.d.ts.map +1 -0
  72. package/dist/domain/platformclient/mint.js +254 -0
  73. package/dist/domain/platformclient/mint.js.map +1 -0
  74. package/dist/domain/platformclient/origin-guard.d.ts +9 -0
  75. package/dist/domain/platformclient/origin-guard.d.ts.map +1 -0
  76. package/dist/domain/platformclient/origin-guard.js +81 -0
  77. package/dist/domain/platformclient/origin-guard.js.map +1 -0
  78. package/dist/domain/platformclient/resource-store.d.ts +4 -0
  79. package/dist/domain/platformclient/resource-store.d.ts.map +1 -0
  80. package/dist/domain/platformclient/resource-store.js +117 -0
  81. package/dist/domain/platformclient/resource-store.js.map +1 -0
  82. package/dist/domain/platformclient/steps.d.ts +51 -0
  83. package/dist/domain/platformclient/steps.d.ts.map +1 -0
  84. package/dist/domain/platformclient/steps.js +339 -0
  85. package/dist/domain/platformclient/steps.js.map +1 -0
  86. package/dist/domain/platformclient/store-contract.d.ts +8 -0
  87. package/dist/domain/platformclient/store-contract.d.ts.map +1 -0
  88. package/dist/domain/platformclient/store-contract.js +208 -0
  89. package/dist/domain/platformclient/store-contract.js.map +1 -0
  90. package/dist/domain/platformclient/store.d.ts +58 -0
  91. package/dist/domain/platformclient/store.d.ts.map +1 -0
  92. package/dist/domain/platformclient/store.js +13 -0
  93. package/dist/domain/platformclient/store.js.map +1 -0
  94. package/dist/domain/platformclient/system-managed.d.ts +18 -0
  95. package/dist/domain/platformclient/system-managed.d.ts.map +1 -0
  96. package/dist/domain/platformclient/system-managed.js +80 -0
  97. package/dist/domain/platformclient/system-managed.js.map +1 -0
  98. package/dist/domain/platformclient/token-controller.d.ts +11 -0
  99. package/dist/domain/platformclient/token-controller.d.ts.map +1 -0
  100. package/dist/domain/platformclient/token-controller.js +32 -0
  101. package/dist/domain/platformclient/token-controller.js.map +1 -0
  102. package/dist/domain/platformclient/verifier.d.ts +10 -0
  103. package/dist/domain/platformclient/verifier.d.ts.map +1 -0
  104. package/dist/domain/platformclient/verifier.js +76 -0
  105. package/dist/domain/platformclient/verifier.js.map +1 -0
  106. package/dist/domain/workflow/agent-call-references.d.ts +60 -33
  107. package/dist/domain/workflow/agent-call-references.d.ts.map +1 -1
  108. package/dist/domain/workflow/agent-call-references.js +43 -23
  109. package/dist/domain/workflow/agent-call-references.js.map +1 -1
  110. package/dist/domain/workflow/registry/data/task-kind-registry.json +2 -2
  111. package/dist/encryption/key-manager.d.ts +20 -3
  112. package/dist/encryption/key-manager.d.ts.map +1 -1
  113. package/dist/encryption/key-manager.js +64 -42
  114. package/dist/encryption/key-manager.js.map +1 -1
  115. package/dist/extensions/caller-guards.d.ts +8 -3
  116. package/dist/extensions/caller-guards.d.ts.map +1 -1
  117. package/dist/extensions/drivers.d.ts +37 -0
  118. package/dist/extensions/drivers.d.ts.map +1 -1
  119. package/dist/extensions/guest-token-minting.d.ts +26 -0
  120. package/dist/extensions/guest-token-minting.d.ts.map +1 -0
  121. package/dist/extensions/guest-token-minting.js +2 -0
  122. package/dist/extensions/guest-token-minting.js.map +1 -0
  123. package/dist/extensions/identity.d.ts +6 -4
  124. package/dist/extensions/identity.d.ts.map +1 -1
  125. package/dist/extensions/identity.js.map +1 -1
  126. package/dist/extensions/registry.d.ts +30 -1
  127. package/dist/extensions/registry.d.ts.map +1 -1
  128. package/dist/extensions/registry.js +34 -1
  129. package/dist/extensions/registry.js.map +1 -1
  130. package/dist/index.d.ts +13 -1
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +6 -0
  133. package/dist/index.js.map +1 -1
  134. package/dist/pipeline/apiresource-labels.d.ts +10 -9
  135. package/dist/pipeline/apiresource-labels.d.ts.map +1 -1
  136. package/dist/pipeline/apiresource-labels.js +10 -9
  137. package/dist/pipeline/apiresource-labels.js.map +1 -1
  138. package/dist/pipeline/interceptors/auth.d.ts +14 -0
  139. package/dist/pipeline/interceptors/auth.d.ts.map +1 -1
  140. package/dist/pipeline/interceptors/auth.js +24 -0
  141. package/dist/pipeline/interceptors/auth.js.map +1 -1
  142. package/dist/pipeline/steps/build-update-state.d.ts +11 -1
  143. package/dist/pipeline/steps/build-update-state.d.ts.map +1 -1
  144. package/dist/pipeline/steps/build-update-state.js +5 -2
  145. package/dist/pipeline/steps/build-update-state.js.map +1 -1
  146. package/dist/pipeline/steps/references.d.ts +30 -4
  147. package/dist/pipeline/steps/references.d.ts.map +1 -1
  148. package/dist/pipeline/steps/references.js +13 -1
  149. package/dist/pipeline/steps/references.js.map +1 -1
  150. package/dist/platformtoken/envelope.d.ts +100 -0
  151. package/dist/platformtoken/envelope.d.ts.map +1 -0
  152. package/dist/platformtoken/envelope.js +184 -0
  153. package/dist/platformtoken/envelope.js.map +1 -0
  154. package/dist/platformtoken/key-ring.d.ts +85 -0
  155. package/dist/platformtoken/key-ring.d.ts.map +1 -0
  156. package/dist/platformtoken/key-ring.js +164 -0
  157. package/dist/platformtoken/key-ring.js.map +1 -0
  158. package/package.json +6 -6
  159. package/src/authorization/README.md +1 -1
  160. package/src/authorization/__tests__/authorizer.test.ts +6 -2
  161. package/src/authorization/__tests__/wire-permissions.test.ts +1 -0
  162. package/src/authorization/authorizer.ts +11 -4
  163. package/src/authorization/model/__tests__/registry.test.ts +8 -1
  164. package/src/authorization/model/index.ts +6 -4
  165. package/src/authorization/model/oauth_app.ts +3 -2
  166. package/src/authorization/model/platform_client.ts +43 -0
  167. package/src/boot/__tests__/auth-enabled-composition.test.ts +2 -1
  168. package/src/boot/__tests__/route-shadowing.test.ts +67 -0
  169. package/src/boot/compose.ts +90 -5
  170. package/src/boot/route-shadowing.ts +47 -0
  171. package/src/domain/iampolicy/__tests__/membership.test.ts +7 -0
  172. package/src/domain/iampolicy/membership.ts +8 -2
  173. package/src/domain/identityaccount/__tests__/platform-client-accounts.test.ts +124 -0
  174. package/src/domain/identityaccount/__tests__/resource-store.test.ts +1 -0
  175. package/src/domain/identityaccount/constants.ts +20 -0
  176. package/src/domain/identityaccount/controller.ts +16 -4
  177. package/src/domain/identityaccount/operator.ts +1 -0
  178. package/src/domain/identityaccount/provisioning.ts +17 -0
  179. package/src/domain/identityaccount/resource-store.ts +9 -5
  180. package/src/domain/identityaccount/steps.ts +58 -3
  181. package/src/domain/identityaccount/store-contract.ts +51 -0
  182. package/src/domain/identityaccount/store.ts +7 -5
  183. package/src/domain/platform/__tests__/platform.test.ts +7 -1
  184. package/src/domain/platform/controller.ts +12 -1
  185. package/src/domain/platformclient/__tests__/controller.test.ts +397 -0
  186. package/src/domain/platformclient/__tests__/credentials.test.ts +46 -0
  187. package/src/domain/platformclient/__tests__/mint.test.ts +415 -0
  188. package/src/domain/platformclient/__tests__/resource-store.test.ts +140 -0
  189. package/src/domain/platformclient/__tests__/system-managed.test.ts +101 -0
  190. package/src/domain/platformclient/__tests__/verifier-and-guard.test.ts +207 -0
  191. package/src/domain/platformclient/constants.ts +120 -0
  192. package/src/domain/platformclient/controller.ts +449 -0
  193. package/src/domain/platformclient/credentials.ts +68 -0
  194. package/src/domain/platformclient/mint.ts +412 -0
  195. package/src/domain/platformclient/origin-guard.ts +117 -0
  196. package/src/domain/platformclient/resource-store.ts +150 -0
  197. package/src/domain/platformclient/steps.ts +472 -0
  198. package/src/domain/platformclient/store-contract.ts +318 -0
  199. package/src/domain/platformclient/store.ts +63 -0
  200. package/src/domain/platformclient/system-managed.ts +115 -0
  201. package/src/domain/platformclient/token-controller.ts +49 -0
  202. package/src/domain/platformclient/verifier.ts +106 -0
  203. package/src/domain/workflow/__tests__/agent-call-references.test.ts +178 -27
  204. package/src/domain/workflow/agent-call-references.ts +95 -48
  205. package/src/domain/workflow/registry/data/task-kind-registry.json +2 -2
  206. package/src/encryption/key-manager.ts +87 -44
  207. package/src/extensions/__tests__/extension-composition.test.ts +3 -2
  208. package/src/extensions/__tests__/platform-client-composed.test.ts +288 -0
  209. package/src/extensions/__tests__/registry.test.ts +4 -1
  210. package/src/extensions/__tests__/tier-truthfulness.test.ts +8 -0
  211. package/src/extensions/caller-guards.ts +8 -3
  212. package/src/extensions/drivers.ts +37 -0
  213. package/src/extensions/guest-token-minting.ts +32 -0
  214. package/src/extensions/identity.ts +6 -4
  215. package/src/extensions/registry.ts +77 -3
  216. package/src/index.ts +55 -0
  217. package/src/pipeline/apiresource-labels.ts +10 -9
  218. package/src/pipeline/interceptors/auth.ts +27 -0
  219. package/src/pipeline/steps/__tests__/references.test.ts +37 -1
  220. package/src/pipeline/steps/build-update-state.ts +5 -2
  221. package/src/pipeline/steps/references.ts +49 -5
  222. package/src/platformtoken/__tests__/envelope.test.ts +277 -0
  223. package/src/platformtoken/__tests__/key-ring.test.ts +205 -0
  224. package/src/platformtoken/envelope.ts +276 -0
  225. package/src/platformtoken/key-ring.ts +266 -0
  226. package/src/runnerauth/__tests__/runner-subject-composed.test.ts +9 -3
@@ -49,9 +49,12 @@ import { BUILT_IN_STORAGE_TYPES } from "../artifactstorage/artifact-storage.js";
49
49
  import type { ChannelRuntime } from "../domain/agentchannel/channel-runtime.js";
50
50
  import type { IdentityAccountStore } from "../domain/identityaccount/store.js";
51
51
  import type { IamPolicyStore } from "../domain/iampolicy/store.js";
52
+ import type { PlatformClientStore } from "../domain/platformclient/store.js";
52
53
  import type { SecretCodec } from "../encryption/codec.js";
53
54
  import { V1_VERSION } from "../encryption/v1-codec.js";
55
+ import type { PlatformTokenKeyRing } from "../platformtoken/key-ring.js";
54
56
  import type { AuthorizationQueryEngine } from "./authorization-queries.js";
57
+ import type { GuestTokenMinting } from "./guest-token-minting.js";
55
58
  import type { IdentityFederation } from "./identity-federation.js";
56
59
  import type { LicenseStatusProvider } from "./license-status.js";
57
60
  import type { OutboundEgressPolicy } from "./outbound-egress.js";
@@ -88,6 +91,17 @@ import type {
88
91
  */
89
92
  export type ExtensionServiceRegistration = (router: ConnectRouter) => void;
90
93
 
94
+ /**
95
+ * A service registration with the unit that contributed it, so the routes
96
+ * stage can name the unit when a registration shadows a core route
97
+ * (compose.ts refuses that at boot: Connect's router keys handlers by
98
+ * request path and the last registration wins, silently).
99
+ */
100
+ export interface ResolvedServiceRegistration {
101
+ readonly unit: string;
102
+ readonly register: ExtensionServiceRegistration;
103
+ }
104
+
91
105
  /**
92
106
  * One named extension unit — a package's whole contribution to the server
93
107
  * composition. All fields but `name` are optional; an omitted field is
@@ -191,7 +205,7 @@ export interface ResolvedExtensions {
191
205
  readonly statusObservers: ReadonlyArray<AgentExecutionStatusObserver>;
192
206
  readonly responseDecorators: ReadonlyArray<AgentExecutionResponseDecorator>;
193
207
  readonly drivers: ResolvedExtensionDrivers;
194
- readonly services: ReadonlyArray<ExtensionServiceRegistration>;
208
+ readonly services: ReadonlyArray<ResolvedServiceRegistration>;
195
209
  readonly workers: ReadonlyArray<WorkerFactory>;
196
210
  }
197
211
 
@@ -284,6 +298,22 @@ export interface ResolvedExtensionDrivers {
284
298
  * source's relaxed posture (only the link-local range refused).
285
299
  */
286
300
  readonly outboundEgress: OutboundEgressPolicy | undefined;
301
+ /**
302
+ * The platform-client store driver — undefined = compose.ts installs the
303
+ * OSS adapter over the generic Store.
304
+ */
305
+ readonly platformClientStore: PlatformClientStore | undefined;
306
+ /**
307
+ * The platform-token key ring — undefined = compose.ts composes open
308
+ * source's ring under an authentication posture, and refuses to boot a
309
+ * non-open-source edition that supplied none.
310
+ */
311
+ readonly platformTokenKeys: PlatformTokenKeyRing | undefined;
312
+ /**
313
+ * The guest-token capability — undefined = `mintGuestToken` refuses
314
+ * UNIMPLEMENTED with the edition sentence.
315
+ */
316
+ readonly guestTokenMinting: GuestTokenMinting | undefined;
287
317
  }
288
318
 
289
319
  /**
@@ -301,7 +331,7 @@ export function resolveExtensions(
301
331
  const gateSteps = new Map<string, ReadonlyArray<PipelineStep<DescMessage>>>();
302
332
  const statusObservers: AgentExecutionStatusObserver[] = [];
303
333
  const responseDecorators: AgentExecutionResponseDecorator[] = [];
304
- const services: ExtensionServiceRegistration[] = [];
334
+ const services: ResolvedServiceRegistration[] = [];
305
335
  const workers: WorkerFactory[] = [];
306
336
 
307
337
  let edition: ServerEdition | undefined;
@@ -341,6 +371,12 @@ export function resolveExtensions(
341
371
  let licenseStatusDeclaredBy: string | undefined;
342
372
  let outboundEgress: OutboundEgressPolicy | undefined;
343
373
  let outboundEgressDeclaredBy: string | undefined;
374
+ let platformClientStore: PlatformClientStore | undefined;
375
+ let platformClientStoreDeclaredBy: string | undefined;
376
+ let platformTokenKeys: PlatformTokenKeyRing | undefined;
377
+ let platformTokenKeysDeclaredBy: string | undefined;
378
+ let guestTokenMinting: GuestTokenMinting | undefined;
379
+ let guestTokenMintingDeclaredBy: string | undefined;
344
380
  const artifactStorageDrivers = new Map<
345
381
  string,
346
382
  ArtifactStorageDriverFactory
@@ -552,6 +588,36 @@ export function resolveExtensions(
552
588
  outboundEgressDeclaredBy = unit.name;
553
589
  }
554
590
 
591
+ if (unit.drivers?.platformClientStore !== undefined) {
592
+ if (platformClientStoreDeclaredBy !== undefined) {
593
+ throw new Error(
594
+ `extension '${unit.name}' registers a PlatformClientStore, but '${platformClientStoreDeclaredBy}' already did — exactly one may be composed`,
595
+ );
596
+ }
597
+ platformClientStore = unit.drivers.platformClientStore;
598
+ platformClientStoreDeclaredBy = unit.name;
599
+ }
600
+
601
+ if (unit.drivers?.platformTokenKeys !== undefined) {
602
+ if (platformTokenKeysDeclaredBy !== undefined) {
603
+ throw new Error(
604
+ `extension '${unit.name}' registers a PlatformTokenKeyRing, but '${platformTokenKeysDeclaredBy}' already did — exactly one may be composed`,
605
+ );
606
+ }
607
+ platformTokenKeys = unit.drivers.platformTokenKeys;
608
+ platformTokenKeysDeclaredBy = unit.name;
609
+ }
610
+
611
+ if (unit.drivers?.guestTokenMinting !== undefined) {
612
+ if (guestTokenMintingDeclaredBy !== undefined) {
613
+ throw new Error(
614
+ `extension '${unit.name}' registers a GuestTokenMinting capability, but '${guestTokenMintingDeclaredBy}' already did — exactly one may be composed`,
615
+ );
616
+ }
617
+ guestTokenMinting = unit.drivers.guestTokenMinting;
618
+ guestTokenMintingDeclaredBy = unit.name;
619
+ }
620
+
555
621
  if (unit.drivers?.artifactStorageDrivers !== undefined) {
556
622
  for (const [name, factory] of unit.drivers.artifactStorageDrivers) {
557
623
  if ((BUILT_IN_STORAGE_TYPES as ReadonlyArray<string>).includes(name)) {
@@ -643,7 +709,12 @@ export function resolveExtensions(
643
709
  responseDecorators.push(
644
710
  ...(unit.statusTransitionHooks?.responseDecorators ?? []),
645
711
  );
646
- services.push(...(unit.services ?? []));
712
+ services.push(
713
+ ...(unit.services ?? []).map((register) => ({
714
+ unit: unit.name,
715
+ register,
716
+ })),
717
+ );
647
718
  workers.push(...(unit.workers ?? []));
648
719
  }
649
720
 
@@ -679,6 +750,9 @@ export function resolveExtensions(
679
750
  authorizationQueries,
680
751
  licenseStatus,
681
752
  outboundEgress,
753
+ platformClientStore,
754
+ platformTokenKeys,
755
+ guestTokenMinting,
682
756
  },
683
757
  services,
684
758
  workers,
package/src/index.ts CHANGED
@@ -59,6 +59,7 @@ export type {
59
59
  export type {
60
60
  ExtensionServiceRegistration,
61
61
  ResolvedExtensions,
62
+ ResolvedServiceRegistration,
62
63
  ServerExtension,
63
64
  } from "./extensions/registry.js";
64
65
  export type {
@@ -166,6 +167,60 @@ export type { AuthorizationQueryEngine } from "./extensions/authorization-querie
166
167
  export type { IdentityFederation } from "./extensions/identity-federation.js";
167
168
  export type { AccountsBySubject } from "./domain/identityaccount/resolve.js";
168
169
  export { identityIdForSubject } from "./domain/identityaccount/resolve.js";
170
+ // The PlatformClient seams: the store PORT a composition drives the domain
171
+ // through (drivers.platformClientStore; a driver throws
172
+ // DuplicatePlatformClientError for a held id, slug or client_id) and its
173
+ // vitest-free contract kit; the guest-token capability
174
+ // (drivers.guestTokenMinting — the one token method only an edition that
175
+ // hosts shared-agent pages serves); and the builder of the system-managed
176
+ // client such an edition keeps per organization under the reserved slug,
177
+ // so the tokens it signs for guests and schedule fires name a real client.
178
+ export type { PlatformClientStore } from "./domain/platformclient/store.js";
179
+ export { DuplicatePlatformClientError } from "./domain/platformclient/store.js";
180
+ export type {
181
+ PlatformClientStoreContractCase,
182
+ PlatformClientStoreContractFixture,
183
+ } from "./domain/platformclient/store-contract.js";
184
+ export { platformClientStoreContract } from "./domain/platformclient/store-contract.js";
185
+ export type { GuestTokenMinting } from "./extensions/guest-token-minting.js";
186
+ export type { SystemManagedPlatformClientInput } from "./domain/platformclient/system-managed.js";
187
+ export { newSystemManagedPlatformClient } from "./domain/platformclient/system-managed.js";
188
+ export { SYSTEM_SHARE_CLIENT_SLUG } from "./domain/platformclient/constants.js";
189
+ // The platform-token envelope and its key ring: the RS256 JWT every token
190
+ // the server signs for itself rides. A composition supplies its ring
191
+ // (drivers.platformTokenKeys, built from its PEMs), signs its own typed
192
+ // lanes through the envelope — each with its own lifetime when it is not
193
+ // the ring's default, the expiry handed back — and verifies them with the
194
+ // same function and refusal copy open source's user-token lane uses.
195
+ export type {
196
+ PlatformTokenClaimValue,
197
+ PlatformTokenRefusal,
198
+ PlatformTokenSigningOptions,
199
+ PlatformTokenVerification,
200
+ SignedPlatformToken,
201
+ VerifiedPlatformToken,
202
+ } from "./platformtoken/envelope.js";
203
+ export {
204
+ PLATFORM_TOKEN_ISSUER,
205
+ PLATFORM_TOKEN_REFUSAL_MESSAGES,
206
+ TOKEN_TYPE_CLAIM,
207
+ decodeVerifiedPlatformTokenPayload,
208
+ platformTokenRefusalError,
209
+ signPlatformToken,
210
+ stringClaim,
211
+ verifyPlatformToken,
212
+ } from "./platformtoken/envelope.js";
213
+ export type {
214
+ PlatformTokenKeyMaterial,
215
+ PlatformTokenKeyRing,
216
+ PlatformTokenSigner,
217
+ SigningPlatformTokenKeyRing,
218
+ } from "./platformtoken/key-ring.js";
219
+ export {
220
+ DEFAULT_PLATFORM_TOKEN_TTL_SECONDS,
221
+ canSign,
222
+ platformTokenKeyRingFromPem,
223
+ } from "./platformtoken/key-ring.js";
169
224
  // The built-in authorizer's model seams: the kind
170
225
  // declarations (transcripts of the cloud's `.fga` files), the evaluator,
171
226
  // and the OpenFGA store-test kit — exported so the cloud's drift test
@@ -45,15 +45,16 @@ export const SYSTEM_MANAGED_LABEL = `${RESERVED_LABEL_PREFIX}system-managed`;
45
45
 
46
46
  /**
47
47
  * Marks system content: a resource the platform itself seeded rather than a
48
- * user authored — the system organization, the default agent, the
49
- * platform's own MCP servers. The label predates this module (the retired
50
- * seedpack wrote it on every row it applied; the official plugins' overlays
51
- * carry it on the resources they replace) and has one server-side meaning:
52
- * a plugin push may adopt a system-content row that holds a slug the plugin
53
- * needs, keeping the row's id and everything bound to it, instead of
54
- * refusing the slug as taken. It never marks a user's resource, so a user's
55
- * row is never adopted (domain/plugin/members.ts, judgeSlug). The CLI names
56
- * the same bytes as SYSTEM_ORG_LABEL for the organization it ensures.
48
+ * user authored. The retired seedpack wrote it on every row it applied, and
49
+ * CLIs before the bootstrap stopped labelling stamped it on the `stigmer`
50
+ * organization they created. A plugin overlay may still carry it on a
51
+ * member (a reserved label, so the push charges the platform permission for
52
+ * it). It has one server-side meaning: a plugin
53
+ * push may adopt a system-content row that holds a slug the plugin needs,
54
+ * keeping the row's id and everything bound to it, instead of refusing the
55
+ * slug as taken. It never marks a user's resource, so a user's row is never
56
+ * adopted (domain/plugin/members.ts, judgeSlug); on an organization it is
57
+ * inert.
57
58
  */
58
59
  export const SYSTEM_LABEL = `${RESERVED_LABEL_PREFIX}system`;
59
60
 
@@ -216,6 +216,33 @@ export function trustedLocalIdentityFor(operator: {
216
216
  };
217
217
  }
218
218
 
219
+ /**
220
+ * The server acting for a principal it has authenticated some other way —
221
+ * the one construction of an `internal` caller outside the in-process
222
+ * transport's own stamp. Its consumer is the PlatformClient mint
223
+ * (domain/platformclient/mint.ts): the request is public and tokenless
224
+ * (so the chain stamped the trusted-local operator), the client proved
225
+ * itself with its secret, and the end-user account and role the mint
226
+ * creates must name the CLIENT as their actor, never the operator.
227
+ * `internal` because the server is the one acting — the create path's
228
+ * Authorize skips it, as for every server-composed request — and never
229
+ * reachable from the wire: no verifier produces it, and the serving
230
+ * chassis overwrites position 1 on every request.
231
+ */
232
+ export function serverActingFor(principalId: string): CallerIdentity {
233
+ if (principalId === "") {
234
+ throw new Error(
235
+ "the server acts for a named principal — refusing to build an internal caller with none",
236
+ );
237
+ }
238
+ return {
239
+ identityId: principalId,
240
+ callerClass: "internal",
241
+ issuer: "",
242
+ rawToken: "",
243
+ };
244
+ }
245
+
219
246
  /**
220
247
  * The serving chain's position-1 identity source: walks the composed
221
248
  * verifiers in order (OSS entries first, extension entries after, in
@@ -5,7 +5,8 @@
5
5
  * task config, through the `reference_kind` field option); each clause on
6
6
  * each kind — no org refused, a same-organization target must exist, the
7
7
  * floor applies to the kinds the run reads as the person and to no other,
8
- * a cross-organization target is admitted only at platform visibility with
8
+ * a relative reference's floor is capped at org and its target must still
9
+ * exist in the resource's organization, a cross-organization target is admitted only at platform visibility with
9
10
  * ONE sentence whether it is missing or merely not shared; the MCP-server
10
11
  * copy that predates the rule is byte-identical and its siblings take its
11
12
  * shape; the walk reads a reference's kind from its field, not from the
@@ -38,6 +39,7 @@ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/
38
39
  import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
39
40
  import { reference_kind } from "@stigmer/protos/ai/stigmer/commons/apiresource/field_options_pb";
40
41
  import { UpdateVisibilityInputSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
42
+ import { PlatformClientSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
41
43
 
42
44
  import type { Store } from "../../../store/interface.js";
43
45
  import { SqliteStore } from "../../../store/sqlite/store.js";
@@ -96,6 +98,7 @@ const SCHEMAS_UNDER_THE_RULE: ReadonlyArray<DescMessage> = [
96
98
  AgentExecutionSchema,
97
99
  WorkflowExecutionSchema,
98
100
  ExecutionContextSchema,
101
+ PlatformClientSchema,
99
102
  AgentCallTaskConfigSchema,
100
103
  ];
101
104
 
@@ -289,6 +292,39 @@ describe("checkReference", () => {
289
292
  ).toEqual({ kind: "below-floor", targetVisibility: V.visibility_org });
290
293
  });
291
294
 
295
+ it("(ii) a relative reference's floor is capped at org: a platform resource may run with its organization's org-visible target, never a private one, and the target must exist", () => {
296
+ const relative = (slug: string): SpecReference => ({
297
+ ...ref(K.mcp_server, "acme", slug),
298
+ resolvesIn: "running-organization",
299
+ });
300
+ const privateTarget: SpecReference = {
301
+ ...ref(K.skill, "acme", "my-skill"),
302
+ resolvesIn: "running-organization",
303
+ };
304
+ const ACME_PLATFORM: ReferenceParent = {
305
+ org: "acme",
306
+ visibility: V.visibility_platform,
307
+ };
308
+ expect(checkReference(targets, ACME_PLATFORM, relative("github"))).toEqual({
309
+ kind: "ok",
310
+ });
311
+ expect(checkReference(targets, ACME_PLATFORM, privateTarget)).toEqual({
312
+ kind: "below-floor",
313
+ targetVisibility: V.visibility_private,
314
+ });
315
+ expect(checkReference(targets, ACME_PLATFORM, relative("ghost"))).toEqual({
316
+ kind: "missing",
317
+ });
318
+ // Below the cap the resource's own level decides, as for any reference.
319
+ expect(checkReference(targets, ACME_ORG, privateTarget)).toEqual({
320
+ kind: "below-floor",
321
+ targetVisibility: V.visibility_private,
322
+ });
323
+ expect(checkReference(targets, ACME_PRIVATE, privateTarget)).toEqual({
324
+ kind: "ok",
325
+ });
326
+ });
327
+
292
328
  it("(ii) the floor does not apply to what the server resolves on the run's behalf", () => {
293
329
  // An org-visible instance over a private personal environment: the
294
330
  // ordinary shape of a personal instance, admitted.
@@ -115,9 +115,12 @@ function copyStatusFromExisting(
115
115
  /**
116
116
  * Go updateAuditFieldsReflect: spec_audit keeps the existing creation
117
117
  * identity with a fresh update stamp; status_audit is fully current (the
118
- * status was rebuilt); both carry event "updated".
118
+ * status was rebuilt); both carry event "updated". Exported for the one
119
+ * write that changes a stored resource outside this step — a
120
+ * PlatformClient secret rotation (domain/platformclient/steps.ts) — so an
121
+ * update's audit has one shape whichever step writes it.
119
122
  */
120
- function updateAuditFields(
123
+ export function updateAuditFields(
121
124
  schema: DescMessage,
122
125
  resource: Message,
123
126
  existing: Message,
@@ -28,7 +28,8 @@
28
28
  * channel apps are resolved by the server on the run's behalf and
29
29
  * never read by the person, so their level is not a leak the floor
30
30
  * closes, and an org-visible instance may hold a private personal
31
- * environment as it always has.
31
+ * environment as it always has. A RELATIVE reference (below) is
32
+ * compared against the resource's level capped at org.
32
33
  * (iii) Another organization: the target must exist AND be
33
34
  * platform-visible, answered with ONE sentence that does not say
34
35
  * which failed. The caller has no standing to learn what another
@@ -48,14 +49,32 @@
48
49
  * the rule must agree with the runtime. A reference with an empty slug is
49
50
  * unset and skipped (the field-level CEL rules own "required").
50
51
  *
52
+ * A reference is RELATIVE when it names no organization of its own and
53
+ * the runtime resolves it in the organization the resource RUNS in, not
54
+ * the one it is stored in. Exactly one exists: a workflow `agent_call`
55
+ * task's bare slug (domain/workflow/agent-call-references.ts), which the
56
+ * runner resolves in the execution's organization; any workflow a person
57
+ * may execute can run from another organization by id. The rule judges
58
+ * it in the resource's own organization — the one its author can see, so
59
+ * the target must exist there — and caps its floor at org: only the
60
+ * running organization's own people ever read a target through it, and
61
+ * they can read an org-visible one. The cap changes nothing for a
62
+ * private or org resource and lets a platform-visible workflow call its
63
+ * organization's org-visible agent. A value fixed only at run (an
64
+ * `agent_call` agent holding a runtime expression) is not a reference at
65
+ * all: the collector yields nothing for it, and the run reads the
66
+ * resolved target as the person the run acts as, as it reads every
67
+ * id-bound binding.
68
+ *
51
69
  * The floor has a second door. Raising a resource's level through
52
70
  * updateVisibility could open the same gap a create cannot, so
53
71
  * GuardReferenceFloorOnEscalation runs on the two chains whose rows carry
54
72
  * run-read references (agent, workflow) after ValidateVisibilityUpdate:
55
73
  * when the requested level is above the stored one, every run-read
56
- * reference the STORED row carries must be at least the requested level,
57
- * judged by the same function over the same collectors, else the
58
- * escalation is refused naming the dependencies. Only the floor is asked at
74
+ * reference the STORED row carries must be at least the requested level
75
+ * (a relative one, at least the requested level capped at org), judged by
76
+ * the same function over the same collectors, else the escalation is
77
+ * refused naming the dependencies. Only the floor is asked at
59
78
  * that door — a dependency that has since left or stopped being shared was
60
79
  * judged when the row was written and is the run's to refuse.
61
80
  *
@@ -182,6 +201,13 @@ export interface SpecReference {
182
201
  readonly kind: ApiResourceKind;
183
202
  readonly slug: string;
184
203
  readonly org: string;
204
+ /**
205
+ * Present only on a RELATIVE reference (the module header): `org` is the
206
+ * resource's own, filled by the collector, and the runtime resolves the
207
+ * slug in the organization the resource runs in. The generic walker
208
+ * never sets it; only the `agent_call` collector does.
209
+ */
210
+ readonly resolvesIn?: "running-organization";
185
211
  }
186
212
 
187
213
  /** The resource the references belong to, as the rule needs it. */
@@ -279,13 +305,31 @@ export function checkReference(
279
305
  const entry = referenceTargetKind(ref.kind);
280
306
  if (
281
307
  entry?.readByRun === true &&
282
- visibilityRank(target) < visibilityRank(parent.visibility)
308
+ visibilityRank(target) < visibilityRank(floorOf(parent, ref))
283
309
  ) {
284
310
  return { kind: "below-floor", targetVisibility: target };
285
311
  }
286
312
  return { kind: "ok" };
287
313
  }
288
314
 
315
+ /**
316
+ * The level a same-organization target must reach: the resource's own,
317
+ * capped at org for a relative reference (the module header).
318
+ */
319
+ function floorOf(
320
+ parent: ReferenceParent,
321
+ ref: SpecReference,
322
+ ): ApiResourceVisibility {
323
+ if (
324
+ ref.resolvesIn === "running-organization" &&
325
+ visibilityRank(parent.visibility) >
326
+ visibilityRank(ApiResourceVisibility.visibility_org)
327
+ ) {
328
+ return ApiResourceVisibility.visibility_org;
329
+ }
330
+ return parent.visibility;
331
+ }
332
+
289
333
  /**
290
334
  * The order the floor compares on: private < org < platform, an unset
291
335
  * level reading as private (the level a row with no config holds). The