registries

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrServiceDiscoveryFailed indicates the service discovery document could not be fetched.
	ErrServiceDiscoveryFailed = errors.New("failed to fetch service discovery document")

	// ErrNoAuthMethodsSupported indicates the registry has no supported authentication methods.
	ErrNoAuthMethodsSupported = errors.New("registry does not support any known authentication methods")

	// ErrCredentialsRequired indicates credentials are required but not provided.
	ErrCredentialsRequired = errors.New("credentials required")

	// ErrAuthenticationFailed indicates the authentication attempt failed.
	ErrAuthenticationFailed = errors.New("authentication failed")

	// ErrTokenExchangeFailed indicates the OAuth2 token exchange failed.
	ErrTokenExchangeFailed = errors.New("failed to exchange authorization code for token")

	// ErrStateMismatch indicates the OAuth2 state parameter didn't match.
	ErrStateMismatch = errors.New("OAuth2 state parameter mismatch")

	// ErrAuthorizationTimeout indicates the user didn't complete auth in time.
	ErrAuthorizationTimeout = errors.New("authorization timeout - please try again")

	// ErrBrowserOpenFailed indicates the browser could not be opened.
	ErrBrowserOpenFailed = errors.New("failed to open browser for authorization")

	// ErrConfigSaveFailed indicates the auth config could not be saved.
	ErrConfigSaveFailed = errors.New("failed to save authentication configuration")

	// ErrTokenRefreshFailed indicates the OAuth2 token refresh failed.
	ErrTokenRefreshFailed = errors.New("failed to refresh access token")

	// ErrPluginNotFound indicates the requested plugin was not found in the registry.
	ErrPluginNotFound = errors.New("plugin not found")

	// ErrVersionNotFound indicates the requested version was not found for the plugin.
	ErrVersionNotFound = errors.New("version not found")

	// ErrDownloadFailed indicates the plugin package download failed.
	ErrDownloadFailed = errors.New("failed to download plugin")

	// ErrChecksumMismatch indicates the downloaded file's checksum doesn't match expected.
	ErrChecksumMismatch = errors.New("checksum verification failed")

	// ErrSignatureInvalid indicates the GPG signature verification failed.
	ErrSignatureInvalid = errors.New("signature verification failed")

	// ErrSignatureMissing indicates a signature is required but not provided by registry.
	ErrSignatureMissing = errors.New("signature required but not provided by registry")

	// ErrSigningKeysMissing indicates signing keys are required but not provided by registry.
	ErrSigningKeysMissing = errors.New("signing keys required but not provided by registry")

	// ErrExtractionFailed indicates the plugin archive extraction failed.
	ErrExtractionFailed = errors.New("failed to extract plugin archive")

	// ErrInvalidPluginID indicates the plugin ID format is invalid.
	ErrInvalidPluginID = errors.New("invalid plugin ID format")

	// ErrNoCredentials indicates no credentials are stored for the registry.
	ErrNoCredentials = errors.New("no credentials found - run 'bluelink plugins login' first")
)

Functions

func DefaultBrowserOpener

func DefaultBrowserOpener(url string) error

DefaultBrowserOpener opens a URL in the default system browser. Uses absolute paths to system utilities to avoid PATH-based attacks.

func GetAuthConfigPath

func GetAuthConfigPath() string

GetAuthConfigPath returns the platform-specific path for plugins.auth.json.

func GetTokenStorePath

func GetTokenStorePath() string

GetTokenStorePath returns the platform-specific path for plugins.tokens.json.

func NormalizeRegistryHost

func NormalizeRegistryHost(host string) string

NormalizeRegistryHost strips the scheme from a registry host for use as a storage key. This ensures consistent lookups regardless of whether the user provided http:// or https://. Examples:

Types

type APIKeyCredentialStore

type APIKeyCredentialStore struct {
	// contains filtered or unexported fields
}

APIKeyCredentialStore handles storing and verifying API keys for registries. Unlike OAuth2 Authorization Code flow, this does not perform authentication during login - it only stores credentials for later use.

