Extension Concepts#
scafctl is built on two types of extensions: providers and auth handlers. Each can be delivered as a builtin (compiled into the scafctl binary) or as a plugin (a standalone binary communicating over gRPC).
This page defines the core terminology and links to the detailed development guides.
Terminology#
| Term | Definition |
|---|
| Provider | A stateless execution unit that performs a single operation: fetching data (from), transforming values (transform), validating inputs (validation), executing side effects (action), or authenticating requests (authentication). Providers are the building blocks used inside solution resolvers. |
| Auth Handler | A stateful authentication manager that handles identity verification, credential storage, token acquisition, and request injection. Auth handlers manage OAuth flows, cache tokens across invocations, and are used by providers (like http) to authenticate outgoing requests. |
| Builtin | An extension compiled directly into the scafctl binary. Builtin extensions are registered at startup and require no external binaries. Contributing a builtin extension means adding code to the scafctl repository. |
| Plugin | An extension delivered as a standalone executable that communicates with scafctl over gRPC using hashicorp/go-plugin
. Plugins run as separate processes, can be written in any gRPC-capable language, and are distributed independently via OCI catalogs, Go modules, or binary releases. |
| Extension | Umbrella term for any provider or auth handler, whether builtin or plugin. |
| Capability | A declared feature of a provider (e.g., from, transform, action) or auth handler (e.g., scopes_on_login, tenant_id). Capabilities let scafctl adapt behavior dynamically without hardcoded knowledge of each extension. |
Extension Matrix#
| Builtin | Plugin |
|---|
| Provider | Compiled into scafctl; 16 built-in providers (http, cel, exec…) | Standalone gRPC binary; auto-fetched from OCI catalogs |
| Auth Handler | Compiled into scafctl; 3 built-in handlers (entra, github, gcp) | Standalone gRPC binary; auto-fetched from OCI catalogs |
When to Choose Builtin vs Plugin#
| Factor | Builtin | Plugin |
|---|
| Distribution | Ships with scafctl | Distributed independently |
| Language | Go only | Any language with gRPC support |
| Dependency management | Part of scafctl’s go.mod | Isolated dependencies |
| Process isolation | Runs in-process | Separate process (crash isolation) |
| Update cycle | Tied to scafctl releases | Independent release cadence |
| Use case | Core functionality, general-purpose | Third-party integrations, proprietary logic |
| Contributing | PR to scafctl repo | Publish to any OCI registry |
Key Differences: Providers vs Auth Handlers#
| Aspect | Provider | Auth Handler |
|---|
| Interface | 2 methods: Descriptor(), Execute() | 8+ methods: Login(), Logout(), Status(), GetToken(), InjectAuth(), … |
| State | Stateless — each execution is independent | Stateful — manages cached tokens, refresh tokens, sessions |
| Used by | Solution resolvers (via from, transform, etc.) | Providers (e.g., http provider calls InjectAuth() on requests) |
| Registry | provider.Registry (versioned, overwrite-protected) | auth.Registry (name-keyed) |
| Capabilities | from, transform, validation, action, authentication | scopes_on_login, scopes_on_token_request, tenant_id, hostname, federated_token |
| Plugin artifact kind | provider | auth-handler |
Plugin Communication#
Both provider plugins and auth handler plugins use hashicorp/go-plugin
with gRPC:
┌─────────────────────┐ gRPC ┌──────────────────────┐
│ scafctl │◄──────────────────►│ Your Plugin │
│ (host process) │ │ (plugin process) │
│ │ │ │
│ - Discovers plugin │ │ - Implements gRPC │
│ - Manages lifecycle│ │ service interface │
│ - Registers in │ │ - Exposes extensions│
│ appropriate │ │ - Handles execution │
│ registry │ │ │
└─────────────────────┘ └──────────────────────┘
Each plugin type has its own handshake and gRPC service:
| Plugin Type | Handshake Cookie | gRPC Service | Go Interface |
|---|
| Provider | scafctl_provider_plugin | PluginService | ProviderPlugin |
| Auth Handler | scafctl_auth_handler_plugin | AuthHandlerService | AuthHandlerPlugin |
A single plugin binary exposes one type: either providers or auth handlers, not both.
Plugin Lifecycle#
- Declaration — plugin dependencies are declared in a solution’s
bundle.plugins section with a kind (provider or auth-handler) - Resolution — scafctl resolves version constraints against configured OCI catalogs
- Caching — binaries are downloaded and cached at
$XDG_CACHE_HOME/scafctl/plugins/ - Loading — scafctl starts the plugin process and performs a gRPC handshake
- Discovery — scafctl queries the plugin for available extensions (
GetProviders or GetAuthHandlers) - Registration — each extension is wrapped and registered in the appropriate registry
- Execution — extensions are used like builtins (transparent to solution authors)
- Cleanup — plugin processes are terminated when scafctl exits
Development Guides#