plugin

package
v0.37.0 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2026 License: Apache-2.0 Imports: 57 Imported by: 0

README

Plugin System

The scafctl plugin system allows extending the provider framework with external plugins using hashicorp/go-plugin.

Architecture

  • hashicorp/go-plugin: Manages plugin lifecycle, process isolation, and crash recovery
  • gRPC: Communication protocol between scafctl and plugins
  • Protocol Buffers: Interface definitions in proto/plugin.proto

Plugin Interface

Plugins must implement the ProviderPlugin interface (8 methods):

type ProviderPlugin interface {
    // GetProviders returns all provider names exposed by this plugin.
    GetProviders(ctx context.Context) ([]string, error)

    // GetProviderDescriptor returns metadata for a specific provider.
    GetProviderDescriptor(ctx context.Context, providerName string) (*provider.Descriptor, error)

    // ConfigureProvider sends host-side configuration to a named provider once
    // after plugin load. Implementations store the config internally for
    // subsequent Execute calls.
    ConfigureProvider(ctx context.Context, providerName string, cfg ProviderConfig) error

    // ExecuteProvider executes a provider with the given input.
    ExecuteProvider(ctx context.Context, providerName string, input map[string]any) (*provider.Output, error)

    // ExecuteProviderStream executes a provider that produces incremental
    // output. The callback is invoked for each chunk; the final chunk carries
    // the Result (or Error). Return ErrStreamingNotSupported if not implemented.
    ExecuteProviderStream(ctx context.Context, providerName string, input map[string]any, cb func(StreamChunk)) error

    // DescribeWhatIf returns a human-readable description of what the provider
    // would do with the given inputs, without executing.
    DescribeWhatIf(ctx context.Context, providerName string, input map[string]any) (string, error)

    // ExtractDependencies returns resolver dependency names from the given
    // inputs. Return nil to let the host use generic extraction.
    ExtractDependencies(ctx context.Context, providerName string, inputs map[string]any) ([]string, error)

    // StopProvider requests graceful shutdown of a running provider execution.
    // providerName may be empty to stop all providers. Return nil if not implemented.
    StopProvider(ctx context.Context, providerName string) error
}

ProviderConfig

After plugin load, scafctl calls ConfigureProvider once per provider with host-side settings:

Field Type Description
Quiet bool Suppress non-essential output
NoColor bool Disable colored output
BinaryName string CLI binary name (e.g. "scafctl" or an embedder name)
HostServiceID uint32 GRPCBroker service ID for HostService callbacks
Settings map[string]json.RawMessage Extensible key-value settings

The ConfigureProvider request also carries a protocol_version field for feature detection. The current version is defined by PluginProtocolVersion.

Auth Handler Plugin Interface

Auth handler plugins implement the AuthHandlerPlugin interface (9 methods):

type AuthHandlerPlugin interface {
    GetAuthHandlers(ctx context.Context) ([]AuthHandlerInfo, error)
    ConfigureAuthHandler(ctx context.Context, handlerName string, cfg ProviderConfig) error
    Login(ctx context.Context, handlerName string, req LoginRequest, cb func(DeviceCodePrompt)) (*LoginResponse, error)
    Logout(ctx context.Context, handlerName string) error
    GetStatus(ctx context.Context, handlerName string) (*auth.Status, error)
    GetToken(ctx context.Context, handlerName string, req TokenRequest) (*TokenResponse, error)
    ListCachedTokens(ctx context.Context, handlerName string) ([]*auth.CachedTokenInfo, error)
    PurgeExpiredTokens(ctx context.Context, handlerName string) (int, error)
    StopAuthHandler(ctx context.Context, handlerName string) error
}

After plugin load, scafctl calls ConfigureAuthHandler once per handler. The request uses the same ProviderConfig fields as ConfigureProvider (quiet, no_color, binary_name, settings, host_service_id, protocol_version). The response includes optional diagnostics for structured warnings/errors.

Auth handler plugins have full access to HostService callbacks (secrets, auth identity) via the GRPCBroker, mirroring the provider plugin pattern.

RegisterAuthHandlerPlugins returns []*AuthHandlerClient. The caller should defer KillAllAuthHandlers(clients) for cleanup.

HostService Callbacks

Plugins that need host-side resources use the HostService callback service, accessed via the GRPCBroker using the service ID from ProviderConfig.HostServiceID.

Callback Purpose
GetSecret / SetSecret / DeleteSecret / ListSecrets Access the host's secret store
GetAuthIdentity Retrieve identity claims from the host's auth registry
ListAuthHandlers List available auth handlers (filtered by AllowedAuthHandlers)
GetAuthToken Retrieve a valid access token from the host's auth registry

Secret names are scoped by a plugin-specific prefix. Auth handler access is restricted to the set declared in AllowedAuthHandlers.

Streaming Execution

ExecuteProviderStream delivers incremental output (stdout/stderr chunks) to the host in real time. The callback receives StreamChunk values:

  • Stdout / Stderr -- incremental output bytes
  • Result -- final *provider.Output on success
  • Error -- terminal error string on failure

Plugins that do not support streaming should return ErrStreamingNotSupported. The host automatically falls back to unary ExecuteProvider.

Diagnostics and Exit Codes

The ExecuteProviderResponse proto carries structured Diagnostic messages alongside the output. Each diagnostic has a severity, summary, detail, and optional attribute path. An exit_code field enables plugins to propagate typed exit codes back to the host.

Protocol Version Negotiation

scafctl sends PluginProtocolVersion in the ConfigureProvider request. Plugins can inspect this to enable or disable features based on the host's capabilities. The plugin may return its own protocol version in the response for the host to check.

Creating a Plugin

  1. Import the plugin SDK package:

    import "github.com/oakwood-commons/scafctl-plugin-sdk/plugin"
    
  2. Implement the ProviderPlugin interface (all 8 methods)

  3. Call plugin.Serve() in your main function:

    func main() {
        plugin.Serve(&YourPlugin{})
    }
    
  4. Build your plugin as an executable:

    go build -o my-plugin main.go
    

Descriptor Caching

The plugin client caches provider descriptors after the first GetProviderDescriptor call to avoid repeated gRPC round-trips. The cache is protected by a sync.RWMutex for concurrent access.

Plugin Discovery

Plugins are discovered by scanning configured plugin directories for executable files. The system:

  1. Searches for executable files in plugin directories
  2. Attempts to connect to each potential plugin
  3. Registers providers from successfully loaded plugins
  4. Skips plugins that fail to load

RegisterPluginProviders returns all created *Client instances so the caller can clean up with KillAll on shutdown.

Plugin Lifecycle

Provider Plugins
  1. Discovery: scafctl finds plugin binaries
  2. Handshake: Protocol version and magic cookie are validated
  3. GetProviders: Plugin advertises provider names
  4. GetProviderDescriptor: scafctl fetches and caches descriptors
  5. ConfigureProvider: Host sends config + protocol version + HostService ID
  6. Execution: ExecuteProvider or ExecuteProviderStream on invocation
  7. Shutdown: StopProvider for graceful cleanup, then Kill() the process
Auth Handler Plugins
  1. Discovery: scafctl finds plugin binaries
  2. Handshake: Protocol version and magic cookie are validated
  3. GetAuthHandlers: Plugin advertises handler names, flows, and capabilities
  4. ConfigureAuthHandler: Host sends config + protocol version + HostService ID
  5. Authentication: Login, Logout, GetStatus, GetToken on user request
  6. Shutdown: StopAuthHandler for graceful cleanup, then Kill() the process

Example Plugin

See examples/plugins/echo/ for a complete example plugin implementation.

Security

  • Plugins run in isolated processes
  • Communication over gRPC provides clear security boundaries
  • Plugins are validated using handshake configuration
  • Failed plugins don't crash the main process
  • Secret access is scoped by plugin-specific prefix
  • Auth handler access restricted by AllowedAuthHandlers allowlist
  • Plugin binary signatures can be verified via Sigstore/cosign (see below)

Signature Verification

Plugin binaries fetched from catalogs support Sigstore/cosign keyless signature verification. This is controlled by a SignaturePolicy:

Mode Behavior
off Digest-only verification (default)
warn Verify signature; warn on failure
enforce Verify signature; fail on missing or invalid signature
Types
  • SignaturePolicy -- policy with Mode, TrustedIssuers, and TrustedIdentities
  • SignatureMode -- string enum: off, warn, enforce
  • SignatureResult -- verification outcome with Verified, Issuer, Identity, SignedAt
  • SignatureVerifier -- interface for signature verification implementations
Build Tags

The cosign implementation is behind a cosign build tag:

  • With tag: cosign.go provides a real Sigstore verifier
  • Without tag (default): cosign_stub.go returns ErrCosignNotAvailable
Context API
  • WithSignaturePolicy(ctx, policy) -- store a policy in context
  • SignaturePolicyFromContext(ctx) -- retrieve a policy from context
  • HandleVerificationError(policy, err, logger) -- apply mode-based error handling

Testing

Use MockProviderPlugin for testing plugin implementations without running actual plugin processes.

Documentation

Index

Constants

View Source
const AuthHandlerPluginName = sdkplugin.AuthHandlerPluginName

AuthHandlerPluginName is the name used to identify the auth handler plugin.

View Source
const (
	// DefaultFetchCooldown is the default duration to suppress retry attempts
	// after a plugin auto-fetch failure.
	DefaultFetchCooldown = 5 * time.Minute
)
View Source
const PluginName = sdkplugin.PluginName

PluginName is the name used to identify the provider plugin.

View Source
const PluginProtocolVersion = sdkplugin.PluginProtocolVersion

PluginProtocolVersion is the current plugin protocol version.

Variables

View Source
var (
	HandshakeConfig            = ptrTo(sdkplugin.HandshakeConfig())
	AuthHandlerHandshakeConfig = ptrTo(sdkplugin.AuthHandlerHandshakeConfig())
)

handshakeConfigVal and authHandlerHandshakeConfigVal cache the SDK handshake configs at init time. In the SDK these are function-based to prevent mutation; here we store them as pointer variables for backward compatibility with existing scafctl code that reads HandshakeConfig.ProtocolVersion etc. Do not mutate; read-only for backward compat.

View Source
var (
	ErrPoolClosed       = errors.New("plugin pool is closed")
	ErrPoolFull         = errors.New("plugin pool at maximum capacity")
	ErrPluginNotAllowed = errors.New("plugin not in allowlist")
	ErrExternalDisabled = errors.New("external plugins disabled")
)

Sentinel errors for Pool operations.

View Source
var ErrCosignNotAvailable = errors.New("cosign signature verification not available: binary compiled without 'cosign' build tag")

ErrCosignNotAvailable indicates that the binary was compiled without cosign support (missing the "cosign" build tag).

View Source
var ErrDedupTimeout = errors.New("operation timed out while waiting for concurrent request")
View Source
var ErrSignatureInvalid = errors.New("cosign signature verification failed")

ErrSignatureInvalid indicates that a signature exists but failed validation (wrong issuer, identity mismatch, expired certificate, etc.).

View Source
var ErrSignatureNotFound = errors.New("no cosign signature found for artifact")

ErrSignatureNotFound indicates that no signature was found for the artifact.

View Source
var ErrStreamingNotSupported = sdkplugin.ErrStreamingNotSupported

ErrStreamingNotSupported is returned by ExecuteProviderStream when the plugin does not support streaming execution.

Functions

func BareNames added in v0.17.0

func BareNames(plugins []AllowedPlugin) []string

BareNames extracts just the plugin name (right of the slash) from each entry. Wildcard entries are skipped since they don't map to a single plugin name. Used for pool-level and preload-level fast-reject allowlists that don't need catalog qualification.

func CanonicalFromCacheKey added in v0.24.0

func CanonicalFromCacheKey(cacheKey string) string

CanonicalFromCacheKey is no longer applicable with hash-based cache keys (hashes are one-way). This function is retained for backward compatibility with the tilde-encoded path segments used in the on-disk layout.

func CatalogNames added in v0.17.0

func CatalogNames(plugins []AllowedPlugin) []string

CatalogNames extracts the unique catalog names from the plugin list. Used to derive the WithAllowedCatalogs set for chain construction.

func CurrentPlatform added in v0.5.0

func CurrentPlatform() string

CurrentPlatform returns the current OS/architecture in OCI platform format (e.g., "linux/amd64", "darwin/arm64").

func GroupByCatalog added in v0.17.0

func GroupByCatalog(plugins []AllowedPlugin) map[string]catalog.PluginPolicy

GroupByCatalog groups parsed AllowedPlugin entries by catalog name. Wildcard entries ("catalog/*") set AllowAll=true for that catalog. Explicit entries after a wildcard for the same catalog are ignored.

func HandleVerificationError added in v0.15.0

func HandleVerificationError(policy *SignaturePolicy, err error, lgr logr.Logger, labels ...any) error

HandleVerificationError applies the policy's mode to a signature verification error. In warn mode the error is logged and nil is returned; in enforce mode (and any unrecognised mode) the error is propagated. A nil policy is treated as enforce (fail-closed).

func KillAll added in v0.8.0

func KillAll(clients []*Client)

KillAll terminates all plugin processes in the given client list. This is safe to call with a nil or empty slice.

func KillAllAuthHandlers added in v0.8.0

func KillAllAuthHandlers(clients []*AuthHandlerClient)

KillAllAuthHandlers terminates all auth handler plugin processes in the given client list. This is safe to call with a nil or empty slice.

func ParsePlatform added in v0.5.0

func ParsePlatform(platform string) (os, arch string, err error)

ParsePlatform splits an OCI platform string into OS and architecture. Returns an error if the format is invalid.

func Paths added in v0.5.0

func Paths(results []FetchResult) []string

Paths returns just the binary paths from a slice of FetchResult.

func PlatformCacheKey added in v0.5.0

func PlatformCacheKey(platform string) string

PlatformCacheKey returns a filesystem-safe key for the platform (e.g., "linux-amd64" from "linux/amd64").

func PluginCacheKey added in v0.15.0

func PluginCacheKey(name string, kind solution.PluginKind) string

PluginCacheKey returns the directory name for a plugin in the cache. Provider plugins use the bare name; auth-handler plugins are prefixed with "auth-handler-". This is the name component only (no registry hash).

func PluginCachePath added in v0.24.0

func PluginCachePath(name string, kind solution.PluginKind, registryHash, version, platform string) string

PluginCachePath builds the relative cache path for a plugin binary directory. When registryHash is empty, the registry-hash directory level is omitted.

func PluginKindFromCacheKey added in v0.17.0

func PluginKindFromCacheKey(cacheKey string) (name string, kind solution.PluginKind)

PluginKindFromCacheKey infers the plugin kind and bare name from a cache key. Cache keys for auth-handlers are prefixed with "auth-handler-".

func PolicyPlugins added in v0.17.0

func PolicyPlugins(policies map[string]catalog.PluginPolicy, catalogName string) []string

PolicyPlugins returns the plugin list for a specific catalog from the policy map. For wildcard catalogs (AllowAll=true), returns nil (meaning unrestricted). For absent catalogs, returns nil.

func PoolErrorHTTPStatus added in v0.14.0

func PoolErrorHTTPStatus(err error) int

PoolErrorHTTPStatus maps pool errors to appropriate HTTP status codes. Returns 0 if the error is not a recognized pool error.

func ProbePluginDescription added in v0.14.0

func ProbePluginDescription(ctx context.Context, binaryPath, name string) (string, bool)

ProbePluginDescription starts a plugin binary, queries the first provider's descriptor for its description, then kills the process. Returns empty string and false on any failure.

func ProbePluginDescriptor added in v0.17.0

func ProbePluginDescriptor(ctx context.Context, binaryPath, providerName string) (*provider.Descriptor, error)

ProbePluginDescriptor starts a plugin binary, queries the named provider's full descriptor, then kills the process. Returns nil and an error on failure. This is a short-lived probe — it does not register the provider in any registry.

func ResolveAllowedCanonicals added in v0.24.0

func ResolveAllowedCanonicals(allowed []string, catalogs []interface{ Name() string }) map[string]bool

ResolveAllowedCanonicals converts a list of user-provided catalog names (which may be aliases or canonical URLs) into a set of canonical IDs for fast lookup. It resolves aliases via the provided catalogs slice.

Names that don't match any known catalog alias are treated as literal canonical IDs (allowing users to specify URLs directly in the allowlist).

func Serve

func Serve(impl ProviderPlugin)

Serve is a helper function for plugin implementers to serve their provider plugins. This should be called from the plugin's main() function.

func ServeAuthHandler added in v0.5.0

func ServeAuthHandler(impl AuthHandlerPlugin)

ServeAuthHandler is a helper function for plugin implementers to serve their auth handler plugins. This should be called from the plugin's main() function.

func ValidateVerificationURI added in v0.14.0

func ValidateVerificationURI(uri string, trusted []string) error

ValidateVerificationURI validates a device code verification URI against security requirements and an optional trusted domain list. This is the host-side defense against malicious plugins sending phishing URLs in device code prompts.

Rules:

  • Must be a valid URL with HTTPS scheme
  • Must not target private/loopback addresses
  • Must not use non-standard ports (only 443 allowed)
  • If trusted is non-empty, host must match or be a subdomain of an entry
  • If trusted is empty, any HTTPS URL passing the above checks is allowed

func WithSignaturePolicy added in v0.15.0

func WithSignaturePolicy(ctx context.Context, policy *SignaturePolicy) context.Context

WithSignaturePolicy stores a SignaturePolicy in the context.

Types

type AllowedPlugin added in v0.17.0

type AllowedPlugin string

AllowedPlugin represents a qualified plugin reference in "catalog/pluginName" format. It encodes both the catalog a plugin may be fetched from and the plugin name.

func ParseAllowedPlugins added in v0.17.0

func ParseAllowedPlugins(raw []string) ([]AllowedPlugin, error)

ParseAllowedPlugins parses and validates a slice of raw "catalog/plugin" strings.

func (AllowedPlugin) Catalog added in v0.17.0

func (a AllowedPlugin) Catalog() string

Catalog returns the catalog segment (left of the slash).

func (AllowedPlugin) IsWildcard added in v0.17.0

func (a AllowedPlugin) IsWildcard() bool

IsWildcard returns true if this entry uses the "catalog/*" wildcard syntax, meaning all plugins from that catalog are permitted.

func (AllowedPlugin) Plugin added in v0.17.0

func (a AllowedPlugin) Plugin() string

Plugin returns the plugin name segment (right of the slash).

func (AllowedPlugin) Validate added in v0.17.0

func (a AllowedPlugin) Validate() error

Validate checks that the AllowedPlugin matches the required "catalog/plugin" format.

type AuthHandlerClient added in v0.5.0

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

AuthHandlerClient wraps a plugin client for auth handler plugins.

func DiscoverAuthHandlers added in v0.5.0

func DiscoverAuthHandlers(pluginDirs []string, opts ...ClientOption) ([]*AuthHandlerClient, error)

DiscoverAuthHandlers discovers auth handler plugins from the given directories.

func NewAuthHandlerClient added in v0.5.0

func NewAuthHandlerClient(pluginPath string, opts ...ClientOption) (*AuthHandlerClient, error)

NewAuthHandlerClient creates a new auth handler plugin client.

func RegisterAuthHandlerPlugins added in v0.5.0

func RegisterAuthHandlerPlugins(ctx context.Context, registry *auth.Registry, pluginDirs []string, cfg *ProviderConfig, clientOpts ...ClientOption) ([]*AuthHandlerClient, error)

RegisterAuthHandlerPlugins discovers auth handler plugins and registers them with the auth registry. Returns the created clients (caller should Kill() them on cleanup).

func RegisterFetchedAuthHandlerPlugins added in v0.5.0

func RegisterFetchedAuthHandlerPlugins(ctx context.Context, registry *auth.Registry, results []FetchResult, cfg *ProviderConfig, clientOpts ...ClientOption) ([]*AuthHandlerClient, error)

RegisterFetchedAuthHandlerPlugins loads and registers fetched auth handler plugin binaries into the auth registry. Returns the created clients (caller should Kill() them on cleanup).

func (*AuthHandlerClient) ActivateServerMode added in v0.33.0

func (c *AuthHandlerClient) ActivateServerMode(ctx context.Context, settings []byte) error

func (*AuthHandlerClient) ConfigureAuthHandler added in v0.8.0

func (c *AuthHandlerClient) ConfigureAuthHandler(ctx context.Context, handlerName string, cfg ProviderConfig) error

ConfigureAuthHandler delegates to the plugin's ConfigureAuthHandler.

func (*AuthHandlerClient) GetAuthHandlers added in v0.5.0

func (c *AuthHandlerClient) GetAuthHandlers(ctx context.Context) ([]AuthHandlerInfo, error)

GetAuthHandlers returns all auth handler names exposed by this plugin.

func (*AuthHandlerClient) GetStatus added in v0.5.0

func (c *AuthHandlerClient) GetStatus(ctx context.Context, handlerName string) (*auth.Status, error)

GetStatus delegates to the plugin's GetStatus.

func (*AuthHandlerClient) GetToken added in v0.5.0

func (c *AuthHandlerClient) GetToken(ctx context.Context, handlerName string, req TokenRequest) (*TokenResponse, error)

GetToken delegates to the plugin's GetToken.

func (*AuthHandlerClient) HostServiceID added in v0.8.0

func (c *AuthHandlerClient) HostServiceID() uint32

HostServiceID returns the broker service ID of the HostService callback server. Returns 0 if no HostService was registered.

func (*AuthHandlerClient) Kill added in v0.5.0

func (c *AuthHandlerClient) Kill()

Kill terminates the plugin process.

func (*AuthHandlerClient) Login added in v0.5.0

func (c *AuthHandlerClient) Login(ctx context.Context, handlerName string, req LoginRequest, cb func(DeviceCodePrompt)) (*LoginResponse, error)

Login delegates to the plugin's Login.

func (*AuthHandlerClient) Logout added in v0.5.0

func (c *AuthHandlerClient) Logout(ctx context.Context, handlerName string) error

Logout delegates to the plugin's Logout.

func (*AuthHandlerClient) Name added in v0.5.0

func (c *AuthHandlerClient) Name() string

Name returns the plugin name.

func (*AuthHandlerClient) Path added in v0.5.0

func (c *AuthHandlerClient) Path() string

Path returns the plugin path.

func (*AuthHandlerClient) StartupDuration added in v0.15.0

func (c *AuthHandlerClient) StartupDuration() time.Duration

StartupDuration returns the time taken to start and handshake with the plugin process.

func (*AuthHandlerClient) StopAuthHandler added in v0.8.0

func (c *AuthHandlerClient) StopAuthHandler(ctx context.Context, handlerName string) error

StopAuthHandler delegates to the plugin's StopAuthHandler.

type AuthHandlerGRPCClient added in v0.5.0

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

AuthHandlerGRPCClient implements AuthHandlerPlugin by calling the gRPC service.

func (*AuthHandlerGRPCClient) ActivateServerMode added in v0.33.0

func (c *AuthHandlerGRPCClient) ActivateServerMode(ctx context.Context, settings json.RawMessage) error

ActivateServerMode sends server-mode settings to the plugin. When settings is nil the plugin stays in CLI mode. When non-nil the bytes are passed as-is to the plugin's ActivateServerMode RPC which unmarshals and validates them.

func (*AuthHandlerGRPCClient) ConfigureAuthHandler added in v0.8.0

func (c *AuthHandlerGRPCClient) ConfigureAuthHandler(ctx context.Context, handlerName string, cfg ProviderConfig) error

ConfigureAuthHandler implements AuthHandlerPlugin.ConfigureAuthHandler.

func (*AuthHandlerGRPCClient) DetectAvailableFlows added in v0.15.0

func (c *AuthHandlerGRPCClient) DetectAvailableFlows(ctx context.Context, handlerName string) ([]FlowAvailability, error)

DetectAvailableFlows implements AuthHandlerPlugin.DetectAvailableFlows.

func (*AuthHandlerGRPCClient) GetAuthHandlers added in v0.5.0

func (c *AuthHandlerGRPCClient) GetAuthHandlers(ctx context.Context) ([]AuthHandlerInfo, error)

GetAuthHandlers implements AuthHandlerPlugin.GetAuthHandlers.

func (*AuthHandlerGRPCClient) GetStatus added in v0.5.0

func (c *AuthHandlerGRPCClient) GetStatus(ctx context.Context, handlerName string, req StatusRequest) (*auth.Status, error)

GetStatus implements AuthHandlerPlugin.GetStatus.

func (*AuthHandlerGRPCClient) GetToken added in v0.5.0

func (c *AuthHandlerGRPCClient) GetToken(ctx context.Context, handlerName string, req TokenRequest) (*TokenResponse, error)

GetToken implements AuthHandlerPlugin.GetToken.

func (*AuthHandlerGRPCClient) ListCachedTokens added in v0.5.0

func (c *AuthHandlerGRPCClient) ListCachedTokens(ctx context.Context, handlerName string) ([]*auth.CachedTokenInfo, error)

ListCachedTokens implements AuthHandlerPlugin.ListCachedTokens.

func (*AuthHandlerGRPCClient) Login added in v0.5.0

func (c *AuthHandlerGRPCClient) Login(ctx context.Context, handlerName string, req LoginRequest, deviceCodeCb func(DeviceCodePrompt)) (*LoginResponse, error)

Login implements AuthHandlerPlugin.Login with streaming device code support.

func (*AuthHandlerGRPCClient) Logout added in v0.5.0

func (c *AuthHandlerGRPCClient) Logout(ctx context.Context, handlerName string, req LogoutRequest) error

Logout implements AuthHandlerPlugin.Logout.

func (*AuthHandlerGRPCClient) PurgeExpiredTokens added in v0.5.0

func (c *AuthHandlerGRPCClient) PurgeExpiredTokens(ctx context.Context, handlerName string) (int, error)

PurgeExpiredTokens implements AuthHandlerPlugin.PurgeExpiredTokens.

func (*AuthHandlerGRPCClient) StopAuthHandler added in v0.8.0

func (c *AuthHandlerGRPCClient) StopAuthHandler(ctx context.Context, handlerName string) error

StopAuthHandler implements AuthHandlerPlugin.StopAuthHandler.

type AuthHandlerGRPCPlugin added in v0.5.0

type AuthHandlerGRPCPlugin struct {
	plugin.Plugin
	Impl AuthHandlerPlugin
	// HostDeps holds host-side dependencies for the HostService callback server.
	// Set by the host before starting the plugin. Nil on the plugin side.
	HostDeps *HostServiceDeps
}

AuthHandlerGRPCPlugin implements plugin.GRPCPlugin from hashicorp/go-plugin for auth handler plugins.

func (*AuthHandlerGRPCPlugin) GRPCClient added in v0.5.0

func (p *AuthHandlerGRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (any, error)

GRPCClient returns the auth handler gRPC client. If HostDeps is non-nil, a HostService gRPC server is started via the broker.

func (*AuthHandlerGRPCPlugin) GRPCServer added in v0.5.0

func (p *AuthHandlerGRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error

GRPCServer registers the auth handler gRPC server.

type AuthHandlerGRPCServer added in v0.5.0

type AuthHandlerGRPCServer struct {
	proto.UnimplementedAuthHandlerServiceServer
	Impl AuthHandlerPlugin
	// contains filtered or unexported fields
}

AuthHandlerGRPCServer implements the gRPC server for auth handler plugins.

func (*AuthHandlerGRPCServer) ConfigureAuthHandler added in v0.8.0

ConfigureAuthHandler implements the ConfigureAuthHandler RPC.

func (*AuthHandlerGRPCServer) DetectAvailableFlows added in v0.15.0

DetectAvailableFlows implements the DetectAvailableFlows RPC.

func (*AuthHandlerGRPCServer) GetAuthHandlers added in v0.5.0

GetAuthHandlers implements the GetAuthHandlers RPC.

func (*AuthHandlerGRPCServer) GetStatus added in v0.5.0

GetStatus implements the GetStatus RPC.

func (*AuthHandlerGRPCServer) GetToken added in v0.5.0

GetToken implements the GetToken RPC.

func (*AuthHandlerGRPCServer) ListCachedTokens added in v0.5.0

ListCachedTokens implements the ListCachedTokens RPC.

func (*AuthHandlerGRPCServer) Login added in v0.5.0

Login implements the Login RPC with server-side streaming.

func (*AuthHandlerGRPCServer) Logout added in v0.5.0

Logout implements the Logout RPC.

func (*AuthHandlerGRPCServer) PurgeExpiredTokens added in v0.5.0

PurgeExpiredTokens implements the PurgeExpiredTokens RPC.

func (*AuthHandlerGRPCServer) StopAuthHandler added in v0.8.0

StopAuthHandler implements the StopAuthHandler RPC.

type AuthHandlerInfo added in v0.5.0

type AuthHandlerInfo = sdkplugin.AuthHandlerInfo

AuthHandlerInfo holds static metadata about an auth handler exposed by a plugin.

type AuthHandlerPlugin added in v0.5.0

type AuthHandlerPlugin = sdkplugin.AuthHandlerPlugin

AuthHandlerPlugin is the interface that auth handler plugins must implement.

type AuthHandlerWrapper added in v0.5.0

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

AuthHandlerWrapper wraps a plugin auth handler to implement the auth.Handler (and optionally auth.TokenLister / auth.TokenPurger) interfaces.

func NewAuthHandlerWrapper added in v0.5.0

func NewAuthHandlerWrapper(client *AuthHandlerClient, info AuthHandlerInfo) *AuthHandlerWrapper

NewAuthHandlerWrapper creates a new wrapper for a plugin auth handler.

func (*AuthHandlerWrapper) ActivateServerMode added in v0.33.0

func (w *AuthHandlerWrapper) ActivateServerMode(ctx context.Context, settings json.RawMessage) error

func (*AuthHandlerWrapper) ApplyOverrides added in v0.17.0

func (w *AuthHandlerWrapper) ApplyOverrides(ctx context.Context, overrides map[string]string) error

ApplyOverrides implements auth.Configurer. It merges the provided key-value overrides into the handler's current plugin configuration and re-sends ConfigureAuthHandler to the plugin process. This allows CLI flags (e.g., --client-id, --tenant) to override config-file and default settings at runtime before login.

func (*AuthHandlerWrapper) Capabilities added in v0.5.0

func (w *AuthHandlerWrapper) Capabilities() []auth.Capability

Capabilities implements auth.Handler.

func (*AuthHandlerWrapper) Client added in v0.5.0

Client returns the underlying plugin client.

func (*AuthHandlerWrapper) DetectAvailableFlows added in v0.15.0

func (w *AuthHandlerWrapper) DetectAvailableFlows(ctx context.Context) ([]auth.FlowAvailability, error)

DetectAvailableFlows implements auth.FlowDetector.

func (*AuthHandlerWrapper) DisplayName added in v0.5.0

func (w *AuthHandlerWrapper) DisplayName() string

DisplayName implements auth.Handler.

func (*AuthHandlerWrapper) GetToken added in v0.5.0

func (w *AuthHandlerWrapper) GetToken(ctx context.Context, opts auth.TokenOptions) (*auth.Token, error)

GetToken implements auth.Handler.

func (*AuthHandlerWrapper) InjectAuth added in v0.5.0

func (w *AuthHandlerWrapper) InjectAuth(ctx context.Context, req *http.Request, opts auth.TokenOptions) error

InjectAuth implements auth.Handler. Since http.Request cannot be serialized over gRPC, this method decomposes into GetToken (over gRPC) + local header injection.

func (*AuthHandlerWrapper) ListCachedTokens added in v0.5.0

func (w *AuthHandlerWrapper) ListCachedTokens(ctx context.Context) ([]*auth.CachedTokenInfo, error)

ListCachedTokens implements auth.TokenLister.

func (*AuthHandlerWrapper) Login added in v0.5.0

Login implements auth.Handler.

func (*AuthHandlerWrapper) Logout added in v0.5.0

func (w *AuthHandlerWrapper) Logout(ctx context.Context) error

Logout implements auth.Handler.

The empty LogoutRequest{} sends no instance Hostname: the auth.Handler interface's Logout(ctx) carries no selector, so scafctl's own commands clear all of a handler's cached credentials. Per-instance status/logout selection (LogoutRequest/StatusRequest.Hostname, backed by CapInstanceHostname) is forwarded faithfully at the gRPC boundary but not yet wired into a hostname-aware auth.Handler path -- that is tracked separately as part of scafctl-plugin-sdk#58 and is intentionally out of scope for the --callback-port work (#589).

func (*AuthHandlerWrapper) Name added in v0.5.0

func (w *AuthHandlerWrapper) Name() string

Name implements auth.Handler.

func (*AuthHandlerWrapper) PurgeExpiredTokens added in v0.5.0

func (w *AuthHandlerWrapper) PurgeExpiredTokens(ctx context.Context) (int, error)

PurgeExpiredTokens implements auth.TokenPurger.

func (*AuthHandlerWrapper) SetRequireTrustedDomains added in v0.30.0

func (w *AuthHandlerWrapper) SetRequireTrustedDomains(require bool)

SetRequireTrustedDomains controls whether device-code verification fails closed when no trusted domains are configured. Set true for non-official handlers so a catalog-resolved third-party handler cannot display arbitrary verification URLs without explicitly configured auth.handlers.<name>.trustedVerificationDomains.

func (*AuthHandlerWrapper) SetStartupLatency added in v0.15.0

func (w *AuthHandlerWrapper) SetStartupLatency(d time.Duration)

SetStartupLatency records the plugin process startup latency.

func (*AuthHandlerWrapper) SetTrustedDomains added in v0.14.0

func (w *AuthHandlerWrapper) SetTrustedDomains(domains []string)

SetTrustedDomains sets the trusted verification URI domains for device code URL validation. When non-empty, device code prompts from this handler are validated against these domains (exact match or subdomain).

func (*AuthHandlerWrapper) StartupLatency added in v0.15.0

func (w *AuthHandlerWrapper) StartupLatency() time.Duration

StartupLatency returns the plugin process startup latency.

func (*AuthHandlerWrapper) Status added in v0.5.0

func (w *AuthHandlerWrapper) Status(ctx context.Context) (*auth.Status, error)

Status implements auth.Handler.

See Logout: StatusRequest{} carries no instance Hostname because the auth.Handler interface has no selector; per-instance status is deferred to the CapInstanceHostname follow-up (scafctl-plugin-sdk#58).

func (*AuthHandlerWrapper) SupportedFlows added in v0.5.0

func (w *AuthHandlerWrapper) SupportedFlows() []auth.Flow

SupportedFlows implements auth.Handler.

type Cache added in v0.5.0

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

Cache manages a local content-addressed cache of plugin binaries.

Cache layout (with optional registry hash):

<cacheDir>/<name>/[<registryHash>/]<version>/<os>-<arch>/<name>[.exe]

The registry hash directory level is present only when a plugin is fetched from a catalog. Plugins installed without a catalog omit it. On Windows platforms, the binary has a ".exe" extension.

Example:

~/.cache/scafctl/plugins/aws-provider/a1b2c3d4e5f67890/1.5.3/darwin-arm64/aws-provider
~/.cache/scafctl/plugins/my-local-plugin/1.0.0/darwin-arm64/my-local-plugin

func NewCache added in v0.5.0

func NewCache(cacheDir string) *Cache

NewCache creates a new Cache. If cacheDir is empty, the default XDG cache directory (paths.PluginCacheDir()) is used.

func NewManagedCache added in v0.24.0

func NewManagedCache(cacheDir string, maxSize int64) (*Cache, error)

NewManagedCache creates a Cache backed by a bounded LRU with pinning. This is intended for API server mode where concurrent access and eviction control are required. maxSize is the total byte budget for cached binaries. cacheDir should be derived from settings.PluginCacheDirFor(binaryName) to ensure embedders with custom binary names get isolated cache directories.

func (*Cache) Digest added in v0.5.0

func (c *Cache) Digest(name, version, platform string, opts ...CacheOption) (string, error)

Digest computes the sha256 digest of a cached plugin binary. Returns the digest in \"sha256:<hex>\" format.

func (*Cache) Dir added in v0.5.0

func (c *Cache) Dir() string

Dir returns the root cache directory.

func (*Cache) Get added in v0.5.0

func (c *Cache) Get(name, version, platform, expectedDigest string, opts ...CacheOption) (string, bool)

Get retrieves the path to a cached plugin binary. Returns the path and true if the binary exists and (optionally) matches the expected digest. If expectedDigest is empty, no digest verification is performed.

func (*Cache) GetLatestBinary added in v0.14.0

func (c *Cache) GetLatestBinary(name string) (string, string, bool)

GetLatestBinary returns the path to the newest cached binary for the given name on the current platform. Returns the path, version, and true if found.

func (*Cache) GetLatestCached added in v0.14.0

func (c *Cache) GetLatestCached(name, platform string, opts ...CacheOption) (string, string, bool)

GetLatestCached returns the path to the newest cached binary for the given name and platform, regardless of version. When WithRegistryHash is provided, only that registry's versions are searched. Otherwise, all versions across all registry hashes (and the no-registry path) are considered. Returns empty string and false if nothing is cached.

func (*Cache) GetLatestCachedPin added in v0.24.0

func (c *Cache) GetLatestCachedPin(name, platform string, opts ...CacheOption) (string, string, func(), bool)

GetLatestCachedPin returns the newest cached binary and pins it. The caller must call release() when done with the binary. This is the atomic alternative to GetLatestCached()+Pin().

func (*Cache) GetPin added in v0.24.0

func (c *Cache) GetPin(name, version, platform, expectedDigest string, opts ...CacheOption) (string, func(), bool)

GetPin retrieves a cached plugin binary and pins it to prevent eviction. The caller must call release() when done with the binary. In CLI mode (unbounded), the release function is a no-op. This is the atomic alternative to Get()+Pin() which has a TOCTOU gap.

func (*Cache) List added in v0.5.0

func (c *Cache) List() ([]CachedPlugin, error)

List returns all cached (name, version, platform) triples. It handles both layouts: with and without a registry hash directory level.

func (*Cache) ListCurrentPlatform added in v0.14.0

func (c *Cache) ListCurrentPlatform() ([]CachedPlugin, error)

ListCurrentPlatform returns cached plugins that match the current platform. When multiple versions of the same plugin are cached, only the latest (by semver) is included.

func (*Cache) Pin added in v0.24.0

func (c *Cache) Pin(name, version, platform string, opts ...CacheOption) (path string, release func(), ok bool)

Pin marks a cached plugin binary as in-use and returns its path. The entry cannot be evicted until the returned release function is called. In CLI mode (unbounded), Pin stats the file and returns a no-op release.

func (*Cache) Prune added in v0.17.0

func (c *Cache) Prune(opts PruneOptions, dryRun bool) (*PruneSummary, error)

Prune removes old cached plugin versions according to the given options. It returns the list of removed entries. If dryRun is true, nothing is deleted but the results reflect what would be removed.

func (*Cache) Put added in v0.5.0

func (c *Cache) Put(name, version, platform string, data []byte, opts ...CacheOption) (string, error)

Put writes a plugin binary to the cache. It creates the directory structure, writes the data, sets executable permissions, and returns the path to the cached binary.

func (*Cache) Remove added in v0.5.0

func (c *Cache) Remove(name, version, platform string, opts ...CacheOption) error

Remove deletes a cached plugin binary.

func (*Cache) ResolveVersion added in v0.33.0

func (c *Cache) ResolveVersion(name, version, platform string) (string, bool, error)

ResolveVersion returns the on-disk path to a cached plugin binary for an exact version, searching across both the flat (no-registry) cache layout and every registry-hash layout. This lets callers that do not know a plugin's catalog registry hash still resolve catalog-installed plugins, which live under <name>/<registryHash>/<version>/... rather than the flat <name>/<version>/... layout.

When the same version resolves under multiple registry layouts with differing binary contents, the result is ambiguous and an error is returned rather than silently choosing one. Identical binaries (matching digests) resolve to a single match.

func (*Cache) SetPin added in v0.24.0

func (c *Cache) SetPin(name, version, platform string, data []byte, opts ...CacheOption) (string, func(), error)

SetPin writes a plugin binary and pins it atomically to prevent eviction between write and use. In CLI mode (unbounded), behaves like Put with a no-op release.

func (*Cache) WarmUp added in v0.24.0

func (c *Cache) WarmUp() error

WarmUp scans the cache directory and populates the LRU and version index. No-op in CLI mode (unbounded cache).

type CacheOption added in v0.24.0

type CacheOption func(*cacheOpts)

CacheOption configures cache operations.

func WithRegistryHash added in v0.24.0

func WithRegistryHash(hash string) CacheOption

WithRegistryHash sets the registry hash directory level for cache lookups. When the hash is empty, this option is a no-op (no registry level is used).

type CachedPlugin added in v0.5.0

type CachedPlugin struct {
	Name         string `json:"name" yaml:"name" doc:"Plugin name"`
	Version      string `json:"version" yaml:"version" doc:"Plugin version"`
	Platform     string `json:"platform" yaml:"platform" doc:"Target platform (os/arch)"`
	Path         string `json:"path" yaml:"path" doc:"Absolute path to cached binary"`
	Size         int64  `json:"size" yaml:"size" doc:"Binary size in bytes"`
	RegistryHash string `json:"registryHash,omitempty" yaml:"registryHash,omitempty" doc:"Registry hash directory (empty for local installs)"`
}

CachedPlugin describes a cached plugin binary.

type CachedPluginInfo added in v0.14.0

type CachedPluginInfo struct {
	Name        string `json:"name" yaml:"name" doc:"Plugin name"`
	Version     string `json:"version" yaml:"version" doc:"Plugin version"`
	Path        string `json:"path" yaml:"path" doc:"Absolute path to cached binary"`
	Description string `json:"description,omitempty" yaml:"description,omitempty" doc:"Human-readable summary of the provider"`
}

CachedPluginInfo describes a cached plugin with metadata obtained by starting the plugin binary and querying its providers.

func DescribeCachedPlugins added in v0.14.0

func DescribeCachedPlugins(ctx context.Context, cached []CachedPlugin, skip map[string]bool) []CachedPluginInfo

DescribeCachedPlugins starts each cached plugin binary, queries its provider descriptors to obtain descriptions, then shuts it down. Plugins that fail to start or respond are excluded from the result (they may be non-provider artifacts such as auth-handler binaries). The skip set contains provider names that should be excluded from the result (e.g., already-listed builtin/official names).

type CatalogIdentity added in v0.24.0

type CatalogIdentity struct {
	// Canonical is the stable, machine-independent identifier derived from
	// the catalog's actual location. For OCI catalogs this is
	// "registry/repository" (e.g. "ghcr.io/acme/plugins"). For filesystem
	// catalogs this is the absolute path.
	Canonical string

	// Alias is the user-facing name from the config file (e.g. "production").
	// Used for display and logging only — never as a cache key.
	Alias string
	// contains filtered or unexported fields
}

CatalogIdentity pairs a stable canonical origin with the user-facing alias. The canonical form is derived from the actual registry location (not the config alias), making it rename-proof and portable across machines.

Create instances via NewCatalogIdentity or IdentityFromCatalog to ensure the registry hash is precomputed.

func IdentityFromCatalog added in v0.24.0

func IdentityFromCatalog(cat interface{ Name() string }) CatalogIdentity

IdentityFromCatalog derives a CatalogIdentity from a catalog implementation. It uses type assertions to extract the canonical location:

  • OCI catalogs (Registry+Repository): canonical = "registry/repository"
  • Filesystem catalogs (Path): canonical = absolute path
  • Fallback: canonical = catalog.Name() (best effort)

func NewCatalogIdentity added in v0.24.0

func NewCatalogIdentity(canonical, alias string) CatalogIdentity

NewCatalogIdentity creates a CatalogIdentity with a precomputed registry hash.

func (CatalogIdentity) IsZero added in v0.24.0

func (id CatalogIdentity) IsZero() bool

IsZero reports whether the identity has no canonical value set.

func (CatalogIdentity) RegistryHash added in v0.24.0

func (id CatalogIdentity) RegistryHash() string

RegistryHash returns a collision-resistant hash of the canonical registry ID. The result is a 16-character lowercase hex string derived from SHA-256, used as a directory level to isolate same-name plugins from different catalogs. Returns an empty string for zero identities (no registry known).

When constructed via NewCatalogIdentity or IdentityFromCatalog, this returns the precomputed value with no allocation. For bare struct literals (e.g. in tests), it computes and returns the hash without caching.

func (CatalogIdentity) String added in v0.24.0

func (id CatalogIdentity) String() string

String returns the canonical ID for logging and display. If an alias is available, it is included in parentheses.

type Client

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

Client wraps a plugin client and manages its lifecycle

func Discover

func Discover(pluginDirs []string, opts ...ClientOption) ([]*Client, error)

Discover discovers plugins from the given directories

func NewClient

func NewClient(pluginPath string, opts ...ClientOption) (*Client, error)

NewClient creates a new plugin client.

func RegisterCachedPlugin added in v0.14.0

func RegisterCachedPlugin(ctx context.Context, name string, registry *provider.Registry, cfg *ProviderConfig, cacheDir string, clientOpts ...ClientOption) ([]*Client, error)

RegisterCachedPlugin looks up a provider plugin by name in the local cache, starts it, and registers its providers into the given registry. The name should be the cache name (e.g. "aws-provider" or "auth-handler-github"). Returns the created clients (caller must Kill them on cleanup) or an error if the plugin is not cached.

func RegisterCachedPluginVersion added in v0.17.0

func RegisterCachedPluginVersion(ctx context.Context, name, version string, registry *provider.Registry, cfg *ProviderConfig, cacheDir string, clientOpts ...ClientOption) ([]*Client, error)

RegisterCachedPluginVersion loads a specific version of a cached plugin into the registry. Returns an error if that exact version is not cached. The name should be the cache name (e.g. "aws-provider").

func RegisterFetchedPlugins added in v0.5.0

func RegisterFetchedPlugins(ctx context.Context, registry *provider.Registry, results []FetchResult, cfg *ProviderConfig, clientOpts ...ClientOption) ([]*Client, error)

RegisterFetchedPlugins loads and registers fetched plugin binaries into the provider registry. Unlike RegisterPluginProviders (which discovers plugins from directories), this loads specific binaries by path. Returns the created clients (caller should Kill() them on cleanup).

func RegisterPluginProviders

func RegisterPluginProviders(ctx context.Context, registry *provider.Registry, pluginDirs []string, cfg *ProviderConfig, clientOpts ...ClientOption) ([]*Client, error)

RegisterPluginProviders discovers plugins and registers them with the provider registry. After registration, each wrapper is configured with the provided ProviderConfig. If cfg is nil, configuration is skipped (providers will use defaults). Returns the created clients; the caller should defer calling KillAll(clients) to clean up plugin processes on exit.

func (*Client) ConfigureProvider added in v0.8.0

func (c *Client) ConfigureProvider(ctx context.Context, providerName string, cfg ProviderConfig) error

ConfigureProvider sends host-side configuration to a named provider.

func (*Client) DescribeWhatIf added in v0.6.0

func (c *Client) DescribeWhatIf(ctx context.Context, providerName string, input map[string]any) (string, error)

DescribeWhatIf returns a human-readable description of what the provider would do

func (*Client) ExecuteProvider

func (c *Client) ExecuteProvider(ctx context.Context, providerName string, input map[string]any) (*provider.Output, error)

ExecuteProvider executes a provider with the given input

func (*Client) ExecuteProviderStream added in v0.8.0

func (c *Client) ExecuteProviderStream(ctx context.Context, providerName string, input map[string]any, cb func(StreamChunk)) error

ExecuteProviderStream executes a provider with streaming output.

func (*Client) ExtractDependencies added in v0.8.0

func (c *Client) ExtractDependencies(ctx context.Context, providerName string, inputs map[string]any) ([]string, error)

ExtractDependencies returns resolver dependency names from the provider's inputs.

func (*Client) GetProviderDescriptor

func (c *Client) GetProviderDescriptor(ctx context.Context, providerName string) (*provider.Descriptor, error)

GetProviderDescriptor returns metadata for a specific provider. The result is cached for the lifetime of the client.

Note: concurrent callers for the same uncached provider may both issue an RPC (classic check-then-act). This is benign because descriptors are immutable — the second writer simply overwrites with an identical value.

func (*Client) GetProviders

func (c *Client) GetProviders(ctx context.Context) ([]string, error)

GetProviders returns all provider names exposed by this plugin

func (*Client) Kill

func (c *Client) Kill()

Kill terminates the plugin process

func (*Client) Name

func (c *Client) Name() string

Name returns the plugin name

func (*Client) Path

func (c *Client) Path() string

Path returns the plugin path

type ClientOption added in v0.8.0

type ClientOption func(*clientOptions)

ClientOption configures plugin client creation.

func AuthClientOptsFromContext added in v0.15.0

func AuthClientOptsFromContext(ctx context.Context) []ClientOption

AuthClientOptsFromContext extracts the auth registry from ctx and returns ClientOption(s) that wire host-side auth deps into plugin clients. Returns nil when no auth registry is available in the context.

func WithDebugLogging added in v0.14.0

func WithDebugLogging() ClientOption

WithDebugLogging enables plugin lifecycle debug traces (plugin started, RPC address, protocol negotiation) in the hclog output. Pass this option when --debug or --log-level debug is active so plugin internals are visible. Without it a null logger is used and no plugin noise appears.

func WithGRPCMaxMessageSize added in v0.17.0

func WithGRPCMaxMessageSize(size int) ClientOption

WithGRPCMaxMessageSize sets the maximum gRPC message size in bytes for plugin communication. This applies to both send and receive directions. If not set, defaults to settings.DefaultGRPCMaxMessageSize (64 MB).

func WithHostDeps added in v0.8.0

func WithHostDeps(deps *HostServiceDeps) ClientOption

WithHostDeps provides host-side dependencies (secrets, auth) that are exposed to the plugin via the HostService gRPC callback server.

func WithSanitizedEnv added in v0.14.0

func WithSanitizedEnv() ClientOption

WithSanitizedEnv restricts the environment variables passed to the plugin process. Only safe variables (PATH, HOME, TMPDIR, etc.) are inherited. Use this in API server contexts to prevent leaking secrets to plugins.

func WithStartTimeout added in v0.14.0

func WithStartTimeout(d time.Duration) ClientOption

WithStartTimeout bounds the plugin startup/handshake phase. If the plugin binary does not complete the gRPC handshake within this duration, the connection attempt fails and the process is killed.

type DescriptorCache added in v0.17.0

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

DescriptorCache persists provider descriptors as JSON files so that schemas are available without spawning plugin processes. Each entry is keyed by provider name and expires after a configurable TTL.

Cache layout:

<dir>/<name>.json

func NewDescriptorCache added in v0.17.0

func NewDescriptorCache(dir string, ttl time.Duration) *DescriptorCache

NewDescriptorCache creates a cache backed by the given directory. If dir is empty, the default XDG path (paths.ProviderSchemaCacheDir()) is used. TTL controls how long entries are considered fresh; zero means entries never expire.

func (*DescriptorCache) Dir added in v0.17.0

func (c *DescriptorCache) Dir() string

Dir returns the cache directory path.

func (*DescriptorCache) Get added in v0.17.0

func (c *DescriptorCache) Get(name string) *provider.Descriptor

Get retrieves a cached descriptor by provider name. Returns nil if the entry does not exist, has expired, or is invalid.

func (*DescriptorCache) Invalidate added in v0.17.0

func (c *DescriptorCache) Invalidate(name string)

Invalidate removes a cached entry by provider name.

func (*DescriptorCache) InvalidateAll added in v0.17.0

func (c *DescriptorCache) InvalidateAll()

InvalidateAll removes all cached entries.

func (*DescriptorCache) Put added in v0.17.0

func (c *DescriptorCache) Put(name string, desc provider.Descriptor) error

Put stores a descriptor in the cache.

type DeviceCodePrompt added in v0.5.0

type DeviceCodePrompt = sdkplugin.DeviceCodePrompt

DeviceCodePrompt is sent over streaming Login to relay device-code info to the host.

type FetchCooldown added in v0.14.0

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

FetchCooldown manages cooldown state for failed plugin auto-fetch attempts. It uses file-based markers in the plugin cache directory to persist cooldown state across CLI invocations.

func NewFetchCooldown added in v0.14.0

func NewFetchCooldown(cacheDir string, cooldown time.Duration) *FetchCooldown

NewFetchCooldown creates a FetchCooldown with the given cooldown duration. If cacheDir is empty, the default plugin cache directory is used. If cooldown is zero, DefaultFetchCooldown is used.

func (*FetchCooldown) Clear added in v0.14.0

func (fc *FetchCooldown) Clear(name string) error

Clear removes the cooldown marker for the named plugin. This is called on explicit install (e.g., `scafctl plugin install`) or with --force-fetch.

func (*FetchCooldown) OnCooldown added in v0.14.0

func (fc *FetchCooldown) OnCooldown(name string) bool

OnCooldown reports whether a fetch attempt for the named plugin should be suppressed due to a recent failure. Returns true if a cooldown marker exists and has not expired.

func (*FetchCooldown) RecordFailure added in v0.14.0

func (fc *FetchCooldown) RecordFailure(name string) error

RecordFailure writes a cooldown marker for the named plugin, indicating that a fetch attempt failed at the current time.

type FetchResult added in v0.5.0

type FetchResult struct {
	// Name is the plugin name.
	Name string

	// Kind is the plugin kind.
	Kind solution.PluginKind

	// Version is the resolved version.
	Version string

	// Path is the local filesystem path to the binary.
	Path string

	// Digest is the content digest.
	Digest string

	// FromCache indicates whether the binary was served from cache.
	FromCache bool

	// Signature holds signature verification metadata when verification
	// was performed. Nil when signatures are disabled or the binary was cached.
	//
	// TODO: surface Signature in CLI output/audit log so users can inspect
	// verification results after a fetch.
	Signature *SignatureResult

	// Catalog is the catalog name the plugin was fetched from (empty if cached).
	Catalog string

	// Release releases the pin on the cached binary. Must be called when the
	// binary is no longer in use. Nil when pinning is not active (CLI mode).
	Release func()
}

FetchResult contains the result of fetching a single plugin.

type Fetcher added in v0.5.0

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

Fetcher resolves, downloads, caches, and loads plugin binaries at runtime. It checks a local cache first, then falls back to fetching from catalogs.

func NewFetcher added in v0.5.0

func NewFetcher(cfg FetcherConfig) *Fetcher

NewFetcher creates a new Fetcher.

func (*Fetcher) FetchPlugins added in v0.5.0

func (f *Fetcher) FetchPlugins(ctx context.Context, plugins []solution.PluginDependency, lockPlugins []bundler.LockPlugin) ([]FetchResult, error)

FetchPlugins resolves and downloads plugin binaries for all declared dependencies. It checks the local cache first, uses lock file entries for pinned versions when available, and falls back to catalog resolution.

When a plugin is resolved without a lock file entry, a warning is logged about potential reproducibility issues.

Returns a list of FetchResult with local binary paths, suitable for passing to RegisterPluginProviders.

type FetcherConfig added in v0.5.0

type FetcherConfig struct {
	// Catalog is the catalog (or chain) to fetch plugins from.
	Catalog catalog.Catalog

	// Cache is the local plugin binary cache. If nil, a default cache is created.
	Cache *Cache

	// Platform overrides the target platform. If empty, CurrentPlatform() is used.
	Platform string

	// NoCache bypasses the local cache, forcing a fresh fetch from the catalog.
	// Cached binaries are still written after fetch (the cache is populated but not read).
	NoCache bool

	// BinaryName is the CLI binary name used in user-facing messages (e.g.,
	// "Run 'mycli build solution' to pin..."). Defaults to "scafctl" when empty.
	BinaryName string

	// AllowedCatalogs restricts which catalog names plugins may be fetched
	// from. If empty, all catalogs are allowed.
	AllowedCatalogs []string

	// SignaturePolicy configures Sigstore/cosign signature verification.
	// When nil or Mode is "off", no signature verification is performed.
	SignaturePolicy *SignaturePolicy

	// SignatureVerifier is the implementation used for OCI signature checks.
	// When nil, NewSignatureVerifier() is used (cosign if built with the
	// "cosign" tag, otherwise a no-op stub).
	SignatureVerifier SignatureVerifier

	// Logger for logging operations.
	Logger logr.Logger
}

FetcherConfig configures a Fetcher.

type FlowAvailability added in v0.15.0

type FlowAvailability = sdkplugin.FlowAvailability

FlowAvailability reports whether a specific auth flow is available based on environment credentials or configuration.

type GRPCClient

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

GRPCClient implements the gRPC client for the plugin

func (*GRPCClient) ConfigureProvider added in v0.8.0

func (c *GRPCClient) ConfigureProvider(ctx context.Context, providerName string, cfg ProviderConfig) error

ConfigureProvider implements ProviderPlugin.ConfigureProvider

func (*GRPCClient) DescribeWhatIf added in v0.6.0

func (c *GRPCClient) DescribeWhatIf(ctx context.Context, providerName string, input map[string]any) (string, error)

DescribeWhatIf implements ProviderPlugin.DescribeWhatIf

func (*GRPCClient) ExecuteProvider

func (c *GRPCClient) ExecuteProvider(ctx context.Context, providerName string, input map[string]any) (*provider.Output, error)

ExecuteProvider implements ProviderPlugin.ExecuteProvider

func (*GRPCClient) ExecuteProviderStream added in v0.8.0

func (c *GRPCClient) ExecuteProviderStream(ctx context.Context, providerName string, input map[string]any, cb func(StreamChunk)) error

ExecuteProviderStream implements ProviderPlugin.ExecuteProviderStream

func (*GRPCClient) ExtractDependencies added in v0.8.0

func (c *GRPCClient) ExtractDependencies(ctx context.Context, providerName string, inputs map[string]any) ([]string, error)

ExtractDependencies implements ProviderPlugin.ExtractDependencies

func (*GRPCClient) GetProviderDescriptor

func (c *GRPCClient) GetProviderDescriptor(ctx context.Context, providerName string) (*provider.Descriptor, error)

GetProviderDescriptor implements ProviderPlugin.GetProviderDescriptor

func (*GRPCClient) GetProviders

func (c *GRPCClient) GetProviders(ctx context.Context) ([]string, error)

GetProviders implements ProviderPlugin.GetProviders

func (*GRPCClient) HostServiceID added in v0.8.0

func (c *GRPCClient) HostServiceID() uint32

HostServiceID returns the broker service ID of the HostService callback server. Returns 0 if no HostService was registered.

func (*GRPCClient) StopProvider added in v0.8.0

func (c *GRPCClient) StopProvider(ctx context.Context, providerName string) error

StopProvider implements ProviderPlugin.StopProvider

type GRPCPlugin

type GRPCPlugin struct {
	plugin.Plugin
	Impl ProviderPlugin
	// HostDeps holds host-side dependencies for the HostService callback server.
	// Set by the host before starting the plugin. Nil on the plugin side.
	HostDeps *HostServiceDeps
}

GRPCPlugin implements plugin.GRPCPlugin from hashicorp/go-plugin

func (*GRPCPlugin) GRPCClient

func (p *GRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (any, error)

GRPCClient returns the gRPC client (host side). If HostDeps is non-nil, a HostService gRPC server is started via the broker and the returned GRPCClient holds the broker service ID for later ConfigureProvider calls.

func (*GRPCPlugin) GRPCServer

func (p *GRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error

GRPCServer registers the gRPC server (plugin side). When HostDeps is set, a HostService callback server is started on the host via the broker and its service ID is stored so that ConfigureProvider can relay it.

type GRPCServer

type GRPCServer struct {
	proto.UnimplementedPluginServiceServer
	Impl ProviderPlugin
	// contains filtered or unexported fields
}

GRPCServer implements the gRPC server for the plugin

func (*GRPCServer) ConfigureProvider added in v0.8.0

ConfigureProvider implements the ConfigureProvider RPC

func (*GRPCServer) DescribeWhatIf added in v0.6.0

DescribeWhatIf implements the DescribeWhatIf RPC

func (*GRPCServer) ExecuteProvider

ExecuteProvider implements the ExecuteProvider RPC

func (*GRPCServer) ExecuteProviderStream added in v0.8.0

ExecuteProviderStream implements the streaming ExecuteProvider RPC

func (*GRPCServer) ExtractDependencies added in v0.8.0

ExtractDependencies implements the ExtractDependencies RPC

func (*GRPCServer) GetProviderDescriptor

GetProviderDescriptor implements the GetProviderDescriptor RPC

func (*GRPCServer) GetProviders

GetProviders implements the GetProviders RPC

func (*GRPCServer) StopProvider added in v0.8.0

StopProvider implements the StopProvider RPC

type HandshakeConfigData

type HandshakeConfigData = sdkplugin.HandshakeConfigData

HandshakeConfigData contains the handshake configuration.

type HostServiceClient added in v0.8.0

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

HostServiceClient wraps the HostService gRPC client (used by plugins). Plugin code uses this to call back into the host process.

func NewHostServiceClient added in v0.8.0

func NewHostServiceClient(conn *grpc.ClientConn) *HostServiceClient

NewHostServiceClient creates a HostServiceClient from a gRPC connection.

func (*HostServiceClient) DeleteSecret added in v0.8.0

func (c *HostServiceClient) DeleteSecret(ctx context.Context, name string) error

DeleteSecret removes a secret from the host's secret store.

func (*HostServiceClient) GetAuthGroups added in v0.14.0

func (c *HostServiceClient) GetAuthGroups(ctx context.Context, handler string) ([]string, error)

GetAuthGroups retrieves group memberships for the authenticated user from the host's auth registry.

func (*HostServiceClient) GetAuthIdentity added in v0.8.0

func (c *HostServiceClient) GetAuthIdentity(ctx context.Context, handler, scope string) (*proto.Claims, error)

GetAuthIdentity retrieves identity claims from the host's auth registry.

func (*HostServiceClient) GetAuthToken added in v0.8.0

func (c *HostServiceClient) GetAuthToken(ctx context.Context, handler, scope string, minValidFor int64, forceRefresh bool) (*proto.GetAuthTokenResponse, error)

GetAuthToken retrieves a valid access token from the host's auth registry.

func (*HostServiceClient) GetSecret added in v0.8.0

func (c *HostServiceClient) GetSecret(ctx context.Context, name string) (string, bool, error)

GetSecret retrieves a secret from the host's secret store.

func (*HostServiceClient) ListAuthHandlers added in v0.8.0

func (c *HostServiceClient) ListAuthHandlers(ctx context.Context) (handlers []string, defaultHandler string, err error)

ListAuthHandlers lists available auth handlers on the host.

func (*HostServiceClient) ListSecrets added in v0.8.0

func (c *HostServiceClient) ListSecrets(ctx context.Context, pattern string) ([]string, error)

ListSecrets lists secret names from the host's secret store.

func (*HostServiceClient) SetSecret added in v0.8.0

func (c *HostServiceClient) SetSecret(ctx context.Context, name, value string) error

SetSecret stores a secret in the host's secret store.

type HostServiceDeps added in v0.8.0

type HostServiceDeps struct {
	// SecretStore provides access to the host's secret store (may be nil).
	SecretStore secrets.Store `json:"-" yaml:"-" doc:"Host secret store (not serialized)."`
	// AllowedSecretPrefix restricts secret access to names starting with this prefix.
	// If empty, all secrets are accessible (for backward compatibility).
	AllowedSecretPrefix string `json:"allowedSecretPrefix,omitempty" yaml:"allowedSecretPrefix,omitempty" doc:"Prefix restriction for secret access."`
	// AuthIdentityFunc returns claims for a named auth handler and scope.
	// handler may be empty for the default handler. May be nil if auth is unavailable.
	AuthIdentityFunc func(ctx context.Context, handler, scope string) (*proto.Claims, error) `json:"-" yaml:"-" doc:"Auth identity callback (not serialized)."`
	// AllowedAuthHandlers restricts which auth handler names a plugin may request.
	// If nil or empty, all handlers are allowed (for backward compatibility).
	AllowedAuthHandlers []string `json:"allowedAuthHandlers,omitempty" yaml:"allowedAuthHandlers,omitempty" doc:"Allowed auth handler names."`
	// AuthHandlersFunc returns available auth handler names and the default handler.
	// May be nil if auth is unavailable.
	AuthHandlersFunc func(ctx context.Context) (handlers []string, defaultHandler string, err error) `json:"-" yaml:"-" doc:"Auth handlers callback (not serialized)."`
	// AuthTokenFunc retrieves a valid access token from the host's auth registry.
	// Plugins call this for authenticated HTTP requests and token refresh on 401.
	// May be nil if auth is unavailable.
	AuthTokenFunc func(ctx context.Context, handler, scope string, minValidFor int64, forceRefresh bool) (*proto.GetAuthTokenResponse, error) `json:"-" yaml:"-" doc:"Auth token callback (not serialized)."`
	// AuthGroupsFunc retrieves group memberships for the authenticated user.
	// Only handlers that implement group queries (e.g. Entra) will return results.
	// May be nil if group queries are unavailable.
	// TODO: Wire this callback in production code (e.g. via RootOptions or plugin SDK)
	// once an auth handler with group query support is available.
	AuthGroupsFunc func(ctx context.Context, handler string) ([]string, error) `json:"-" yaml:"-" doc:"Auth groups callback (not serialized)."`
	// ProfileResolverFunc resolves the active auth profile for a handler when
	// the plugin request does not specify one explicitly. This bridges the gap
	// between the host's --auth-profile flag / config activeProfile and plugin
	// providers that call back for auth tokens.
	// May be nil — in which case no profile resolution is performed.
	ProfileResolverFunc func(handlerName string) string `json:"-" yaml:"-" doc:"Profile resolver callback (not serialized)."`
}

HostServiceDeps holds the host-side dependencies that the HostService exposes to plugins via gRPC callbacks. This is an internal dependency-injection struct passed by value into HostServiceServer; fields are not serialized.

func HostDepsFromAuthRegistry added in v0.15.0

func HostDepsFromAuthRegistry(authReg *auth.Registry) *HostServiceDeps

HostDepsFromAuthRegistry builds a HostServiceDeps that delegates auth callbacks to the given auth.Registry. Returns nil when the registry is nil, so callers can safely pass the result to WithHostDeps without a nil check.

type HostServiceServer added in v0.8.0

type HostServiceServer struct {
	proto.UnimplementedHostServiceServer
	Deps HostServiceDeps
}

HostServiceServer implements the HostService gRPC server (runs on the host). Plugins call these RPCs back into the host process via the go-plugin GRPCBroker.

func (*HostServiceServer) DeleteSecret added in v0.8.0

DeleteSecret implements HostService.DeleteSecret

func (*HostServiceServer) GetAuthGroups added in v0.14.0

GetAuthGroups implements HostService.GetAuthGroups

func (*HostServiceServer) GetAuthIdentity added in v0.8.0

GetAuthIdentity implements HostService.GetAuthIdentity

func (*HostServiceServer) GetAuthToken added in v0.8.0

GetAuthToken implements HostService.GetAuthToken

func (*HostServiceServer) GetSecret added in v0.8.0

GetSecret implements HostService.GetSecret

func (*HostServiceServer) ListAuthHandlers added in v0.8.0

ListAuthHandlers implements HostService.ListAuthHandlers

func (*HostServiceServer) ListSecrets added in v0.8.0

ListSecrets implements HostService.ListSecrets

func (*HostServiceServer) SetSecret added in v0.8.0

SetSecret implements HostService.SetSecret

type LazyAuthHandlerConfig added in v0.17.0

type LazyAuthHandlerConfig struct {
	// Name is the handler name (e.g., "entra").
	Name string

	// BinPath is the absolute path to the cached plugin binary.
	BinPath string

	// PluginCfg is passed to ConfigureAuthHandler after startup.
	PluginCfg *ProviderConfig

	// ClientOpts are options for the plugin client (e.g., host deps).
	ClientOpts []ClientOption

	// OfficialRegistry provides trusted domains for the handler.
	OfficialRegistry *authofficial.Registry
}

LazyAuthHandlerConfig holds parameters needed to construct a lazy wrapper.

type LazyAuthHandlerWrapper added in v0.17.0

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

LazyAuthHandlerWrapper implements auth.Handler by deferring plugin subprocess startup until the first method that requires it. Name() and DisplayName() are served from static metadata, so registry.List() and registry.Has() work without any I/O.

func NewLazyAuthHandlerWrapper added in v0.17.0

func NewLazyAuthHandlerWrapper(cfg LazyAuthHandlerConfig) *LazyAuthHandlerWrapper

NewLazyAuthHandlerWrapper creates a lazy auth handler that defers plugin startup until a method requiring the plugin is called.

func (*LazyAuthHandlerWrapper) ActivateServerMode added in v0.33.0

func (l *LazyAuthHandlerWrapper) ActivateServerMode(ctx context.Context, settings json.RawMessage) error

func (*LazyAuthHandlerWrapper) ApplyOverrides added in v0.17.0

func (l *LazyAuthHandlerWrapper) ApplyOverrides(ctx context.Context, overrides map[string]string) error

ApplyOverrides implements auth.Configurer. Triggers plugin initialization if needed, then delegates to the wrapper.

func (*LazyAuthHandlerWrapper) Capabilities added in v0.17.0

func (l *LazyAuthHandlerWrapper) Capabilities() []auth.Capability

Capabilities implements auth.Handler. This triggers plugin initialization because capabilities determine which flags and validation rules apply (e.g., CapScopesOnTokenRequest controls whether --scope is accepted by 'auth token').

func (*LazyAuthHandlerWrapper) Client added in v0.17.0

Client returns the underlying AuthHandlerClient, or nil if not yet initialized.

func (*LazyAuthHandlerWrapper) DetectAvailableFlows added in v0.17.0

func (l *LazyAuthHandlerWrapper) DetectAvailableFlows(ctx context.Context) ([]auth.FlowAvailability, error)

DetectAvailableFlows implements auth.FlowDetector.

func (*LazyAuthHandlerWrapper) DisplayName added in v0.17.0

func (l *LazyAuthHandlerWrapper) DisplayName() string

DisplayName implements auth.Handler.

func (*LazyAuthHandlerWrapper) GetToken added in v0.17.0

GetToken implements auth.Handler.

func (*LazyAuthHandlerWrapper) InjectAuth added in v0.17.0

func (l *LazyAuthHandlerWrapper) InjectAuth(ctx context.Context, req *http.Request, opts auth.TokenOptions) error

InjectAuth implements auth.Handler.

func (*LazyAuthHandlerWrapper) IsInitialized added in v0.17.0

func (l *LazyAuthHandlerWrapper) IsInitialized() bool

IsInitialized reports whether the plugin subprocess has been started.

func (*LazyAuthHandlerWrapper) ListCachedTokens added in v0.17.0

func (l *LazyAuthHandlerWrapper) ListCachedTokens(ctx context.Context) ([]*auth.CachedTokenInfo, error)

ListCachedTokens implements auth.TokenLister.

func (*LazyAuthHandlerWrapper) Login added in v0.17.0

Login implements auth.Handler.

func (*LazyAuthHandlerWrapper) Logout added in v0.17.0

Logout implements auth.Handler.

func (*LazyAuthHandlerWrapper) Name added in v0.17.0

func (l *LazyAuthHandlerWrapper) Name() string

Name implements auth.Handler.

func (*LazyAuthHandlerWrapper) PurgeExpiredTokens added in v0.17.0

func (l *LazyAuthHandlerWrapper) PurgeExpiredTokens(ctx context.Context) (int, error)

PurgeExpiredTokens implements auth.TokenPurger.

func (*LazyAuthHandlerWrapper) SetContext added in v0.17.0

func (l *LazyAuthHandlerWrapper) SetContext(ctx context.Context)

SetContext stores a wired application context for later use by metadata methods (SupportedFlows, Capabilities) that don't receive a context parameter. Only the first call takes effect.

func (*LazyAuthHandlerWrapper) Status added in v0.17.0

Status implements auth.Handler.

func (*LazyAuthHandlerWrapper) SupportedFlows added in v0.17.0

func (l *LazyAuthHandlerWrapper) SupportedFlows() []auth.Flow

SupportedFlows implements auth.Handler. This triggers plugin initialization because flow information is needed for command validation (e.g., determining which login flags to show).

type LoginRequest added in v0.5.0

type LoginRequest = sdkplugin.LoginRequest

LoginRequest contains parameters for a plugin Login call.

type LoginResponse added in v0.5.0

type LoginResponse = sdkplugin.LoginResponse

LoginResponse contains the result of a plugin Login call.

type LoginStreamMessage added in v0.5.0

type LoginStreamMessage = sdkplugin.LoginStreamMessage

LoginStreamMessage represents a message in the Login server-stream.

type LogoutRequest added in v0.32.0

type LogoutRequest = sdkplugin.LogoutRequest

LogoutRequest contains parameters for a plugin Logout call.

type Pool added in v0.14.0

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

Pool manages shared, long-lived plugin processes with lazy initialization and idle eviction. It is safe for concurrent use.

Official providers pre-loaded at startup can be added via Adopt; external plugins declared in bundle.plugins are loaded on-demand via Ensure.

The pool owns a lifetime context (derived from the context passed to NewPool). All long-lived plugin setup -- fetching binaries, starting processes, registering provider wrappers, and configuring them -- runs under this context, NOT the per-request context that happened to trigger the lazy load. This ensures a registered wrapper is never backed by a client whose initialization context died with a transient request. Per-request contexts scope only per-operation Execute/ExecuteStream calls.

func NewPool added in v0.14.0

func NewPool(ctx context.Context, fetcher *Fetcher, registry *provider.Registry, logger logr.Logger, opts ...PoolOption) *Pool

NewPool creates a plugin pool backed by the given fetcher and registry. The fetcher may be nil if only Adopt (pre-loaded) plugins are used.

ctx is the pool-lifetime context: it governs the background eviction loop and all long-lived plugin initialization. Pass a long-lived context (e.g. the server context) so plugin loads survive individual requests; the pool cancels its derived child context on Shutdown. A nil ctx defaults to context.Background().

func (*Pool) Acquire added in v0.14.0

func (p *Pool) Acquire(name string) bool

Acquire increments the reference count for a plugin, preventing eviction. Returns false if the plugin is not in the pool or is dead.

func (*Pool) Adopt added in v0.14.0

func (p *Pool) Adopt(name string, client *Client, dep solution.PluginDependency, registeredProviders []string)

Adopt registers an already-running plugin client into the pool so that its lifecycle (idle eviction, shutdown) is managed by the pool. This is used for official providers pre-loaded at startup.

func (*Pool) ClientOptsLen added in v0.15.0

func (p *Pool) ClientOptsLen() int

ClientOptsLen returns the number of extra client options configured on the pool. This is primarily useful for testing that WithClientOptions was wired correctly.

func (*Pool) Ensure added in v0.14.0

func (p *Pool) Ensure(ctx context.Context, deps []solution.PluginDependency) error

Ensure guarantees that all plugins in deps are running and registered in the provider registry. For plugins already in the pool and healthy, this is a no-op. For new plugins, it fetches, spawns, and registers them.

func (*Pool) EnsureAndAcquire added in v0.14.0

func (p *Pool) EnsureAndAcquire(ctx context.Context, deps []solution.PluginDependency) (release func(), err error)

EnsureAndAcquire ensures all plugins are available and acquires them (increments their refcounts), preventing idle eviction for the duration of the caller's work. Returns a release function that must be called when the caller is done using the plugins (typically via defer).

Acquisition happens atomically with readiness validation (under the entry lock), closing the window where a just-readied plugin could be evicted or torn down before the caller records its reference.

func (*Pool) Ping added in v0.14.0

func (p *Pool) Ping(ctx context.Context, name string) bool

Ping checks if a plugin is alive by issuing a lightweight RPC.

func (*Pool) Release added in v0.14.0

func (p *Pool) Release(name string)

Release decrements the reference count for a plugin. If this drains the last reference on an entry whose teardown was deferred (pendingKill), the client is killed now.

func (*Pool) SanitizeEnv added in v0.15.0

func (p *Pool) SanitizeEnv() bool

SanitizeEnv reports whether this pool sanitizes the environment for spawned plugin clients. See WithSanitizeEnv.

func (*Pool) Shutdown added in v0.14.0

func (p *Pool) Shutdown()

Shutdown kills all managed plugin processes. Called once on server stop.

func (*Pool) Stats added in v0.14.0

func (p *Pool) Stats() PoolStats

Stats returns current pool metrics.

type PoolOption added in v0.14.0

type PoolOption func(*poolOptions)

PoolOption configures pool behavior.

func WithAllowedPlugins added in v0.14.0

func WithAllowedPlugins(names []string) PoolOption

WithAllowedPlugins restricts which external plugins may be loaded via Ensure. Plugins not in this list are rejected with ErrPluginNotAllowed. A nil or empty list means all plugins are allowed (no restriction). Adopted (pre-loaded) plugins bypass this check.

func WithClientOptions added in v0.15.0

func WithClientOptions(opts ...ClientOption) PoolOption

WithClientOptions sets additional ClientOption values that are passed to every plugin client spawned by the pool. Use this to inject host-side dependencies such as auth registries (via WithHostDeps).

func WithDisableExternal added in v0.14.0

func WithDisableExternal(disabled bool) PoolOption

WithDisableExternal rejects all plugin load attempts via Ensure. Only pre-loaded (Adopt) plugins are usable. This is the safe default for API server deployments.

func WithHealthCheckInterval added in v0.14.0

func WithHealthCheckInterval(d time.Duration) PoolOption

WithHealthCheckInterval sets the background health check frequency. Zero disables background checks (health is verified on use).

NOTE: Background health checks are not yet implemented. This option stores the interval for future use. Currently, dead plugins are only detected when a caller invokes Ping or when a request fails.

func WithIdleTimeout added in v0.14.0

func WithIdleTimeout(d time.Duration) PoolOption

WithIdleTimeout sets how long an unused plugin stays alive before eviction. Zero disables idle eviction.

func WithMaxPlugins added in v0.14.0

func WithMaxPlugins(n int) PoolOption

WithMaxPlugins sets the maximum number of external plugin processes. Zero means unlimited.

func WithSanitizeEnv added in v0.15.0

func WithSanitizeEnv(sanitize bool) PoolOption

WithSanitizeEnv controls whether spawned plugin clients get a sanitized environment (true) or inherit the host environment (false). Defaults to true. API server deployments should use true; MCP interactive sessions may use false so plugins can access host credentials (SSH_AUTH_SOCK, tokens, etc.).

func WithSpawnTimeout added in v0.29.0

func WithSpawnTimeout(d time.Duration) PoolOption

WithSpawnTimeout bounds the entire spawn sequence for a single plugin -- fetch, process start, gRPC handshake, provider registration, and configure. Spawns run under the pool-lifetime context (not the caller's request context), so this timeout is the only bound on how long a lazy load may take. Zero disables the bound (spawn is limited only by pool shutdown).

type PoolStats added in v0.14.0

type PoolStats struct {
	Active  int // Entries with refCount > 0
	Idle    int // Entries in ready state with refCount == 0
	Dead    int // Entries that crashed and await eviction or re-spawn
	Total   int // All entries
	Evicted int // Cumulative evictions since pool creation
}

PoolStats holds pool metrics.

type ProviderConfig added in v0.8.0

type ProviderConfig = sdkplugin.ProviderConfig

ProviderConfig holds host-side configuration sent to a provider once after plugin load via the ConfigureProvider RPC.

type ProviderPlugin

type ProviderPlugin = sdkplugin.ProviderPlugin

ProviderPlugin is the interface that plugins must implement.

type ProviderWrapper

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

ProviderWrapper wraps a plugin provider to implement the provider.Provider interface

func NewProviderWrapper

func NewProviderWrapper(client *Client, providerName string, opts ...WrapperOption) (*ProviderWrapper, error)

NewProviderWrapper creates a new provider wrapper for a plugin provider

func (*ProviderWrapper) Client

func (w *ProviderWrapper) Client() *Client

Client returns the underlying plugin client

func (*ProviderWrapper) Configure added in v0.8.0

func (w *ProviderWrapper) Configure(ctx context.Context, cfg ProviderConfig) error

Configure sends host-side configuration to the plugin provider. This should be called once after wrapper creation, before any Execute calls.

func (*ProviderWrapper) Descriptor

func (w *ProviderWrapper) Descriptor() *provider.Descriptor

Descriptor returns the provider descriptor

func (*ProviderWrapper) Execute

func (w *ProviderWrapper) Execute(ctx context.Context, input any) (*provider.Output, error)

Execute executes the provider. When IOStreams are present in the context, it attempts to use streaming execution so that incremental output (e.g. from exec-like providers) reaches the terminal in real time. Falls back to the unary RPC when the plugin does not support streaming.

Streaming error contract: the GRPCClient.ExecuteProviderStream callback receives stdout/stderr chunks as they arrive. Terminal errors are delivered in two ways: (1) the callback receives a chunk with Error set, and (2) ExecuteProviderStream returns a non-nil error. This wrapper checks the return value first — if it signals ErrStreamingNotSupported it falls back to unary; for other errors it returns immediately. When the stream completes successfully (nil return), any chunk-level error is surfaced.

func (*ProviderWrapper) ExecuteStream added in v0.8.0

func (w *ProviderWrapper) ExecuteStream(ctx context.Context, input any, cb func(StreamChunk)) error

ExecuteStream executes the provider with streaming output. The callback receives chunks as they arrive from the plugin.

type PruneOptions added in v0.17.0

type PruneOptions struct {
	// Keep is the number of most recent versions to retain per plugin.
	// Defaults to 1 if <= 0.
	Keep int

	// Names limits pruning to the specified plugin names.
	// If empty, all cached plugins are pruned.
	Names []string

	// Platform limits pruning to the specified platform.
	// If empty, all platforms are pruned.
	Platform string

	// All removes all cached plugins (requires Force).
	All bool

	// Force skips confirmation for destructive operations.
	Force bool
}

PruneOptions configures a prune operation.

type PruneResult added in v0.17.0

type PruneResult struct {
	Name         string `json:"name" yaml:"name" doc:"Plugin name"`
	Version      string `json:"version" yaml:"version" doc:"Removed version"`
	Platform     string `json:"platform" yaml:"platform" doc:"Platform"`
	Path         string `json:"path" yaml:"path" doc:"Removed path"`
	Size         int64  `json:"size" yaml:"size" doc:"Freed bytes"`
	RegistryHash string `json:"registryHash,omitempty" yaml:"registryHash,omitempty" doc:"Registry hash directory"`
}

PruneResult describes the outcome of a prune operation for a single plugin version.

type PruneSkipped added in v0.17.0

type PruneSkipped struct {
	Name     string `json:"name" yaml:"name" doc:"Plugin name"`
	Version  string `json:"version,omitempty" yaml:"version,omitempty" doc:"Skipped version"`
	Platform string `json:"platform,omitempty" yaml:"platform,omitempty" doc:"Platform"`
	Reason   string `json:"reason" yaml:"reason" doc:"Why removal failed"`
}

PruneSkipped describes an entry that could not be removed.

type PruneSummary added in v0.17.0

type PruneSummary struct {
	Removed    []PruneResult  `json:"removed" yaml:"removed" doc:"Removed entries"`
	Skipped    []PruneSkipped `json:"skipped,omitempty" yaml:"skipped,omitempty" doc:"Entries that could not be removed"`
	TotalFreed int64          `json:"totalFreed" yaml:"totalFreed" doc:"Total bytes freed"`
}

PruneSummary holds the full result of a prune operation.

type SignatureMode added in v0.15.0

type SignatureMode string

SignatureMode controls how signature verification failures are handled.

const (
	// SignatureModeOff disables signature verification entirely (digest-only).
	SignatureModeOff SignatureMode = "off"

	// SignatureModeWarn verifies signatures but logs a warning on failure
	// instead of blocking execution.
	SignatureModeWarn SignatureMode = "warn"

	// SignatureModeEnforce requires valid signatures; missing or invalid
	// signatures cause a hard failure.
	SignatureModeEnforce SignatureMode = "enforce"
)

func ParseSignatureMode added in v0.15.0

func ParseSignatureMode(s string) (SignatureMode, error)

ParseSignatureMode converts a string to a SignatureMode, returning an error for unrecognized values.

type SignaturePolicy added in v0.15.0

type SignaturePolicy struct {
	// Mode controls behavior on verification failure.
	Mode SignatureMode `json:"mode" yaml:"mode" doc:"Signature verification mode" enum:"off,warn,enforce" example:"warn"`

	// TrustedIssuers are OIDC issuers whose signing certificates are trusted
	// (e.g., "https://token.actions.githubusercontent.com").
	TrustedIssuers []string `json:"trustedIssuers,omitempty" yaml:"trustedIssuers,omitempty" doc:"Trusted OIDC certificate issuers" maxItems:"20"`

	// TrustedIdentities are glob patterns matching the certificate
	// subject/identity (e.g., "https://github.com/oakwood-commons/*").
	TrustedIdentities []string `` /* 130-byte string literal not displayed */
}

SignaturePolicy defines the verification policy for plugin binary signatures.

func SignaturePolicyFromContext added in v0.15.0

func SignaturePolicyFromContext(ctx context.Context) *SignaturePolicy

SignaturePolicyFromContext retrieves the SignaturePolicy from the context. Returns nil if none is set.

func SignaturePolicyFromRaw added in v0.15.0

func SignaturePolicyFromRaw(mode string, trustedIssuers, trustedIdentities []string) (*SignaturePolicy, error)

SignaturePolicyFromRaw constructs a SignaturePolicy from raw configuration values. Returns (nil, nil) when mode is "off" or empty. Returns an error for unrecognized mode values.

func (*SignaturePolicy) IsEnabled added in v0.15.0

func (p *SignaturePolicy) IsEnabled() bool

IsEnabled reports whether signature verification is active (mode != off). An empty Mode is treated as off (consistent with ParseSignatureMode).

func (*SignaturePolicy) Validate added in v0.15.0

func (p *SignaturePolicy) Validate() error

Validate checks the policy for configuration errors. It returns an error when the mode requires verification but no trusted issuer/identity pairs are configured.

type SignatureResult added in v0.15.0

type SignatureResult struct {
	// Verified is true when a valid signature was confirmed.
	Verified bool `json:"verified" yaml:"verified" doc:"Whether a valid signature was confirmed"`

	// Issuer is the OIDC issuer from the signing certificate.
	Issuer string `` /* 147-byte string literal not displayed */

	// Identity is the certificate subject/identity.
	Identity string `` /* 221-byte string literal not displayed */

	// SignedAt is the signature timestamp in RFC 3339 format (empty if unknown).
	SignedAt string `` /* 249-byte string literal not displayed */
}

SignatureResult holds the outcome of a signature verification attempt.

type SignatureVerifier added in v0.15.0

type SignatureVerifier interface {
	// VerifySignature checks the Sigstore/cosign signature for an OCI artifact
	// identified by its image reference (registry/repo@digest).
	// Returns the verification result or an error if verification could not
	// be performed (network failure, missing cosign support, etc.).
	VerifySignature(ctx context.Context, imageRef string, policy *SignaturePolicy) (*SignatureResult, error)
}

SignatureVerifier verifies OCI artifact signatures.

func NewSignatureVerifier added in v0.15.0

func NewSignatureVerifier() SignatureVerifier

NewSignatureVerifier returns a stub verifier when the cosign build tag is not present. The stub returns ErrCosignNotAvailable only when verification is requested (policy is non-nil and enabled).

type StatusRequest added in v0.32.0

type StatusRequest = sdkplugin.StatusRequest

StatusRequest contains parameters for a plugin GetStatus call.

type StreamChunk added in v0.8.0

type StreamChunk = sdkplugin.StreamChunk

StreamChunk represents one chunk from a streaming provider execution.

type TokenRequest added in v0.5.0

type TokenRequest = sdkplugin.TokenRequest

TokenRequest contains parameters for a plugin GetToken call.

type TokenResponse added in v0.5.0

type TokenResponse = sdkplugin.TokenResponse

TokenResponse contains the result of a plugin GetToken call.

type UpdateEntry added in v0.17.0

type UpdateEntry struct {
	Name       string `json:"name" yaml:"name" doc:"Plugin cache key"`
	OldVersion string `json:"oldVersion" yaml:"oldVersion" doc:"Currently cached version"`
	NewVersion string `json:"newVersion" yaml:"newVersion" doc:"Available version"`
	Kind       string `json:"kind" yaml:"kind" doc:"Plugin kind (provider or auth-handler)"`
}

UpdateEntry describes a single pending update.

type UpdateError added in v0.17.0

type UpdateError struct {
	Name  string `json:"name" yaml:"name" doc:"Plugin cache key"`
	Error string `json:"error" yaml:"error" doc:"Error message"`
}

UpdateError describes a resolution failure for a plugin.

type UpdateOptions added in v0.17.0

type UpdateOptions struct {
	// Names limits updates to the specified plugin cache keys.
	// If empty and All is true, all cached plugins are updated.
	Names []string

	// All updates all cached plugins. Required when Names is empty.
	All bool

	// Target specifies the semver constraint boundary.
	Target UpdateTarget

	// Platform overrides the target platform. If empty, CurrentPlatform() is used.
	Platform string
}

UpdateOptions configures an update operation.

type UpdatePlan added in v0.17.0

type UpdatePlan struct {
	Updates  []UpdateEntry `json:"updates" yaml:"updates" doc:"Plugins with available updates"`
	UpToDate []string      `json:"upToDate" yaml:"upToDate" doc:"Plugins already at latest"`
	Failed   []UpdateError `json:"failed,omitempty" yaml:"failed,omitempty" doc:"Plugins that failed resolution"`
}

UpdatePlan describes what an update operation would do.

func PlanUpdates added in v0.17.0

func PlanUpdates(ctx context.Context, cache *Cache, catalogFetcher *catalog.PluginFetcher, opts UpdateOptions) (*UpdatePlan, error)

PlanUpdates checks the catalog for newer versions of cached plugins and returns an UpdatePlan describing what would change.

If catalogFetcher is nil, all plugins that require catalog resolution are reported as failed with a "no remote catalogs configured" error.

type UpdateTarget added in v0.17.0

type UpdateTarget string

UpdateTarget specifies the semver boundary for updates.

const (
	// UpdateTargetLatest allows updates to any newer version.
	UpdateTargetLatest UpdateTarget = "latest"

	// UpdateTargetMinor constrains updates within the same major version (^).
	// For 0.x versions, constrains to same minor (0.x.y only).
	UpdateTargetMinor UpdateTarget = "minor"

	// UpdateTargetPatch constrains updates within the same minor version (~).
	UpdateTargetPatch UpdateTarget = "patch"
)

type WrapperOption added in v0.8.0

type WrapperOption func(*wrapperConfig)

WrapperOption configures NewProviderWrapper.

func WithContext added in v0.8.0

func WithContext(ctx context.Context) WrapperOption

WithContext sets the context used during wrapper initialisation (e.g. for the initial GetProviderDescriptor RPC). Defaults to context.Background(). A nil context is silently replaced with context.Background().

Jump to

Keyboard shortcuts

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