feat: custom checkers / template packages #852

Closed
opened 2026-03-28 04:29:14 +00:00 by mfreeman451 · 2 comments
Owner

Imported from GitHub.

Original GitHub issue: #2487
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/issues/2487
Original created: 2026-01-24T04:27:58Z


Is your feature request related to a problem?

Similiar to how zabbix pulls in their lightweight javascript engine to process user scripts/plugins/templates/whatever they call it, we might want to consider doing something similar, maybe with something a bit more civilized like lua.

High level idea:

Plugins get packaged up with yaml + lua code that define the checks.
Users can upload plugin package to SR and then deploy to an agent, agent conducts checks, etc.

There are some obvious security risks here that make this 100% a bad idea, we would have to convince ourselves first that we can address this, maybe with a sandbox or something.

Looks like this is do-able and safe with wasi.

Describe the solution you'd like

A clear and concise description of what you want to happen.

Describe alternatives you've considered

A clear and concise description of any alternative solutions or features you've considered.

Additional context

Add any other context or screenshots about the feature request here.

Imported from GitHub. Original GitHub issue: #2487 Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/issues/2487 Original created: 2026-01-24T04:27:58Z --- **Is your feature request related to a problem?** Similiar to how zabbix pulls in their lightweight javascript engine to process user scripts/plugins/templates/whatever they call it, we might want to consider doing something similar, maybe with something a bit more civilized like lua. High level idea: Plugins get packaged up with yaml + lua code that define the checks. Users can upload plugin package to SR and then deploy to an agent, agent conducts checks, etc. ~~There are some obvious security risks here that make this 100% a bad idea, we would have to convince ourselves first that we can address this, maybe with a sandbox or something.~~ Looks like this is do-able and safe with wasi. **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here.
mfreeman451 added this to the 1.1.0 milestone 2026-03-28 04:29:14 +00:00
Author
Owner

Imported GitHub comment.

Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/issues/2487#issuecomment-3793795199
Original created: 2026-01-24T04:46:28Z


This Product Requirements Document (PRD) outlines the implementation of a WebAssembly (Wasm)-based plugin system for ServiceRadar.

By leveraging wazero, we provide a high-performance, zero-dependency, and securely sandboxed environment for user-defined checkers.


PRD: ServiceRadar Custom Plugin System (Wasm)

1. Overview

ServiceRadar currently supports fixed checkers (SNMP, ICMP, Sysmon). This project enables users to extend the agent's capabilities by uploading custom .wasm modules through the ServiceRadar UI. These modules will run in a highly restricted sandbox on the agents, communicating via a controlled host-guest bridge.

2. Goals

  • Security: Execute untrusted user code without risk to the host agent or filesystem.
  • Ease of Deployment: No compiler toolchains required on the agent; just download and run.
  • Portability: Plugins written in Go (TinyGo), Rust, or Zig run identically across Linux/Windows/macOS agents.
  • Performance: Near-native execution speed with strict memory and CPU caps.

3. User Stories

  • As an Admin, I want to upload a .wasm file in the UI and assign it to a group of agents.
  • As a Developer, I want to write a custom checker that performs a specific API call and returns a JSON status.
  • As an Operator, I want to ensure a custom plugin cannot scan my internal network unless I explicitly grant it permission.

4. Technical Architecture

4.1 The Agent "PluginService"

A new service implementing the agent.Service interface (see interfaces.go) will be added to the agent.

  • Runtime: wazero (pure Go, no CGO).
  • Lifecycle:
    1. PushLoop receives a PluginConfig from the Gateway.
    2. PluginService downloads the .wasm blob via the ObjectStore (already defined in interfaces.go).
    3. The agent instantiates a Wasm module per check execution.
    4. Results are pushed back to the Gateway via PushLoop.

4.2 The Guest-Host Bridge (Capabilities)

The sandbox will block all raw syscalls. Access to the outside world is provided strictly through Host Functions:

Function Description
http_call(request) Agent performs the HTTP call on behalf of the plugin (enforces allowlists).
get_config() Returns JSON configuration specific to that plugin instance.
log(msg) Pipes plugin logs into the agent.logger.
submit_result(json) Submits the final check status and metrics.

5. Implementation Plan (Agent-Side)

5.1 Configuration Changes (pkg/agent/types.go)

