Documentation
¶
Overview ¶
Package registry implements Cloudpath plugin discovery, manifest validation, registry entries, digest verification and the plugins.lock contract.
Index ¶
- Constants
- Variables
- func CheckCoreCompatibility(manifest *Manifest, current string) error
- func HasCustomUI(m *Manifest) bool
- func LoadManifestSchema(path string) (data []byte, source string, err error)
- func NormalizeDigest(value string) (string, error)
- func ParseChecksumBlob(data []byte, filename string) (string, bool)
- func ParseChecksumLine(line string) (filename, digest string, ok bool)
- func PermissionExpansion(existing, incoming *Permissions) []string
- func SHA256Bytes(data []byte) string
- func SHA256File(path string) (string, error)
- func SafePluginID(id string) string
- func ValidateContributions(m *Manifest) error
- func ValidateManifestContract(manifest *Manifest, coreVersion string, supportedProtocol int) error
- func ValidateRegistryBinding(entry *RegistryEntry, manifest *Manifest, source string) error
- func ValidateRegistryEntry(entry RegistryEntry) error
- func VerifyDigest(path, expected string) error
- func WriteLockFile(path string, lock *LockFile) error
- type ApplicationContribution
- type Attestation
- type AttestationSubject
- type AttestationVerifier
- type Compatibility
- type ConnectorContribution
- type Contributes
- type DriverContribution
- type GitHubClient
- func (c *GitHubClient) Download(ctx context.Context, rawURL string, limit int64) ([]byte, error)
- func (c *GitHubClient) DownloadToFile(ctx context.Context, rawURL, dest string, limit int64) (string, error)
- func (c *GitHubClient) FetchManifest(ctx context.Context, repo Repo) ([]byte, error)
- func (c *GitHubClient) GetLatestRelease(ctx context.Context, repo Repo) (*Release, error)
- func (c *GitHubClient) Search(ctx context.Context, query string) ([]SearchResult, error)
- type InstallOptions
- type InstallResult
- type Installer
- type LockFile
- type LockedPlugin
- type Manifest
- type ManifestSource
- type Permissions
- type PluginUI
- type RegistryEntry
- type RegistryIndex
- type Release
- type ReleaseAsset
- type Repo
- type SchemaValidator
- type SearchResult
- type TrustMode
- type UIDevice
- type UINavigation
- type UIPage
- type UISection
Constants ¶
const LockFormatVersion = 1
LockFormatVersion is the current plugins.lock schema version.
Variables ¶
var ( // ErrInvalidManifest means the root plugin.yaml failed schema validation. ErrInvalidManifest = errors.New("invalid plugin manifest") // ErrCoreIncompatible means compatibility.core does not cover current Core. ErrCoreIncompatible = errors.New("core version incompatible") // ErrProtocolIncompatible means the plugin declares an unsupported protocol. ErrProtocolIncompatible = errors.New("plugin protocol incompatible") ErrDigestUnavailable = errors.New("release asset digest unavailable") // ErrDigestMismatch means the downloaded asset digest does not match. ErrDigestMismatch = errors.New("release asset digest mismatch") // ErrInvalidDigest means --digest was not a valid sha256 digest. ErrInvalidDigest = errors.New("invalid digest") // ErrTrustConfirmationRequired means install has no independent digest, // verified Registry entry or attestation, and unreviewed TOFU was not // explicitly allowed. ErrTrustConfirmationRequired = errors.New("trust confirmation required") // ErrRegistryBindingMismatch means a verified Registry entry did not bind // plugin id/version/source/digest/publisher. ErrRegistryBindingMismatch = errors.New("registry binding mismatch") // ErrAttestationFailed means an attestation verifier did not authenticate // the artifact. ErrAttestationFailed = errors.New("attestation verification failed") // ErrTrustDowngrade means an update would replace a verified installation // with unreviewed TOFU or change source/publisher without confirmation. ErrTrustDowngrade = errors.New("trust downgrade rejected") // ErrUnsupportedSource means the source could not be resolved. ErrUnsupportedSource = errors.New("unsupported plugin source") // ErrNotFound means a repository, manifest or installed plugin was not found. ErrNotFound = errors.New("plugin not found") ErrHostRuntimeUnavailable = errors.New("plugin host runtime unavailable") // ErrPermissionConfirmationRequired means the user did not confirm permissions. ErrPermissionConfirmationRequired = errors.New("permission confirmation required") // ErrRateLimited means the GitHub API rate limit was exceeded. ErrRateLimited = errors.New("github api rate limit exceeded") // ErrUnsafeArtifact means a plugin id, asset name or download URL would escape // the plugin data root or use an unsupported scheme. ErrUnsafeArtifact = errors.New("unsafe plugin artifact") )
Sentinels used by the CLI to translate failures into stable error codes.
Functions ¶
func CheckCoreCompatibility ¶
CheckCoreCompatibility verifies compatibility.core against current version. It supports operators >=, <=, >, <, =, ^ and ~, joined by whitespace or commas.
func HasCustomUI ¶ added in v0.2.23
HasCustomUI reports whether a manifest declares at least one custom UI entry.
func LoadManifestSchema ¶ added in v0.2.1
LoadManifestSchema 返回校验 plugin.yaml 用的 JSON Schema,并说明它来自哪里。
path 非空且文件可读时用文件(运维可覆盖);文件不存在则回落到二进制内嵌副本。 发布的 CLI 因此在没有仓库 checkout 的机器上也能完成 inspect / install / validate, 而不需要额外分发 schema 文件。其它读取错误(权限、目录、IO)照常失败,不静默降级。
func NormalizeDigest ¶
NormalizeDigest accepts `sha256:<hex>`, `SHA256(...)=<hex>`, a bare hex digest, or a base64 SHA-256 digest from GitHub. It returns lowercase hex.
func ParseChecksumBlob ¶
ParseChecksumBlob searches a checksum file for the digest of filename.
func ParseChecksumLine ¶
ParseChecksumLine parses common sha256sum / GitHub checksum formats.
func PermissionExpansion ¶
func PermissionExpansion(existing, incoming *Permissions) []string
PermissionExpansion returns the permission entries present in incoming but not in existing, formatted as category:value, in deterministic order. It returns nil when incoming is a subset of or equal to existing, i.e. no expansion.
func SHA256Bytes ¶
SHA256Bytes returns the lowercase hex sha256 digest of data.
func SHA256File ¶
SHA256File returns the lowercase hex sha256 digest of a file.
func SafePluginID ¶
SafePluginID turns arbitrary plugin IDs into a filesystem-safe directory name. It never returns ".", ".." or a path separator, so the result is always a single safe path component that cannot escape its parent.
func ValidateContributions ¶
ValidateContributions enforces the typed contributes contract:
- every contribution id is stable and filesystem-safe: non-empty, no path separators, no "..", no whitespace and no control characters;
- contribution ids are unique across the whole plugin;
- contributes match the plugin kind (a Driver plugin must contribute drivers, never applications or connectors, and so on).
A plugin that declares no contributes block is unchanged and remains valid, so legacy manifests keep working.
func ValidateManifestContract ¶
ValidateManifestContract enforces the id/version/protocol/compatibility contract for a manifest before installation. Any failure is returned wrapped with a sentinel so the CLI can map it to a stable error code. It deliberately fails closed on malformed or unsupported values.
func ValidateRegistryBinding ¶
func ValidateRegistryBinding(entry *RegistryEntry, manifest *Manifest, source string) error
ValidateRegistryBinding enforces the fixed binding for a verified Registry entry: plugin id, version, source, digest and publisher must all match the resolved install. Any inconsistency fails closed.
func ValidateRegistryEntry ¶
func ValidateRegistryEntry(entry RegistryEntry) error
ValidateRegistryEntry enforces the fixed registry fields. A registry record is source-controlled metadata, so malformed entries are rejected before use.
func VerifyDigest ¶
VerifyDigest compares a file's sha256 digest with expected, using the same accepted digest representations as NormalizeDigest.
func WriteLockFile ¶
WriteLockFile writes the lockfile atomically using a same-directory temporary file, fsync, then rename.
Types ¶
type ApplicationContribution ¶
type ApplicationContribution struct {
ID string `yaml:"id" json:"id"`
Title string `yaml:"title,omitempty" json:"title,omitempty"`
I18n map[string]string `yaml:"i18n,omitempty" json:"i18n,omitempty"`
Requirements []map[string]any `yaml:"requirements,omitempty" json:"requirements,omitempty"`
UI *PluginUI `yaml:"ui,omitempty" json:"ui,omitempty"`
}
ApplicationContribution is one application provided by an Application plugin.
type Attestation ¶
Attestation is the non-secret evidence returned by a verifier. Evidence must never contain tokens, keys or other secrets; it is persisted to plugins.lock.
type AttestationSubject ¶
type AttestationSubject struct {
Owner string
Name string
Digest string // lowercase hex sha256 of the downloaded artifact
}
AttestationSubject is the artifact an attestation must cover.
type AttestationVerifier ¶
type AttestationVerifier interface {
Verify(ctx context.Context, subject AttestationSubject) (*Attestation, error)
}
AttestationVerifier authenticates a build attestation for a release asset. Implementations must fail closed: an error means the artifact is unverified.
type Compatibility ¶
type Compatibility struct {
Core string `yaml:"core,omitempty" json:"core,omitempty"`
}
Compatibility describes Core version compatibility.
type ConnectorContribution ¶
type ConnectorContribution struct {
ID string `yaml:"id" json:"id"`
Title string `yaml:"title,omitempty" json:"title,omitempty"`
I18n map[string]string `yaml:"i18n,omitempty" json:"i18n,omitempty"`
Direction string `yaml:"direction,omitempty" json:"direction,omitempty"`
Host string `yaml:"host,omitempty" json:"host,omitempty"`
}
ConnectorContribution is one connector provided by a Connector plugin.
type Contributes ¶
type Contributes struct {
Drivers []DriverContribution `yaml:"drivers,omitempty" json:"drivers,omitempty"`
Applications []ApplicationContribution `yaml:"applications,omitempty" json:"applications,omitempty"`
Connectors []ConnectorContribution `yaml:"connectors,omitempty" json:"connectors,omitempty"`
}
Contributes is the typed manifest contributions block. A plugin declares the drivers / applications / connectors it provides; each contribution has a stable id that is distinct from the plugin id, and a single plugin may contribute multiple entries of its own kind.
type DriverContribution ¶
type DriverContribution struct {
ID string `yaml:"id" json:"id"`
Title string `yaml:"title,omitempty" json:"title,omitempty"`
I18n map[string]string `yaml:"i18n,omitempty" json:"i18n,omitempty"`
Descriptor string `yaml:"descriptor,omitempty" json:"descriptor,omitempty"`
ConfigSchema string `yaml:"configSchema,omitempty" json:"configSchema,omitempty"`
Discovery string `yaml:"discovery,omitempty" json:"discovery,omitempty"`
CapabilityCatalog string `yaml:"capabilityCatalog,omitempty" json:"capabilityCatalog,omitempty"`
UI *PluginUI `yaml:"ui,omitempty" json:"ui,omitempty"`
}
DriverContribution is one driver provided by a Driver plugin.
type GitHubClient ¶
type GitHubClient struct {
HTTPClient *http.Client
Token string
BaseURL string // API base URL; defaults to githubAPIBase
MaxJSONBytes int64 // response body cap for JSON endpoints
MaxRetries int // retries for transient network errors
}
GitHubClient is a small stdlib HTTP client for the GitHub REST API. BaseURL and HTTPClient are injectable so tests can point the client at an httptest server.
func NewGitHubClient ¶
func NewGitHubClient() *GitHubClient
NewGitHubClient returns a client using GITHUB_TOKEN or GH_TOKEN.
func (*GitHubClient) DownloadToFile ¶
func (c *GitHubClient) DownloadToFile(ctx context.Context, rawURL, dest string, limit int64) (string, error)
DownloadToFile streams rawURL to dest, computing the sha256 digest incrementally and enforcing limit. On any failure the destination file is removed so no half file is left behind. It returns the lowercase hex digest.
func (*GitHubClient) FetchManifest ¶
FetchManifest downloads repository root plugin.yaml through the contents API.
func (*GitHubClient) GetLatestRelease ¶
GetLatestRelease returns the latest GitHub Release and its assets.
func (*GitHubClient) Search ¶
func (c *GitHubClient) Search(ctx context.Context, query string) ([]SearchResult, error)
Search queries GitHub repositories with topic cloudpath-plugin plus query. It follows Link rel="next" pagination up to a bounded number of pages/results.
type InstallOptions ¶
type InstallOptions struct {
Source string
Asset string
// Digest is an independent, user-supplied sha256 (--digest). It is never
// derived from a same-origin release response.
Digest string
// ConfirmPerms confirms the displayed permissions (fresh installs always
// require it; upgrades require it only on permission expansion).
ConfirmPerms bool
// AllowUnreviewed explicitly permits trust-on-first-use from a same-origin
// release checksum. The resulting lock entry is recorded as unverified.
AllowUnreviewed bool
// Existing is the previously locked plugin during an update. When set,
// update trust invariants are enforced before any install side effect.
Existing *LockedPlugin
}
InstallOptions controls one install operation.
type InstallResult ¶
type InstallResult struct {
Manifest *Manifest
PluginDir string
AssetPath string
Digest string
// Mode records the trust mode used to authenticate the artifact.
Mode TrustMode
// Verified is true only when independent evidence authenticated the artifact.
Verified bool
// Evidence is a non-secret, human-readable reference to the trust evidence.
Evidence string
// SchemaSource 说明 manifest schema 来自哪里(file:<path> 或 embedded),
// 用于验收时证明校验真的发生过,而不是被跳过。
SchemaSource string
LockEntry LockedPlugin
RegistryEntry RegistryEntry
}
InstallResult is the successful local installation outcome.
type Installer ¶
type Installer struct {
Client *GitHubClient
PluginsDir string
LockPath string
SchemaPath string
CoreVersion string
SupportedProtocol int
MaxDownloadBytes int64
// RegistryIndex, when set, supplies curated verified entries.
RegistryIndex *RegistryIndex
// Attestation, when set, authenticates artifacts via build attestations.
// A nil value means attestation is unavailable and is never treated as
// verified evidence.
Attestation AttestationVerifier
}
Installer installs a GitHub release asset into plugins.d/ after verification.
func NewInstaller ¶
NewInstaller constructs an installer with repository-local defaults.
func (*Installer) Install ¶
func (i *Installer) Install(ctx context.Context, opts InstallOptions) (*InstallResult, error)
Install resolves the source, validates manifest/version/protocol fail-closed, resolves a trust decision (independent digest, verified Registry entry, attestation, or explicitly allowed unreviewed TOFU), downloads the selected release asset while streaming its sha256, verifies the digest, writes plugins.d/ and updates plugins.lock. The asset is stored under the plugin dir by digest name, so a malicious asset name cannot overwrite another plugin or escape the plugin data root.
type LockFile ¶
type LockFile struct {
FormatVersion int `yaml:"format_version" json:"formatVersion"`
Plugins []LockedPlugin `yaml:"plugins" json:"plugins"`
}
LockFile is the local, machine-readable installation record.
func LoadLockFile ¶
LoadLockFile reads a lockfile. A missing file is treated as an empty lock.
func NewLockFile ¶
func NewLockFile() *LockFile
NewLockFile returns an empty lockfile with the current format version.
func (*LockFile) Find ¶
func (l *LockFile) Find(id string) (*LockedPlugin, bool)
Find returns a locked plugin by id.
func (*LockFile) Upsert ¶
func (l *LockFile) Upsert(entry LockedPlugin)
Upsert adds or replaces a lock entry by id.
type LockedPlugin ¶
type LockedPlugin struct {
ID string `yaml:"id" json:"id"`
Version string `yaml:"version" json:"version"`
Digest string `yaml:"digest" json:"digest"`
Source string `yaml:"source" json:"source"`
Verified bool `yaml:"verified" json:"verified"`
Mode TrustMode `yaml:"mode,omitempty" json:"mode,omitempty"`
Evidence string `yaml:"evidence,omitempty" json:"evidence,omitempty"`
VerifiedPublisher string `yaml:"verifiedPublisher,omitempty" json:"verifiedPublisher,omitempty"`
Protocol int `yaml:"protocol,omitempty" json:"protocol,omitempty"`
Compatibility string `yaml:"compatibility,omitempty" json:"compatibility,omitempty"`
}
LockedPlugin pins one installed plugin to an exact release and digest.
type Manifest ¶
type Manifest struct {
APIVersion string `yaml:"apiVersion" json:"apiVersion"`
Kind string `yaml:"kind" json:"kind"`
ID string `yaml:"id" json:"id"`
Version string `yaml:"version" json:"version"`
Protocol int `yaml:"protocol" json:"protocol"`
Entrypoint string `yaml:"entrypoint" json:"entrypoint"`
Compatibility Compatibility `yaml:"compatibility,omitempty" json:"compatibility,omitempty"`
Permissions Permissions `yaml:"permissions,omitempty" json:"permissions,omitempty"`
Capabilities []string `yaml:"capabilities,omitempty" json:"capabilities,omitempty"`
Requirements []map[string]any `yaml:"requirements,omitempty" json:"requirements,omitempty"`
Contributes *Contributes `yaml:"contributes,omitempty" json:"contributes,omitempty"`
}
Manifest is the machine-readable root plugin.yaml contract. The field names follow spec/plugin-manifest.schema.json exactly.
func LoadManifest ¶
LoadManifest reads a manifest file without schema validation.
func ParseManifest ¶
ParseManifest decodes manifest data without schema validation.
func ReadManifest ¶
ReadManifest reads and parses a plugin.yaml file without schema or semantic validation. It is the untyped read path; callers that need the full contract should use ValidateManifest / ValidateManifestFile.
func ValidateManifest ¶
ValidateManifest validates YAML manifest data against a JSON Schema document and returns the typed manifest on success.
func ValidateManifestFile ¶
ValidateManifestFile reads and validates a manifest file.
func (*Manifest) PermissionSummary ¶
PermissionSummary returns a compact human-readable permission disclosure.
func (*Manifest) PublicContributions ¶ added in v0.2.23
func (m *Manifest) PublicContributions() api.PluginContributionsData
PublicContributions maps a manifest's typed contributions to the public API DTO. This is the single whitelist projection used by Edge and AppHost installation status; local paths, entrypoints and requirement payloads are never copied.
type ManifestSource ¶
ManifestSource is manifest bytes plus its resolution label/path.
func ReadManifestSource ¶
func ReadManifestSource(ctx context.Context, client *GitHubClient, source, pluginsDir string) (*ManifestSource, error)
ReadManifestSource reads root plugin.yaml from a local path, a GitHub repository URL, or an installed plugin id under plugins.d.
type Permissions ¶
type Permissions struct {
Hardware []string `yaml:"hardware,omitempty" json:"hardware,omitempty"`
Network []string `yaml:"network,omitempty" json:"network,omitempty"`
Filesystem []string `yaml:"filesystem,omitempty" json:"filesystem,omitempty"`
Secrets []string `yaml:"secrets,omitempty" json:"secrets,omitempty"`
}
Permissions is the permission disclosure block.
type PluginUI ¶ added in v0.2.23
type PluginUI struct {
APIVersion int `yaml:"apiVersion" json:"apiVersion"`
Pages []UIPage `yaml:"pages,omitempty" json:"pages,omitempty"`
Device *UIDevice `yaml:"device,omitempty" json:"device,omitempty"`
}
PluginUI is the declarative UI contribution declared by an Application or Driver contribution. It is metadata only: no arbitrary script or local path is executable through this type.
type RegistryEntry ¶
type RegistryEntry struct {
ID string `yaml:"id" json:"id"`
Version string `yaml:"version" json:"version"`
Kind string `yaml:"kind" json:"kind"`
Source string `yaml:"source" json:"source"`
Digest string `yaml:"digest" json:"digest"`
VerifiedPublisher string `yaml:"verifiedPublisher,omitempty" json:"verifiedPublisher,omitempty"`
Protocol int `yaml:"protocol" json:"protocol"`
Compatibility string `yaml:"compatibility" json:"compatibility"`
}
RegistryEntry is one reviewed plugin record. Field names are fixed by docs/architecture/registry.md and stay stable in lock/index files.
type RegistryIndex ¶
type RegistryIndex struct {
Version int `yaml:"version" json:"version"`
Plugins []RegistryEntry `yaml:"plugins" json:"plugins"`
}
RegistryIndex is the in-memory form of a curated registry index.
func LoadRegistryIndex ¶
func LoadRegistryIndex(path string) (*RegistryIndex, error)
LoadRegistryIndex reads and validates a curated registry index YAML file. Malformed entries are rejected before use so a corrupt index fails closed rather than silently supplying unverified metadata.
func (*RegistryIndex) Find ¶
func (idx *RegistryIndex) Find(id string) (*RegistryEntry, bool)
Find returns the registry entry for id.
type Release ¶
type Release struct {
TagName string `json:"tag_name"`
Name string `json:"name"`
URL string `json:"html_url"`
Assets []ReleaseAsset `json:"assets"`
}
Release is a subset of GitHub Release metadata needed by the installer.
type ReleaseAsset ¶
type ReleaseAsset struct {
Name string `json:"name"`
URL string `json:"browser_download_url"`
Size int64 `json:"size"`
}
ReleaseAsset describes one downloadable GitHub Release asset.
type Repo ¶
Repo is a normalized GitHub repository reference.
func ResolveRepository ¶
ResolveRepository parses an input into a GitHub owner/repo reference.
type SchemaValidator ¶
type SchemaValidator struct {
// contains filtered or unexported fields
}
SchemaValidator validates YAML/JSON values against the JSON Schema subset used by CloudPath contract assets. It intentionally stays dependency-free, but supports local $ref/$defs, object/array/string/number bounds, patterns, additionalProperties, enums/consts and composition keywords used by the manifest and plugin UI schemas.
func NewSchemaValidator ¶
func NewSchemaValidator(data []byte) (*SchemaValidator, error)
NewSchemaValidator parses a JSON Schema document.
func (*SchemaValidator) Validate ¶
func (v *SchemaValidator) Validate(value any) error
Validate checks value against the loaded schema.
type SearchResult ¶
type SearchResult struct {
FullName string `json:"full_name"`
Name string `json:"name"`
Description string `json:"description"`
Stars int `json:"stargazers_count"`
URL string `json:"html_url"`
}
SearchResult is a plugin candidate from the open discovery channel.
type TrustMode ¶
type TrustMode string
TrustMode records how an installed plugin's artifact was authenticated. It is persisted in plugins.lock and surfaced by inspect/install.
const ( // TrustModeExplicitDigest means the user supplied an independent sha256 // digest out of band (--digest). This is verified evidence. TrustModeExplicitDigest TrustMode = "explicit-digest" // TrustModeVerifiedRegistry means a curated Registry entry bound the plugin // id/version/source/digest/publisher. This is verified evidence. TrustModeVerifiedRegistry TrustMode = "verified-registry" // TrustModeAttestation means a build attestation verifier authenticated the // artifact. This is verified evidence. TrustModeAttestation TrustMode = "attestation" // TrustModeUnreviewedTOFU means only the same-origin release checksum was // available. It is never treated as verified. TrustModeUnreviewedTOFU TrustMode = "unreviewed-tofu" )
type UIDevice ¶ added in v0.2.23
type UIDevice struct {
Sections []UISection `yaml:"sections,omitempty" json:"sections,omitempty"`
}
UIDevice extends a Driver's device detail page.
type UINavigation ¶ added in v0.2.23
type UINavigation struct {
}
UINavigation declares the stable business navigation entry for an Application.
type UIPage ¶ added in v0.2.23
type UIPage struct {
ID string `yaml:"id" json:"id"`
Title string `yaml:"title" json:"title"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
I18n map[string]string `yaml:"i18n,omitempty" json:"i18n,omitempty"`
Sections []UISection `yaml:"sections" json:"sections"`
}
UIPage is one declarative page under an Application route.
type UISection ¶ added in v0.2.23
type UISection struct {
Type string `yaml:"type" json:"type"`
Title string `yaml:"title,omitempty" json:"title,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
EmptyText string `yaml:"emptyText,omitempty" json:"emptyText,omitempty"`
Source string `yaml:"source,omitempty" json:"source,omitempty"`
RecordType string `yaml:"recordType,omitempty" json:"recordType,omitempty"`
Presentation string `yaml:"presentation,omitempty" json:"presentation,omitempty"`
Text string `yaml:"text,omitempty" json:"text,omitempty"`
Entry string `yaml:"entry,omitempty" json:"entry,omitempty"`
Scopes []string `yaml:"scopes,omitempty" json:"scopes,omitempty"`
Fields []map[string]any `yaml:"fields,omitempty" json:"fields,omitempty"`
}
UISection is a Core-rendered, allowlisted UI primitive.