Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ task license-fix # Add missing license headers
| `logging` | Pre-configured `*slog.Logger` factory with consistent ToolHive defaults (Alpha) |
| `oci/artifact` | Artifact-agnostic OCI tar/gzip/extraction/platform primitives shared by oci/skills and oci/plugins (Alpha) |
| `oci/skills` | OCI artifact types, media types, and registry operations for ToolHive skills (Alpha) |
| `oci/plugins` | OCI artifact types, media types, and registry operations for ToolHive plugins (Alpha) |
| `postgres` | PostgreSQL connection pool with optional AWS RDS IAM dynamic auth (Alpha) |
| `recovery` | HTTP panic recovery middleware (Beta) |
| `validation/http` | RFC 7230/8707 compliant HTTP header and URI validation |
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ The ToolHive ecosystem spans multiple Go repositories, and several of these proj
| `httperr` | Stable | Wrap errors with HTTP status codes |
| `logging` | Alpha | Pre-configured `*slog.Logger` factory with consistent ToolHive defaults |
| `oci/skills` | Alpha | OCI artifact types, media types, and registry operations for skills |
| `oci/plugins` | Alpha | OCI artifact types, media types, and registry operations for plugins |
| `postgres` | Alpha | PostgreSQL connection pool with optional AWS RDS IAM dynamic auth |
| `recovery` | Beta | HTTP panic recovery middleware |
| `validation/http` | Stable | RFC 7230/8707 compliant HTTP header and URI validation |
Expand Down
53 changes: 53 additions & 0 deletions oci/plugins/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2026 Stacklok, Inc.
// SPDX-License-Identifier: Apache-2.0

/*
Package plugins provides OCI artifact types, media types, local storage, and
remote registry operations for ToolHive plugin packages.

A plugin is an OCI artifact containing a .claude-plugin/plugin.json manifest and
its component directories. This package defines the constants, data structures,
storage layer, packager, and registry client that the rest of the ToolHive
ecosystem uses to package, push, pull, and cache plugins as OCI images.

# Media Types and Constants

Standard OCI media types and ToolHive-specific annotation/label keys:

// Artifact type identifies a plugin manifest
plugins.ArtifactTypePlugin // "dev.toolhive.plugins.v1"

// Annotations carry metadata in manifests
plugins.AnnotationPluginName
plugins.AnnotationPluginVersion
plugins.AnnotationPluginComponents

// Labels carry metadata in OCI image configs
plugins.LabelPluginName
plugins.LabelPluginFiles

# Registry Client

The [Registry] type implements [RegistryClient] for pushing and pulling plugin
artifacts to/from OCI-compliant registries (GHCR, ECR, Docker Hub, etc.). It
uses ORAS for registry operations and the Docker credential store for
authentication by default:

reg, err := plugins.NewRegistry()
// Push an artifact
err = reg.Push(ctx, store, indexDigest, "ghcr.io/myorg/my-plugin:v1.0.0")
// Pull an artifact
digest, err := reg.Pull(ctx, store, "ghcr.io/myorg/my-plugin:v1.0.0")

Use functional options to customise behaviour:

reg, err := plugins.NewRegistry(
plugins.WithPlainHTTP(true), // for local dev registries
plugins.WithCredentialStore(myStore), // custom auth
)

# Stability

This package is Alpha. Breaking changes are possible between minor versions.
*/
package plugins
36 changes: 36 additions & 0 deletions oci/plugins/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2026 Stacklok, Inc.
// SPDX-License-Identifier: Apache-2.0

package plugins

import "errors"

// Sentinel errors returned (wrapped) by the packager so callers can classify
// failures with errors.Is instead of matching error message strings. The
// underlying error message is preserved at each call site via fmt.Errorf
// with %w; only the classification is added.
var (
// ErrInvalidPluginDir indicates the plugin directory is missing, not a
// directory, or otherwise unsafe to read (e.g. contains path traversal).
ErrInvalidPluginDir = errors.New("invalid plugin directory")

// ErrPluginManifestMissing indicates .claude-plugin/plugin.json is not
// present in the plugin directory.
ErrPluginManifestMissing = errors.New(".claude-plugin/plugin.json missing")

// ErrInvalidPluginManifest indicates the plugin manifest is malformed,
// oversized, or missing required fields such as the plugin name.
ErrInvalidPluginManifest = errors.New("invalid plugin manifest")

// ErrTooManyFiles indicates the plugin directory exceeds the maximum
// allowed number of files.
ErrTooManyFiles = errors.New("too many files in plugin directory")

// ErrPluginTooLarge indicates the plugin directory exceeds the maximum
// allowed total size.
ErrPluginTooLarge = errors.New("plugin directory too large")

// ErrInvalidPluginFile indicates a per-file issue inside the plugin
// directory: a symlink, a non-regular file, or an unreadable entry.
ErrInvalidPluginFile = errors.New("invalid plugin file")
)
Loading
Loading