@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
|
@@ -78,10 +78,16 @@ import { formatObjectRef } from "./tuples.js";
|
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
80
|
* Kinds whose missing row is a denial, never `not-found` (arm 4): the
|
|
81
|
-
* cloud's `PROBE_EXEMPT_KINDS`
|
|
82
|
-
*
|
|
83
|
-
* `invitation` are refused by
|
|
84
|
-
*
|
|
81
|
+
* cloud's `PROBE_EXEMPT_KINDS` (stigmer-cloud
|
|
82
|
+
* src/authorizer/fga-authorizer.ts) restricted to the kinds this edition
|
|
83
|
+
* serves — `platform`, `identity_provider` and `invitation` are refused by
|
|
84
|
+
* arm 2 before any row is asked about. The cloud exempts them because
|
|
85
|
+
* their rows live in its own tables, out of its probe's reach; matching
|
|
86
|
+
* the set here keeps the wire identical across editions (an unknown
|
|
87
|
+
* platform client's id is PERMISSION_DENIED with the annotation's copy in
|
|
88
|
+
* both). The cloud's model drift test pins every kind here inside its
|
|
89
|
+
* `PROBE_EXEMPT_KINDS`, which is why this set is exported; a kind that
|
|
90
|
+
* moves into open source joins this set in the same change.
|
|
85
91
|
*
|
|
86
92
|
* `iam_policy` is reachable as a target through the IamPolicy RPCs' own
|
|
87
93
|
* `resource.kind` (a caller may name it); the grant scope refuses such a
|
|
@@ -91,6 +97,7 @@ import { formatObjectRef } from "./tuples.js";
|
|
|
91
97
|
export const NOT_FOUND_EXEMPT_KINDS: ReadonlySet<ApiResourceKind> = new Set([
|
|
92
98
|
ApiResourceKind.identity_account,
|
|
93
99
|
ApiResourceKind.iam_policy,
|
|
100
|
+
ApiResourceKind.platform_client,
|
|
94
101
|
]);
|
|
95
102
|
|
|
96
103
|
/** The Java RequestAuthorizationService's pre-check copy, byte for byte (the cloud renders the same). */
|
|
@@ -34,6 +34,7 @@ const DECLARED_KINDS = [
|
|
|
34
34
|
ApiResourceKind.iam_policy,
|
|
35
35
|
ApiResourceKind.api_key,
|
|
36
36
|
ApiResourceKind.oauth_app,
|
|
37
|
+
ApiResourceKind.platform_client,
|
|
37
38
|
ApiResourceKind.organization,
|
|
38
39
|
ApiResourceKind.agent,
|
|
39
40
|
ApiResourceKind.agent_channel,
|
|
@@ -94,6 +95,13 @@ const WIRE_PERMISSIONS_BY_TYPE: Readonly<
|
|
|
94
95
|
"can_grant_access",
|
|
95
96
|
"can_view_access",
|
|
96
97
|
],
|
|
98
|
+
platform_client: [
|
|
99
|
+
"can_view",
|
|
100
|
+
"can_edit",
|
|
101
|
+
"can_delete",
|
|
102
|
+
"can_grant_access",
|
|
103
|
+
"can_view_access",
|
|
104
|
+
],
|
|
97
105
|
organization: [
|
|
98
106
|
"can_view",
|
|
99
107
|
"can_edit",
|
|
@@ -262,7 +270,6 @@ describe("the built-in model's registry", () => {
|
|
|
262
270
|
for (const unserved of [
|
|
263
271
|
ApiResourceKind.platform,
|
|
264
272
|
ApiResourceKind.identity_provider,
|
|
265
|
-
ApiResourceKind.platform_client,
|
|
266
273
|
ApiResourceKind.invitation,
|
|
267
274
|
]) {
|
|
268
275
|
expect(declarationFor(unserved), kindEnumName(unserved)).toBeUndefined();
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
* chain past the depth bound) and for the cloud's drift test, which
|
|
13
13
|
* builds a model from the live `.fga` files and compares.
|
|
14
14
|
*
|
|
15
|
-
* Registered: every kind of the open-source tier — the twenty-
|
|
16
|
-
* `kind_meta.tier: open_source` members — and nothing else. The
|
|
15
|
+
* Registered: every kind of the open-source tier — the twenty-four
|
|
16
|
+
* `kind_meta.tier: open_source` members — and nothing else. The three
|
|
17
17
|
* files `fga.mod` lists that have no declaration here (`platform`,
|
|
18
|
-
* `identity_provider`, `
|
|
19
|
-
*
|
|
18
|
+
* `identity_provider`, `invitation`) are kinds this edition does not
|
|
19
|
+
* serve: a check that targets one is refused by the
|
|
20
20
|
* driver, and a tuple that names one (`identity_provider#platform_user`)
|
|
21
21
|
* resolves to nobody. The registry test pins the list against the tier.
|
|
22
22
|
*/
|
|
@@ -38,6 +38,7 @@ import { mcpServerDeclaration } from "./mcp_server.js";
|
|
|
38
38
|
import { memoryDeclaration } from "./memory.js";
|
|
39
39
|
import { oauthAppDeclaration } from "./oauth_app.js";
|
|
40
40
|
import { organizationDeclaration } from "./organization.js";
|
|
41
|
+
import { platformClientDeclaration } from "./platform_client.js";
|
|
41
42
|
import { pluginDeclaration } from "./plugin.js";
|
|
42
43
|
import type { KindDeclaration } from "./rewrite.js";
|
|
43
44
|
import { scheduleDeclaration } from "./schedule.js";
|
|
@@ -77,6 +78,7 @@ export const builtInModel: Model = newModel([
|
|
|
77
78
|
iamPolicyDeclaration,
|
|
78
79
|
apiKeyDeclaration,
|
|
79
80
|
oauthAppDeclaration,
|
|
81
|
+
platformClientDeclaration,
|
|
80
82
|
organizationDeclaration,
|
|
81
83
|
agentDeclaration,
|
|
82
84
|
agentChannelDeclaration,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Transcript of fga/model/iam/oauth_app.fga —
|
|
3
|
-
*
|
|
2
|
+
* Transcript of fga/model/iam/oauth_app.fga — an organization-scoped kind
|
|
3
|
+
* whose `owner` has NO admin arm while its `viewer` does (platform_client
|
|
4
|
+
* shares the shape, for the same reason): the file's
|
|
4
5
|
* "administrative visibility" for the organization's admins over a
|
|
5
6
|
* registration that holds vendor client credentials, with change and
|
|
6
7
|
* deletion the creator's (or an explicit grant's) alone, and no public
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript of fga/model/iam/platform_client.fga — relation for relation
|
|
3
|
+
* the oauth_app shape: organization-scoped, an `owner` with no admin arm
|
|
4
|
+
* and a `viewer` that has one. A platform client holds credential material,
|
|
5
|
+
* so the model is restricted: its creator (the owner) edits, deletes,
|
|
6
|
+
* rotates and grants; the organization's admins may view it; ordinary
|
|
7
|
+
* members have no access at all, which is why listByOrg narrows through
|
|
8
|
+
* the list read scope. There is no public level.
|
|
9
|
+
*/
|
|
10
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
11
|
+
import { PlatformClientSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
computed,
|
|
15
|
+
declareKind,
|
|
16
|
+
direct,
|
|
17
|
+
from,
|
|
18
|
+
objectOf,
|
|
19
|
+
union,
|
|
20
|
+
} from "./rewrite.js";
|
|
21
|
+
|
|
22
|
+
export const platformClientDeclaration = declareKind({
|
|
23
|
+
kind: ApiResourceKind.platform_client,
|
|
24
|
+
schema: PlatformClientSchema,
|
|
25
|
+
source: "fga/model/iam/platform_client.fga",
|
|
26
|
+
relations: [
|
|
27
|
+
["organization", direct(objectOf("organization"))],
|
|
28
|
+
["owner", direct(objectOf("identity_account"))],
|
|
29
|
+
[
|
|
30
|
+
"viewer",
|
|
31
|
+
union(
|
|
32
|
+
direct(objectOf("identity_account")),
|
|
33
|
+
computed("owner"),
|
|
34
|
+
from("admin", "organization"),
|
|
35
|
+
),
|
|
36
|
+
],
|
|
37
|
+
["can_view", computed("viewer")],
|
|
38
|
+
["can_edit", computed("owner")],
|
|
39
|
+
["can_delete", computed("owner")],
|
|
40
|
+
["can_grant_access", computed("owner")],
|
|
41
|
+
["can_view_access", computed("viewer")],
|
|
42
|
+
],
|
|
43
|
+
});
|
|
@@ -236,10 +236,11 @@ describe("the require-authentication posture on the wire", () => {
|
|
|
236
236
|
);
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
-
it("is_public methods stay reachable tokenless (getServerInfo)", async () => {
|
|
239
|
+
it("is_public methods stay reachable tokenless (getServerInfo), and it reports the posture", async () => {
|
|
240
240
|
const platform = createClient(PlatformQueryController, transportWith());
|
|
241
241
|
const info = await platform.getServerInfo({});
|
|
242
242
|
expect(info.edition).not.toBe("");
|
|
243
|
+
expect(info.authenticationRequired).toBe(true);
|
|
243
244
|
});
|
|
244
245
|
|
|
245
246
|
it("the gRPC health service stays reachable tokenless — a Kubernetes grpc probe survives the posture (stigmer#974)", async () => {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins the list-indexed surface (list-indexes.ts). The table below is
|
|
3
|
+
* every declaration's keys against its revision: a store trusts a row's
|
|
4
|
+
* facts only under the revision they were derived with, so a change to a
|
|
5
|
+
* declaration's keys without a new revision would read rows written under
|
|
6
|
+
* the old keys as if they carried the new ones. Changing a declaration
|
|
7
|
+
* therefore fails here until its revision is bumped and this table is
|
|
8
|
+
* updated in the same change.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, it } from "vitest";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
ListIndexRegistry,
|
|
14
|
+
listIndexFingerprint,
|
|
15
|
+
} from "../../store/list-index.js";
|
|
16
|
+
import { LIST_INDEXES } from "../list-indexes.js";
|
|
17
|
+
|
|
18
|
+
const PINNED: Readonly<
|
|
19
|
+
Record<string, { revision: number; fingerprint: string }>
|
|
20
|
+
> = {
|
|
21
|
+
agent_execution: {
|
|
22
|
+
revision: 1,
|
|
23
|
+
fingerprint: "agent_execution{session=field:spec.session_id}",
|
|
24
|
+
},
|
|
25
|
+
artifact: {
|
|
26
|
+
revision: 1,
|
|
27
|
+
fingerprint:
|
|
28
|
+
"artifact{agent_execution=field:spec.source.agent_execution_id,workflow_execution=field:spec.source.workflow_execution_id}",
|
|
29
|
+
},
|
|
30
|
+
session: {
|
|
31
|
+
revision: 1,
|
|
32
|
+
fingerprint:
|
|
33
|
+
"session{agent_instance=field:spec.agent_instance_id,channel=label:stigmer.ai/channel-id}",
|
|
34
|
+
},
|
|
35
|
+
workflow_execution: {
|
|
36
|
+
revision: 1,
|
|
37
|
+
fingerprint:
|
|
38
|
+
"workflow_execution{workflow=field:spec.workflow_id,workflow_instance=field:spec.workflow_instance_id}",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
describe("the list-indexed surface", () => {
|
|
43
|
+
it("opens as one registry: no kind declared twice", () => {
|
|
44
|
+
expect(() => new ListIndexRegistry(LIST_INDEXES)).not.toThrow();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("declares exactly the pinned kinds", () => {
|
|
48
|
+
const kinds = LIST_INDEXES.map(
|
|
49
|
+
(d) => listIndexFingerprint(d).split("{")[0],
|
|
50
|
+
);
|
|
51
|
+
expect(kinds.sort()).toEqual(Object.keys(PINNED).sort());
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it.each(LIST_INDEXES.map((d) => [listIndexFingerprint(d), d] as const))(
|
|
55
|
+
"%s carries the revision its keys are pinned to",
|
|
56
|
+
(fingerprint, declaration) => {
|
|
57
|
+
const kind = fingerprint.split("{")[0] ?? "";
|
|
58
|
+
const pinned = PINNED[kind];
|
|
59
|
+
expect(
|
|
60
|
+
{ revision: declaration.revision, fingerprint },
|
|
61
|
+
"a declaration's keys changed: bump its revision and update this table together",
|
|
62
|
+
).toEqual(pinned);
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins the routes stage's refusal of a shadowing registration: Connect's
|
|
3
|
+
* router lets the last registration of a path win silently, so an
|
|
4
|
+
* extension that registers a service the core already serves — or that
|
|
5
|
+
* another extension already registered — must fail boot, naming itself and
|
|
6
|
+
* whoever serves the path. Distinct services pass untouched, in order.
|
|
7
|
+
*/
|
|
8
|
+
import { createConnectRouter } from "@connectrpc/connect";
|
|
9
|
+
import { describe, expect, it } from "vitest";
|
|
10
|
+
|
|
11
|
+
import { OAuthAppQueryController } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/query_pb";
|
|
12
|
+
import { PlatformClientQueryController } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/query_pb";
|
|
13
|
+
import { PlatformClientTokenController } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/token_pb";
|
|
14
|
+
|
|
15
|
+
import { registerExtensionServicesUnshadowed } from "../route-shadowing.js";
|
|
16
|
+
|
|
17
|
+
describe("registerExtensionServicesUnshadowed", () => {
|
|
18
|
+
it("refuses an extension service the core already serves, naming the unit and the path", () => {
|
|
19
|
+
const router = createConnectRouter();
|
|
20
|
+
router.service(PlatformClientTokenController, {});
|
|
21
|
+
expect(() =>
|
|
22
|
+
registerExtensionServicesUnshadowed(router, [
|
|
23
|
+
{
|
|
24
|
+
unit: "cloud-iam",
|
|
25
|
+
register: (r) => void r.service(PlatformClientTokenController, {}),
|
|
26
|
+
},
|
|
27
|
+
]),
|
|
28
|
+
).toThrow(
|
|
29
|
+
/extension 'cloud-iam' registers \/ai\.stigmer\.iam\.platformclient\.v1\.PlatformClientTokenController\/mintUserToken, which the core already serves/,
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("refuses a second extension registering what a first one did", () => {
|
|
34
|
+
const router = createConnectRouter();
|
|
35
|
+
expect(() =>
|
|
36
|
+
registerExtensionServicesUnshadowed(router, [
|
|
37
|
+
{
|
|
38
|
+
unit: "first",
|
|
39
|
+
register: (r) => void r.service(OAuthAppQueryController, {}),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
unit: "second",
|
|
43
|
+
register: (r) =>
|
|
44
|
+
void r.rpc(OAuthAppQueryController.method.get, () => {
|
|
45
|
+
throw new Error("never called");
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
]),
|
|
49
|
+
).toThrow(
|
|
50
|
+
/extension 'second' registers .*OAuthAppQueryController\/get, which extension 'first' already serves/,
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("registers distinct services from every unit", () => {
|
|
55
|
+
const router = createConnectRouter();
|
|
56
|
+
router.service(OAuthAppQueryController, {});
|
|
57
|
+
registerExtensionServicesUnshadowed(router, [
|
|
58
|
+
{
|
|
59
|
+
unit: "billing",
|
|
60
|
+
register: (r) => void r.service(PlatformClientQueryController, {}),
|
|
61
|
+
},
|
|
62
|
+
]);
|
|
63
|
+
expect(router.handlers.map((handler) => handler.requestPath)).toContain(
|
|
64
|
+
"/ai.stigmer.iam.platformclient.v1.PlatformClientQueryController/listByOrg",
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
});
|
package/src/boot/compose.ts
CHANGED
|
@@ -108,6 +108,11 @@ import { registerExecutionContextServices } from "../domain/executioncontext/con
|
|
|
108
108
|
import { registerGitHubServices } from "../domain/github/controller.js";
|
|
109
109
|
import { registerMemoryServices } from "../domain/memory/controller.js";
|
|
110
110
|
import { registerOAuthAppServices } from "../domain/oauthapp/controller.js";
|
|
111
|
+
import { registerPlatformClientServices } from "../domain/platformclient/controller.js";
|
|
112
|
+
import { newPlatformClientOriginGuard } from "../domain/platformclient/origin-guard.js";
|
|
113
|
+
import { newResourcePlatformClientStore } from "../domain/platformclient/resource-store.js";
|
|
114
|
+
import { registerPlatformClientTokenService } from "../domain/platformclient/token-controller.js";
|
|
115
|
+
import { newPlatformClientTokenVerifier } from "../domain/platformclient/verifier.js";
|
|
111
116
|
import { registerOrganizationServices } from "../domain/organization/controller.js";
|
|
112
117
|
import { registerMcpServerServices } from "../domain/mcpserver/controller.js";
|
|
113
118
|
import { registerPlatformServices } from "../domain/platform/controller.js";
|
|
@@ -144,6 +149,7 @@ import {
|
|
|
144
149
|
import type { SecretCodec } from "../encryption/codec.js";
|
|
145
150
|
import { getOrCreateNamedKey } from "../encryption/key-manager.js";
|
|
146
151
|
import { V1_VERSION } from "../encryption/v1-codec.js";
|
|
152
|
+
import { resolvePlatformTokenKeys } from "../platformtoken/key-ring.js";
|
|
147
153
|
import { registerActivityServices } from "../query/activity/controller.js";
|
|
148
154
|
import { ActivityHandler } from "../query/activity/handler.js";
|
|
149
155
|
import { registerSearchServices } from "../query/search/controller.js";
|
|
@@ -203,9 +209,11 @@ import {
|
|
|
203
209
|
} from "../transport/console/handler.js";
|
|
204
210
|
import { createRegistryLanes } from "../transport/registry/lanes.js";
|
|
205
211
|
import { createUnifiedPortServer } from "../transport/server.js";
|
|
212
|
+
import { registerExtensionServicesUnshadowed } from "./route-shadowing.js";
|
|
206
213
|
import type { ServerConfig } from "./config.js";
|
|
207
214
|
import { createInProcessClients } from "./inprocess.js";
|
|
208
215
|
import type { InProcessClients } from "./inprocess.js";
|
|
216
|
+
import { LIST_INDEXES } from "./list-indexes.js";
|
|
209
217
|
import type { Logger } from "./logger.js";
|
|
210
218
|
|
|
211
219
|
export interface ComposedServer {
|
|
@@ -329,20 +337,22 @@ export async function composeServer(
|
|
|
329
337
|
}
|
|
330
338
|
// Stage: storage — the driver selection seam (DD-010): DATABASE_URL
|
|
331
339
|
// present → Postgres (async connect + advisory-locked migrations), else
|
|
332
|
-
// sqlite on DB_PATH (
|
|
340
|
+
// sqlite on DB_PATH (its whole chain, incl. adopting a Go-created
|
|
333
341
|
// database — D2 §3 schema continuity). Postgres wins when both are set:
|
|
334
342
|
// DB_PATH always has a default value, so no other precedence could ever
|
|
335
|
-
// select Postgres (config.ts documents the contract).
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
//
|
|
339
|
-
//
|
|
343
|
+
// select Postgres (config.ts documents the contract). Either driver
|
|
344
|
+
// opens with the list indexes (list-indexes.ts) and reconciles them
|
|
345
|
+
// before it returns. A failure here is a loud boot throw, never a
|
|
346
|
+
// degraded server. The operator identity (#400) is installed by main.ts
|
|
347
|
+
// — once per PROCESS, before any writer exists — not here: composeServer
|
|
348
|
+
// is re-entrant for tests, the identity seam deliberately is not.
|
|
340
349
|
let store: Store;
|
|
350
|
+
const storeOptions = { listIndexes: LIST_INDEXES };
|
|
341
351
|
if (config.databaseUrl !== "") {
|
|
342
|
-
store = await PostgresStore.open(config.databaseUrl, logger);
|
|
352
|
+
store = await PostgresStore.open(config.databaseUrl, logger, storeOptions);
|
|
343
353
|
logger.info("storage driver selected", { driver: "postgres" });
|
|
344
354
|
} else {
|
|
345
|
-
store = SqliteStore.open(config.dbPath, logger);
|
|
355
|
+
store = SqliteStore.open(config.dbPath, logger, storeOptions);
|
|
346
356
|
logger.info("storage driver selected", {
|
|
347
357
|
driver: "sqlite",
|
|
348
358
|
dbPath: config.dbPath,
|
|
@@ -413,6 +423,14 @@ export async function composeServer(
|
|
|
413
423
|
// bindings are what the IamPolicy controller registers over (routes).
|
|
414
424
|
const iamPolicies =
|
|
415
425
|
extensions.drivers.iamPolicyStore ?? newResourceIamPolicyStore(store);
|
|
426
|
+
// The PlatformClient store PORT, bound the same way
|
|
427
|
+
// (`drivers.platformClientStore`; open source's adapter over `store`
|
|
428
|
+
// when absent): the chains, the mint and the verifier's liveness read all
|
|
429
|
+
// go through this one binding, so a composed driver is followed
|
|
430
|
+
// everywhere by construction.
|
|
431
|
+
const platformClients =
|
|
432
|
+
extensions.drivers.platformClientStore ??
|
|
433
|
+
newResourcePlatformClientStore(store);
|
|
416
434
|
const iamPolicyGrantPath = newIamPolicyGrantPath({
|
|
417
435
|
policies: iamPolicies,
|
|
418
436
|
lifecycle: extensions.drivers.resourceAuthorizationLifecycle,
|
|
@@ -690,6 +708,26 @@ export async function composeServer(
|
|
|
690
708
|
writeVersionValue === "" ? DEFAULT_WRITE_VERSION : writeVersionValue,
|
|
691
709
|
registeredCodecs: [...secretCodecs.keys()].sort(),
|
|
692
710
|
});
|
|
711
|
+
// The platform-token key ring joins the FATAL side of the asymmetry:
|
|
712
|
+
// under an authentication posture a server that cannot sign or verify
|
|
713
|
+
// its own tokens would refuse every PlatformClient-minted caller, and an
|
|
714
|
+
// explicitly configured key that does not load is configuration the
|
|
715
|
+
// operator meant (the ladder's rule). No posture, no ring
|
|
716
|
+
// (resolvePlatformTokenKeys carries the four arms).
|
|
717
|
+
const platformTokenKeys = resolvePlatformTokenKeys({
|
|
718
|
+
requireAuthentication,
|
|
719
|
+
supplied: extensions.drivers.platformTokenKeys,
|
|
720
|
+
edition: extensions.edition,
|
|
721
|
+
});
|
|
722
|
+
logger.info("platform-token keys resolved", {
|
|
723
|
+
source:
|
|
724
|
+
platformTokenKeys === undefined
|
|
725
|
+
? "none (no authentication posture)"
|
|
726
|
+
: extensions.drivers.platformTokenKeys !== undefined
|
|
727
|
+
? "extension"
|
|
728
|
+
: "open-source ladder",
|
|
729
|
+
signing: platformTokenKeys?.signer !== undefined,
|
|
730
|
+
});
|
|
693
731
|
// Stage: temporal (#18). Construction is sync and connection-free —
|
|
694
732
|
// initialConnect/startWorkers/startHealthMonitor run in start(), all
|
|
695
733
|
// NON-fatal (Go server.go: the server boots and serves with the engine
|
|
@@ -985,7 +1023,10 @@ export async function composeServer(
|
|
|
985
1023
|
reserve: (key, declaredSizeBytes) => {
|
|
986
1024
|
const { ttlMs } = skillUploadSlots.reserve(key, declaredSizeBytes);
|
|
987
1025
|
return {
|
|
988
|
-
url: skillUploadUrl(
|
|
1026
|
+
url: skillUploadUrl(
|
|
1027
|
+
config.skillTransferBaseUrl,
|
|
1028
|
+
uploadRefOf(key),
|
|
1029
|
+
),
|
|
989
1030
|
ttlMs,
|
|
990
1031
|
};
|
|
991
1032
|
},
|
|
@@ -1161,9 +1202,12 @@ export async function composeServer(
|
|
|
1161
1202
|
case "configured":
|
|
1162
1203
|
break;
|
|
1163
1204
|
case "derived":
|
|
1164
|
-
logger.info(
|
|
1165
|
-
|
|
1166
|
-
|
|
1205
|
+
logger.info(
|
|
1206
|
+
"STIGMER_OAUTH_REDIRECT_URI is not set — deriving the served console's callback for MCP server OAuth Connect",
|
|
1207
|
+
{
|
|
1208
|
+
redirectUri: oauthRedirect.uri,
|
|
1209
|
+
},
|
|
1210
|
+
);
|
|
1167
1211
|
break;
|
|
1168
1212
|
case "absent":
|
|
1169
1213
|
logger.warn(
|
|
@@ -1175,7 +1219,8 @@ export async function composeServer(
|
|
|
1175
1219
|
throw new Error(String(exhaustive));
|
|
1176
1220
|
}
|
|
1177
1221
|
}
|
|
1178
|
-
const oauthRedirectUri =
|
|
1222
|
+
const oauthRedirectUri =
|
|
1223
|
+
oauthRedirect.kind === "absent" ? "" : oauthRedirect.uri;
|
|
1179
1224
|
// The ONE fetch the McpServer domain dials user-supplied URLs with: the
|
|
1180
1225
|
// endpoint it probes at save time and the login server it reaches on
|
|
1181
1226
|
// Sign in. Every hop is judged under the edition's egress policy
|
|
@@ -1284,6 +1329,34 @@ export async function composeServer(
|
|
|
1284
1329
|
authorizer,
|
|
1285
1330
|
authorizationLifecycle,
|
|
1286
1331
|
});
|
|
1332
|
+
// PlatformClient, OAuthApp's inbound counterpart: served in every
|
|
1333
|
+
// composition over the store PORT bound above. The token service is
|
|
1334
|
+
// registered here too — mintGuestToken dispatches to the composed
|
|
1335
|
+
// capability — so nothing else may register it (route-shadowing.ts).
|
|
1336
|
+
// The mint writes end users through the identity-account create path
|
|
1337
|
+
// and the one grant path, as the platform client (mint.ts), and signs
|
|
1338
|
+
// with the ring the keys stage resolved (none without a posture: the
|
|
1339
|
+
// mint then refuses).
|
|
1340
|
+
registerPlatformClientServices(router, {
|
|
1341
|
+
clients: platformClients,
|
|
1342
|
+
store,
|
|
1343
|
+
logger,
|
|
1344
|
+
authorizer,
|
|
1345
|
+
authorizationLifecycle,
|
|
1346
|
+
listReadScope,
|
|
1347
|
+
});
|
|
1348
|
+
registerPlatformClientTokenService(router, {
|
|
1349
|
+
mint: {
|
|
1350
|
+
clients: platformClients,
|
|
1351
|
+
accounts: identityAccounts,
|
|
1352
|
+
createAccount: createIdentityAccount,
|
|
1353
|
+
grantPath: iamPolicyGrantPath,
|
|
1354
|
+
keys: platformTokenKeys,
|
|
1355
|
+
logger,
|
|
1356
|
+
now: () => new Date(),
|
|
1357
|
+
},
|
|
1358
|
+
guestTokenMinting: extensions.drivers.guestTokenMinting,
|
|
1359
|
+
});
|
|
1287
1360
|
registerExecutionContextServices(router, {
|
|
1288
1361
|
store,
|
|
1289
1362
|
logger,
|
|
@@ -1549,6 +1622,7 @@ export async function composeServer(
|
|
|
1549
1622
|
runnerAuthService: runnerCredentials,
|
|
1550
1623
|
edition: extensions.edition,
|
|
1551
1624
|
version: options.version ?? SERVER_VERSION,
|
|
1625
|
+
authenticationRequired: requireAuthentication,
|
|
1552
1626
|
// The built-in `absent` answer installs here, at the consumer, when
|
|
1553
1627
|
// no unit registered a license-status provider: open source and the
|
|
1554
1628
|
// cloud hold no key, and only an Enterprise unit ever has one.
|
|
@@ -1559,10 +1633,9 @@ export async function composeServer(
|
|
|
1559
1633
|
// Extension services register after the whole OSS set, inside the ONE
|
|
1560
1634
|
// routes closure — so the serving router AND the in-process transport
|
|
1561
1635
|
// see them by construction (blueprint §2a; DD-002's parity doctrine
|
|
1562
|
-
// extends to extension services unchanged).
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
}
|
|
1636
|
+
// extends to extension services unchanged). A registration that would
|
|
1637
|
+
// shadow a route already served is a boot throw (route-shadowing.ts).
|
|
1638
|
+
registerExtensionServicesUnshadowed(router, extensions.services);
|
|
1566
1639
|
};
|
|
1567
1640
|
const inProcessWiring = createInProcessClients(routes, logger);
|
|
1568
1641
|
inProcess = inProcessWiring.clients;
|
|
@@ -1610,6 +1683,18 @@ export async function composeServer(
|
|
|
1610
1683
|
...(requireAuthentication
|
|
1611
1684
|
? [newApiKeyIdentityVerifier({ store, accounts: identityAccounts })]
|
|
1612
1685
|
: []),
|
|
1686
|
+
// The PlatformClient user-token lane rides the ring, which the keys
|
|
1687
|
+
// stage resolves only under an authentication posture. It sits before
|
|
1688
|
+
// `oidc` for the runner-subject verifier's reason (stigmer#1137): the
|
|
1689
|
+
// OIDC verifier would claim the platform token and throw on it.
|
|
1690
|
+
...(platformTokenKeys !== undefined
|
|
1691
|
+
? [
|
|
1692
|
+
newPlatformClientTokenVerifier({
|
|
1693
|
+
keys: platformTokenKeys,
|
|
1694
|
+
clients: platformClients,
|
|
1695
|
+
}),
|
|
1696
|
+
]
|
|
1697
|
+
: []),
|
|
1613
1698
|
...(authorizationPosture === "built-in"
|
|
1614
1699
|
? [
|
|
1615
1700
|
newRunnerSubjectIdentityVerifier({
|
|
@@ -1652,6 +1737,16 @@ export async function composeServer(
|
|
|
1652
1737
|
? [`extension '${extensions.requireAuthentication.declaredBy}'`]
|
|
1653
1738
|
: []),
|
|
1654
1739
|
];
|
|
1740
|
+
// Caller guards: open source's own first, then the extensions' — the
|
|
1741
|
+
// rule the verifier list follows ("OSS entries first"). The origin guard
|
|
1742
|
+
// exists only where PlatformClient tokens can be presented, under the
|
|
1743
|
+
// posture that verifies them.
|
|
1744
|
+
const callerGuards = [
|
|
1745
|
+
...(platformTokenKeys !== undefined
|
|
1746
|
+
? [newPlatformClientOriginGuard({ clients: platformClients, logger })]
|
|
1747
|
+
: []),
|
|
1748
|
+
...extensions.callerGuards,
|
|
1749
|
+
];
|
|
1655
1750
|
logger.info("authentication posture resolved", {
|
|
1656
1751
|
posture: requireAuthentication ? "required" : "trusted-local",
|
|
1657
1752
|
source: postureSources.length > 0 ? postureSources.join(" + ") : "none",
|
|
@@ -1680,7 +1775,7 @@ export async function composeServer(
|
|
|
1680
1775
|
logger,
|
|
1681
1776
|
createVerifierChainInterceptor(
|
|
1682
1777
|
identityVerifiers,
|
|
1683
|
-
|
|
1778
|
+
callerGuards,
|
|
1684
1779
|
logger,
|
|
1685
1780
|
requireAuthentication,
|
|
1686
1781
|
),
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The whole list-indexed surface, one explicit list (the search
|
|
3
|
+
* registry's composition-root idiom, query/search/registry.ts; house rule:
|
|
4
|
+
* no import side effects). The store is opened with it
|
|
5
|
+
* (`StoreOpenOptions.listIndexes`), keeps a declared kind's facts on every
|
|
6
|
+
* write, and refuses a read through a declaration it was not opened with,
|
|
7
|
+
* so a lane that reads an index and this list cannot disagree silently.
|
|
8
|
+
*
|
|
9
|
+
* A kind joins when a lane needs to read one organization's or one
|
|
10
|
+
* parent's rows without decoding the whole kind; the kinds here were
|
|
11
|
+
* chosen on the hosted edition's row counts and sizes (2026-09-23: every
|
|
12
|
+
* other org-scoped kind held under a hundred rows and a hundred kilobytes).
|
|
13
|
+
*/
|
|
14
|
+
import { agentExecutionListIndex } from "../domain/agentexecution/list-index.js";
|
|
15
|
+
import { artifactListIndex } from "../domain/artifact/list-index.js";
|
|
16
|
+
import { sessionListIndex } from "../domain/session/list-index.js";
|
|
17
|
+
import { workflowExecutionListIndex } from "../domain/workflowexecution/list-index.js";
|
|
18
|
+
import type { ListIndexDeclaration } from "../store/list-index.js";
|
|
19
|
+
|
|
20
|
+
export const LIST_INDEXES: ReadonlyArray<ListIndexDeclaration> = [
|
|
21
|
+
agentExecutionListIndex,
|
|
22
|
+
artifactListIndex,
|
|
23
|
+
sessionListIndex,
|
|
24
|
+
workflowExecutionListIndex,
|
|
25
|
+
];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The routes stage's one refusal: an extension may not register a route
|
|
3
|
+
* the router already serves.
|
|
4
|
+
*
|
|
5
|
+
* Connect's router keeps every registration, and both the serving adapter
|
|
6
|
+
* and the in-process transport build their path table with a plain map
|
|
7
|
+
* set, so a second registration of `/<service>/<method>` silently wins.
|
|
8
|
+
* Extensions register after the whole core set (compose.ts), so a
|
|
9
|
+
* composition that re-registers a service the core has come to serve —
|
|
10
|
+
* the moment a cloud domain moves into open source and the cloud's pin
|
|
11
|
+
* advances without deleting its own handlers — would shadow the core with
|
|
12
|
+
* no error, and serve from whatever store its old handlers read. Refusing
|
|
13
|
+
* at boot turns that into the loudest possible failure; the fix is always
|
|
14
|
+
* the domain's driver point, never a second service.
|
|
15
|
+
*
|
|
16
|
+
* Checked per request path, because `router.service` registers every
|
|
17
|
+
* method of a descriptor (the unimplemented ones answering UNIMPLEMENTED)
|
|
18
|
+
* and `router.rpc` registers one; two extensions colliding with each
|
|
19
|
+
* other are refused the same way.
|
|
20
|
+
*/
|
|
21
|
+
import type { ConnectRouter } from "@connectrpc/connect";
|
|
22
|
+
|
|
23
|
+
import type { ResolvedServiceRegistration } from "../extensions/registry.js";
|
|
24
|
+
|
|
25
|
+
/** Runs each extension registration, refusing any path already routed. */
|
|
26
|
+
export function registerExtensionServicesUnshadowed(
|
|
27
|
+
router: ConnectRouter,
|
|
28
|
+
registrations: ReadonlyArray<ResolvedServiceRegistration>,
|
|
29
|
+
): void {
|
|
30
|
+
// Request path → who serves it, as the refusal names them.
|
|
31
|
+
const servedBy = new Map<string, string>(
|
|
32
|
+
router.handlers.map((handler) => [handler.requestPath, "the core"]),
|
|
33
|
+
);
|
|
34
|
+
for (const { unit, register } of registrations) {
|
|
35
|
+
const before = router.handlers.length;
|
|
36
|
+
register(router);
|
|
37
|
+
for (const handler of router.handlers.slice(before)) {
|
|
38
|
+
const server = servedBy.get(handler.requestPath);
|
|
39
|
+
if (server !== undefined) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`extension '${unit}' registers ${handler.requestPath}, which ${server} already serves — a route is never registered twice; an edition changes a core domain's behaviour through its driver points`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
servedBy.set(handler.requestPath, `extension '${unit}'`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|