Skip to content

[tacet] Strip quarantine before anything execs the bundle - #6

Merged
drycode merged 1 commit into
mainfrom
dy/tacet-strip-quarantine
Aug 6, 2026
Merged

[tacet] Strip quarantine before anything execs the bundle#6
drycode merged 1 commit into
mainfrom
dy/tacet-strip-quarantine

Conversation

@drycode

@drycode drycode commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes the _dyld_start hang tracked in DRYCodeWorks/tacet#25, which currently makes this cask unsafe to recommend.

Why

Casks quarantine by default. Nothing ever launches tacet through LaunchServices — launchd starts it, and install-server.sh execs it to verify the install. Exec'ing a quarantined bundle directly blocks in _dyld_start waiting on a first-launch consent decision that has no UI to answer it, so the app deadlocks before running a line of its own code.

Notarization does not help. The gate is consent, not assessment: spctl -a -vvv returns accepted / Notarized Developer ID on the exact bundle that hangs.

/Applications was never the variable either. A quarantined copy in ~/Applications hangs identically; the earlier "same bytes run elsewhere" observation held only because those copies were never quarantined.

The first blocked launch then wedges the path, not the file — removing the xattr afterward does not recover it, and neither does replacing the file or lsregister -u. Only a different path does. That is why the strip has to run in postflight, before launchd or the installer can touch the binary.

Evidence

Isolated against the published v0.1.1 Tacet.zip on macOS 26.6 (25G72), Apple Silicon:

path quarantine result
/Applications/Tacet.app no usage:, rc=2
/Applications/Tacet.app set rc=124, stack is entirely _dyld_start
~/Applications/TacetQ.app set rc=124
/Applications/Tacet.app removed after hang rc=124
/Applications/TacetProbe3.app no usage:, rc=2

Full isolation table and the sampled stacks are in the issue.

Verification

  • brew style Casks/tacet.rb — no offenses
  • brew audit --cask --tap drycodeworks/tap tacet — clean

Not yet verified live. A real brew install --cask / uninstall cycle would stop the running com.drycodeworks.tacet* agents on the test machine, so it was left for a deliberate run rather than done as a side effect.

Casks quarantine by default. For tacet that is fatal rather than
annoying: nothing launches this bundle through LaunchServices — launchd
starts it, and install-server.sh execs it to verify the install.
Exec'ing a quarantined bundle directly blocks in _dyld_start waiting on
a first-launch consent decision that has no UI to answer it, so the app
deadlocks before running a line of its own code.

Notarization does not help, which is what made this confusing: the gate
is consent, not assessment, and `spctl -a` accepts the exact bundle that
hangs. /Applications was never the variable either — a quarantined copy
in ~/Applications hangs identically.

The first blocked launch then wedges the path, not the file. Removing
the xattr afterward does not recover it and neither does replacing the
file; only a different path does. So the strip has to happen in
postflight, before launchd or the installer can touch the binary.

Solves: DRYCodeWorks/tacet#25
Tests: brew style + brew audit --cask clean; mechanism isolated against
       the published v0.1.1 artifact (quarantined -> rc=124 in
       _dyld_start, unquarantined -> rc=2) across five paths
@drycode
drycode merged commit d07680d into main Aug 6, 2026
3 checks passed
@drycode
drycode deleted the dy/tacet-strip-quarantine branch August 6, 2026 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant