@themoltnet/sandbox-gondolin 0.1.0 → 0.2.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/README.md CHANGED
@@ -20,7 +20,7 @@ implementation can be added beside it without going through Pi.
20
20
  (`assertGuestEnvironmentBoundary`).
21
21
  - `VmConfig.brokeredSecrets` — host-brokered secrets: the guest sees a
22
22
  placeholder, Gondolin's `SecretManager` substitutes the value only in
23
- requests to the declared host patterns.
23
+ requests to the declared protocol, host patterns, and ports.
24
24
  - `ensureSnapshot` — build and cache the base checkpoint.
25
25
  - Small helpers: `findMainWorktree`, `isResolvedPathInsideRoot`,
26
26
  `abortableResource` / `throwIfAborted`.
@@ -36,11 +36,76 @@ implementation can be added beside it without going through Pi.
36
36
 
37
37
  ## Known boundary facts
38
38
 
39
- - Egress policy is hostname-granular: no scheme or port narrowing.
39
+ - Network egress policy is hostname-granular. Brokered credentials add a
40
+ separately attested protocol and port boundary.
40
41
  - Guest `127.0.0.1` / `localhost` never leave the VM; names are resolved on
41
42
  the host side by the proxy.
42
43
  - Exec abort only drops the host session; a caller that needs the guest
43
44
  process gone must kill it in the guest.
44
45
 
46
+ ## Brokered HTTP secrets
47
+
48
+ `brokeredSecrets` is trusted-host input to `resumeVm`; it is deliberately not
49
+ part of `SandboxConfig`. A remotely stored runtime profile may constrain
50
+ network access and refer to a logical credential requirement, but it must not
51
+ contain a value or choose a host secret-provider coordinate.
52
+
53
+ ```ts
54
+ const managed = await resumeVm({
55
+ checkpointPath,
56
+ agentName: 'worker',
57
+ guestCredentialMode: 'host-authenticated',
58
+ mountPath: workspace,
59
+ sandboxConfig: {
60
+ network: { allowedHosts: ['api.example.com'] },
61
+ },
62
+ brokeredSecrets: [
63
+ {
64
+ id: 'example-api',
65
+ guestEnv: 'EXAMPLE_API_TOKEN',
66
+ hosts: ['api.example.com'],
67
+ value: await trustedHostSecretProvider.get('example-api'),
68
+ },
69
+ ],
70
+ });
71
+ ```
72
+
73
+ Inside the VM, `$EXAMPLE_API_TOKEN` is a random Gondolin placeholder. A normal
74
+ guest command can use it in an HTTP header:
75
+
76
+ ```bash
77
+ curl -fsS \
78
+ -H "Authorization: Bearer $EXAMPLE_API_TOKEN" \
79
+ https://api.example.com/v1/items
80
+ ```
81
+
82
+ The host proxy substitutes the real value only for the declared origin. HTTPS
83
+ on port 443 is the default. Plain HTTP requires an explicit `protocol: 'http'`
84
+ and should be limited to an exact port for a controlled local fixture.
85
+ Preflight fails before VM resume when the binding is missing, an environment
86
+ name collides with another guest source, or a credential host is outside the
87
+ effective network policy. Values are not substituted in request bodies or URL
88
+ queries. Gondolin also decodes, substitutes, and re-encodes HTTP Basic
89
+ authorization, covering the password encoding used by HTTPS Git credential
90
+ helpers; OAuth client secrets sent in form bodies remain host-only.
91
+
92
+ Rotation and revocation do not require exposing or changing the guest
93
+ placeholder:
94
+
95
+ ```ts
96
+ managed.secretManager.rotateSecret('EXAMPLE_API_TOKEN', rotated);
97
+ managed.secretManager.revokeSecret('EXAMPLE_API_TOKEN');
98
+ ```
99
+
100
+ Keep signing keys, GitHub App private keys, SSH keys, and other non-HTTP
101
+ credentials out of this channel. They require narrow host capabilities rather
102
+ than bearer-secret substitution.
103
+
104
+ The portable boundary is the sequence _requirement → trusted local binding →
105
+ resolved delivery_. A Docker sandbox can implement the same sequence with its
106
+ native secret broker; hostname rewriting, placeholder lifecycle, and other
107
+ provider behavior remain adapter-specific, while the attested protocol, host,
108
+ and port boundary must remain equivalent.
109
+
45
110
  Live tests (`vm-manager.integration.test.ts`) boot real VMs and run only with