func NewAPIKeyCredentialStore

func NewAPIKeyCredentialStore(authConfigStore *AuthConfigStore) *APIKeyCredentialStore

NewAPIKeyCredentialStore creates a new API key credential store with default settings.

func NewAPIKeyCredentialStoreWithHTTPClient

func NewAPIKeyCredentialStoreWithHTTPClient(
	httpClient *http.Client,
	authConfigStore *AuthConfigStore,
) *APIKeyCredentialStore

NewAPIKeyCredentialStoreWithHTTPClient creates a new API key credential store with a custom HTTP client. This is primarily useful for testing.

func (*APIKeyCredentialStore) Store

func (a *APIKeyCredentialStore) Store(
	_ context.Context,
	registryHost string,
	_ *AuthV1Config,
	apiKey string,
) error

Store saves the API key in the auth config for later use. The key is not verified during storage - validation happens when making authenticated requests to the registry.

func (*APIKeyCredentialStore) Verify

func (a *APIKeyCredentialStore) Verify(
	ctx context.Context,
	registryHost string,
	authConfig *AuthV1Config,
	apiKey string,
) error

Verify checks if the API key is valid by making a test request to the registry.

type AuthCodeResult

type AuthCodeResult struct {
	AccessToken  string
	RefreshToken string
	TokenExpiry  *time.Time
	ClientId     string
}

AuthCodeResult contains the result of an authorization code flow.

type AuthConfigFile

type AuthConfigFile map[string]*RegistryAuthConfig

AuthConfigFile represents the plugins.auth.json file structure. Map of registry host to auth configuration.

type AuthConfigStore

type AuthConfigStore struct {
	// contains filtered or unexported fields
}

AuthConfigStore manages the plugins.auth.json file.

func NewAuthConfigStore

func NewAuthConfigStore() *AuthConfigStore

NewAuthConfigStore creates a new auth config store using the default path.

func NewAuthConfigStoreWithPath

func NewAuthConfigStoreWithPath(path string) *AuthConfigStore

NewAuthConfigStoreWithPath creates a new auth config store with a custom path. This is primarily useful for testing.

func (*AuthConfigStore) GetRegistryAuth

func (s *AuthConfigStore) GetRegistryAuth(registryHost string) (*RegistryAuthConfig, error)

GetRegistryAuth retrieves authentication for a specific registry. The registry host is normalized (scheme stripped) for consistent lookups.

func (*AuthConfigStore) HasRegistryAuth

func (s *AuthConfigStore) HasRegistryAuth(registryHost string) (bool, error)

HasRegistryAuth checks if authentication exists for a specific registry. The registry host is normalized (scheme stripped) for consistent lookups.

func (*AuthConfigStore) Load

func (s *AuthConfigStore) Load() (AuthConfigFile, error)

Load loads the auth config file.

func (*AuthConfigStore) Path

func (s *AuthConfigStore) Path() string

Path returns the path to the auth config file.

func (*AuthConfigStore) RemoveRegistryAuth

func (s *AuthConfigStore) RemoveRegistryAuth(registryHost string) error

RemoveRegistryAuth removes authentication for a specific registry. The registry host is normalized (scheme stripped) for consistent lookups.

func (*AuthConfigStore) Save

func (s *AuthConfigStore) Save(config AuthConfigFile) error

Save saves the auth config file with restrictive permissions.

func (*AuthConfigStore) SaveRegistryAuth

func (s *AuthConfigStore) SaveRegistryAuth(registryHost string, auth *RegistryAuthConfig) error

SaveRegistryAuth saves authentication for a specific registry. The registry host is normalized (scheme stripped) for consistent storage.

type AuthType

type AuthType string

AuthType represents the type of authentication supported by a registry.

const (
	AuthTypeAPIKey            AuthType = "api_key"
	AuthTypeOAuth2ClientCreds AuthType = "oauth2_client_credentials"
	AuthTypeOAuth2AuthCode    AuthType = "oauth2_authorization_code"
)

