Skip to content

Adding a Provider

Guide for implementing a new SecretProvider backend.

Create a new package under internal/provider/<name>/:

internal/provider/gcp/
gcp.go -- Provider implementation
auth.go -- Credential helpers (if needed)
errors.go -- Error translation (if needed)

Implement the SecretProvider interface from internal/provider/provider.go:

package gcp
import (
"context"
"github.com/n24q02m/skret/internal/config"
"github.com/n24q02m/skret/internal/provider"
)
type Provider struct {
// Client, config, etc.
}
func New(cfg *config.ResolvedConfig) (*Provider, error) {
// Initialize the provider using cfg.Path, cfg.Region, etc.
return &Provider{}, nil
}
func (p *Provider) Name() string { return "gcp" }
func (p *Provider) Capabilities() provider.Capabilities { /* ... */ }
func (p *Provider) Get(ctx context.Context, key string) (*provider.Secret, error) { /* ... */ }
func (p *Provider) List(ctx context.Context, prefix string) ([]*provider.Secret, error) { /* ... */ }
func (p *Provider) Set(ctx context.Context, key, value string, meta provider.SecretMeta) error { /* ... */ }
func (p *Provider) Delete(ctx context.Context, key string) error { /* ... */ }
func (p *Provider) GetHistory(ctx context.Context, key string) ([]*provider.Secret, error) { /* ... */ }
func (p *Provider) Rollback(ctx context.Context, key string, version int64) error { /* ... */ }
func (p *Provider) Close() error { /* ... */ }

Add the provider factory in two places:

internal/cli/root.go (CLI usage):

import "github.com/n24q02m/skret/internal/provider/gcp"
reg.Register("gcp", func(c *config.ResolvedConfig) (provider.SecretProvider, error) {
return gcp.New(c)
})

pkg/skret/client.go (library usage):

import "github.com/n24q02m/skret/internal/provider/gcp"
reg.Register("gcp", func(c *config.ResolvedConfig) (provider.SecretProvider, error) {
return gcp.New(c)
})

Update internal/config/schema.go to validate provider-specific fields:

case "gcp":
if e.Path == "" {
return fmt.Errorf("config: environment %q: path is required for gcp provider", name)
}

Add any new fields to the Environment struct if the provider needs them.

Test each method with mocked SDK calls:

internal/provider/gcp/gcp_test.go
func TestGet(t *testing.T) { /* ... */ }
func TestList(t *testing.T) { /* ... */ }
func TestSet(t *testing.T) { /* ... */ }
func TestDelete(t *testing.T) { /* ... */ }
func TestGetHistory(t *testing.T) { /* ... */ }

Env-gated tests against the real service:

tests/integration/gcp_test.go
func TestGCPIntegration(t *testing.T) {
if os.Getenv("SKRET_E2E_GCP") == "" {
t.Skip("SKRET_E2E_GCP not set")
}
// Test against real GCP Secret Manager
}

Target coverage: >=95% on the provider package.

Map provider-specific errors to skret’s standard error codes:

import "github.com/n24q02m/skret/internal/provider"
// Map GCP "NOT_FOUND" to provider.ErrNotFound
if status.Code(err) == codes.NotFound {
return nil, provider.ErrNotFound
}

Create docs/providers/<name>.md with:

  • Prerequisites and setup
  • IAM / permission requirements
  • .skret.yaml configuration example
  • Provider-specific quotas and limits
  • Capabilities table (read, write, versioning, tagging, etc.)

Add the new provider to docs/reference/config-schema.md and docs/guide/configuration.md.

  • SecretProvider interface fully implemented
  • Factory registered in CLI and library
  • Config validation for provider-specific fields
  • Unit tests (>=95% coverage)
  • Integration tests (env-gated)
  • Error translation to standard codes
  • Provider documentation page
  • Config docs updated