Skip to content

AevonXApp/AXPluginKit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

AXPluginKit — AevonX Plugin Developer Starter Kit

Build powerful server management tools in minutes — no native code required.

AXPluginKit is the official starter kit for building plugins on AevonX, the macOS server management platform. Write a .json file, drop it on your server, and your plugin appears instantly inside AevonX. When you're ready for more, graduate to a native binary plugin with a full configuration UI, lifecycle scripts, and a systemd-managed daemon.


What You Can Build

AevonX plugins can do anything you need inside the app:

Capability Component
Action buttons on website, database, or server cards "button"
Live data tables with auto-refresh, sorting, search, and filters "data_table"
Full sidebar pages with dashboards, charts, and cards "page"
Forms that collect input before running a command "form"
Stats cards that show key metrics at a glance "stats_card"
Modals that pop up with detailed information "modal"
Area, line, bar, pie charts from any data source "chart"
Streaming log viewers with live tail output streaming_cmd

Plugins inject into 40+ hook points across the app — website cards, database cards, server overview, sidebar tabs, global toolbar, and more. You don't write any Swift or Go — the platform handles all rendering, caching, permissions, and security automatically.


Two Ways to Build

JSON-Only Plugin

Drop .json files into /etc/aevonx/your-tool/ on any server. No build step, no binary. Best for UI extensions, dashboards, and action buttons that call built-in actions.

Native Binary Plugin

Publish a Go binary alongside a config.avx schema, setup.sh, uninstall.sh, and a _manifest.json that declares your own custom actions. AevonX installs it as a systemd service, exposes a configuration UI from your schema, and routes action calls to your binary. Best for tools that need their own data, APIs, or background processing.


How It Works

AevonX scans /etc/aevonx/ on every server connection. Each subdirectory is your plugin's namespace:

/etc/aevonx/
  my-tool/                     ← your namespace
    _manifest.json             ← plugin metadata (optional for JSON-only)
    traffic-table.json         ← plugin #1
    backup-button.json         ← plugin #2
    dashboard.json             ← plugin #3

No registration, no API keys, no build step. AevonX discovers and renders your plugins automatically.


Quick Start: Your First Plugin in 5 Minutes

1. SSH into your server

ssh user@your-server

2. Create your namespace

sudo mkdir -p /etc/aevonx/my-first-plugin

3. Create a plugin file

sudo nano /etc/aevonx/my-first-plugin/backup-button.json

Paste this:

{
  "id": "my-first-plugin-backup",
  "name": "Quick Backup",
  "description": "One-click website backup from any website card.",
  "hook": "remote_fleets.websites.card.actions",
  "component": "button",
  "icon": "externaldrive.badge.plus",
  "style": "primary",
  "label": "Backup Now",
  "version": "1.0.0",
  "enabled": true,
  "confirmation_message": "Create a backup of {{website.name}}?",
  "command": {
    "type": "core_cmd",
    "action": "website.backup",
    "payload": { "website_id": "{{website.id}}" },
    "on_success": "Backup started for {{website.name}}",
    "on_error": "Backup failed. Please try again."
  }
}

4. Open AevonX

Connect to the server. Your plugin appears automatically on every website card.

Reload plugins anytime from the Plugins tab — no reconnection needed.


Examples in This Kit

Seven progressively complex examples. Start from #1 and work your way up.

A single action button on every website card. Confirms before triggering a backup.

Teaches: button component · command · template variables · confirmation dialogs · button styles


A sidebar tab showing real-time Nginx access logs as a sortable, searchable table. Auto-refreshes every 30 seconds.

Teaches: data_table · data_source · data formats (csv, nginx) · column types (ip, url, status, bytes, datetime) · refresh_interval


Two plugins working together: a full dashboard page with metric cards, and a services table — both in the sidebar.

Teaches: page component · layout.type: "dashboard" · multiple plugins per namespace · json format with rows_path


04 — Form Action Intermediate

Two card-action plugins: a form that collects input before cloning a website, and a button that force-renews SSL. Both use async_cmd for long-running operations.

Teaches: form component · auto-generated form fields from {{payload_keys}} · async_cmd vs core_cmd · conditions · timeout


