Renames the product across the codebase: Go module path, config/data paths, keychain service name, export file extension, build tooling, docs, and UI text. Sidebar brand mark now reads ">_ AlchementaryLink" with "Alchementary" in the connection-group gold accent colour, and the window title is "Alchementary Link". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019T8bJS8BCtY5GeSG2HBj1n
67 lines
2.2 KiB
Go
67 lines
2.2 KiB
Go
package secrets
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/zalando/go-keyring"
|
|
)
|
|
|
|
// KeychainStore attempts to use the OS keychain for secret storage.
|
|
// On macOS this uses the Keychain, on Linux it uses the Secret Service API,
|
|
// and on Windows it uses the Credential Manager.
|
|
//
|
|
// This is the preferred storage method when available. Falls back to
|
|
// the encrypted file-based SecretStore when the keychain is not accessible.
|
|
type KeychainStore struct {
|
|
serviceName string
|
|
}
|
|
|
|
// NewKeychainStore creates a KeychainStore that stores credentials under the
|
|
// given service name in the operating system's native keychain.
|
|
func NewKeychainStore(serviceName string) *KeychainStore {
|
|
return &KeychainStore{serviceName: serviceName}
|
|
}
|
|
|
|
// Available checks whether the OS keychain is accessible by performing a
|
|
// harmless write-then-delete probe. Returns true if the keychain responded
|
|
// without error.
|
|
func (k *KeychainStore) Available() bool {
|
|
// Attempt a round-trip to verify the keychain is usable.
|
|
probeKey := "__link_keychain_probe__"
|
|
probeVal := "probe"
|
|
|
|
if err := keyring.Set(k.serviceName, probeKey, probeVal); err != nil {
|
|
return false
|
|
}
|
|
_ = keyring.Delete(k.serviceName, probeKey)
|
|
return true
|
|
}
|
|
|
|
// SetSecret stores a secret in the OS keychain under the configured service
|
|
// name with the given key.
|
|
func (k *KeychainStore) SetSecret(key, value string) error {
|
|
if err := keyring.Set(k.serviceName, key, value); err != nil {
|
|
return fmt.Errorf("keychain: failed to store secret %q: %w", key, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetSecret retrieves a secret from the OS keychain. Returns an error if the
|
|
// key does not exist or the keychain is inaccessible.
|
|
func (k *KeychainStore) GetSecret(key string) (string, error) {
|
|
val, err := keyring.Get(k.serviceName, key)
|
|
if err != nil {
|
|
return "", fmt.Errorf("keychain: failed to retrieve secret %q: %w", key, err)
|
|
}
|
|
return val, nil
|
|
}
|
|
|
|
// DeleteSecret removes a secret from the OS keychain. Returns an error if the
|
|
// key does not exist or the keychain is inaccessible.
|
|
func (k *KeychainStore) DeleteSecret(key string) error {
|
|
if err := keyring.Delete(k.serviceName, key); err != nil {
|
|
return fmt.Errorf("keychain: failed to delete secret %q: %w", key, err)
|
|
}
|
|
return nil
|
|
}
|