diff --git a/packages/varlock/src/index.ts b/packages/varlock/src/index.ts index 27b7ae843..fcda3a7d3 100644 --- a/packages/varlock/src/index.ts +++ b/packages/varlock/src/index.ts @@ -2,6 +2,7 @@ import { checkForConfigErrors } from './cli/helpers/error-checks'; import { loadVarlockEnvGraph } from './lib/load-graph'; import { initVarlockEnv } from './runtime/env'; import { checkBunVersion } from './lib/check-bun-version'; +import { cleanupDaemonClient } from './lib/local-encrypt'; // Import env-graph components for internal API import { @@ -26,9 +27,24 @@ export async function load() { // loadFromSerializedGraph(envGraph.getSerializedGraph()); process.env.__VARLOCK_ENV = JSON.stringify(envGraph.getSerializedGraph()); initVarlockEnv(); + // Close daemon socket so the process can exit naturally after load() resolves. + // The socket is unref'd by default (see DaemonClient.connectToSocket), but + // explicitly closing it here is belt-and-suspenders for runtimes/environments + // where unref() may not be sufficient. + cleanupDaemonClient(); // TODO: return resolved env and schema / meta info } +/** + * Close the daemon client socket opened during `load()` or other operations + * that resolved `keychain(...)` values. This is called automatically by + * `load()`, but you can call it explicitly if you manage the connection + * lifecycle yourself. + */ +export function cleanup() { + cleanupDaemonClient(); +} + export function getBuildTimeReplacements(opts?: { objectKey?: string, diff --git a/packages/varlock/src/lib/local-encrypt/daemon-client.ts b/packages/varlock/src/lib/local-encrypt/daemon-client.ts index 78834d57e..e6131141a 100644 --- a/packages/varlock/src/lib/local-encrypt/daemon-client.ts +++ b/packages/varlock/src/lib/local-encrypt/daemon-client.ts @@ -441,6 +441,10 @@ export class DaemonClient { socket.on('connect', () => { clearTimeout(timeout); this.socket = socket; + // Unref the socket so it does not keep the process alive when idle. + // Pending sendMessage calls re-ref the socket via the timeout timer, + // so interactive/biometric operations are not affected. + this.socket.unref(); this.isConnected = true; this.buffer = Buffer.alloc(0); resolve(); diff --git a/packages/varlock/src/lib/local-encrypt/index.ts b/packages/varlock/src/lib/local-encrypt/index.ts index 5265af4ca..2e98dc2f1 100644 --- a/packages/varlock/src/lib/local-encrypt/index.ts +++ b/packages/varlock/src/lib/local-encrypt/index.ts @@ -286,6 +286,11 @@ export function getDaemonClient(): DaemonClient { return daemonClient; } +/** Close the daemon client socket if one was created. Safe to call multiple times. */ +export function cleanupDaemonClient(): void { + daemonClient?.cleanup(); +} + // ── Key management ───────────────────────────────────────────────────── /** Check if a key exists. */