Summary
The published macOS binaries are ad-hoc (linker) signed, with no Developer ID and
no Team ID. macOS keychain access control binds "Always Allow" to a binary's code
signature, and an ad-hoc signature gives the binary no stable identity to match
against. So after pup auth login, the keychain keeps prompting on later runs even
after you click "Always Allow", and the grant breaks again after every brew upgrade
when the binary changes.
This is the same symptom as #247 (closed as completed at 0.36.0), which still
reproduces on 0.63.0. The underlying cause looks unaddressed: it isn't a storage-path
problem, it's code signing.
Root cause
Both the installed 0.63.0 and the current 0.64.2 release binary are ad-hoc signed:
$ codesign -dv "$(command -v pup)"
CodeDirectory ... flags=0x20002(adhoc,linker-signed)
Signature=adhoc
TeamIdentifier=not set
$ spctl -a -t install "$(command -v pup)"
... : rejected
macOS records a keychain item's trusted apps by their Designated Requirement, which
comes from a real code signature. Ad-hoc binaries have no stable DR, so:
- "Always Allow" can't reliably match pup on the next run, and the prompt returns.
brew upgrade pup installs a new binary at a new versioned Cellar path, which never
matches the old ACL entry, so the prompt always comes back after an upgrade.
(The cosign/sigstore signature in the release notes verifies the download's
provenance. It's a separate layer from the Mach-O code signature that Gatekeeper and
keychain ACLs use.)
Steps to reproduce
pup auth login on macOS (Apple Silicon) and complete the browser flow. The CLI
prints an authorize URL like:
https://app.us5.datadoghq.com/oauth2/v1/authorize?response_type=code&client_id=<redacted>&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000%2Foauth%2Fcallback&state=<redacted>&scope=apm_read+…+user_access_read&code_challenge=<redacted>&code_challenge_method=S256
- At the keychain prompt, enter your login password and click "Always Allow".
- Run a command that reads the stored session (e.g.
pup traces aggregate …). The
keychain prompts again.
brew upgrade pup, then run a command. The prompt returns regardless of the earlier
"Always Allow".
Environment
- OS: macOS, Apple Silicon (arm64)
- pup: 0.63.0 (0.64.2 release binary verified to be the same)
- Install: Homebrew tap
datadog-labs/pack
- Auth: OAuth2
Suggested fix
Sign the macOS release binaries with a Developer ID Application certificate and
notarize them (notarytool, with stapling) in the GoReleaser release pipeline. A stable
signature gives the binary a Designated Requirement the keychain ACL can match, so
"Always Allow" persists across runs and survives upgrades, and it clears the Gatekeeper
rejected result.
Workarounds today (both have downsides)
- API-key env vars (
DD_API_KEY / DD_APP_KEY / DD_SITE) so the keychain isn't
involved, at the cost of giving up short-lived OAuth tokens for long-lived keys.
- Re-grant the keychain item's ACL in Keychain Access after each upgrade — manual and
easy to forget.
Summary
The published macOS binaries are ad-hoc (linker) signed, with no Developer ID and
no Team ID. macOS keychain access control binds "Always Allow" to a binary's code
signature, and an ad-hoc signature gives the binary no stable identity to match
against. So after
pup auth login, the keychain keeps prompting on later runs evenafter you click "Always Allow", and the grant breaks again after every
brew upgradewhen the binary changes.
This is the same symptom as #247 (closed as completed at 0.36.0), which still
reproduces on 0.63.0. The underlying cause looks unaddressed: it isn't a storage-path
problem, it's code signing.
Root cause
Both the installed 0.63.0 and the current 0.64.2 release binary are ad-hoc signed:
macOS records a keychain item's trusted apps by their Designated Requirement, which
comes from a real code signature. Ad-hoc binaries have no stable DR, so:
brew upgrade pupinstalls a new binary at a new versioned Cellar path, which nevermatches the old ACL entry, so the prompt always comes back after an upgrade.
(The cosign/sigstore signature in the release notes verifies the download's
provenance. It's a separate layer from the Mach-O code signature that Gatekeeper and
keychain ACLs use.)
Steps to reproduce
pup auth loginon macOS (Apple Silicon) and complete the browser flow. The CLIprints an authorize URL like:
https://app.us5.datadoghq.com/oauth2/v1/authorize?response_type=code&client_id=<redacted>&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000%2Foauth%2Fcallback&state=<redacted>&scope=apm_read+…+user_access_read&code_challenge=<redacted>&code_challenge_method=S256pup traces aggregate …). Thekeychain prompts again.
brew upgrade pup, then run a command. The prompt returns regardless of the earlier"Always Allow".
Environment
datadog-labs/packSuggested fix
Sign the macOS release binaries with a Developer ID Application certificate and
notarize them (notarytool, with stapling) in the GoReleaser release pipeline. A stable
signature gives the binary a Designated Requirement the keychain ACL can match, so
"Always Allow" persists across runs and survives upgrades, and it clears the Gatekeeper
rejectedresult.Workarounds today (both have downsides)
DD_API_KEY/DD_APP_KEY/DD_SITE) so the keychain isn'tinvolved, at the cost of giving up short-lived OAuth tokens for long-lived keys.
easy to forget.