plugin

package
v0.8.5 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: Apache-2.0 Imports: 37 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"
	AuditUpgrade     = "plugin.upgrade"
	AuditRestart     = "plugin.restart"
	AuditAbilityUsed = "plugin.ability_used"
	AuditTrustAdd    = "plugin.trust_add"
	AuditTrustRemove = "plugin.trust_remove"
)

Audit actions written for plugin lifecycle and ability use.

View Source
const (
	VerifyVerified      = "verified"
	VerifyUnsigned      = "unsigned"
	VerifyUnsignedBlock = "unsigned_refused"
	VerifyUntrustedKey  = "untrusted_key"
	VerifyBadSignature  = "bad_signature"
	VerifyDigest        = "digest_mismatch"
	VerifyUnverified    = "unverified"
)

Verification statuses reported to operators.

View Source
const (
	HealthHealthy   = "healthy"
	HealthUnhealthy = "unhealthy"
	HealthStopped   = "stopped"
	HealthRemoving  = "removing"
)

Health values reported for an installed plugin.

View Source
const (
	FixRestart   = "restart"
	FixApprove   = "approve"
	FixTrustKey  = "trust_key"
	FixReinstall = "reinstall"
)

Fix kinds the dashboard turns into a button.

View Source
const (
	KindNotificationChannel = "notification_channel"
	KindDNSProvider         = "dns_provider"
)

Extension kinds. KindPanelProvider is declared in panel.go.

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 (
	KindFetch     = "fetch"
	KindSignature = "signature"
	KindFormat    = "format"
	KindReplay    = "replay"
	KindTooLarge  = "too_large"
)

Index error kinds shown next to a Re-check button.

View Source
const (
	SlotPlugins   = "plugins"
	SlotAppDetail = "app-detail"
	SlotSettings  = "settings"
	SlotDashboard = "dashboard"
)

Placement slots a panel can be rendered in.

View Source
const (
	WidgetStat     = "stat"
	WidgetTable    = "table"
	WidgetList     = "list"
	WidgetChart    = "chart"
	WidgetMarkdown = "markdown"
	WidgetStatus   = "status"
	WidgetButton   = "button"
)

Widget types. The set is closed: the dashboard renders only these.

View Source
const (
	StatusOK      = "ok"
	StatusWarning = "warning"
	StatusError   = "error"
	StatusUnknown = "unknown"
)

Limits on what a plugin can make the dashboard render.

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

Lifecycle states of an installed plugin.

View Source
const AuditMarketplaceInstall = "plugin.marketplace_install"

AuditMarketplaceInstall is written once per one-click install or update.

View Source
const BuiltinSourceID = "default"

BuiltinSourceID names the source configured by the operator's environment.

View Source
const (

	// EnvAllowUnsigned lets an operator install and run a package with no signature.
	EnvAllowUnsigned = "APP_PLUGIN_ALLOW_UNSIGNED"
)

Environment variables that tune the plugin runtime.

View Source
const KindPanelProvider = "panel_provider"

KindPanelProvider is a plugin that only contributes panels and commands.

View Source
const ManifestAPIVersion = 1

ManifestAPIVersion is the manifest schema version this build accepts.

View Source
const ScopeKeyAppID = "app_id"

ScopeKeyAppID is the only scope key a panel call may carry in this release.

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 (
	ErrEntryNotFound = errors.New("plugin: that plugin is not listed in the marketplace source")
	ErrChanged       = errors.New("plugin: the marketplace listing changed since you reviewed it, review it again")
	ErrNoRuntime     = errors.New("plugin: the plugin runtime is not available, so nothing can be installed")
)

Marketplace errors beyond the index ones. Callers match with errors.Is.

View Source
var (
	ErrIndexSignature = errors.New("plugin: the marketplace index signature does not verify against the source's root key")
	ErrIndexReplay    = errors.New("plugin: the marketplace index is older than one already seen (replay or rollback)")
	ErrIndexTooLarge  = errors.New("plugin: the marketplace index is larger than the allowed size")
	ErrIndexFormat    = errors.New("plugin: the marketplace index is malformed")
)

Index failures. Callers match with errors.Is.

View Source
var (
	ErrNoPanel  = errors.New("plugin: no such panel, source or action")
	ErrNotPanel = errors.New("plugin: not a panel provider")

	// ErrCallFailed wraps a transport or plugin error from a panel call.
	ErrCallFailed = errors.New("plugin: call failed")
)

