Docs Plugin operations

Plugin operations: deployment, limits, publishing

Use this page when you administer plugins for a Mockarty installation. It shows
which deployment types support each plugin, how to control registries and
signatures, and how to publish a bundle. For installation steps, see
Plugins; for creating one, see Writing plugins.

Deployment shapes

Choose the installation path that matches your Mockarty deployment:

  • Single node: upload a .zip in Admin → Plugins. PostgreSQL and SQLite installations both support file uploads.
  • Cluster: use the same Admin → Plugins flow. Admin nodes share installed bundles and enabled state, so an administrator does not need to upload the file separately to each node. Check that all nodes can reach the shared database and cluster messaging service before enabling a plugin.
  • Desktop: install a local file without network access. Browsing a remote registry requires a connection to that registry.
  • Air-gapped installation: upload a local bundle. A remote registry is optional and stays off until configured.

Managed native programs have additional placement restrictions: a multi-node cluster refuses them. Check the install preview before enabling one. Mock kits, content packs, and sandboxed WebAssembly plugins can be used without a managed native program.

Environment variables

Variable Default Effect
MOCKARTY_PLUGINS_ENABLED on Kill-switch. false / 0 / off removes the plugin API, UI section and agent tools entirely.
MOCKARTY_PLUGINS_REGISTRY_URL disabled URL of a registry index.json (for example, a GitHub raw URL or an internal catalogue). Unset means no external registry and no registry request. Set it explicitly to enable a catalogue, or to off to disable one configured elsewhere.
MOCKARTY_PLUGINS_REGISTRY_ALLOW_HTTP off Dev mode: allows http:// download URLs only for loopback hosts, so a plugin author can test the full list→install flow against a local static server. Never needed in production.
MOCKARTY_PLUGINS_REGISTRY_ALLOW_PRIVATE off Corporate mode: the registry may live on the intranet (no GitHub access) — private-range hosts and plain http:// are admitted for the registry client only. Cloud-metadata and link-local addresses stay blocked regardless. Use with an internal static host serving index.json + zips.
MOCKARTY_PLUGINS_TRUSTED_KEYS unset Comma-separated base64 ed25519 public keys. Bundles signed by one of them show signatureStatus: trusted.
MOCKARTY_PLUGINS_REQUIRE_SIGNATURE off true refuses to install unsigned bundles or bundles signed by an untrusted key — for regulated contours.
MOCKARTY_WIKI_IFRAME_ALLOWLIST unset (Wiki) restricts which hosts wiki !iframe: embeds may point at; none forbids external embeds.

Namespace-install policy

Whether a namespace owner may self-install no-code plugins into their own
namespace is an administrator toggle in the UI (Admin → Plugins), not an
environment variable — it changes product behaviour, so it lives where an
administrator can flip it without a redeploy. Default OFF. When on, a namespace
owner can install only no-code bundles (mock kits, content packs, wiki macros,
link types, connector presets) from a marketplace visible to their namespace,
scoped to that namespace alone; WASM modules, UI panels, event types and task
types still require an administrator. Every namespace install is audited
(plugin_ns_installed), and administrators keep the kill-switch — they see and
can uninstall namespace plugins from the same tab.

Hard limits

These are validated at install time and fail with a structured error code —
they are not configurable:

Limit Value
Bundle zip size (compressed) 32 MiB
Files per bundle 2000
Uncompressed total / per file 64 MiB / 16 MiB
Contributions per family (kits, packs, links, …) 50
Name / description length 200 / 4000 characters
Plugin task-type name plugin- prefix, ≤ 19 characters
Plugin event-type name plugin. prefix

Security model (what a plugin can and cannot do)

  • Declarative contributions (mock kits, content packs, connectors, links,
    event/task types) are data. The host renders and applies them; they execute
    nothing.
  • Code runs only inside a WebAssembly sandbox: no disk, no network, no
    clock, no host memory — with fuel, time and memory caps per call. A broken or
    slow module fails only its own call, never the request or the process.
  • UI panels render in a sandboxed iframe with a strict Content-Security-
    Policy at an opaque origin: no host session, no cookies, no DOM access. The
    host pushes the plugin’s (non-secret) settings in via postMessage.
  • Downloads are pinned and guarded. Registry installs verify the bundle’s
    sha256 against the index; install-by-URL accepts an optional sha256 pin; both
    run through an SSRF-guarded HTTP client that refuses loopback and private
    addresses.
  • Secrets never live in a manifest. Connector plugins ship configuration
    templates; credentials are entered per namespace in Settings → Integrations
    and encrypted at rest.
  • Multi-tenancy: an administrator installs once; every namespace
    independently enables/disables each plugin and overrides its settings. All
    catalogue and hot-path reads are filtered per namespace.
  • Audit: every install, enable, disable, uninstall and per-namespace toggle
    lands in the audit log with the acting user.

