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
.zipin 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 viapostMessage. - 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.
- Build and validate the bundle:
mockarty-cli plugin pack .then
mockarty-cli plugin inspect <zip>. - 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. - Upload the zip as a Release asset at exactly that URL and add the object to
thepluginsarray of the repository’sindex.json(via a pull request in
a shared registry). - 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 inrevokedReason— both
people and agents see why before planning around it; - operator-managed rows carry
operatorManaged: true, amanagementKey, and
the exactmanagementNamespace; 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 insourceDisabled); - re-installing or re-enabling the source does not resurrect the
capability — the attempt fails withcontribution_conflictand 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_offflag. healthy: falsein 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_URLor 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.