← Docs

Writing Plugins

Goose Panel plugins are Elixir modules that hook into panel lifecycle events. Write them in any language that compiles to BEAM bytecode (Elixir, Erlang, Gleam) or as standalone services that the panel calls via HTTP.

Plugin Structure

A plugin is a single .ex file placed in lib/goose_panel/plugins/ or installed from the Marketplace. It implements the GoosePanel.Plugin behaviour with three callbacks:

defmodule MyApp.Plugins.CustomAuditLogger do
@behaviour GoosePanel.Plugin

@impl true
def name, do: "Custom Audit Logger"

@impl true
def version, do: "1.0.0"

@impl true
def hooks do
[
  # Called every time an audit entry is written
  {"panel:audit", &handle_audit/1},
  # Called when a run starts or finishes
  {"panel:runs", &handle_run/1},
  # Called when an alert triggers
  {"panel:alerts", &handle_alert/1},
  # Called when insights are generated
  {"panel:insights", &handle_insight/1}
]
end

defp handle_audit(event) do
# event is a map with :type, :user_id, :description, :timestamp
IO.puts("Audit: #{event.description}")
:ok
end

defp handle_run(event), do: :ok
defp handle_alert(event), do: :ok
defp handle_insight(event), do: :ok
end

Available Hooks

panel:audit Fires on every auditable action. Event map has :type, :user_id, :description, :timestamp, :subject_type, :subject_id.
panel:runs Fires on run start, completion, and failure. Event has :run_id, :status, :user_id, :prompt.
panel:alerts Fires when a configured alert threshold is crossed. Event has :alert_id, :severity, :message.
panel:insights Fires after nightly insight generation. Event has :insight_id, :type, :summary, :user_id.

Writing in Other Languages

The panel's plugin system runs on the BEAM VM, which supports multiple languages that compile to BEAM bytecode. You can write plugins in:

Elixir

Native. Full access to all panel internals. Write .ex files implementing the Plugin behaviour as shown above. This is the recommended approach.
Elixir modules guide →

Erlang

Place .erl files in lib/goose_panel/plugins/. Erlang modules are automatically available to Elixir via atom names.
Erlang modules →

Gleam

A typed language that compiles to Erlang. Write plugins in Gleam's friendly syntax and import them as Erlang modules. Gleam's type system catches errors at compile time.
gleam.run →

External Services

Any language (Python, JavaScript, Go, Rust) can integrate via the MCP (Model Context Protocol) agent system at /mcp. Register an external HTTP/WebSocket endpoint and the panel forwards agent requests to it.
modelcontextprotocol.io →

Installing Plugins

Drop your plugin file into lib/goose_panel/plugins/ and restart the panel. Plugins are discovered automatically at boot.

For one-click install, publish to the Marketplace. Other panel users can install your plugin without touching the filesystem.

Example: Discord Notifier

A plugin that sends audit events to a Discord channel via webhook:

defmodule MyApp.Plugins.DiscordNotifier do
@behaviour GoosePanel.Plugin
@webhook "https://discord.com/api/webhooks/..."

def name, do: "Discord Notifier"
def version, do: "1.0.0"

def hooks do
[{"panel:audit", ¬ify/1}]
end

defp notify(event) do
body = %{
  content: "**#{event.description}**\nUser ##{event.user_id} at #{event.timestamp}"
}

Req.post!(@webhook, json: body)
:ok
end
end

Plugins run in the same process as the panel. Keep hook handlers fast — offload heavy work to a background process or queue.