Appearance
plugin-manifest schema
A real .spk business-application plugin's own spk-assembly/plugin.json contract (id/version/permissions/roles/dependencies).
Pull the full JSON Schema: erp schema pull plugin-manifest · MCP: erp_get_schema {"name":"plugin-manifest"}
Top-level properties
| Property | Type | Required | Notes |
|---|---|---|---|
id | string | yes | |
name | string | yes | |
version | string | yes | |
type | string | ||
vendor | string | ||
license | string | ||
licenseTier | string | ||
category | string | ||
erpVersion | string | ||
minErpVersion | string | ||
maxErpVersion | string | ||
mainClass | string | null | Fully-qualified PF4J plugin main class. Optional (null/absent) for a pure-JSON plugin that ships zero Java classes -- the platform loads it via the built-in JsonOnlyPlugin entry point. | |
schemaName | string | App-owned Postgres schema this plugin's Entity-Engine tables route to — see feedback-entity-engine-tables-must-route-to-app-schema. | |
scope | string | one of: global, tenant — Tenant-Safe Plugin & Extension Architecture spec §54. 'global' (default, every manifest that predates this field): a shared plugin installable by any tenant. 'tenant': a private single-tenant L5 extension — MUST also set tenantId, and if it carries Java code (mainClass, or type java-extension/code-plugin) it MUST declare runtimeModes: ['service'] — the shared runtime refuses to load it (ExtensionInstallValidator, spec §11/§28.10/§74). | |
tenantId | string | Spec §54 — the owning tenant of a scope:'tenant' plugin. Required iff scope=='tenant'; must match the tenant the plugin is installed into. Absent for a global plugin. | |
erpApiVersion | string | Spec §25 — the ERP extension-API contract version this plugin targets (e.g. '5.0'), distinct from erpVersion/minErpVersion (platform build versions). Optional; informational in E1. | |
supportedApplications | array | Spec §25 — applications this extension supports, e.g. ['HCM']. Optional; empty/absent means unconstrained. | |
dependencies | array | ||
optionalDependencies | array | ||
permissions | array | Legacy flat permission-key catalogue; real installs read roles[].permissionKeys instead (PluginRoleInstaller) — commonly left empty on modules whose roles[] is populated. | |
owner | string | ||
editable | boolean | ||
extendable | boolean | ||
capabilitiesProvided | array | ||
capabilitiesRequired | array | ||
featureFlags | array | ||
configSchemaJson | object | string | null | ||
frontendBundle | object | Optional — dynamic frontend-plugin-loading (ai/patterns/code-plugin-sdk.md). Absent on every plugin.json that predates it. | |
roles | array | ||
runtimeModes | array | Added 2026-09-03 (real gap — this field is genuinely load-bearing on every shipped plugin.json today, e.g. hcm-learning's own ["embedded"], but was entirely absent from this schema until now, so erp_get_schema/erp schema pull misrepresented the real manifest shape). Which Plugin Runtime modes this plugin can run under: 'embedded' (loaded in-process via PF4J, the only behavior every pre-existing manifest declares) and/or 'service' (its own standalone Spring Boot process, routed to over HTTP — see PluginManifest#runtimeModes' own Javadoc in engine-plugin-api). Defaults to ['embedded'] when absent. Declaring 'service' here does not itself switch anything — the actual mode is chosen per-plugin via Studio's Plugin Runtime toggle (erp_core.application.deployment_type). | |
serviceRuntime | string | null | one of: java, python, nodejs, null — Added 2026-09-14 (Polyglot Plugin Runtime initiative, PluginManifest#serviceRuntime's own Javadoc in engine-plugin-api). Which language a Service-mode plugin's own process is implemented in. Only meaningful when runtimeModes contains 'service' — an embedded-only plugin is always Java (nothing else can load into the JVM), and installing a manifest that sets this to 'python'/'nodejs' without declaring 'service' in runtimeModes fails install (PluginCodeLifecycle#validateServiceRuntime). null/absent defaults to 'java' — every manifest that predates this field keeps its exact current behavior. Real, proven implementations exist for all three values: hello-plugin-service (java), hello-plugin-service-python (python), hello-plugin-service-node (nodejs) — see 'Build a Service-mode plugin in Python or Node.js' for the SDK and full build/deploy path for each. | |
serviceFramework | string | null | one of: spring-boot, fastapi, express, null — Added 2026-09-14, same initiative as serviceRuntime — purely informational (shown as a read-only chip in Studio's Plugin Runtime panel; not validated against serviceRuntime today, so keep them consistent yourself: spring-boot pairs with java, fastapi with python, express with nodejs). null/absent defaults to 'spring-boot'. | |
serviceDeployment | object | null | Added 2026-09-03 (same real-gap fix as runtimeModes). Recommended deployment/runtime defaults for this plugin when run in 'service' mode (PluginManifest.PluginServiceDeploymentSpec in engine-plugin-api). null (the default for almost every manifest) means Studio seeds an empty config the admin fills in the first time they switch this plugin to Service mode. Only healthPath/timeoutMs/heartbeatIntervalSeconds actually drive runtime behavior today (health checks, proxy call timeout) — docker/kubernetes/cloud are real, persisted, Studio-editable data for a future Deployment Manager, not wired to any live provisioning API. |
Named sub-definitions
resourceLimits