Skip to main content

Wasm Plugins

ServiceRadar supports sandboxed WebAssembly (Wasm) plugins for custom checkers and integrations. Plugins are uploaded or imported through the web UI, reviewed for capabilities and allowlists, and then assigned to agents. Agents run plugins in an embedded Wasm runtime (wazero) with strict resource limits and a capability-based host ABI.

This page is an operator-facing conceptual overview. For the full plugin SDK and authoring reference — manifest fields, config and result schemas, the host ABI, code examples, and build instructions — see the developer portal at developer.serviceradar.cloud.

Why Wasm Plugins

Wasm plugins let ServiceRadar extend its checking capabilities without trusting arbitrary native code on the edge:

  • Sandboxed. Each plugin runs inside an isolated Wasm runtime. It cannot touch the host filesystem, network, or processes directly.
  • Capability-based. A plugin can only do what its manifest explicitly declares and an operator explicitly approves. Every host call is mediated and enforced.
  • Resource-limited. The agent enforces per-plugin budgets for memory, CPU time, and open connections.
  • Portable. Plugins compile to a single wasm32-wasi artifact that runs identically across agent platforms.

The current edge model is push-based: the agent streams results to agent-gateway. External "pull" checkers are not part of the primary architecture; prefer Wasm plugins or first-party collectors that publish into the normal pipelines.

Wasm is also how ServiceRadar ships certain first-party checks. For example, the Dusk checker runs as a Wasm plugin executed by serviceradar-agent rather than as a standalone service. Wasm plugins are one part of the edge runtime — the agent also runs embedded engines (sync integrations, SNMP polling, discovery/mapping, mDNS) alongside plugins.

Package Format

Each plugin package is made up of:

  • plugin.yaml — the manifest (plugin identity, capabilities, permissions, resource requests)
  • plugin.wasm — the compiled Wasm binary
  • optional sidecars such as a config JSON Schema or display contract

The control plane stores the manifest and config schema in the database and stores the Wasm binary in the configured package storage backend.

The exact manifest fields, the supported config JSON Schema subset, and the serviceradar.plugin_result.v1 result schema are documented in full on the developer portal.

Capability and Permission Model

Capabilities and permissions are the core of the plugin security model. They are declared in the manifest and approved during import review. The agent enforces both the capability list and the permission allowlists on every host call.

  • Capabilities name the host functions a plugin is allowed to call — for example, retrieving its config, emitting logs, submitting a result, making HTTP requests, or opening TCP/UDP connections. A plugin cannot call a host function it did not declare.
  • Permissions are the allowlists that scope those capabilities — for example, the set of HTTP hostnames, CIDR networks, and ports a plugin may reach. Network access is denied by default and only widened by explicit allowlist entries.

Because capabilities and permissions are visible in the manifest, reviewers can see a plugin's full blast radius before approving it. Always confirm them during import review, especially for plugins assigned to customer edge agents or networks that can reach sensitive systems.

The full list of capability names and permission keys lives on the developer portal.

SDKs and Authoring

Plugins compile to wasm32-wasi and export a zero-argument entrypoint that matches the manifest. ServiceRadar publishes SDKs that provide a higher-level API over the host ABI so you do not have to work with raw host imports.

See the SDKs & Plugin Development overview for a summary of the available SDKs, and the developer portal for the complete authoring reference, code examples, and build instructions.

Upload and Import Workflow

The plugin lifecycle is operator-facing and gated by an approval step:

  1. Upload or import a plugin package in the admin UI.
  2. The package is staged and must be approved before it can be used.
  3. During review, confirm the requested capabilities, permissions, and resource budget.
  4. Approved packages can be assigned to agents.
  5. Agents download packages only from the ServiceRadar control plane — never directly from GitHub.

Plugin blob upload and download tokens are transported only in explicit headers or POST bodies. Query-string bearer tokens are not supported.

Assigned health-result plugins, including first-party plugins such as UniFi and AlienVault OTX, appear in /services with a stable plugin service identity. When an assignment is created, the control plane seeds a pending service row; the next agent-reported plugin result updates that row with the plugin status and summary.

First-party plugin import

ServiceRadar ships first-party Wasm plugins as signed artifacts published by release automation. The Plugins UI can sync a first-party plugin index, verify the referenced signed bundle, mirror the Wasm payload into ServiceRadar-managed plugin storage, and stage the package for normal capability review. Imported first-party packages are not assignable until an authorized operator approves them.

GitHub imports and verification

For GitHub-sourced plugins, the control plane fetches plugin.yaml, plugin.wasm, and an optional config schema. Commit verification is captured from GitHub. If PLUGIN_REQUIRE_GPG_FOR_GITHUB=true, unsigned or unverified commits are rejected during import.

Deployment and Storage Configuration

