Documentation
¶
Overview ¶
Package plugin — lifecycle audit helpers.
Wires plugin install/remove/update events to nself_ops.audit_log. Each event is recorded as: plugin name, operation type, actor (CLI user or system), timestamp, and optional metadata (version, source, error).
Writes are best-effort: failure to write the audit row never blocks the underlying operation. Errors are logged via stderr fallback (slog when available in callers).
Initial wiring and completion verified via integration test suite.
Index ¶
- Constants
- func CacheLicense(key string, valid bool, cacheDir string) error
- func CheckCLICompat(compat *CompatBlock, cliVersion string) error
- func CheckComplianceJSON(ctx context.Context, name string, port int) ([]byte, error)
- func CheckEOLBlock(ctx context.Context, name string, allowEOL bool) error
- func CheckServiceCompat(compat *CompatBlock, actual map[string]string) []string
- func CompareVersions(a, b string) int
- func DefaultCacheDir() string
- func DeriveCanonicalPermissions(grouped map[string][]string) []string
- func DisablePlugin(name, pluginDir string) error
- func DormantBanner(rec *PluginLifecycleRecord, now time.Time) string
- func EffectiveStatus(status string) string
- func EnablePlugin(name, pluginDir string) error
- func GenerateEd25519Keypair(pluginDataDir, pluginName string) (pubKey ed25519.PublicKey, err error)
- func IdentityKeyExists(pluginDataDir, pluginName string) bool
- func IncompatiblePlugins(pluginDir, newCLIVersion string) []string
- func Install(ctx context.Context, cfg *config.Config, name string, pluginDir string) error
- func InstallFromURL(ctx context.Context, cfg *config.Config, sourceURL string, pluginDir string, ...) error
- func IsCommandInstalled(cmdName string) bool
- func IsDisabled(name, pluginDir string) bool
- func IsFreeKey(key string) bool
- func IsPaidPlugin(name string) bool
- func IsRateLimited(err error) (retryAfter string, ok bool)
- func IsThirdPartyInstallSource(ref string) bool
- func LicenseCacheDir() string
- func LoadPrivateKey(pluginDataDir, pluginName string) (ed25519.PrivateKey, error)
- func LogPluginError(ctx context.Context, cfg *config.Config, op AuditOp, name string, err error)
- func LogPluginInstall(ctx context.Context, cfg *config.Config, name, version, source string)
- func LogPluginRemove(ctx context.Context, cfg *config.Config, name string, keepData bool)
- func LogPluginUpdate(ctx context.Context, cfg *config.Config, name, fromVersion, toVersion string)
- func NeedsRevalidation(key string, cacheDir string) bool
- func PlatformArch() (string, error)
- func PluginBinDir() string
- func PluginEnv(projectDir string, m *PluginManifest) []string
- func ProxyCommand(cmdName string, args []string) error
- func ProxyCommandWithHint(cmdName string, args []string, installHint string) error
- func PublishedBinaryPath(binName string) string
- func RegisterFreeAccount(ctx context.Context, pingURL string) (string, error)
- func RegisterIdentity(ctx context.Context, pluginName string, pubKey ed25519.PublicKey) error
- func RegistryFetchedAt() string
- func Remove(ctx context.Context, cfg *config.Config, name string, pluginDir string, ...) error
- func RemoveIdentityKey(pluginDataDir, pluginName string) error
- func RequestToken(ctx context.Context, pluginDataDir, sourcePlugin, targetPlugin string) (string, error)
- func RevokeIdentity(ctx context.Context, pluginName string) error
- func SatisfiesRange(version, rangeStr string) (bool, error)
- func SendFreeInstallTelemetry(pingURL string, licenseKey string, pluginName string)
- func SignCanonicalObject(bundle, name, sha256, visibility, version string, size int64, ...) (string, error)
- func Start(ctx context.Context, pluginDir string, name string) error
- func Stop(ctx context.Context, name string) error
- func Update(ctx context.Context, cfg *config.Config, name string, pluginDir string) error
- func ValidateIncomingJWT(ctx context.Context, tokenStr, expectedAudience string, store ReplayStore) error
- func ValidateLicenseRemote(ctx context.Context, key string, pingURL string) (bool, error)
- func ValidateNetworkAccess(ctx context.Context, registryURL string) error
- func ValidateThirdPartyURL(ref string) (*url.URL, error)
- func VerifyCanonicalSignature(bundle, name, sha256, visibility, version string, size int64, ...) error
- type APIEndpointList
- type AuditEvent
- type AuditOp
- type CLICommand
- type CompatBlock
- type ComplianceResult
- type DeprecationBlock
- type EndpointCheck
- type EntitlementFunc
- type EnvVar
- type EnvVarList
- type ErrRateLimited
- type ExitCodeError
- type InstalledPluginInfo
- type LicenseClient
- type LicenseValidateResponse
- type LifecycleState
- type LifecycleStore
- type MultiApp
- type PermissionSet
- type PluginGraphQLBlock
- type PluginGraphQLEntityKey
- type PluginInfo
- type PluginLifecycleRecord
- type PluginManifest
- type PluginStatus
- type Registry
- type RegistryClient
- type ReplayStore
- type StandardEndpoint
- type SystemDependencies
- type SystemDependency
- type TieredPluginInfo
- type WebhookNames
Constants ¶
const ( // DefaultRegistryURL is the primary registry endpoint (Cloudflare Worker). DefaultRegistryURL = "https://plugins.nself.org/registry.json" // FallbackRegistryURL is the GitHub raw fallback when the primary is down. FallbackRegistryURL = "https://raw.githubusercontent.com/nself-org/plugins/main/registry.json" // DefaultCacheTTL is the registry cache lifetime in seconds. DefaultCacheTTL = 300 )
const ArtifactKindSource = "source"
ArtifactKindSource is the artifact kind downloadPluginPackageForTier reports when it fetched the source tarball. Any other value it returns is one of PlatformArch()'s platform strings (darwin-arm64, darwin-amd64, linux-amd64, linux-arm64, windows-amd64), identifying a per-platform binary tarball instead. Callers use this to pick the checksum that actually matches the bytes on disk — see installer_locked.go's Step 5.
Variables ¶
This section is empty.
Functions ¶
func CacheLicense ¶
CacheLicense writes the validation result for a license key to a cache file inside cacheDir. The cache format is: {data}|{hmac_hex} where data is {key_prefix}|{status}|{timestamp} and hmac_hex is the HMAC-SHA256 of data keyed by the random 32-byte key persisted at ~/.nself/license/.hmac-key (SIEGE V03-F02 fix — replaces observable-derived machineID key).
Before writing, it prunes any cache entries older than 30 days.
func CheckCLICompat ¶ added in v1.0.6
func CheckCLICompat(compat *CompatBlock, cliVersion string) error
CheckCLICompat checks whether cliVersion satisfies the plugin's compat.nself range. Returns nil if no compat block is set or if the version is in range.
func CheckComplianceJSON ¶ added in v1.0.6
CheckComplianceJSON returns the compliance result as JSON bytes.
func CheckEOLBlock ¶ added in v1.0.11
CheckEOLBlock fetches the registry entry for name and returns an error if the plugin's status is "eol" and allowEOL is false. (S58-T03) If the registry cannot be reached or the plugin is not found, this function returns nil (install will fail downstream with a more specific error).
func CheckServiceCompat ¶ added in v1.0.6
func CheckServiceCompat(compat *CompatBlock, actual map[string]string) []string
CheckServiceCompat checks whether each service version in actual satisfies the corresponding constraint in compat.requires. Returns a list of services that fail the check.
func CompareVersions ¶ added in v1.0.11
CompareVersions compares two semver strings a and b. Returns -1 if a < b, 0 if a == b, +1 if a > b. (S58-T06)
func DefaultCacheDir ¶ added in v1.0.11
func DefaultCacheDir() string
DefaultCacheDir is the exported alias of defaultCacheDir for use by commands that need to call FetchRegistry directly. (S58-T06)
func DeriveCanonicalPermissions ¶ added in v1.3.2
DeriveCanonicalPermissions reduces the descriptive permission object to the canonical vocabulary the allowlist validates.
The result is sorted and de-duplicated so a manifest produces the same set every time regardless of map iteration order.
func DisablePlugin ¶ added in v1.0.2
DisablePlugin creates a .disabled marker file in the plugin's directory, causing it to be excluded from compose files on the next build.
func DormantBanner ¶ added in v1.1.1
func DormantBanner(rec *PluginLifecycleRecord, now time.Time) string
DormantBanner returns the warning text for a dormant plugin showing days remaining.
func EffectiveStatus ¶ added in v1.3.6
EffectiveStatus normalises a plugin's raw registry `status` value for every status-driven decision: an absent (empty) status defaults to "stable" — see pluginEntry.PublishStatus / PluginManifest.PublishStatus's doc comment ("Missing status defaults to stable for backwards compatibility"). Before FIX-CLI-6 every status gate compared the raw field directly against the literal string "stable", so "" and "stable" were treated inconsistently: the lifecycle switch in installLocked already special-cased both the same way, but verifyChecksum/verifyPluginSignature's "missing value" branch did not, leaving all 130 (of 177, as of 2026-09-04) registry entries without an explicit status effectively unable to ever hit the "stable" hard-fail path. Every status-driven gate must compare against EffectiveStatus(status), not the raw field, so "" and an explicit "stable" are never inconsistent again.
func EnablePlugin ¶ added in v1.0.2
EnablePlugin removes the .disabled marker file from the plugin's directory, allowing it to be included in compose files on the next build.
func GenerateEd25519Keypair ¶ added in v1.0.12
GenerateEd25519Keypair generates a new Ed25519 keypair and writes the encrypted private key to PLUGIN_DATA_DIR/<name>/identity.key. It returns the public key bytes (32 bytes) for registration with ping_api.
The encryption key is derived from PLUGIN_INTERNAL_SECRET and the plugin name using HKDF-SHA256 so each plugin has a unique encryption key.
func IdentityKeyExists ¶ added in v1.0.12
IdentityKeyExists reports whether a plugin identity key file is present on disk. It does NOT validate the key contents.
func IncompatiblePlugins ¶ added in v1.0.6
IncompatiblePlugins checks all installed plugins and returns a list of those whose compat.nself range will not be satisfied by newCLIVersion. This is used by `nself upgrade` to warn the user before upgrading.
func Install ¶
Install downloads, extracts, and configures a plugin. For paid plugins it checks the license first. Dependencies are resolved and recursively installed before the target plugin. If any step after extraction fails, the extracted directory and database schema are rolled back.
A file lock on {pluginDir}/.install.lock is held for the duration of the operation to prevent concurrent installs from corrupting plugin state.
func InstallFromURL ¶ added in v1.3.0
func InstallFromURL(ctx context.Context, cfg *config.Config, sourceURL string, pluginDir string, expectedChecksum string) error
InstallFromURL downloads a plugin tarball from an arbitrary HTTPS URL, extracts it, validates its bundled plugin.json, and installs it exactly like a registry plugin (schema creation, route/table conflict checks) EXCEPT for the registry-specific trust steps that don't apply to a source with no registry entry: license check (there is no license tier for a URL) and Ed25519 signature verification (there is no registry-pinned author key to verify against). Checksum verification still runs against expectedChecksum when the caller supplies one (see the package doc comment above for why that must come from outside the tarball, not from the extracted plugin.json).
Callers MUST obtain user confirmation (or an explicit non-interactive opt-in such as --yes) BEFORE calling this — it downloads and executes arbitrary third-party code with no vetting beyond what's described above.
func IsCommandInstalled ¶ added in v1.3.0
IsCommandInstalled reports whether a plugin providing the named command is present, i.e. whether ProxyCommand would find a binary for it.
Used by the CLI to decide whether a relocated command still needs its "moved to a plugin" notice. Once the plugin is installed the old spelling is the supported spelling, and repeating the install hint is noise.
func IsDisabled ¶ added in v1.0.2
IsDisabled returns true if the named plugin has a .disabled marker file.
func IsPaidPlugin ¶ added in v1.0.12
IsPaidPlugin is the exported wrapper around the package-private isPaidPlugin. Returns true if the named plugin requires a paid license key.
func IsRateLimited ¶ added in v1.1.0
IsRateLimited reports whether err is (or wraps) an ErrRateLimited.
func IsThirdPartyInstallSource ¶ added in v1.3.0
IsThirdPartyInstallSource reports whether ref looks like a URL rather than a plain registry plugin name. Plugin names are validated elsewhere against namePattern (lowercase-with-hyphens, no scheme), so any ref containing a "://" is unambiguously a URL, not a name.
func LicenseCacheDir ¶
func LicenseCacheDir() string
LicenseCacheDir returns the directory used for license validation caching. This is ~/.nself/license/ (or /tmp/.nself/license/ if the home directory cannot be determined).
func LoadPrivateKey ¶ added in v1.0.12
func LoadPrivateKey(pluginDataDir, pluginName string) (ed25519.PrivateKey, error)
LoadPrivateKey decrypts and returns the Ed25519 private key stored at PLUGIN_DATA_DIR/<name>/identity.key. Returns an error if the key does not exist, cannot be decrypted, or the file has been corrupted.
func LogPluginError ¶ added in v1.0.16
LogPluginError records a failed lifecycle event for traceability.
func LogPluginInstall ¶ added in v1.0.16
LogPluginInstall records a plugin install event. Best-effort: never blocks the install path on failure.
func LogPluginRemove ¶ added in v1.0.16
LogPluginRemove records a plugin remove event.
func LogPluginUpdate ¶ added in v1.0.16
LogPluginUpdate records a plugin update event.
func NeedsRevalidation ¶
NeedsRevalidation returns true if the license cache in cacheDir is missing, tampered with, or if the most recent validation timestamp is older than revalidationInterval (7 days). This is used during nself start to trigger a periodic heartbeat check against the license server. Owner keys never need revalidation.
func PlatformArch ¶ added in v1.1.10
PlatformArch returns the platform string for binary plugin archive selection. Supported values: darwin-arm64, darwin-amd64, linux-amd64, linux-arm64, windows-amd64. Any other GOOS/GOARCH pair returns an error.
func PluginBinDir ¶ added in v1.3.0
func PluginBinDir() string
PluginBinDir returns the directory the command proxy searches for plugin binaries. Exported so the installer and the router cannot drift apart on where that is.
func PluginEnv ¶ added in v1.3.0
func PluginEnv(projectDir string, m *PluginManifest) []string
PluginEnv returns the environment entries to append for a plugin process.
Returns nil when the plugin declares nothing, when there is no project here, or when a variable is not set anywhere — an absent value is passed as absent, not as empty, so the plugin's own default applies.
func ProxyCommand ¶
ProxyCommand checks if a plugin binary exists and executes it. If not, it instructs the user to install it.
For security, the binary is looked up ONLY in the plugin bin directory, never via the full system PATH. This prevents PATH hijacking attacks.
func ProxyCommandWithHint ¶ added in v1.3.0
ProxyCommandWithHint is ProxyCommand with control over the install hint in the not-found error.
The generic hint assumes the plugin is named after the command, which is not always true: `claw` lives in a plugin called `claw-cli`, because a paid `claw` service plugin already owns that name. When the deprecation registry has already told the user the right thing to install, passing an empty hint here stops the proxy contradicting it — the user was being shown "use 'nself install claw-cli'" and "nself install claw" one line apart.
func PublishedBinaryPath ¶ added in v1.3.0
PublishedBinaryPath returns where a plugin's command binary is published, including the platform's executable suffix.
Exported so tests assert against the same path the installer writes. Having the test compute the name itself is how the Windows job went red: the installer appended .exe and the test did not.
func RegisterFreeAccount ¶ added in v1.0.12
RegisterFreeAccount calls ping_api to create a new nself_free_ key. Returns the raw key string on success.
func RegisterIdentity ¶ added in v1.0.12
RegisterIdentity posts the plugin's public key to ping_api. pubKey is the raw 32-byte Ed25519 public key returned by GenerateEd25519Keypair.
If the plugin has previously registered (e.g., re-install without uninstall), this is treated as a "register" action; ping_api will update the record if the plugin_name already exists (idempotent).
func RegistryFetchedAt ¶ added in v1.3.0
func RegistryFetchedAt() string
RegistryFetchedAt returns the top-level "fetchedAt" snapshot timestamp from the most recently cached registry response, or "" when no cache exists or the cached registry didn't carry one (CLI-R16). It reads the on-disk cache only — no network call — so it is safe to call right after a List/Fetch that already populated the cache in the same command invocation.
func Remove ¶
func Remove(ctx context.Context, cfg *config.Config, name string, pluginDir string, keepData bool, force bool) error
Remove stops a plugin (if running), optionally drops its database schema, and removes its directory from disk. When force is false and other installed plugins depend on the target, Remove returns an error listing them.
A file lock on {pluginDir}/.install.lock is held for the duration of the operation so that concurrent install and remove calls serialize correctly.
func RemoveIdentityKey ¶ added in v1.0.12
RemoveIdentityKey deletes the stored identity key for a plugin. This is called during uninstall so the revocation endpoint can be called before the key is removed from disk.
func RequestToken ¶ added in v1.0.12
func RequestToken(ctx context.Context, pluginDataDir, sourcePlugin, targetPlugin string) (string, error)
RequestToken authenticates sourcePlugin with ping_api via Ed25519 challenge-response and returns a 5-minute JWT scoped to targetPlugin. The private key for sourcePlugin must already exist on disk (generated during plugin install).
func RevokeIdentity ¶ added in v1.0.12
RevokeIdentity calls DELETE /plugin/identity/<name> on ping_api to mark the plugin identity as revoked. Call this before removing the private key from disk so that in-flight tokens issued to this plugin are rejected promptly.
Auth: the owner license key from NSELF_PLUGIN_LICENSE_KEY is used as the authorization credential for the delete endpoint.
func SatisfiesRange ¶ added in v1.0.6
SatisfiesRange checks whether version satisfies a space-separated list of semver constraints. All constraints must be satisfied (AND logic). Supported operators: >=, <=, >, <, =, != Examples: ">=1.0.0 <2.0.0", ">=14", ">=2.30"
func SendFreeInstallTelemetry ¶ added in v1.0.12
SendFreeInstallTelemetry fires a non-blocking install event to ping_api. Errors are silently discarded — telemetry must never block installs.
func SignCanonicalObject ¶ added in v1.1.2
func SignCanonicalObject(bundle, name, sha256, visibility, version string, size int64, privateKeyHex string) (string, error)
SignCanonicalObject signs the canonical plugin object with the provided Ed25519 private key and returns the hex-encoded signature.
privateKeyHex is a hex-encoded 64-byte Ed25519 private key (seed || public key). This function is intended for use in the ping_api worker and in tests; the CLI itself only verifies — it does not sign.
func Start ¶
Start launches a plugin process in the background. It locates the plugin's entry point inside pluginDir, starts it, writes the PID to ~/.nself/runtime/pids/{name}.pid, and records the state in ~/.nself/runtime/states/{name}.state.
func Update ¶
Update backs up the current installation, then reinstalls from the registry. If the new install fails, the previous version is restored automatically.
func ValidateIncomingJWT ¶ added in v1.0.12
func ValidateIncomingJWT(ctx context.Context, tokenStr, expectedAudience string, store ReplayStore) error
ValidateIncomingJWT validates a JWT presented in an inter-plugin HTTP call. expectedAudience is the short plugin name (e.g., "mux"), NOT the full aud claim (which has the "plugin:" prefix).
Returns nil on success, a descriptive error on any validation failure.
func ValidateLicenseRemote ¶
ValidateLicenseRemote performs a remote license check by POSTing to the given pingURL. It returns (true, nil) on HTTP 200, (false, nil) on 401/403/404, and (false, error) on network or unexpected failures.
func ValidateNetworkAccess ¶
ValidateNetworkAccess does a HEAD request to the registry URL. Returns an error with a clear message if the registry is unreachable. Call before any operation that requires network access to the plugin registry.
func ValidateThirdPartyURL ¶ added in v1.3.0
ValidateThirdPartyURL parses ref and enforces the transport-security policy for third-party plugin sources: HTTPS only, except for localhost/127.0.0.1 (dev and integration-test use, matching enforceRegistryHTTPS's convention for the official registry URL).
func VerifyCanonicalSignature ¶ added in v1.1.2
func VerifyCanonicalSignature(bundle, name, sha256, visibility, version string, size int64, publicKeyHex, signatureHex string) error
VerifyCanonicalSignature checks an Ed25519 signature over the canonical plugin object constructed from the provided fields.
publicKeyHex is a hex-encoded 32-byte Ed25519 public key. signatureHex is a hex-encoded 64-byte Ed25519 signature.
Returns nil on success, an error describing the failure otherwise.
Types ¶
type APIEndpointList ¶ added in v1.3.0
type APIEndpointList []string
APIEndpointList is the set of HTTP endpoints a plugin exposes.
Published manifests use two shapes: an array of "METHOD /path" strings, and an array of {method, path, description} objects. The field was typed for the first, so the one plugin using the second was unparseable and therefore invisible.
func (*APIEndpointList) UnmarshalJSON ¶ added in v1.3.0
func (a *APIEndpointList) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts an array of strings, an array of objects, or null.
type AuditEvent ¶ added in v1.0.16
type AuditEvent struct {
Op AuditOp `json:"op"`
PluginName string `json:"plugin"`
Actor string `json:"actor"`
Version string `json:"version,omitempty"`
Source string `json:"source,omitempty"` // "free" | "pro" | "local"
Error string `json:"error,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
AuditEvent captures one plugin lifecycle event for the audit log.
type AuditOp ¶ added in v1.0.16
type AuditOp string
AuditOp identifies a plugin lifecycle operation type.
type CLICommand ¶
CLICommand describes a CLI command provided by a plugin.
type CompatBlock ¶ added in v1.0.6
type CompatBlock struct {
Nself string `json:"nself,omitempty" yaml:"nself,omitempty"`
Requires map[string]string `json:"requires,omitempty" yaml:"requires,omitempty"`
}
CompatBlock describes the compatibility requirements declared by a plugin manifest. The Nself field is a semver range constraint for the CLI version (e.g. ">=1.0.0 <2.0.0"). Requires maps service names to their own semver range constraints (e.g. "postgres": ">=14", "hasura": ">=2.30").
type ComplianceResult ¶ added in v1.0.6
type ComplianceResult struct {
PluginName string
Compliant bool
Endpoints []EndpointCheck
}
ComplianceResult holds the outcome of a plugin compliance check.
func CheckCompliance ¶ added in v1.0.6
func CheckCompliance(ctx context.Context, name string, port int) *ComplianceResult
CheckCompliance verifies that a running plugin implements the standard interface endpoints (/health, /version, /capabilities). Non-compliant plugins log a warning but are not stopped.
type DeprecationBlock ¶ added in v1.0.11
type DeprecationBlock struct {
AnnouncedDate string `json:"announcedDate"`
EOLDate string `json:"eolDate"`
ReplacedBy string `json:"replacedBy,omitempty"`
MigrationGuide string `json:"migrationGuide"`
MigrationScript string `json:"migrationScript,omitempty"`
}
DeprecationBlock carries the required deprecation metadata when a plugin's status is "deprecated". All five fields must be present (S58-T02). announcedDate and eolDate are ISO 8601 date strings (YYYY-MM-DD). replacedBy is the name of the replacement plugin (optional but recommended). migrationGuide is a URL that must resolve with HTTP 200 at CI time. migrationScript is a repo-relative path to an automated migration script (optional).
type EndpointCheck ¶ added in v1.0.6
EndpointCheck records whether a specific endpoint responded.
type EntitlementFunc ¶ added in v1.3.6
EntitlementFunc reports whether the operator's current license entitles at least one of the given bundles.nself.org slugs. Production callers use defaultEntitlement; tests inject a fixture so resolution logic is verifiable without a live ping_api call.
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 required or used by a plugin.
type EnvVarList ¶ added in v1.3.0
type EnvVarList []EnvVar
EnvVarList is the set of environment variables a plugin declares.
Purpose: accept every shape real plugin.json files use. The field was typed `[]EnvVar`, but the manifest template every CLI-R11 extraction was copied from writes an object instead:
"envVars": { "required": [], "optional": ["HCLOUD_TOKEN"] }
Unmarshalling that into a slice fails, and a manifest that fails to parse is discarded — readPluginManifest returns nil, ListInstalled skips the plugin. So every extracted plugin was absent from `nself plugin list` and from anything else that enumerates what is installed, silently. This is the same failure the `webhooks` field had, for the same reason: the type described one of the shapes in use rather than all of them.
Inputs: the `envVars` value from plugin.json, in any of three shapes.
Outputs: the declared variables, with Required set from whichever shape said so.
Constraints: parsing must never fail on a shape a real manifest uses. A manifest this code cannot read makes a plugin invisible rather than loud, which is the worst of both.
func (EnvVarList) MarshalJSON ¶ added in v1.3.0
func (e EnvVarList) MarshalJSON() ([]byte, error)
MarshalJSON always writes the canonical array-of-objects form.
func (EnvVarList) Names ¶ added in v1.3.0
func (e EnvVarList) Names() []string
Names returns every declared variable name, in declaration order.
func (*EnvVarList) UnmarshalJSON ¶ added in v1.3.0
func (e *EnvVarList) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts an array of objects, an array of names, an object with required/optional name lists, or null.
type ErrRateLimited ¶ added in v1.1.0
type ErrRateLimited struct {
RetryAfter string
}
ErrRateLimited is returned when the license validation server responds with HTTP 429 Too Many Requests. RetryAfter holds the value of the Retry-After header (in seconds) as a string, defaulting to "60" when the header is absent.
func (*ErrRateLimited) Error ¶ added in v1.1.0
func (e *ErrRateLimited) Error() string
type ExitCodeError ¶ added in v1.0.2
type ExitCodeError struct {
Code int
}
ExitCodeError is returned when a plugin process exits with a non-zero code. main() reads ExitCode() and mirrors the plugin's status; the message is not printed, because the plugin already wrote its own output to the terminal.
func (*ExitCodeError) Error ¶ added in v1.0.2
func (e *ExitCodeError) Error() string
func (*ExitCodeError) ExitCode ¶ added in v1.3.0
func (e *ExitCodeError) ExitCode() int
ExitCode satisfies the interface main() and errs.ExitCodeFor look for.
func (*ExitCodeError) Silent ¶ added in v1.3.0
func (e *ExitCodeError) Silent() bool
Silent reports that main() must not print this error: the plugin subprocess inherited stdout/stderr and has already reported the failure itself.
type InstalledPluginInfo ¶
type InstalledPluginInfo struct {
Name string
Version string
Tier string
Status string
Description string
}
InstalledPluginInfo is a richer view of an installed plugin used by the inventory subcommand. It includes Description and Tier from the manifest.
func ListInstalled ¶
func ListInstalled(pluginDir string) ([]InstalledPluginInfo, error)
ListInstalled scans pluginDir and returns detailed information for every installed plugin. Each entry's Status is "running", "installed", or "unknown" depending on the plugin's current runtime state.
type LicenseClient ¶
LicenseClient abstracts license validation for testability.
type LicenseValidateResponse ¶ added in v1.0.4
type LicenseValidateResponse struct {
Valid bool `json:"valid"`
Reason string `json:"reason,omitempty"`
Tier string `json:"tier"`
Plugins []string `json:"plugins"`
Expires string `json:"expires,omitempty"`
}
LicenseValidateResponse is the JSON body returned by the license validation endpoint on HTTP 200. The Tier and Plugins fields are used to populate the local entitlement cache.
func ValidateLicenseRemoteWithDetails ¶ added in v1.0.4
func ValidateLicenseRemoteWithDetails(ctx context.Context, key string, pingURL string) (bool, *LicenseValidateResponse, error)
ValidateLicenseRemoteWithDetails performs a remote license check and returns the full response including tier, plugins, and expiry information.
type LifecycleState ¶ added in v1.1.1
type LifecycleState string
LifecycleState represents the current state of a plugin's license lifecycle.
const ( // StateActive means the plugin license is valid. StateActive LifecycleState = "active" // StateDormant means the license expired but the grace period is still active. StateDormant LifecycleState = "dormant" // StateExpired means the grace period exhausted — plugin is an auto-remove candidate. StateExpired LifecycleState = "expired" // DefaultGracePeriod is the time a plugin stays dormant before auto-removal. DefaultGracePeriod = 7 * 24 * time.Hour )
type LifecycleStore ¶ added in v1.1.1
type LifecycleStore struct {
Version int `json:"version"`
Records map[string]*PluginLifecycleRecord `json:"records"`
}
LifecycleStore is the persistent state file for plugin lifecycle tracking.
func LoadLifecycleStore ¶ added in v1.1.1
func LoadLifecycleStore() (*LifecycleStore, error)
LoadLifecycleStore reads the persistent store, creating an empty one if missing.
func (*LifecycleStore) CheckExpiry ¶ added in v1.1.1
func (s *LifecycleStore) CheckExpiry(now time.Time) (dormant, autoRemove []string)
CheckExpiry transitions plugins to DORMANT or EXPIRED based on the given time. It returns two slices: plugins newly transitioned to DORMANT, and plugins now EXPIRED (auto-remove candidates). The caller must call Save() to persist transitions.
Renewal check: MarkRenewal must be called before CheckExpiry to clear dormant state for any plugin that was renewed; this prevents a renewed plugin from immediately cycling back to EXPIRED.
func (*LifecycleStore) MarkRenewal ¶ added in v1.1.1
func (s *LifecycleStore) MarkRenewal(name string, newExpiry time.Time)
MarkRenewal transitions a plugin back to active state with a new expiry. If the record does not exist yet, it is created.
func (*LifecycleStore) Save ¶ added in v1.1.1
func (s *LifecycleStore) Save() error
Save writes the store atomically via a temp file + rename to prevent corruption on concurrent CLI invocations (TOCTOU protection).
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 configuration for a plugin.
type PermissionSet ¶ added in v1.3.0
type PermissionSet struct {
// Canonical holds array-form entries, validated against the allowlist.
Canonical []string
// Grouped holds object-form entries verbatim, in their own vocabulary.
Grouped map[string][]string
}
PermissionSet is what a plugin declares it needs.
Two vocabularies are in use and they do not describe the same things:
- The canonical one this CLI validates: "db:read", "fs:write", "network:internet", "system:exec" — an array of strings, checked fail-closed against an allowlist (S71-T01).
- A descriptive one used by 121 shipped manifests: an object such as {"database": ["create","read"], "filesystem": ["logs"], "network": ["*"]}. None of its tokens appear in the allowlist.
Flattening the second into the first would produce "database:create" and be rejected, turning an invisible plugin into one that refuses to install. Mapping between them — deciding that "database:create" means "db:write" — would be inventing security policy, which is not a parser's job.
So both are kept, separately. Canonical is validated. Grouped is recorded and displayed, and the divergence is a real finding for someone to settle.
func (PermissionSet) Effective ¶ added in v1.3.2
func (p PermissionSet) Effective() []string
Effective returns the permissions to validate: the canonical entries as written, plus the canonical reduction of any descriptive ones.
Both are checked against the same allowlist. The descriptive form used to be recorded and skipped, which left 121 plugins outside a fail-closed check; see DeriveCanonicalPermissions for why reducing it is sound and which direction each mapping moves in.
func (PermissionSet) Len ¶ added in v1.3.0
func (p PermissionSet) Len() int
Len reports how many permissions were declared, in either vocabulary.
func (PermissionSet) MarshalJSON ¶ added in v1.3.0
func (p PermissionSet) MarshalJSON() ([]byte, error)
MarshalJSON writes back whichever form was read, preferring the canonical array when both are somehow present.
func (PermissionSet) Strings ¶ added in v1.3.0
func (p PermissionSet) Strings() []string
Strings renders every declared permission for display. Grouped entries are shown as "category:action" so a human can read them, which is NOT the same as treating them as canonical — nothing validates these.
func (*PermissionSet) UnmarshalJSON ¶ added in v1.3.0
func (p *PermissionSet) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts the array form, the object form, or null.
func (PermissionSet) UsesLegacyVocabulary ¶ added in v1.3.0
func (p PermissionSet) UsesLegacyVocabulary() bool
UsesLegacyVocabulary reports whether this plugin declared permissions in the descriptive form.
type PluginGraphQLBlock ¶ added in v1.0.12
type PluginGraphQLBlock struct {
// Enabled must be true for this plugin to register as a subgraph.
Enabled bool `json:"enabled" yaml:"enabled"`
// SubgraphName is the unique lowercase subgraph identifier.
// Must not be "hasura" (reserved). Format: [a-z][a-z0-9_]*.
SubgraphName string `json:"subgraph_name" yaml:"subgraph_name"`
// SubgraphURL is the HTTP endpoint serving the subgraph SDL.
// ${PORT} is substituted with the plugin's declared Port field.
SubgraphURL string `json:"subgraph_url" yaml:"subgraph_url"`
// SchemaPath is a repo-relative path to the static SDL file.
// Used by rover supergraph compose. Optional when SubgraphURL is live.
SchemaPath string `json:"schema_path,omitempty" yaml:"schema_path,omitempty"`
// Entities lists Apollo Federation entity types this subgraph owns.
Entities []PluginGraphQLEntityKey `json:"entities,omitempty" yaml:"entities,omitempty"`
}
PluginGraphQLBlock is the graphql: block in a plugin manifest. When Enabled is true and NSELF_FEDERATION=true, nself build registers this plugin as a subgraph in the Apollo Router supergraph (G05).
type PluginGraphQLEntityKey ¶ added in v1.0.12
type PluginGraphQLEntityKey struct {
// Type is the GraphQL type name (e.g. "User").
Type string `json:"type" yaml:"type"`
// Key is the @key field used for entity resolution (e.g. "id").
Key string `json:"key" yaml:"key"`
}
PluginGraphQLEntityKey describes a single Apollo Federation entity type that a plugin subgraph owns. Used for cross-subgraph entity resolution.
type PluginInfo ¶
type PluginInfo struct {
Name string
Version string
Category string
Installed bool
Running bool
PublishStatus string // S58-T01: "experimental"|"planned"|"beta"|"stable"|"deprecated"|"eol" — from registry
UpdatedAt string // CLI-R16: per-plugin freshness, empty when the source (registry entry or local plugin.json) doesn't carry one
}
PluginInfo describes a plugin's identity and current state.
type PluginLifecycleRecord ¶ added in v1.1.1
type PluginLifecycleRecord struct {
Name string `json:"name"`
State LifecycleState `json:"state"`
LicenseExpiry time.Time `json:"license_expiry"`
DormantSince *time.Time `json:"dormant_since,omitempty"`
// GracePeriod is stored per-record so that future config changes cannot
// retroactively shorten grace periods that are already in progress.
GracePeriod time.Duration `json:"grace_period"`
}
PluginLifecycleRecord tracks a single plugin's license lifecycle state.
type PluginManifest ¶
type PluginManifest struct {
// Required fields
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
Category string `json:"category"`
License string `json:"license"`
// Optional metadata
Author string `json:"author,omitempty"`
Homepage string `json:"homepage,omitempty"`
Repository string `json:"repository,omitempty"`
Tags []string `json:"tags,omitempty"`
IsCommercial bool `json:"isCommercial,omitempty"`
LicenseType string `json:"licenseType,omitempty"`
RequiredEntitlements []string `json:"requiredEntitlements,omitempty"`
RequiresLicense bool `json:"requires_license,omitempty"`
MinNselfVersion string `json:"minNselfVersion,omitempty"`
MinNodeVersion string `json:"minNodeVersion,omitempty"`
ArchSupport []string `json:"arch_support,omitempty"`
// Implementation
Language string `json:"language,omitempty"`
Runtime string `json:"runtime,omitempty"`
Port int `json:"port,omitempty"`
EntryPoint string `json:"entryPoint,omitempty"`
CLI string `json:"cli,omitempty"`
// PluginType distinguishes a plugin that adds a CLI command ("cli") from
// one that runs as a service. Only a "cli" plugin gets its binary published
// into the directory ProxyCommand searches — see cli_binary.go.
PluginType string `json:"pluginType,omitempty"`
// BinaryName overrides the nself-<name> convention for the published
// binary. ProxyCommand resolves nself-<command>, so this should only differ
// when the command name differs from the plugin name.
BinaryName string `json:"binaryName,omitempty"`
HealthEndpoint string `json:"health_endpoint,omitempty"`
PackageManager string `json:"packageManager,omitempty"`
Framework string `json:"framework,omitempty"`
// Database
Tables []string `json:"tables,omitempty"`
Views []string `json:"views,omitempty"`
// API
APIEndpoints APIEndpointList `json:"apiEndpoints,omitempty"`
// WebhookNames, not []string: real manifests use both an array of event
// names and an object keyed by event name. See webhooks.go.
Webhooks WebhookNames `json:"webhooks,omitempty"`
CLICommands []CLICommand `json:"cliCommands,omitempty"`
// Environment
EnvVars EnvVarList `json:"envVars,omitempty"`
// Dependencies
Dependencies []string `json:"dependencies,omitempty"`
OptionalDependencies []string `json:"optionalDependencies,omitempty"`
SystemDependencies SystemDependencies `json:"systemDependencies,omitempty"`
// Inter-plugin communication contract (S43-T17).
// Consumes lists the plugin names whose HTTP APIs this plugin may call via
// X-Source-Plugin. Every entry must be installed for this plugin to work
// correctly; install-time validation enforces this.
// Provides lists the API services this plugin exposes for other plugins to
// consume. Both fields use plugin name format (lowercase-with-hyphens).
Consumes []string `json:"consumes,omitempty"`
Provides []string `json:"provides,omitempty"`
// Multi-tenancy
MultiApp MultiApp `json:"multiApp,omitempty"`
// Permissions
Permissions PermissionSet `json:"permissions,omitempty"`
// Compatibility
Compat *CompatBlock `json:"compat,omitempty"`
// Registry-specific fields (not in plugin.json, populated by registry)
Tier string `json:"tier,omitempty"`
Checksum string `json:"checksum,omitempty"`
// TierPair marks a genuine free/pro pair of the SAME product sharing one
// slug (e.g. cron, notify) — set true in BOTH the free and pro registry
// entries by the plugins/plugins-pro registry authors. Install-time
// resolution (tier_resolve.go) uses this to tell a real tier pair from an
// unrelated registry-data collision, which is a hard error instead
// (OWNER-ACTIONS.md item 15).
TierPair bool `json:"tier_pair,omitempty"`
// Bundles lists the bundles.json slugs this specific registry entry
// belongs to (e.g. cron's pro entry: ["claw"]). Used by tier_resolve.go
// to decide entitlement for a TierPair's pro entry via
// license.BundleEntitled. Empty for free entries and for pro plugins sold
// standalone rather than through a bundle.
Bundles []string `json:"bundles,omitempty"`
// PublishStatus is the plugin's lifecycle status from the registry (S58-T01).
// Valid values: "experimental" | "planned" | "beta" | "stable" | "deprecated" | "eol"
// Missing status defaults to "stable" for backwards compatibility (CR-A).
// "planned" — not yet installable; rejected at install time.
// "experimental"— early-access; installs with a prominent warning.
// "beta" — pre-stable; installs with a warning.
// "stable" — default production-ready state; no warning.
// "deprecated" — install warns and offers replacedBy alternative if set.
// Requires a Deprecation block in the manifest.
// "eol" — install blocked by default; --allow-eol override required.
PublishStatus string `json:"status,omitempty"`
// Deprecation carries the required metadata when status == "deprecated". (S58-T02)
// All five fields are required when the block is present.
Deprecation *DeprecationBlock `json:"deprecation,omitempty"`
// GraphQL carries the Apollo Federation subgraph registration for this
// plugin. When graphql.enabled is true and NSELF_FEDERATION=true, the
// build pipeline includes this plugin as a subgraph in the Apollo Router
// supergraph. The field is ignored in non-federation deployments (G05).
GraphQL *PluginGraphQLBlock `json:"graphql,omitempty"`
// MaxNselfVersion is the last CLI version this plugin is compatible with. (S58-T06)
// Empty means "no upper bound". compat-check reads this field.
MaxNselfVersion string `json:"maxNselfVersion,omitempty"`
// AuthorPublicKey is the hex-encoded Ed25519 public key of the plugin
// publisher, pinned in the registry. Used to verify Signature.
AuthorPublicKey string `json:"author_public_key,omitempty"`
// Signature is the hex-encoded Ed25519 signature of the tarball checksum.
// Format: Ed25519Sign(privateKey, sha256(tarball)).
// Pinned in registry alongside AuthorPublicKey.
Signature string `json:"signature,omitempty"`
// UpdatedAt is a per-plugin freshness timestamp (CLI-R16). The live
// plugins.nself.org registry does not send this field as of 2026-08-23 —
// only a registry-wide "fetchedAt" (see Registry.FetchedAt /
// RegistryFetchedAt). This field is plumbing: `nself plugin list
// --detailed` displays it when present (registry entry or local
// plugin.json) and shows nothing invented when it is not.
UpdatedAt string `json:"updated_at,omitempty"`
// PlatformChecksums holds one SHA-256 checksum (lowercase hex, no
// "sha256:" prefix) per per-platform binary tarball, keyed by the exact
// platform string PlatformArch() returns (darwin-arm64, darwin-amd64,
// linux-amd64, linux-arm64, windows-amd64). Populated from the registry's
// nested `checksums.platforms` object.
//
// Checksum above is ALWAYS the source tarball's checksum, never a
// platform one — the two packages have different bytes, so one flat
// field can never validate both (FIX-CLI-plugins-83: a platform tarball
// downloaded via binaryPluginDownloadURL was being checked against
// Checksum, which could never match). downloadPluginPackageForTier
// reports which artifact it fetched; installLocked picks the matching
// entry from this map, or Checksum for the source artifact — see
// verifyChecksum's caller in installer_locked.go.
//
// Only present for a plugin with a binaryName. A plugin whose release
// predates this field (or a platform the release never built a checksum
// for) has no entry here — see downloadPluginPackageForTier's doc
// comment for what happens then.
PlatformChecksums map[string]string `json:"platform_checksums,omitempty"`
}
PluginManifest describes a single plugin, parsed from plugin.json.
func FindPluginByName ¶ added in v1.0.11
func FindPluginByName(reg *Registry, name string) (*PluginManifest, bool)
FindPluginByName is the exported wrapper around findPlugin for use by commands outside the plugin package. (S58-T06)
func LoadManifestsFromDir ¶ added in v1.0.12
func LoadManifestsFromDir(pluginDir string) ([]*PluginManifest, error)
LoadManifestsFromDir scans pluginDir and returns the full PluginManifest for every installed plugin. It is the full-manifest counterpart to ListInstalled, used by features that need manifest fields beyond name/version/tier/status (e.g., federation's GraphQL block). Directories without a valid plugin.json are silently skipped. If pluginDir does not exist, nil is returned.
func ResolvePlugin ¶ added in v1.3.6
func ResolvePlugin(ctx context.Context, reg *Registry, name, tierOverride string, entitled EntitlementFunc) (*PluginManifest, error)
ResolvePlugin picks the single registry entry `nself plugin install <name>` (or a bundle's own install loop) should install.
- Zero matches: errs.ErrPluginNotFound.
- One match: returned as-is; tierOverride is validated against its own tier and rejected if it disagrees (e.g. --tier pro for a plugin that only has a free entry).
- Two matches that are a declared tier pair (TierPair: true on both): tierOverride "free"/"pro" picks that entry directly ("pro" still runs the entitlement check below — an override never bypasses licensing); no override resolves by entitlement, defaulting to free when the entitlement check is false, erroring, or the operator has no key.
- Two-or-more matches that are NOT a declared tier pair: duplicateSlugError — a registry-data problem, never resolved by picking one.
func (PluginManifest) MarshalJSON ¶ added in v1.3.0
func (m PluginManifest) MarshalJSON() ([]byte, error)
MarshalJSON writes the canonical shapes, so anything this package re-emits round-trips through its own parser.
func (*PluginManifest) UnmarshalJSON ¶ added in v1.3.0
func (m *PluginManifest) UnmarshalJSON(data []byte) error
UnmarshalJSON reads a manifest, normalising the field shapes that vary between published plugins.
Dependencies needs handling here rather than on the field, because the grouped form carries optional entries that belong in a DIFFERENT field (OptionalDependencies), and a field-level unmarshaller cannot write to a sibling. 31 shipped plugins have non-empty optional dependencies, so flattening them into one list or dropping them both change behaviour.
type PluginStatus ¶
type PluginStatus struct {
Name string
State string // starting, running, stopping, stopped, failed
PID int
}
PluginStatus describes the current state of a plugin process.
func Status ¶
func Status(name string) (*PluginStatus, error)
Status returns the current status of a plugin by reading its PID and state files. If the PID file indicates a process that is no longer running, the state is corrected to "stopped".
type Registry ¶
type Registry struct {
Plugins []PluginManifest
// FetchedAt is the registry-wide snapshot timestamp reported by the live
// endpoint's top-level "fetchedAt" field (CLI-R16). Empty for registries
// that don't send it (e.g. the GitHub raw fallback).
FetchedAt string
}
Registry represents the full plugin registry response.
func FetchRegistry ¶
FetchRegistry is the standalone entry point for fetching a registry. It creates an ephemeral client and delegates to Fetch.
Fallback chain:
- registryURL (default: https://plugins.nself.org/registry.json)
- GitHub raw fallback
- Stale cache at cacheDir/registry.json
func (Registry) MarshalJSON ¶
MarshalJSON implements json.Marshaler for Registry so the cache round-trips through the array format consistently.
type RegistryClient ¶
type RegistryClient interface {
Fetch(ctx context.Context) (*Registry, error)
GetPlugin(ctx context.Context, name string) (*PluginManifest, error)
}
RegistryClient abstracts plugin registry HTTP operations for testability.
type ReplayStore ¶ added in v1.0.12
type ReplayStore interface {
// Seen returns true and records the jti if it has not been seen before.
// The entry expires after ttl. Returns (false, nil) if jti is new.
Seen(ctx context.Context, jti string, ttl time.Duration) (bool, error)
}
ReplayStore is the interface for jti replay caching. A Redis-backed implementation is provided by the shared auth package. A no-op shim is used when Redis is unavailable (degrades replay protection, logs a warning).
type StandardEndpoint ¶ added in v1.0.6
type StandardEndpoint struct {
Path string
Required bool // if false, missing endpoint is a warning not an error
}
StandardEndpoint describes one of the standard plugin interface endpoints.
type SystemDependencies ¶
type SystemDependencies struct {
Required []SystemDependency `json:"required,omitempty"`
Recommended []SystemDependency `json:"recommended,omitempty"`
}
SystemDependencies groups required and recommended system deps.
func (*SystemDependencies) UnmarshalJSON ¶ added in v1.3.0
func (s *SystemDependencies) UnmarshalJSON(data []byte) error
UnmarshalJSON lets SystemDependencies accept the empty-array form.
Four shipped manifests write `"systemDependencies": []` where this package expects an object. An empty array means "none", which is the zero value.
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 describes a system-level dependency for a plugin.
type TieredPluginInfo ¶ added in v1.3.6
type TieredPluginInfo struct {
Name string
Tier string
Version string
Category string
TierPair bool
IsDefault bool // true if ResolvePlugin(ctx, reg, Name, "", entitled) picks this exact entry
}
TieredPluginInfo describes one registry entry as shown by `nself plugin list --available`.
func ListAvailableWithTiers ¶ added in v1.3.6
func ListAvailableWithTiers(ctx context.Context, entitled EntitlementFunc) ([]TieredPluginInfo, error)
ListAvailableWithTiers fetches the registry and returns every entry, grouped by case-insensitive slug in first-seen order, with IsDefault set on whichever entry a tier-override-free install would currently resolve to.
type WebhookNames ¶ added in v1.3.0
type WebhookNames []string
WebhookNames is the list of webhook event names a plugin declares.
Purpose: accept both shapes real plugin.json files use. The field was typed `[]string`, but manifests in the wild use an object as often as an array — `{"progress.updated": "Update playback position"}` alongside `["acquisition.subscribed"]`. Unmarshalling an object into []string fails, and ListInstalled skips any plugin whose manifest fails to parse, so those plugins vanished silently from `nself plugin list`, `nself costs`, and everything else that enumerates installed plugins. Nothing reported an error; the plugin was simply absent.
Inputs: the `webhooks` value from plugin.json, in either shape.
Outputs: the event names. For the object form the keys are the event names and the values are human descriptions, so the keys are what this carries.
Constraints: unmarshalling must never fail on a shape a real manifest uses — a manifest this code cannot read makes a plugin invisible rather than loud.
func (WebhookNames) MarshalJSON ¶ added in v1.3.0
func (w WebhookNames) MarshalJSON() ([]byte, error)
MarshalJSON always writes the array form, which is the documented shape.
func (*WebhookNames) UnmarshalJSON ¶ added in v1.3.0
func (w *WebhookNames) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts an array of names, an object keyed by name, or null.
Source Files
¶
- arch.go
- audit.go
- cli_binary.go
- compat.go
- compliance.go
- config.go
- deps.go
- download.go
- env_passthrough.go
- envvars.go
- flatten_extracted.go
- free_account.go
- hmackey.go
- identity.go
- identity_register.go
- install_thirdparty.go
- installer.go
- installer_identity.go
- installer_locked.go
- installer_remove_update.go
- interfaces.go
- license.go
- license_cache.go
- license_hmac_entitlements.go
- lifecycle.go
- loader.go
- manager.go
- manifest.go
- manifest_locate.go
- manifest_shapes.go
- network.go
- paths.go
- permission_derive.go
- permission_set.go
- plugin_nginx.go
- registry.go
- registry_cache.go
- registry_json.go
- registry_parse.go
- router.go
- runtime.go
- runtime_start.go
- runtime_unix.go
- schema.go
- security.go
- security_license.go
- tier_list.go
- tier_resolve.go
- token_client.go
- token_validator.go
- verify.go
- webhooks.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package count is the CLI's single source for "how many plugins are there".
|
Package count is the CLI's single source for "how many plugins are there". |
|
Package lifecycle provides the dormant-plugin state machine for the nSelf plugin license lifecycle.
|
Package lifecycle provides the dormant-plugin state machine for the nSelf plugin license lifecycle. |
|
Package scaffold provides the canonical plugin scaffolding logic shared between the nself CLI (plugin new command) and the standalone new-plugin binary in plugin-sdk-go/devkit.
|
Package scaffold provides the canonical plugin scaffolding logic shared between the nself CLI (plugin new command) and the standalone new-plugin binary in plugin-sdk-go/devkit. |