Documentation
¶
Overview ¶
Package manifestv2 owns the plugin manifest v2 contract (contract:plugin.manifest).
Purpose: one machine-checkable definition of plugin.json. The Go types here generate schemas/plugin-manifest.v2.schema.json (tools/schemagen), Load is the single reader (v2 natively, v1 through one normalizer), and Projection is the single derivation of the compatibility keys that released 1.4.x CLIs decode.
Inputs: plugin.json bytes or a path. Outputs: a validated *Manifest or a coded error (E106, E111-E114). Constraints: layer L0 (Constitution 2.1): the standard library, internal/errs and internal/compat only; it never imports internal/plugin or cmd/. Released CLIs decode plugin.json permissively, so a v2 key never reuses a v1 key with another JSON type (Epic D1, ADR 0009).
Index ¶
- Constants
- Variables
- func ApplyCompat(data []byte) ([]byte, error)
- func ApplyProjection(m *Manifest)
- func Canonical(data []byte) ([]byte, error)
- func Canonicalize(m *Manifest)
- func CheckCLITargets(plugin string, targets []CLITarget) error
- func CheckCompat(m *Manifest) error
- func IsMappedV1Key(k string) bool
- func IsV1Version(v json.RawMessage) bool
- func Marshal(m *Manifest) ([]byte, error)
- func PostgresExtensions(m *Manifest) []string
- func RegistryKeys(data []byte) []string
- func Validate(m *Manifest) error
- type Arg
- type CLICommand
- type CLITarget
- type Commands
- type Compat
- type CompatBlock
- type Confirm
- type Deprecation
- type Env
- type EnvVar
- type Flag
- type GraphQLBlock
- type GraphQLEntityKey
- type Manifest
- type Migrations
- type MultiApp
- type Plan
- type Requires
- type RestRoute
- type Route
- type Seed
- type Service
- type Shared
- type Subcommand
- type SystemDependencies
- type SystemDependency
- type Unmapped
Constants ¶
const ( LicenseFree = "free" LicenseLicensed = "licensed" )
Licence values (D2: bundle membership is not a manifest field).
const ( MaturityImplemented = "implemented" MaturityExperimental = "experimental" MaturityScaffolded = "scaffolded" MaturityPlanned = "planned" MaturityDeferred = "deferred" )
Maturity values (the lifecycle enum; the v1 `status` key is a compatibility key).
const ( StateDeprecated = "deprecated" StateEOL = "eol" )
Deprecation states (deprecation.state, required when maturity is deferred).
const ( KindCompose = "compose" KindCLI = "cli" KindLibrary = "library" )
Service kinds.
const ( SideRead = "read" SideWrite = "write" SideRemote = "remote" SideDestructive = "destructive" OutDocument = "document" OutStream = "stream" OutInteractive = "interactive" JSONNone = "none" JSONLegacy = "legacy" JSONEnvelope = "envelope" SurfaceAll = "all" SurfaceCLIOnly = "cli-only" )
Command attribute values (CANON D5-D7 vocabulary).
const ( NamePattern = `^[a-z][a-z0-9-]*$` BinaryPattern = `^nself-[a-z][a-z0-9-]*$` VersionPattern = `^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$` )
Patterns, shared by validation and the generated schema.
const ManifestVersion = 2
ManifestVersion is the value of manifest_version in a v2 file.
Variables ¶
var ( Licenses = []string{LicenseFree, LicenseLicensed} Maturities = []string{MaturityImplemented, MaturityExperimental, MaturityScaffolded, MaturityPlanned, MaturityDeferred} States = []string{StateDeprecated, StateEOL} ServiceKinds = []string{KindCompose, KindCLI, KindLibrary} SideEffects = []string{SideRead, SideWrite, SideRemote, SideDestructive} Outputs = []string{OutDocument, OutStream, OutInteractive} JSONModes = []string{JSONNone, JSONLegacy, JSONEnvelope} Surfaces = []string{SurfaceAll, SurfaceCLIOnly} Languages = []string{"go", "rust", "typescript", "python", "bash"} )
Enum lists, shared by validation and the generated schema.
var CoreVerbs = []string{"init", "start", "stop", "restart", "status", "logs", "doctor", "build", "reset",
"clean", "add", "remove", "config", "db", "backup", "deploy", "update", "license", "mcp", "exec"}
CoreVerbs are the ADR 0016 core command verbs a plugin may not claim (E113). TestCoreVerbsMatchCanon keeps this list equal to internal/canon.Verbs.
var ForbiddenKeys = []string{"author_public_key", "bundles", "checksum", "signature", "tier_pair"}
ForbiddenKeys are rejected (E111) in a v2 file: they belong to the registry, the release pipeline or ADR 0008 (bundle membership), never to a manifest.
Functions ¶
func ApplyCompat ¶
ApplyCompat returns the canonical bytes of a v2 file with only its compatibility keys rewritten to Projection (E112 is what a stale file fails). Every other key keeps its value.
func ApplyProjection ¶
func ApplyProjection(m *Manifest)
ApplyProjection writes Projection(m) into m's compatibility keys.
func Canonical ¶
Canonical re-encodes any JSON document with sorted keys, 2-space indent and a trailing newline. Numbers keep their literal text.
func Canonicalize ¶
func Canonicalize(m *Manifest)
Canonicalize makes absent and empty the same value (nil), so a v1 file and its v2 twin decode to equal Manifests. confirm.flags keeps its empty list.
func CheckCLITargets ¶ added in v1.4.14
CheckCLITargets refuses command targets the proxy cannot serve (D-0259).
A command outside NamePattern (a space, an upper-case letter, a leading digit) or a binary outside BinaryPattern is never reached by `nself <command>`; a core verb never reaches the proxy because the built-in command wins, so its binary would sit unused; a command declared twice publishes one binary over itself. plugin names the plugin in the message.
Outputs: nil, E106 (name or binary pattern, duplicate) or E113 (core verb), for the first bad target. CoreVerbs is the canon list (TestCoreVerbsMatchCanon keeps it equal to internal/canon.Verbs).
func CheckCompat ¶
CheckCompat proves the compatibility keys of m equal Projection(m) (E112). Only the derived keys are compared: the carried ones have no v2 source.
func IsMappedV1Key ¶
IsMappedV1Key reports whether the normalizer carries or converts the v1 key k in at least one shape (the migrate tool refuses a drop list that names one).
func IsV1Version ¶
func IsV1Version(v json.RawMessage) bool
IsV1Version reports whether a raw manifest_version value marks a v1 file. Released CLIs ignore the key, so every spelling of "one or nothing" loads as v1: null, 0, "1", 1 and 1.0 (an absent key is v1 too).
func Marshal ¶
Marshal renders m as the canonical plugin.json: keys sorted at every level, 2-space indent, trailing newline, HTML never escaped.
func PostgresExtensions ¶
PostgresExtensions returns requires.postgres_extensions, empty when absent (the install check of P7-ADOPT-06 reads it).
func RegistryKeys ¶
RegistryKeys lists the registry-owned keys (E111 in v2) a v1 file carries; converting removes them on purpose and the migrate tool says so.
func Validate ¶
Validate checks the structural rules of a v2 manifest: required keys, slug and semver shape, enums, service rules, np_ tables, boot migrations, deprecation state and the commands block. Failures are E106 naming the field, except a core-verb command, which is E113. It does not check compatibility keys (CheckCompat, E112) or the runtime permission allowlist.
Types ¶
type Arg ¶
type Arg struct {
Name string `json:"name"`
Required bool `json:"required,omitempty"`
Variadic bool `json:"variadic,omitempty"`
Secret bool `json:"secret,omitempty"`
}
Arg is a positional argument (registry Arg shape plus secret).
type CLICommand ¶
CLICommand is one v1 cliCommands entry.
type CLITarget ¶
CLITarget is one command binary a CLI plugin publishes: the binary file name the command proxy execs and the command that names it.
func CLITargets ¶
CLITargets returns every command binary a plugin installs, by the installer's rule (P7-PLUG-55). It is the one definition: internal/plugin cliBinaryNames calls it, and manifestv2migrate -targets prints it, so the release pipeline builds exactly what the installer will look for.
Inputs are the v1 view of a manifest (a v2 manifest through Projection): pluginType, binaryName and the cliCommands names. A plugin whose pluginType is set and is not "cli" has none. Each cliCommands entry with a name publishes nself-<name>. With no usable entry, an explicit binaryName is the single binary, and pluginType "cli" without one falls back to nself-<name>. The command of a single binary is its name without the nself- prefix.
func CLITargetsOf ¶
CLITargetsOf is CLITargets over a parsed manifest through its compatibility projection, which is what a released CLI reads from a v2 plugin.json.
type Commands ¶
type Commands struct {
Command string `json:"command"`
Binary string `json:"binary"`
Summary string `json:"summary,omitempty"`
SideEffect string `json:"side_effect,omitempty"`
Output string `json:"output,omitempty"`
JSON string `json:"json,omitempty"`
Confirm *Confirm `json:"confirm,omitempty"`
Surface string `json:"surface,omitempty"`
Subcommands []Subcommand `json:"subcommands,omitempty"`
}
Commands is the CLI surface block (v1 `cli` is a different key and type). The optional root summary, side_effect, output and json decode and round-trip; absent they mean side_effect destructive and json none (CANON mount contract).
type Compat ¶
type Compat struct {
PluginType string `json:"pluginType,omitempty"`
BinaryName string `json:"binaryName,omitempty"`
CLICommands []CLICommand `json:"cliCommands,omitempty"`
EntryPoint string `json:"entryPoint,omitempty"`
Runtime string `json:"runtime,omitempty"`
CLI string `json:"cli,omitempty"`
MinNselfVersion string `json:"minNselfVersion,omitempty"`
Status string `json:"status,omitempty"`
IsCommercial bool `json:"isCommercial,omitempty"`
LicenseType string `json:"licenseType,omitempty"`
RequiresLicense bool `json:"requires_license,omitempty"`
Tier string `json:"tier,omitempty"`
}
Compat holds the generated compatibility keys (Epic D1). They exist so released 1.4.x CLIs keep decoding a v2 file; authors never write them. Projection derives them; CheckCompat (E112) proves they match. They are removed at v1.6.0.
EntryPoint, Runtime, CLI and the cliCommands entries other than the canonical command are carried verbatim from v1: no v2 field defines them. Tier and LicenseType are carried for a licensed plugin (max, cloud, internal) and generated as free for a free one.
func Projection ¶
Projection derives the compatibility keys of m (Epic D1). Derived keys are recomputed from the v2 fields: pluginType and binaryName from commands, minNselfVersion from requires.nself, status from maturity and deprecation.state, isCommercial, licenseType, requires_license and tier from license, and the description of the canonical command's cliCommands entry from commands.summary. EntryPoint, Runtime, CLI and the other cliCommands entries are carried from m unchanged, and so are a licensed plugin's tier and licenseType (pro when it has none).
type CompatBlock ¶
type CompatBlock struct {
Nself string `json:"nself,omitempty"`
Requires map[string]string `json:"requires,omitempty"`
}
CompatBlock is the v1 `compat` key: CLI and service version ranges.
type Confirm ¶
Confirm names the bool flags that confirm a destructive action. An empty Flags list is valid: a nonce-only round trip (Epic D16).
type Deprecation ¶
type Deprecation struct {
AnnouncedDate string `json:"announcedDate,omitempty"`
EOLDate string `json:"eolDate,omitempty"`
ReplacedBy string `json:"replacedBy,omitempty"`
MigrationGuide string `json:"migrationGuide,omitempty"`
MigrationScript string `json:"migrationScript,omitempty"`
// State is deprecated or eol; released CLIs ignore it.
State string `json:"state,omitempty"`
}
Deprecation is the v1 deprecation block plus the v2 state.
type Env ¶
type Env struct {
Required []string `json:"required,omitempty"`
Optional []string `json:"optional,omitempty"`
}
Env lists environment variable names.
type EnvVar ¶
type EnvVar struct {
Name string `json:"name"`
Required bool `json:"required"`
Description string `json:"description"`
Default string `json:"default,omitempty"`
}
EnvVar describes an environment variable (v1 shape).
type Flag ¶
type Flag struct {
Name string `json:"name"`
Shorthand string `json:"shorthand,omitempty"`
Type string `json:"type"`
Default string `json:"default,omitempty"`
Usage string `json:"usage,omitempty"`
Hidden bool `json:"hidden,omitempty"`
Required bool `json:"required,omitempty"`
Persistent bool `json:"persistent,omitempty"`
Env string `json:"env,omitempty"`
SideEffect string `json:"side_effect,omitempty"`
JSON string `json:"json,omitempty"`
Output string `json:"output,omitempty"`
Secret bool `json:"secret,omitempty"`
CLIOnly bool `json:"cli_only,omitempty"`
}
Flag is a flag (registry Flag shape plus secret and cli_only).
type GraphQLBlock ¶
type GraphQLBlock struct {
Enabled bool `json:"enabled"`
SubgraphName string `json:"subgraph_name"`
SubgraphURL string `json:"subgraph_url"`
SchemaPath string `json:"schema_path,omitempty"`
Entities []GraphQLEntityKey `json:"entities,omitempty"`
}
GraphQLBlock registers the plugin as a federation subgraph (v1 shape).
type GraphQLEntityKey ¶
GraphQLEntityKey names an Apollo Federation entity type.
type Manifest ¶
type Manifest struct {
ManifestVersion int `json:"manifest_version"`
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
Category string `json:"category"`
// License is free or licensed (a v1 plugin.json carried an SPDX-style string
// here; released CLIs only require it to be non-empty).
License string `json:"license"`
// LicenseSPDX is the licence text a v1 file carried in `license` (an SPDX
// identifier or "Source-Available"). Optional; `plugin info` shows it.
LicenseSPDX string `json:"license_spdx,omitempty"`
Maturity string `json:"maturity"`
// Installable defaults to true when absent (ADR 0009).
Installable *bool `json:"installable,omitempty"`
Requires *Requires `json:"requires,omitempty"`
Service *Service `json:"service"`
// Commands is nil for a plugin that adds no CLI command.
Commands *Commands `json:"commands,omitempty"`
Schema *string `json:"schema,omitempty"`
Migrations *Migrations `json:"migrations,omitempty"`
Seed *Seed `json:"seed,omitempty"`
Env *Env `json:"env,omitempty"`
Routes []Route `json:"routes,omitempty"`
// Capabilities lists what the plugin offers (a v1 key some plugins carry,
// read from the installed plugin.json by the MCP plugin). Optional, same
// name and type as v1.
Capabilities []string `json:"capabilities,omitempty"`
DocsURL string `json:"docs_url,omitempty"`
Compat
}
Manifest is a manifest_version 2 plugin.json: the canonical v2 keys, the shared v1 keys (same name and JSON type as v1) and the generated compatibility keys.
func Load ¶
Load reads a plugin.json and returns the validated v2 Manifest. A v2 file is decoded strictly and checked (Validate, CheckCompat); a v1 file (no manifest_version, or 1) is converted by the one normalizer (Normalize). Under compat.V15() a v1 file also prints the once-per-process deprecation notice.
func Normalize ¶
Normalize converts v1 plugin.json bytes into a validated v2 Manifest. It is the one v1 reader: the shape rules of released CLIs (grouped dependencies, object-form webhooks and envVars, object-form apiEndpoints), the status and licence tables of Epic D1, the commands mapping (binaryName becomes commands.binary, the cliCommands entry named like it becomes commands.command, `binaryName: null` becomes `commands: null`) and the service block. It checks what v1.4.12's validateManifest checks and no more (validateV1), so a file the released CLI accepts always normalizes; Validate is for v2-native input. Keys no v1.4.12 reader decodes are not mapped: UnmappedV1Keys lists them and the migrate tool refuses to write a file that has any.
func ParseQuiet ¶
ParseQuiet is Parse without the v1 deprecation notice, for tools that convert v1 files on purpose.
type Migrations ¶
Migrations declares boot-applied SQL (ADR 0010).
type MultiApp ¶
type MultiApp struct {
Supported bool `json:"supported"`
IsolationColumn string `json:"isolationColumn,omitempty"`
PKStrategy string `json:"pkStrategy,omitempty"`
DefaultValue string `json:"defaultValue,omitempty"`
}
MultiApp describes multi-tenancy (v1 shape).
type Requires ¶
type Requires struct {
// Nself is a semver range for the CLI, e.g. ">=1.5.0".
Nself string `json:"nself,omitempty"`
// Plugins are plugin slugs.
Plugins []string `json:"plugins,omitempty"`
// PostgresExtensions are extension names checked at install (ADOPT).
PostgresExtensions []string `json:"postgres_extensions,omitempty"`
}
Requires lists what must exist before the plugin can run.
type RestRoute ¶
type RestRoute struct {
Method string `json:"method"`
Path string `json:"path"`
Summary string `json:"summary,omitempty"`
Auth string `json:"auth,omitempty"`
HMAC string `json:"hmac,omitempty"`
}
RestRoute is one entry of the v1 rest_routes key (read by apidocs). Auth and HMAC are optional additions that carry the v1 routes entries' auth scheme and the name of the env var holding the webhook secret; released readers ignore them.
type Seed ¶
type Seed struct {
Command []string `json:"command"`
}
Seed is an idempotent argv run inside the plugin's service.
type Service ¶
type Service struct {
Kind string `json:"kind"`
Compose *string `json:"compose,omitempty"`
Image *string `json:"image,omitempty"`
Port *int `json:"port,omitempty"`
Healthcheck *string `json:"healthcheck,omitempty"`
}
Service describes how the plugin runs.
type Shared ¶
type Shared struct {
// or an object grouping actions by category. Validation checks the shape;
// the fail-closed allowlist stays with the runtime (internal/plugin).
Permissions any `json:"permissions,omitempty"`
}
Shared holds the v1 keys that v2 keeps with the same name and JSON type. The authoritative list is tools/manifestv2migrate/testdata/v1412-readers.txt (the keys every v1.4.12 reader decodes); TestReaderScan keeps it covered. Polymorphic v1 shapes (grouped dependencies, object-form webhooks and so on) are authored in their one canonical shape here; the v1 normalizer converts.
type Subcommand ¶
type Subcommand struct {
// Name is one or more space-separated segments.
Name string `json:"name"`
Summary string `json:"summary,omitempty"`
SideEffect string `json:"side_effect,omitempty"`
Output string `json:"output,omitempty"`
JSON string `json:"json,omitempty"`
Args []Arg `json:"args,omitempty"`
Flags []Flag `json:"flags,omitempty"`
Confirm *Confirm `json:"confirm,omitempty"`
Surface string `json:"surface,omitempty"`
}
Subcommand is one nested command path below the root.
type SystemDependencies ¶
type SystemDependencies struct {
Required []SystemDependency `json:"required,omitempty"`
Recommended []SystemDependency `json:"recommended,omitempty"`
}
SystemDependencies groups required and recommended host dependencies.
type SystemDependency ¶
type SystemDependency struct {
Name string `json:"name"`
Verify string `json:"verify"`
MinVersion string `json:"minVersion,omitempty"`
Apt string `json:"apt,omitempty"`
Brew string `json:"brew,omitempty"`
CustomInstall string `json:"custom_install,omitempty"`
}
SystemDependency is a host-level dependency (v1 shape).
type Unmapped ¶
type Unmapped struct {
Key string // top-level key as written
Path string // JSON path of its value, e.g. $.config
Kind string // object, array, string, number or bool
Size int // entries of an object or array, else 0
}
Unmapped is one v1 key the normalizer has no v2 home for.
func UnmappedV1Keys ¶
UnmappedV1Keys lists the non-empty top-level keys of a v1 plugin.json that the normalizer does not carry into v2 (config, hooks, actions, notes, api_version, entry and so on, plus a routes, capabilities, env or env_vars value whose shape does not convert losslessly), sorted by key. Registry-owned keys (ForbiddenKeys) are not listed: ADR 0008 and the release pipeline own them, see RegistryKeys. A null, empty string, empty list or empty object has nothing to lose and is skipped.