registry

package
v0.2.10 Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package registry implements Cloudpath plugin discovery, manifest validation, registry entries, digest verification and the plugins.lock contract.

Index

Constants

View Source
const LockFormatVersion = 1

LockFormatVersion is the current plugins.lock schema version.

Variables

View Source
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 means no expected digest was found on the release.
	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 means instance operations require A4 Plugin Host.
	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

func CheckCoreCompatibility(manifest *Manifest, current string) error

CheckCoreCompatibility verifies compatibility.core against current version. It supports operators >=, <=, >, <, =, ^ and ~, joined by whitespace or commas.

func EncodeRegistryEntry

func EncodeRegistryEntry(entry RegistryEntry) ([]byte, error)

EncodeRegistryEntry is a round-trip helper used by future registry readers.

func LoadManifestSchema added in v0.2.1

func LoadManifestSchema(path string) (data []byte, source string, err error)

LoadManifestSchema 返回校验 plugin.yaml 用的 JSON Schema,并说明它来自哪里。

path 非空且文件可读时用文件(运维可覆盖);文件不存在则回落到二进制内嵌副本。 发布的 CLI 因此在没有仓库 checkout 的机器上也能完成 inspect / install / validate, 而不需要额外分发 schema 文件。其它读取错误(权限、目录、IO)照常失败,不静默降级。

func NormalizeDigest

func NormalizeDigest(value string) (string, error)

NormalizeDigest accepts `sha256:<hex>`, `SHA256(...)=<hex>`, a bare hex digest, or a base64 SHA-256 digest from GitHub. It returns lowercase hex.

func ParseChecksumBlob

func ParseChecksumBlob(data []byte, filename string) (string, bool)

ParseChecksumBlob searches a checksum file for the digest of filename.

func ParseChecksumLine

func ParseChecksumLine(line string) (filename, digest string, ok bool)

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

func SHA256Bytes(data []byte) string

SHA256Bytes returns the lowercase hex sha256 digest of data.

func SHA256File

func SHA256File(path string) (string, error)

SHA256File returns the lowercase hex sha256 digest of a file.

func SafePluginID

func SafePluginID(id string) string

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

func ValidateContributions(m *Manifest) error

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

func ValidateManifestContract(manifest *Manifest, coreVersion string, supportedProtocol int) error

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

func VerifyDigest(path, expected string) error

VerifyDigest compares a file's sha256 digest with expected, using the same accepted digest representations as NormalizeDigest.

func WriteLockFile

func WriteLockFile(path string, lock *LockFile) error

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"`
	Requirements []map[string]any `yaml:"requirements,omitempty" json:"requirements,omitempty"`
}

ApplicationContribution is one application provided by an Application plugin.

type Attestation

type Attestation struct {
	Digest    string
	Predicate string
	Evidence  string
}

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"`
	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"`
	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"`
}

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

func (c *GitHubClient) Download(ctx context.Context, rawURL string, limit int64) ([]byte, error)

Download fetches a release asset into memory with a size guard.

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

func (c *GitHubClient) FetchManifest(ctx context.Context, repo Repo) ([]byte, error)

FetchManifest downloads repository root plugin.yaml through the contents API.

func (*GitHubClient) GetLatestRelease

func (c *GitHubClient) GetLatestRelease(ctx context.Context, repo Repo) (*Release, error)

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

func NewInstaller(pluginsDir, lockPath, schemaPath, coreVersion string) *Installer

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

func LoadLockFile(path string) (*LockFile, error)

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

func (l *LockFile) Remove(id string) bool

Remove deletes a lock entry 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

func LoadManifest(path string) (*Manifest, error)

LoadManifest reads a manifest file without schema validation.

func ParseManifest

func ParseManifest(data []byte) (*Manifest, error)

ParseManifest decodes manifest data without schema validation.

func ReadManifest

func ReadManifest(path string) (*Manifest, error)

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

func ValidateManifest(data []byte, schema []byte) (*Manifest, error)

ValidateManifest validates YAML manifest data against a JSON Schema document and returns the typed manifest on success.

func ValidateManifestFile

func ValidateManifestFile(path, schemaPath string) (*Manifest, error)

ValidateManifestFile reads and validates a manifest file.

func (*Manifest) PermissionSummary

func (m *Manifest) PermissionSummary() string

PermissionSummary returns a compact human-readable permission disclosure.

type ManifestSource

type ManifestSource struct {
	Data []byte
	Path string
}

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

type Repo struct {
	Owner string
	Name  string
	URL   string
}

Repo is a normalized GitHub repository reference.

func ResolveRepository

func ResolveRepository(input string) (Repo, error)

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 a JSON Schema draft-07 document. It intentionally implements only the keywords used by spec/plugin-manifest.schema.json: type, required, properties, const, enum and items. This keeps the CLI free of third-party schema dependencies.

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

Jump to

Keyboard shortcuts

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