46
111
  `MOLTNET_PI_VM_INTEGRATION=1`; CI runs them in the agent-daemon Core lane.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { MemoryProvider } from '@earendil-works/gondolin';
2
+ import { SecretDefinition } from '@earendil-works/gondolin';
2
3
  import { VirtualFileHandle } from '@earendil-works/gondolin';
3
4
  import { VM } from '@earendil-works/gondolin';
4
5
 
@@ -38,6 +39,52 @@ export declare class AutoParentMemoryProvider extends MemoryProvider {
38
39
  openSync(pathname: string, flags: string, mode?: number): VirtualFileHandle;
39
40
  }
40
41
 
42
+ /** Per-attempt host binding. The value must never be persisted or evidenced. */
43
+ export declare interface BrokeredHttpSecretBinding extends BrokeredHttpSecretDescriptor {
44
+ /** Resolved only by trusted host code immediately before VM resume. */
45
+ value?: string;
46
+ }
47
+
48
+ export declare class BrokeredHttpSecretBoundaryError extends Error {
49
+ readonly issues: readonly string[];
50
+ constructor(issues: readonly string[]);
51
+ }
52
+
53
+ /**
54
+ * Value-free declaration of one HTTP credential delivered by the Gondolin
55
+ * host proxy. This is an adapter contract, not a persisted runtime-profile
56
+ * schema: trusted host code resolves the matching value for each attempt.
57
+ */
58
+ export declare interface BrokeredHttpSecretDescriptor {
59
+ /** Stable, evidence-safe logical requirement id. */
60
+ id: string;
61
+ /** Guest variable that receives an opaque Gondolin placeholder. */
62
+ guestEnv: string;
63
+ /** Hostname patterns to which the proxy may send the resolved value. */
64
+ hosts: readonly string[];
65
+ /** Attested upstream transport. Defaults to HTTPS. */
66
+ protocol?: 'https' | 'http';
67
+ /** Attested upstream ports. Defaults to 443 for HTTPS and 80 for HTTP. */
68
+ ports?: readonly number[];
69
+ /** Missing values fail preflight unless explicitly optional. */
70
+ required?: boolean;
71
+ }
72
+
73
+ /** Host-only capabilities that cannot widen a validated destination grant. */
74
+ export declare interface BrokeredHttpSecretManager {
75
+ /** Rotate a configured secret value without changing its destination set. */
76
+ rotateSecret(guestEnv: string, value: string): void;
77
+ /** Revoke a configured secret for the remainder of this VM lifetime. */
78
+ revokeSecret(guestEnv: string): void;
79
+ }
80
+
81
+ /**
82
+ * Canonicalize and validate the value-free portion of a brokered binding.
83
+ * Runtime attestation and Gondolin enforcement both consume this function so
84
+ * the evidenced destination set cannot drift from the enforced one.
85
+ */
86
+ export declare function canonicalizeBrokeredHttpSecretDescriptor(descriptor: BrokeredHttpSecretDescriptor): Required<BrokeredHttpSecretDescriptor>;
87
+
41
88
  export declare function delay(ms: number, signal: AbortSignal | undefined, label: string): Promise<void>;
42
89
 
43
90
  /**
@@ -109,11 +156,23 @@ export declare function loadCredentials(agentDir: string, mode?: GuestCredential
109
156
  export declare interface ManagedVm {
110
157
  vm: VM;
111
158
  credentials: VmCredentials;
159
+ /** Host-only rotation and revocation handle. It cannot widen destinations. */
160
+ secretManager: BrokeredHttpSecretManager;
112
161
  mountPath: string;
113
162
  guestWorkspace: string;
114
163
  agentDir: string;
115
164
  }
116
165
 
166
+ /**
167
+ * Validate value-free descriptors, host-local bindings, environment
168
+ * collisions, and destination coverage before Gondolin creates any VM.
169
+ */
170
+ export declare function prepareBrokeredHttpSecrets(options: {
171
+ bindings?: readonly BrokeredHttpSecretBinding[];
172
+ allowedHosts: readonly string[];
173
+ occupiedGuestEnvNames?: readonly string[];
174
+ }): Record<string, SecretDefinition>;
175
+
117
176
  export declare interface ProviderAuthSource {
118
177
  /** Read the auth blob from the host, or return null when absent. */
119
178
  load(): string | null;
@@ -318,6 +377,12 @@ export declare interface VmConfig {
318
377
  * into the guest without storing secret values in the profile.
319
378
  */
320
379
  forwardEnv?: string[];
380
+ /**
381
+ * Trusted-host, per-attempt HTTP secret bindings. These are deliberately
382
+ * outside SandboxConfig so remotely stored runtime profiles cannot carry
383
+ * raw values or select host secret-provider coordinates.
384
+ */
385
+ brokeredSecrets?: readonly BrokeredHttpSecretBinding[];
321
386
  /** Structured credential-boundary diagnostics for daemon loggers. */
322
387
  onDiagnostic?: (diagnostic: VmDiagnostic) => void;
323
388
  /** Abort resume/setup work, closing any live VM owned by resumeVm. */
@@ -355,10 +420,12 @@ export declare interface VmCredentials {
355
420
  }
356
421
 
357
422
  export declare interface VmDiagnostic {
358
- event: 'vm.credentials.mode' | 'vm.credentials.github_key_missing';
423
+ event: 'vm.credentials.mode' | 'vm.credentials.github_key_missing' | 'vm.http_secrets.bound';
359
424
  level: 'info' | 'warning';
360
425
  message: string;
361
426
  credentialMode: GuestCredentialMode;
427
+ /** Present only for the value-free broker summary event. */
428
+ brokeredSecretCount?: number;
362
429
  }
363
430
 
364
431
  export { }
package/dist/index.js CHANGED
@@ -529,6 +529,174 @@ var GuestEnvironmentBoundaryError = class extends Error {
529
529
  this.name = "GuestEnvironmentBoundaryError";
530
530
  }
531
531
  };
532
+ var BrokeredHttpSecretBoundaryError = class extends Error {
533
+ constructor(issues) {
534
+ super("Brokered HTTP secret boundary refused the resolved bindings: " + issues.join("; "));
535
+ this.issues = issues;
536
+ this.name = "BrokeredHttpSecretBoundaryError";
537
+ }
538
+ };
539
+ var BROKERED_SECRET_ID_REGEXP = /^[a-z][a-z0-9._-]{0,63}$/;
540
+ var GUEST_ENV_NAME_REGEXP = /^[A-Za-z_][A-Za-z0-9_]*$/;
541
+ var OBJECT_META_PROPERTY_NAMES = new Set([
542
+ "__proto__",
543
+ "constructor",
544
+ "prototype"
545
+ ]);
546
+ function normalizeHostPattern(pattern) {
547
+ return pattern.trim().toLowerCase();
548
+ }
549
+ function isValidHostPattern(pattern) {
550
+ return pattern !== "" && !pattern.includes("://") && !pattern.includes("/") && !pattern.includes(":") && !/\s/.test(pattern);
551
+ }
552
+ /**
553
+ * Canonicalize and validate the value-free portion of a brokered binding.
554
+ * Runtime attestation and Gondolin enforcement both consume this function so
555
+ * the evidenced destination set cannot drift from the enforced one.
556
+ */
557
+ function canonicalizeBrokeredHttpSecretDescriptor(descriptor) {
558
+ const issues = [];
559
+ const id = descriptor.id.trim();
560
+ const guestEnv = descriptor.guestEnv.trim();
561
+ const hosts = [...new Set(descriptor.hosts.map(normalizeHostPattern))].sort();
562
+ const protocolInput = descriptor.protocol ?? "https";
563
+ const protocol = protocolInput === "http" ? "http" : "https";
564
+ const ports = [...new Set(descriptor.ports ?? [protocol === "https" ? 443 : 80])].sort((left, right) => left - right);
565
+ if (!BROKERED_SECRET_ID_REGEXP.test(id)) issues.push(`invalid requirement id "${id || "<empty>"}"`);
566
+ if (!GUEST_ENV_NAME_REGEXP.test(guestEnv)) issues.push(`requirement "${id}" has invalid guest env "${guestEnv}"`);
567
+ else if (isReservedGuestEnvironmentName(guestEnv) || OBJECT_META_PROPERTY_NAMES.has(guestEnv)) issues.push(`requirement "${id}" uses reserved guest env "${guestEnv}"`);
568
+ if (hosts.length === 0) issues.push(`requirement "${id}" has no destination hosts`);
569
+ for (const host of hosts) if (!isValidHostPattern(host)) issues.push(`requirement "${id}" has invalid host pattern "${host}"`);
570
+ if (protocolInput !== "https" && protocolInput !== "http") issues.push(`requirement "${id}" has invalid protocol "${String(protocolInput)}"`);
571
+ if (ports.length === 0) issues.push(`requirement "${id}" has no destination ports`);
572
+ for (const port of ports) if (!Number.isInteger(port) || port < 1 || port > 65535) issues.push(`requirement "${id}" has invalid port "${port}"`);
573
+ if (issues.length > 0) throw new BrokeredHttpSecretBoundaryError(issues);
574
+ return Object.freeze({
575
+ id,
576
+ guestEnv,
577
+ hosts: Object.freeze(hosts),
578
+ protocol,
579
+ ports: Object.freeze(ports),
580
+ required: descriptor.required !== false
581
+ });
582
+ }
583
+ function hostMatchesPattern(hostname, pattern) {
584
+ const expression = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*");
585
+ return new RegExp(`^${expression}$`, "i").test(hostname);
586
+ }
587
+ /**
588
+ * Conservative subset check for Gondolin hostname globs. Exact secret hosts
589
+ * may sit below a broader network wildcard. A wildcard secret destination must
590
+ * be repeated exactly (or covered by `*`) so a credential grant cannot widen
591
+ * through an ambiguous glob comparison.
592
+ */
593
+ function networkPatternCoversSecretPattern(networkPattern, secretPattern) {
594
+ if (networkPattern === "*") return true;
595
+ if (secretPattern.includes("*")) return networkPattern === secretPattern;
596
+ return hostMatchesPattern(secretPattern, networkPattern);
597
+ }
598
+ /**
599
+ * Validate value-free descriptors, host-local bindings, environment
600
+ * collisions, and destination coverage before Gondolin creates any VM.
601
+ */
602
+ function prepareBrokeredHttpSecrets(options) {
603
+ const issues = [];
604
+ const ids = /* @__PURE__ */ new Set();
605
+ const guestEnvNames = /* @__PURE__ */ new Set();
606
+ const occupiedGuestEnvNames = new Set(options.occupiedGuestEnvNames ?? []);
607
+ const allowedHosts = [...new Set(options.allowedHosts.map(normalizeHostPattern))];
608
+ const secrets = Object.create(null);
609
+ for (const binding of options.bindings ?? []) {
610
+ let descriptor;
611
+ try {
612
+ descriptor = canonicalizeBrokeredHttpSecretDescriptor(binding);
613
+ } catch (error) {
614
+ if (error instanceof BrokeredHttpSecretBoundaryError) {
615
+ issues.push(...error.issues);
616
+ continue;
617
+ }
618
+ throw error;
619
+ }
620
+ const { id, guestEnv, hosts } = descriptor;
621
+ if (ids.has(id)) issues.push(`duplicate requirement id "${id}"`);
622
+ ids.add(id);
623
+ if (guestEnvNames.has(guestEnv)) issues.push(`duplicate guest env "${guestEnv}"`);
624
+ else if (occupiedGuestEnvNames.has(guestEnv)) issues.push(`guest env "${guestEnv}" is already supplied by another source`);
625
+ guestEnvNames.add(guestEnv);
626
+ for (const host of hosts) if (!allowedHosts.some((allowedHost) => networkPatternCoversSecretPattern(allowedHost, host))) issues.push(`requirement "${id}" host "${host}" is outside the effective network policy`);
627
+ const value = binding.value;
628
+ if (value === void 0 || value === "") {
629
+ if (descriptor.required) issues.push(`required binding "${id}" has no resolved value`);
630
+ continue;
631
+ }
632
+ secrets[guestEnv] = {
633
+ hosts: [...hosts],
634
+ value
635
+ };
636
+ }
637
+ if (issues.length > 0) throw new BrokeredHttpSecretBoundaryError(issues);
638
+ return secrets;
639
+ }
640
+ function decodeBasicAuthorization(value) {
641
+ const match = /^Basic\s+([^\s]+)$/i.exec(value);
642
+ if (!match) return void 0;
643
+ try {
644
+ return Buffer.from(match[1], "base64").toString("utf8");
645
+ } catch {
646
+ return;
647
+ }
648
+ }
649
+ function headersContainSecretValue(headers, value) {
650
+ if (value === "") return false;
651
+ for (const [name, headerValue] of headers.entries()) {
652
+ if (headerValue.includes(value)) return true;
653
+ if (/^(authorization|proxy-authorization)$/i.test(name) && decodeBasicAuthorization(headerValue)?.includes(value)) return true;
654
+ }
655
+ return false;
656
+ }
657
+ function createBrokeredHttpSecretOriginPolicy(bindings) {
658
+ const entries = /* @__PURE__ */ new Map();
659
+ for (const binding of bindings) {
660
+ if (binding.value === void 0 || binding.value === "") continue;
661
+ const descriptor = canonicalizeBrokeredHttpSecretDescriptor(binding);
662
+ entries.set(descriptor.guestEnv, {
663
+ guestEnv: descriptor.guestEnv,
664
+ hosts: descriptor.hosts,
665
+ protocol: descriptor.protocol,
666
+ ports: descriptor.ports,
667
+ value: binding.value,
668
+ deleted: false
669
+ });
670
+ }
671
+ return {
672
+ isRequestAllowed(request) {
673
+ let url;
674
+ try {
675
+ url = new URL(request.url);
676
+ } catch {
677
+ return false;
678
+ }
679
+ const protocol = url.protocol.slice(0, -1);
680
+ const port = url.port ? Number(url.port) : protocol === "https" ? 443 : 80;
681
+ for (const entry of entries.values()) {
682
+ if (entry.deleted || !headersContainSecretValue(request.headers, entry.value)) continue;
683
+ if (protocol !== entry.protocol || !entry.ports.includes(port) || !entry.hosts.some((host) => hostMatchesPattern(url.hostname, host))) return false;
684
+ }
685
+ return true;
686
+ },
687
+ rotateSecret(guestEnv, value) {
688
+ const entry = entries.get(guestEnv);
689
+ if (!entry) throw new Error(`unknown brokered secret: ${guestEnv}`);
690
+ if (entry.deleted) throw new Error(`brokered secret revoked: ${guestEnv}`);
691
+ entry.value = value;
692
+ },
693
+ revokeSecret(guestEnv) {
694
+ const entry = entries.get(guestEnv);
695
+ if (!entry) throw new Error(`unknown brokered secret: ${guestEnv}`);
696
+ entry.deleted = true;
697
+ }
698
+ };
699
+ }
532
700
  function assertGuestEnvironmentBoundary(options) {
533
701
  const refusedForwardEnv = (options.forwardEnv ?? []).filter((name) => isReservedGuestEnvironmentName(name) || options.guestCredentialMode === "host-authenticated" && !HOST_AUTHENTICATED_GUEST_ENV_ALLOWLIST.has(name));
534
702
  const refusedSandboxEnv = Object.keys(options.sandboxEnv ?? {}).filter(isReservedGuestEnvironmentName);
@@ -637,9 +805,47 @@ async function resumeVm(config) {
637
805
  ...config.extraAllowedHosts ?? []
638
806
  ])];