Panel 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")
	ErrUnsigned       = errors.New("plugin: the package is unsigned and unsigned plugins are not allowed (set " + EnvAllowUnsigned + "=true to allow them)")
)

Verification failures. Callers match with errors.Is.

View Source
var ContractVersions = []uint32{1}

ContractVersions are the plugin contract versions this host speaks.

View Source
var ErrBadData = errors.New("plugin: returned data that does not fit the panel")

ErrBadData marks plugin data that does not fit the panel it feeds.

View Source
var ErrNotDNSProvider = errors.New("plugin: not a dns_provider")

ErrNotDNSProvider is returned when a DNS call names a plugin of another kind.

View Source
var ErrSourceExists = errors.New("plugin: marketplace source already configured")

ErrSourceExists marks a source that is already configured.

View Source
var ErrSourceNotFound = errors.New("plugin: source not found")

ErrSourceNotFound marks a source URL that answered 404.

View Source
var ErrSourceUnknown = errors.New("plugin: marketplace source not found")

ErrSourceUnknown marks a source id that is not configured.

Functions

func Fetch

func Fetch(ctx context.Context, rawURL string, limit int64) ([]byte, error)

Fetch downloads a package file through the SSRF-guarded client. Only https is accepted.

func Fingerprint

func Fingerprint(pub ed25519.PublicKey) string

Fingerprint is the stable identifier of an ed25519 public key.

func IndexSignedMessage

func IndexSignedMessage(payload []byte) []byte

IndexSignedMessage is the exact byte string an index root signs.

func LoadSource

func LoadSource(ctx context.Context, fetch FetchFunc, manifestURL, signatureURL string) (manifest, signature string, err error)

LoadSource turns a manifest URL (and optional signature URL, default <manifest>.sig) into manifest and signature text. A missing .sig file is not an error: the package is then treated as unsigned.

func ManifestSchema

func ManifestSchema() []byte

ManifestSchema returns the JSON Schema manifests are validated against.

func ParsePublicKey

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

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

func SanitizeMarkdown

func SanitizeMarkdown(s string) string

SanitizeMarkdown reduces plugin text to a safe markdown subset: raw HTML, HTML entities, images, reference links and non-http(s) links are removed. The dashboard renders what remains with its own minimal renderer, never as HTML.

func Sign

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

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

func SignIndex

func SignIndex(priv ed25519.PrivateKey, idx Index) ([]byte, error)

SignIndex validates idx and returns the signed envelope bytes.

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 ActionResult

type ActionResult struct {
	OK      bool   `json:"ok"`
	Message string `json:"message"`
}

ActionResult is the plugin's answer to a button press.

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 Catalog

type Catalog struct {
	Entries []CatalogEntry `json:"entries"`
	Sources []SourceStatus `json:"sources"`
}

Catalog is every listing from every source plus each source's status.

type CatalogEntry

type CatalogEntry struct {
	Entry
	SourceID         string `json:"source_id"`
	SourceName       string `json:"source_name"`
	PublisherKeyName string `json:"publisher_key_name"`
	Verified         bool   `json:"verified"`
	KeyTrusted       bool   `json:"key_trusted"`
	Installed        bool   `json:"installed"`
	InstalledVersion string `json:"installed_version"`
	UpdateAvailable  bool   `json:"update_available"`
	Compatible       bool   `json:"compatible"`
}

CatalogEntry is an index entry with what the control plane knows about it locally.

type ChartPoint

type ChartPoint struct {
	Label string  `json:"label"`
	Value float64 `json:"value"`
}

ChartPoint is one labelled value of a chart widget.

type Column

type Column struct {
	Key   string `yaml:"key" json:"key"`
	Label string `yaml:"label" json:"label"`
}

Column maps a key in each table row object to a header.

type Command

type Command struct {
	ID          string `yaml:"id" json:"id"`
	Description string `yaml:"description" json:"description"`
	Panel       string `yaml:"panel" json:"panel"`
	Action      string `yaml:"action" json:"action,omitempty"`
}

Command is a CLI subcommand (`plugins run <plugin> <id>`). With Action it presses that panel button, otherwise it prints the panel's data.

type Config

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

	AllowUnsigned bool

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