type AuthV1Config

type AuthV1Config struct {
	// APIKeyHeader is the HTTP header name for API key authentication.
	// When present, indicates API key auth is supported.
	APIKeyHeader string `json:"apiKeyHeader,omitempty"`

	// DownloadAuth specifies the authentication scheme for downloading artifacts.
	// Values: "bearer", "basic", "digest". Defaults to "bearer".
	DownloadAuth string `json:"downloadAuth,omitempty"`

	// Endpoint is the OAuth2 server base URL (e.g., "https://github.com/login/oauth").
	Endpoint string `json:"endpoint,omitempty"`

	// ClientId is the OAuth2 client ID for the authorization code flow.
	// This is provided by the server for auth code flow, not by the user.
	ClientId string `json:"clientId,omitempty"`

	// GrantTypes lists the supported OAuth2 grant types.
	// Supported values: "client_credentials", "authorization_code".
	GrantTypes []string `json:"grantTypes,omitempty"`

	// Authorize is the path for the authorization endpoint (defaults to "/authorize").
	Authorize string `json:"authorize,omitempty"`

	// Token is the path for the token endpoint (required for OAuth2).
	Token string `json:"token,omitempty"`

	// PKCE indicates whether PKCE (Proof Key for Code Exchange) is supported.
	PKCE bool `json:"pkce,omitempty"`
}

AuthV1Config represents the auth.v1 configuration from service discovery.

func (*AuthV1Config) GetAuthorizeURL

func (c *AuthV1Config) GetAuthorizeURL() string

GetAuthorizeURL returns the full authorization URL.

func (*AuthV1Config) GetSupportedAuthTypes

func (c *AuthV1Config) GetSupportedAuthTypes() []AuthType

GetSupportedAuthTypes returns all supported authentication types.

func (*AuthV1Config) GetTokenURL

func (c *AuthV1Config) GetTokenURL() string

GetTokenURL returns the full token URL.

func (*AuthV1Config) SupportsAuthType

func (c *AuthV1Config) SupportsAuthType(authType AuthType) bool

SupportsAuthType checks if the config supports a given authentication type.

func (*AuthV1Config) SupportsPKCE

func (c *AuthV1Config) SupportsPKCE() bool

SupportsPKCE returns whether PKCE is enabled for this config.

type BrowserOpener

type BrowserOpener func(url string) error

BrowserOpener is a function that opens a URL in the default browser.

type OAuth2AuthCodeAuthenticator

type OAuth2AuthCodeAuthenticator struct {
	// contains filtered or unexported fields
}

OAuth2AuthCodeAuthenticator handles OAuth2 authorization code authentication.

func NewOAuth2AuthCodeAuthenticator

func NewOAuth2AuthCodeAuthenticator(tokenStore *TokenStore) *OAuth2AuthCodeAuthenticator

NewOAuth2AuthCodeAuthenticator creates a new OAuth2 authorization code authenticator.

func NewOAuth2AuthCodeAuthenticatorWithOptions

func NewOAuth2AuthCodeAuthenticatorWithOptions(
	httpClient *http.Client,
	tokenStore *TokenStore,
	browserOpener BrowserOpener,
	timeout time.Duration,
) *OAuth2AuthCodeAuthenticator

NewOAuth2AuthCodeAuthenticatorWithOptions creates a new authenticator with custom options. This is primarily useful for testing.

func (*OAuth2AuthCodeAuthenticator) Authenticate

func (a *OAuth2AuthCodeAuthenticator) Authenticate(
	ctx context.Context,
	registryHost string,
	authConfig *AuthV1Config,
) (*AuthCodeResult, error)

Authenticate performs the authorization code flow and stores the resulting tokens.

func (*OAuth2AuthCodeAuthenticator) RefreshTokens

func (a *OAuth2AuthCodeAuthenticator) RefreshTokens(
	ctx context.Context,
	registryHost string,
	authConfig *AuthV1Config,
) (*AuthCodeResult, error)

