Developer ID sign and notarize the standalone macOS CLI binaries - #1012
Open
philmillman wants to merge 1 commit into
Open
Developer ID sign and notarize the standalone macOS CLI binaries#1012philmillman wants to merge 1 commit into
philmillman wants to merge 1 commit into
Conversation
The `varlock` binary shipped in varlock-macos-{x64,arm64}.tar.gz was not
signed at all. Only VarlockEnclave.app was. That left the process that
actually holds resolved secrets with no hardened runtime, so any process
running as the same user could attach a debugger and read them out of its
memory.
Sign it with Developer ID, hardened runtime on, and no entitlement
exceptions granted. Bun's codesigning guide suggests granting five of
them; none are needed. Verified locally that the compiled binary runs
under `--options runtime` with an empty entitlement set, including the
path that spawns VarlockEnclave.app and talks to it over its unix
socket, and that `lldb -p` is refused as a result.
codesign and notarytool only exist on macOS, so the two macOS archives
now build on a macOS runner via a new reusable workflow while the other
five keep cross-compiling on linux. build-binaries.ts gained `--targets=`
for that split, `--sign` / `--no-sign`, and a shasum fallback so it can
generate checksums on macOS.
There is no stapling step: `xcrun stapler` only handles bundles, disk
images and installer packages, not bare Mach-O executables. Apple
publishes the ticket and Gatekeeper resolves it online.
Contributor
|
This run croaked 😵 The workflow encountered an error before any progress could be reported. Please check the link below for details. |
Contributor
|
The changes in this PR will be included in the next version bump.
|
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
varlock-website | 28b98a0 | Commit Preview URL Branch Preview URL |
Aug 17 2026, 07:04 PM |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What
The
varlockbinary shipped invarlock-macos-{x64,arm64}.tar.gzwas not signed at all (codesign -dvvvon an installed one reports "code object is not signed"). OnlyVarlockEnclave.appwas signed and notarized. This signs the CLI binary too, with Developer ID, hardened runtime enabled, and no entitlement exceptions granted.Why
Hardened runtime is the thing that stops another process running as the same user from attaching to varlock and reading resolved secret values out of its memory. Without it, the process that holds every secret varlock just fetched is the least protected artifact we ship.
Measured before/after with
lldb -pagainst a livevarlock run:lldb -presultProcess stopped— attachederror: attach failed (Not allowed to attach to process)Entitlements
Bun's codesigning guide suggests granting
allow-jit,allow-unsigned-executable-memory,disable-executable-page-protection,allow-dyld-environment-variables, anddisable-library-validation. None are needed. I verified the compiled binary runs under--options runtimewith an empty entitlement set across--version,--help,load,run -- node,explain,scan,cache status, andkeychain list— the last one being the important case, since it spawnsVarlockEnclave.appand talks to it over its unix socket. Release builds with--bytecodework too.varlock-cli.entitlementstherefore lists all seven exceptions as<false/>rather than omitting them, matching the convention inVarlockEnclave.entitlements, so a future edit that flips one shows up in a diff. Worth knowing: the plist cannot carry XML comments, codesign feeds it toAMFIUnserializeXMLwhich rejects them.CI shape
codesignandnotarytoolonly exist on macOS, but the other five targets cross-compile fine on linux and there was no reason to move them onto a macOS runner. So:build-cli-binaries-macos.yamlbuilds, signs, notarizes, and verifies the two macOS archives onmacos-latest. It reuses the existing Apple credentials from the same 1Password item the native-binary workflows use.release-binariesinrelease.yamlandbinary-release.yamlnow build with--targets=for the non-macOS five, download the macOS archives, and append their checksums.build-binaries.tsgained--targets=,--sign/--no-sign, and ashasum -a 256fallback so it can generate checksums when running on macOS.Verification in the macOS job asserts the hardened runtime flag is set, the authority is Developer ID, and no entitlement is
<true/>. It then extracts each archive and checks the signature survived tar, that no AppleDouble sidecars leaked in, and that the bundled.appkept its own signature and stapled ticket. The arm64 slice is smoke tested from the extracted archive.Notes and tradeoffs
xcrun stapleronly handles bundles, disk images, and installer packages, not bare Mach-O executables. Apple publishes the ticket and Gatekeeper resolves it online, which is the normal arrangement for a signed CLI in a tarball.binary-release.yamlnow needs a macOS runner and Apple credentials. Its header comment previously stated it needed neither. The signed helpers are still pulled from npm; it is the CLI binary itself that has to be rebuilt and re-signed, and that cannot be recovered from the published package. Comment updated.--no-signopts out, which you need if you want to attach a debugger to the compiled binary.binary-preview.yaml) stay unsigned. They build all seven targets on linux, wheresignMacBinaryno-ops with a log line. Worth doing separately if PR preview archives should be Gatekeeper-clean.--options runtimeis applied to thevarlockMach-O. No--deep, deliberately: re-signing the bundledVarlockEnclave.appwould invalidate its stapled ticket.Related
While measuring this I found that official Node.js ships
get-task-allow, sonodeprocesses are attachable by any same-user process despite having the hardened runtime flag set. That caps what any node-based install can promise and is called out in the docs added here.