type Contrib struct {
	Panels   []Panel   `yaml:"panels" json:"panels"`
	Commands []Command `yaml:"commands" json:"commands"`
}

Contrib lists the UI a plugin adds. It is data the core renders with its own components, never code.

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 DNSError

type DNSError struct {
	Op        string
	Detail    string
	Retryable bool
}

DNSError is a failure the plugin itself reported. Detail has the call's secret removed and is safe to show an operator.

func (*DNSError) Error

func (e *DNSError) Error() string

type DNSRecord

type DNSRecord struct {
	Name    string
	Type    string
	Value   string
	TTL     time.Duration
	Proxied bool
}

DNSRecord is one record in a zone, named relative to it ("@" for the apex).

type Delivery

type Delivery struct {
	Delivered bool
	Detail    string
	Retryable bool
}

Delivery is the plugin's answer to a Send.

type Entry

type Entry struct {
	ID                string   `json:"id"`
	Name              string   `json:"name"`
	Publisher         string   `json:"publisher"`
	PublisherKeyID    string   `json:"publisher_key_id"`
	Version           string   `json:"version"`
	Summary           string   `json:"summary"`
	Categories        []string `json:"categories"`
	Icon              string   `json:"icon"`
	Abilities         []string `json:"abilities"`
	OutboundHosts     []string `json:"outbound_hosts"`
	Digest            string   `json:"digest"`
	ManifestURL       string   `json:"manifest_url"`
	ManifestSignature string   `json:"manifest_signature"`
	Changelog         string   `json:"changelog"`
	MinVersion        string   `json:"min_version"`
	Screenshots       []string `json:"screenshots"`
}

Entry is one plugin listing. Everything in it is covered by the index signature.

func BuildEntry

func BuildEntry(manifestRaw []byte, manifestURL, signature string, pub ed25519.PublicKey, meta Entry) (Entry, error)

BuildEntry makes the index entry for a signed manifest. The signature must already verify under the given publisher key, so a bad pair never gets listed.

type FetchFunc

type FetchFunc func(ctx context.Context, rawURL string, limit int64) ([]byte, error)

FetchFunc downloads at most max bytes from an https URL.

type Index

type Index struct {
	Version       int        `json:"version"`
	GeneratedAt   string     `json:"generated_at"`
	Sequence      int64      `json:"sequence"`
	PublisherKeys []IndexKey `json:"publisher_keys"`
	Plugins       []Entry    `json:"plugins"`
}

Index is the signed catalog payload. Sequence only ever grows.

func ParseIndex

func ParseIndex(raw []byte, root ed25519.PublicKey) (Index, string, error)

ParseIndex verifies the envelope against root and returns the index plus the sha256 of the signed payload. The signature is checked before the payload is decoded.

type IndexEnvelope

type IndexEnvelope struct {
	Format    string `json:"format"`
	Payload   string `json:"payload"`
	Signature string `json:"signature"`
}

IndexEnvelope is the file served at an index URL. Payload is the base64 of the Index JSON exactly as signed, so no canonicalization is needed.

type IndexFetchFunc

type IndexFetchFunc func(ctx context.Context, rawURL string, limit int64, etag string) (IndexFetchResult, error)

IndexFetchFunc downloads an index from an https URL, sending etag as If-None-Match.

type IndexFetchResult

type IndexFetchResult struct {
	Body        []byte
	ETag        string
	NotModified bool
}

IndexFetchResult is one conditional GET of an index.

func FetchIndex

func FetchIndex(ctx context.Context, rawURL string, limit int64, etag string) (IndexFetchResult, error)

FetchIndex is the default IndexFetchFunc: https only, SSRF guarded, size capped.

type IndexKey

type IndexKey struct {
	KeyID     string `json:"key_id"`
	Name      string `json:"name"`
	PublicKey string `json:"public_key"`
}

IndexKey is a publisher key the index root vouches for.

type InstallRequest

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

InstallRequest is everything an operator submits to install or upgrade a plugin. Approve, when non-nil, must list exactly the abilities the manifest requests.

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

func (m *Manager) DNSDelete(ctx context.Context, name, secret, zone string, rec DNSRecord) error

DNSDelete removes the record equal to rec in name, type and value.

func (*Manager) DNSListRecords

func (m *Manager) DNSListRecords(ctx context.Context, name, secret, zone string) ([]DNSRecord, error)