A complete "Security Auditor" with four components: sidebar dashboard, failed-logins table, open-ports widget, and a conditional audit button.

Teaches: Multi-component namespaces · overview.widgets hook · servers.card.actions · conditions with operators · streaming_cmd · allowed_actions


A complete native plugin — "ServerPulse" — with a Go binary, config.avx configuration schema, setup.sh, uninstall.sh, Makefile, and three hook definitions.

Teaches: config.avx schema · setup.sh / uninstall.sh pattern · full _manifest.json · allowed_actions · lifecycle · health_check · row_actions · filters · batch_actions


Four plugins demonstrating every advanced table feature: per-row actions, multi-select batch actions, column filters, area chart, and streaming log viewer.

Teaches: row_actions · batch_actions · filters with operators · chart component with chart_config · streaming_cmd · {{selected.field}} batch variables


Plugin File Reference

Anatomy of a Plugin

{
  "id": "my-namespace-plugin-name",
  "name": "Display Name",
  "description": "What it does — shown in the Plugins tab.",
  "hook": "remote_fleets.sidebar.tabs",
  "component": "data_table",
  "icon": "chart.bar",
  "version": "1.0.0",
  "enabled": true
}

Hook Points

Hooks define where your plugin appears.

Sidebar & Global

Hook Location
remote_fleets.sidebar.tabs Left sidebar — new tab
remote_fleets.global.header.actions Top-right global toolbar
remote_fleets.global.toolbar Main toolbar
remote_fleets.global.status_bar Status bar at the bottom

Server Overview

Hook Location
remote_fleets.overview.widgets Overview widget area
remote_fleets.overview.header Overview tab header
remote_fleets.overview.stats Stats row
remote_fleets.overview.charts Charts section
remote_fleets.overview.alerts Alerts section

Websites

Hook Location
remote_fleets.websites.card.actions Action buttons on each website card
remote_fleets.websites.details.header Website detail page header
remote_fleets.websites.details.tabs Tabs inside website detail
remote_fleets.websites.details.sidebar Right sidebar in website detail
remote_fleets.websites.list.toolbar Toolbar above the website list
remote_fleets.websites.nginx.actions Nginx section actions

Databases

Hook Location
remote_fleets.databases.card.actions Action buttons on each database card
remote_fleets.databases.details.actions Database detail actions
remote_fleets.databases.details.tabs Tabs inside database detail
remote_fleets.databases.list.toolbar Toolbar above the database list

Servers

Hook Location
remote_fleets.servers.card.actions Server list card actions

Security & Firewall

Hook Location
remote_fleets.firewall.actions Firewall section actions
remote_fleets.security.firewall.rules Firewall rules view
remote_fleets.security.ssl.actions SSL certificates actions
remote_fleets.security.audit.widgets Security audit widget area

Docker

Hook Location
remote_fleets.docker.container.actions Docker container actions
remote_fleets.docker.toolbar Docker section toolbar
remote_fleets.docker.stats.widgets Docker stats widget area

More

Hook Location
remote_fleets.monitoring.widgets Monitoring tab widgets
remote_fleets.dns.actions DNS management actions
remote_fleets.cron.actions Cron jobs actions
remote_fleets.backups.actions Backups section actions
remote_fleets.users.actions Server users actions
remote_fleets.plugins.marketplace Plugin marketplace view
remote_fleets.plugins.settings Plugin settings view

Component Types

button — Action trigger

{
  "component": "button",
  "label": "Run Action",
  "icon": "play.fill",
  "style": "primary",
  "confirmation_message": "Are you sure?",
  "command": { ... }
}

Styles: primary · secondary · danger · warning · success · ghost


data_table — Rich data table

{
  "component": "data_table",
  "data_source": {
    "action": "monitoring.nginx.access_stats",
    "format": "csv",
    "refresh_interval": 30,
    "rows_path": "data.rows"
  },
  "columns": [
    { "key": "ip", "label": "IP", "type": "ip", "width": 140, "sortable": true }
  ]
}

Data formats:

