HF PanelDocsv1.0.0

Plugins

Write an HF Panel plugin — plugin.json, hook scripts and pages in the panel — with a complete example, and install it.

A plugin adds features to HF Panel without changing the panel itself. It can run hook scripts when events happen (as root), and add pages to the menu of administrators, resellers or customers. A plugin is a folder with a plugin.json, packed as a .zip.

Where: Server Admin › System › Plugins & hooks, or hf-panel plugin on the server

Warning: a plugin's hooks run as root, and its pages open inside the panel with the signed-in person's session. Only install plugins from people you trust. HF Panel does not check signatures.

What is in a plugin

text
plugin-hello/
├── plugin.json                 the plugin's name, version, hooks, menu entries and pages
├── hooks/
│   └── account-created.sh      a hook script (runs as root for account.created)
└── page/
    └── index.html              a page shown in the panel

When it is installed, the folder is /usr/local/hostfog/plugins/<id>/, owned by root.

plugin.json

json
{
  "id": "hello",
  "name": "Hello",
  "version": "1.0.0",
  "description": "An example plugin: writes a line for every new hosting account and adds a Hello page to Server Admin.",
  "author": "Example Ltd",
  "homepage": "https://example.com/hf-plugins/hello",
  "min_panel": "1.0.0",
  "hooks": {
    "account.created": "hooks/account-created.sh"
  },
  "menu": [
    { "role": "admin", "group": "System", "label": "Hello", "path": "index.html" }
  ],
  "pages": "page"
}
Field Required What it is
id yes 2–40 lowercase letters, digits or -. The folder's name once installed, and the address of its pages.
name yes the name shown on the screen, up to 60 characters
version yes for example 1.0.0 (letters, digits, ., +, -, up to 30 characters)
description no one sentence, up to 300 characters
author no up to 100 characters
homepage no an https:// address
min_panel no the oldest HF Panel version the plugin works with; a plugin for a newer panel can't be enabled
hooks no an object: event name → the path of a script inside the plugin (see Event hooks for the events)
menu no up to 10 entries: role (admin, reseller or client), label (up to 40 characters), group (the menu heading to put it under) and path (a page inside the pages folder, default index.html)
pages with menu the folder of the plugin's pages

Every path is relative to the plugin's folder: letters, digits, ., _ and - in each part, never .., never starting with /, . or -. Any other field in plugin.json makes the plugin invalid.

Hook scripts

A plugin's hook runs exactly like a script hook: as root, with the event's JSON payload on standard input (and in $HF_PAYLOAD_FILE), the event's name in $HF_EVENT, for 30 seconds at most, with its output in /var/log/hostfog-hooks.log. It runs after the files in /etc/hostfog/hooks/<event>.d/, and only while the plugin is enabled. The script must be a file in the plugin (not a symbolic link); HF Panel makes it executable when it installs the plugin.

hooks/account-created.sh:

sh
#!/bin/sh
# Runs as root when a hosting account is created (event account.created).
set -eu
payload="$(cat)"
account="$(printf '%s' "$payload" | python3 -I -c 'import json, sys; print(json.load(sys.stdin).get("account", ""))')"
case "$account" in
  "" | *[!a-z0-9]*) echo "no valid account in the payload"; exit 1 ;;
esac
echo "$(date -u +%FT%TZ) new hosting account: $account" >>/var/log/hostfog-hello.log
echo "hello: noted $account"

Tip: check every value you take from the payload before you use it, as this script does with the username.

Pages

Each menu entry adds a link to the panel menu of its role, under the heading named by group (a new heading at the end of the menu when there is none of that name, Plugins when group is empty). The link opens the page in the panel's content area, in a frame of /plugins/<id>/<path>.

  • The pages are served by HF Panel from the plugin's pages folder, at /plugins/<id>/…, on the panel's own address.
  • Only signed-in people whose role has a menu entry in the plugin can open them. Team members of a hosting account never can.
  • Use plain HTML, CSS and JavaScript files in the pages folder. Scripts and styles may be inline or files of the plugin; nothing can be loaded from other sites.
  • The frame is sandboxed with allow-scripts allow-same-origin allow-forms.

page/index.html:

html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello</title>
<style>
  :root { color-scheme: light dark; font-family: system-ui, sans-serif; }
  body { margin: 0; padding: 20px; line-height: 1.5; }
  .box { border: 1px solid #8884; border-radius: 10px; padding: 16px 20px; max-width: 640px; }
  code { background: #8882; padding: 1px 5px; border-radius: 4px; }
</style>
</head>
<body>
  <div class="box">
    <h2>Hello from a plugin</h2>
    <p>This page is <code>page/index.html</code> of the <b>hello</b> plugin. HF Panel serves it at
      <code>/plugins/hello/index.html</code>, only to signed-in administrators (the role of its menu entry).</p>
    <p>Each new hosting account is written to <code>/var/log/hostfog-hello.log</code> by its hook,
      <code>hooks/account-created.sh</code>.</p>
    <p>Opened at <span id="now"></span>.</p>
  </div>
  <script>
    document.getElementById('now').textContent = new Date().toLocaleString();
  </script>
</body>
</html>

The whole example is in the HF Panel source, in tools/examples/plugin-hello/.

Pack and install a plugin

Zip the folder. plugin.json may be at the top of the zip, or in one folder that holds everything:

bash
cd tools/examples
zip -r plugin-hello.zip plugin-hello

Then install it, either on the Plugins tab of Plugins & hooks (choose the file and click Install plugin), or on the server as root:

bash
sudo hf-panel plugin install plugin-hello.zip     # or an https:// address
sudo hf-panel plugin enable hello

A new plugin stays switched off until you enable it. Installing a plugin with the same id again replaces it (an update) and keeps it enabled when it was.

Command What it does
hf-panel plugin list the installed plugins, their versions and whether they are on
hf-panel plugin install <file.zip|https://…> [--yes] install a plugin (it asks first unless you add --yes)
hf-panel plugin enable <id> switch a plugin on: its menu entries and hooks start working
hf-panel plugin disable <id> switch a plugin off
hf-panel plugin remove <id> delete the plugin's folder

What HF Panel checks

Before anything is written, the .zip and plugin.json are checked. A plugin is refused when:

  • a path in the zip leaves the plugin's folder (.., a path starting with /, a backslash), is hidden (starts with .), or is a symbolic link or special file;
  • it has more than 2000 files, or more than 50 MB unpacked;
  • plugin.json is missing, not valid JSON, has an unknown field, or breaks a rule in the table above;
  • a hook script or the pages folder named in plugin.json is not in the zip.

The files are then written as root, owned by root and not writable by anyone else. The plugin's menu entries, hooks and pages are only used while every path still points at a real file or folder inside the plugin; a symbolic link anywhere stops it.