DNSListRecords returns every record the provider holds in zone.

func (*Manager) DNSListZones

func (m *Manager) DNSListZones(ctx context.Context, name, secret string) ([]string, error)

DNSListZones returns the zone names the credential can manage.

func (*Manager) DNSUpsert

func (m *Manager) DNSUpsert(ctx context.Context, name, secret, zone string, rec DNSRecord) error

DNSUpsert creates the record or replaces the one with the same name and type.

func (*Manager) DNSVerify

func (m *Manager) DNSVerify(ctx context.Context, name, secret, zone string, rec DNSRecord) (bool, error)

DNSVerify reports whether the provider now serves exactly rec.

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

func (m *Manager) Inspect(ctx context.Context, req InstallRequest) (Preview, error)

Inspect parses and verifies a package and reports what installing or upgrading it would change. It writes nothing.

func (*Manager) Install

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

Install verifies and records a plugin. It starts nothing, and approves only when the request carries the exact requested ability set.

func (*Manager) List

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

List returns every installed plugin.

func (*Manager) PanelAction

func (m *Manager) PanelAction(ctx context.Context, name, panelID, actionID string, scope map[string]string) (ActionResult, error)

PanelAction presses a declared button. The action id must belong to a button of the panel, so a caller can never reach an arbitrary plugin method.

func (*Manager) PanelData

func (m *Manager) PanelData(ctx context.Context, name, panelID string, scope map[string]string) (PanelView, error)

PanelData asks the plugin for the panel's data and binds it to the declared widgets. Only the panel's declared source abilities that the operator approved are used, and the call is bounded by the call timeout and the panel response cap.

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

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

Restart clears a plugin's failure backoff and breaker and starts it again now.

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

func (m *Manager) StatusOf(ctx context.Context, rec Record) Status

StatusOf reports health, verification and the fix that would help, if any.

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.

func (*Manager) Upgrade

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

Upgrade replaces an installed plugin's package with a newer verified one. Abilities or outbound hosts that were not requested before need a fresh approval: the plugin is stopped and unapproved until the operator gives it, unless the request carries the exact new ability set.

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"`
	Contributes   Contrib  `yaml:"contributes" json:"contributes"`
}

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.

func (Manifest) Panel

func (m Manifest) Panel(id string) (Panel, bool)

Panel returns the named panel of the manifest.

func (Manifest) SlotPanels

func (m Manifest) SlotPanels(slot string) []Panel

SlotPanels returns data-backed panels placed in slot, sorted by id.

type MarketInstallRequest

type MarketInstallRequest struct {
	SourceID  string   `json:"source_id"`
	ID        string   `json:"id"`
	Version   string   `json:"version"`
	Digest    string   `json:"digest"`
	Abilities []string `json:"abilities"`
	// TrustKeyID must equal the publisher key fingerprint when that key is not yet trusted.
	TrustKeyID string `json:"trust_key_id"`
}

MarketInstallRequest is the operator's confirmation of exactly what the sheet showed.

type MarketInstallResult

type MarketInstallResult struct {
	Plugin     Record `json:"plugin"`
	Action     string `json:"action"`
	TrustAdded bool   `json:"trust_added"`
}

MarketInstallResult reports what the one-click flow did.

type MarketPreview

type MarketPreview struct {
	Entry            Entry    `json:"entry"`
	SourceID         string   `json:"source_id"`
	SourceName       string   `json:"source_name"`
	KeyID            string   `json:"key_id"`
	KeyName          string   `json:"key_name"`
	KeyTrusted       bool     `json:"key_trusted"`
	Action           string   `json:"action"`
	InstalledVersion string   `json:"installed_version"`
	AddedAbilities   []string `json:"added_abilities"`
	AddedHosts       []string `json:"added_hosts"`
	Reapproval       bool     `json:"reapproval_required"`
}

MarketPreview is the confirm sheet: everything proven about a listing before any change.

type MarketStore

type MarketStore interface {
	ListSources(ctx context.Context) ([]Source, error)
	AddSource(ctx context.Context, s Source) error
	RemoveSource(ctx context.Context, id string) error
	GetState(ctx context.Context, sourceID string) (SourceState, error)
	SaveState(ctx context.Context, st SourceState) error
}

MarketStore persists sources and their fetch state.

type Marketplace

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

Marketplace lists plugins from signed indexes and installs them in one confirmed step.

func NewMarketplace

func NewMarketplace(store MarketStore, mgr *Manager, builtinURL, builtinRootKey, controlVersion string) (*Marketplace, error)

NewMarketplace builds the service. mgr may be nil, which leaves browsing on and installing off. A builtinURL with an unusable key is skipped and reported.

func (*Marketplace) AddSource

func (m *Marketplace) AddSource(ctx context.Context, name, rawURL, rootKey, actor string) (Source, error)

AddSource configures an extra index source.

func (*Marketplace) Catalog

func (m *Marketplace) Catalog(ctx context.Context, force bool) (Catalog, error)

Catalog merges every source. force refetches instead of using a cache under a day old.

func (*Marketplace) Install

Install trusts the publisher key if confirmed, installs or upgrades, approves exactly the confirmed abilities and enables, rolling back what it added on failure.

func (*Marketplace) Preview

func (m *Marketplace) Preview(ctx context.Context, sourceID, id string) (MarketPreview, error)

Preview verifies a listing end to end and reports what installing it would do. It writes nothing.

func (*Marketplace) Refresh

func (m *Marketplace) Refresh(ctx context.Context, sourceID string) ([]SourceStatus, error)

Refresh refetches one source, or all when sourceID is empty, and reports each status.

func (*Marketplace) RemoveSource

func (m *Marketplace) RemoveSource(ctx context.Context, id string) error

RemoveSource drops an operator-added source and its cached index.

func (*Marketplace) SetFetchers

func (m *Marketplace) SetFetchers(index IndexFetchFunc, file FetchFunc)

SetFetchers replaces the network fetchers, for tests and offline mirrors.

func (*Marketplace) Sources

func (m *Marketplace) Sources(ctx context.Context) ([]Source, error)

Sources lists the built-in source (if configured) and the operator-added ones.

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 Panel

type Panel struct {
	ID          string       `yaml:"id" json:"id"`
	Title       string       `yaml:"title" json:"title"`
	Description string       `yaml:"description" json:"description"`
	Rows        []PanelRow   `yaml:"rows" json:"rows"`
	Slot        string       `yaml:"slot" json:"slot"`
	Source      *PanelSource `yaml:"source" json:"source,omitempty"`
	Widgets     []Widget     `yaml:"widgets" json:"widgets"`
}

Panel is a card the dashboard renders. Without a Source it is static rows on the Plugins page; with one it is widgets fed by a plugin call, in Slot.

type PanelRow

type PanelRow struct {
	Label string `yaml:"label" json:"label"`
	Value string `yaml:"value" json:"value"`
}

PanelRow is one label and plain-text value in a Panel.

type PanelSource

type PanelSource struct {
	ID        string   `yaml:"id" json:"id"`
	Abilities []string `yaml:"abilities" json:"abilities"`
}

PanelSource is the plugin call that feeds a panel. Abilities are what the host hands the call, and must be a subset of what the operator approved.

type PanelView

type PanelView struct {
	Widgets []WidgetView `json:"widgets"`
}

PanelView is a panel's data after the host bound it to the declared widgets. Every value is plain text or a number: the dashboard never receives raw plugin JSON.

func BindPanel

func BindPanel(p Panel, data []byte) (PanelView, error)

BindPanel validates data against the panel's widgets and returns the view. It returns an error only when data is not a JSON object.

type Preview

type Preview struct {
	Manifest           Manifest     `json:"manifest"`
	Verification       Verification `json:"verification"`
	Action             string       `json:"action"`
	InstalledVersion   string       `json:"installed_version"`
	ApprovedAbilities  []string     `json:"approved_abilities"`
	AddedAbilities     []string     `json:"added_abilities"`
	RemovedAbilities   []string     `json:"removed_abilities"`
	AddedHosts         []string     `json:"added_hosts"`
	RemovedHosts       []string     `json:"removed_hosts"`
	ReapprovalRequired bool         `json:"reapproval_required"`
}

Preview is the review an operator sees before approving an install or upgrade.

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 SQLMarketStore

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

SQLMarketStore implements MarketStore over the control plane database.

func NewSQLMarketStore

func NewSQLMarketStore(db DBTX) *SQLMarketStore

NewSQLMarketStore wraps db.