Format Use when
json Response is a JSON array (or nested via rows_path)
csv First line = headers, rest = rows
tsv Tab-separated, same rules as CSV
lines One value per line → { "value": "..." }
key_value KEY=VALUE or KEY: VALUE pairs
nginx Nginx combined log — auto-parses ip, method, path, status, bytes, date
apache Apache combined log format
raw Raw text, no parsing

Column types:

Type Renders as
text Plain text
number Formatted number: 1,234,567
bytes Auto-scaled: 1.2 MB, 3.4 GB
percent Progress bar + percentage
status Colored dot: active = green, error = red
badge Blue pill badge
url Underlined blue link
ip Monospaced IP address
boolean ✓ or ✗ icon
datetime Formatted date and time

page — Full sidebar page

{
  "component": "page",
  "layout": {
    "type": "dashboard",
    "title": "My Page",
    "columns": 2,
    "cards": [
      {
        "title": "System Metrics",
        "icon": "cpu",
        "command": { "type": "core_cmd", "action": "monitoring.metrics" }
      }
    ]
  }
}

Layout types: dashboard · overview · cards_details · table · grid · split


chart — Data visualization

{
  "component": "chart",
  "chart_type": "area",
  "chart_config": {
    "x_key": "hour",
    "y_key": "requests",
    "color": "#3B82F6",
    "fill_opacity": 0.2,
    "show_grid": true,
    "smooth": true
  },
  "data_source": { ... }
}

Chart types: line · bar · area · pie · donut


form — Input before action

{
  "component": "form",
  "label": "Clone Site",
  "command": {
    "type": "async_cmd",
    "action": "website.clone",
    "payload": {
      "website_id": "{{website.id}}",
      "destination_domain": "{{destination_domain}}"
    }
  }
}

Payload keys in {{double_braces}} that don't match a context variable become form fields automatically.


stats_card · modal

{ "component": "stats_card", "command": { "type": "core_cmd", "action": "monitoring.metrics" } }
{ "component": "modal", "label": "View Details", "command": { "type": "core_cmd", "action": "website.status" } }

Commands

{
  "command": {
    "type": "core_cmd",
    "action": "website.backup",
    "payload": { "website_id": "{{website.id}}" },
    "timeout": 30,
    "retries": 2,
    "on_success": "Backup completed for {{website.name}}",
    "on_error": "Backup failed. Please try again."
  }
}
Type Use when
core_cmd Fast, synchronous operations
async_cmd Long-running background operations (backup, clone, audit)
streaming_cmd Live output — logs, builds, streaming processes

Row Actions & Batch Actions

Row actions appear in a context menu on each table row. Batch actions appear when two or more rows are selected.

{
  "row_actions": [
    {
      "id": "delete-row",
      "label": "Delete",
      "icon": "trash",
      "style": "danger",
      "confirmation_message": "Delete {{name}}?",
      "command": {
        "type": "async_cmd",
        "action": "your-plugin.delete",
        "payload": { "id": "{{id}}" }
      }
    }
  ],
  "batch_actions": [
    {
      "id": "delete-selected",
      "label": "Delete Selected",
      "icon": "trash",
      "style": "danger",
      "confirmation_message": "Delete {{count}} items?",
      "command": {
        "type": "async_cmd",
        "action": "your-plugin.delete_batch",
        "payload": { "ids": "{{selected.id}}" }
      }
    }
  ]
}

{{count}} — number of selected rows {{selected.field}} — array of that field from all selected rows


Filters

Column filters render above the table and narrow results client-side:

{
  "filters": [
    {
      "key": "status",
      "label": "Status",
      "type": "select",
      "options": ["active", "inactive", "error"]
    },
    {
      "key": "size_bytes",
      "label": "Larger than (MB)",
      "type": "number",
      "operator": "gt",
      "transform": "mb_to_bytes"
    },
    {
      "key": "name",
      "label": "Name contains",
      "type": "text",
      "operator": "contains"
    }
  ]
}

Filter types: text · number · select · boolean Operators: eq · neq · contains · gt · lt · exists


Allowed Actions

Actions are whitelisted for security. Only use actions from this list in command.action or data_source.action:

Websites website.backup · website.ssl.renew · website.clone · website.status · website.restart · website.logs · website.access_stats

