Skip to content

Commit b4e128c

Browse files
committed
feat(install): re-sign with stable cert → TCC permissions persist
Adhoc-signed bundles use the Mach-O hash as the signing identity, so every rebuild looks like a brand-new app to macOS TCC. Files-and-Folders and Full Disk Access prompts return on every install, and once-granted permissions reset. install.hexa now re-signs the built bundle with a stable keychain identity (Developer ID Application preferred, falls back to Apple Development) before copying to /Applications. TCC keys to the cert chain + TeamID instead of the binary hash, so granted permissions carry across rebuilds. Falls back silently to ad-hoc + a one-time hint if no signing identity is found, so fresh-clone installs still succeed. Override via VOID_SIGN_IDENTITY=<sha1> or VOID_SIGN_SKIP=1.
1 parent 27c80f9 commit b4e128c

1 file changed

Lines changed: 42 additions & 0 deletions

File tree

install.hexa

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,48 @@ fn main() {
8686
}
8787
println("⬡ void: built " + built)
8888

89+
// Re-sign with a stable codesign identity if available. The xcodebuild
90+
// ReleaseLocal config produces an ad-hoc signed bundle whose Mach-O hash
91+
// changes every build → macOS TCC treats each rebuild as a new app and
92+
// re-prompts for Files-and-Folders / Full Disk Access. Re-signing with a
93+
// user keychain identity (Developer ID / Apple Development) keys TCC to
94+
// the cert chain instead, so granted permissions persist across rebuilds.
95+
//
96+
// Falls back silently to the original ad-hoc signature if no identity is
97+
// found — install still works, but TCC prompts will keep returning.
98+
//
99+
// Override with VOID_SIGN_IDENTITY=<sha1>, or VOID_SIGN_SKIP=1 to skip.
100+
let entitlements = pkg_dir + "/macos/VoidReleaseLocal.entitlements"
101+
if env("VOID_SIGN_SKIP") != "1" {
102+
let override_id = env("VOID_SIGN_IDENTITY")
103+
let identity = if override_id != "" { override_id } else {
104+
// Pick first "Developer ID Application" (preferred — distribution),
105+
// else first "Apple Development" (paid dev account).
106+
let devid = to_string(exec(
107+
"security find-identity -v -p codesigning 2>/dev/null | grep -m1 'Developer ID Application' | awk '{print $2}'"
108+
)).trim()
109+
if devid != "" { devid } else {
110+
to_string(exec(
111+
"security find-identity -v -p codesigning 2>/dev/null | grep -m1 'Apple Development' | awk '{print $2}'"
112+
)).trim()
113+
}
114+
}
115+
if identity != "" {
116+
println("⬡ void: re-sign with " + identity + " (TCC permission persistence)")
117+
run_or_die(
118+
"codesign --force --deep --options runtime " +
119+
"--sign " + identity + " " +
120+
"--entitlements '" + entitlements + "' " +
121+
"'" + built + "' 2>&1 | tail -5"
122+
)
123+
} else {
124+
println("⬡ void: no stable codesign identity found — keeping ad-hoc signature")
125+
println(" TCC permissions will reset on each rebuild. To fix:")
126+
println(" 1) Get an Apple Development cert (Xcode → Settings → Accounts → +)")
127+
println(" 2) Or set VOID_SIGN_IDENTITY=<sha1> to use a specific identity")
128+
}
129+
}
130+
89131
// Install to /Applications atomically-ish — stage then rename.
90132
let dest = "/Applications/Void.app"
91133
let stage = "/Applications/Void.app.new"

0 commit comments

Comments
 (0)