Native Add-ons (Agent Feature Sets)
ServiceRadar agents gain optional capabilities through native add-ons — signed, per-architecture binaries that operators select in Edge Ops and push down to chosen agents. Add-ons are the native counterpart to Wasm Plugins: use a Wasm plugin for a sandboxed checker, and a native add-on when a capability needs a real OS process (a sidecar daemon, a scheduled scanner, a host-level collector).
This page is the operator/author overview for the add-on framework defined in issue
#3425. The framework spine is in place; some delivery models and UI surfaces are
landing in follow-up changes (see the add-native-addon-* OpenSpec changes).
Why native add-ons
The base serviceradar-agent package stays small and ships only the core agent.
Every optional capability is a separately built, signed add-on that stays dormant
until an operator selects it:
- Selectable. Operators choose which agents (or cohorts) run which add-ons in the Edge Ops UI — no rebuild, no redeploy of the base agent.
- Signed. Add-on artifacts are Cosign-signed (Rekor transparency log) and carry an ed25519 upload-signature; the agent verifies before activation.
- Out-of-process by default. The
agent-sidecarmodel runs add-ons as HashiCorpgo-pluginsubprocesses (gRPC over a Unix-domain socket with AutoMTLS). The base agent never imports an add-on's code — isolation is CI-enforced — so an add-on crash cannot take down the agent. - Polyglot. Add-ons can be written in Go or Rust; both speak the same gRPC contract to the agent's plugin client.
Delivery and supervision models
An add-on declares two axes in its manifest. Together they tell the agent how to obtain and run it:
Delivery — how the artifact reaches the host:
compiled-in— capability already in the base agent; the assignment is just a config toggle (reserved for legacy/coupled capabilities such as remote-access).pushed-artifact— a signed per-arch tarball delivered over the existing runtime-push rail, staged and activated by the agent.os-package— a deb/rpm that depends onserviceradar-agentand is dormant until selected.
Supervision — how the agent runs it:
config-toggle— flip a flag on an in-agent capability.agent-sidecar— supervisedgo-pluginsubprocess (health checks, restart backoff, circuit breaker).systemd-service/systemd-timer— a long-running unit or a scheduled job that spools results for ingest.ephemeral-helper— a short-lived one-shot process.
The reference sample add-on is pushed-artifact / agent-sidecar.
Package format
Each add-on's manifest package lives under addons/<id>/:
addon.yaml— the manifest (identity, delivery/supervision, capabilities,requires,exec,config_schemapointer). Mirrorsplugin.yaml.config.schema.json— JSON Schema (draft 2020-12) for operator config; the control plane validatesAddonAssignment.paramsagainst it before persisting.BUILD.bazel,README.md.
Implementation sources live elsewhere (go/cmd/serviceradar-<id>-addon/ for Go,
rust/ for Rust). See addons/sample-addon/README.md
for the worked example.
Capability and approval model
Like Wasm plugins, add-ons declare capabilities in the manifest, and an operator
approves a package before it can be assigned. On approval the operator may narrow the
granted set via approved_capabilities; the control plane sends that narrowed subset
(not the full manifest list) to the agent. Confirm the capabilities and the
delivery/supervision model during review, especially for add-ons that run as a
privileged sidecar or apply OS capabilities.
SDKs and authoring
The Go SDK (go/pkg/addon) wraps the go-plugin server boilerplate — handshake,
gRPC serving over the UDS, AutoMTLS, health, config decode from the typed assignment,
and result submission. The gRPC contract lives in proto/agent/addon/v1/. A Rust
helper/contract for Rust add-ons (e.g. fingerprintd) is landing in a follow-up
change; until then the documented contract in proto/agent/addon/v1/ is the source
of truth for Rust interop.
Author checklist
Mirror the Wasm plugin author flow:
- Scaffold
addons/<id>/— copyaddons/sample-addon/and editaddon.yaml(id,version,delivery,supervision,language,capabilities,requires,exec) andconfig.schema.json. - Implement the add-on service against the Go SDK (
go/pkg/addon) or the Rust contract, using the gRPC service inproto/agent/addon/v1/. Put sources ingo/cmd/serviceradar-<id>-addon/(or underrust/). - Validate the manifest against the add-on manifest schema and validate
config.schema.jsonis a supported JSON-Schema subset. - Enroll in the build — add an entry to
build/native_addons/addon_inventory.bzlso the release build cross-compiles, bundles, signs, and indexes your add-on per(os, arch)without bespoke release wiring. - Verify locally — build the binary and run the agent's add-on tests
(
go test ./go/pkg/agent/addon/...); confirmaddon.yamlrequiresandapp_protocol_versionmatch the agent's plugin client. - Publish & approve — the signed bundle and discovery index ship with the
release; import/approve the
AddonPackagein Edge Ops, narrowingapproved_capabilitiesas needed. - Assign — create an
AddonAssignmentfor the target agent or cohort with validatedparams; the control plane compiles it into the agent config push and the agent supervises it.
Lifecycle
- Build a signed, per-arch bundle + discovery index (release workflow).
- Import/approve the
AddonPackage(staged → approved) in the admin UI. - Assign to agents/cohort with config validated against
config.schema.json. - The control plane pushes the typed add-on section in the versioned agent config.
- The agent fetches/verifies/activates and supervises the add-on per its model.
- Per-add-on
installed/active/unhealthystatus is reported back and reconciled against the desired assignment in the UI.