Add a new PluginConfig struct to handle the distribution of Wasm modules.

type PluginConfig struct {
    Plugins []PluginSpec `json:"plugins"`
}

type PluginSpec struct {
    ID           string            `json:"id"`
    Name         string            `json:"name"`
    WasmObjectKey string           `json:"wasm_object_key"` // Link to ObjectStore
    ConfigHash   string            `json:"config_hash"`
    Interval     Duration          `json:"interval"`
    Timeout      Duration          `json:"timeout"`
    Params       map[string]string `json:"params"`        // Custom user params
    Permissions  Permissions       `json:"permissions"`   // e.g., Allowed domains
}

5.2 The Plugin Service (pkg/agent/plugin_service.go)

This service will manage the wazero.Runtime.

type PluginService struct {
    runtime wazero.Runtime
    cache   map[string]wazero.CompiledModule // Cache compiled modules for speed
    // ... typical service fields (mu, logger, config)
}

5.3 Integration with PushLoop (pkg/agent/push_loop.go)

Update fetchAndApplyConfig to recognize the new plugin section and pushStatus to include plugin results.

  1. Polling: fetchAndApplyConfig checks if PluginConfig has changed.
  2. Artifact Sync: If a new WasmObjectKey is found, the agent uses ObjectStore.DownloadObject to fetch the binary.
  3. Result Aggregation: Plugin results are serialized into a standard proto.GatewayServiceStatus message.

6. Security Model

  1. Memory Limits: Each Wasm instance is limited to 16-32MB (configurable).
  2. Execution Timeouts: Strict context cancellation to kill runaway plugins.
  3. Network Sandboxing: Plugins cannot open raw TCP/UDP sockets. They must use the agent's http_call host function, which validates against a domain allowlist.
  4. No Filesystem: The Wasm environment will have no mounted filesystems (FS-less).

7. UI/Gateway Requirements (Brief)

  • Registry: The Gateway must store uploaded .wasm files in the existing ObjectStore (NATS JetStream/S3).
  • Validation: The Gateway should verify that uploaded files are valid Wasm binaries.
  • Versioning: Increment ConfigVersion in the gRPC AgentConfigResponse whenever a plugin is added or updated.

8. Success Metrics

  • Initialization Time: Wasm modules should instantiate in < 10ms.
  • Stability: A crashing plugin (panic) must not crash the Go Agent.
  • Cleanup: 100% memory reclamation after a plugin execution finishes.

9. Next Steps for Development

  1. Add wazero to go.mod.
  2. Implement plugin_service.go in pkg/agent.
  3. Define the Host Function signatures (the "ABI").
  4. Create a "Hello World" TinyGo checker to test the end-to-end flow.
