plugin

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: Apache-2.0 Imports: 27 Imported by: 0

Documentation

Overview

Package plugin runs out-of-process extensions behind a signed, digest-pinned package and an operator-approved ability list (ADR 034).

Index

Constants

View Source
const (
	AuditInstall     = "plugin.install"
	AuditApprove     = "plugin.approve"
	AuditEnable      = "plugin.enable"
	AuditDisable     = "plugin.disable"
	AuditRemove      = "plugin.remove"
	AuditAbilityUsed = "plugin.ability_used"
	AuditTrustAdd    = "plugin.trust_add"
	AuditTrustRemove = "plugin.trust_remove"
)

Audit actions written for plugin lifecycle and ability use.

View Source
const (
	AbilityRead          = "read"
	AbilityReadSensitive = "read:sensitive"
)

Abilities a plugin may be granted. Both come from the scoped-token vocabulary (ADR 012); root and every write ability are refused.

View Source
const (
	StateInstalled = "installed"
	StateEnabled   = "enabled"
	StateDisabled  = "disabled"
	StateRemoving  = "removing"
)

Lifecycle states of an installed plugin.

View Source
const (
	KindNotificationChannel = "notification_channel"
)

Extension kinds. Slice 1 ships one.

View Source
const ManifestAPIVersion = 1

ManifestAPIVersion is the manifest schema version this build accepts.

Variables

View Source
var (
	ErrUnavailable = errors.New("plugin: not running")
	ErrBusy        = errors.New("plugin: too many calls in flight")
	ErrCircuitOpen = errors.New("plugin: paused after repeated failures")
	ErrTooLarge    = errors.New("plugin: payload is larger than the configured limit")
	ErrKind        = errors.New("plugin: not a notification channel")
)

Call failures. Callers match with errors.Is.

View Source
var (
	ErrNotFound    = errors.New("plugin: not found")
	ErrExists      = errors.New("plugin: already installed")
	ErrNotApproved = errors.New("plugin: abilities have not been approved")
	ErrState       = errors.New("plugin: not allowed in its current state")
)

Sentinel errors. Callers match with errors.Is.

View Source
var (
	ErrUntrusted      = errors.New("plugin: signing key is not in the trust list")
	ErrBadSignature   = errors.New("plugin: signature does not verify")
	ErrDigestMismatch = errors.New("plugin: image digest does not match the pinned digest")
)

Verification failures. Callers match with errors.Is.

View Source
var ContractVersions = []uint32{1}

ContractVersions are the plugin contract versions this host speaks.

Functions

func Fingerprint

func Fingerprint(pub ed25519.PublicKey) string

Fingerprint is the stable identifier of an ed25519 public key.

func ParsePublicKey

func ParsePublicKey(b64 string) (ed25519.PublicKey, error)

ParsePublicKey decodes a standard base64 raw 32 byte ed25519 public key.

func Sign

func Sign(priv ed25519.PrivateKey, manifestRaw []byte, digest string) string

Sign produces the detached base64 signature for a manifest and digest.

func SignedMessage

func SignedMessage(manifestRaw []byte, digest string) []byte

SignedMessage is the exact byte string a publisher signs: a domain tag, the sha256 of the manifest bytes as shipped, and the pinned image digest.

func Verify

func Verify(keys []TrustedKey, keyID string, manifestRaw []byte, digest, sigB64 string) error

Verify checks sigB64 over the manifest and digest against the key the signature names. An empty or unknown key list rejects everything.

Types

type AuditEvent

type AuditEvent struct {
	Action string
	Actor  string
	Plugin string
	Detail map[string]string
}

AuditEvent is one plugin audit record. Detail must never carry a secret.

type Auditor

type Auditor interface {
	Record(ctx context.Context, e AuditEvent) error
}

Auditor persists audit events. A failure on an ability use refuses the call.

type Config

type Config struct {
	Namespace string
	DataDir   string
	UID, GID  int

	MemoryBytes int64
	NanoCPUs    int64
	PidsLimit   int64

	CallTimeout       time.Duration
	StartTimeout      time.Duration
	HealthTimeout     time.Duration
	ReconcileInterval time.Duration
	StopTimeout       time.Duration
	BackoffBase       time.Duration
	BackoffMax        time.Duration
	BreakerCooldown   time.Duration

	HealthFailures  int
	BreakerFailures int
	MaxConcurrent   int

	MaxEventBytes    int
	MaxConfigBytes   int
	MaxSecretBytes   int
	MaxResponseBytes int
}

Config holds the runtime limits. Zero fields take the defaults in withDefaults.

func ConfigFromEnv

func ConfigFromEnv(namespace, dataDir string, lookup func(string) (string, bool)) Config