RefreshTokens refreshes expired tokens using the refresh token.

type OAuth2ClientConfig

type OAuth2ClientConfig struct {
	ClientId     string `json:"clientId"`
	ClientSecret string `json:"clientSecret,omitempty"`
}

OAuth2ClientConfig holds OAuth2 client credentials for client_credentials flow.

type OAuth2ClientCredsStore

type OAuth2ClientCredsStore struct {
	// contains filtered or unexported fields
}

OAuth2ClientCredsStore handles storing OAuth2 client credentials for registries. Unlike OAuth2 Authorization Code flow, this does not perform authentication during login - it only stores credentials for later use.

func NewOAuth2ClientCredsStore

func NewOAuth2ClientCredsStore(authConfigStore *AuthConfigStore) *OAuth2ClientCredsStore

NewOAuth2ClientCredsStore creates a new OAuth2 client credentials store.

func NewOAuth2ClientCredsStoreWithHTTPClient

func NewOAuth2ClientCredsStoreWithHTTPClient(
	httpClient *http.Client,
	authConfigStore *AuthConfigStore,
) *OAuth2ClientCredsStore

NewOAuth2ClientCredsStoreWithHTTPClient creates a new credential store with a custom HTTP client. This is primarily useful for testing.

func (*OAuth2ClientCredsStore) ObtainToken

func (a *OAuth2ClientCredsStore) ObtainToken(
	ctx context.Context,
	authConfig *AuthV1Config,
	clientId string,
	clientSecret string,
) (*oauth2.Token, error)

ObtainToken exchanges client credentials for an access token using the oauth2 package.

func (*OAuth2ClientCredsStore) Store

func (a *OAuth2ClientCredsStore) Store(
	_ context.Context,
	registryHost string,
	_ *AuthV1Config,
	clientId string,
	clientSecret string,
) error

Store saves the client credentials in the auth config for later use. The credentials are not verified during storage - validation happens when making authenticated requests to the registry.

type PluginPackageMetadata

type PluginPackageMetadata struct {
	Filename            string            `json:"filename"`
	DownloadURL         string            `json:"downloadUrl"`
	OS                  string            `json:"os"`
	Arch                string            `json:"arch"`
	Shasum              string            `json:"shasum"`
	ShasumsURL          string            `json:"shasumsUrl,omitempty"`
	ShasumsSignatureURL string            `json:"shasumsSignatureUrl,omitempty"`
	SigningKeys         map[string]string `json:"signingKeys,omitempty"`
	Dependencies        map[string]string `json:"dependencies,omitempty"`
}

PluginPackageMetadata contains metadata about a downloadable plugin package.

type PluginServiceConfig

type PluginServiceConfig struct {
	// Endpoint is the base URL for the plugin API (e.g., "/v1/plugins").
	Endpoint string `json:"endpoint"`

	// DownloadAcceptContentType specifies the expected content type for downloads.
	// Defaults to "application/octet-stream" if not specified.
	DownloadAcceptContentType string `json:"downloadAcceptContentType,omitempty"`
}

PluginServiceConfig represents the configuration for plugin services (provider.v1, transformer.v1).

type PluginType

type PluginType string

PluginType represents the type of a plugin as discovered from the registry.

const (
	PluginTypeProvider    PluginType = "provider"
	PluginTypeTransformer PluginType = "transformer"
	PluginTypeUnknown     PluginType = ""
)

type PluginVersionInfo

type PluginVersionInfo struct {
	Version            string   `json:"version"`
	SupportedProtocols []string `json:"supportedProtocols,omitempty"`
}

PluginVersionInfo contains information about a single plugin version.

type PluginVersionsResponse

type PluginVersionsResponse struct {
	Versions []PluginVersionInfo `json:"versions"`
}

PluginVersionsResponse represents the response from the list versions endpoint.

type ProgressFunc

type ProgressFunc func(downloaded, total int64)