Databases database.optimize · database.backup · database.size

Monitoring monitoring.metrics · monitoring.processes · monitoring.disk.usage · monitoring.network.stats · monitoring.nginx.access_stats

Security firewall.status · security.fail2ban.status · security.audit · security.scan · security.open_ports · security.failed_logins

System system.info · system.services · system.cron · system.env · system.users

Need an action that isn't listed? Open a discussion. For native plugins, you define your own actions in _manifest.json under allowed_actions.


Template Variables

Hook context Variables
websites.card.actions {{website.id}} · {{website.name}} · {{website.domain}}
databases.card.actions {{database.name}} · {{database.engine}}
servers.card.actions {{server.id}} · {{server.name}} · {{server.host}}
sidebar.tabs {{server.id}} · {{server.name}} · {{server.host}}
overview.widgets {{server.id}} · {{server.name}}
Row actions Any column key from the row: {{ip}}, {{name}}, {{id}}, etc.

Conditional Rendering

Show a plugin only when a condition is true:

{
  "conditions": [
    { "field": "website.status", "operator": "eq", "value": "active" }
  ]
}
Operator Meaning
eq Equals
neq Not equals
contains String contains
gt Greater than
lt Less than
exists Field exists and is not null

Multiple conditions are AND-ed together.


Namespace Manifest

A _manifest.json gives your namespace a name and icon in the Plugins tab.

JSON-only plugin (minimal):

{
  "name": "My Tool",
  "description": "What this tool does",
  "author": "Your Name",
  "version": "1.0.0",
  "icon": "puzzlepiece.fill",
  "website": "https://github.com/your-username/my-tool"
}

Native binary plugin (full):

{
  "name": "My Tool",
  "slug": "my-tool",
  "description": "...",
  "version": "1.0.0",
  "icon": "puzzlepiece.fill",
  "binary": "/usr/local/bin/my-tool exec",
  "arg_style": "positional",
  "service_name": "my-tool",
  "config_dir": "/etc/aevonx/plugins/my-tool",
  "hooks_dir": "/etc/aevonx/hooks/my-tool",
  "allowed_actions": [
    "my-tool.data.list",
    "my-tool.action.run"
  ],
  "health_check": {
    "type": "systemd",
    "service_name": "my-tool",
    "interval": 30,
    "success_pattern": "active"
  },
  "lifecycle": {
    "directories": [
      "/var/lib/my-tool:0755",
      "/var/log/my-tool:0755"
    ],
    "systemd": {
      "description": "My Tool daemon",
      "exec_args": "daemon --config /etc/aevonx/plugins/my-tool/config.avx",
      "user": "nobody",
      "restart_sec": 5,
      "capabilities": []
    }
  }
}

The full template with all fields is at templates/_manifest.full.json.


Configuration Schema (config.avx)

Native plugins expose a configuration UI in AevonX automatically — define the schema in config.avx and AevonX renders it.

{
  "project": "MyTool",
  "description": "...",
  "version": "1.0.0",
  "config_info": {
    "color": "#3B82F6",
    "accent_color": "#60A5FA",
    "website_url": "https://..."
  },
  "config_schema": [
    {
      "section": "General",
      "description": "Core settings.",
      "fields": [
        { "key": "api_key",     "label": "API Key",      "type": "password", "value": "" },
        { "key": "timeout",     "label": "Timeout (s)",  "type": "number",   "value": 30 },
        { "key": "log_level",   "label": "Log Level",    "type": "select",   "value": "info", "options": ["debug","info","warn","error"] },
        { "key": "enabled",     "label": "Enabled",      "type": "boolean",  "value": true }
      ]
    },
    {
      "section": "Actions",
      "fields": [
        {
          "key": "test",
          "label": "Test Connection",
          "type": "action",
          "value": "my-tool test",
          "action_style": "primary",
          "description": "Verify the plugin can reach its endpoint."
        },
        {
          "key": "reset",
          "label": "Reset to Defaults",
          "type": "action",
          "value": "my-tool reset",
          "action_style": "danger",
          "confirm_message": "Reset all settings?"
        }
      ]
    }
  ]
}

Field types:

