Files
HiveGrid-Wireframes/hivegrid-cloud/docs/plugins/cli-reference.md
T

5.7 KiB

Plugin CLI Reference

toolctrl-cli plugins ... — the author + operator command surface for the plugin pipeline. Operator subcommands manage installed-plugin rows; author subcommands scaffold / build / sign / lint / publish.

toolctrl-cli plugins --help

Author subcommands

scaffold

toolctrl-cli plugins scaffold \
  --name slack-notifier \
  --publisher de.acme \
  --description "Send tenant events to Slack" \
  [--target-dir ./my-plugins] [--dry-run]

Generates a new plugin skeleton under <targetDir>/<name>/:

slack-notifier/
├── plugin.toml      # manifest seed (placeholder sig/hash)
├── src/index.ts     # HookHandler example
├── package.json     # @toolctrl/plugin-sdk devDependency
├── tsconfig.json    # ES2022 strict
└── README.md        # next-step checklist

--dry-run prints the file list without writing anything.

build

toolctrl-cli plugins build [--cwd ./slack-notifier] [--output plugin.wasm]

Detects the project's wasm toolchain (AssemblyScript via asconfig.json, TinyGo via go.mod, Rust via Cargo.toml) and prints the canonical build command. Does NOT auto-execute — pipe the command into your CI or run it yourself. Exits non-zero when no toolchain marker is found.

sign

toolctrl-cli plugins sign \
  --wasm plugin.wasm \
  --manifest plugin.json \
  --key ./keys/publisher.priv \
  [--key-id staging-2026-05] \
  [--out-sig plugin.wasm.sig]
  1. SHA-256-hashes the wasm.
  2. Builds the same canonical manifest projection used by the runtime verifier (sorted-keys JSON minus signature/signedBy/wasmSha256).
  3. Signs <canonical>:<wasmSha256> with the ed25519 private key.
  4. Writes the raw signature hex to <wasm>.sig (or --out-sig).
  5. Prints the wasmSha256 / signature / signedBy lines to splice back into the manifest.

The private key is read from disk and never echoed. --key-id is logged only — useful for operators to track which signing key produced which artifact.

lint

toolctrl-cli plugins lint --manifest plugin.json

Runs the host Zod schema, then layers author checks on top:

  • manifest_invalid (error) — Zod rejected the manifest.
  • unknown_capability (error) — capability not in the host registry.
  • invalid_host (error) — allowedHosts entry is not a hostname.
  • unsigned_placeholder (warn) — signature/signedBy still zeros.
  • unsigned_wasm (warn) — wasmSha256 still zeros.
  • http_outbound_unrestricted (warn) — http.outbound declared but allowedHosts is empty.
  • high_risk_capability (warn) — db.write declared.

Exit codes:

Code Meaning
0 Clean
1 At least one error
2 Warnings only

publish

toolctrl-cli plugins publish \
  --wasm plugin.wasm \
  --manifest plugin.json \
  [--signature plugin.wasm.sig] \
  [--out ./dist]

Packs plugin.wasm + plugin.json (+ plugin.wasm.sig if present) into <pluginId>-<version>.tar.gz ready for operator upload. Local-only — no HTTP push. Operators must inspect what they upload; auto-uploading would hide bad signatures from review.

Operator subcommands

Command Action
plugins list List installed plugin rows (filter by tenantId + enabled state)
plugins enable <tenantId> <pluginId> Enable an installed plugin
plugins disable <tenantId> <pluginId> Disable an installed plugin
plugins uninstall <tenantId> <pluginId> Operator-only deletion of the tenant Plugin row (cascades user configs)
plugins verify-audit-chain <tenantId> [--pluginId <pluginId>] [--from <iso>] [--to <iso>] [--json] Verify the tenant-wide PluginCapabilityUse hash chain; --pluginId reports matching rows without slicing the chain

These operate against the host database directly; they are intended for incident-response and don't substitute the tenant-admin UI. Uninstall is deliberately available only through this operator CLI path until plugin-owned data cleanup/down-migration semantics are reviewed.

Operator lifecycle commands use the same production gates as the service path: production enables require PLUGIN_RUNTIME_PRODUCTION_ENABLED=true, reject PLUGIN_REQUIRE_CAPABILITY_CONSENT=false, reject capabilities whose host bindings are not production-reviewed, check current grants before enabling, and write lifecycle audit events transactionally with the mutation. Disable remains available as the emergency containment path.

Permission split: tenant routes and UI use app.plugins.use for installed plugin reads, per-user non-secret settings, and settings/audit surfaces. app.plugins.manage is still required for tenant API/UI install, grant/revoke, enable/disable, and future update flows. CLI commands are operator escape hatches against the host database and do not replace the tenant permission model; CLI uninstall is not exposed as a tenant permissioned API/UI flow.

Round-trip smoke

toolctrl-cli plugins scaffold --name smoke --publisher de.smoke --dry-run
toolctrl-cli plugins build --cwd ./smoke
# (run the printed build command in your wasm toolchain)
toolctrl-cli plugins lint --manifest ./smoke/plugin.json
toolctrl-cli plugins sign --wasm ./smoke/plugin.wasm --manifest ./smoke/plugin.json --key ./keys/dev.priv
toolctrl-cli plugins publish --wasm ./smoke/plugin.wasm --manifest ./smoke/plugin.json

The output is the file you upload through the tenant-admin plugins settings page.