ProgressFunc is called during download to report progress.

type RegistryAuthConfig

type RegistryAuthConfig struct {
	// APIKey is the API key for API key authentication.
	APIKey string `json:"apiKey,omitempty"`

	// OAuth2 holds OAuth2 client credentials.
	OAuth2 *OAuth2ClientConfig `json:"oauth2,omitempty"`
}

RegistryAuthConfig holds authentication configuration for a single registry. This is stored in plugins.auth.json.

type RegistryClient

type RegistryClient struct {
	// contains filtered or unexported fields
}

RegistryClient handles authenticated requests to plugin registries.

func NewRegistryClient

func NewRegistryClient(
	authConfigStore *AuthConfigStore,
	tokenStore *TokenStore,
	discoveryClient *ServiceDiscoveryClient,
) *RegistryClient

NewRegistryClient creates a new registry client with default settings.

func NewRegistryClientWithHTTPClient

func NewRegistryClientWithHTTPClient(
	httpClient *http.Client,
	authConfigStore *AuthConfigStore,
	tokenStore *TokenStore,
	discoveryClient *ServiceDiscoveryClient,
) *RegistryClient

NewRegistryClientWithHTTPClient creates a new registry client with a custom HTTP client.

func (*RegistryClient) DownloadPackage

func (c *RegistryClient) DownloadPackage(
	ctx context.Context,
	registryHost string,
	metadata *PluginPackageMetadata,
	destPath string,
	progressFn ProgressFunc,
) error

DownloadPackage downloads a plugin package to the specified destination.

func (*RegistryClient) DownloadShasums

func (c *RegistryClient) DownloadShasums(
	ctx context.Context,
	registryHost, shasumsURL string,
) ([]byte, error)

DownloadShasums downloads the shasums file from the given URL.

func (*RegistryClient) DownloadSignature

func (c *RegistryClient) DownloadSignature(
	ctx context.Context,
	registryHost, signatureURL string,
) ([]byte, error)

DownloadSignature downloads the GPG signature file from the given URL.

func (*RegistryClient) GetPackageMetadata

func (c *RegistryClient) GetPackageMetadata(
	ctx context.Context,
	registryHost, namespace, pluginName, version, osName, arch string,
) (*PluginPackageMetadata, error)

GetPackageMetadata fetches metadata for a specific plugin version package.

func (*RegistryClient) ListVersions

func (c *RegistryClient) ListVersions(
	ctx context.Context,
	registryHost, namespace, pluginName string,
) (*PluginVersionsResponse, error)

ListVersions fetches available versions for a plugin from the registry.

type RegistryTokens

type RegistryTokens struct {
	// ClientId is the OAuth2 client ID (server-provided for auth code flow).
	ClientId string `json:"clientId"`

	// AccessToken is the current access token.
	AccessToken string `json:"accessToken"`

	// RefreshToken is the refresh token for obtaining new access tokens.
	RefreshToken string `json:"refreshToken,omitempty"`

	// TokenExpiry is when the access token expires.
	TokenExpiry *time.Time `json:"tokenExpiry,omitempty"`
}

RegistryTokens holds OAuth2 tokens for authorization code flow. This is stored in plugins.tokens.json.

func (*RegistryTokens) IsExpired

func (t *RegistryTokens) IsExpired() bool

IsExpired returns true if the access token is expired or about to expire. Considers a token expired if it expires within the next 30 seconds.

type ServiceDiscoveryClient

type ServiceDiscoveryClient struct {
	// contains filtered or unexported fields
}

ServiceDiscoveryClient fetches and parses service discovery documents from registries.

func NewServiceDiscoveryClient

func NewServiceDiscoveryClient() *ServiceDiscoveryClient

NewServiceDiscoveryClient creates a new service discovery client with default settings.

func NewServiceDiscoveryClientWithHTTPClient

func NewServiceDiscoveryClientWithHTTPClient(client *http.Client) *ServiceDiscoveryClient