Type UI
string Single-line text input
text Multi-line text area
password Masked input, stored encrypted
number Numeric input
boolean Toggle switch
select Dropdown picker (use options array)
action Button that runs value as a shell command

The full template is at templates/config.avx.


Plugin Lifecycle Scripts

For native plugins, AevonX calls two scripts that you write:

setup.sh — Post-install

Runs once after AevonX places your binary and creates the systemd service. Use it to:

  • Create runtime directories your binary needs
  • Check and install system dependencies
  • Generate secrets and tokens
  • Find a free port and open it in the firewall
  • Configure log rotation
  • Run any first-boot initialization
#!/usr/bin/env bash
set -euo pipefail

PLUGIN_DIR="/etc/aevonx/plugins/my-tool"
DATA_DIR="/var/lib/my-tool"

mkdir -p "$DATA_DIR"

# Generate a token only on first install
if [ ! -f "$PLUGIN_DIR/.token" ]; then
    openssl rand -hex 32 > "$PLUGIN_DIR/.token"
    chmod 600 "$PLUGIN_DIR/.token"
fi

# Find a free port
PORT=9460
while ss -ltn | grep -q ":$PORT "; do PORT=$((PORT+1)); done
echo "$PORT" > "$PLUGIN_DIR/.port"

# Open in firewall (loopback only)
ufw allow in on lo to any port "$PORT" proto tcp 2>/dev/null || true

uninstall.sh — Pre-removal

Runs before AevonX stops the service and removes the binary. Undo every change setup.sh made, in reverse order.

#!/usr/bin/env bash
set -euo pipefail

PLUGIN_DIR="/etc/aevonx/plugins/my-tool"

# Close firewall port
PORT=$(cat "$PLUGIN_DIR/.port" 2>/dev/null || echo "")
[ -n "$PORT" ] && ufw delete allow in on lo to any port "$PORT" proto tcp 2>/dev/null || true

# Remove logrotate
rm -f /etc/logrotate.d/my-tool

# Remove runtime data
rm -rf /var/lib/my-tool /var/log/my-tool

Rule: AevonX removes: binary, config dir, hooks dir, systemd unit. You remove: everything else your setup.sh created.

The full annotated templates are at templates/setup.sh and templates/uninstall.sh.


Makefile for Native Plugins

BINARY_NAME = my-tool
BUILD_DIR   = dist/build
VERSION     = 1.0.0

all: build-amd64 build-arm64 package

build-amd64:
	mkdir -p $(BUILD_DIR)
	GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
		go build -trimpath -ldflags="-s -w -X main.Version=$(VERSION)" \
		-o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 ./cmd/$(BINARY_NAME)

build-arm64:
	mkdir -p $(BUILD_DIR)
	GOOS=linux GOARCH=arm64 CGO_ENABLED=0 \
		go build -trimpath -ldflags="-s -w -X main.Version=$(VERSION)" \
		-o $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64 ./cmd/$(BINARY_NAME)

package:
	cd dist && zip -r build/$(BINARY_NAME)-$(VERSION).zip \
		config.avx setup.sh uninstall.sh hooks/ build/$(BINARY_NAME)-linux-*

AevonX detects the server's architecture (amd64 or arm64) during install and picks the correct binary automatically.


Deployment

JSON-Only Plugin

sudo mkdir -p /etc/aevonx/my-tool
sudo cp _manifest.json /etc/aevonx/my-tool/
sudo cp *.json /etc/aevonx/my-tool/
# Open AevonX → connect → plugin loads automatically

Native Binary Plugin

Upload via the AevonX Marketplace tab, or install a dev build directly from a ZIP:

dist/
  config.avx          ← configuration schema
  setup.sh            ← post-install script
  uninstall.sh        ← pre-removal script
  hooks/
    _manifest.json    ← full manifest with lifecycle + allowed_actions
    *.json            ← hook definitions
  build/
    my-tool-linux-amd64
    my-tool-linux-arm64
    my-tool-1.0.0.zip ← what you upload

Reload JSON-only plugins anytime from the Plugins tab without reconnecting.


Templates