ConfigFromEnv builds a Config from APP_PLUGIN_* variables. Unparseable values are ignored so a typo keeps the safe default.

type Containers

type Containers interface {
	InspectByName(ctx context.Context, name string) (*docker.ContainerState, error)
	ListByPrefix(ctx context.Context, prefix string) ([]docker.ContainerState, error)
	Create(ctx context.Context, spec docker.ContainerSpec) (string, error)
	Start(ctx context.Context, id string) error
	Stop(ctx context.Context, id string, timeout time.Duration) error
	Remove(ctx context.Context, id string, force bool) error
	EnsureNetwork(ctx context.Context, name string) (string, error)
}

Containers is the slice of the Docker wrapper the runtime uses.

type DBTX

type DBTX interface {
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}

DBTX is the slice of *sql.DB (and the control plane's store.DB) the SQL store needs.

type Delivery

type Delivery struct {
	Delivered bool
	Detail    string
	Retryable bool
}

Delivery is the plugin's answer to a Send.

type InstallRequest

type InstallRequest struct {
	Manifest       []byte
	Signature      string
	KeyID          string
	ExpectedDigest string
	ConfigJSON     string
}

InstallRequest is everything an operator submits to install a plugin.

type InvalidError

type InvalidError struct{ Msg string }

InvalidError marks a request the operator can fix, as opposed to an internal failure.

func (*InvalidError) Error

func (e *InvalidError) Error() string

type Manager

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

Manager owns plugin installation, approval and supervision.

func NewManager

func NewManager(store Store, dk Containers, audit Auditor, cfg Config, log *slog.Logger) *Manager

NewManager builds a Manager. audit and docker must be non-nil.

func (*Manager) Approve

func (m *Manager) Approve(ctx context.Context, name string, abilities []string, actor string) (Record, error)

Approve records the abilities the operator grants. They must be a subset of what the manifest requested; nothing is ever granted implicitly.

func (*Manager) Disable

func (m *Manager) Disable(ctx context.Context, name, actor string) (Record, error)

Disable stops the plugin and keeps its record and approval.

func (*Manager) Enable

func (m *Manager) Enable(ctx context.Context, name, actor string) (Record, error)

Enable allows the supervisor to run the plugin. It needs a prior approval that includes read.

func (*Manager) Get

func (m *Manager) Get(ctx context.Context, name string) (Record, error)

Get returns one plugin.

func (*Manager) Install

func (m *Manager) Install(ctx context.Context, req InstallRequest, actor string) (Record, error)

Install verifies and records a plugin. It never approves abilities and never starts anything.

func (*Manager) List

func (m *Manager) List(ctx context.Context) ([]Record, error)

List returns every installed plugin.

func (*Manager) Reconcile

func (m *Manager) Reconcile(ctx context.Context)

Reconcile converges running containers to the stored plugin state. It is level-triggered and safe to call at any time.

func (*Manager) Remove

func (m *Manager) Remove(ctx context.Context, name, actor string) error

Remove marks a plugin for removal and tries to clean up at once. If the container cannot be removed now the row stays in the removing state and the supervisor retries on its next pass.

func (*Manager) Run

func (m *Manager) Run(ctx context.Context)

Run reconciles on an interval until ctx ends.

func (*Manager) Running

func (m *Manager) Running(name string) bool

Running reports whether the plugin has a live, handshaken connection.

func (*Manager) Send

func (m *Manager) Send(ctx context.Context, name, secret string, n Notification) (Delivery, error)

Send hands one notification to an enabled notification_channel plugin. secret is the credential for this call only: it is never stored or logged here.

func (*Manager) TrustAdd

func (m *Manager) TrustAdd(ctx context.Context, name, publicKeyB64, actor string) (TrustedKey, error)

TrustAdd adds a publisher key to the trust list.

func (*Manager) TrustKeys

func (m *Manager) TrustKeys(ctx context.Context) ([]TrustedKey, error)

TrustKeys returns the operator trust list.

func (*Manager) TrustRemove

func (m *Manager) TrustRemove(ctx context.Context, keyID, actor string) error

TrustRemove removes a key. Plugins signed by it stop at the next supervisor pass.

type Manifest

type Manifest struct {
	APIVersion    int      `yaml:"api_version" json:"api_version"`
	Name          string   `yaml:"name" json:"name"`
	Version       string   `yaml:"version" json:"version"`
	Kind          string   `yaml:"kind" json:"kind"`
	Publisher     string   `yaml:"publisher" json:"publisher"`
	Description   string   `yaml:"description" json:"description"`
	Image         string   `yaml:"image" json:"image"`
	Abilities     []string `yaml:"abilities" json:"abilities"`
	OutboundHosts []string `yaml:"outbound_hosts" json:"outbound_hosts"`
}

Manifest is the plugin.yaml a publisher signs together with the image digest.

func ParseManifest

func ParseManifest(raw []byte) (Manifest, error)

ParseManifest decodes and validates raw manifest bytes. Unknown fields are an error so a typo in an ability list can never silently widen or narrow it.

func (Manifest) Digest

func (m Manifest) Digest() string

Digest returns the sha256 digest the image reference is pinned to.

type Notification

type Notification struct {
	ID         string
	Title      string
	Body       string
	Status     string
	Severity   string
	ResourceID string
	Link       string
	At         time.Time
	// SensitiveLines are log excerpts. They reach the plugin only when the
	// operator approved read:sensitive for it.
	SensitiveLines []string
}

Notification is the one event a notification_channel plugin receives.

type Record

type Record struct {
	Name               string   `json:"name"`
	Version            string   `json:"version"`
	Kind               string   `json:"kind"`
	Publisher          string   `json:"publisher"`
	Description        string   `json:"description"`
	Image              string   `json:"image"`
	Digest             string   `json:"digest"`
	Manifest           string   `json:"manifest"`
	Signature          string   `json:"-"`
	KeyID              string   `json:"key_id"`
	RequestedAbilities []string `json:"requested_abilities"`
	ApprovedAbilities  []string `json:"approved_abilities"`
	OutboundHosts      []string `json:"outbound_hosts"`
	ConfigJSON         string   `json:"config_json"`
	State              string   `json:"state"`
	ApprovedAt         string   `json:"approved_at"`
	ApprovedBy         string   `json:"approved_by"`
	InstalledAt        string   `json:"installed_at"`
	InstalledBy        string   `json:"installed_by"`
	UpdatedAt          string   `json:"updated_at"`
	LastError          string   `json:"last_error"`
}

Record is one installed plugin as stored.

type SQLStore

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

SQLStore implements Store over the control plane database.

func NewSQLStore

func NewSQLStore(db DBTX) *SQLStore

NewSQLStore wraps db.

func (*SQLStore) AddKey

func (s *SQLStore) AddKey(ctx context.Context, k TrustedKey) error

AddKey trusts a publisher key.

func (*SQLStore) DeletePlugin

func (s *SQLStore) DeletePlugin(ctx context.Context, name string) error

DeletePlugin removes a plugin row.

func (*SQLStore) GetPlugin

func (s *SQLStore) GetPlugin(ctx context.Context, name string) (Record, error)

GetPlugin returns one plugin or ErrNotFound.

func (*SQLStore) InsertPlugin

func (s *SQLStore) InsertPlugin(ctx context.Context, r Record) error

InsertPlugin adds a plugin, or returns ErrExists.

func (*SQLStore) ListKeys

func (s *SQLStore) ListKeys(ctx context.Context) ([]TrustedKey, error)

ListKeys returns the trust list.

func (*SQLStore) ListPlugins

func (s *SQLStore) ListPlugins(ctx context.Context) ([]Record, error)

ListPlugins returns every plugin, by name.

func (*SQLStore) RemoveKey

func (s *SQLStore) RemoveKey(ctx context.Context, keyID string) error

RemoveKey stops trusting a key.

func (*SQLStore) SetLastError

func (s *SQLStore) SetLastError(ctx context.Context, name, msg, updatedAt string) error

SetLastError records the latest runtime failure without touching state or approvals.

func (*SQLStore) UpdatePlugin

func (s *SQLStore) UpdatePlugin(ctx context.Context, r Record) error

UpdatePlugin writes the mutable columns of a plugin.

type Store

type Store interface {
	ListPlugins(ctx context.Context) ([]Record, error)
	GetPlugin(ctx context.Context, name string) (Record, error)
	InsertPlugin(ctx context.Context, r Record) error
	UpdatePlugin(ctx context.Context, r Record) error
	SetLastError(ctx context.Context, name, msg, updatedAt string) error
	DeletePlugin(ctx context.Context, name string) error
	ListKeys(ctx context.Context) ([]TrustedKey, error)
	AddKey(ctx context.Context, k TrustedKey) error
	RemoveKey(ctx context.Context, keyID string) error
}

Store persists plugins and the trust list.

type TrustedKey

type TrustedKey struct {
	KeyID     string `json:"key_id"`
	Name      string `json:"name"`
	PublicKey string `json:"public_key"`
	AddedAt   string `json:"added_at"`
	AddedBy   string `json:"added_by"`
}

TrustedKey is one operator-approved publisher key.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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