Documentation
¶
Index ¶
- Constants
- Variables
- func DetectPotentialSecret(value, fieldName string) (isSecret bool, confidence float64)
- func IsSecretRef(input string) bool
- func MaskSecretValue(value string) string
- type ConfigSecretsResponse
- type EnvProvider
- func (p *EnvProvider) CanResolve(secretType string) bool
- func (p *EnvProvider) Delete(_ context.Context, _ Ref) error
- func (p *EnvProvider) IsAvailable() bool
- func (p *EnvProvider) List(_ context.Context) ([]Ref, error)
- func (p *EnvProvider) Resolve(_ context.Context, ref Ref) (string, error)
- func (p *EnvProvider) Store(_ context.Context, _ Ref, _ string) error
- type EnvVarStatus
- type KeyringProvider
- func (p *KeyringProvider) CanResolve(secretType string) bool
- func (p *KeyringProvider) Delete(_ context.Context, ref Ref) error
- func (p *KeyringProvider) DeleteWithRegistry(ctx context.Context, ref Ref) error
- func (p *KeyringProvider) IsAvailable() bool
- func (p *KeyringProvider) List(_ context.Context) ([]Ref, error)
- func (p *KeyringProvider) Resolve(_ context.Context, ref Ref) (string, error)
- func (p *KeyringProvider) SetWritesEnabled(enabled bool)
- func (p *KeyringProvider) Store(_ context.Context, ref Ref, value string) error
- func (p *KeyringProvider) StoreWithRegistry(ctx context.Context, ref Ref, value string) error
- type KeyringSecretStatus
- type MigrationAnalysis
- type MigrationCandidate
- type Provider
- type Ref
- type ResolveResult
- type Resolver
- func (r *Resolver) AnalyzeForMigration(v interface{}) *MigrationAnalysis
- func (r *Resolver) Delete(ctx context.Context, ref Ref) error
- func (r *Resolver) ExpandSecretRefs(ctx context.Context, input string) (string, error)
- func (r *Resolver) ExpandStructSecrets(ctx context.Context, v interface{}) error
- func (r *Resolver) ExpandStructSecretsCollectErrors(ctx context.Context, v interface{}) []SecretExpansionError
- func (r *Resolver) ExtractConfigSecrets(ctx context.Context, v interface{}) (*ConfigSecretsResponse, error)
- func (r *Resolver) GetAvailableProviders() []string
- func (r *Resolver) ListAll(ctx context.Context) ([]Ref, error)
- func (r *Resolver) RegisterProvider(secretType string, provider Provider)
- func (r *Resolver) Resolve(ctx context.Context, ref Ref) (string, error)
- func (r *Resolver) Store(ctx context.Context, ref Ref, value string) error
- type SecretExpansionError
Constants ¶
const ( // ServiceName for keyring entries ServiceName = "mcpproxy" SecretTypeKeyring = "keyring" RegistryKey = "_mcpproxy_secret_registry" )
const (
SecretTypeEnv = "env"
)
Variables ¶
var ErrKeyringTimeout = errors.New("keyring operation timed out (backend unresponsive — likely waiting on a user prompt)")
ErrKeyringTimeout is returned when a keyring operation exceeds keyringOpTimeout. Callers SHOULD treat this identically to "keyring unavailable" and fall back to an alternative secret store (in-memory, config-file, etc.) rather than propagating the error.
ErrKeyringUnavailable is returned by write operations (Store/Delete) when a previous IsAvailable() call determined the keyring backend is not usable on this system. We refuse to call keyring.Set in that state because on macOS it pops a system modal ("Keychain Not Found") whose default action is destructive. Callers MUST fall back to an alternative secret store.
Functions ¶
func DetectPotentialSecret ¶
DetectPotentialSecret analyzes a string to determine if it might be a secret
func IsSecretRef ¶
IsSecretRef returns true if the string looks like a secret reference
func MaskSecretValue ¶
MaskSecretValue masks a secret value for safe display
Types ¶
type ConfigSecretsResponse ¶
type ConfigSecretsResponse struct {
Secrets []KeyringSecretStatus `json:"secrets"`
EnvironmentVars []EnvVarStatus `json:"environment_vars"`
TotalSecrets int `json:"total_secrets"`
TotalEnvVars int `json:"total_env_vars"`
}
ConfigSecretsResponse contains secrets and environment variables referenced in config
type EnvProvider ¶
type EnvProvider struct{}
EnvProvider resolves secrets from environment variables
func NewEnvProvider ¶
func NewEnvProvider() *EnvProvider
NewEnvProvider creates a new environment variable provider
func (*EnvProvider) CanResolve ¶
func (p *EnvProvider) CanResolve(secretType string) bool
CanResolve returns true if this provider can handle the given secret type
func (*EnvProvider) Delete ¶
func (p *EnvProvider) Delete(_ context.Context, _ Ref) error
Delete is not supported for environment variables
func (*EnvProvider) IsAvailable ¶
func (p *EnvProvider) IsAvailable() bool
IsAvailable always returns true as environment variables are always available
func (*EnvProvider) List ¶
func (p *EnvProvider) List(_ context.Context) ([]Ref, error)
List returns all environment variables that look like secrets
type EnvVarStatus ¶
EnvVarStatus represents the status of an environment variable reference
type KeyringProvider ¶
type KeyringProvider struct {
// contains filtered or unexported fields
}
KeyringProvider resolves secrets from OS keyring (Keychain, Secret Service, WinCred)
func NewKeyringProvider ¶
func NewKeyringProvider() *KeyringProvider
NewKeyringProvider creates a new keyring provider
func (*KeyringProvider) CanResolve ¶
func (p *KeyringProvider) CanResolve(secretType string) bool
CanResolve returns true if this provider can handle the given secret type
func (*KeyringProvider) Delete ¶
func (p *KeyringProvider) Delete(_ context.Context, ref Ref) error
Delete removes a secret from the OS keyring and updates the registry. Same three-layer guard as Store: known-unavailable short-circuit, first-time probe, and failure cache.
func (*KeyringProvider) DeleteWithRegistry ¶
func (p *KeyringProvider) DeleteWithRegistry(ctx context.Context, ref Ref) error
DeleteWithRegistry deletes a secret and updates the registry
func (*KeyringProvider) IsAvailable ¶
func (p *KeyringProvider) IsAvailable() bool
IsAvailable checks if the keyring is available on the current system, and caches the result. Subsequent calls to Store/Delete will short-circuit to ErrKeyringUnavailable WITHOUT calling keyring.Set if the probe decided the keyring is not usable — this is the key property that prevents the macOS "Keychain Not Found" modal from ever being shown to the user.
This is deliberately conservative and NEVER calls keyring.Set as a probe: on macOS, Set triggers the "Keychain Not Found" system modal when the user's default keychain is missing/locked/corrupted, whose default button is the destructive "Reset To Defaults" action.
Instead, we:
- Skip the probe entirely in obvious headless/CI environments.
- Do a single read-only Get for a non-existent key and treat ErrNotFound as "available, no secret yet". Any other error (including ErrUnsupportedPlatform and backend communication failures) is treated as "unavailable".
- Run the Get inside a goroutine with a 2s hard timeout so a hung keychain backend cannot stall the caller.
- Cache the result via markAvailable/markUnavailable so Store/Delete can short-circuit on the fast path.
func (*KeyringProvider) List ¶
func (p *KeyringProvider) List(_ context.Context) ([]Ref, error)
List returns all secret references stored in the keyring Note: go-keyring doesn't provide a list function, so we'll track them differently
func (*KeyringProvider) SetWritesEnabled ¶ added in v0.24.0
func (p *KeyringProvider) SetWritesEnabled(enabled bool)
SetWritesEnabled is a test-only helper that forces the opt-in state regardless of env vars. Pass true to allow keyring.Set, false to disallow. It is safe to call at any time — Store checks the override synchronously.
func (*KeyringProvider) Store ¶
Store saves a secret to the OS keyring and updates the registry.
By default on macOS this method does NOT actually call keyring.Set — it returns ErrKeyringUnavailable immediately. Rationale: on macOS keyring.Set wraps Security.framework under the hood; if the user's default keychain is missing/locked/in an unusual state, Security.framework pops a system modal ("Keychain Not Found" with a destructive default button). The underlying call is blocking and the goroutine we'd wrap it in cannot be cancelled once started — it keeps running until Security. framework finally responds, which may involve the user clicking buttons. No wrapper / timeout / probe can prevent the modal from appearing, so the only safe option is to not call keyring.Set at all.
Users who want keyring-backed storage on macOS can opt in by setting the environment variable MCPPROXY_KEYRING_WRITE=1 or calling EnableKeyringWrites(true) on the provider. When opted in, Store runs a three-layer guard (known-unavailable cache, first-time probe, failure cache) plus a 3s runWithTimeout wrapper — but note that the wrapper only protects the CALLING goroutine from blocking; it cannot prevent the modal from appearing on the user's screen once keyring.Set is called.
On non-macOS platforms (Linux Secret Service, Windows Credential Manager), Store goes through the three-layer guard without requiring the opt-in, because those backends don't have the same modal issue.
Callers MUST treat ErrKeyringUnavailable as "fall back to in-config / in-memory storage" and surface a clear log message.
func (*KeyringProvider) StoreWithRegistry ¶
StoreWithRegistry stores a secret and updates the registry
type KeyringSecretStatus ¶
KeyringSecretStatus represents the status of a keyring secret reference
type MigrationAnalysis ¶
type MigrationAnalysis struct {
Candidates []MigrationCandidate `json:"candidates"`
TotalFound int `json:"total_found"`
}
MigrationAnalysis contains analysis of potential secrets to migrate
type MigrationCandidate ¶
type MigrationCandidate struct {
Field string `json:"field"` // Field path in config
Value string `json:"value"` // Current plaintext value (masked in responses)
Suggested string `json:"suggested"` // Suggested Ref
Confidence float64 `json:"confidence"` // Confidence this is a secret (0-1)
}
MigrationCandidate represents a potential secret that could be migrated
type Provider ¶
type Provider interface {
// CanResolve returns true if this provider can handle the given secret type
CanResolve(secretType string) bool
// Resolve retrieves the actual secret value
Resolve(ctx context.Context, ref Ref) (string, error)
// Store saves a secret (if supported by the provider)
Store(ctx context.Context, ref Ref, value string) error
// Delete removes a secret (if supported by the provider)
Delete(ctx context.Context, ref Ref) error
// List returns all secret references handled by this provider
List(ctx context.Context) ([]Ref, error)
// IsAvailable checks if the provider is available on the current system
IsAvailable() bool
}
Provider interface for secret resolution
type Ref ¶
type Ref struct {
Type string `json:"type"` // env, keyring, op, age
Name string `json:"name"` // environment variable name, keyring alias, etc.
Original string `json:"original"` // original reference string
}
Ref represents a reference to a secret
func FindSecretRefs ¶
FindSecretRefs finds all secret references in a string
func ParseSecretRef ¶
ParseSecretRef parses a string that may contain secret references
type ResolveResult ¶
ResolveResult contains the result of secret resolution
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver manages secret resolution using multiple providers
func (*Resolver) AnalyzeForMigration ¶
func (r *Resolver) AnalyzeForMigration(v interface{}) *MigrationAnalysis
AnalyzeForMigration analyzes a struct for potential secrets that could be migrated
func (*Resolver) ExpandSecretRefs ¶
ExpandSecretRefs replaces all secret references in a string with resolved values
func (*Resolver) ExpandStructSecrets ¶
ExpandStructSecrets recursively expands secret references in a struct
func (*Resolver) ExpandStructSecretsCollectErrors ¶ added in v0.21.2
func (r *Resolver) ExpandStructSecretsCollectErrors(ctx context.Context, v interface{}) []SecretExpansionError
ExpandStructSecretsCollectErrors expands secret references in all string fields of v. Unlike ExpandStructSecrets, it does not fail fast: it collects all expansion errors and continues processing remaining fields. On error, the field retains its original value. v must be a non-nil pointer to a struct.
func (*Resolver) ExtractConfigSecrets ¶
func (r *Resolver) ExtractConfigSecrets(ctx context.Context, v interface{}) (*ConfigSecretsResponse, error)
ExtractConfigSecrets extracts all secret and environment references from a config structure
func (*Resolver) GetAvailableProviders ¶
GetAvailableProviders returns a list of available providers
func (*Resolver) RegisterProvider ¶
RegisterProvider registers a new secret provider
type SecretExpansionError ¶ added in v0.21.2
type SecretExpansionError struct {
FieldPath string // e.g. "WorkingDir", "Isolation.WorkingDir", "Args[0]", "Env[MY_VAR]"
Reference string // the original unresolved reference pattern, e.g. "${env:HOME}"
Err error
}
SecretExpansionError records a failure to resolve a single secret reference during struct expansion.