Wasm packages are served by the web-ng API and stored using a configurable backend. For production, store plugin blobs on persistent storage and back them up with normal platform operations. Plugin blob authorization is token-gated, with bearer tokens carried in request headers or POST bodies rather than embedded in request URLs.

Filesystem backend (default)

  • Storage path: /var/lib/serviceradar/plugin-packages
  • Configure web-ng with:
    • PLUGIN_STORAGE_BACKEND=filesystem
    • PLUGIN_STORAGE_PATH=/var/lib/serviceradar/plugin-packages
    • PLUGIN_STORAGE_SIGNING_SECRET (shared with core for signed plugin blob tokens)
  • Docker: mount a volume to /var/lib/serviceradar/plugin-packages in the web-ng container.
  • Kubernetes: mount a PVC at /var/lib/serviceradar/plugin-packages for the web-ng deployment.

For core plugin blob delivery, set:

  • PLUGIN_STORAGE_PUBLIC_URL — base URL for web-ng (your deployment's web-ng endpoint)
  • PLUGIN_STORAGE_SIGNING_SECRET — must match web-ng
  • PLUGIN_STORAGE_DOWNLOAD_TTL_SECONDS — default 86400

Agents receive a plain plugin blob endpoint plus a separate short-lived token, so plugin config never contains a tokenized URL.

JetStream object store

To store plugin blobs in NATS JetStream instead, set:

  • PLUGIN_STORAGE_BACKEND=jetstream
  • PLUGIN_STORAGE_BUCKET=serviceradar_plugins
  • PLUGIN_STORAGE_JS_MAX_BUCKET_BYTES
  • PLUGIN_STORAGE_JS_MAX_CHUNK_BYTES
  • PLUGIN_STORAGE_JS_REPLICAS
  • PLUGIN_STORAGE_JS_STORAGE (file or memory)
  • PLUGIN_STORAGE_JS_TTL_SECONDS

This backend requires NATS JetStream to be available to web-ng.

GitHub access and verification policy

  • GITHUB_TOKEN or GH_TOKEN for private repos
  • PLUGIN_REQUIRE_GPG_FOR_GITHUB=true to reject unverified commits
  • PLUGIN_ALLOW_UNSIGNED_UPLOADS=false to require signatures for uploads

AlienVault OTX Threat Intel

ServiceRadar ships a first-party alienvault-otx-threat-intel Wasm plugin for edge-side OTX collection. Use Settings -> Networks -> Threat Intel to assign the approved package to an agent, set the OTX base URL, page size, timeout, and a secret reference for the API key. The key is used by the plugin through the normal secret-ref flow and is not displayed back in the UI. The edge plugin needs outbound HTTPS egress to the configured OTX host, normally otx.alienvault.com:443.

Core-hosted OTX sync is also available for deployments that prefer the control plane to poll OTX directly. Configure the core worker with these environment variables:

  • SERVICERADAR_OTX_API_KEY or SERVICERADAR_OTX_API_KEY_FILE
  • SERVICERADAR_OTX_BASE_URL (defaults to https://otx.alienvault.com)
  • SERVICERADAR_OTX_PAGE_SIZE
  • SERVICERADAR_OTX_TIMEOUT_MS
  • SERVICERADAR_OTX_MAX_INDICATORS
  • SERVICERADAR_OTX_MAX_RETRIES
  • SERVICERADAR_OTX_BACKOFF_MS
  • SERVICERADAR_OTX_MODIFIED_SINCE
  • SERVICERADAR_OTX_PARTITION

Prefer the *_FILE form for Kubernetes secrets. Rotate OTX keys through the secret backend or Kubernetes secret, then restart or roll the affected pod so runtime config is refreshed. After rotation, use Sync Now on the Threat Intel settings page and verify Sync Health shows a fresh successful run.

When raw payload archival is enabled in Threat Intel settings, core stores decoded OTX page payload snapshots in NATS Object Store. Archival is optional; if NATS Object Store is unavailable, normalized indicator ingest continues and the archive failure is logged. The core defaults are:

  • SERVICERADAR_OTX_RAW_BUCKET=serviceradar_threat_intel
  • SERVICERADAR_OTX_RAW_TTL_SECONDS=0
  • SERVICERADAR_OTX_RAW_MAX_BUCKET_BYTES
  • SERVICERADAR_OTX_RAW_MAX_CHUNK_BYTES
  • SERVICERADAR_OTX_RAW_REPLICAS=1
  • SERVICERADAR_OTX_RAW_STORAGE=file

Operational Tips

  • Keep per-agent engine limits conservative and override down in assignments if needed.
  • Use the Settings -> Agent capacity view to confirm headroom before assignments.
  • Store plugin source details in the manifest source section for auditability.
  • Plugin result payloads should use canonical statuses OK, WARNING, CRITICAL, or UNKNOWN. The agent maps common failure aliases (failed, fail, error) to CRITICAL so a failed execution is visible as unhealthy.