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
- Variables
- func ConfigHosts(man Manifest, configJSON string) ([]string, error)
- func EffectiveOutboundHosts(man Manifest, configJSON string) ([]string, error)
- func Fetch(ctx context.Context, rawURL string, limit int64) ([]byte, error)
- func Fingerprint(pub ed25519.PublicKey) string
- func IndexSignedMessage(payload []byte) []byte
- func LoadSource(ctx context.Context, fetch FetchFunc, manifestURL, signatureURL string) (manifest, signature string, err error)
- func ManifestSchema() []byte
- func ParsePublicKey(b64 string) (ed25519.PublicKey, error)
- func SanitizeMarkdown(s string) string
- func Sign(priv ed25519.PrivateKey, manifestRaw []byte, digest string) string
- func SignIndex(priv ed25519.PrivateKey, idx Index) ([]byte, error)
- func SignedMessage(manifestRaw []byte, digest string) []byte
- func Verify(keys []TrustedKey, keyID string, manifestRaw []byte, digest, sigB64 string) error
- type ActionResult
- type AuditEvent
- type Auditor
- type Catalog
- type CatalogEntry
- type ChartPoint
- type Column
- type Command
- type Config
- type Containers
- type Contrib
- type DBTX
- type DNSError
- type DNSRecord
- type Delivery
- type Entry
- type FetchFunc
- type Index
- type IndexEnvelope
- type IndexFetchFunc
- type IndexFetchResult
- type IndexKey
- type InstallRequest
- type InvalidError
- type Manager
- func (m *Manager) Approve(ctx context.Context, name string, abilities []string, actor string) (Record, error)
- func (m *Manager) DNSDelete(ctx context.Context, name, secret, zone string, rec DNSRecord) error
- func (m *Manager) DNSListRecords(ctx context.Context, name, secret, zone string) ([]DNSRecord, error)
- func (m *Manager) DNSListZones(ctx context.Context, name, secret string) ([]string, error)
- func (m *Manager) DNSUpsert(ctx context.Context, name, secret, zone string, rec DNSRecord) error
- func (m *Manager) DNSVerify(ctx context.Context, name, secret, zone string, rec DNSRecord) (bool, error)
- func (m *Manager) Disable(ctx context.Context, name, actor string) (Record, error)
- func (m *Manager) EgressDenials(ctx context.Context) map[string][]egress.DenialCount
- func (m *Manager) EgressMode() string
- func (m *Manager) Enable(ctx context.Context, name, actor string) (Record, error)
- func (m *Manager) Get(ctx context.Context, name string) (Record, error)
- func (m *Manager) Inspect(ctx context.Context, req InstallRequest) (Preview, error)
- func (m *Manager) Install(ctx context.Context, req InstallRequest, actor string) (Record, error)
- func (m *Manager) List(ctx context.Context) ([]Record, error)
- func (m *Manager) PanelAction(ctx context.Context, name, panelID, actionID string, scope map[string]string) (ActionResult, error)
- func (m *Manager) PanelData(ctx context.Context, name, panelID string, scope map[string]string) (PanelView, error)
- func (m *Manager) Reconcile(ctx context.Context)
- func (m *Manager) Remove(ctx context.Context, name, actor string) error
- func (m *Manager) Restart(ctx context.Context, name, actor string) (Record, error)
- func (m *Manager) Run(ctx context.Context)
- func (m *Manager) Running(name string) bool
- func (m *Manager) Send(ctx context.Context, name, secret string, n Notification) (Delivery, error)
- func (m *Manager) StatusOf(ctx context.Context, rec Record) Status
- func (m *Manager) TrustAdd(ctx context.Context, name, publicKeyB64, actor string) (TrustedKey, error)
- func (m *Manager) TrustKeys(ctx context.Context) ([]TrustedKey, error)
- func (m *Manager) TrustRemove(ctx context.Context, keyID, actor string) error
- func (m *Manager) Upgrade(ctx context.Context, req InstallRequest, actor string) (Record, error)
- type Manifest
- type MarketInstallRequest
- type MarketInstallResult
- type MarketPreview
- type MarketStore
- type Marketplace
- func (m *Marketplace) AddSource(ctx context.Context, name, rawURL, rootKey, actor string) (Source, error)
- func (m *Marketplace) Catalog(ctx context.Context, force bool) (Catalog, error)
- func (m *Marketplace) Install(ctx context.Context, req MarketInstallRequest, actor string) (MarketInstallResult, error)
- func (m *Marketplace) Preview(ctx context.Context, sourceID, id string) (MarketPreview, error)
- func (m *Marketplace) Refresh(ctx context.Context, sourceID string) ([]SourceStatus, error)
- func (m *Marketplace) RemoveSource(ctx context.Context, id string) error
- func (m *Marketplace) SetFetchers(index IndexFetchFunc, file FetchFunc)
- func (m *Marketplace) Sources(ctx context.Context) ([]Source, error)
- type Notification
- type Panel
- type PanelRow
- type PanelSource
- type PanelView
- type Preview
- type Record
- type SQLMarketStore
- func (s *SQLMarketStore) AddSource(ctx context.Context, r Source) error
- func (s *SQLMarketStore) GetState(ctx context.Context, sourceID string) (SourceState, error)
- func (s *SQLMarketStore) ListSources(ctx context.Context) ([]Source, error)
- func (s *SQLMarketStore) RemoveSource(ctx context.Context, id string) error
- func (s *SQLMarketStore) SaveState(ctx context.Context, st SourceState) error
- type SQLStore
- func (s *SQLStore) AddKey(ctx context.Context, k TrustedKey) error
- func (s *SQLStore) DeletePlugin(ctx context.Context, name string) error
- func (s *SQLStore) GetPlugin(ctx context.Context, name string) (Record, error)
- func (s *SQLStore) InsertPlugin(ctx context.Context, r Record) error
- func (s *SQLStore) ListKeys(ctx context.Context) ([]TrustedKey, error)
- func (s *SQLStore) ListPlugins(ctx context.Context) ([]Record, error)
- func (s *SQLStore) RemoveKey(ctx context.Context, keyID string) error
- func (s *SQLStore) SetLastError(ctx context.Context, name, msg, updatedAt string) error
- func (s *SQLStore) UpdatePlugin(ctx context.Context, r Record) error
- type Source
- type SourceState
- type SourceStatus
- type Status
- type Store
- type TrustedKey
- type Verification
- type Widget
- type WidgetView
Constants ¶
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.
const ( EgressOpen = "open" EgressProxy = "proxy" // EnvEgressMode selects the mode, EnvEgressImage the image that runs the sidecar. EnvEgressMode = "APP_PLUGIN_EGRESS_MODE" EnvEgressImage = "APP_PLUGIN_EGRESS_IMAGE" )
Egress modes. Open keeps the shared bridge; proxy forces every plugin connection through the allowlisting sidecar.
const ( VerifyVerified = "verified" VerifyUnsigned = "unsigned" VerifyUnsignedBlock = "unsigned_refused" VerifyUntrustedKey = "untrusted_key" VerifyBadSignature = "bad_signature" VerifyDigest = "digest_mismatch" VerifyUnverified = "unverified" )
Verification statuses reported to operators.
const ( HealthHealthy = "healthy" HealthUnhealthy = "unhealthy" HealthStopped = "stopped" HealthRemoving = "removing" )
Health values reported for an installed plugin.
const ( FixRestart = "restart" FixApprove = "approve" FixTrustKey = "trust_key" FixReinstall = "reinstall" )
Fix kinds the dashboard turns into a button.
const ( KindNotificationChannel = "notification_channel" KindDNSProvider = "dns_provider" )
Extension kinds. KindPanelProvider is declared in panel.go.
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.
const ( KindFetch = "fetch" KindSignature = "signature" KindFormat = "format" KindReplay = "replay" KindTooLarge = "too_large" )
Index error kinds shown next to a Re-check button.
const ( SlotPlugins = "plugins" SlotAppDetail = "app-detail" SlotSettings = "settings" SlotDashboard = "dashboard" )
Placement slots a panel can be rendered in.
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.
const ( StatusOK = "ok" StatusWarning = "warning" StatusError = "error" StatusUnknown = "unknown" )
Limits on what a plugin can make the dashboard render.
const ( StateInstalled = "installed" StateEnabled = "enabled" StateDisabled = "disabled" StateRemoving = "removing" )
Lifecycle states of an installed plugin.
const AuditMarketplaceInstall = "plugin.marketplace_install"
AuditMarketplaceInstall is written once per one-click install or update.
const BuiltinSourceID = "default"
BuiltinSourceID names the source configured by the operator's environment.
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.
const KindPanelProvider = "panel_provider"
KindPanelProvider is a plugin that only contributes panels and commands.
const ManifestAPIVersion = 1
ManifestAPIVersion is the manifest schema version this build accepts.
const ScopeKeyAppID = "app_id"
ScopeKeyAppID is the only scope key a panel call may carry in this release.
Variables ¶
var ( 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.
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.
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.
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.
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.
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.
var ContractVersions = []uint32{1}
ContractVersions are the plugin contract versions this host speaks.
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.
ErrEgressUnavailable means proxy mode is on but the sidecar cannot be used. Plugins that need the network then stay down instead of falling back to open.
var ErrNotDNSProvider = errors.New("plugin: not a dns_provider")
ErrNotDNSProvider is returned when a DNS call names a plugin of another kind.
var ErrSourceExists = errors.New("plugin: marketplace source already configured")
ErrSourceExists marks a source that is already configured.
var ErrSourceNotFound = errors.New("plugin: source not found")
ErrSourceNotFound marks a source URL that answered 404.
var ErrSourceUnknown = errors.New("plugin: marketplace source not found")
ErrSourceUnknown marks a source id that is not configured.
Functions ¶
func ConfigHosts ¶
ConfigHosts parses the operator-configured values named by the manifest's outbound_hosts_from_config into host or host:port entries. A missing or empty value is skipped; a malformed one is an error so a bad host is never allowed.
func EffectiveOutboundHosts ¶
EffectiveOutboundHosts is the full allowed host set: declared hosts plus the hosts parsed from operator config. The egress layer should call only this.
func Fetch ¶
Fetch downloads a package file through the SSRF-guarded client. Only https is accepted.
func Fingerprint ¶
Fingerprint is the stable identifier of an ed25519 public key.
func IndexSignedMessage ¶
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 ¶
ParsePublicKey decodes a standard base64 raw 32 byte ed25519 public key.
func SanitizeMarkdown ¶
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 ¶
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.
Types ¶
type ActionResult ¶
ActionResult is the plugin's answer to a button press.
type AuditEvent ¶
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 ¶
ChartPoint is one labelled value of a chart widget.
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
// EgressMode is open (shared bridge) or proxy (allowlisting sidecar).
EgressMode string
// EgressImage runs the sidecar; EgressEnv carries its limits through.
EgressImage string
EgressEnv map[string]string
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
// DenialsTTL caches the sidecar denial tally; DenialsTimeout bounds one read.
DenialsTTL time.Duration
DenialsTimeout 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.
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)
EnsureIsolatedNetwork(ctx context.Context, name string, opts docker.IsolatedNetworkOptions) (string, bool, error)
NetworkConnect(ctx context.Context, name, containerID 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 ¶
DNSError is a failure the plugin itself reported. Detail has the call's secret removed and is safe to show an operator.
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.
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.
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 ¶
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 ¶
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) 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 ¶
DNSListZones returns the zone names the credential can manage.
func (*Manager) DNSUpsert ¶
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) EgressDenials ¶
EgressDenials returns the sidecar's refusal counts grouped by plugin name. It is empty in open mode or when the sidecar cannot be read; it never errors.
func (*Manager) EgressMode ¶
EgressMode reports the active mode, "open" unless proxy was asked for.
func (*Manager) Enable ¶
Enable allows the supervisor to run the plugin. It needs a prior approval that includes read.
func (*Manager) Inspect ¶
Inspect parses and verifies a package and reports what installing or upgrading it would change. It writes nothing.
func (*Manager) Install ¶
Install verifies and records a plugin. It starts nothing, and approves only when the request carries the exact requested ability set.
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 ¶
Reconcile converges running containers to the stored plugin state. It is level-triggered and safe to call at any time.
func (*Manager) Remove ¶
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 ¶
Restart clears a plugin's failure backoff and breaker and starts it again now.
func (*Manager) Send ¶
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 ¶
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 ¶
TrustRemove removes a key. Plugins signed by it stop at the next supervisor pass.
func (*Manager) Upgrade ¶
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"`
// OutboundHostsFromConfig names config fields whose values are hosts or URLs the plugin may reach.
OutboundHostsFromConfig []string `yaml:"outbound_hosts_from_config" json:"outbound_hosts_from_config"`
Contributes Contrib `yaml:"contributes" json:"contributes"`
}
Manifest is the plugin.yaml a publisher signs together with the image digest.
func ParseManifest ¶
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) SlotPanels ¶
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 ¶
Catalog merges every source. force refetches instead of using a cache under a day old.
func (*Marketplace) Install ¶
func (m *Marketplace) Install(ctx context.Context, req MarketInstallRequest, actor string) (MarketInstallResult, error)
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.
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.
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"`
ConfigHosts []string `json:"config_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 (*SQLStore) AddKey ¶
func (s *SQLStore) AddKey(ctx context.Context, k TrustedKey) error
AddKey trusts a publisher key.
func (*SQLStore) DeletePlugin ¶
DeletePlugin removes a plugin row.
func (*SQLStore) InsertPlugin ¶
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 ¶
ListPlugins returns every plugin, by name.
func (*SQLStore) SetLastError ¶
SetLastError records the latest runtime failure without touching state or approvals.
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.