diff --git a/internal/secrets/store.go b/internal/secrets/store.go index 9f40d82..ec5b1f6 100644 --- a/internal/secrets/store.go +++ b/internal/secrets/store.go @@ -173,8 +173,14 @@ func openKeyring() (keyring.Keyring, error) { } cfg := keyring.Config{ - ServiceName: config.AppName, - KeychainTrustApplication: runtime.GOOS == "darwin", + ServiceName: config.AppName, + // KeychainTrustApplication is intentionally false to support Homebrew upgrades. + // When true, macOS Keychain ties access control to the specific binary hash. + // Homebrew upgrades install a new binary with a different hash, causing the + // new binary to lose access to existing keychain items. With false, users may + // see a one-time keychain prompt after upgrade (click "Always Allow"), but + // tokens survive across upgrades. See: https://github.com/steipete/gogcli/issues/86 + KeychainTrustApplication: false, AllowedBackends: backends, FileDir: keyringDir, FilePasswordFunc: fileKeyringPasswordFunc(), diff --git a/internal/secrets/store_test.go b/internal/secrets/store_test.go index 560b5bb..238e053 100644 --- a/internal/secrets/store_test.go +++ b/internal/secrets/store_test.go @@ -17,10 +17,11 @@ import ( var errKeyringOpenBlocked = errors.New("keyring open blocked") // keyringConfig creates a keyring.Config for testing. +// KeychainTrustApplication is false to match production config (see store.go). func keyringConfig(keyringDir string) keyring.Config { return keyring.Config{ ServiceName: config.AppName, - KeychainTrustApplication: runtime.GOOS == "darwin", + KeychainTrustApplication: false, AllowedBackends: []keyring.BackendType{keyring.FileBackend}, FileDir: keyringDir, FilePasswordFunc: fileKeyringPasswordFunc(),