Template Use for
_manifest.template.json Minimal namespace manifest
_manifest.full.json Full native plugin manifest with all fields
button.template.json Action button
data_table.template.json Basic data table
data_table_advanced.template.json Table with row actions, batch actions, filters
page.template.json Full sidebar page
form.template.json Form with input collection
chart.template.json Chart (area, line, bar, pie, donut)
config.avx Native plugin configuration schema
setup.sh Post-install lifecycle script
uninstall.sh Pre-removal lifecycle script

Remove _comment_* keys before deploying — they are documentation only.


Repository Structure

AXPluginKit/
├── README.md
├── templates/
│   ├── _manifest.template.json         ← minimal manifest
│   ├── _manifest.full.json             ← full native plugin manifest
│   ├── button.template.json
│   ├── data_table.template.json
│   ├── data_table_advanced.template.json  ← row actions, batch, filters
│   ├── page.template.json
│   ├── form.template.json
│   ├── chart.template.json
│   ├── config.avx                      ← configuration schema
│   ├── setup.sh                        ← post-install script
│   └── uninstall.sh                    ← pre-removal script
└── examples/
    ├── 01-hello-button/                ← Beginner: action button
    ├── 02-live-traffic-table/          ← Beginner: live data table
    ├── 03-server-dashboard/            ← Intermediate: dashboard page
    ├── 04-form-action/                 ← Intermediate: forms + async
    ├── 05-advanced-multi-component/    ← Advanced: full plugin suite
    ├── 06-native-plugin/               ← Advanced: binary + full lifecycle
    │   └── dist/
    │       ├── config.avx
    │       ├── setup.sh
    │       ├── uninstall.sh
    │       ├── Makefile
    │       └── hooks/
    │           ├── _manifest.json
    │           ├── overview-widget.json
    │           ├── history-table.json
    │           └── top-processes.json
    └── 07-advanced-table/              ← Advanced: row actions, filters, chart, streaming
        ├── fail2ban-table.json
        ├── traffic-chart.json
        ├── live-log-stream.json
        └── db-size-table.json

Security Model

AevonX handles all security automatically — you focus on building your plugin.

  • Actions are whitelisted. Only actions in allowed_actions (or the platform whitelist) are executed. Everything else is rejected before reaching your binary.
  • Commands run as a limited user. Your binary runs as nobody by default unless you request specific Linux capabilities.
  • Lifecycle scripts run as root during install/uninstall only — your daemon runs as the user you specify.
  • Rate limiting is enforced automatically per plugin per server.
  • Configuration is stored encrypted on the server — passwords and tokens are never stored in plaintext.

Tips

One file, one purpose. Name files by their hook location: sidebar-dashboard.json, website-backup.json, server-audit.json.

Use async_cmd for anything slow. Anything taking more than a second should use async_cmd — AevonX shows a spinner and notifies the user on completion.

Use conditions to reduce noise. A button that appears only when relevant feels native, not bolted on.

Test with refresh_interval: 5. When building a data table, use a short interval to verify your data, then increase it for production.

Mirror setup.sh in uninstall.sh. Every port opened, every directory created, every logrotate file written — undo it in reverse order. A clean uninstall is a mark of quality.

Use descriptive IDs. Format: namespace-component-purpose — e.g., my-tool-sidebar-traffic, my-tool-website-backup.


FAQ

Do I need to write any code? No — for JSON-only plugins. Native plugins require a Go binary, but you only need to write the parts specific to your tool. AevonX handles all rendering, caching, and security.

Can I use my own backend or external API? Native plugins can call any external API from their binary. JSON-only plugins communicate only through the built-in action whitelist.

How do I update a plugin? For JSON-only plugins: edit the file and click reload. For native plugins: upload a new ZIP — AevonX preserves your config.avx settings across updates.

How do I share my plugin? Publish as a GitHub repository tagged aevonx-plugin. Submit to the AevonX Marketplace to reach all users.

Where do I find SF Symbols icon names? Use the SF Symbols app (free, Mac only) to browse and search 6000+ icons.


License

MIT — free to use as the basis for your own plugins.


Built for AevonX — the macOS server management platform.

AXPluginKit

About

AXPluginKit is the official starter kit for building plugins on AevonX

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages