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.