Structured error codes

Plugin API failures return {"error": "<detail>", "code": "<stable code>"}.
The UI shows a localized message per code; agents and scripts can branch on it:

bundle_too_large, bundle_invalid, manifest_missing, manifest_invalid,
asset_missing, signature_invalid, host_incompatible,
contribution_conflict, not_found, storage_error, internal.

Publishing to a registry

A registry is a static index.json plus downloadable zips — a GitHub
repository with Releases is the reference setup; any static host works.

  1. Build and validate the bundle: mockarty-cli plugin pack . then
    mockarty-cli plugin inspect <zip>.
  2. Generate the index entry:
    mockarty-cli plugin publish <zip> --download-url https://github.com/<org>/<repo>/releases/download/<id>-v<version>/<zip>
    — it prints a ready JSON object with the correct sha256.
  3. Upload the zip as a Release asset at exactly that URL and add the object to
    the plugins array of the repository’s index.json (via a pull request in
    a shared registry).
  4. Point servers at the raw index URL via MOCKARTY_PLUGINS_REGISTRY_URL. The
    catalogue appears under Admin → Plugins; installs verify the sha256.

The digest is of the file you upload, not of a re-packed source. Packing
the same plugin twice can produce different bytes — archives record mtimes and
file modes, and compressors differ between builds — so a sha256 taken from a
bundle packed on your machine may not match the artifact on the release, and
the install is then refused with downloaded bundle sha256 … does not match
the registry index
. Take the digest from the file you actually publish
(mockarty-cli plugin publish <the-uploaded.zip> prints it), and re-run the
publish step whenever you replace an asset.

Version upgrades are in-place: publishing 1.1.0 and installing it over
1.0.0 keeps the enabled state and swaps the contributions atomically.

The capability kill-switch

Everything a plugin or an external integration contributes — mission
components, runner task types — is listed in the unified capability catalogue
(GET /api/v1/capabilities), alongside the built-in capabilities. When a
dynamic capability must be shut down instantly — a compromised vendor, a
misbehaving integration — an administrator can open Autonomous Missions →
Capabilities
and use Disable. The same operation is available for
automation in one call:

curl -X POST "$MOCKARTY_URL/api/v1/capabilities/<key>/revoke" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"reason": "vendor key compromised"}'

The reason is mandatory and is shown wherever the capability would have
appeared. One call is enough:

  • the capability turns unavailable in the catalogue with
    availability.reason: "revoked" and your reason in revokedReason — both
    people and agents see why before planning around it;
  • operator-managed rows carry operatorManaged: true, a managementKey, and
    the exact managementNamespace; the Missions UI uses that durable identity
    for Disable/Restore without guessing from a display facet or confusing a
    namespace-local row with an instance-wide policy;
  • if the capability came from a plugin, that plugin is disabled on every node
    in the same call (the response names it in sourceDisabled);
  • re-installing or re-enabling the source does not resurrect the
    capability — the attempt fails with contribution_conflict and the stored
    reason, until the revocation is lifted explicitly.

The operation is fail-closed. If Mockarty cannot disable the plugin source or
cannot read/persist the durable kill-switch state, it returns 503 and never
claims a successful revocation while executable contributions may still be
active. Retry after restoring the database/plugin store; a plugin that was
already disabled remains safely off during that retry.

Lifting it is a deliberate two-step decision:
POST /api/v1/capabilities/<key>/restore removes the mark, and the source
plugin still has to be enabled again by hand. External capabilities that are
registered but not executable on this server appear in the catalogue with
availability.reason: "declared" — visible for planning, not a promise of
execution. An administrator can also register such a row from Autonomous
Missions → Capabilities → Register capability
. All three operations are
administrator-only and audited.

Troubleshooting

  • Plugin installed but nothing appeared — it lands disabled by design;
    press Enable. If it is enabled and still missing in a namespace, check the
    per-namespace toggle (Admin → Plugins → Enable here) and the plugin’s
    default_off flag.
  • healthy: false in plugin status — the node failed to apply a
    contribution (typically a WASM module built for WASI instead of
    wasm-unknown, or a key conflict with another plugin). The server log names
    the cause.
  • Registry list is empty — this is expected until
    MOCKARTY_PLUGINS_REGISTRY_URL or an operator-managed source is configured.
    If a source is configured, the index may be unreachable or may fail
    validation. The API response carries a hint in every case: a source URL is
    checked when it is added, and “we reached it and it is not a registry”
    answers 400 rather than 502.
  • Install from URL fails with bundle_invalid — the URL must be http(s)
    and reachable without touching private address space; pin the sha256 to rule
    out a corrupted download.