feat: dynamic UI for plugins #858

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

Imported from GitHub.

Original GitHub issue: #2498
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/issues/2498
Original created: 2026-01-25T06:13:39Z


Is your feature request related to a problem?

Initial prompt:

got any ideas around how we could sort of extend the UI for plugins, for example, say I want to create a plugin that can do a simple HTTP GET on a website and return the status kinda thing, but I know my users might have to setup hundreds of these types of checks, or just a few whatever, how can we give plugin authors the ability to ship custom UI essentially, again our web frontend is built on elixir/phoenix liveview, you've seen a lot of the agent code, what would be the best way to go about doing this maybe? maybe i'm just overthinking this too and this is sort of a stupid idea, but i do think we sohuld be shipping some default plugins like something that checks HTTP or some other simple kind of check, that is easy for them to configure without hacking up a bunch of yaml and messing around in the plugin settings page?

Response:

This is actually a **brilliant** idea and it’s exactly how industrial-grade platforms (like Grafana, Datadog, or even VS Code) manage extensibility without the UI team having to hardcode every possible integration.

Since your frontend is **Phoenix LiveView**, you have a secret weapon: **Server-side dynamic form generation.** 

Here is the best way to implement "Plugin UI" without letting plugin authors write dangerous Javascript or messing up your LiveView templates.

---

### 1. The Strategy: JSON Schema-Driven UI
Instead of letting authors write "UI code," they ship a **UI Contract** inside their plugin package. 

Your `plugin.yaml` (from your specs) already mentions an optional `schema.json`. You should formalize this as a **JSON Schema** that defines the plugin's configuration variables.

