Skip to content

Start the native helper from its own directory - #1169

Merged
theoephraim merged 2 commits into
dmno-dev:mainfrom
JayOfTheKeyboard:fix-native-helper-spawn-cwd
Oct 1, 2026
Merged

theoephraim merged 2 commits into
dmno-dev:mainfrom
JayOfTheKeyboard:fix-native-helper-spawn-cwd

Conversation

@JayOfTheKeyboard

@JayOfTheKeyboard JayOfTheKeyboard commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

The standalone binary fails to start its native helper when varlock runs from a directory the current user cannot enter:

EACCES: permission denied, posix_spawn '/usr/local/bin/varlock-local-encrypt'

I hit this on a production host. A secret-rotation script ran runuser -u <service-user> -- varlock cache clear --yes for each service identity. runuser keeps the caller's cwd, here a home directory with mode 750, so the clear failed for all six identities. With cacheTtl=forever, the services kept the old credential until the cache files were deleted by hand. Reproduced in a clean container, the same command exits 1 with the error above.

The cause is in Bun 1.3.14, which the binary is built with (packageManager: [email protected]). Its child_process spawn fails with EACCES when the parent's cwd cannot be entered, even for /bin/true. Bun 1.4.2 and Node both spawn fine from the same cwd.

Change

getNativeBinarySpawnOptions() in binary-resolver.ts starts the helper from its own directory, which anyone who can run it can enter. It is used for every spawn of the helper (runNativeBinary, spawnNativeBinaryAsync, decrypt via the daemon, the detached daemon) and for the two WSL tools that start the Windows daemon (wslpath, powershell.exe). The daemon also stops holding the caller's directory open for its lifetime.

A relative XDG_CONFIG_HOME was the only thing tying the helper to the caller's cwd. It is now resolved to an absolute path, both in getUserVarlockDir() (which builds the daemon's socket and pid paths) and in the env passed to the helper, so keys and the daemon socket stay where they were. An empty XDG_CONFIG_HOME is dropped from the helper's env, since getUserVarlockDir() treats it as unset and the helpers would otherwise read it as a relative path.

This only covers the helper. Other spawns from the binary, including the child of varlock run, still use the caller's cwd, which is correct for them, but they hit the same Bun bug from a cwd that cannot be entered. Upgrading Bun would fix those too.

Test

  • Unit: getNativeBinarySpawnOptions returns the helper's directory and resolves a relative XDG_CONFIG_HOME; getUserVarlockDir returns an absolute path for one.
  • Smoke (binary.test.ts): enters a temp directory, removes access to it (chmod 000 .), runs cache clear with VARLOCK_DEBUG=1 and a scratch XDG_CONFIG_HOME, and checks that the helper was spawned and did not fail. On main it fails with the EACCES above. It needs the helper next to the binary, and the smoke-test workflow builds with --skip-native, so there it is skipped rather than passing without spawning anything. Skipped on Windows and as root.

I ran the smoke test locally against a binary built with Bun 1.3.14 with the Rust helper bundled, and also by hand in a Debian container as a service user via runuser from a mode 750 directory. cache clear now exits 0.

JayOfTheKeyboard added a commit to JayOfTheKeyboard/JayOfTheKeyboard that referenced this pull request Sep 30, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Handle an empty XDG_CONFIG_HOME before changing the helper cwd, or the Rust helper can select the install directory as its key store.

Reviewed changes in commit 675409cd, covering the native-helper spawn options, every production helper and daemon launch path, user config path normalization, unit coverage, and the locked-cwd binary smoke test.

  • Helper working directory: Native helper processes and detached daemons now start from the resolved helper directory so Bun can spawn them when the caller cwd is inaccessible.
  • Config path preservation: Relative XDG_CONFIG_HOME values are made absolute for both JS path consumers and child-process environments.
  • WSL launch paths: Helper probes, wslpath, and PowerShell daemon startup receive the same cwd and environment handling.
  • Regression coverage: Unit tests pin spawn options and config resolution, while a conditional binary smoke test exercises an inaccessible cwd with a bundled helper.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using azure/gpt-5.6-sol | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes since the prior Pullfrog review, focused on the empty XDG_CONFIG_HOME correction and its regression coverage.

  • Normalized empty config paths: Dropped an empty XDG_CONFIG_HOME from native-helper child environments so the helper uses the same default config location as getUserVarlockDir(), with a focused unit test covering the behavior.

Pullfrog  | View workflow run | Using azure/gpt-5.6-sol | 𝕏

Bun's spawn fails with EACCES when the caller's cwd cannot be entered, which happens when varlock runs as a service user via runuser from a private home directory. Start the helper, and the WSL tools that start its daemon, from the helper's directory. A relative XDG_CONFIG_HOME is the only thing that tied the helper to the caller's cwd, so it is now resolved to an absolute path: in getUserVarlockDir, which builds the daemon's socket and pid paths, and in the env passed to the helper.
getUserVarlockDir treats an empty value as unset, but the Rust helper read it as the relative path "varlock", which now resolved under the helper's install directory.
@theoephraim
theoephraim force-pushed the fix-native-helper-spawn-cwd branch from 062c8b7 to ea3ff2e Compare October 1, 2026 19:27
@pkg-pr-new

pkg-pr-new Bot commented Oct 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@varlock/ci-env-info

npm i https://pkg.pr.new/@varlock/ci-env-info@1169

varlock

npm i https://pkg.pr.new/varlock@1169

@varlock/native-helper-darwin

npm i https://pkg.pr.new/@varlock/native-helper-darwin@1169

@varlock/native-helper-linux-arm64

npm i https://pkg.pr.new/@varlock/native-helper-linux-arm64@1169

@varlock/native-helper-linux-x64

npm i https://pkg.pr.new/@varlock/native-helper-linux-x64@1169

@varlock/native-helper-win32-x64

npm i https://pkg.pr.new/@varlock/native-helper-win32-x64@1169

@varlock/google-secret-manager-plugin

npm i https://pkg.pr.new/@varlock/google-secret-manager-plugin@1169

commit: ea3ff2e

@theoephraim
theoephraim merged commit 64f0fdb into dmno-dev:main Oct 1, 2026
27 checks passed
@JayOfTheKeyboard
JayOfTheKeyboard deleted the fix-native-helper-spawn-cwd branch November 7, 2026 07:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants