plugin

package
v0.13.0 Latest Latest
Warning

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

Go to latest
Published: Apr 30, 2026 License: Apache-2.0 Imports: 34 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

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 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 ErrStreamingNotSupported = sdkplugin.ErrStreamingNotSupported

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

Functions

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 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 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.

Types

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) 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) 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) ConfigureAuthHandler added in v0.8.0

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

ConfigureAuthHandler implements AuthHandlerPlugin.ConfigureAuthHandler.

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) (*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) 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) 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) 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) 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.

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) Status added in v0.5.0

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

Status implements auth.Handler.

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
}

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

Cache layout:

<cacheDir>/<name>/<version>/<os>-<arch>/<name>

Example:

~/.cache/scafctl/plugins/aws-provider/1.5.3/darwin-arm64/aws-provider

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 (*Cache) Digest added in v0.5.0

func (c *Cache) Digest(name, version, platform string) (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) (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) List added in v0.5.0

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

List returns all cached (name, version, platform) triples.

func (*Cache) Put added in v0.5.0

func (c *Cache) Put(name, version, platform string, data []byte) (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) error

Remove deletes a cached plugin binary.

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"`
}

CachedPlugin describes a cached plugin binary.

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 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 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.

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 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

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

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

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

FetcherConfig configures a Fetcher.

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) 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)."`
}

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.

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) 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 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 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 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 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