NewServiceDiscoveryClientWithHTTPClient creates a new service discovery client with a custom HTTP client. This is primarily useful for testing.

func (*ServiceDiscoveryClient) Discover

func (c *ServiceDiscoveryClient) Discover(ctx context.Context, registryHost string) (*ServiceDiscoveryDocument, error)

Discover fetches and parses the service discovery document from a registry host. The host should be the registry hostname without scheme (e.g., "registry.example.com"). HTTPS is enforced for security unless:

  • The host already includes a scheme (for testing with local HTTP registries)
  • The host is localhost or 127.0.0.1 (for local development/testing)

func (*ServiceDiscoveryClient) DiscoverAuthConfig

func (c *ServiceDiscoveryClient) DiscoverAuthConfig(ctx context.Context, registryHost string) (*AuthV1Config, error)

DiscoverAuthConfig fetches the service discovery document and returns just the auth configuration. Returns ErrNoAuthMethodsSupported if the registry has no auth.v1 configuration.

func (*ServiceDiscoveryClient) GetPluginType

func (c *ServiceDiscoveryClient) GetPluginType(
	ctx context.Context,
	registryHost string,
) (PluginType, error)

GetPluginType determines the plugin type served by a registry host by checking which service endpoint is configured in the discovery document.

type ServiceDiscoveryDocument

type ServiceDiscoveryDocument struct {
	Auth          *AuthV1Config        `json:"auth.v1,omitempty"`
	ProviderV1    *PluginServiceConfig `json:"provider.v1,omitempty"`
	TransformerV1 *PluginServiceConfig `json:"transformer.v1,omitempty"`
}

ServiceDiscoveryDocument represents the .well-known/bluelink-services.json response.

type TokenStore

type TokenStore struct {
	// contains filtered or unexported fields
}

TokenStore manages the plugins.tokens.json file for OAuth2 auth code flow tokens.

func NewTokenStore

func NewTokenStore() *TokenStore

NewTokenStore creates a new token store using the default path.

func NewTokenStoreWithPath

func NewTokenStoreWithPath(path string) *TokenStore

NewTokenStoreWithPath creates a new token store with a custom path. This is primarily useful for testing.

func (*TokenStore) GetRegistryTokens

func (s *TokenStore) GetRegistryTokens(registryHost string) (*RegistryTokens, error)

GetRegistryTokens retrieves tokens for a specific registry. The registry host is normalized (scheme stripped) for consistent lookups.

func (*TokenStore) GetValidTokens

func (s *TokenStore) GetValidTokens(registryHost string) (*RegistryTokens, error)

GetValidTokens retrieves tokens for a registry only if they are not expired. Returns nil if tokens don't exist or are expired.

func (*TokenStore) HasRegistryTokens

func (s *TokenStore) HasRegistryTokens(registryHost string) (bool, error)

HasRegistryTokens checks if tokens exist for a specific registry. The registry host is normalized (scheme stripped) for consistent lookups.

func (*TokenStore) Load

func (s *TokenStore) Load() (TokensFile, error)

Load loads the token store file.

func (*TokenStore) Path

func (s *TokenStore) Path() string

Path returns the path to the token store file.

func (*TokenStore) RemoveRegistryTokens

func (s *TokenStore) RemoveRegistryTokens(registryHost string) error

RemoveRegistryTokens removes tokens for a specific registry. The registry host is normalized (scheme stripped) for consistent lookups.

func (*TokenStore) Save

func (s *TokenStore) Save(tokens TokensFile) error

Save saves the token store file with restrictive permissions.

func (*TokenStore) SaveRegistryTokens

func (s *TokenStore) SaveRegistryTokens(registryHost string, tokens *RegistryTokens) error

SaveRegistryTokens saves tokens for a specific registry. The registry host is normalized (scheme stripped) for consistent storage.

type TokensFile

type TokensFile map[string]*RegistryTokens

TokensFile represents the plugins.tokens.json file structure. Map of registry host to OAuth2 tokens.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL