@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.
- package/dist/authorization/authorizer.d.ts +10 -4
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +11 -4
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/model/index.d.ts +4 -4
- package/dist/authorization/model/index.d.ts.map +1 -1
- package/dist/authorization/model/index.js +2 -0
- package/dist/authorization/model/index.js.map +1 -1
- package/dist/authorization/model/oauth_app.d.ts.map +1 -1
- package/dist/authorization/model/oauth_app.js +3 -2
- package/dist/authorization/model/oauth_app.js.map +1 -1
- package/dist/authorization/model/platform_client.d.ts +2 -0
- package/dist/authorization/model/platform_client.d.ts.map +1 -0
- package/dist/authorization/model/platform_client.js +31 -0
- package/dist/authorization/model/platform_client.js.map +1 -0
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +99 -13
- package/dist/boot/compose.js.map +1 -1
- package/dist/boot/list-indexes.d.ts +3 -0
- package/dist/boot/list-indexes.d.ts.map +1 -0
- package/dist/boot/list-indexes.js +24 -0
- package/dist/boot/list-indexes.js.map +1 -0
- package/dist/boot/route-shadowing.d.ts +25 -0
- package/dist/boot/route-shadowing.d.ts.map +1 -0
- package/dist/boot/route-shadowing.js +17 -0
- package/dist/boot/route-shadowing.js.map +1 -0
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +6 -8
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentexecution/list-index.d.ts +2 -0
- package/dist/domain/agentexecution/list-index.d.ts.map +1 -0
- package/dist/domain/agentexecution/list-index.js +23 -0
- package/dist/domain/agentexecution/list-index.js.map +1 -0
- package/dist/domain/agentexecution/steps.d.ts +17 -33
- package/dist/domain/agentexecution/steps.d.ts.map +1 -1
- package/dist/domain/agentexecution/steps.js +94 -95
- package/dist/domain/agentexecution/steps.js.map +1 -1
- package/dist/domain/agentexecution/usage.d.ts.map +1 -1
- package/dist/domain/agentexecution/usage.js +37 -14
- package/dist/domain/agentexecution/usage.js.map +1 -1
- package/dist/domain/artifact/controller.d.ts.map +1 -1
- package/dist/domain/artifact/controller.js +27 -15
- package/dist/domain/artifact/controller.js.map +1 -1
- package/dist/domain/artifact/list-index.d.ts +2 -0
- package/dist/domain/artifact/list-index.d.ts.map +1 -0
- package/dist/domain/artifact/list-index.js +21 -0
- package/dist/domain/artifact/list-index.js.map +1 -0
- package/dist/domain/iampolicy/membership.d.ts +6 -1
- package/dist/domain/iampolicy/membership.d.ts.map +1 -1
- package/dist/domain/iampolicy/membership.js +8 -2
- package/dist/domain/iampolicy/membership.js.map +1 -1
- package/dist/domain/identityaccount/constants.d.ts +13 -0
- package/dist/domain/identityaccount/constants.d.ts.map +1 -1
- package/dist/domain/identityaccount/constants.js +17 -0
- package/dist/domain/identityaccount/constants.js.map +1 -1
- package/dist/domain/identityaccount/controller.d.ts +3 -1
- package/dist/domain/identityaccount/controller.d.ts.map +1 -1
- package/dist/domain/identityaccount/controller.js +15 -6
- package/dist/domain/identityaccount/controller.js.map +1 -1
- package/dist/domain/identityaccount/operator.d.ts.map +1 -1
- package/dist/domain/identityaccount/operator.js +1 -0
- package/dist/domain/identityaccount/operator.js.map +1 -1
- package/dist/domain/identityaccount/provisioning.d.ts +18 -0
- package/dist/domain/identityaccount/provisioning.d.ts.map +1 -1
- package/dist/domain/identityaccount/provisioning.js +1 -0
- package/dist/domain/identityaccount/provisioning.js.map +1 -1
- package/dist/domain/identityaccount/resource-store.js +9 -5
- package/dist/domain/identityaccount/resource-store.js.map +1 -1
- package/dist/domain/identityaccount/steps.d.ts +11 -2
- package/dist/domain/identityaccount/steps.d.ts.map +1 -1
- package/dist/domain/identityaccount/steps.js +42 -4
- package/dist/domain/identityaccount/steps.js.map +1 -1
- package/dist/domain/identityaccount/store-contract.d.ts.map +1 -1
- package/dist/domain/identityaccount/store-contract.js +33 -0
- package/dist/domain/identityaccount/store-contract.js.map +1 -1
- package/dist/domain/identityaccount/store.d.ts +7 -5
- package/dist/domain/identityaccount/store.d.ts.map +1 -1
- package/dist/domain/identityaccount/store.js.map +1 -1
- package/dist/domain/platform/controller.d.ts +10 -0
- package/dist/domain/platform/controller.d.ts.map +1 -1
- package/dist/domain/platform/controller.js +2 -1
- package/dist/domain/platform/controller.js.map +1 -1
- package/dist/domain/platformclient/constants.d.ts +60 -0
- package/dist/domain/platformclient/constants.d.ts.map +1 -0
- package/dist/domain/platformclient/constants.js +78 -0
- package/dist/domain/platformclient/constants.js.map +1 -0
- package/dist/domain/platformclient/controller.d.ts +59 -0
- package/dist/domain/platformclient/controller.d.ts.map +1 -0
- package/dist/domain/platformclient/controller.js +191 -0
- package/dist/domain/platformclient/controller.js.map +1 -0
- package/dist/domain/platformclient/credentials.d.ts +13 -0
- package/dist/domain/platformclient/credentials.d.ts.map +1 -0
- package/dist/domain/platformclient/credentials.js +56 -0
- package/dist/domain/platformclient/credentials.js.map +1 -0
- package/dist/domain/platformclient/mint.d.ts +23 -0
- package/dist/domain/platformclient/mint.d.ts.map +1 -0
- package/dist/domain/platformclient/mint.js +254 -0
- package/dist/domain/platformclient/mint.js.map +1 -0
- package/dist/domain/platformclient/origin-guard.d.ts +9 -0
- package/dist/domain/platformclient/origin-guard.d.ts.map +1 -0
- package/dist/domain/platformclient/origin-guard.js +81 -0
- package/dist/domain/platformclient/origin-guard.js.map +1 -0
- package/dist/domain/platformclient/resource-store.d.ts +4 -0
- package/dist/domain/platformclient/resource-store.d.ts.map +1 -0
- package/dist/domain/platformclient/resource-store.js +117 -0
- package/dist/domain/platformclient/resource-store.js.map +1 -0
- package/dist/domain/platformclient/steps.d.ts +51 -0
- package/dist/domain/platformclient/steps.d.ts.map +1 -0
- package/dist/domain/platformclient/steps.js +339 -0
- package/dist/domain/platformclient/steps.js.map +1 -0
- package/dist/domain/platformclient/store-contract.d.ts +8 -0
- package/dist/domain/platformclient/store-contract.d.ts.map +1 -0
- package/dist/domain/platformclient/store-contract.js +208 -0
- package/dist/domain/platformclient/store-contract.js.map +1 -0
- package/dist/domain/platformclient/store.d.ts +58 -0
- package/dist/domain/platformclient/store.d.ts.map +1 -0
- package/dist/domain/platformclient/store.js +13 -0
- package/dist/domain/platformclient/store.js.map +1 -0
- package/dist/domain/platformclient/system-managed.d.ts +18 -0
- package/dist/domain/platformclient/system-managed.d.ts.map +1 -0
- package/dist/domain/platformclient/system-managed.js +80 -0
- package/dist/domain/platformclient/system-managed.js.map +1 -0
- package/dist/domain/platformclient/token-controller.d.ts +11 -0
- package/dist/domain/platformclient/token-controller.d.ts.map +1 -0
- package/dist/domain/platformclient/token-controller.js +32 -0
- package/dist/domain/platformclient/token-controller.js.map +1 -0
- package/dist/domain/platformclient/verifier.d.ts +10 -0
- package/dist/domain/platformclient/verifier.d.ts.map +1 -0
- package/dist/domain/platformclient/verifier.js +76 -0
- package/dist/domain/platformclient/verifier.js.map +1 -0
- package/dist/domain/session/list-index.d.ts +4 -0
- package/dist/domain/session/list-index.d.ts.map +1 -0
- package/dist/domain/session/list-index.js +26 -0
- package/dist/domain/session/list-index.js.map +1 -0
- package/dist/domain/session/steps.d.ts +7 -8
- package/dist/domain/session/steps.d.ts.map +1 -1
- package/dist/domain/session/steps.js +76 -78
- package/dist/domain/session/steps.js.map +1 -1
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +53 -55
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowexecution/execution-filter.d.ts +4 -0
- package/dist/domain/workflowexecution/execution-filter.d.ts.map +1 -1
- package/dist/domain/workflowexecution/execution-filter.js +11 -5
- package/dist/domain/workflowexecution/execution-filter.js.map +1 -1
- package/dist/domain/workflowexecution/get-execution-summary.d.ts.map +1 -1
- package/dist/domain/workflowexecution/get-execution-summary.js +23 -17
- package/dist/domain/workflowexecution/get-execution-summary.js.map +1 -1
- package/dist/domain/workflowexecution/list-index.d.ts +2 -0
- package/dist/domain/workflowexecution/list-index.d.ts.map +1 -0
- package/dist/domain/workflowexecution/list-index.js +21 -0
- package/dist/domain/workflowexecution/list-index.js.map +1 -0
- package/dist/domain/workflowexecution/list-pending-approvals.d.ts.map +1 -1
- package/dist/domain/workflowexecution/list-pending-approvals.js +69 -32
- package/dist/domain/workflowexecution/list-pending-approvals.js.map +1 -1
- package/dist/domain/workflowexecution/queries.d.ts +30 -4
- package/dist/domain/workflowexecution/queries.d.ts.map +1 -1
- package/dist/domain/workflowexecution/queries.js +76 -22
- package/dist/domain/workflowexecution/queries.js.map +1 -1
- package/dist/encryption/key-manager.d.ts +20 -3
- package/dist/encryption/key-manager.d.ts.map +1 -1
- package/dist/encryption/key-manager.js +64 -42
- package/dist/encryption/key-manager.js.map +1 -1
- package/dist/extensions/caller-guards.d.ts +8 -3
- package/dist/extensions/caller-guards.d.ts.map +1 -1
- package/dist/extensions/drivers.d.ts +37 -0
- package/dist/extensions/drivers.d.ts.map +1 -1
- package/dist/extensions/guest-token-minting.d.ts +26 -0
- package/dist/extensions/guest-token-minting.d.ts.map +1 -0
- package/dist/extensions/guest-token-minting.js +2 -0
- package/dist/extensions/guest-token-minting.js.map +1 -0
- package/dist/extensions/identity.d.ts +6 -4
- package/dist/extensions/identity.d.ts.map +1 -1
- package/dist/extensions/identity.js.map +1 -1
- package/dist/extensions/list-read-scope.d.ts +9 -12
- package/dist/extensions/list-read-scope.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.js.map +1 -1
- package/dist/extensions/registry.d.ts +30 -1
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/extensions/registry.js +34 -1
- package/dist/extensions/registry.js.map +1 -1
- package/dist/index.d.ts +15 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/interceptors/auth.d.ts +14 -0
- package/dist/pipeline/interceptors/auth.d.ts.map +1 -1
- package/dist/pipeline/interceptors/auth.js +24 -0
- package/dist/pipeline/interceptors/auth.js.map +1 -1
- package/dist/pipeline/steps/build-update-state.d.ts +11 -1
- package/dist/pipeline/steps/build-update-state.d.ts.map +1 -1
- package/dist/pipeline/steps/build-update-state.js +5 -2
- package/dist/pipeline/steps/build-update-state.js.map +1 -1
- package/dist/pipeline/steps/list-page.d.ts +63 -0
- package/dist/pipeline/steps/list-page.d.ts.map +1 -0
- package/dist/pipeline/steps/list-page.js +187 -0
- package/dist/pipeline/steps/list-page.js.map +1 -0
- package/dist/platformtoken/envelope.d.ts +100 -0
- package/dist/platformtoken/envelope.d.ts.map +1 -0
- package/dist/platformtoken/envelope.js +184 -0
- package/dist/platformtoken/envelope.js.map +1 -0
- package/dist/platformtoken/key-ring.d.ts +85 -0
- package/dist/platformtoken/key-ring.d.ts.map +1 -0
- package/dist/platformtoken/key-ring.js +164 -0
- package/dist/platformtoken/key-ring.js.map +1 -0
- package/dist/query/activity/handler.d.ts +6 -7
- package/dist/query/activity/handler.d.ts.map +1 -1
- package/dist/query/activity/handler.js +42 -59
- package/dist/query/activity/handler.js.map +1 -1
- package/dist/store/interface.d.ts +29 -0
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/dist/store/list-index.d.ts +123 -0
- package/dist/store/list-index.d.ts.map +1 -0
- package/dist/store/list-index.js +277 -0
- package/dist/store/list-index.js.map +1 -0
- package/dist/store/logger.d.ts +3 -0
- package/dist/store/logger.d.ts.map +1 -1
- package/dist/store/logger.js +5 -1
- package/dist/store/logger.js.map +1 -1
- package/dist/store/postgres/migrations.d.ts +10 -6
- package/dist/store/postgres/migrations.d.ts.map +1 -1
- package/dist/store/postgres/migrations.js +64 -6
- package/dist/store/postgres/migrations.js.map +1 -1
- package/dist/store/postgres/store.d.ts +26 -5
- package/dist/store/postgres/store.d.ts.map +1 -1
- package/dist/store/postgres/store.js +377 -22
- package/dist/store/postgres/store.js.map +1 -1
- package/dist/store/sqlite/migrations.d.ts +11 -5
- package/dist/store/sqlite/migrations.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.js +50 -4
- package/dist/store/sqlite/migrations.js.map +1 -1
- package/dist/store/sqlite/store.d.ts +18 -3
- package/dist/store/sqlite/store.d.ts.map +1 -1
- package/dist/store/sqlite/store.js +321 -15
- package/dist/store/sqlite/store.js.map +1 -1
- package/package.json +6 -6
- package/src/authorization/README.md +1 -1
- package/src/authorization/__tests__/authorizer.test.ts +6 -2
- package/src/authorization/__tests__/drivers.ts +6 -1
- package/src/authorization/__tests__/wire-permissions.test.ts +1 -0
- package/src/authorization/authorizer.ts +11 -4
- package/src/authorization/model/__tests__/registry.test.ts +8 -1
- package/src/authorization/model/index.ts +6 -4
- package/src/authorization/model/oauth_app.ts +3 -2
- package/src/authorization/model/platform_client.ts +43 -0
- package/src/boot/__tests__/auth-enabled-composition.test.ts +2 -1
- package/src/boot/__tests__/list-indexes.test.ts +65 -0
- package/src/boot/__tests__/route-shadowing.test.ts +67 -0
- package/src/boot/compose.ts +113 -18
- package/src/boot/list-indexes.ts +25 -0
- package/src/boot/route-shadowing.ts +47 -0
- package/src/domain/agentexecution/__tests__/agentexecution.test.ts +85 -3
- package/src/domain/agentexecution/controller.ts +8 -10
- package/src/domain/agentexecution/list-index.ts +24 -0
- package/src/domain/agentexecution/steps.ts +118 -115
- package/src/domain/agentexecution/usage.ts +38 -17
- package/src/domain/artifact/controller.ts +28 -18
- package/src/domain/artifact/list-index.ts +22 -0
- package/src/domain/iampolicy/__tests__/membership.test.ts +7 -0
- package/src/domain/iampolicy/membership.ts +8 -2
- package/src/domain/identityaccount/__tests__/platform-client-accounts.test.ts +124 -0
- package/src/domain/identityaccount/__tests__/resource-store.test.ts +1 -0
- package/src/domain/identityaccount/constants.ts +20 -0
- package/src/domain/identityaccount/controller.ts +16 -4
- package/src/domain/identityaccount/operator.ts +1 -0
- package/src/domain/identityaccount/provisioning.ts +17 -0
- package/src/domain/identityaccount/resource-store.ts +9 -5
- package/src/domain/identityaccount/steps.ts +58 -3
- package/src/domain/identityaccount/store-contract.ts +51 -0
- package/src/domain/identityaccount/store.ts +7 -5
- package/src/domain/platform/__tests__/platform.test.ts +7 -1
- package/src/domain/platform/controller.ts +12 -1
- package/src/domain/platformclient/__tests__/controller.test.ts +397 -0
- package/src/domain/platformclient/__tests__/credentials.test.ts +46 -0
- package/src/domain/platformclient/__tests__/mint.test.ts +415 -0
- package/src/domain/platformclient/__tests__/resource-store.test.ts +140 -0
- package/src/domain/platformclient/__tests__/system-managed.test.ts +101 -0
- package/src/domain/platformclient/__tests__/verifier-and-guard.test.ts +207 -0
- package/src/domain/platformclient/constants.ts +120 -0
- package/src/domain/platformclient/controller.ts +449 -0
- package/src/domain/platformclient/credentials.ts +68 -0
- package/src/domain/platformclient/mint.ts +412 -0
- package/src/domain/platformclient/origin-guard.ts +117 -0
- package/src/domain/platformclient/resource-store.ts +150 -0
- package/src/domain/platformclient/steps.ts +472 -0
- package/src/domain/platformclient/store-contract.ts +318 -0
- package/src/domain/platformclient/store.ts +63 -0
- package/src/domain/platformclient/system-managed.ts +115 -0
- package/src/domain/platformclient/token-controller.ts +49 -0
- package/src/domain/platformclient/verifier.ts +106 -0
- package/src/domain/session/list-index.ts +28 -0
- package/src/domain/session/steps.ts +108 -123
- package/src/domain/workflowexecution/__tests__/list-read-scope-summaries.test.ts +155 -99
- package/src/domain/workflowexecution/__tests__/workflowexecution.test.ts +193 -31
- package/src/domain/workflowexecution/controller.ts +54 -82
- package/src/domain/workflowexecution/execution-filter.ts +20 -7
- package/src/domain/workflowexecution/get-execution-summary.ts +31 -24
- package/src/domain/workflowexecution/list-index.ts +22 -0
- package/src/domain/workflowexecution/list-pending-approvals.ts +82 -25
- package/src/domain/workflowexecution/queries.ts +134 -23
- package/src/encryption/key-manager.ts +87 -44
- package/src/extensions/__tests__/extension-composition.test.ts +3 -2
- package/src/extensions/__tests__/platform-client-composed.test.ts +288 -0
- package/src/extensions/__tests__/registry.test.ts +4 -1
- package/src/extensions/__tests__/tier-truthfulness.test.ts +8 -0
- package/src/extensions/caller-guards.ts +8 -3
- package/src/extensions/drivers.ts +37 -0
- package/src/extensions/guest-token-minting.ts +32 -0
- package/src/extensions/identity.ts +6 -4
- package/src/extensions/list-read-scope.ts +9 -12
- package/src/extensions/registry.ts +77 -3
- package/src/index.ts +65 -1
- package/src/pipeline/interceptors/auth.ts +27 -0
- package/src/pipeline/steps/__tests__/list-page.test.ts +186 -0
- package/src/pipeline/steps/__tests__/references.test.ts +2 -0
- package/src/pipeline/steps/build-update-state.ts +5 -2
- package/src/pipeline/steps/list-page.ts +284 -0
- package/src/platformtoken/__tests__/envelope.test.ts +277 -0
- package/src/platformtoken/__tests__/key-ring.test.ts +205 -0
- package/src/platformtoken/envelope.ts +276 -0
- package/src/platformtoken/key-ring.ts +266 -0
- package/src/query/activity/handler.ts +57 -78
- package/src/runnerauth/__tests__/runner-subject-composed.test.ts +9 -3
- package/src/store/README.md +51 -32
- package/src/store/__tests__/list-index.measure.test.ts +293 -0
- package/src/store/__tests__/list-index.test.ts +328 -0
- package/src/store/__tests__/store-contract.ts +346 -3
- package/src/store/interface.ts +39 -0
- package/src/store/list-index.ts +465 -0
- package/src/store/logger.ts +8 -1
- package/src/store/postgres/__tests__/list-index-repair.test.ts +133 -0
- package/src/store/postgres/__tests__/migrations.test.ts +27 -19
- package/src/store/postgres/__tests__/store-contract.test.ts +50 -3
- package/src/store/postgres/migrations.ts +69 -7
- package/src/store/postgres/store.ts +485 -29
- package/src/store/sqlite/__tests__/migrations.test.ts +51 -7
- package/src/store/sqlite/__tests__/store-contract.test.ts +70 -16
- package/src/store/sqlite/__tests__/support.ts +12 -3
- package/src/store/sqlite/migrations.ts +57 -6
- package/src/store/sqlite/store.ts +462 -21
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PlatformClient lane on a composed server in the authentication
|
|
3
|
+
* posture with the built-in Authorizer (a unit that vouches for tokens,
|
|
4
|
+
* declares the posture and registers no Authorizer — the open-source OIDC
|
|
5
|
+
* self-host's shape), with the key ring supplied so the run is hermetic:
|
|
6
|
+
* - the founder creates a client; an anonymous mint answers a token; the
|
|
7
|
+
* token authenticates as the end user's derived account, which holds the
|
|
8
|
+
* auto-grant role on the owning organization and sees that one alone;
|
|
9
|
+
* - the platform-client verifier sits ahead of the unit's verifier and
|
|
10
|
+
* claims only its lane: the founder's tokens still pass, and a TYPED
|
|
11
|
+
* platform token (a lane open source does not speak) is refused as
|
|
12
|
+
* unclaimed;
|
|
13
|
+
* - the origin guard refuses an unlisted Origin with the cloud's copy and
|
|
14
|
+
* passes a request without one;
|
|
15
|
+
* - deleting the client refuses its token on the next request with the
|
|
16
|
+
* cloud's liveness copy;
|
|
17
|
+
* - an outsider cannot read the client by reference.
|
|
18
|
+
*/
|
|
19
|
+
import { generateKeyPairSync } from "node:crypto";
|
|
20
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
21
|
+
import { tmpdir } from "node:os";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
|
|
24
|
+
import { Code, ConnectError, createClient } from "@connectrpc/connect";
|
|
25
|
+
import type { Interceptor, Transport } from "@connectrpc/connect";
|
|
26
|
+
import { createGrpcTransport } from "@connectrpc/connect-node";
|
|
27
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
28
|
+
|
|
29
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
30
|
+
import { IdentityAccountCommandController } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/command_pb";
|
|
31
|
+
import { IdentityAccountQueryController } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/query_pb";
|
|
32
|
+
import { PlatformClientCommandController } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/command_pb";
|
|
33
|
+
import { PlatformClientQueryController } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/query_pb";
|
|
34
|
+
import { PlatformClientTokenController } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/token_pb";
|
|
35
|
+
import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
|
|
36
|
+
import { OrganizationCommandController } from "@stigmer/protos/ai/stigmer/tenancy/organization/v1/command_pb";
|
|
37
|
+
import { OrganizationQueryController } from "@stigmer/protos/ai/stigmer/tenancy/organization/v1/query_pb";
|
|
38
|
+
|
|
39
|
+
import { loadConfig } from "../../boot/config.js";
|
|
40
|
+
import { composeServer } from "../../boot/compose.js";
|
|
41
|
+
import type { ComposedServer } from "../../boot/compose.js";
|
|
42
|
+
import { accountIdFor } from "../../domain/identityaccount/constants.js";
|
|
43
|
+
import {
|
|
44
|
+
DELETED_CLIENT_MESSAGE,
|
|
45
|
+
originRefusalMessage,
|
|
46
|
+
} from "../../domain/platformclient/constants.js";
|
|
47
|
+
import { signPlatformToken } from "../../platformtoken/envelope.js";
|
|
48
|
+
import { platformTokenKeyRingFromPem } from "../../platformtoken/key-ring.js";
|
|
49
|
+
import type { SigningPlatformTokenKeyRing } from "../../platformtoken/key-ring.js";
|
|
50
|
+
import type { ServerExtension } from "../registry.js";
|
|
51
|
+
import {
|
|
52
|
+
baseConfig,
|
|
53
|
+
bearer,
|
|
54
|
+
fakeJwt,
|
|
55
|
+
fakeVerifier,
|
|
56
|
+
silentLogger,
|
|
57
|
+
transportFor,
|
|
58
|
+
} from "./composed-support.js";
|
|
59
|
+
|
|
60
|
+
const FOUNDER = "fake|pc-founder";
|
|
61
|
+
const OUTSIDER = "fake|pc-outsider";
|
|
62
|
+
const ORG = "pc-org";
|
|
63
|
+
|
|
64
|
+
const { privateKey, publicKey } = generateKeyPairSync("rsa", {
|
|
65
|
+
modulusLength: 2048,
|
|
66
|
+
});
|
|
67
|
+
const built = platformTokenKeyRingFromPem({
|
|
68
|
+
privateKeyPem: privateKey.export({ format: "pem", type: "pkcs8" }).toString(),
|
|
69
|
+
publicKeyPems: [publicKey.export({ format: "pem", type: "spki" }).toString()],
|
|
70
|
+
});
|
|
71
|
+
if (built.signer === undefined) throw new Error("fixture ring must sign");
|
|
72
|
+
const RING: SigningPlatformTokenKeyRing = { ...built, signer: built.signer };
|
|
73
|
+
|
|
74
|
+
/** Stamps the lowercase `origin` header a browser attaches to a cross-origin call. */
|
|
75
|
+
function browserOrigin(origin: string): Interceptor {
|
|
76
|
+
return (next) => (request) => {
|
|
77
|
+
request.header.set("origin", origin);
|
|
78
|
+
return next(request);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function refusal(promise: Promise<unknown>): Promise<ConnectError> {
|
|
83
|
+
try {
|
|
84
|
+
await promise;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (error instanceof ConnectError) return error;
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
throw new Error("expected a ConnectError refusal");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
describe("PlatformClient on a composed server (authentication posture, built-in Authorizer)", () => {
|
|
93
|
+
let dir: string;
|
|
94
|
+
let server: ComposedServer;
|
|
95
|
+
let port: number;
|
|
96
|
+
|
|
97
|
+
const asFounder = (): Transport =>
|
|
98
|
+
transportFor(port, fakeJwt(FOUNDER, "founder@example.com"));
|
|
99
|
+
const asOutsider = (): Transport =>
|
|
100
|
+
transportFor(port, fakeJwt(OUTSIDER, "outsider@example.com"));
|
|
101
|
+
const presenting = (token: string, origin?: string): Transport =>
|
|
102
|
+
createGrpcTransport({
|
|
103
|
+
baseUrl: `http://127.0.0.1:${port}`,
|
|
104
|
+
interceptors: [
|
|
105
|
+
bearer(token),
|
|
106
|
+
...(origin !== undefined ? [browserOrigin(origin)] : []),
|
|
107
|
+
],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
beforeAll(async () => {
|
|
111
|
+
dir = mkdtempSync(path.join(tmpdir(), "platform-client-composed-"));
|
|
112
|
+
const unit: ServerExtension = {
|
|
113
|
+
name: "fake-oidc-only",
|
|
114
|
+
requireAuthentication: true,
|
|
115
|
+
identityVerifiers: [fakeVerifier],
|
|
116
|
+
drivers: { platformTokenKeys: RING },
|
|
117
|
+
};
|
|
118
|
+
server = await composeServer({
|
|
119
|
+
config: loadConfig(baseConfig(dir)),
|
|
120
|
+
logger: silentLogger,
|
|
121
|
+
extensions: [unit],
|
|
122
|
+
portOverride: 0,
|
|
123
|
+
host: "127.0.0.1",
|
|
124
|
+
});
|
|
125
|
+
port = await server.start();
|
|
126
|
+
|
|
127
|
+
await createClient(
|
|
128
|
+
IdentityAccountCommandController,
|
|
129
|
+
asFounder(),
|
|
130
|
+
).provisionMyAccount({});
|
|
131
|
+
await createClient(OrganizationCommandController, asFounder()).create({
|
|
132
|
+
apiVersion: "tenancy.stigmer.ai/v1",
|
|
133
|
+
kind: "Organization",
|
|
134
|
+
metadata: { name: ORG, slug: ORG, org: "" },
|
|
135
|
+
spec: { description: ORG },
|
|
136
|
+
});
|
|
137
|
+
await createClient(
|
|
138
|
+
IdentityAccountCommandController,
|
|
139
|
+
asOutsider(),
|
|
140
|
+
).provisionMyAccount({});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
afterAll(async () => {
|
|
144
|
+
await server?.shutdown();
|
|
145
|
+
rmSync(dir, { recursive: true, force: true });
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
async function createPlatformClient(allowedOrigins: string[] = []) {
|
|
149
|
+
const created = await createClient(
|
|
150
|
+
PlatformClientCommandController,
|
|
151
|
+
asFounder(),
|
|
152
|
+
).create({
|
|
153
|
+
apiVersion: "iam.stigmer.ai/v1",
|
|
154
|
+
kind: "PlatformClient",
|
|
155
|
+
metadata: {
|
|
156
|
+
name: `client-${Math.random().toString(36).slice(2, 8)}`,
|
|
157
|
+
org: ORG,
|
|
158
|
+
},
|
|
159
|
+
spec: {
|
|
160
|
+
autoProvisionAccounts: true,
|
|
161
|
+
autoGrantOnOrg: true,
|
|
162
|
+
autoGrantRole: IamRole.member,
|
|
163
|
+
allowedOrigins,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
id: created.platformClient?.metadata?.id ?? "",
|
|
168
|
+
slug: created.platformClient?.metadata?.slug ?? "",
|
|
169
|
+
clientId: created.platformClient?.spec?.clientId ?? "",
|
|
170
|
+
clientSecret: created.clientSecret,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function mint(
|
|
175
|
+
client: { clientId: string; clientSecret: string },
|
|
176
|
+
userId: string,
|
|
177
|
+
) {
|
|
178
|
+
const minted = await createClient(
|
|
179
|
+
PlatformClientTokenController,
|
|
180
|
+
transportFor(port),
|
|
181
|
+
).mintUserToken({
|
|
182
|
+
clientId: client.clientId,
|
|
183
|
+
clientSecret: client.clientSecret,
|
|
184
|
+
userId,
|
|
185
|
+
userEmail: `${userId}@product.example`,
|
|
186
|
+
userName: "Product User",
|
|
187
|
+
});
|
|
188
|
+
return minted.accessToken;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
it("mints for the end user's derived account, which sees its owning organization alone", async () => {
|
|
192
|
+
const client = await createPlatformClient();
|
|
193
|
+
const token = await mint(client, "user-1");
|
|
194
|
+
|
|
195
|
+
const me = await createClient(
|
|
196
|
+
IdentityAccountQueryController,
|
|
197
|
+
presenting(token),
|
|
198
|
+
).whoAmI({});
|
|
199
|
+
expect(me.metadata?.id).toBe(accountIdFor(`stgm_pc|${ORG}|user-1`));
|
|
200
|
+
|
|
201
|
+
const mine = await createClient(
|
|
202
|
+
OrganizationQueryController,
|
|
203
|
+
presenting(token),
|
|
204
|
+
).findMyOrganizations({});
|
|
205
|
+
expect(
|
|
206
|
+
mine.entries.map((organization) => organization.metadata?.slug),
|
|
207
|
+
).toEqual([ORG]);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("claims only its lane: the unit's tokens still pass, and a typed platform token is refused as unclaimed", async () => {
|
|
211
|
+
await createClient(
|
|
212
|
+
OrganizationQueryController,
|
|
213
|
+
asFounder(),
|
|
214
|
+
).findMyOrganizations({});
|
|
215
|
+
|
|
216
|
+
const guest = signPlatformToken(RING, {
|
|
217
|
+
sub: "ida_guest",
|
|
218
|
+
token_type: "guest",
|
|
219
|
+
}).token;
|
|
220
|
+
const refused = await refusal(
|
|
221
|
+
createClient(
|
|
222
|
+
OrganizationQueryController,
|
|
223
|
+
presenting(guest),
|
|
224
|
+
).findMyOrganizations({}),
|
|
225
|
+
);
|
|
226
|
+
expect(refused.code).toBe(Code.Unauthenticated);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("refuses an unlisted Origin with the cloud's copy and passes a request without one", async () => {
|
|
230
|
+
const client = await createPlatformClient(["https://app.example"]);
|
|
231
|
+
const token = await mint(client, "user-2");
|
|
232
|
+
|
|
233
|
+
const denied = await refusal(
|
|
234
|
+
createClient(
|
|
235
|
+
OrganizationQueryController,
|
|
236
|
+
presenting(token, "https://evil.example"),
|
|
237
|
+
).findMyOrganizations({}),
|
|
238
|
+
);
|
|
239
|
+
expect(denied.code).toBe(Code.PermissionDenied);
|
|
240
|
+
expect(denied.rawMessage).toBe(
|
|
241
|
+
originRefusalMessage("https://evil.example"),
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
await createClient(
|
|
245
|
+
OrganizationQueryController,
|
|
246
|
+
presenting(token),
|
|
247
|
+
).findMyOrganizations({});
|
|
248
|
+
await createClient(
|
|
249
|
+
OrganizationQueryController,
|
|
250
|
+
presenting(token, "https://app.example"),
|
|
251
|
+
).findMyOrganizations({});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("refuses a deleted client's token on the next request with the liveness copy", async () => {
|
|
255
|
+
const client = await createPlatformClient();
|
|
256
|
+
const token = await mint(client, "user-3");
|
|
257
|
+
await createClient(
|
|
258
|
+
OrganizationQueryController,
|
|
259
|
+
presenting(token),
|
|
260
|
+
).findMyOrganizations({});
|
|
261
|
+
|
|
262
|
+
await createClient(PlatformClientCommandController, asFounder()).delete({
|
|
263
|
+
resourceId: client.id,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const denied = await refusal(
|
|
267
|
+
createClient(
|
|
268
|
+
OrganizationQueryController,
|
|
269
|
+
presenting(token),
|
|
270
|
+
).findMyOrganizations({}),
|
|
271
|
+
);
|
|
272
|
+
expect(denied.code).toBe(Code.Unauthenticated);
|
|
273
|
+
expect(denied.rawMessage).toBe(DELETED_CLIENT_MESSAGE);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("an outsider cannot read the client by reference", async () => {
|
|
277
|
+
const client = await createPlatformClient();
|
|
278
|
+
const denied = await refusal(
|
|
279
|
+
createClient(PlatformClientQueryController, asOutsider()).getByReference({
|
|
280
|
+
org: ORG,
|
|
281
|
+
slug: client.slug,
|
|
282
|
+
kind: ApiResourceKind.platform_client,
|
|
283
|
+
}),
|
|
284
|
+
);
|
|
285
|
+
expect(denied.code).toBe(Code.PermissionDenied);
|
|
286
|
+
expect(denied.rawMessage).toBe("unauthorized to view platform client");
|
|
287
|
+
});
|
|
288
|
+
});
|
|
@@ -193,7 +193,10 @@ describe("resolveExtensions — merge semantics", () => {
|
|
|
193
193
|
"g2",
|
|
194
194
|
"g3",
|
|
195
195
|
]);
|
|
196
|
-
expect(resolved.services).toEqual([
|
|
196
|
+
expect(resolved.services).toEqual([
|
|
197
|
+
{ unit: "alpha", register: registerA },
|
|
198
|
+
{ unit: "beta", register: registerB },
|
|
199
|
+
]);
|
|
197
200
|
expect(resolved.workers).toEqual([workerFactory]);
|
|
198
201
|
expect(resolved.statusObservers).toEqual([observerA]);
|
|
199
202
|
expect(resolved.responseDecorators).toEqual([decoratorB]);
|
|
@@ -218,6 +218,14 @@ describe("tier truthfulness against the empty composition", () => {
|
|
|
218
218
|
);
|
|
219
219
|
expect(served.has("iampolicy")).toBe(true);
|
|
220
220
|
});
|
|
221
|
+
|
|
222
|
+
it("platform_client is open_source and served in the empty composition — the mint refuses without a posture, the kind does not", () => {
|
|
223
|
+
const served = servedKindSegments(server.routes);
|
|
224
|
+
expect(getKindMeta(ApiResourceKind.platform_client).tier).toBe(
|
|
225
|
+
ResourceTier.open_source,
|
|
226
|
+
);
|
|
227
|
+
expect(served.has("platformclient")).toBe(true);
|
|
228
|
+
});
|
|
221
229
|
});
|
|
222
230
|
|
|
223
231
|
describe("the check bites (mutation proofs over the same function)", () => {
|
|
@@ -10,9 +10,14 @@
|
|
|
10
10
|
* allowed_origins, oss#375). Java separates the two classes the same way.
|
|
11
11
|
*
|
|
12
12
|
* List point — guards concatenate in unit order and run in composed
|
|
13
|
-
* order; the first throw wins.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* order; the first throw wins. Open source's own guards run first (the
|
|
14
|
+
* verifier list's "OSS entries first" rule): today the PlatformClient
|
|
15
|
+
* origin guard (domain/platformclient/origin-guard.ts), composed only
|
|
16
|
+
* under an authentication posture, where PlatformClient tokens exist.
|
|
17
|
+
* Liveness is not a guard in open source: the PlatformClient verifier
|
|
18
|
+
* refuses a deleted client's tokens itself, so verifier-only lanes (the
|
|
19
|
+
* extension HTTP edges) refuse them too. With no posture and no unit
|
|
20
|
+
* guards, the serving chain runs no guard at all.
|
|
16
21
|
*
|
|
17
22
|
* The in-process exemption is STRUCTURAL, not policed: guards are a
|
|
18
23
|
* parameter of the serving chain's identity source only
|
|
@@ -35,6 +35,11 @@
|
|
|
35
35
|
* it reaches a URL a user supplied: the MCP endpoint it probes at save
|
|
36
36
|
* time and the login server it reaches on Sign in) — landed with the
|
|
37
37
|
* save-time OAuth completion for URL-only MCP servers, 2026-09-19
|
|
38
|
+
* - platform-client store + platform-token keys + guest-token minting
|
|
39
|
+
* (PlatformClient served in every edition: the port the cloud's
|
|
40
|
+
* `cloud.iam_platform_client` driver fills, the RS256 key ring every
|
|
41
|
+
* self-signed token rides, and the one token method only an edition
|
|
42
|
+
* that hosts shared-agent pages serves) — landed 2026-09-23
|
|
38
43
|
*
|
|
39
44
|
* Merge rules (enforced by resolveExtensions, DD-006 §2b): the two
|
|
40
45
|
* provider kinds are single-instance points — a second declaring unit is
|
|
@@ -50,12 +55,15 @@ import type { ArtifactStorageDriverFactory } from "../artifactstorage/artifact-s
|
|
|
50
55
|
import type { ChannelRuntime } from "../domain/agentchannel/channel-runtime.js";
|
|
51
56
|
import type { IdentityAccountStore } from "../domain/identityaccount/store.js";
|
|
52
57
|
import type { IamPolicyStore } from "../domain/iampolicy/store.js";
|
|
58
|
+
import type { PlatformClientStore } from "../domain/platformclient/store.js";
|
|
53
59
|
import type { SecretCodec } from "../encryption/codec.js";
|
|
54
60
|
import type { ModelCatalogProvider } from "../domain/workflow/registry/model-catalog-provider.js";
|
|
55
61
|
import type { VisitorErrorPolicy } from "../pipeline/interceptors/error-boundary.js";
|
|
62
|
+
import type { PlatformTokenKeyRing } from "../platformtoken/key-ring.js";
|
|
56
63
|
import type { RunnerCredentialProvider } from "../runnerauth/runner-credential-provider.js";
|
|
57
64
|
import type { SandboxProvisionerFactory } from "../sandbox/provisioner.js";
|
|
58
65
|
import type { AuthorizationQueryEngine } from "./authorization-queries.js";
|
|
66
|
+
import type { GuestTokenMinting } from "./guest-token-minting.js";
|
|
59
67
|
import type { IdentityFederation } from "./identity-federation.js";
|
|
60
68
|
import type { LicenseStatusProvider } from "./license-status.js";
|
|
61
69
|
import type { OutboundEgressPolicy } from "./outbound-egress.js";
|
|
@@ -247,4 +255,33 @@ export interface ExtensionDrivers {
|
|
|
247
255
|
* (extensions/outbound-egress.ts carries the contract).
|
|
248
256
|
*/
|
|
249
257
|
readonly outboundEgress?: OutboundEgressPolicy;
|
|
258
|
+
/**
|
|
259
|
+
* The platform-client store driver (single-instance point). The
|
|
260
|
+
* platform-client domain's persistence is a PORT like the
|
|
261
|
+
* identity-account domain's: when composed, every chain, the mint and
|
|
262
|
+
* the verifier's liveness read go through this driver (the cloud serves
|
|
263
|
+
* the domain over its own `cloud.iam_platform_client` table this way,
|
|
264
|
+
* legacy `pc_` ids included). When absent, the OSS adapter over the
|
|
265
|
+
* generic Store installs at the compose.ts consumption site.
|
|
266
|
+
*/
|
|
267
|
+
readonly platformClientStore?: PlatformClientStore;
|
|
268
|
+
/**
|
|
269
|
+
* The platform-token key ring (single-instance point): the RS256 keys
|
|
270
|
+
* every token the server signs for itself rides
|
|
271
|
+
* (platformtoken/key-ring.ts). Consumed only under an authentication
|
|
272
|
+
* posture — nothing verifies a token on a server that trusts every
|
|
273
|
+
* request. When absent there, open source composes its own ring on the
|
|
274
|
+
* key-manager ladder; a composition declaring an edition other than
|
|
275
|
+
* open source must supply one (compose.ts refuses to boot without it,
|
|
276
|
+
* so a hosted edition never signs with a generated key).
|
|
277
|
+
*/
|
|
278
|
+
readonly platformTokenKeys?: PlatformTokenKeyRing;
|
|
279
|
+
/**
|
|
280
|
+
* The guest-token capability (single-instance point): the one
|
|
281
|
+
* PlatformClientTokenController method only an edition hosting
|
|
282
|
+
* shared-agent pages serves. When absent, `mintGuestToken` refuses
|
|
283
|
+
* UNIMPLEMENTED with the edition sentence
|
|
284
|
+
* (extensions/guest-token-minting.ts carries the contract).
|
|
285
|
+
*/
|
|
286
|
+
readonly guestTokenMinting?: GuestTokenMinting;
|
|
250
287
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guest-token capability: `PlatformClientTokenController.mintGuestToken`,
|
|
3
|
+
* the credential-free mint for anonymous visitors of a shared agent's
|
|
4
|
+
* hosted page. Single instance, registered as `drivers.guestTokenMinting`
|
|
5
|
+
* (the identityFederation shape: a driver point whose absence is a
|
|
6
|
+
* refusal, never a silent no-op).
|
|
7
|
+
*
|
|
8
|
+
* Why a capability and not a second service: open source registers the
|
|
9
|
+
* whole PlatformClientTokenController, because `mintUserToken` is core,
|
|
10
|
+
* and the composition refuses an extension that registers a service the
|
|
11
|
+
* core already routes. Guest sharing — the hosted page, its rate limits,
|
|
12
|
+
* the share resolution, the org's system-managed client and guest
|
|
13
|
+
* account — is an edition's, so the one method that needs it is
|
|
14
|
+
* dispatched here.
|
|
15
|
+
*
|
|
16
|
+
* Absent, the controller answers UNIMPLEMENTED with the edition sentence
|
|
17
|
+
* (domain/platformclient/constants.ts). Present, the capability owns the
|
|
18
|
+
* whole arm: the request has passed the protovalidate interceptor, and
|
|
19
|
+
* `headers` carries the request's `origin` for the embed checks. It signs
|
|
20
|
+
* with the ring its own composition supplied (`signPlatformToken`).
|
|
21
|
+
*/
|
|
22
|
+
import type {
|
|
23
|
+
MintGuestTokenRequest,
|
|
24
|
+
MintGuestTokenResponse,
|
|
25
|
+
} from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/token_pb";
|
|
26
|
+
|
|
27
|
+
export interface GuestTokenMinting {
|
|
28
|
+
mintGuestToken(
|
|
29
|
+
request: MintGuestTokenRequest,
|
|
30
|
+
headers: Headers,
|
|
31
|
+
): Promise<MintGuestTokenResponse>;
|
|
32
|
+
}
|
|
@@ -23,10 +23,12 @@
|
|
|
23
23
|
* known classes from the type surface).
|
|
24
24
|
*
|
|
25
25
|
* `internal` is ratified contract with a structural guarantee: it is
|
|
26
|
-
* minted ONLY by
|
|
27
|
-
* (boot/inprocess.ts)
|
|
28
|
-
*
|
|
29
|
-
*
|
|
26
|
+
* minted ONLY by server code, in pipeline/interceptors/auth.ts — the
|
|
27
|
+
* in-process chain's identity interceptor (boot/inprocess.ts) and
|
|
28
|
+
* `serverActingFor`, the server acting for a principal it authenticated
|
|
29
|
+
* itself (the PlatformClient mint). No IdentityVerifier may produce it —
|
|
30
|
+
* the serving chain always overwrites the position-1 identity from the
|
|
31
|
+
* wire, so a spoofed internal class cannot enter through a transport.
|
|
30
32
|
*/
|
|
31
33
|
export type CallerClass =
|
|
32
34
|
| "user"
|
|
@@ -81,18 +81,15 @@
|
|
|
81
81
|
* driver's cost is proportional to the candidates offered (the cloud
|
|
82
82
|
* checks each one against its authorization engine), and the whole
|
|
83
83
|
* kind across every tenant is what rolled back on 2026-09-14; the
|
|
84
|
-
* request's org is tens of rows.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* untouched with the org unconsulted (the first line above). Two lanes
|
|
94
|
-
* have no org on the wire (`session.list`, `apiKey.findAll`) and offer
|
|
95
|
-
* the kind until P1 entry 12 pages them.
|
|
84
|
+
* request's org is tens of rows. The lanes over the session, execution
|
|
85
|
+
* and artifact kinds narrow by the request's org or parent in the
|
|
86
|
+
* store's indexed read (store/list-index.ts; every posture honours the
|
|
87
|
+
* request) and page there (pipeline/steps/list-page.ts, one batch per
|
|
88
|
+
* scope call), passing `""`; the small org-scoped lists (environment,
|
|
89
|
+
* memory, schedule, agent-instance, agent-share and agent-channel; the
|
|
90
|
+
* getBy* parent filters) apply their own filter above the helper call
|
|
91
|
+
* and pass `""` too. `apiKey.findAll` has no org on the wire and offers
|
|
92
|
+
* the kind: a few rows per person.
|
|
96
93
|
* - A KIND WHOSE AUTHORIZATION IS ITS PARENT'S CARRIES THAT PARENT ON
|
|
97
94
|
* EVERY CANDIDATE (stigmer-cloud 20260913.04 T04, Q-LB-33..36). When
|
|
98
95
|
* `kind_meta` declares PARENT scope with INHERITED owner
|
|
@@ -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<
|
|
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:
|
|
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(
|
|
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,
|