@stigmer/server 3.18.0-dev.20260918103812 → 3.18.1-dev.20260919070736
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/README.md +1 -1
- package/dist/boot/compose.d.ts +12 -0
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +46 -8
- package/dist/boot/compose.js.map +1 -1
- package/dist/boot/config.d.ts +6 -2
- package/dist/boot/config.d.ts.map +1 -1
- package/dist/boot/config.js.map +1 -1
- package/dist/boot/oauth-redirect-uri.d.ts +54 -0
- package/dist/boot/oauth-redirect-uri.d.ts.map +1 -0
- package/dist/boot/oauth-redirect-uri.js +37 -0
- package/dist/boot/oauth-redirect-uri.js.map +1 -0
- package/dist/domain/agentexecution/lifecycle.js +1 -1
- package/dist/domain/agentexecution/lifecycle.js.map +1 -1
- package/dist/domain/iampolicy/constants.d.ts +3 -3
- package/dist/domain/iampolicy/constants.js +3 -3
- package/dist/domain/mcpserver/complete-endpoint-auth.d.ts +16 -0
- package/dist/domain/mcpserver/complete-endpoint-auth.d.ts.map +1 -0
- package/dist/domain/mcpserver/complete-endpoint-auth.js +260 -0
- package/dist/domain/mcpserver/complete-endpoint-auth.js.map +1 -0
- package/dist/domain/mcpserver/complete-oauth-connect.d.ts.map +1 -1
- package/dist/domain/mcpserver/complete-oauth-connect.js +1 -1
- package/dist/domain/mcpserver/complete-oauth-connect.js.map +1 -1
- package/dist/domain/mcpserver/connect.d.ts +25 -0
- package/dist/domain/mcpserver/connect.d.ts.map +1 -1
- package/dist/domain/mcpserver/connect.js +8 -23
- package/dist/domain/mcpserver/connect.js.map +1 -1
- package/dist/domain/mcpserver/controller.d.ts.map +1 -1
- package/dist/domain/mcpserver/controller.js +12 -0
- package/dist/domain/mcpserver/controller.js.map +1 -1
- package/dist/domain/mcpserver/initiate-oauth-connect.d.ts.map +1 -1
- package/dist/domain/mcpserver/initiate-oauth-connect.js +16 -13
- package/dist/domain/mcpserver/initiate-oauth-connect.js.map +1 -1
- package/dist/domain/mcpserver/oauth/dcr.d.ts +10 -1
- package/dist/domain/mcpserver/oauth/dcr.d.ts.map +1 -1
- package/dist/domain/mcpserver/oauth/dcr.js +1 -9
- package/dist/domain/mcpserver/oauth/dcr.js.map +1 -1
- package/dist/domain/mcpserver/oauth/discovery.d.ts +43 -20
- package/dist/domain/mcpserver/oauth/discovery.d.ts.map +1 -1
- package/dist/domain/mcpserver/oauth/discovery.js +69 -54
- package/dist/domain/mcpserver/oauth/discovery.js.map +1 -1
- package/dist/domain/mcpserver/oauth/preflight.d.ts +27 -1
- package/dist/domain/mcpserver/oauth/preflight.d.ts.map +1 -1
- package/dist/domain/mcpserver/oauth/preflight.js +1 -26
- package/dist/domain/mcpserver/oauth/preflight.js.map +1 -1
- package/dist/domain/mcpserver/oauth/refresh.d.ts +2 -1
- package/dist/domain/mcpserver/oauth/refresh.d.ts.map +1 -1
- package/dist/domain/mcpserver/oauth/refresh.js +1 -1
- package/dist/domain/mcpserver/oauth/refresh.js.map +1 -1
- package/dist/domain/mcpserver/oauth/token.d.ts +9 -2
- package/dist/domain/mcpserver/oauth/token.d.ts.map +1 -1
- package/dist/domain/mcpserver/oauth/token.js +2 -8
- package/dist/domain/mcpserver/oauth/token.js.map +1 -1
- package/dist/domain/oauthapp/refresolution/refresolution.js +1 -1
- package/dist/domain/oauthapp/refresolution.js +1 -1
- package/dist/domain/platform/controller.d.ts +8 -1
- package/dist/domain/platform/controller.d.ts.map +1 -1
- package/dist/domain/platform/controller.js +2 -3
- package/dist/domain/platform/controller.js.map +1 -1
- package/dist/domain/plugin/constants.d.ts +7 -1
- package/dist/domain/plugin/constants.d.ts.map +1 -1
- package/dist/domain/plugin/constants.js +7 -1
- package/dist/domain/plugin/constants.js.map +1 -1
- package/dist/domain/plugin/materialize/plan.d.ts.map +1 -1
- package/dist/domain/plugin/materialize/plan.js +16 -1
- package/dist/domain/plugin/materialize/plan.js.map +1 -1
- package/dist/domain/plugin/members.d.ts +46 -0
- package/dist/domain/plugin/members.d.ts.map +1 -1
- package/dist/domain/plugin/members.js +31 -8
- package/dist/domain/plugin/members.js.map +1 -1
- package/dist/domain/plugin/push.d.ts +5 -3
- package/dist/domain/plugin/push.d.ts.map +1 -1
- package/dist/domain/plugin/push.js +58 -14
- package/dist/domain/plugin/push.js.map +1 -1
- package/dist/domain/workflowexecution/lifecycle.js +1 -1
- package/dist/domain/workflowexecution/lifecycle.js.map +1 -1
- package/dist/extensions/drivers.d.ts +17 -0
- package/dist/extensions/drivers.d.ts.map +1 -1
- package/dist/extensions/outbound-egress.d.ts +49 -0
- package/dist/extensions/outbound-egress.d.ts.map +1 -0
- package/dist/extensions/outbound-egress.js +51 -0
- package/dist/extensions/outbound-egress.js.map +1 -0
- package/dist/extensions/registry.d.ts +6 -0
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/extensions/registry.js +10 -0
- package/dist/extensions/registry.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/apiresource-labels.d.ts +34 -1
- package/dist/pipeline/apiresource-labels.d.ts.map +1 -1
- package/dist/pipeline/apiresource-labels.js +31 -0
- package/dist/pipeline/apiresource-labels.js.map +1 -1
- package/dist/sandbox/kubernetes.d.ts.map +1 -1
- package/dist/sandbox/kubernetes.js +1 -8
- package/dist/sandbox/kubernetes.js.map +1 -1
- package/dist/sandbox/provisioner.d.ts +5 -20
- package/dist/sandbox/provisioner.d.ts.map +1 -1
- package/dist/sandbox/provisioner.js.map +1 -1
- package/dist/sandbox/steps.d.ts +8 -22
- package/dist/sandbox/steps.d.ts.map +1 -1
- package/dist/sandbox/steps.js +12 -17
- package/dist/sandbox/steps.js.map +1 -1
- package/dist/store/interface.d.ts +6 -2
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/package.json +6 -5
- package/src/boot/__tests__/oauth-redirect-uri.test.ts +38 -0
- package/src/boot/compose.ts +63 -10
- package/src/boot/config.ts +6 -2
- package/src/boot/oauth-redirect-uri.ts +53 -0
- package/src/domain/agentexecution/lifecycle.ts +1 -1
- package/src/domain/iampolicy/constants.ts +3 -3
- package/src/domain/mcpserver/__tests__/complete-endpoint-auth.test.ts +270 -0
- package/src/domain/mcpserver/__tests__/connect-authorization.test.ts +3 -0
- package/src/domain/mcpserver/__tests__/connect.test.ts +31 -0
- package/src/domain/mcpserver/complete-endpoint-auth.ts +277 -0
- package/src/domain/mcpserver/complete-oauth-connect.ts +1 -0
- package/src/domain/mcpserver/connect.ts +21 -15
- package/src/domain/mcpserver/controller.ts +16 -0
- package/src/domain/mcpserver/initiate-oauth-connect.ts +16 -12
- package/src/domain/mcpserver/oauth/__tests__/discovery.test.ts +120 -91
- package/src/domain/mcpserver/oauth/__tests__/token-refresh.test.ts +3 -1
- package/src/domain/mcpserver/oauth/dcr.ts +2 -1
- package/src/domain/mcpserver/oauth/discovery.ts +120 -89
- package/src/domain/mcpserver/oauth/preflight.ts +2 -1
- package/src/domain/mcpserver/oauth/refresh.ts +2 -1
- package/src/domain/mcpserver/oauth/token.ts +4 -3
- package/src/domain/oauthapp/__tests__/oauthapp.test.ts +2 -2
- package/src/domain/oauthapp/__tests__/refresolution.test.ts +2 -2
- package/src/domain/oauthapp/refresolution/refresolution.ts +1 -1
- package/src/domain/oauthapp/refresolution.ts +1 -1
- package/src/domain/platform/__tests__/platform.test.ts +47 -0
- package/src/domain/platform/controller.ts +10 -4
- package/src/domain/plugin/__tests__/materialize.test.ts +42 -47
- package/src/domain/plugin/__tests__/members.test.ts +149 -0
- package/src/domain/plugin/__tests__/plugin.test.ts +219 -8
- package/src/domain/plugin/constants.ts +7 -1
- package/src/domain/plugin/materialize/plan.ts +20 -1
- package/src/domain/plugin/members.ts +76 -7
- package/src/domain/plugin/push.ts +80 -20
- package/src/domain/workflowexecution/lifecycle.ts +1 -1
- package/src/extensions/__tests__/outbound-egress-points.test.ts +58 -0
- package/src/extensions/drivers.ts +17 -0
- package/src/extensions/outbound-egress.ts +55 -0
- package/src/extensions/registry.ts +19 -0
- package/src/index.ts +10 -0
- package/src/pipeline/apiresource-labels.ts +42 -1
- package/src/sandbox/__tests__/docker.integration.test.ts +0 -2
- package/src/sandbox/__tests__/kubernetes.test.ts +2 -10
- package/src/sandbox/__tests__/local-process.integration.test.ts +0 -3
- package/src/sandbox/__tests__/steps.test.ts +19 -72
- package/src/sandbox/kubernetes.ts +1 -8
- package/src/sandbox/provisioner.ts +5 -20
- package/src/sandbox/steps.ts +12 -29
- package/src/store/interface.ts +6 -2
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins the outboundEgress driver point in the single-instance shape the
|
|
3
|
+
* registry already enforces for policyGrantScope and licenseStatus:
|
|
4
|
+
*
|
|
5
|
+
* - `drivers.outboundEgress` — the address policy the composition root
|
|
6
|
+
* builds the McpServer slice's one guarded fetch from; single instance;
|
|
7
|
+
* absent = open source's relaxed posture installs at the compose.ts
|
|
8
|
+
* consumption site.
|
|
9
|
+
*
|
|
10
|
+
* And the two exported postures: relaxed refuses only the link-local
|
|
11
|
+
* (cloud metadata) range, strict refuses loopback, private and unspecified
|
|
12
|
+
* too, and neither owns a copy of the ranges (both read the shared table).
|
|
13
|
+
*/
|
|
14
|
+
import { describe, expect, it } from "vitest";
|
|
15
|
+
|
|
16
|
+
import { relaxedEgressPolicy, strictEgressPolicy } from "../outbound-egress.js";
|
|
17
|
+
import { resolveExtensions } from "../registry.js";
|
|
18
|
+
|
|
19
|
+
describe("the outboundEgress driver point", () => {
|
|
20
|
+
it("is undefined with no extensions — the relaxed posture installs at the consumption site", () => {
|
|
21
|
+
expect(resolveExtensions([]).drivers.outboundEgress).toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("carries the one registered instance through", () => {
|
|
25
|
+
const policy = strictEgressPolicy();
|
|
26
|
+
const resolved = resolveExtensions([{ name: "cloud-core", drivers: { outboundEgress: policy } }]);
|
|
27
|
+
expect(resolved.drivers.outboundEgress).toBe(policy);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("throws on a second registration, naming both units", () => {
|
|
31
|
+
expect(() =>
|
|
32
|
+
resolveExtensions([
|
|
33
|
+
{ name: "egress-a", drivers: { outboundEgress: strictEgressPolicy() } },
|
|
34
|
+
{ name: "egress-b", drivers: { outboundEgress: relaxedEgressPolicy() } },
|
|
35
|
+
]),
|
|
36
|
+
).toThrowError(/extension 'egress-b' registers an OutboundEgressPolicy, but 'egress-a' already did/);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("the two postures", () => {
|
|
41
|
+
it("relaxed refuses only the link-local range, so a local MCP server stays reachable", () => {
|
|
42
|
+
const relaxed = relaxedEgressPolicy();
|
|
43
|
+
expect(relaxed.name).toBe("relaxed");
|
|
44
|
+
expect(relaxed.blockedReason("127.0.0.1")).toBeNull();
|
|
45
|
+
expect(relaxed.blockedReason("10.0.0.1")).toBeNull();
|
|
46
|
+
expect(relaxed.blockedReason("169.254.169.254")).toBe("link-local (cloud metadata)");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("strict refuses loopback, private, link-local and unspecified addresses", () => {
|
|
50
|
+
const strict = strictEgressPolicy();
|
|
51
|
+
expect(strict.name).toBe("strict");
|
|
52
|
+
expect(strict.blockedReason("127.0.0.1")).toBe("loopback");
|
|
53
|
+
expect(strict.blockedReason("10.0.0.1")).toBe("private (RFC 1918)");
|
|
54
|
+
expect(strict.blockedReason("169.254.169.254")).toBe("link-local (cloud metadata)");
|
|
55
|
+
expect(strict.blockedReason("0.0.0.0")).toBe("unspecified");
|
|
56
|
+
expect(strict.blockedReason("104.16.0.1")).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
* port, which kinds an edition grants on, and the tuple-half query
|
|
32
32
|
* engine only an authorization backend can answer) — landed with
|
|
33
33
|
* 20260913.01, gate ruling Q-OR-10
|
|
34
|
+
* - outbound egress (which addresses the control plane may dial when
|
|
35
|
+
* it reaches a URL a user supplied: the MCP endpoint it probes at save
|
|
36
|
+
* time and the login server it reaches on Sign in) — landed with the
|
|
37
|
+
* save-time OAuth completion for URL-only MCP servers, 2026-09-19
|
|
34
38
|
*
|
|
35
39
|
* Merge rules (enforced by resolveExtensions, DD-006 §2b): the two
|
|
36
40
|
* provider kinds are single-instance points — a second declaring unit is
|
|
@@ -54,6 +58,7 @@ import type { SandboxProvisionerFactory } from "../sandbox/provisioner.js";
|
|
|
54
58
|
import type { AuthorizationQueryEngine } from "./authorization-queries.js";
|
|
55
59
|
import type { IdentityFederation } from "./identity-federation.js";
|
|
56
60
|
import type { LicenseStatusProvider } from "./license-status.js";
|
|
61
|
+
import type { OutboundEgressPolicy } from "./outbound-egress.js";
|
|
57
62
|
import type { ListReadScope } from "./list-read-scope.js";
|
|
58
63
|
import type { OrganizationDirectory } from "./organization-directory.js";
|
|
59
64
|
import type { PolicyGrantScope } from "./policy-grant-scope.js";
|
|
@@ -230,4 +235,16 @@ export interface ExtensionDrivers {
|
|
|
230
235
|
* way (extensions/license-status.ts carries the contract).
|
|
231
236
|
*/
|
|
232
237
|
readonly licenseStatus?: LicenseStatusProvider;
|
|
238
|
+
/**
|
|
239
|
+
* The outbound-egress policy (single-instance point): which addresses
|
|
240
|
+
* this edition's control plane may dial when it reaches a URL a user
|
|
241
|
+
* supplied. The composition root builds one guarded fetch from it and
|
|
242
|
+
* hands it to the McpServer connect slice, the only fetch its OAuth code
|
|
243
|
+
* and its save-time endpoint probe hold. When absent, open source's
|
|
244
|
+
* `relaxedEgressPolicy()` installs at the compose.ts consumption site:
|
|
245
|
+
* everything but the link-local (cloud metadata) range is allowed, so a
|
|
246
|
+
* server beside a self-hosted control plane keeps working
|
|
247
|
+
* (extensions/outbound-egress.ts carries the contract).
|
|
248
|
+
*/
|
|
249
|
+
readonly outboundEgress?: OutboundEgressPolicy;
|
|
233
250
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The outbound-egress driver point: WHICH addresses this edition's control
|
|
3
|
+
* plane may dial when it reaches a URL a user supplied. Single instance,
|
|
4
|
+
* registered as `drivers.outboundEgress` (the policyGrantScope shape: a
|
|
5
|
+
* driver point whose absence is open source's own behaviour). Absent, the
|
|
6
|
+
* composition root installs `relaxedEgressPolicy()`: everything but the
|
|
7
|
+
* link-local range is allowed, because a server beside a self-hosted
|
|
8
|
+
* control plane (`http://localhost:3000/mcp`, the proto's own example) is
|
|
9
|
+
* the normal case and the link-local range, which carries the cloud
|
|
10
|
+
* metadata endpoint, costs a local user nothing to refuse. The cloud
|
|
11
|
+
* registers `strictEgressPolicy()`: loopback, private, link-local and
|
|
12
|
+
* unspecified refused, because its control plane sits on infrastructure
|
|
13
|
+
* with reach to internal services.
|
|
14
|
+
*
|
|
15
|
+
* Who reads it: nobody in the domains directly. The composition root builds
|
|
16
|
+
* ONE guarded fetch from the policy (`@stigmer/outbound/egress`
|
|
17
|
+
* `guardedFetch`, which judges the first URL and every redirect hop) and
|
|
18
|
+
* hands it to the McpServer connect slice as `outboundFetch`, the only
|
|
19
|
+
* fetch the McpServer domain's OAuth code and its save-time endpoint probe
|
|
20
|
+
* hold. Before this point existed every OAuth fetch (discovery, dynamic
|
|
21
|
+
* client registration, the authorize preflight, token exchange and
|
|
22
|
+
* refresh) defaulted to the raw global `fetch` in every edition, which is
|
|
23
|
+
* the exposure the point closes.
|
|
24
|
+
*
|
|
25
|
+
* The contract every policy is held to:
|
|
26
|
+
*
|
|
27
|
+
* - It judges ADDRESSES, not names: the check resolves a hostname and
|
|
28
|
+
* asks about every address it has, so a name with one public and one
|
|
29
|
+
* private record is refused. A policy that wanted a hostname allowlist
|
|
30
|
+
* would be a different seam.
|
|
31
|
+
* - It is TOTAL: an unparseable address is refused ("unrecognized"),
|
|
32
|
+
* never a throw.
|
|
33
|
+
* - It is SYNCHRONOUS: an edition's egress posture is a fact about where
|
|
34
|
+
* it runs, never I/O.
|
|
35
|
+
*
|
|
36
|
+
* Both constructors are thin over the shared classification in
|
|
37
|
+
* `@stigmer/outbound/egress`, so a composition registers a posture and owns
|
|
38
|
+
* no copy of the ranges; the runner's `web_fetch` guard reads the same
|
|
39
|
+
* table, so the two processes refuse the same addresses under the same
|
|
40
|
+
* posture name.
|
|
41
|
+
*/
|
|
42
|
+
import { egressPolicyForPosture, type EgressPolicy } from "@stigmer/outbound/egress";
|
|
43
|
+
|
|
44
|
+
/** The egress contract (single-instance point, ExtensionDrivers.outboundEgress). */
|
|
45
|
+
export type OutboundEgressPolicy = EgressPolicy;
|
|
46
|
+
|
|
47
|
+
/** Open source's own posture: only the link-local (cloud metadata) range is refused. */
|
|
48
|
+
export function relaxedEgressPolicy(): OutboundEgressPolicy {
|
|
49
|
+
return egressPolicyForPosture("relaxed");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The managed-cloud posture: loopback, private, link-local and unspecified addresses refused. */
|
|
53
|
+
export function strictEgressPolicy(): OutboundEgressPolicy {
|
|
54
|
+
return egressPolicyForPosture("strict");
|
|
55
|
+
}
|
|
@@ -54,6 +54,7 @@ import { V1_VERSION } from "../encryption/v1-codec.js";
|
|
|
54
54
|
import type { AuthorizationQueryEngine } from "./authorization-queries.js";
|
|
55
55
|
import type { IdentityFederation } from "./identity-federation.js";
|
|
56
56
|
import type { LicenseStatusProvider } from "./license-status.js";
|
|
57
|
+
import type { OutboundEgressPolicy } from "./outbound-egress.js";
|
|
57
58
|
import type { ListReadScope } from "./list-read-scope.js";
|
|
58
59
|
import type { PolicyGrantScope } from "./policy-grant-scope.js";
|
|
59
60
|
import type { ModelCatalogProvider } from "../domain/workflow/registry/model-catalog-provider.js";
|
|
@@ -278,6 +279,11 @@ export interface ResolvedExtensionDrivers {
|
|
|
278
279
|
* built-in `absent` answer, so every edition serves getLicenseStatus.
|
|
279
280
|
*/
|
|
280
281
|
readonly licenseStatus: LicenseStatusProvider | undefined;
|
|
282
|
+
/**
|
|
283
|
+
* The outbound-egress policy — undefined = compose.ts installs open
|
|
284
|
+
* source's relaxed posture (only the link-local range refused).
|
|
285
|
+
*/
|
|
286
|
+
readonly outboundEgress: OutboundEgressPolicy | undefined;
|
|
281
287
|
}
|
|
282
288
|
|
|
283
289
|
/**
|
|
@@ -333,6 +339,8 @@ export function resolveExtensions(
|
|
|
333
339
|
let authorizationQueriesDeclaredBy: string | undefined;
|
|
334
340
|
let licenseStatus: LicenseStatusProvider | undefined;
|
|
335
341
|
let licenseStatusDeclaredBy: string | undefined;
|
|
342
|
+
let outboundEgress: OutboundEgressPolicy | undefined;
|
|
343
|
+
let outboundEgressDeclaredBy: string | undefined;
|
|
336
344
|
const artifactStorageDrivers = new Map<
|
|
337
345
|
string,
|
|
338
346
|
ArtifactStorageDriverFactory
|
|
@@ -534,6 +542,16 @@ export function resolveExtensions(
|
|
|
534
542
|
licenseStatusDeclaredBy = unit.name;
|
|
535
543
|
}
|
|
536
544
|
|
|
545
|
+
if (unit.drivers?.outboundEgress !== undefined) {
|
|
546
|
+
if (outboundEgressDeclaredBy !== undefined) {
|
|
547
|
+
throw new Error(
|
|
548
|
+
`extension '${unit.name}' registers an OutboundEgressPolicy, but '${outboundEgressDeclaredBy}' already did — exactly one may be composed`,
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
outboundEgress = unit.drivers.outboundEgress;
|
|
552
|
+
outboundEgressDeclaredBy = unit.name;
|
|
553
|
+
}
|
|
554
|
+
|
|
537
555
|
if (unit.drivers?.artifactStorageDrivers !== undefined) {
|
|
538
556
|
for (const [name, factory] of unit.drivers.artifactStorageDrivers) {
|
|
539
557
|
if ((BUILT_IN_STORAGE_TYPES as ReadonlyArray<string>).includes(name)) {
|
|
@@ -660,6 +678,7 @@ export function resolveExtensions(
|
|
|
660
678
|
policyGrantScope,
|
|
661
679
|
authorizationQueries,
|
|
662
680
|
licenseStatus,
|
|
681
|
+
outboundEgress,
|
|
663
682
|
},
|
|
664
683
|
services,
|
|
665
684
|
workers,
|
package/src/index.ts
CHANGED
|
@@ -119,6 +119,16 @@ export type {
|
|
|
119
119
|
LicenseStatusProvider,
|
|
120
120
|
LicenseStatusReport,
|
|
121
121
|
} from "./extensions/license-status.js";
|
|
122
|
+
// The outbound-egress seam (drivers.outboundEgress): which addresses the
|
|
123
|
+
// control plane may dial when it reaches a URL a user supplied. Both
|
|
124
|
+
// postures are exported so a composition registers one line and owns no
|
|
125
|
+
// copy of the address ranges (the classification lives in
|
|
126
|
+
// @stigmer/outbound/egress, shared with the runner's web_fetch guard).
|
|
127
|
+
export type { OutboundEgressPolicy } from "./extensions/outbound-egress.js";
|
|
128
|
+
export {
|
|
129
|
+
relaxedEgressPolicy,
|
|
130
|
+
strictEgressPolicy,
|
|
131
|
+
} from "./extensions/outbound-egress.js";
|
|
122
132
|
// The 20260911.11 identity-account seams (Q-IA-9): the store PORT a
|
|
123
133
|
// composition drives the domain through (drivers.identityAccountStore;
|
|
124
134
|
// a driver throws DuplicateAccountError for a held id), the federation
|
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
* authorizer's permissive default ALLOWS reserved-label writes by design
|
|
14
14
|
* (the self-hosted operator owns the store), which is why a restrict-shaped
|
|
15
15
|
* predicate here still pairs the label with server-owned state: the
|
|
16
|
-
* plugin-managed guard refuses only beside an existing plugin row
|
|
16
|
+
* plugin-managed guard refuses only beside an existing plugin row, and a
|
|
17
|
+
* plugin adopts a system-content row only when its own claim to the same
|
|
18
|
+
* label was just charged to `can_write_reserved_labels` by the sanitiser
|
|
19
|
+
* (domain/plugin/members.ts) — the label narrows which rows are eligible,
|
|
20
|
+
* the live authorization is what grants.
|
|
17
21
|
*/
|
|
18
22
|
import type { ApiResourceMetadata } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
|
|
19
23
|
|
|
@@ -39,6 +43,20 @@ export const DEFAULT_INSTANCE_LABEL = `${RESERVED_LABEL_PREFIX}default-instance`
|
|
|
39
43
|
*/
|
|
40
44
|
export const SYSTEM_MANAGED_LABEL = `${RESERVED_LABEL_PREFIX}system-managed`;
|
|
41
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Marks system content: a resource the platform itself seeded rather than a
|
|
48
|
+
* user authored — the system organization, the default agent, the
|
|
49
|
+
* platform's own MCP servers. The label predates this module (the retired
|
|
50
|
+
* seedpack wrote it on every row it applied; the official plugins' overlays
|
|
51
|
+
* carry it on the resources they replace) and has one server-side meaning:
|
|
52
|
+
* a plugin push may adopt a system-content row that holds a slug the plugin
|
|
53
|
+
* needs, keeping the row's id and everything bound to it, instead of
|
|
54
|
+
* refusing the slug as taken. It never marks a user's resource, so a user's
|
|
55
|
+
* row is never adopted (domain/plugin/members.ts, judgeSlug). The CLI names
|
|
56
|
+
* the same bytes as SYSTEM_ORG_LABEL for the organization it ensures.
|
|
57
|
+
*/
|
|
58
|
+
export const SYSTEM_LABEL = `${RESERVED_LABEL_PREFIX}system`;
|
|
59
|
+
|
|
42
60
|
/**
|
|
43
61
|
* The only value that activates a reserved marker label; any other value is
|
|
44
62
|
* inert (matching cloud's "true".equals(...)). Stamped by the flows that
|
|
@@ -64,6 +82,22 @@ export const PLUGIN_LABEL = `${RESERVED_LABEL_PREFIX}plugin`;
|
|
|
64
82
|
*/
|
|
65
83
|
export const PLUGIN_VERSION_LABEL = `${RESERVED_LABEL_PREFIX}plugin-version`;
|
|
66
84
|
|
|
85
|
+
/**
|
|
86
|
+
* How an MCP server's `spec.auth` came to be. The one value, `endpoint`,
|
|
87
|
+
* says the server itself completed it: the author supplied a URL and
|
|
88
|
+
* nothing about authentication, the endpoint answered the save-time probe
|
|
89
|
+
* with an OAuth challenge, and the server wrote the token variable, its
|
|
90
|
+
* declaration and the Bearer header (domain/mcpserver/complete-endpoint-auth.ts).
|
|
91
|
+
* The label is provenance, not permission: it lets the next save tell a
|
|
92
|
+
* completion it may reuse from an author's own `auth` it must not touch.
|
|
93
|
+
* Stamped through the server-stamped mechanism; dropped when the author
|
|
94
|
+
* writes their own `auth`.
|
|
95
|
+
*/
|
|
96
|
+
export const MCP_AUTH_LABEL = `${RESERVED_LABEL_PREFIX}mcp-auth`;
|
|
97
|
+
|
|
98
|
+
/** The MCP_AUTH_LABEL value for a completion the endpoint's own challenge produced. */
|
|
99
|
+
export const MCP_AUTH_ENDPOINT = "endpoint";
|
|
100
|
+
|
|
67
101
|
/** The plugin id a resource's labels claim, or undefined when unlabelled. */
|
|
68
102
|
export function pluginIdOf(
|
|
69
103
|
metadata: ApiResourceMetadata | undefined,
|
|
@@ -86,3 +120,10 @@ export function isDefaultInstance(
|
|
|
86
120
|
): boolean {
|
|
87
121
|
return metadata?.labels[DEFAULT_INSTANCE_LABEL] === RESERVED_LABEL_TRUE;
|
|
88
122
|
}
|
|
123
|
+
|
|
124
|
+
/** Whether the metadata carries the SYSTEM_LABEL marker (undefined-safe). */
|
|
125
|
+
export function isSystemContent(
|
|
126
|
+
metadata: ApiResourceMetadata | undefined,
|
|
127
|
+
): boolean {
|
|
128
|
+
return metadata?.labels[SYSTEM_LABEL] === RESERVED_LABEL_TRUE;
|
|
129
|
+
}
|
|
@@ -68,7 +68,6 @@ describe.skipIf(!optedIn || !dockerAnswers())(
|
|
|
68
68
|
await driver.ensureSessionSandbox(sessionId, {
|
|
69
69
|
taskQueue: `session:${sessionId}`,
|
|
70
70
|
stigmerToken: "tok-docker-smoke",
|
|
71
|
-
callerClass: "user",
|
|
72
71
|
});
|
|
73
72
|
expect(await driver.probe("session", sessionId)).toBe("running");
|
|
74
73
|
|
|
@@ -92,7 +91,6 @@ describe.skipIf(!optedIn || !dockerAnswers())(
|
|
|
92
91
|
await driver.ensureSessionSandbox(sessionId, {
|
|
93
92
|
taskQueue: `session:${sessionId}`,
|
|
94
93
|
stigmerToken: "tok-docker-smoke",
|
|
95
|
-
callerClass: "user",
|
|
96
94
|
});
|
|
97
95
|
expect(await driver.probe("session", sessionId)).toBe("running");
|
|
98
96
|
|
|
@@ -89,11 +89,7 @@ function fakeGateway(
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
const env = {
|
|
93
|
-
taskQueue: "session:ses_1",
|
|
94
|
-
stigmerToken: "tok-1",
|
|
95
|
-
callerClass: "user",
|
|
96
|
-
};
|
|
92
|
+
const env = { taskQueue: "session:ses_1", stigmerToken: "tok-1" };
|
|
97
93
|
|
|
98
94
|
describe("the ensure state machine", () => {
|
|
99
95
|
it("absent → Secret, PVC, Deployment — in that order, no readiness wait", async () => {
|
|
@@ -215,11 +211,7 @@ describe("the manifest shapes (the Java SandboxManifestFactory pins)", () => {
|
|
|
215
211
|
});
|
|
216
212
|
|
|
217
213
|
it("a token-less sandbox omits the token env but keeps the (empty) Secret", () => {
|
|
218
|
-
const tokenless = {
|
|
219
|
-
taskQueue: "session:ses_1",
|
|
220
|
-
stigmerToken: "",
|
|
221
|
-
callerClass: "user",
|
|
222
|
-
};
|
|
214
|
+
const tokenless = { taskQueue: "session:ses_1", stigmerToken: "" };
|
|
223
215
|
const deployment = buildSandboxDeployment(
|
|
224
216
|
"session",
|
|
225
217
|
"ses_1",
|
|
@@ -81,7 +81,6 @@ describe.skipIf(process.platform === "win32")(
|
|
|
81
81
|
await driver.ensureSessionSandbox("ses_smoke", {
|
|
82
82
|
taskQueue: "session:ses_smoke",
|
|
83
83
|
stigmerToken: "tok-smoke",
|
|
84
|
-
callerClass: "user",
|
|
85
84
|
});
|
|
86
85
|
await vi.waitFor(() => expect(spawnCount()).toBe(1), {
|
|
87
86
|
timeout: CHILD_SETTLE_TIMEOUT_MS,
|
|
@@ -92,7 +91,6 @@ describe.skipIf(process.platform === "win32")(
|
|
|
92
91
|
await driver.ensureSessionSandbox("ses_smoke", {
|
|
93
92
|
taskQueue: "session:ses_smoke",
|
|
94
93
|
stigmerToken: "tok-smoke",
|
|
95
|
-
callerClass: "user",
|
|
96
94
|
});
|
|
97
95
|
expect(spawnCount()).toBe(1);
|
|
98
96
|
|
|
@@ -126,7 +124,6 @@ describe.skipIf(process.platform === "win32")(
|
|
|
126
124
|
await driver.ensureSessionSandbox("ses_smoke", {
|
|
127
125
|
taskQueue: "session:ses_smoke",
|
|
128
126
|
stigmerToken: "",
|
|
129
|
-
callerClass: "user",
|
|
130
127
|
});
|
|
131
128
|
await vi.waitFor(() => expect(spawnCount()).toBe(2), {
|
|
132
129
|
timeout: CHILD_SETTLE_TIMEOUT_MS,
|
|
@@ -11,10 +11,7 @@
|
|
|
11
11
|
* - the terminal observer deprovisions exactly on transitions INTO a
|
|
12
12
|
* terminal phase, fire-and-forget, and swallows (logs) teardown
|
|
13
13
|
* failures;
|
|
14
|
-
* - a disabled mint lane launches token-less rather than failing
|
|
15
|
-
* - the caller splits two ways on both lanes: the identity id reaches
|
|
16
|
-
* the credential mint, the caller class reaches the driver's
|
|
17
|
-
* environment unchanged (a composed lane such as `guest` included).
|
|
14
|
+
* - a disabled mint lane launches token-less rather than failing.
|
|
18
15
|
*/
|
|
19
16
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
20
17
|
import { tmpdir } from "node:os";
|
|
@@ -56,25 +53,14 @@ import {
|
|
|
56
53
|
ensureWorkflowSandboxForExecution,
|
|
57
54
|
newWorkflowSandboxTerminalObserver,
|
|
58
55
|
SANDBOX_PROVISIONING_FAILED_PREFIX,
|
|
59
|
-
type SandboxCaller,
|
|
60
56
|
} from "../steps.js";
|
|
61
57
|
|
|
62
58
|
/**
|
|
63
|
-
* The caller the ensure bodies
|
|
64
|
-
*
|
|
65
|
-
* token assertions stay binding-shaped
|
|
66
|
-
* environment. `user` is the plain lane; the class-propagation cases hand
|
|
67
|
-
* in a composed lane's word to prove the body copies, never normalises.
|
|
59
|
+
* The caller identity the ensure bodies thread into the credential mint
|
|
60
|
+
* (C4): the OSS execution-scoped mint ignores it, so these tests pass a
|
|
61
|
+
* fixed value and the token assertions stay binding-shaped.
|
|
68
62
|
*/
|
|
69
63
|
const TEST_CALLER_IDENTITY_ID = "ida_test_caller";
|
|
70
|
-
const TEST_CALLER: SandboxCaller = {
|
|
71
|
-
identityId: TEST_CALLER_IDENTITY_ID,
|
|
72
|
-
callerClass: "user",
|
|
73
|
-
};
|
|
74
|
-
const GUEST_CALLER: SandboxCaller = {
|
|
75
|
-
identityId: "ida_guest_caller",
|
|
76
|
-
callerClass: "guest",
|
|
77
|
-
};
|
|
78
64
|
|
|
79
65
|
const silentLogger = createLogger({
|
|
80
66
|
level: "error",
|
|
@@ -249,7 +235,7 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
249
235
|
temporalConfig: sessionRoutingCloudDefault,
|
|
250
236
|
},
|
|
251
237
|
execution,
|
|
252
|
-
|
|
238
|
+
TEST_CALLER_IDENTITY_ID,
|
|
253
239
|
);
|
|
254
240
|
expect(provisioner.ensured).toEqual([
|
|
255
241
|
{
|
|
@@ -258,32 +244,11 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
258
244
|
env: {
|
|
259
245
|
taskQueue: `session:${sessionId}`,
|
|
260
246
|
stigmerToken: `tok-${execution.metadata?.id ?? ""}`,
|
|
261
|
-
callerClass: "user",
|
|
262
247
|
},
|
|
263
248
|
},
|
|
264
249
|
]);
|
|
265
250
|
});
|
|
266
251
|
|
|
267
|
-
it("hands the caller's class to the driver unchanged and the caller's id to the mint", async () => {
|
|
268
|
-
const provisioner = fakeProvisioner();
|
|
269
|
-
const credentials = capabilityCredentials();
|
|
270
|
-
const { execution } = await seed(ExecutionTarget.CLOUD);
|
|
271
|
-
await ensureSessionSandboxForExecution(
|
|
272
|
-
{
|
|
273
|
-
store,
|
|
274
|
-
logger: silentLogger,
|
|
275
|
-
lane: lane(provisioner, credentials),
|
|
276
|
-
temporalConfig: sessionRoutingCloudDefault,
|
|
277
|
-
},
|
|
278
|
-
execution,
|
|
279
|
-
GUEST_CALLER,
|
|
280
|
-
);
|
|
281
|
-
expect(provisioner.ensured[0]?.env.callerClass).toBe("guest");
|
|
282
|
-
expect(credentials.minted[0]?.callerIdentityId).toBe(
|
|
283
|
-
GUEST_CALLER.identityId,
|
|
284
|
-
);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
252
|
it("delegates the mint to the capability provider with the full provisioning context (C4)", async () => {
|
|
288
253
|
const provisioner = fakeProvisioner();
|
|
289
254
|
const credentials = capabilityCredentials();
|
|
@@ -312,7 +277,7 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
312
277
|
temporalConfig: sessionRoutingCloudDefault,
|
|
313
278
|
},
|
|
314
279
|
execution,
|
|
315
|
-
|
|
280
|
+
TEST_CALLER_IDENTITY_ID,
|
|
316
281
|
);
|
|
317
282
|
|
|
318
283
|
expect(credentials.minted).toEqual([
|
|
@@ -338,7 +303,7 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
338
303
|
temporalConfig: sessionRoutingCloudDefault,
|
|
339
304
|
},
|
|
340
305
|
execution,
|
|
341
|
-
|
|
306
|
+
TEST_CALLER_IDENTITY_ID,
|
|
342
307
|
);
|
|
343
308
|
expect(provisioner.ensured).toEqual([]);
|
|
344
309
|
});
|
|
@@ -362,7 +327,7 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
362
327
|
temporalConfig: sessionRoutingCloudDefault,
|
|
363
328
|
},
|
|
364
329
|
execution,
|
|
365
|
-
|
|
330
|
+
TEST_CALLER_IDENTITY_ID,
|
|
366
331
|
);
|
|
367
332
|
});
|
|
368
333
|
|
|
@@ -380,7 +345,7 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
380
345
|
temporalConfig: sessionRoutingCloudDefault,
|
|
381
346
|
},
|
|
382
347
|
execution,
|
|
383
|
-
|
|
348
|
+
TEST_CALLER_IDENTITY_ID,
|
|
384
349
|
);
|
|
385
350
|
expect(provisioner.ensured).toEqual([]);
|
|
386
351
|
});
|
|
@@ -401,7 +366,7 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
401
366
|
),
|
|
402
367
|
},
|
|
403
368
|
execution,
|
|
404
|
-
|
|
369
|
+
TEST_CALLER_IDENTITY_ID,
|
|
405
370
|
);
|
|
406
371
|
expect(provisioner.ensured).toEqual([]);
|
|
407
372
|
});
|
|
@@ -417,7 +382,7 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
417
382
|
temporalConfig: sessionRoutingCloudDefault,
|
|
418
383
|
},
|
|
419
384
|
execution,
|
|
420
|
-
|
|
385
|
+
TEST_CALLER_IDENTITY_ID,
|
|
421
386
|
);
|
|
422
387
|
expect(provisioner.ensured[0]?.env.stigmerToken).toBe("");
|
|
423
388
|
});
|
|
@@ -436,7 +401,7 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
436
401
|
temporalConfig: sessionRoutingCloudDefault,
|
|
437
402
|
},
|
|
438
403
|
execution,
|
|
439
|
-
|
|
404
|
+
TEST_CALLER_IDENTITY_ID,
|
|
440
405
|
);
|
|
441
406
|
const stamped = await store.getResource(
|
|
442
407
|
ApiResourceKind.agent_execution,
|
|
@@ -474,7 +439,7 @@ describe("the session lane (ensureSessionSandboxForExecution)", () => {
|
|
|
474
439
|
temporalConfig: sessionRoutingCloudDefault,
|
|
475
440
|
},
|
|
476
441
|
execution,
|
|
477
|
-
|
|
442
|
+
TEST_CALLER_IDENTITY_ID,
|
|
478
443
|
);
|
|
479
444
|
const after = await store.getResource(
|
|
480
445
|
ApiResourceKind.agent_execution,
|
|
@@ -502,35 +467,17 @@ describe("the workflow lane (ensureWorkflowSandboxForExecution)", () => {
|
|
|
502
467
|
temporalConfig: executionRoutingConfig,
|
|
503
468
|
},
|
|
504
469
|
workflowExecution(ExecutionTarget.CLOUD),
|
|
505
|
-
|
|
470
|
+
TEST_CALLER_IDENTITY_ID,
|
|
506
471
|
);
|
|
507
472
|
expect(provisioner.ensured).toEqual([
|
|
508
473
|
{
|
|
509
474
|
scope: "workflow",
|
|
510
475
|
id: "wfx_sbx_1",
|
|
511
|
-
env: {
|
|
512
|
-
taskQueue: "wfexec:wfx_sbx_1",
|
|
513
|
-
stigmerToken: "tok-wfx_sbx_1",
|
|
514
|
-
callerClass: "user",
|
|
515
|
-
},
|
|
476
|
+
env: { taskQueue: "wfexec:wfx_sbx_1", stigmerToken: "tok-wfx_sbx_1" },
|
|
516
477
|
},
|
|
517
478
|
]);
|
|
518
479
|
});
|
|
519
480
|
|
|
520
|
-
it("hands the caller's class to the driver unchanged on the workflow lane", async () => {
|
|
521
|
-
const provisioner = fakeProvisioner();
|
|
522
|
-
await ensureWorkflowSandboxForExecution(
|
|
523
|
-
{
|
|
524
|
-
logger: silentLogger,
|
|
525
|
-
lane: lane(provisioner),
|
|
526
|
-
temporalConfig: executionRoutingConfig,
|
|
527
|
-
},
|
|
528
|
-
workflowExecution(ExecutionTarget.CLOUD),
|
|
529
|
-
GUEST_CALLER,
|
|
530
|
-
);
|
|
531
|
-
expect(provisioner.ensured[0]?.env.callerClass).toBe("guest");
|
|
532
|
-
});
|
|
533
|
-
|
|
534
481
|
it("delegates the mint to the capability provider on the workflow scope (C4)", async () => {
|
|
535
482
|
const provisioner = fakeProvisioner();
|
|
536
483
|
const credentials = capabilityCredentials();
|
|
@@ -544,7 +491,7 @@ describe("the workflow lane (ensureWorkflowSandboxForExecution)", () => {
|
|
|
544
491
|
metadata: { id: "wfx_sbx_cap", name: "wfx_sbx_cap", org: "org-test" },
|
|
545
492
|
spec: { executionTarget: ExecutionTarget.CLOUD },
|
|
546
493
|
}),
|
|
547
|
-
|
|
494
|
+
TEST_CALLER_IDENTITY_ID,
|
|
548
495
|
);
|
|
549
496
|
expect(credentials.minted).toEqual([
|
|
550
497
|
{
|
|
@@ -567,7 +514,7 @@ describe("the workflow lane (ensureWorkflowSandboxForExecution)", () => {
|
|
|
567
514
|
temporalConfig: executionRoutingConfig,
|
|
568
515
|
},
|
|
569
516
|
workflowExecution(ExecutionTarget.LOCAL),
|
|
570
|
-
|
|
517
|
+
TEST_CALLER_IDENTITY_ID,
|
|
571
518
|
);
|
|
572
519
|
await ensureWorkflowSandboxForExecution(
|
|
573
520
|
{
|
|
@@ -581,7 +528,7 @@ describe("the workflow lane (ensureWorkflowSandboxForExecution)", () => {
|
|
|
581
528
|
),
|
|
582
529
|
},
|
|
583
530
|
workflowExecution(ExecutionTarget.CLOUD),
|
|
584
|
-
|
|
531
|
+
TEST_CALLER_IDENTITY_ID,
|
|
585
532
|
);
|
|
586
533
|
expect(provisioner.ensured).toEqual([]);
|
|
587
534
|
});
|
|
@@ -598,7 +545,7 @@ describe("the workflow lane (ensureWorkflowSandboxForExecution)", () => {
|
|
|
598
545
|
temporalConfig: executionRoutingConfig,
|
|
599
546
|
},
|
|
600
547
|
workflowExecution(ExecutionTarget.CLOUD),
|
|
601
|
-
|
|
548
|
+
TEST_CALLER_IDENTITY_ID,
|
|
602
549
|
);
|
|
603
550
|
expect.unreachable("the workflow lane must throw on failure");
|
|
604
551
|
} catch (error) {
|
|
@@ -85,14 +85,7 @@ const RUNNER_RESOURCES = {
|
|
|
85
85
|
limits: { cpu: "2", memory: "2Gi" },
|
|
86
86
|
} as const;
|
|
87
87
|
|
|
88
|
-
/**
|
|
89
|
-
* The persistent-workspace scopes (SandboxScope.persistentWorkspace).
|
|
90
|
-
* Durability here is decided by SCOPE alone: this driver reads neither the
|
|
91
|
-
* environment's caller class nor any per-session policy, so every session
|
|
92
|
-
* keeps its PVC whoever asked. A driver that gives some caller classes a
|
|
93
|
-
* workspace that dies with the conversation is a composition's choice
|
|
94
|
-
* built on SandboxEnvironment.callerClass, not a built-in behaviour.
|
|
95
|
-
*/
|
|
88
|
+
/** The persistent-workspace scopes (SandboxScope.persistentWorkspace). */
|
|
96
89
|
const PERSISTENT_SCOPES: ReadonlySet<SandboxScope> = new Set([
|
|
97
90
|
"session",
|
|
98
91
|
"workflow",
|
|
@@ -39,16 +39,13 @@
|
|
|
39
39
|
* runner process polls the queues.
|
|
40
40
|
*/
|
|
41
41
|
import type { Logger } from "../boot/logger.js";
|
|
42
|
-
import type { CallerClass } from "../extensions/identity.js";
|
|
43
42
|
|
|
44
43
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* never travels here. Endpoints, images and resource shapes are driver
|
|
51
|
-
* configuration, not per-sandbox state.
|
|
44
|
+
* The runtime handed to a provisioner for one sandbox: which queue the
|
|
45
|
+
* sandboxed runner must poll and the credential it authenticates with.
|
|
46
|
+
* Deliberately minimal (the Java SandboxEnvironment carries the same two
|
|
47
|
+
* load-bearing fields plus cloud-only accounting) — endpoints, images,
|
|
48
|
+
* and resource shapes are DRIVER configuration, not per-sandbox state.
|
|
52
49
|
*/
|
|
53
50
|
export interface SandboxEnvironment {
|
|
54
51
|
/** The Temporal task queue the sandboxed runner polls (session:{id} / wfexec:{id}). */
|
|
@@ -59,18 +56,6 @@ export interface SandboxEnvironment {
|
|
|
59
56
|
* and EC decrypt falls back to redaction, the oss#535 posture.
|
|
60
57
|
*/
|
|
61
58
|
readonly stigmerToken: string;
|
|
62
|
-
/**
|
|
63
|
-
* The class of the caller whose request provisioned this sandbox — the
|
|
64
|
-
* identity interceptor's word for it (`user`, `internal`, a composed
|
|
65
|
-
* verifier's own lane such as a guest or a scheduled fire). A driver
|
|
66
|
-
* that gives some session classes a workspace that does not outlive the
|
|
67
|
-
* conversation reads it here; the built-in drivers (`local-process`,
|
|
68
|
-
* `docker`, `kubernetes`) ignore it and keep every session persistent.
|
|
69
|
-
* Required rather than optional so a construction site that forgets it
|
|
70
|
-
* fails to compile instead of silently reading as a persistent user
|
|
71
|
-
* session to every driver.
|
|
72
|
-
*/
|
|
73
|
-
readonly callerClass: CallerClass;
|
|
74
59
|
}
|
|
75
60
|
|
|
76
61
|
/** One sandbox's observed live state (the Q5 probe result). */
|