**Example `schema.json` for an HTTP Checker:**
```json
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "title": "Target URL",
      "format": "uri",
      "default": "https://google.com"
    },
    "method": {
      "type": "string",
      "title": "HTTP Method",
      "enum": ["GET", "POST", "HEAD"],
      "default": "GET"
    },
    "expected_status": {
      "type": "integer",
      "title": "Expected Status Code",
      "minimum": 100,
      "maximum": 599,
      "default": 200
    }
  },
  "required": ["url"]
}

2. The Elixir/LiveView Implementation

On the Phoenix side, you create a "Dynamic Form Component." When a user goes to configure a plugin, the LiveView does the following:

  1. Read the Schema: Fetches the schema.json from the Ash resource.
  2. Generate the Changeset: Use a library like ex_json_schema to validate the user's input against that schema.
  3. Render the Form: Iterate over the schema properties and render the appropriate Phoenix components.

Conceptual LiveView Code:

defmodule ServiceRadarWeb.PluginConfigComponent do
  use ServiceRadarWeb, :live_component

  def render(assigns) do
    ~H"""
    <div class="space-y-4">
      <%= for {key, field_meta} <- @schema["properties"] do %>
        <div class="form-control">
          <label class="label"><%= field_meta["title"] %></label>
          
          <%# Dynamic Input Selection %>
          <%= case field_meta["type"] do %>
            <% "string" -> %>
              <%= if field_meta["enum"] do %>
                <select class="select select-bordered" name={key}>
                  <%= for opt <- field_meta["enum"] do %>
                    <option value={opt} selected={@current_values[key] == opt}><%= opt %></option>
                  <% end %>
                </select>
              <% else %>
                <input type="text" class="input input-bordered" name={key} value={@current_values[key]} />
              <% end %>

            <% "integer" -> %>
              <input type="number" class="input input-bordered" name={key} value={@current_values[key]} />
          <% end %>
        </div>
      <% end %>
    </div>
    """
  end
end

3. The "Standard Library" (Bundled Plugins)

You are right—users shouldn't have to write a plugin for basic HTTP checks.

The Solution: Ship ServiceRadar with a "Standard Library" of Wasm plugins.

  • These are just normal .wasm files.
  • They are baked into your core-elx container or assets.
  • On first boot, core-elx inserts them into the plugins Ash resource automatically.
  • Users see "HTTP Check (Official)" in the UI immediately.

4. Why this is the "Best Way"

  1. Consistency: Every plugin config looks like it belongs in ServiceRadar because you control the CSS/HTML components.
  2. Validation for Free: You can validate the user's config in the UI (via JSON Schema) before it even hits the agent. This prevents "Garbage In, Garbage Out."
  3. Agent Simplicity: The agent just receives the final JSON blob. The plugin calls get_config() and gets a pre-validated map of its parameters.
  4. No Scripting: Users don't have to hack YAML. They get a nice form with dropdowns, toggles, and range sliders based on the schema.json.

5. Moving toward "UI Plugins" (Advanced)

If you really wanted custom UI (like a special graph or a custom map), the safest "LiveView way" is to let authors specify a custom result-mapping template.

  • The plugin returns specific JSON.
  • The UI has a set of "Result Display Widgets" (Gauge, List, Table, Terminal).
  • The plugin.yaml tells the UI: "display_widget": "gauge".

Summary of the Workflow:

  1. Author: Writes Wasm code + plugin.yaml + schema.json.
  2. ServiceRadar Core: Parses schema.json and creates a dynamic Ash form.
  3. Admin: Fills out a nice UI form generated from that schema.
  4. Agent: Pulls the JSON config and runs the Wasm check.
  5. User: Sees the results in a UI component tailored to that plugin's output.

This keeps your "Experience Layer" (Phoenix) clean and your "Execution Layer" (Wasm Agent) secure!



**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: #2498 Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/issues/2498 Original created: 2026-01-25T06:13:39Z --- **Is your feature request related to a problem?** Initial prompt: ``` got any ideas around how we could sort of extend the UI for plugins, for example, say I want to create a plugin that can do a simple HTTP GET on a website and return the status kinda thing, but I know my users might have to setup hundreds of these types of checks, or just a few whatever, how can we give plugin authors the ability to ship custom UI essentially, again our web frontend is built on elixir/phoenix liveview, you've seen a lot of the agent code, what would be the best way to go about doing this maybe? maybe i'm just overthinking this too and this is sort of a stupid idea, but i do think we sohuld be shipping some default plugins like something that checks HTTP or some other simple kind of check, that is easy for them to configure without hacking up a bunch of yaml and messing around in the plugin settings page? ``` Response: ``` This is actually a **brilliant** idea and it’s exactly how industrial-grade platforms (like Grafana, Datadog, or even VS Code) manage extensibility without the UI team having to hardcode every possible integration. Since your frontend is **Phoenix LiveView**, you have a secret weapon: **Server-side dynamic form generation.** Here is the best way to implement "Plugin UI" without letting plugin authors write dangerous Javascript or messing up your LiveView templates. --- ### 1. The Strategy: JSON Schema-Driven UI Instead of letting authors write "UI code," they ship a **UI Contract** inside their plugin package. Your `plugin.yaml` (from your specs) already mentions an optional `schema.json`. You should formalize this as a **JSON Schema** that defines the plugin's configuration variables. **Example `schema.json` for an HTTP Checker:** ```json { "type": "object", "properties": { "url": { "type": "string", "title": "Target URL", "format": "uri", "default": "https://google.com" }, "method": { "type": "string", "title": "HTTP Method", "enum": ["GET", "POST", "HEAD"], "default": "GET" }, "expected_status": { "type": "integer", "title": "Expected Status Code", "minimum": 100, "maximum": 599, "default": 200 } }, "required": ["url"] } ``` ### 2. The Elixir/LiveView Implementation On the Phoenix side, you create a "Dynamic Form Component." When a user goes to configure a plugin, the LiveView does the following: 1. **Read the Schema:** Fetches the `schema.json` from the Ash resource. 2. **Generate the Changeset:** Use a library like `ex_json_schema` to validate the user's input against that schema. 3. **Render the Form:** Iterate over the schema properties and render the appropriate Phoenix components. **Conceptual LiveView Code:** ```elixir defmodule ServiceRadarWeb.PluginConfigComponent do use ServiceRadarWeb, :live_component def render(assigns) do ~H""" <div class="space-y-4"> <%= for {key, field_meta} <- @schema["properties"] do %> <div class="form-control"> <label class="label"><%= field_meta["title"] %></label> <%# Dynamic Input Selection %> <%= case field_meta["type"] do %> <% "string" -> %> <%= if field_meta["enum"] do %> <select class="select select-bordered" name={key}> <%= for opt <- field_meta["enum"] do %> <option value={opt} selected={@current_values[key] == opt}><%= opt %></option> <% end %> </select> <% else %> <input type="text" class="input input-bordered" name={key} value={@current_values[key]} /> <% end %> <% "integer" -> %> <input type="number" class="input input-bordered" name={key} value={@current_values[key]} /> <% end %> </div> <% end %> </div> """ end end ``` ### 3. The "Standard Library" (Bundled Plugins) You are right—users shouldn't have to write a plugin for basic HTTP checks. **The Solution:** Ship ServiceRadar with a **"Standard Library"** of Wasm plugins. * These are just normal `.wasm` files. * They are baked into your `core-elx` container or assets. * On first boot, `core-elx` inserts them into the `plugins` Ash resource automatically. * Users see "HTTP Check (Official)" in the UI immediately. ### 4. Why this is the "Best Way" 1. **Consistency:** Every plugin config looks like it belongs in ServiceRadar because *you* control the CSS/HTML components. 2. **Validation for Free:** You can validate the user's config in the UI (via JSON Schema) *before* it even hits the agent. This prevents "Garbage In, Garbage Out." 3. **Agent Simplicity:** The agent just receives the final JSON blob. The plugin calls `get_config()` and gets a pre-validated map of its parameters. 4. **No Scripting:** Users don't have to hack YAML. They get a nice form with dropdowns, toggles, and range sliders based on the `schema.json`. ### 5. Moving toward "UI Plugins" (Advanced) If you *really* wanted custom UI (like a special graph or a custom map), the safest "LiveView way" is to let authors specify a **custom result-mapping template**. * The plugin returns specific JSON. * The UI has a set of "Result Display Widgets" (Gauge, List, Table, Terminal). * The `plugin.yaml` tells the UI: `"display_widget": "gauge"`. ### Summary of the Workflow: 1. **Author:** Writes Wasm code + `plugin.yaml` + `schema.json`. 2. **ServiceRadar Core:** Parses `schema.json` and creates a dynamic Ash form. 3. **Admin:** Fills out a nice UI form generated from that schema. 4. **Agent:** Pulls the JSON config and runs the Wasm check. 5. **User:** Sees the results in a UI component tailored to that plugin's output. This keeps your "Experience Layer" (Phoenix) clean and your "Execution Layer" (Wasm Agent) secure! ``` **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.
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#858
No description provided.