@agent-vm/gateway-interface 0.0.69 → 0.0.71

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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { SecretResolver, SecretSpec, VfsMountSpec } from "@agent-vm/gondolin-adapter";
1
+ import { MediatedSecretSpec, SecretResolver } from "@agent-vm/secret-management";
2
+ import { VfsMountSpec } from "@agent-vm/gondolin-adapter";
2
3
 
3
4
  //#region src/gateway-runtime-contract.d.ts
4
5
  declare const gatewayTypeValues: readonly ["openclaw", "worker"];
@@ -19,6 +20,73 @@ declare function targetsAudience(configAudience: VmAudience, runtimeAudience: Ru
19
20
  declare function egressHostsForAudience(egressHosts: readonly EgressHostConfig[], runtimeAudience: RuntimeVmAudience): readonly string[];
20
21
  declare function gatewayVmAllowedHosts(egressHosts: readonly EgressHostConfig[]): readonly string[];
21
22
  //#endregion
23
+ //#region src/force-ipv4-egress.d.ts
24
+ /**
25
+ * Canonical NODE_OPTIONS value for forcing IPv4-preference egress
26
+ * in agent-vm VMs.
27
+ *
28
+ * Background: Gondolin's synthetic DNS (when tcpHosts is enabled)
29
+ * returns a per-host IPv4 (reverse-lookable) and a single shared
30
+ * IPv4-mapped IPv6 (::ffff:198.18.0.1, NOT reverse-lookable). Node
31
+ * 20+'s fetch (via undici, autoSelectFamily: true) races both
32
+ * families; when the IPv6 race wins (~5-20% under sequential load),
33
+ * gondolin cannot route it and the request fails with a non-JSON
34
+ * 400 (HTTP) or 403 (TLS). The two flags below stop the race:
35
+ *
36
+ * --dns-result-order=ipv4first changes dns.lookup() so
37
+ * IPv4 addresses are listed
38
+ * before IPv6.
39
+ *
40
+ * --no-network-family-autoselection disables Node's Happy
41
+ * Eyeballs entirely. This is
42
+ * the load-bearing flag —
43
+ * --dns-result-order alone
44
+ * doesn't prevent Node from
45
+ * racing both families if
46
+ * IPv4 is slow.
47
+ *
48
+ * Composition: NODE_OPTIONS is whitespace-separated. To add more
49
+ * flags downstream, append rather than replace. Example:
50
+ *
51
+ * NODE_OPTIONS: `${FORCE_IPV4_EGRESS_NODE_OPTIONS} --inspect`
52
+ *
53
+ * Reference: see `shravan-claw@0ddf5f2:docs/wip/debugging/
54
+ * 2026-05-21-lease-keepalive-400-and-discord-403-ipv6-race.md`
55
+ * for the full root-cause analysis. Node-side flag references:
56
+ * https://github.com/nodejs/node/issues/54359 (autoSelectFamily
57
+ * revert recommendation by the Node core team).
58
+ */
59
+ declare const FORCE_IPV4_EGRESS_NODE_OPTIONS = "--dns-result-order=ipv4first --no-network-family-autoselection";
60
+ /**
61
+ * Compose the forced IPv4-preference flags with a user-provided
62
+ * NODE_OPTIONS value (if any).
63
+ *
64
+ * Use this at every site where NODE_OPTIONS is set into a VM env
65
+ * block AFTER a spread of user-controlled secrets, to guarantee
66
+ * the forced flags are always present in the final value even if
67
+ * a zone secret happens to provide its own NODE_OPTIONS.
68
+ *
69
+ * Forced flags come FIRST so they are unambiguously applied.
70
+ * User-provided flags are appended verbatim. Node treats NODE_OPTIONS
71
+ * as a whitespace-separated list and all flags apply.
72
+ *
73
+ * Returns just the forced flags if the user value is undefined,
74
+ * empty, or whitespace-only.
75
+ *
76
+ * Examples:
77
+ *
78
+ * composeNodeOptions(undefined)
79
+ * ──► '--dns-result-order=ipv4first --no-network-family-autoselection'
80
+ *
81
+ * composeNodeOptions('')
82
+ * ──► '--dns-result-order=ipv4first --no-network-family-autoselection'
83
+ *
84
+ * composeNodeOptions('--inspect=0.0.0.0:9229')
85
+ * ──► '--dns-result-order=ipv4first --no-network-family-autoselection
86
+ * --inspect=0.0.0.0:9229'
87
+ */
88
+ declare function composeNodeOptions(userValue: string | undefined): string;
89
+ //#endregion
22
90
  //#region src/gateway-process-spec.d.ts
23
91
  type GatewayHealthCheck = {
24
92
  readonly type: 'http';
@@ -48,7 +116,7 @@ interface GatewayProcessSpec {
48
116
  interface GatewayVmSpec {
49
117
  readonly environment: Record<string, string>;
50
118
  readonly vfsMounts: Record<string, VfsMountSpec>;
51
- readonly mediatedSecrets: Record<string, SecretSpec>;
119
+ readonly mediatedSecrets: Record<string, MediatedSecretSpec>;
52
120
  readonly tcpHosts: Record<string, string>;
53
121
  readonly allowedHosts: readonly string[];
54
122
  readonly rootfsMode: 'readonly' | 'memory' | 'cow';
@@ -105,6 +173,7 @@ interface OpenClawGatewayZoneGatewayConfig extends GatewayZoneBaseGatewayConfig
105
173
  readonly type: 'openclaw';
106
174
  readonly zoneFilesDir: string;
107
175
  readonly authProfilesByAgent?: Readonly<Record<string, OnePasswordGatewayAuthProfilesRef | EnvironmentGatewayAuthProfilesRef>>;
176
+ readonly rawEnvSecrets?: readonly string[];
108
177
  }
109
178
  interface WorkerGatewayZoneGatewayConfig extends GatewayZoneBaseGatewayConfig {
110
179
  readonly type: 'worker';
@@ -137,8 +206,9 @@ interface GatewayZoneConfig {
137
206
  readonly id: string;
138
207
  readonly agents?: readonly GatewayZoneAgentConfig[];
139
208
  readonly gateway: GatewayZoneGatewayConfig;
140
- readonly mcp?: GatewayZoneMcpPortalConfig;
209
+ readonly mcpPortal?: GatewayZoneMcpPortalConfig;
141
210
  readonly runtimeMcpServers?: Readonly<Record<string, GatewayZoneMcpServerConfig>>;
211
+ readonly runtimeMediatedSecrets?: Readonly<Record<string, MediatedSecretSpec>>;
142
212
  readonly runtimeEnvironment?: Readonly<Record<string, string>>;
143
213
  readonly runtimePluginConfigs?: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
144
214
  readonly secrets: Readonly<Record<string, GatewaySecretConfig>>;
@@ -196,7 +266,12 @@ interface GatewayLifecycle {
196
266
  //#region src/split-resolved-gateway-secrets.d.ts
197
267
  interface SplitResolvedSecretsResult {
198
268
  readonly environmentSecrets: Record<string, string>;
199
- readonly mediatedSecrets: Record<string, SecretSpec>;
269
+ readonly mediatedSecrets: Record<string, MediatedSecretSpec>;
270
+ }
271
+ interface MergeRuntimeGatewaySecretsOptions {
272
+ readonly logPrefix?: string;
273
+ readonly runtimeEnvironment?: Readonly<Record<string, string>> | undefined;
274
+ readonly runtimeMediatedSecrets?: Readonly<Record<string, MediatedSecretSpec>> | undefined;
200
275
  }
201
276
  type SecretInjectionConfig = GatewaySecretConfig;
202
277
  interface SplitResolvedSecretsOptions {
@@ -206,6 +281,7 @@ interface SplitResolvedSecretsOptions {
206
281
  declare function splitResolvedSecretsByInjection(secretConfigs: Readonly<Record<string, SecretInjectionConfig>>, resolvedSecrets: Record<string, string>, options: SplitResolvedSecretsOptions): SplitResolvedSecretsResult;
207
282
  type SplitResolvedGatewaySecretsResult = SplitResolvedSecretsResult;
208
283
  declare function splitResolvedGatewaySecrets(zone: GatewayZoneConfig, resolvedSecrets: Record<string, string>): SplitResolvedGatewaySecretsResult;
284
+ declare function mergeRuntimeGatewaySecrets(baseSecrets: SplitResolvedSecretsResult, options?: MergeRuntimeGatewaySecretsOptions): SplitResolvedSecretsResult;
209
285
  //#endregion
210
- export { type BuildGatewayVmSpecOptions, type EgressHostConfig, type EnvInjectedGatewaySecretConfig, type GatewayAuthConfig, type GatewayHealthCheck, type GatewayLifecycle, type GatewayProcessSpec, type GatewaySecretConfig, type GatewayType, type GatewayVmSpec, type GatewayZoneAgentConfig, type GatewayZoneConfig, type GatewayZoneMcpPortalConfig, type HttpMediatedGatewaySecretConfig, type RuntimeVmAudience, type SecretInjectionConfig, type SplitResolvedGatewaySecretsResult, type SplitResolvedSecretsResult, type VmAudience, buildGatewaySessionLabel, buildToolSessionLabel, controllerVmHost, egressHostsForAudience, gatewayTypeValues, gatewayVmAllowedHosts, splitResolvedGatewaySecrets, splitResolvedSecretsByInjection, targetsAudience, vmAudienceValues };
286
+ export { type BuildGatewayVmSpecOptions, type EgressHostConfig, type EnvInjectedGatewaySecretConfig, FORCE_IPV4_EGRESS_NODE_OPTIONS, type GatewayAuthConfig, type GatewayHealthCheck, type GatewayLifecycle, type GatewayProcessSpec, type GatewaySecretConfig, type GatewayType, type GatewayVmSpec, type GatewayZoneAgentConfig, type GatewayZoneConfig, type GatewayZoneMcpPortalConfig, type HttpMediatedGatewaySecretConfig, type RuntimeVmAudience, type SecretInjectionConfig, type SplitResolvedGatewaySecretsResult, type SplitResolvedSecretsResult, type VmAudience, buildGatewaySessionLabel, buildToolSessionLabel, composeNodeOptions, controllerVmHost, egressHostsForAudience, gatewayTypeValues, gatewayVmAllowedHosts, mergeRuntimeGatewaySecrets, splitResolvedGatewaySecrets, splitResolvedSecretsByInjection, targetsAudience, vmAudienceValues };
211
287
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/gateway-runtime-contract.ts","../src/audience.ts","../src/gateway-process-spec.ts","../src/gateway-vm-spec.ts","../src/gateway-lifecycle.ts","../src/split-resolved-gateway-secrets.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/gateway-runtime-contract.ts","../src/audience.ts","../src/force-ipv4-egress.ts","../src/gateway-process-spec.ts","../src/gateway-vm-spec.ts","../src/gateway-lifecycle.ts","../src/split-resolved-gateway-secrets.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -24,6 +24,77 @@ function gatewayVmAllowedHosts(egressHosts) {
24
24
  return Array.from(new Set([controllerVmHost, ...egressHostsForAudience(egressHosts, "gateway")]));
25
25
  }
26
26
  //#endregion
27
+ //#region src/force-ipv4-egress.ts
28
+ /**
29
+ * Canonical NODE_OPTIONS value for forcing IPv4-preference egress
30
+ * in agent-vm VMs.
31
+ *
32
+ * Background: Gondolin's synthetic DNS (when tcpHosts is enabled)
33
+ * returns a per-host IPv4 (reverse-lookable) and a single shared
34
+ * IPv4-mapped IPv6 (::ffff:198.18.0.1, NOT reverse-lookable). Node
35
+ * 20+'s fetch (via undici, autoSelectFamily: true) races both
36
+ * families; when the IPv6 race wins (~5-20% under sequential load),
37
+ * gondolin cannot route it and the request fails with a non-JSON
38
+ * 400 (HTTP) or 403 (TLS). The two flags below stop the race:
39
+ *
40
+ * --dns-result-order=ipv4first changes dns.lookup() so
41
+ * IPv4 addresses are listed
42
+ * before IPv6.
43
+ *
44
+ * --no-network-family-autoselection disables Node's Happy
45
+ * Eyeballs entirely. This is
46
+ * the load-bearing flag —
47
+ * --dns-result-order alone
48
+ * doesn't prevent Node from
49
+ * racing both families if
50
+ * IPv4 is slow.
51
+ *
52
+ * Composition: NODE_OPTIONS is whitespace-separated. To add more
53
+ * flags downstream, append rather than replace. Example:
54
+ *
55
+ * NODE_OPTIONS: `${FORCE_IPV4_EGRESS_NODE_OPTIONS} --inspect`
56
+ *
57
+ * Reference: see `shravan-claw@0ddf5f2:docs/wip/debugging/
58
+ * 2026-05-21-lease-keepalive-400-and-discord-403-ipv6-race.md`
59
+ * for the full root-cause analysis. Node-side flag references:
60
+ * https://github.com/nodejs/node/issues/54359 (autoSelectFamily
61
+ * revert recommendation by the Node core team).
62
+ */
63
+ const FORCE_IPV4_EGRESS_NODE_OPTIONS = "--dns-result-order=ipv4first --no-network-family-autoselection";
64
+ /**
65
+ * Compose the forced IPv4-preference flags with a user-provided
66
+ * NODE_OPTIONS value (if any).
67
+ *
68
+ * Use this at every site where NODE_OPTIONS is set into a VM env
69
+ * block AFTER a spread of user-controlled secrets, to guarantee
70
+ * the forced flags are always present in the final value even if
71
+ * a zone secret happens to provide its own NODE_OPTIONS.
72
+ *
73
+ * Forced flags come FIRST so they are unambiguously applied.
74
+ * User-provided flags are appended verbatim. Node treats NODE_OPTIONS
75
+ * as a whitespace-separated list and all flags apply.
76
+ *
77
+ * Returns just the forced flags if the user value is undefined,
78
+ * empty, or whitespace-only.
79
+ *
80
+ * Examples:
81
+ *
82
+ * composeNodeOptions(undefined)
83
+ * ──► '--dns-result-order=ipv4first --no-network-family-autoselection'
84
+ *
85
+ * composeNodeOptions('')
86
+ * ──► '--dns-result-order=ipv4first --no-network-family-autoselection'
87
+ *
88
+ * composeNodeOptions('--inspect=0.0.0.0:9229')
89
+ * ──► '--dns-result-order=ipv4first --no-network-family-autoselection
90
+ * --inspect=0.0.0.0:9229'
91
+ */
92
+ function composeNodeOptions(userValue) {
93
+ const trimmed = userValue?.trim() ?? "";
94
+ if (trimmed === "") return FORCE_IPV4_EGRESS_NODE_OPTIONS;
95
+ return `${FORCE_IPV4_EGRESS_NODE_OPTIONS} ${trimmed}`;
96
+ }
97
+ //#endregion
27
98
  //#region src/split-resolved-gateway-secrets.ts
28
99
  function splitResolvedSecretsByInjection(secretConfigs, resolvedSecrets, options) {
29
100
  const environmentSecrets = {};
@@ -56,7 +127,29 @@ function splitResolvedGatewaySecrets(zone, resolvedSecrets) {
56
127
  logPrefix: "split-resolved-gateway-secrets"
57
128
  });
58
129
  }
130
+ function assertNoRuntimeSecretCollision(secretName, target, baseSecrets, runtimeSeen, logPrefix) {
131
+ if (runtimeSeen.has(secretName)) throw new Error(`[${logPrefix}] Runtime gateway secret '${secretName}' is declared for both environment and http-mediation injection.`);
132
+ if (secretName in baseSecrets.environmentSecrets) throw new Error(`[${logPrefix}] Runtime gateway ${target} secret '${secretName}' would overwrite an authored environment secret.`);
133
+ if (secretName in baseSecrets.mediatedSecrets) throw new Error(`[${logPrefix}] Runtime gateway ${target} secret '${secretName}' would overwrite an authored http-mediation secret.`);
134
+ runtimeSeen.add(secretName);
135
+ }
136
+ function mergeRuntimeGatewaySecrets(baseSecrets, options = {}) {
137
+ const logPrefix = options.logPrefix ?? "merge-runtime-gateway-secrets";
138
+ const runtimeSeen = /* @__PURE__ */ new Set();
139
+ for (const secretName of Object.keys(options.runtimeEnvironment ?? {})) assertNoRuntimeSecretCollision(secretName, "environment", baseSecrets, runtimeSeen, logPrefix);
140
+ for (const secretName of Object.keys(options.runtimeMediatedSecrets ?? {})) assertNoRuntimeSecretCollision(secretName, "http-mediation", baseSecrets, runtimeSeen, logPrefix);
141
+ return {
142
+ environmentSecrets: {
143
+ ...baseSecrets.environmentSecrets,
144
+ ...options.runtimeEnvironment
145
+ },
146
+ mediatedSecrets: {
147
+ ...baseSecrets.mediatedSecrets,
148
+ ...options.runtimeMediatedSecrets
149
+ }
150
+ };
151
+ }
59
152
  //#endregion
60
- export { buildGatewaySessionLabel, buildToolSessionLabel, controllerVmHost, egressHostsForAudience, gatewayTypeValues, gatewayVmAllowedHosts, splitResolvedGatewaySecrets, splitResolvedSecretsByInjection, targetsAudience, vmAudienceValues };
153
+ export { FORCE_IPV4_EGRESS_NODE_OPTIONS, buildGatewaySessionLabel, buildToolSessionLabel, composeNodeOptions, controllerVmHost, egressHostsForAudience, gatewayTypeValues, gatewayVmAllowedHosts, mergeRuntimeGatewaySecrets, splitResolvedGatewaySecrets, splitResolvedSecretsByInjection, targetsAudience, vmAudienceValues };
61
154
 
62
155
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/gateway-runtime-contract.ts","../src/audience.ts","../src/split-resolved-gateway-secrets.ts"],"sourcesContent":["export const gatewayTypeValues = ['openclaw', 'worker'] as const;\n\nexport type GatewayType = (typeof gatewayTypeValues)[number];\n\nexport function buildGatewaySessionLabel(projectNamespace: string, zoneId: string): string {\n\treturn `${projectNamespace}:${zoneId}:gateway`;\n}\n\nexport function buildToolSessionLabel(\n\tprojectNamespace: string,\n\tzoneId: string,\n\ttcpSlot: number,\n): string {\n\treturn `${projectNamespace}:${zoneId}:tool:${tcpSlot}`;\n}\n","export const vmAudienceValues = ['gateway', 'tool-vm', 'both'] as const;\n\nexport type VmAudience = (typeof vmAudienceValues)[number];\nexport type RuntimeVmAudience = Exclude<VmAudience, 'both'>;\n\nexport interface EgressHostConfig {\n\treadonly host: string;\n\treadonly audience: VmAudience;\n}\n\nexport const controllerVmHost = 'controller.vm.host';\n\nexport function targetsAudience(\n\tconfigAudience: VmAudience,\n\truntimeAudience: RuntimeVmAudience,\n): boolean {\n\treturn configAudience === runtimeAudience || configAudience === 'both';\n}\n\nexport function egressHostsForAudience(\n\tegressHosts: readonly EgressHostConfig[],\n\truntimeAudience: RuntimeVmAudience,\n): readonly string[] {\n\treturn egressHosts\n\t\t.filter((egressHost) => targetsAudience(egressHost.audience, runtimeAudience))\n\t\t.map((egressHost) => egressHost.host);\n}\n\nexport function gatewayVmAllowedHosts(egressHosts: readonly EgressHostConfig[]): readonly string[] {\n\treturn Array.from(new Set([controllerVmHost, ...egressHostsForAudience(egressHosts, 'gateway')]));\n}\n","import type { SecretSpec } from '@agent-vm/gondolin-adapter';\n\nimport { targetsAudience, type RuntimeVmAudience } from './audience.js';\nimport type { GatewaySecretConfig, GatewayZoneConfig } from './gateway-lifecycle.js';\n\nexport interface SplitResolvedSecretsResult {\n\treadonly environmentSecrets: Record<string, string>;\n\treadonly mediatedSecrets: Record<string, SecretSpec>;\n}\n\nexport type SecretInjectionConfig = GatewaySecretConfig;\n\nexport interface SplitResolvedSecretsOptions {\n\treadonly audience: RuntimeVmAudience;\n\treadonly logPrefix?: string;\n}\n\nexport function splitResolvedSecretsByInjection(\n\tsecretConfigs: Readonly<Record<string, SecretInjectionConfig>>,\n\tresolvedSecrets: Record<string, string>,\n\toptions: SplitResolvedSecretsOptions,\n): SplitResolvedSecretsResult {\n\tconst environmentSecrets: Record<string, string> = {};\n\tconst mediatedSecrets: Record<string, SecretSpec> = {};\n\tconst logPrefix = options.logPrefix ?? 'split-resolved-secrets';\n\n\tfor (const [secretName, secretValue] of Object.entries(resolvedSecrets)) {\n\t\tconst secretConfig = secretConfigs[secretName];\n\t\tif (!secretConfig) {\n\t\t\tthrow new Error(\n\t\t\t\t`[${logPrefix}] Secret '${secretName}' was resolved but has no matching secret config.`,\n\t\t\t);\n\t\t}\n\t\tif (!targetsAudience(secretConfig.audience, options.audience)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (secretConfig.injection === 'http-mediation') {\n\t\t\tif (secretConfig.hosts.length === 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[${logPrefix}] Secret '${secretName}' uses http-mediation but declares no hosts.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tmediatedSecrets[secretName] = {\n\t\t\t\thosts: [...secretConfig.hosts],\n\t\t\t\tvalue: secretValue,\n\t\t\t};\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst envSecretAudience = (secretConfig as { readonly audience: string }).audience;\n\t\tif (envSecretAudience !== 'gateway') {\n\t\t\tthrow new Error(\n\t\t\t\t`[${logPrefix}] Secret '${secretName}' uses env injection with non-gateway audience '${envSecretAudience}'.`,\n\t\t\t);\n\t\t}\n\t\tif (options.audience === 'gateway') {\n\t\t\tenvironmentSecrets[secretName] = secretValue;\n\t\t}\n\t}\n\n\treturn { environmentSecrets, mediatedSecrets };\n}\n\nexport type SplitResolvedGatewaySecretsResult = SplitResolvedSecretsResult;\n\nexport function splitResolvedGatewaySecrets(\n\tzone: GatewayZoneConfig,\n\tresolvedSecrets: Record<string, string>,\n): SplitResolvedGatewaySecretsResult {\n\treturn splitResolvedSecretsByInjection(zone.secrets, resolvedSecrets, {\n\t\taudience: 'gateway',\n\t\tlogPrefix: 'split-resolved-gateway-secrets',\n\t});\n}\n"],"mappings":";AAAA,MAAa,oBAAoB,CAAC,YAAY,SAAS;AAIvD,SAAgB,yBAAyB,kBAA0B,QAAwB;CAC1F,OAAO,GAAG,iBAAiB,GAAG,OAAO;;AAGtC,SAAgB,sBACf,kBACA,QACA,SACS;CACT,OAAO,GAAG,iBAAiB,GAAG,OAAO,QAAQ;;;;ACb9C,MAAa,mBAAmB;CAAC;CAAW;CAAW;CAAO;AAU9D,MAAa,mBAAmB;AAEhC,SAAgB,gBACf,gBACA,iBACU;CACV,OAAO,mBAAmB,mBAAmB,mBAAmB;;AAGjE,SAAgB,uBACf,aACA,iBACoB;CACpB,OAAO,YACL,QAAQ,eAAe,gBAAgB,WAAW,UAAU,gBAAgB,CAAC,CAC7E,KAAK,eAAe,WAAW,KAAK;;AAGvC,SAAgB,sBAAsB,aAA6D;CAClG,OAAO,MAAM,KAAK,IAAI,IAAI,CAAC,kBAAkB,GAAG,uBAAuB,aAAa,UAAU,CAAC,CAAC,CAAC;;;;ACZlG,SAAgB,gCACf,eACA,iBACA,SAC6B;CAC7B,MAAM,qBAA6C,EAAE;CACrD,MAAM,kBAA8C,EAAE;CACtD,MAAM,YAAY,QAAQ,aAAa;CAEvC,KAAK,MAAM,CAAC,YAAY,gBAAgB,OAAO,QAAQ,gBAAgB,EAAE;EACxE,MAAM,eAAe,cAAc;EACnC,IAAI,CAAC,cACJ,MAAM,IAAI,MACT,IAAI,UAAU,YAAY,WAAW,mDACrC;EAEF,IAAI,CAAC,gBAAgB,aAAa,UAAU,QAAQ,SAAS,EAC5D;EAGD,IAAI,aAAa,cAAc,kBAAkB;GAChD,IAAI,aAAa,MAAM,WAAW,GACjC,MAAM,IAAI,MACT,IAAI,UAAU,YAAY,WAAW,8CACrC;GAEF,gBAAgB,cAAc;IAC7B,OAAO,CAAC,GAAG,aAAa,MAAM;IAC9B,OAAO;IACP;GACD;;EAGD,MAAM,oBAAqB,aAA+C;EAC1E,IAAI,sBAAsB,WACzB,MAAM,IAAI,MACT,IAAI,UAAU,YAAY,WAAW,kDAAkD,kBAAkB,IACzG;EAEF,IAAI,QAAQ,aAAa,WACxB,mBAAmB,cAAc;;CAInC,OAAO;EAAE;EAAoB;EAAiB;;AAK/C,SAAgB,4BACf,MACA,iBACoC;CACpC,OAAO,gCAAgC,KAAK,SAAS,iBAAiB;EACrE,UAAU;EACV,WAAW;EACX,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/gateway-runtime-contract.ts","../src/audience.ts","../src/force-ipv4-egress.ts","../src/split-resolved-gateway-secrets.ts"],"sourcesContent":["export const gatewayTypeValues = ['openclaw', 'worker'] as const;\n\nexport type GatewayType = (typeof gatewayTypeValues)[number];\n\nexport function buildGatewaySessionLabel(projectNamespace: string, zoneId: string): string {\n\treturn `${projectNamespace}:${zoneId}:gateway`;\n}\n\nexport function buildToolSessionLabel(\n\tprojectNamespace: string,\n\tzoneId: string,\n\ttcpSlot: number,\n): string {\n\treturn `${projectNamespace}:${zoneId}:tool:${tcpSlot}`;\n}\n","export const vmAudienceValues = ['gateway', 'tool-vm', 'both'] as const;\n\nexport type VmAudience = (typeof vmAudienceValues)[number];\nexport type RuntimeVmAudience = Exclude<VmAudience, 'both'>;\n\nexport interface EgressHostConfig {\n\treadonly host: string;\n\treadonly audience: VmAudience;\n}\n\nexport const controllerVmHost = 'controller.vm.host';\n\nexport function targetsAudience(\n\tconfigAudience: VmAudience,\n\truntimeAudience: RuntimeVmAudience,\n): boolean {\n\treturn configAudience === runtimeAudience || configAudience === 'both';\n}\n\nexport function egressHostsForAudience(\n\tegressHosts: readonly EgressHostConfig[],\n\truntimeAudience: RuntimeVmAudience,\n): readonly string[] {\n\treturn egressHosts\n\t\t.filter((egressHost) => targetsAudience(egressHost.audience, runtimeAudience))\n\t\t.map((egressHost) => egressHost.host);\n}\n\nexport function gatewayVmAllowedHosts(egressHosts: readonly EgressHostConfig[]): readonly string[] {\n\treturn Array.from(new Set([controllerVmHost, ...egressHostsForAudience(egressHosts, 'gateway')]));\n}\n","/**\n * Canonical NODE_OPTIONS value for forcing IPv4-preference egress\n * in agent-vm VMs.\n *\n * Background: Gondolin's synthetic DNS (when tcpHosts is enabled)\n * returns a per-host IPv4 (reverse-lookable) and a single shared\n * IPv4-mapped IPv6 (::ffff:198.18.0.1, NOT reverse-lookable). Node\n * 20+'s fetch (via undici, autoSelectFamily: true) races both\n * families; when the IPv6 race wins (~5-20% under sequential load),\n * gondolin cannot route it and the request fails with a non-JSON\n * 400 (HTTP) or 403 (TLS). The two flags below stop the race:\n *\n * --dns-result-order=ipv4first changes dns.lookup() so\n * IPv4 addresses are listed\n * before IPv6.\n *\n * --no-network-family-autoselection disables Node's Happy\n * Eyeballs entirely. This is\n * the load-bearing flag —\n * --dns-result-order alone\n * doesn't prevent Node from\n * racing both families if\n * IPv4 is slow.\n *\n * Composition: NODE_OPTIONS is whitespace-separated. To add more\n * flags downstream, append rather than replace. Example:\n *\n * NODE_OPTIONS: `${FORCE_IPV4_EGRESS_NODE_OPTIONS} --inspect`\n *\n * Reference: see `shravan-claw@0ddf5f2:docs/wip/debugging/\n * 2026-05-21-lease-keepalive-400-and-discord-403-ipv6-race.md`\n * for the full root-cause analysis. Node-side flag references:\n * https://github.com/nodejs/node/issues/54359 (autoSelectFamily\n * revert recommendation by the Node core team).\n */\nexport const FORCE_IPV4_EGRESS_NODE_OPTIONS =\n\t'--dns-result-order=ipv4first --no-network-family-autoselection';\n\n/**\n * Compose the forced IPv4-preference flags with a user-provided\n * NODE_OPTIONS value (if any).\n *\n * Use this at every site where NODE_OPTIONS is set into a VM env\n * block AFTER a spread of user-controlled secrets, to guarantee\n * the forced flags are always present in the final value even if\n * a zone secret happens to provide its own NODE_OPTIONS.\n *\n * Forced flags come FIRST so they are unambiguously applied.\n * User-provided flags are appended verbatim. Node treats NODE_OPTIONS\n * as a whitespace-separated list and all flags apply.\n *\n * Returns just the forced flags if the user value is undefined,\n * empty, or whitespace-only.\n *\n * Examples:\n *\n * composeNodeOptions(undefined)\n * ──► '--dns-result-order=ipv4first --no-network-family-autoselection'\n *\n * composeNodeOptions('')\n * ──► '--dns-result-order=ipv4first --no-network-family-autoselection'\n *\n * composeNodeOptions('--inspect=0.0.0.0:9229')\n * ──► '--dns-result-order=ipv4first --no-network-family-autoselection\n * --inspect=0.0.0.0:9229'\n */\nexport function composeNodeOptions(userValue: string | undefined): string {\n\tconst trimmed = userValue?.trim() ?? '';\n\tif (trimmed === '') {\n\t\treturn FORCE_IPV4_EGRESS_NODE_OPTIONS;\n\t}\n\treturn `${FORCE_IPV4_EGRESS_NODE_OPTIONS} ${trimmed}`;\n}\n","import type { MediatedSecretSpec } from '@agent-vm/secret-management';\n\nimport { targetsAudience, type RuntimeVmAudience } from './audience.js';\nimport type { GatewaySecretConfig, GatewayZoneConfig } from './gateway-lifecycle.js';\n\nexport interface SplitResolvedSecretsResult {\n\treadonly environmentSecrets: Record<string, string>;\n\treadonly mediatedSecrets: Record<string, MediatedSecretSpec>;\n}\n\nexport interface MergeRuntimeGatewaySecretsOptions {\n\treadonly logPrefix?: string;\n\treadonly runtimeEnvironment?: Readonly<Record<string, string>> | undefined;\n\treadonly runtimeMediatedSecrets?: Readonly<Record<string, MediatedSecretSpec>> | undefined;\n}\n\nexport type SecretInjectionConfig = GatewaySecretConfig;\n\nexport interface SplitResolvedSecretsOptions {\n\treadonly audience: RuntimeVmAudience;\n\treadonly logPrefix?: string;\n}\n\nexport function splitResolvedSecretsByInjection(\n\tsecretConfigs: Readonly<Record<string, SecretInjectionConfig>>,\n\tresolvedSecrets: Record<string, string>,\n\toptions: SplitResolvedSecretsOptions,\n): SplitResolvedSecretsResult {\n\tconst environmentSecrets: Record<string, string> = {};\n\tconst mediatedSecrets: Record<string, MediatedSecretSpec> = {};\n\tconst logPrefix = options.logPrefix ?? 'split-resolved-secrets';\n\n\tfor (const [secretName, secretValue] of Object.entries(resolvedSecrets)) {\n\t\tconst secretConfig = secretConfigs[secretName];\n\t\tif (!secretConfig) {\n\t\t\tthrow new Error(\n\t\t\t\t`[${logPrefix}] Secret '${secretName}' was resolved but has no matching secret config.`,\n\t\t\t);\n\t\t}\n\t\tif (!targetsAudience(secretConfig.audience, options.audience)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (secretConfig.injection === 'http-mediation') {\n\t\t\tif (secretConfig.hosts.length === 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[${logPrefix}] Secret '${secretName}' uses http-mediation but declares no hosts.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tmediatedSecrets[secretName] = {\n\t\t\t\thosts: [...secretConfig.hosts],\n\t\t\t\tvalue: secretValue,\n\t\t\t};\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst envSecretAudience = (secretConfig as { readonly audience: string }).audience;\n\t\tif (envSecretAudience !== 'gateway') {\n\t\t\tthrow new Error(\n\t\t\t\t`[${logPrefix}] Secret '${secretName}' uses env injection with non-gateway audience '${envSecretAudience}'.`,\n\t\t\t);\n\t\t}\n\t\tif (options.audience === 'gateway') {\n\t\t\tenvironmentSecrets[secretName] = secretValue;\n\t\t}\n\t}\n\n\treturn { environmentSecrets, mediatedSecrets };\n}\n\nexport type SplitResolvedGatewaySecretsResult = SplitResolvedSecretsResult;\n\nexport function splitResolvedGatewaySecrets(\n\tzone: GatewayZoneConfig,\n\tresolvedSecrets: Record<string, string>,\n): SplitResolvedGatewaySecretsResult {\n\treturn splitResolvedSecretsByInjection(zone.secrets, resolvedSecrets, {\n\t\taudience: 'gateway',\n\t\tlogPrefix: 'split-resolved-gateway-secrets',\n\t});\n}\n\nfunction assertNoRuntimeSecretCollision(\n\tsecretName: string,\n\ttarget: 'environment' | 'http-mediation',\n\tbaseSecrets: SplitResolvedSecretsResult,\n\truntimeSeen: Set<string>,\n\tlogPrefix: string,\n): void {\n\tif (runtimeSeen.has(secretName)) {\n\t\tthrow new Error(\n\t\t\t`[${logPrefix}] Runtime gateway secret '${secretName}' is declared for both environment and http-mediation injection.`,\n\t\t);\n\t}\n\tif (secretName in baseSecrets.environmentSecrets) {\n\t\tthrow new Error(\n\t\t\t`[${logPrefix}] Runtime gateway ${target} secret '${secretName}' would overwrite an authored environment secret.`,\n\t\t);\n\t}\n\tif (secretName in baseSecrets.mediatedSecrets) {\n\t\tthrow new Error(\n\t\t\t`[${logPrefix}] Runtime gateway ${target} secret '${secretName}' would overwrite an authored http-mediation secret.`,\n\t\t);\n\t}\n\truntimeSeen.add(secretName);\n}\n\nexport function mergeRuntimeGatewaySecrets(\n\tbaseSecrets: SplitResolvedSecretsResult,\n\toptions: MergeRuntimeGatewaySecretsOptions = {},\n): SplitResolvedSecretsResult {\n\tconst logPrefix = options.logPrefix ?? 'merge-runtime-gateway-secrets';\n\tconst runtimeSeen = new Set<string>();\n\tfor (const secretName of Object.keys(options.runtimeEnvironment ?? {})) {\n\t\tassertNoRuntimeSecretCollision(secretName, 'environment', baseSecrets, runtimeSeen, logPrefix);\n\t}\n\tfor (const secretName of Object.keys(options.runtimeMediatedSecrets ?? {})) {\n\t\tassertNoRuntimeSecretCollision(\n\t\t\tsecretName,\n\t\t\t'http-mediation',\n\t\t\tbaseSecrets,\n\t\t\truntimeSeen,\n\t\t\tlogPrefix,\n\t\t);\n\t}\n\n\treturn {\n\t\tenvironmentSecrets: {\n\t\t\t...baseSecrets.environmentSecrets,\n\t\t\t...options.runtimeEnvironment,\n\t\t},\n\t\tmediatedSecrets: {\n\t\t\t...baseSecrets.mediatedSecrets,\n\t\t\t...options.runtimeMediatedSecrets,\n\t\t},\n\t};\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-vm/gateway-interface",
3
- "version": "0.0.69",
3
+ "version": "0.0.71",
4
4
  "description": "Shared TypeScript interfaces for VM gateway lifecycles, VmSpec, and ProcessSpec.",
5
5
  "homepage": "https://github.com/ShravanSunder/agent-vm#readme",
6
6
  "bugs": {
@@ -29,7 +29,8 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@agent-vm/gondolin-adapter": "0.0.69"
32
+ "@agent-vm/secret-management": "0.0.71",
33
+ "@agent-vm/gondolin-adapter": "0.0.71"
33
34
  },
34
35
  "scripts": {
35
36
  "build": "tsdown",