Imported GitHub comment. Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/issues/2487#issuecomment-3793795199 Original created: 2026-01-24T04:46:28Z --- This Product Requirements Document (PRD) outlines the implementation of a WebAssembly (Wasm)-based plugin system for **ServiceRadar**. By leveraging **wazero**, we provide a high-performance, zero-dependency, and securely sandboxed environment for user-defined checkers. --- # PRD: ServiceRadar Custom Plugin System (Wasm) ## 1. Overview ServiceRadar currently supports fixed checkers (SNMP, ICMP, Sysmon). This project enables users to extend the agent's capabilities by uploading custom `.wasm` modules through the ServiceRadar UI. These modules will run in a highly restricted sandbox on the agents, communicating via a controlled host-guest bridge. ## 2. Goals * **Security:** Execute untrusted user code without risk to the host agent or filesystem. * **Ease of Deployment:** No compiler toolchains required on the agent; just download and run. * **Portability:** Plugins written in Go (TinyGo), Rust, or Zig run identically across Linux/Windows/macOS agents. * **Performance:** Near-native execution speed with strict memory and CPU caps. ## 3. User Stories * **As an Admin**, I want to upload a `.wasm` file in the UI and assign it to a group of agents. * **As a Developer**, I want to write a custom checker that performs a specific API call and returns a JSON status. * **As an Operator**, I want to ensure a custom plugin cannot scan my internal network unless I explicitly grant it permission. --- ## 4. Technical Architecture ### 4.1 The Agent "PluginService" A new service implementing the `agent.Service` interface (see `interfaces.go`) will be added to the agent. * **Runtime:** `wazero` (pure Go, no CGO). * **Lifecycle:** 1. `PushLoop` receives a `PluginConfig` from the Gateway. 2. `PluginService` downloads the `.wasm` blob via the `ObjectStore` (already defined in `interfaces.go`). 3. The agent instantiates a Wasm module per check execution. 4. Results are pushed back to the Gateway via `PushLoop`. ### 4.2 The Guest-Host Bridge (Capabilities) The sandbox will block all raw syscalls. Access to the outside world is provided strictly through **Host Functions**: | Function | Description | | :--- | :--- | | `http_call(request)` | Agent performs the HTTP call on behalf of the plugin (enforces allowlists). | | `get_config()` | Returns JSON configuration specific to that plugin instance. | | `log(msg)` | Pipes plugin logs into the `agent.logger`. | | `submit_result(json)` | Submits the final check status and metrics. | --- ## 5. Implementation Plan (Agent-Side) ### 5.1 Configuration Changes (`pkg/agent/types.go`) Add a new `PluginConfig` struct to handle the distribution of Wasm modules. ```go type PluginConfig struct { Plugins []PluginSpec `json:"plugins"` } type PluginSpec struct { ID string `json:"id"` Name string `json:"name"` WasmObjectKey string `json:"wasm_object_key"` // Link to ObjectStore ConfigHash string `json:"config_hash"` Interval Duration `json:"interval"` Timeout Duration `json:"timeout"` Params map[string]string `json:"params"` // Custom user params Permissions Permissions `json:"permissions"` // e.g., Allowed domains } ``` ### 5.2 The Plugin Service (`pkg/agent/plugin_service.go`) This service will manage the `wazero.Runtime`. ```go type PluginService struct { runtime wazero.Runtime cache map[string]wazero.CompiledModule // Cache compiled modules for speed // ... typical service fields (mu, logger, config) } ``` ### 5.3 Integration with `PushLoop` (`pkg/agent/push_loop.go`) Update `fetchAndApplyConfig` to recognize the new plugin section and `pushStatus` to include plugin results. 1. **Polling:** `fetchAndApplyConfig` checks if `PluginConfig` has changed. 2. **Artifact Sync:** If a new `WasmObjectKey` is found, the agent uses `ObjectStore.DownloadObject` to fetch the binary. 3. **Result Aggregation:** Plugin results are serialized into a standard `proto.GatewayServiceStatus` message. --- ## 6. Security Model 1. **Memory Limits:** Each Wasm instance is limited to 16-32MB (configurable). 2. **Execution Timeouts:** Strict `context` cancellation to kill runaway plugins. 3. **Network Sandboxing:** Plugins cannot open raw TCP/UDP sockets. They must use the agent's `http_call` host function, which validates against a domain allowlist. 4. **No Filesystem:** The Wasm environment will have no mounted filesystems (FS-less). --- ## 7. UI/Gateway Requirements (Brief) * **Registry:** The Gateway must store uploaded `.wasm` files in the existing `ObjectStore` (NATS JetStream/S3). * **Validation:** The Gateway should verify that uploaded files are valid Wasm binaries. * **Versioning:** Increment `ConfigVersion` in the gRPC `AgentConfigResponse` whenever a plugin is added or updated. --- ## 8. Success Metrics * **Initialization Time:** Wasm modules should instantiate in < 10ms. * **Stability:** A crashing plugin (panic) must not crash the Go Agent. * **Cleanup:** 100% memory reclamation after a plugin execution finishes. --- ## 9. Next Steps for Development 1. Add `wazero` to `go.mod`. 2. Implement `plugin_service.go` in `pkg/agent`. 3. Define the Host Function signatures (the "ABI"). 4. Create a "Hello World" TinyGo checker to test the end-to-end flow.
Author
Owner

Imported GitHub comment.

Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/issues/2487#issuecomment-3794284781
Original created: 2026-01-24T09:15:31Z


https://github.com/wazero/wazero/issues/2393 -- Code-signing entitlements needed for OSX

Imported GitHub comment. Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/issues/2487#issuecomment-3794284781 Original created: 2026-01-24T09:15:31Z --- https://github.com/wazero/wazero/issues/2393 -- Code-signing entitlements needed for OSX
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
carverauto/serviceradar#852
No description provided.