@stigmer/server 3.22.1-dev.20260922095755 → 3.23.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/artifactstorage/artifact-storage.d.ts +29 -31
- package/dist/artifactstorage/artifact-storage.d.ts.map +1 -1
- package/dist/artifactstorage/artifact-storage.js +5 -7
- package/dist/artifactstorage/artifact-storage.js.map +1 -1
- package/dist/artifactstorage/r2-storage.d.ts +12 -7
- package/dist/artifactstorage/r2-storage.d.ts.map +1 -1
- package/dist/artifactstorage/r2-storage.js +6 -14
- package/dist/artifactstorage/r2-storage.js.map +1 -1
- package/dist/authorization/authorizer.d.ts +8 -4
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +9 -5
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/derived-tuples.d.ts.map +1 -1
- package/dist/authorization/derived-tuples.js +25 -10
- package/dist/authorization/derived-tuples.js.map +1 -1
- package/dist/authorization/evaluator.d.ts +8 -7
- package/dist/authorization/evaluator.d.ts.map +1 -1
- package/dist/authorization/evaluator.js +20 -4
- package/dist/authorization/evaluator.js.map +1 -1
- package/dist/authorization/list-read-scope.d.ts.map +1 -1
- package/dist/authorization/list-read-scope.js +9 -5
- package/dist/authorization/list-read-scope.js.map +1 -1
- package/dist/authorization/model/agent.d.ts.map +1 -1
- package/dist/authorization/model/agent.js +5 -4
- package/dist/authorization/model/agent.js.map +1 -1
- package/dist/authorization/model/agent_instance.d.ts.map +1 -1
- package/dist/authorization/model/agent_instance.js +2 -2
- package/dist/authorization/model/agent_instance.js.map +1 -1
- package/dist/authorization/model/mcp_server.d.ts.map +1 -1
- package/dist/authorization/model/mcp_server.js +2 -2
- package/dist/authorization/model/mcp_server.js.map +1 -1
- package/dist/authorization/model/plugin.d.ts.map +1 -1
- package/dist/authorization/model/plugin.js +4 -3
- package/dist/authorization/model/plugin.js.map +1 -1
- package/dist/authorization/model/rewrite.d.ts +10 -8
- package/dist/authorization/model/rewrite.d.ts.map +1 -1
- package/dist/authorization/model/rewrite.js +3 -0
- package/dist/authorization/model/rewrite.js.map +1 -1
- package/dist/authorization/model/skill.d.ts.map +1 -1
- package/dist/authorization/model/skill.js +2 -2
- package/dist/authorization/model/skill.js.map +1 -1
- package/dist/authorization/model/workflow.d.ts.map +1 -1
- package/dist/authorization/model/workflow.js +2 -2
- package/dist/authorization/model/workflow.js.map +1 -1
- package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
- package/dist/authorization/model/workflow_instance.js +2 -2
- package/dist/authorization/model/workflow_instance.js.map +1 -1
- package/dist/authorization/organization-directory.d.ts.map +1 -1
- package/dist/authorization/organization-directory.js +3 -2
- package/dist/authorization/organization-directory.js.map +1 -1
- package/dist/authorization/store-test-kit.d.ts +6 -0
- package/dist/authorization/store-test-kit.d.ts.map +1 -1
- package/dist/authorization/store-test-kit.js +62 -16
- package/dist/authorization/store-test-kit.js.map +1 -1
- package/dist/authorization/tuples.d.ts +32 -13
- package/dist/authorization/tuples.d.ts.map +1 -1
- package/dist/authorization/tuples.js +11 -3
- package/dist/authorization/tuples.js.map +1 -1
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +49 -24
- package/dist/boot/compose.js.map +1 -1
- package/dist/boot/config.d.ts +4 -3
- package/dist/boot/config.d.ts.map +1 -1
- package/dist/boot/config.js.map +1 -1
- package/dist/boot/skill-transfer-origin.d.ts +52 -0
- package/dist/boot/skill-transfer-origin.d.ts.map +1 -0
- package/dist/boot/skill-transfer-origin.js +54 -0
- package/dist/boot/skill-transfer-origin.js.map +1 -0
- package/dist/domain/agent/controller.d.ts.map +1 -1
- package/dist/domain/agent/controller.js +4 -4
- package/dist/domain/agent/controller.js.map +1 -1
- package/dist/domain/agentchannel/controller.d.ts.map +1 -1
- package/dist/domain/agentchannel/controller.js +1 -3
- package/dist/domain/agentchannel/controller.js.map +1 -1
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +1 -3
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentinstance/controller.d.ts.map +1 -1
- package/dist/domain/agentinstance/controller.js +4 -3
- package/dist/domain/agentinstance/controller.js.map +1 -1
- package/dist/domain/agentshare/constants.d.ts +11 -6
- package/dist/domain/agentshare/constants.d.ts.map +1 -1
- package/dist/domain/agentshare/constants.js +16 -8
- package/dist/domain/agentshare/constants.js.map +1 -1
- package/dist/domain/agentshare/controller.d.ts.map +1 -1
- package/dist/domain/agentshare/controller.js +1 -3
- package/dist/domain/agentshare/controller.js.map +1 -1
- package/dist/domain/agentshare/steps.d.ts +25 -18
- package/dist/domain/agentshare/steps.d.ts.map +1 -1
- package/dist/domain/agentshare/steps.js +142 -41
- package/dist/domain/agentshare/steps.js.map +1 -1
- package/dist/domain/environment/controller.d.ts.map +1 -1
- package/dist/domain/environment/controller.js +3 -2
- package/dist/domain/environment/controller.js.map +1 -1
- package/dist/domain/executioncontext/controller.d.ts.map +1 -1
- package/dist/domain/executioncontext/controller.js +1 -2
- package/dist/domain/executioncontext/controller.js.map +1 -1
- package/dist/domain/mcpserver/controller.d.ts.map +1 -1
- package/dist/domain/mcpserver/controller.js +4 -3
- package/dist/domain/mcpserver/controller.js.map +1 -1
- package/dist/domain/plugin/controller.d.ts +3 -7
- package/dist/domain/plugin/controller.d.ts.map +1 -1
- package/dist/domain/plugin/controller.js +11 -9
- package/dist/domain/plugin/controller.js.map +1 -1
- package/dist/domain/plugin/push.d.ts +5 -7
- package/dist/domain/plugin/push.d.ts.map +1 -1
- package/dist/domain/plugin/push.js +14 -8
- package/dist/domain/plugin/push.js.map +1 -1
- package/dist/domain/schedule/controller.d.ts.map +1 -1
- package/dist/domain/schedule/controller.js +1 -3
- package/dist/domain/schedule/controller.js.map +1 -1
- package/dist/domain/session/controller.d.ts.map +1 -1
- package/dist/domain/session/controller.js +2 -3
- package/dist/domain/session/controller.js.map +1 -1
- package/dist/domain/skill/constants.d.ts +26 -3
- package/dist/domain/skill/constants.d.ts.map +1 -1
- package/dist/domain/skill/constants.js +26 -3
- package/dist/domain/skill/constants.js.map +1 -1
- package/dist/domain/skill/controller.d.ts +6 -11
- package/dist/domain/skill/controller.d.ts.map +1 -1
- package/dist/domain/skill/controller.js +35 -22
- package/dist/domain/skill/controller.js.map +1 -1
- package/dist/domain/skill/push.d.ts +2 -2
- package/dist/domain/skill/push.d.ts.map +1 -1
- package/dist/domain/skill/push.js +2 -2
- package/dist/domain/skill/push.js.map +1 -1
- package/dist/domain/skill/transfer/handler.d.ts +17 -9
- package/dist/domain/skill/transfer/handler.d.ts.map +1 -1
- package/dist/domain/skill/transfer/handler.js +19 -8
- package/dist/domain/skill/transfer/handler.js.map +1 -1
- package/dist/domain/skill/transfer/slots.d.ts +26 -41
- package/dist/domain/skill/transfer/slots.d.ts.map +1 -1
- package/dist/domain/skill/transfer/slots.js +80 -87
- package/dist/domain/skill/transfer/slots.js.map +1 -1
- package/dist/domain/skill/transfer/staging.d.ts +48 -0
- package/dist/domain/skill/transfer/staging.d.ts.map +1 -0
- package/dist/domain/skill/transfer/staging.js +127 -0
- package/dist/domain/skill/transfer/staging.js.map +1 -0
- package/dist/domain/workflow/controller.d.ts.map +1 -1
- package/dist/domain/workflow/controller.js +6 -14
- package/dist/domain/workflow/controller.js.map +1 -1
- package/dist/domain/workflow/registry/data/task-kind-registry.json +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.d.ts +1 -10
- package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.js +2 -3
- package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +2 -4
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
- package/dist/domain/workflowinstance/controller.js +4 -3
- package/dist/domain/workflowinstance/controller.js.map +1 -1
- package/dist/extensions/authorization-queries.d.ts +5 -6
- package/dist/extensions/authorization-queries.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.d.ts +1 -1
- package/dist/extensions/resource-authorization.d.ts +2 -3
- package/dist/extensions/resource-authorization.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/apiresource-meta.d.ts +3 -12
- package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
- package/dist/pipeline/apiresource-meta.js +10 -21
- package/dist/pipeline/apiresource-meta.js.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.d.ts +3 -9
- package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.js +11 -9
- package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
- package/dist/pipeline/steps/references.d.ts +20 -178
- package/dist/pipeline/steps/references.d.ts.map +1 -1
- package/dist/pipeline/steps/references.js +43 -344
- package/dist/pipeline/steps/references.js.map +1 -1
- package/dist/pipeline/steps/resolve-artifact-source.d.ts +11 -8
- package/dist/pipeline/steps/resolve-artifact-source.d.ts.map +1 -1
- package/dist/pipeline/steps/resolve-artifact-source.js +8 -4
- package/dist/pipeline/steps/resolve-artifact-source.js.map +1 -1
- package/dist/pipeline/steps/visibility-gates.d.ts +27 -0
- package/dist/pipeline/steps/visibility-gates.d.ts.map +1 -0
- package/dist/pipeline/steps/visibility-gates.js +139 -0
- package/dist/pipeline/steps/visibility-gates.js.map +1 -0
- package/dist/query/search/controller.js +1 -0
- package/dist/query/search/controller.js.map +1 -1
- package/dist/query/search/criteria.d.ts +5 -1
- package/dist/query/search/criteria.d.ts.map +1 -1
- package/dist/query/search/criteria.js +13 -3
- package/dist/query/search/criteria.js.map +1 -1
- package/dist/query/search/handler.d.ts +4 -5
- package/dist/query/search/handler.d.ts.map +1 -1
- package/dist/query/search/handler.js +6 -7
- package/dist/query/search/handler.js.map +1 -1
- package/dist/query/search/query-store.d.ts.map +1 -1
- package/dist/query/search/query-store.js +2 -0
- package/dist/query/search/query-store.js.map +1 -1
- package/dist/store/interface.d.ts +6 -3
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/dist/store/postgres/migrations.d.ts +1 -3
- package/dist/store/postgres/migrations.d.ts.map +1 -1
- package/dist/store/postgres/migrations.js +1 -35
- package/dist/store/postgres/migrations.js.map +1 -1
- package/dist/store/postgres/store.d.ts.map +1 -1
- package/dist/store/postgres/store.js +9 -4
- package/dist/store/postgres/store.js.map +1 -1
- package/dist/store/sqlite/migrations.d.ts +1 -3
- package/dist/store/sqlite/migrations.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.js +1 -36
- package/dist/store/sqlite/migrations.js.map +1 -1
- package/dist/store/sqlite/store.d.ts.map +1 -1
- package/dist/store/sqlite/store.js +8 -4
- package/dist/store/sqlite/store.js.map +1 -1
- package/package.json +6 -6
- package/src/artifactstorage/__tests__/artifact-storage.test.ts +36 -33
- package/src/artifactstorage/__tests__/r2-storage.test.ts +11 -13
- package/src/artifactstorage/artifact-storage.ts +37 -33
- package/src/artifactstorage/r2-storage.ts +6 -15
- package/src/authorization/README.md +26 -24
- package/src/authorization/__tests__/authorizer.test.ts +9 -10
- package/src/authorization/__tests__/derived-tuples.test.ts +11 -4
- package/src/authorization/__tests__/evaluator.test.ts +73 -14
- package/src/authorization/__tests__/facts.test.ts +2 -2
- package/src/authorization/__tests__/fixtures/fga/agent-instance-creation.fga.yaml +185 -0
- package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +98 -6
- package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +98 -0
- package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +61 -4
- package/src/authorization/__tests__/fixtures/fga/mcp-server-authoring.fga.yaml +143 -0
- package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +21 -1
- package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +25 -1
- package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +52 -0
- package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +3 -1
- package/src/authorization/__tests__/list-read-scope.test.ts +17 -18
- package/src/authorization/__tests__/store-tests.test.ts +8 -1
- package/src/authorization/__tests__/tuples.test.ts +11 -12
- package/src/authorization/authorizer.ts +9 -4
- package/src/authorization/derived-tuples.ts +31 -14
- package/src/authorization/evaluator.ts +44 -8
- package/src/authorization/list-read-scope.ts +11 -5
- package/src/authorization/model/agent.ts +5 -2
- package/src/authorization/model/agent_instance.ts +2 -0
- package/src/authorization/model/mcp_server.ts +2 -0
- package/src/authorization/model/plugin.ts +4 -1
- package/src/authorization/model/rewrite.ts +13 -8
- package/src/authorization/model/skill.ts +2 -0
- package/src/authorization/model/workflow.ts +2 -0
- package/src/authorization/model/workflow_instance.ts +2 -0
- package/src/authorization/organization-directory.ts +3 -1
- package/src/authorization/store-test-kit.ts +75 -16
- package/src/authorization/tuples.ts +39 -16
- package/src/boot/__tests__/skill-transfer-origin.test.ts +72 -0
- package/src/boot/compose.ts +62 -25
- package/src/boot/config.ts +4 -3
- package/src/boot/skill-transfer-origin.ts +76 -0
- package/src/domain/agent/__tests__/agent.test.ts +4 -11
- package/src/domain/agent/controller.ts +7 -7
- package/src/domain/agentchannel/__tests__/agentchannel.test.ts +48 -223
- package/src/domain/agentchannel/controller.ts +1 -6
- package/src/domain/agentexecution/controller.ts +1 -6
- package/src/domain/agentinstance/controller.ts +12 -6
- package/src/domain/agentshare/__tests__/agentshare.test.ts +172 -196
- package/src/domain/agentshare/__tests__/create-authorization.test.ts +31 -16
- package/src/domain/agentshare/constants.ts +26 -14
- package/src/domain/agentshare/controller.ts +1 -6
- package/src/domain/agentshare/steps.ts +194 -41
- package/src/domain/environment/controller.ts +8 -5
- package/src/domain/executioncontext/controller.ts +1 -5
- package/src/domain/mcpserver/__tests__/mcpserver.test.ts +28 -115
- package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +18 -53
- package/src/domain/mcpserver/controller.ts +12 -6
- package/src/domain/plugin/controller.ts +23 -18
- package/src/domain/plugin/push.ts +16 -8
- package/src/domain/schedule/controller.ts +1 -6
- package/src/domain/session/controller.ts +4 -12
- package/src/domain/skill/__tests__/skill.test.ts +5 -3
- package/src/domain/skill/__tests__/slots.test.ts +113 -60
- package/src/domain/skill/__tests__/staging.test.ts +304 -0
- package/src/domain/skill/constants.ts +28 -3
- package/src/domain/skill/controller.ts +51 -35
- package/src/domain/skill/push.ts +3 -3
- package/src/domain/skill/transfer/handler.ts +33 -15
- package/src/domain/skill/transfer/slots.ts +89 -100
- package/src/domain/skill/transfer/staging.ts +191 -0
- package/src/domain/workflow/controller.ts +10 -28
- package/src/domain/workflow/registry/data/task-kind-registry.json +1 -1
- package/src/domain/workflow/validation/task-config-constraints.ts +3 -7
- package/src/domain/workflowexecution/controller.ts +2 -7
- package/src/domain/workflowinstance/controller.ts +12 -6
- package/src/extensions/__tests__/built-in-authorization-composed.test.ts +19 -32
- package/src/extensions/__tests__/extension-composition.test.ts +4 -4
- package/src/extensions/__tests__/list-read-scope-composed.test.ts +6 -9
- package/src/extensions/authorization-queries.ts +5 -6
- package/src/extensions/list-read-scope.ts +1 -1
- package/src/extensions/resource-authorization.ts +5 -3
- package/src/index.ts +14 -0
- package/src/pipeline/apiresource-meta.ts +10 -21
- package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +31 -18
- package/src/pipeline/steps/__tests__/visibility-gates.test.ts +187 -0
- package/src/pipeline/steps/authorization-tuples.ts +11 -9
- package/src/pipeline/steps/references.ts +70 -578
- package/src/pipeline/steps/resolve-artifact-source.ts +20 -13
- package/src/pipeline/steps/visibility-gates.ts +174 -0
- package/src/query/search/__tests__/criteria.test.ts +4 -0
- package/src/query/search/__tests__/query-store.test.ts +40 -11
- package/src/query/search/controller.ts +1 -0
- package/src/query/search/criteria.ts +14 -0
- package/src/query/search/handler.ts +7 -6
- package/src/query/search/query-store.ts +2 -0
- package/src/store/__tests__/store-contract.ts +51 -6
- package/src/store/interface.ts +6 -3
- package/src/store/postgres/__tests__/migrations.test.ts +6 -187
- package/src/store/postgres/__tests__/store-contract.test.ts +2 -0
- package/src/store/postgres/migrations.ts +1 -48
- package/src/store/postgres/store.ts +11 -4
- package/src/store/sqlite/__tests__/migrations.test.ts +5 -191
- package/src/store/sqlite/migrations.ts +1 -45
- package/src/store/sqlite/store.ts +10 -4
- package/src/transport/registry/__tests__/lanes.test.ts +1 -1
- package/dist/domain/workflow/agent-call-references.d.ts +0 -54
- package/dist/domain/workflow/agent-call-references.d.ts.map +0 -1
- package/dist/domain/workflow/agent-call-references.js +0 -97
- package/dist/domain/workflow/agent-call-references.js.map +0 -1
- package/dist/store/public-visibility-retired.d.ts +0 -67
- package/dist/store/public-visibility-retired.d.ts.map +0 -1
- package/dist/store/public-visibility-retired.js +0 -55
- package/dist/store/public-visibility-retired.js.map +0 -1
- package/src/domain/workflow/__tests__/agent-call-references.test.ts +0 -222
- package/src/domain/workflow/agent-call-references.ts +0 -157
- package/src/pipeline/__tests__/supports-visibility.test.ts +0 -112
- package/src/pipeline/steps/__tests__/references.test.ts +0 -703
- package/src/store/__tests__/public-visibility-retired.test.ts +0 -127
- package/src/store/public-visibility-retired.ts +0 -119
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Archive staging — how an artifact above the gRPC message cap travels,
|
|
3
|
+
* on every blob driver, through one code path (#675, stigmer#1219).
|
|
4
|
+
*
|
|
5
|
+
* The domain owns the two identities and the mapping between them: the
|
|
6
|
+
* wire reference a client is handed (`sau_<hex>`, the capability the
|
|
7
|
+
* push presents) and the staging key the bytes land under in the blob
|
|
8
|
+
* store (`skills/staging/<hex>.zip`, the surface a sweep targets). A
|
|
9
|
+
* driver is asked only to sign or stage a PUT for a key it is given, to
|
|
10
|
+
* read the key back, and to delete it — so the local driver rides the
|
|
11
|
+
* transfer lane's slot registry and a bucket driver signs a PUT straight
|
|
12
|
+
* to the bucket, and nothing above this module can tell which.
|
|
13
|
+
*
|
|
14
|
+
* Consume reads then deletes: the delete is what keeps a reference
|
|
15
|
+
* single-use on a bucket, whose signed URL is otherwise repeatable within
|
|
16
|
+
* its TTL. The delete is best effort and loud — the bytes are already in
|
|
17
|
+
* hand, a replay of the same reference is a content-addressed no-op, and
|
|
18
|
+
* the bucket lifecycle rule on the staging prefix sweeps what the delete
|
|
19
|
+
* missed. On the local driver the same delete retires the staged file the
|
|
20
|
+
* slot registry would otherwise sweep at expiry.
|
|
21
|
+
*
|
|
22
|
+
* A reference the driver cannot read — malformed, never uploaded,
|
|
23
|
+
* expired, already consumed — is one condition on every driver
|
|
24
|
+
* ("unknown or expired"): a bucket cannot distinguish them, so neither
|
|
25
|
+
* does the wire.
|
|
26
|
+
*
|
|
27
|
+
* Proven by __tests__/staging.test.ts over a fake driver and over the
|
|
28
|
+
* real local driver with its slot registry; the conformance suite's
|
|
29
|
+
* transfer-lane block pins the wire behaviour on both editions.
|
|
30
|
+
*/
|
|
31
|
+
import { randomBytes } from "node:crypto";
|
|
32
|
+
|
|
33
|
+
import type { ArtifactStorage } from "../../../artifactstorage/artifact-storage.js";
|
|
34
|
+
import { ArtifactStorageNotFoundError } from "../../../artifactstorage/artifact-storage.js";
|
|
35
|
+
import type { Logger } from "../../../boot/logger.js";
|
|
36
|
+
import {
|
|
37
|
+
DEFAULT_SLOT_TTL_MS,
|
|
38
|
+
DOWNLOAD_URL_TTL_MS,
|
|
39
|
+
MAX_ZIP_SIZE,
|
|
40
|
+
REF_BYTE_LEN,
|
|
41
|
+
REF_PREFIX,
|
|
42
|
+
STAGING_KEY_PREFIX,
|
|
43
|
+
} from "../constants.js";
|
|
44
|
+
import { SlotUnknownError } from "./slots.js";
|
|
45
|
+
|
|
46
|
+
/** The hex the reference and the staging key share; 128 bits, lowercase. */
|
|
47
|
+
const HEX = new RegExp(`^[0-9a-f]{${REF_BYTE_LEN * 2}}$`);
|
|
48
|
+
const STAGED_ARCHIVE_SUFFIX = ".zip";
|
|
49
|
+
|
|
50
|
+
/** The shared hex of a well-formed reference or key, else undefined. */
|
|
51
|
+
function hexBetween(
|
|
52
|
+
value: string,
|
|
53
|
+
prefix: string,
|
|
54
|
+
suffix: string,
|
|
55
|
+
): string | undefined {
|
|
56
|
+
if (!value.startsWith(prefix) || !value.endsWith(suffix)) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
const hex = value.slice(prefix.length, value.length - suffix.length);
|
|
60
|
+
return HEX.test(hex) ? hex : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** A fresh upload reference: the prefix and 128 random bits, hex. */
|
|
64
|
+
export function newUploadRef(): string {
|
|
65
|
+
return REF_PREFIX + randomBytes(REF_BYTE_LEN).toString("hex");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The staging key a reference's bytes land under, or undefined when the
|
|
70
|
+
* reference is not one this server could have minted. Refs arrive from
|
|
71
|
+
* clients (the push request, the lane's URL path), so the shape is
|
|
72
|
+
* checked before it becomes a storage key.
|
|
73
|
+
*/
|
|
74
|
+
export function stagingKeyOf(ref: string): string | undefined {
|
|
75
|
+
const hex = hexBetween(ref, REF_PREFIX, "");
|
|
76
|
+
return hex === undefined
|
|
77
|
+
? undefined
|
|
78
|
+
: `${STAGING_KEY_PREFIX}${hex}${STAGED_ARCHIVE_SUFFIX}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The reference for a staging key this module minted. Keys reach here from
|
|
83
|
+
* the domain's own mint, never from a client, so a foreign shape is a
|
|
84
|
+
* programming error and throws.
|
|
85
|
+
*/
|
|
86
|
+
export function uploadRefOf(stagingKey: string): string {
|
|
87
|
+
const hex = hexBetween(stagingKey, STAGING_KEY_PREFIX, STAGED_ARCHIVE_SUFFIX);
|
|
88
|
+
if (hex === undefined) {
|
|
89
|
+
throw new Error(`${stagingKey} is not a staging key this domain minted`);
|
|
90
|
+
}
|
|
91
|
+
return REF_PREFIX + hex;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** What createArtifactUploadUrl hands the client. */
|
|
95
|
+
export interface StagedUpload {
|
|
96
|
+
/** The single-use reference the push presents. */
|
|
97
|
+
readonly ref: string;
|
|
98
|
+
/** Accepts one HTTP PUT of exactly the declared byte count. */
|
|
99
|
+
readonly url: string;
|
|
100
|
+
/** How long the reference and the URL stay valid. */
|
|
101
|
+
readonly ttlMs: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** What getArtifactDownloadUrl hands the client. */
|
|
105
|
+
export interface DownloadCapability {
|
|
106
|
+
readonly url: string;
|
|
107
|
+
/** The floor of the URL's validity (DOWNLOAD_URL_TTL_MS). */
|
|
108
|
+
readonly ttlMs: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The staging port both archive kinds (skills, plugins) push through. */
|
|
112
|
+
export interface ArchiveStaging {
|
|
113
|
+
/** Mints a reference and the URL its bytes are PUT to. */
|
|
114
|
+
mint(declaredSizeBytes: number): Promise<StagedUpload>;
|
|
115
|
+
/**
|
|
116
|
+
* Reads the staged bytes for a reference and retires it. Throws
|
|
117
|
+
* SlotUnknownError when the driver holds nothing under the reference;
|
|
118
|
+
* any other failure is the driver's and propagates as an infrastructure
|
|
119
|
+
* fault.
|
|
120
|
+
*/
|
|
121
|
+
consume(ref: string): Promise<Uint8Array>;
|
|
122
|
+
/** A time-limited download URL for a stored archive key. */
|
|
123
|
+
downloadUrl(storageKey: string): Promise<DownloadCapability>;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function newArchiveStaging(
|
|
127
|
+
driver: ArtifactStorage,
|
|
128
|
+
logger: Logger,
|
|
129
|
+
): ArchiveStaging {
|
|
130
|
+
return {
|
|
131
|
+
async mint(declaredSizeBytes) {
|
|
132
|
+
// The controllers refuse over-limit declarations with the limit in
|
|
133
|
+
// the message before reaching here; this is the module's own
|
|
134
|
+
// invariant, so no driver is ever asked to stage more than the
|
|
135
|
+
// archive ceiling.
|
|
136
|
+
if (declaredSizeBytes <= 0 || declaredSizeBytes > MAX_ZIP_SIZE) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`declared size ${declaredSizeBytes} outside (0, ${MAX_ZIP_SIZE}]`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const ref = newUploadRef();
|
|
142
|
+
const key = stagingKeyOf(ref);
|
|
143
|
+
if (key === undefined) {
|
|
144
|
+
throw new Error(`minted reference ${ref} has no staging key`);
|
|
145
|
+
}
|
|
146
|
+
const presigned = await driver.presignPut(
|
|
147
|
+
key,
|
|
148
|
+
declaredSizeBytes,
|
|
149
|
+
DEFAULT_SLOT_TTL_MS,
|
|
150
|
+
);
|
|
151
|
+
return { ref, url: presigned.url, ttlMs: presigned.ttlMs };
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
async consume(ref) {
|
|
155
|
+
const key = stagingKeyOf(ref);
|
|
156
|
+
if (key === undefined) {
|
|
157
|
+
throw new SlotUnknownError();
|
|
158
|
+
}
|
|
159
|
+
let data: Uint8Array;
|
|
160
|
+
try {
|
|
161
|
+
// The allocation is bounded by construction: a bucket accepts only
|
|
162
|
+
// the signed Content-Length, the slot registry only the reserved
|
|
163
|
+
// size, and both were checked against MAX_ZIP_SIZE at mint.
|
|
164
|
+
data = await driver.download(key);
|
|
165
|
+
} catch (error) {
|
|
166
|
+
if (error instanceof ArtifactStorageNotFoundError) {
|
|
167
|
+
throw new SlotUnknownError();
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
await driver.delete(key);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
logger.error("failed to delete consumed staged artifact", {
|
|
175
|
+
stagingKey: key,
|
|
176
|
+
error: error instanceof Error ? error.message : String(error),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return data;
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
async downloadUrl(storageKey) {
|
|
183
|
+
const url = await driver.getSignedUrl(
|
|
184
|
+
storageKey,
|
|
185
|
+
DOWNLOAD_URL_TTL_MS,
|
|
186
|
+
"",
|
|
187
|
+
);
|
|
188
|
+
return { url, ttlMs: DOWNLOAD_URL_TTL_MS };
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
@@ -78,6 +78,10 @@ import {
|
|
|
78
78
|
} from "../../pipeline/steps/authorize-resolved-target.js";
|
|
79
79
|
import { versionHistoryTarget } from "../../pipeline/steps/version-history.js";
|
|
80
80
|
import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
|
|
81
|
+
import {
|
|
82
|
+
newAuthorizeVisibilityTransitionStep,
|
|
83
|
+
newGuardPublicVisibilityStep,
|
|
84
|
+
} from "../../pipeline/steps/visibility-gates.js";
|
|
81
85
|
import { newBuildUpdateStateStep } from "../../pipeline/steps/build-update-state.js";
|
|
82
86
|
import {
|
|
83
87
|
setAuditFieldsForUpdate,
|
|
@@ -108,16 +112,7 @@ import {
|
|
|
108
112
|
TARGET_RESOURCE_KEY,
|
|
109
113
|
newLoadTargetStep,
|
|
110
114
|
} from "../../pipeline/steps/load-target.js";
|
|
111
|
-
import {
|
|
112
|
-
collectSpecReferences,
|
|
113
|
-
newGuardReferenceFloorOnEscalationStep,
|
|
114
|
-
newNormalizeReferencesStep,
|
|
115
|
-
newValidateReferencesStep,
|
|
116
|
-
} from "../../pipeline/steps/references.js";
|
|
117
|
-
import {
|
|
118
|
-
collectAgentCallReferences,
|
|
119
|
-
newValidateAgentCallReferencesStep,
|
|
120
|
-
} from "./agent-call-references.js";
|
|
115
|
+
import { newNormalizeReferencesStep } from "../../pipeline/steps/references.js";
|
|
121
116
|
import {
|
|
122
117
|
newCleanupIamPoliciesStep,
|
|
123
118
|
newCreateAuthorizationTuplesStep,
|
|
@@ -243,10 +238,9 @@ async function createWorkflow(
|
|
|
243
238
|
.addStep(newResolveSlugStep())
|
|
244
239
|
.addStep(newCheckDuplicateStep(deps.store))
|
|
245
240
|
.addStep(newBuildNewStateStep())
|
|
241
|
+
.addStep(newGuardPublicVisibilityStep(deps.authorizer))
|
|
246
242
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
247
243
|
.addStep(newNormalizeReferencesStep())
|
|
248
|
-
.addStep(newValidateReferencesStep(deps.store))
|
|
249
|
-
.addStep(newValidateAgentCallReferencesStep(deps.store))
|
|
250
244
|
.addStep(newPopulateServerlessValidationStep(deps.logger))
|
|
251
245
|
.addStep(newComputeVersionHashStep(deps.logger))
|
|
252
246
|
.addStep(newPopulateVersionHashStep(true))
|
|
@@ -311,8 +305,6 @@ async function update(
|
|
|
311
305
|
.addStep(newBuildUpdateStateStep())
|
|
312
306
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
313
307
|
.addStep(newNormalizeReferencesStep())
|
|
314
|
-
.addStep(newValidateReferencesStep(deps.store))
|
|
315
|
-
.addStep(newValidateAgentCallReferencesStep(deps.store))
|
|
316
308
|
.addStep(newPopulateServerlessValidationStepForUpdate(deps.logger))
|
|
317
309
|
.addStep(newComputeVersionHashStep(deps.logger))
|
|
318
310
|
.addStep(newCheckVersionChangedStep(deps.logger))
|
|
@@ -419,8 +411,8 @@ async function deleteWorkflow(
|
|
|
419
411
|
// updateVisibility — update_visibility.go: a targeted metadata update (only
|
|
420
412
|
// metadata.visibility changes; spec/status untouched). The level check runs
|
|
421
413
|
// AFTER load, preserving the cross-edition error precedence: unknown id +
|
|
422
|
-
// bad level = NOT_FOUND on both editions.
|
|
423
|
-
//
|
|
414
|
+
// bad level = NOT_FOUND on both editions. CW-9's suite block is the wire
|
|
415
|
+
// pin (clientPublicVisibilityWrites, true on local targets).
|
|
424
416
|
// ---------------------------------------------------------------------------
|
|
425
417
|
|
|
426
418
|
const UPDATE_VISIBILITY_WORKFLOW_KEY = "updateVisibilityWorkflow";
|
|
@@ -460,20 +452,10 @@ async function updateVisibility(
|
|
|
460
452
|
newRecordVisibilityBeforeUpdateStep(UPDATE_VISIBILITY_WORKFLOW_KEY),
|
|
461
453
|
)
|
|
462
454
|
.addStep(newValidateVisibilityUpdateStep())
|
|
463
|
-
// The reference floor's second door: a workflow may not be raised above
|
|
464
|
-
// the agents its agent_call tasks run.
|
|
465
455
|
.addStep(
|
|
466
|
-
|
|
467
|
-
deps.store,
|
|
456
|
+
newAuthorizeVisibilityTransitionStep(
|
|
468
457
|
UPDATE_VISIBILITY_WORKFLOW_KEY,
|
|
469
|
-
|
|
470
|
-
(row) => collectSpecReferences(WorkflowSchema, row),
|
|
471
|
-
(row) =>
|
|
472
|
-
collectAgentCallReferences(
|
|
473
|
-
(row as Workflow).spec,
|
|
474
|
-
(row as Workflow).metadata?.org ?? "",
|
|
475
|
-
),
|
|
476
|
-
],
|
|
458
|
+
deps.authorizer,
|
|
477
459
|
),
|
|
478
460
|
)
|
|
479
461
|
.addStep(newSetWorkflowVisibilityStep())
|
|
@@ -1525,7 +1525,7 @@
|
|
|
1525
1525
|
"type": "string"
|
|
1526
1526
|
},
|
|
1527
1527
|
"org": {
|
|
1528
|
-
"description": "Organization that owns the referenced resource. When non-empty: must be a valid org slug (lowercase alphanumeric with hyphens, starts with a letter, 1-63 characters). Example: \"stigmer\", \"acme-corp\". When empty: the reference is relative — the server resolves it to the parent resource's organization at write time. All stored and returned references always have org populated (absolute form). Use empty org for same-org references (the common case).
|
|
1528
|
+
"description": "Organization that owns the referenced resource. When non-empty: must be a valid org slug (lowercase alphanumeric with hyphens, starts with a letter, 1-63 characters). Example: \"stigmer\", \"acme-corp\". When empty: the reference is relative — the server resolves it to the parent resource's organization at write time. All stored and returned references always have org populated (absolute form). Use empty org for same-org references (the common case). Use explicit org for cross-org references (e.g., marketplace resources).",
|
|
1529
1529
|
"maxLength": 63,
|
|
1530
1530
|
"pattern": "^$|^[a-z][a-z0-9-]*[a-z0-9]$",
|
|
1531
1531
|
"type": "string"
|
|
@@ -105,10 +105,7 @@ function taskConstraintViolations(task: WorkflowTask): string[] {
|
|
|
105
105
|
* task_config is a Struct again — that is what the walker's recursion is
|
|
106
106
|
* for).
|
|
107
107
|
*/
|
|
108
|
-
function configConstraintViolations(
|
|
109
|
-
task: WorkflowTask,
|
|
110
|
-
cfg: Message,
|
|
111
|
-
): string[] {
|
|
108
|
+
function configConstraintViolations(task: WorkflowTask, cfg: Message): string[] {
|
|
112
109
|
const schema = taskConfigSchemaFor(task.kind);
|
|
113
110
|
if (schema === undefined) {
|
|
114
111
|
// Unknown kinds were already rejected by validateTaskKinds; an
|
|
@@ -132,10 +129,9 @@ function configConstraintViolations(
|
|
|
132
129
|
* The WorkflowTask lists embedded in a control-flow task config, in
|
|
133
130
|
* declaration order — the same recursion set the converter walks
|
|
134
131
|
* (convertForTask/convertForkTask/convertTryTask) and the cloud Java
|
|
135
|
-
* TaskConfigStrictParser mirrors.
|
|
136
|
-
* collector (../agent-call-references.ts), which walks the same tree.
|
|
132
|
+
* TaskConfigStrictParser mirrors.
|
|
137
133
|
*/
|
|
138
|
-
|
|
134
|
+
function nestedTasks(cfg: Message): WorkflowTask[] {
|
|
139
135
|
switch (cfg.$typeName) {
|
|
140
136
|
case ForTaskConfigSchema.typeName:
|
|
141
137
|
return (cfg as ForTaskConfig).do;
|
|
@@ -72,10 +72,7 @@ import {
|
|
|
72
72
|
TARGET_RESOURCE_KEY,
|
|
73
73
|
newLoadTargetStep,
|
|
74
74
|
} from "../../pipeline/steps/load-target.js";
|
|
75
|
-
import {
|
|
76
|
-
newNormalizeReferencesStep,
|
|
77
|
-
newValidateReferencesStep,
|
|
78
|
-
} from "../../pipeline/steps/references.js";
|
|
75
|
+
import { newNormalizeReferencesStep } from "../../pipeline/steps/references.js";
|
|
79
76
|
import {
|
|
80
77
|
newCleanupIamPoliciesStep,
|
|
81
78
|
newCreateAuthorizationTuplesStep,
|
|
@@ -302,8 +299,7 @@ async function createExecution(
|
|
|
302
299
|
.addStep(newCheckDuplicateStep(deps.store))
|
|
303
300
|
.addStep(newBuildNewStateStep())
|
|
304
301
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
305
|
-
.addStep(newNormalizeReferencesStep())
|
|
306
|
-
.addStep(newValidateReferencesStep(deps.store));
|
|
302
|
+
.addStep(newNormalizeReferencesStep());
|
|
307
303
|
// The ratified sandbox-acquisition gate slot (blueprint 03 §3a; C4):
|
|
308
304
|
// the Java-verified capacity-gate position — after Authorize and every
|
|
309
305
|
// resolution step (the default instance, like Java's, side-effects
|
|
@@ -393,7 +389,6 @@ async function update(
|
|
|
393
389
|
.addStep(newBuildUpdateStateStep())
|
|
394
390
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
395
391
|
.addStep(newNormalizeReferencesStep())
|
|
396
|
-
.addStep(newValidateReferencesStep(deps.store))
|
|
397
392
|
.addStep(newPersistStep(deps.store))
|
|
398
393
|
.addStep(
|
|
399
394
|
newIndexSearchStep(
|
|
@@ -58,6 +58,10 @@ import {
|
|
|
58
58
|
newAuthorizeResolvedTargetStep,
|
|
59
59
|
} from "../../pipeline/steps/authorize-resolved-target.js";
|
|
60
60
|
import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
|
|
61
|
+
import {
|
|
62
|
+
newAuthorizeVisibilityTransitionStep,
|
|
63
|
+
newGuardPublicVisibilityStep,
|
|
64
|
+
} from "../../pipeline/steps/visibility-gates.js";
|
|
61
65
|
import {
|
|
62
66
|
newBuildNewStateStep,
|
|
63
67
|
setAuditFieldsForUpdate,
|
|
@@ -87,10 +91,7 @@ import {
|
|
|
87
91
|
TARGET_RESOURCE_KEY,
|
|
88
92
|
newLoadTargetStep,
|
|
89
93
|
} from "../../pipeline/steps/load-target.js";
|
|
90
|
-
import {
|
|
91
|
-
newNormalizeReferencesStep,
|
|
92
|
-
newValidateReferencesStep,
|
|
93
|
-
} from "../../pipeline/steps/references.js";
|
|
94
|
+
import { newNormalizeReferencesStep } from "../../pipeline/steps/references.js";
|
|
94
95
|
import {
|
|
95
96
|
newCleanupIamPoliciesStep,
|
|
96
97
|
newCreateAuthorizationTuplesStep,
|
|
@@ -197,9 +198,9 @@ async function createInstance(
|
|
|
197
198
|
.addStep(newValidateSameOrgBusinessRuleStep(deps.logger))
|
|
198
199
|
.addStep(newCheckDuplicateStep(deps.store))
|
|
199
200
|
.addStep(newBuildNewStateStep())
|
|
201
|
+
.addStep(newGuardPublicVisibilityStep(deps.authorizer))
|
|
200
202
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
201
203
|
.addStep(newNormalizeReferencesStep())
|
|
202
|
-
.addStep(newValidateReferencesStep(deps.store))
|
|
203
204
|
.addStep(newPersistStep(deps.store))
|
|
204
205
|
.addStep(
|
|
205
206
|
newCreateAuthorizationTuplesStep(
|
|
@@ -248,7 +249,6 @@ async function update(
|
|
|
248
249
|
.addStep(newBuildUpdateStateStep())
|
|
249
250
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
250
251
|
.addStep(newNormalizeReferencesStep())
|
|
251
|
-
.addStep(newValidateReferencesStep(deps.store))
|
|
252
252
|
.addStep(newPersistStep(deps.store))
|
|
253
253
|
.addStep(
|
|
254
254
|
newIndexSearchStep(
|
|
@@ -407,6 +407,12 @@ async function updateVisibility(
|
|
|
407
407
|
),
|
|
408
408
|
)
|
|
409
409
|
.addStep(newValidateVisibilityUpdateStep())
|
|
410
|
+
.addStep(
|
|
411
|
+
newAuthorizeVisibilityTransitionStep(
|
|
412
|
+
UPDATE_VISIBILITY_INSTANCE_KEY,
|
|
413
|
+
deps.authorizer,
|
|
414
|
+
),
|
|
415
|
+
)
|
|
410
416
|
.addStep(newSetInstanceVisibilityStep())
|
|
411
417
|
.addStep(
|
|
412
418
|
newPersistInstanceStep(
|
|
@@ -16,9 +16,8 @@
|
|
|
16
16
|
* outsider on an EXISTING resource hears PERMISSION_DENIED, the
|
|
17
17
|
* cloud's answer; `findMyOrganizations` lists what a person may view
|
|
18
18
|
* and `find` refuses enumeration; `checkMyPermission` tells the truth;
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* subject writes nothing until provisioned. The LIST lanes are a member's
|
|
19
|
+
* nobody sets public visibility; an unprovisioned subject
|
|
20
|
+
* writes nothing until provisioned. The LIST lanes are a member's
|
|
22
21
|
* lists: an outsider lists nothing of an organization they hold no
|
|
23
22
|
* row in; environments and API keys are their owner's even to the
|
|
24
23
|
* organization's owner; the library (a search) omits a private agent
|
|
@@ -27,9 +26,8 @@
|
|
|
27
26
|
* provisioned.
|
|
28
27
|
* - trusted-local: the permissive default stays (one caller,
|
|
29
28
|
* nothing to separate; the laptop's rows are stamped `"system"`):
|
|
30
|
-
* tokenless reads and organization enumeration
|
|
31
|
-
*
|
|
32
|
-
* every other posture (the level is gone, not gated).
|
|
29
|
+
* tokenless reads, public visibility and organization enumeration all
|
|
30
|
+
* keep working.
|
|
33
31
|
* - a unit's own Authorizer: nothing built in is installed; the unit's
|
|
34
32
|
* answer stands even against the founder.
|
|
35
33
|
*
|
|
@@ -82,6 +80,7 @@ import { loadConfig } from "../../boot/config.js";
|
|
|
82
80
|
import { composeServer } from "../../boot/compose.js";
|
|
83
81
|
import type { ComposedServer } from "../../boot/compose.js";
|
|
84
82
|
import { metadataOf } from "../../pipeline/steps/shapes.js";
|
|
83
|
+
import { PUBLIC_VISIBILITY_DENY_MESSAGE } from "../../pipeline/steps/visibility-gates.js";
|
|
85
84
|
import type { Store } from "../../store/interface.js";
|
|
86
85
|
import type { Authorizer } from "../authorizer.js";
|
|
87
86
|
import type { ListReadScope } from "../list-read-scope.js";
|
|
@@ -133,15 +132,6 @@ function organizationInput(slug: string) {
|
|
|
133
132
|
};
|
|
134
133
|
}
|
|
135
134
|
|
|
136
|
-
/**
|
|
137
|
-
* The one sentence for a level a kind does not support, as the agent chain
|
|
138
|
-
* answers it (pipeline/steps/validate-visibility.ts builds it from the
|
|
139
|
-
* kind's config); pinned as bytes because clients render it.
|
|
140
|
-
*/
|
|
141
|
-
const PUBLIC_LEVEL_REFUSED_FOR_AGENT =
|
|
142
|
-
"agent resources cannot be set to visibility_public. " +
|
|
143
|
-
"Supported visibility levels: visibility_private, visibility_org, visibility_platform.";
|
|
144
|
-
|
|
145
135
|
function agentInput(
|
|
146
136
|
name: string,
|
|
147
137
|
visibility: ApiResourceVisibility,
|
|
@@ -387,14 +377,14 @@ describe("built-in authorizer (composed server, OIDC with no unit Authorizer: th
|
|
|
387
377
|
);
|
|
388
378
|
});
|
|
389
379
|
|
|
390
|
-
it("
|
|
380
|
+
it("nobody sets PUBLIC visibility on an enforcing self-host — the platform gate denies the founder with the policy copy", async () => {
|
|
391
381
|
const failure = await failureOf(
|
|
392
382
|
createClient(AgentCommandController, asFounder()).create(
|
|
393
383
|
agentInput("Public Agent", ApiResourceVisibility.visibility_public),
|
|
394
384
|
),
|
|
395
385
|
);
|
|
396
|
-
expect(failure.code).toBe(Code.
|
|
397
|
-
expect(failure.rawMessage).toBe(
|
|
386
|
+
expect(failure.code).toBe(Code.PermissionDenied);
|
|
387
|
+
expect(failure.rawMessage).toBe(PUBLIC_VISIBILITY_DENY_MESSAGE);
|
|
398
388
|
});
|
|
399
389
|
|
|
400
390
|
describe("the list lanes (the built-in ListReadScope)", () => {
|
|
@@ -605,18 +595,20 @@ describe("built-in authorizer (composed server, trusted-local: the permissive de
|
|
|
605
595
|
expect(read.metadata?.id).toBe(created.metadata!.id);
|
|
606
596
|
});
|
|
607
597
|
|
|
608
|
-
it("the laptop keeps
|
|
598
|
+
it("the laptop keeps public visibility and organization enumeration — no directory, no platform gate that bites", async () => {
|
|
609
599
|
const anonymous = transportFor(port);
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
600
|
+
const publicAgent = await createClient(
|
|
601
|
+
AgentCommandController,
|
|
602
|
+
anonymous,
|
|
603
|
+
).create(
|
|
604
|
+
agentInput(
|
|
605
|
+
"Public Laptop Agent",
|
|
606
|
+
ApiResourceVisibility.visibility_public,
|
|
616
607
|
),
|
|
617
608
|
);
|
|
618
|
-
expect(
|
|
619
|
-
|
|
609
|
+
expect(publicAgent.metadata?.visibility).toBe(
|
|
610
|
+
ApiResourceVisibility.visibility_public,
|
|
611
|
+
);
|
|
620
612
|
const all = await createClient(OrganizationQueryController, anonymous).find(
|
|
621
613
|
{ org: ORG },
|
|
622
614
|
);
|
|
@@ -625,11 +617,6 @@ describe("built-in authorizer (composed server, trusted-local: the permissive de
|
|
|
625
617
|
|
|
626
618
|
it("the laptop's lists are the full scan — no scope composed, the rows stamped `system` included", async () => {
|
|
627
619
|
const anonymous = transportFor(port);
|
|
628
|
-
// A second agent beside the private one the earlier test created, so
|
|
629
|
-
// the search below proves a scan and not a single-row coincidence.
|
|
630
|
-
await createClient(AgentCommandController, anonymous).create(
|
|
631
|
-
agentInput("Second Laptop Agent", ApiResourceVisibility.visibility_org),
|
|
632
|
-
);
|
|
633
620
|
const environments = createClient(EnvironmentCommandController, anonymous);
|
|
634
621
|
await environments.create(environmentInput("laptop env one"));
|
|
635
622
|
await environments.create(environmentInput("laptop env two"));
|
|
@@ -1074,7 +1074,7 @@ describe("extension composition (C2 tuple lifecycle + organization directory)",
|
|
|
1074
1074
|
});
|
|
1075
1075
|
});
|
|
1076
1076
|
|
|
1077
|
-
it("updateVisibility fires the set-diff transition (org→
|
|
1077
|
+
it("updateVisibility fires the set-diff transition (org→public keeps the floor)", async () => {
|
|
1078
1078
|
const command = createClient(AgentCommandController, portTransport);
|
|
1079
1079
|
const agent = await command.create(
|
|
1080
1080
|
create(AgentSchema, {
|
|
@@ -1091,12 +1091,12 @@ describe("extension composition (C2 tuple lifecycle + organization directory)",
|
|
|
1091
1091
|
visibilityEvents.length = 0;
|
|
1092
1092
|
await command.updateVisibility({
|
|
1093
1093
|
resourceId: agent.metadata?.id ?? "",
|
|
1094
|
-
visibility: ApiResourceVisibility.
|
|
1094
|
+
visibility: ApiResourceVisibility.visibility_public,
|
|
1095
1095
|
});
|
|
1096
1096
|
expect(visibilityEvents).toHaveLength(1);
|
|
1097
1097
|
const event = visibilityEvents[0]!;
|
|
1098
|
-
expect(event.shapesToCreate).toEqual([]);
|
|
1099
|
-
expect(event.shapesToDelete).toEqual([
|
|
1098
|
+
expect(event.shapesToCreate).toEqual(["public-viewer"]);
|
|
1099
|
+
expect(event.shapesToDelete).toEqual([]);
|
|
1100
1100
|
});
|
|
1101
1101
|
|
|
1102
1102
|
it("delete fires the cleanup event; a cleanup failure never fails the delete", async () => {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* narrows, blank spans orgs — lane 6),
|
|
11
11
|
* - activity.listRecentActivity (enumeration verb, two kinds — lane 22),
|
|
12
12
|
* - search (enumeration verb feeding the engine allowlist, and the
|
|
13
|
-
*
|
|
13
|
+
* crossOrgPublic bypass where FGA is never consulted — lane 21),
|
|
14
14
|
* - the OUTAGE arm: a throwing scope answers the sanitized INTERNAL,
|
|
15
15
|
* never an empty (or full!) list;
|
|
16
16
|
* - the SERVER'S OWN reads over `inProcessTransport` (stigmer#1207):
|
|
@@ -277,23 +277,20 @@ describe("list read scope (composed server, fake scope)", () => {
|
|
|
277
277
|
]);
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
-
it("search narrows through the engine allowlist
|
|
280
|
+
it("search narrows through the engine allowlist; crossOrgPublic bypasses the scope (lane 21)", async () => {
|
|
281
281
|
allowed = new Set(["agt_mine"]);
|
|
282
282
|
const search = createClient(SearchService, transport);
|
|
283
283
|
const scoped = await search.search({ query: "scopedagent" });
|
|
284
284
|
expect(scoped.entries.map((e) => e.id)).toEqual(["agt_mine"]);
|
|
285
285
|
|
|
286
|
-
// The
|
|
287
|
-
// that once skipped it (the retired public level's cross-organization
|
|
288
|
-
// discovery) no longer exists on the wire.
|
|
286
|
+
// The Java bypass: public-widened discovery never consults FGA.
|
|
289
287
|
seenKinds.length = 0;
|
|
290
|
-
|
|
291
|
-
kinds: [ApiResourceKind.agent],
|
|
288
|
+
await search.search({
|
|
292
289
|
query: "scopedagent",
|
|
293
290
|
org: "acme",
|
|
291
|
+
crossOrgPublic: true,
|
|
294
292
|
});
|
|
295
|
-
expect(
|
|
296
|
-
expect(seenKinds).toEqual([ApiResourceKind.agent]);
|
|
293
|
+
expect(seenKinds).toEqual([]);
|
|
297
294
|
});
|
|
298
295
|
|
|
299
296
|
it("a scope outage answers the sanitized INTERNAL — never an unscoped or empty success", async () => {
|
|
@@ -32,10 +32,9 @@
|
|
|
32
32
|
* Semantics the controller and the conformance suite rely on (the Java
|
|
33
33
|
* check-handler posture the cloud's client already implements):
|
|
34
34
|
*
|
|
35
|
-
* - `check`
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* - the two listings never expand usersets, and `listPrincipalIds`
|
|
35
|
+
* - `check` answers a public grant (a wildcard principal resolves), so
|
|
36
|
+
* "may I read this public agent" is true for anyone.
|
|
37
|
+
* - the two listings never expand wildcards, and `listPrincipalIds`
|
|
39
38
|
* answers DIRECT principals only — a listing is an inventory of who
|
|
40
39
|
* was named, never a resolution of who could reach.
|
|
41
40
|
* - contextual policies are evaluated as if held, and nothing is written.
|
|
@@ -75,7 +74,7 @@ export interface AuthorizationQueryEngine {
|
|
|
75
74
|
): Promise<boolean>;
|
|
76
75
|
/**
|
|
77
76
|
* The ids of `resourceKind` resources on which `principal` holds
|
|
78
|
-
* `relation` (ListAuthorizedResourceIdsInput's field order).
|
|
77
|
+
* `relation` (ListAuthorizedResourceIdsInput's field order). Wildcards
|
|
79
78
|
* are not expanded.
|
|
80
79
|
*/
|
|
81
80
|
listResourceIds(
|
|
@@ -87,7 +86,7 @@ export interface AuthorizationQueryEngine {
|
|
|
87
86
|
/**
|
|
88
87
|
* The ids of `principalKind` principals holding `relation` on `resource`
|
|
89
88
|
* (ListAuthorizedPrincipalIdsInput's field order). Direct principals
|
|
90
|
-
* only;
|
|
89
|
+
* only; wildcards are not expanded.
|
|
91
90
|
*/
|
|
92
91
|
listPrincipalIds(
|
|
93
92
|
resource: ApiResourceRef,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* is yours). On a multi-tenant edition the same scans serve OTHER
|
|
9
9
|
* tenants' rows, so the cloud baseline (the Java list handlers) narrows
|
|
10
10
|
* every one of them to the caller's FGA-authorized id set (ListObjects
|
|
11
|
-
* on can_view), with a second guest
|
|
11
|
+
* on can_view, public wildcards suppressed), with a second guest
|
|
12
12
|
* cookie-label rule on the session and agent-execution lanes.
|
|
13
13
|
*
|
|
14
14
|
* This port is the ONE seam for that fork (DD-007 addendum 3: every
|
|
@@ -65,12 +65,14 @@ import type { CallerIdentity } from "./identity.js";
|
|
|
65
65
|
* The visibility tuple shapes of the shared FGA model, named
|
|
66
66
|
* edition-neutrally. The driver maps each to its tuple:
|
|
67
67
|
* - org-viewer: <kind>:<id>#viewer@organization:<org>#viewer
|
|
68
|
+
* - public-viewer: <kind>:<id>#viewer@identity_account:* (conditional)
|
|
68
69
|
* - platform-viewer: <kind>:<id>#platform_viewer@identity_provider:<idp>#platform_user
|
|
69
70
|
* (fans out per IdP the org owns — the driver's lookup, not OSS's)
|
|
70
|
-
* Every shape names a bounded set of readers; the model has no shape that
|
|
71
|
-
* reaches every account.
|
|
72
71
|
*/
|
|
73
|
-
export type VisibilityTupleShape =
|
|
72
|
+
export type VisibilityTupleShape =
|
|
73
|
+
| "org-viewer"
|
|
74
|
+
| "public-viewer"
|
|
75
|
+
| "platform-viewer";
|
|
74
76
|
|
|
75
77
|
/**
|
|
76
78
|
* One resolved structural link from the created resource to a parent
|
package/src/index.ts
CHANGED
|
@@ -185,6 +185,7 @@ export {
|
|
|
185
185
|
newModel,
|
|
186
186
|
} from "./authorization/model/index.js";
|
|
187
187
|
export type {
|
|
188
|
+
CheckContext,
|
|
188
189
|
ObjectRef,
|
|
189
190
|
Person,
|
|
190
191
|
Subject,
|
|
@@ -308,6 +309,19 @@ export { newAuthorizeStep } from "./pipeline/steps/authorize.js";
|
|
|
308
309
|
// composition never re-derives the set.
|
|
309
310
|
export { isRunGateCheck } from "./pipeline/steps/authorize-run-target.js";
|
|
310
311
|
export { newValidateProtoStep } from "./pipeline/steps/validation.js";
|
|
312
|
+
// The create-shaping steps, for an envelope kind a composition serves
|
|
313
|
+
// itself (a cloud_only kind has no OSS controller to run them): between
|
|
314
|
+
// ValidateProto and its own domain step the composition runs the SAME
|
|
315
|
+
// ResolveSlug and BuildNewState every OSS create runs, so how a slug is
|
|
316
|
+
// derived from a name, how an id is spelled, how status.audit is stamped
|
|
317
|
+
// for the caller and how visibility defaults live exactly once and move
|
|
318
|
+
// together. The RequestContext must carry the kind (its fourth
|
|
319
|
+
// constructor argument) for BuildNewState to pick the id prefix.
|
|
320
|
+
// CheckDuplicate is deliberately not here: it reads this server's Store,
|
|
321
|
+
// and a composition's own table answers that question in a step of the
|
|
322
|
+
// same name over its own repo.
|
|
323
|
+
export { newBuildNewStateStep } from "./pipeline/steps/defaults.js";
|
|
324
|
+
export { newResolveSlugStep } from "./pipeline/steps/slug.js";
|
|
311
325
|
|
|
312
326
|
// The driver interfaces and the store-fault classes the ratified mapping
|
|
313
327
|
// keys on (typed not-found → NotFound; anything else rethrows as an
|