func (*SQLMarketStore) AddSource

func (s *SQLMarketStore) AddSource(ctx context.Context, r Source) error

AddSource inserts a source, or returns ErrSourceExists.

func (*SQLMarketStore) GetState

func (s *SQLMarketStore) GetState(ctx context.Context, sourceID string) (SourceState, error)

GetState returns the saved state, or a zero state for a source never fetched.

func (*SQLMarketStore) ListSources

func (s *SQLMarketStore) ListSources(ctx context.Context) ([]Source, error)

ListSources returns the operator-added sources, oldest first.

func (*SQLMarketStore) RemoveSource

func (s *SQLMarketStore) RemoveSource(ctx context.Context, id string) error

RemoveSource deletes a source and its fetch state.

func (*SQLMarketStore) SaveState

func (s *SQLMarketStore) SaveState(ctx context.Context, st SourceState) error

SaveState upserts the state row.

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 every column except the name and kind; callers pass a record read from the store.

type Source

type Source struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	URL      string `json:"url"`
	RootKey  string `json:"root_public_key"`
	Builtin  bool   `json:"builtin"`
	AddedAt  string `json:"added_at"`
	AddedBy  string `json:"added_by"`
	KeyPrint string `json:"root_key_id"`
}

Source is one signed index an operator trusts to list plugins.

type SourceState

type SourceState struct {
	SourceID      string
	LastSequence  int64
	LastGenerated string
	LastHash      string
	ETag          string
	Body          string
	FetchedAt     string
	LastError     string
	ErrorKind     string
}

SourceState is what the control plane remembers about a source between fetches.

type SourceStatus

type SourceStatus struct {
	Source
	Status      string `json:"status"`
	Error       string `json:"error"`
	ErrorKind   string `json:"error_kind"`
	Sequence    int64  `json:"sequence"`
	GeneratedAt string `json:"generated_at"`
	FetchedAt   string `json:"fetched_at"`
	PluginCount int    `json:"plugin_count"`
}

SourceStatus is a source plus the outcome of its last fetch.

type Status

type Status struct {
	Health       string `json:"health"`
	Verification string `json:"verification"`
	Fix          string `json:"fix"`
}

Status is the live health, verification and suggested fix of an installed 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.

type Verification

type Verification struct {
	Status       string `json:"status"`
	DigestPinned bool   `json:"digest_pinned"`
	Digest       string `json:"digest"`
	Signed       bool   `json:"signed"`
	KeyID        string `json:"key_id"`
	KeyName      string `json:"key_name"`
	Installable  bool   `json:"installable"`
	Reason       string `json:"reason"`
}

Verification is what the host could prove about a package before running it.

type Widget

type Widget struct {
	Type     string   `yaml:"type" json:"type"`
	Title    string   `yaml:"title" json:"title"`
	Field    string   `yaml:"field" json:"field,omitempty"`
	Unit     string   `yaml:"unit" json:"unit,omitempty"`
	Columns  []Column `yaml:"columns" json:"columns,omitempty"`
	LabelKey string   `yaml:"label_key" json:"label_key,omitempty"`
	ValueKey string   `yaml:"value_key" json:"value_key,omitempty"`
	Action   string   `yaml:"action" json:"action,omitempty"`
	Label    string   `yaml:"label" json:"label,omitempty"`
	Confirm  string   `yaml:"confirm" json:"confirm,omitempty"`
}

Widget is one element of a panel. Field is a dotted path into the source's JSON document.

type WidgetView

type WidgetView struct {
	Type    string       `json:"type"`
	Title   string       `json:"title"`
	Error   string       `json:"error,omitempty"`
	Value   string       `json:"value,omitempty"`
	Unit    string       `json:"unit,omitempty"`
	State   string       `json:"state,omitempty"`
	Text    string       `json:"text,omitempty"`
	Columns []Column     `json:"columns,omitempty"`
	Rows    [][]string   `json:"rows,omitempty"`
	Items   []string     `json:"items,omitempty"`
	Points  []ChartPoint `json:"points,omitempty"`
	Action  string       `json:"action,omitempty"`
	Label   string       `json:"label,omitempty"`
	Confirm string       `json:"confirm,omitempty"`
}

WidgetView is one bound widget. Error is set when the plugin's data did not match the manifest, so one bad widget does not blank the whole panel.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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