Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/docs/plugins/manifest-reference.md
T
2026-07-08 16:15:37 +02:00

8.0 KiB

Plugin Manifest Reference

The plugin manifest is the contract between an installable WASM binary and the host runtime. The host parses the manifest with the Zod schema in packages/plugin-runtime/src/manifest.ts and rejects anything that does not validate — adding a field here without updating the schema does nothing.

The manifest may be TOML or JSON (whichever your toolchain emits). The runtime works against the parsed object, not the source bytes — except during signing, where the canonical form is the sorted-keys JSON projection (see cli-reference.md#sign).

Top-level fields

Field Type Required Notes
schemaVersion 1 yes Bumped on breaking schema changes. Host rejects unknown versions.
id reverse-DNS string yes Pattern ^[a-z]+(\.[a-z0-9-]+){1,}$. Globally unique across hosts.
version semver yes MAJOR.MINOR.PATCH[-prerelease]. Host enforces SemVer for upgrades.
name string ≤ 256 chars yes Display name.
description string ≤ 2048 chars no Free-form for the marketplace.
entry string ≤ 256 chars yes Path of the wasm relative to the manifest, e.g. plugin.wasm.
wasmSha256 64-char hex yes SHA-256 of the binary at signing time. Re-checked on every load.
signature 128-char hex yes Ed25519 signature over canonicalManifest:wasmSha256.
signedBy 64-char hex yes Raw 32-byte ed25519 public key (hex). Registered with operator.
allowedHosts array of public hostnames no Outbound HTTP allowlist for http.outbound. Empty disables HTTP. No scheme, port, localhost, IP literal, .local, or .internal.
capabilities array of capability entries no Capability declarations. See below.
hooks array of hook entries (≥1) yes At least one hook must be declared; a zero-hook plugin never runs.
ui.settings.entry relative bundle path no Optional settings HTML entry rendered by the host in a sandboxed PluginSettingsFrame. Absolute paths, URL schemes, and .. segments are rejected. When present, the settings entry must also declare ui.settings.sha256 or ui.settings.integrity so the host can verify the bundled HTML before rendering it.

Settings HTML communicates with the host through the versioned plugin.settings.* postMessage protocol documented in sdk-reference.md#settings-iframe-protocol.

Capability entries

[[capabilities]]
name = "http.outbound"
scope = { allowedHosts = ["hooks.slack.com"] }

[[capabilities]]
name = "secret.read"
scope = { keyPattern = "slack-webhook-.*" }

Each entry has:

  • name — capability name from the closed enum (see capability-matrix.md).

  • scope — optional, capability-specific narrowing. Permitted shape:

    Field Allowed for Meaning
    allowedHosts http.outbound Hostname allowlist (no scheme, no port)
    keyPattern secret.read Regex (1-128 chars) the key must match
    tablePrefix db.read, db.write Required prefix; pattern ^[a-z][a-z0-9_]*_$

    Other keys are rejected by .strict().

Hook entries

[[hooks]]
name = "entity.created"
export = "handleEntityCreated"
  • name — host hook event name; must match an entry the host emits.
  • export — function name exported from the wasm binary that handles the hook.

Both fields cap at 128 characters.

Foundation Hook Payloads

The SDK exports matching payload types from @hivegrid/plugin-sdk (TypedHookHandler<"entity.updated">, PluginHookWorkflowExecutedPayload, and friends). Runtime validation is strict, so handlers should rely on these field names instead of ad-hoc shapes.

Hook Payload summary
tenant.created tenantId, slug, optional ownerEmail, optional plan, createdAt
user.created userId, optional nullable tenantId, email, optional locale, createdAt
entity.created / entity.updated / entity.deleted entityType, entityId, nullable tenantId, optional userId, optional changedFields, occurredAt
workflow.created / workflow.updated / workflow.deleted nullable tenantId, workflowId, nullable userId, status, appliesToAllTenants, optional changedFields, occurredAt; workflow blocks, credentials, and raw input are intentionally not included.
workflow.executed tenantId, workflowId, workflowExecutionId, optional nullable userId, type, status, durationMs, errorCode (null for success, required for error), appliesToAllTenants, occurredAt; raw execution input, output, and error bodies are intentionally not included.
workflow.failed tenantId, workflowId, workflowExecutionId, optional nullable userId, type, fixed status: "error", required errorCode, durationMs, appliesToAllTenants, occurredAt; raw execution input, output, and error bodies are intentionally not included.

Strictness

The schema uses Zod's .strict() everywhere — unknown fields cause parse failure. This is intentional: extending the manifest is a deliberate, host-side operation, never a plugin-author hack.

Canonical bytes (for signing)

The CLI and runtime verifier both sign and verify <canonicalManifestJSON>:<wasmSha256> where the canonical form is the manifest minus signature, signedBy, and wasmSha256, projected to JSON with deeply sorted keys (see canonicalPluginSignaturePayload in packages/plugin-runtime/src/signature.ts).

This means adding / removing capabilities, hooks, or any other field invalidates the signature — re-signing is required for every authoring change.

Example (TOML)

schemaVersion = 1
id = "de.acme.slack-notifier"
version = "1.2.0"
name = "Slack Notifier"
description = "Sends tenant events to a Slack channel."
entry = "plugin.wasm"
wasmSha256 = "a1b2..."
signature  = "c3d4..."
signedBy   = "e5f6..."
allowedHosts = ["hooks.slack.com"]

[[capabilities]]
name = "log"

[[capabilities]]
name = "secret.read"
scope = { keyPattern = "slack-webhook-.*" }

[[capabilities]]
name = "http.outbound"
scope = { allowedHosts = ["hooks.slack.com"] }

[[hooks]]
name = "entity.created"
export = "handleEntityCreated"

[[hooks]]
name = "entity.updated"
export = "handleEntityUpdated"

Versioning + compatibility

Plugins may carry a minHostVersion / maxHostVersion constraint (see versioning.md). Install rejects manifests whose declared host-version range does not include the current host version.