639
807
  assertInternalHostsDoNotOverlapProtectedHosts(runtimeAllowedInternalHosts, protectedExternalHosts);
640
- const { httpHooks, env: secretEnv } = createHttpHooks({
641
- allowedHosts: [...new Set([...protectedExternalHosts, ...runtimeAllowedHosts])],
642
- allowedInternalHosts: runtimeAllowedInternalHosts
808
+ const allowedHosts = [...new Set([...protectedExternalHosts, ...runtimeAllowedHosts])];
809
+ const brokeredSecrets = prepareBrokeredHttpSecrets({
810
+ bindings: config.brokeredSecrets,
811
+ allowedHosts: [...allowedHosts, ...runtimeAllowedInternalHosts],
812
+ occupiedGuestEnvNames: [
813
+ "PATH",
814
+ "HOME",
815
+ "NODE_NO_WARNINGS",
816
+ "NODE_EXTRA_CA_CERTS",
817
+ "MOLTNET_GUEST_WORKSPACE",
818
+ ...config.forwardEnv ?? [],
819
+ ...Object.keys(creds.agentEnv),
820
+ ...Object.keys(config.sandboxConfig?.env ?? {})
821
+ ]
822
+ });
823
+ const brokeredSecretOriginPolicy = createBrokeredHttpSecretOriginPolicy(config.brokeredSecrets ?? []);
824
+ const { httpHooks, env: secretEnv, secretManager: gondolinSecretManager } = createHttpHooks({
825
+ allowedHosts,
826
+ allowedInternalHosts: runtimeAllowedInternalHosts,
827
+ ...Object.keys(brokeredSecrets).length > 0 && {
828
+ secrets: brokeredSecrets,
829
+ isRequestAllowed: brokeredSecretOriginPolicy.isRequestAllowed
830
+ }
831
+ });
832
+ const secretManager = Object.freeze({
833
+ rotateSecret(guestEnv, value) {
834
+ gondolinSecretManager.updateSecret(guestEnv, { value });
835
+ brokeredSecretOriginPolicy.rotateSecret(guestEnv, value);
836
+ },
837
+ revokeSecret(guestEnv) {
838
+ gondolinSecretManager.deleteSecret(guestEnv);
839
+ brokeredSecretOriginPolicy.revokeSecret(guestEnv);
840
+ }
841
+ });
842
+ const brokeredSecretCount = Object.keys(brokeredSecrets).length;
843
+ if (brokeredSecretCount > 0) config.onDiagnostic?.({
844
+ event: "vm.http_secrets.bound",
845
+ level: "info",
846
+ credentialMode: guestCredentialMode,
847
+ brokeredSecretCount,
848
+ message: `Bound ${brokeredSecretCount} HTTP secret placeholder${brokeredSecretCount === 1 ? "" : "s"} to the host proxy`
643
849
  });
644
850
  const vmAgentDir = `/home/agent/.moltnet/${config.agentName}`;
645
851
  const vmAgentEnv = {};
@@ -678,7 +884,6 @@ async function resumeVm(config) {
678
884
  }
679
885
  const envOverrides = config.sandboxConfig?.env ?? {};
680
886
  const vmEnv = {
681
- ...secretEnv,
682
887
  ...vmAgentEnv,
683
888
  ...forwardedEnv,
684
889
  PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/go/bin",
@@ -686,7 +891,8 @@ async function resumeVm(config) {
686
891
  NODE_NO_WARNINGS: "1",
687
892
  NODE_EXTRA_CA_CERTS: "/etc/ssl/certs/ca-certificates.crt",
688
893
  ...envOverrides,
689
- MOLTNET_GUEST_WORKSPACE: guestWorkspace
894
+ MOLTNET_GUEST_WORKSPACE: guestWorkspace,
895
+ ...secretEnv
690
896
  };
691
897
  const resources = config.sandboxConfig?.resources;
692
898
  const workspaceMode = config.workspaceMode ?? "shared_mount";
@@ -788,6 +994,7 @@ async function resumeVm(config) {
788
994
  return {
789
995
  vm,
790
996
  credentials: creds,
997
+ secretManager,
791
998
  mountPath: config.mountPath,
792
999
  guestWorkspace,
793
1000
  agentDir
@@ -864,4 +1071,4 @@ function rewriteMoltnetJsonPaths(moltnetJson, vmAgentDir, vmSshDir, githubAppPem
864
1071
  return JSON.stringify(config);
865
1072
  }
866
1073
  //#endregion
867
- export { AutoParentMemoryProvider, GONDOLIN_BASE_EXECUTABLES, GUEST_TASK_CONTEXT_MOUNT, GUEST_TASK_SKILLS_MOUNT, GuestEnvironmentBoundaryError, abortableResource, activateAgentEnv, assertGuestEnvironmentBoundary, assertHostAuthenticatedGuestEnvironment, delay, ensureSnapshot, findMainWorktree, isResolvedPathInsideRoot, loadCredentials, resolveVfsShadowConfig, resolveVmAgentDir, resumeVm, rewriteGitconfigPaths, rewriteMoltnetJsonPaths, shouldRunResumeCommand, shouldShadowNodeModulesPath, throwIfAborted };
1074
+ export { AutoParentMemoryProvider, BrokeredHttpSecretBoundaryError, GONDOLIN_BASE_EXECUTABLES, GUEST_TASK_CONTEXT_MOUNT, GUEST_TASK_SKILLS_MOUNT, GuestEnvironmentBoundaryError, abortableResource, activateAgentEnv, assertGuestEnvironmentBoundary, assertHostAuthenticatedGuestEnvironment, canonicalizeBrokeredHttpSecretDescriptor, delay, ensureSnapshot, findMainWorktree, isResolvedPathInsideRoot, loadCredentials, prepareBrokeredHttpSecrets, resolveVfsShadowConfig, resolveVmAgentDir, resumeVm, rewriteGitconfigPaths, rewriteMoltnetJsonPaths, shouldRunResumeCommand, shouldShadowNodeModulesPath, throwIfAborted };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themoltnet/sandbox-gondolin",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Gondolin microVM sandbox lifecycle for MoltNet runtimes: checkpoint resume, egress policy, VFS shadowing, guest credential boundary, host-brokered secrets",
5
5
  "keywords": [
6
6
  "moltnet",