View .md

FeatherQuilld plugins

Extend FeatherQuilld with .NET plugins: plugin.yml, capabilities, routes, settings and the 162 events you can hook, generated from the Quilld SDK.

Last synced from FeatherQuilld 72bc34d on .

On this page

FeatherQuilld can load plugins written in .NET. A plugin can add HTTP routes to the daemon, run code before or after its own requests, and hook events such as a WebSpace starting or a backup being created. These are daemon plugins and are separate from FeatherPanel plugins.

Before you enable plugins

Plugins run inside the daemon, which runs as root. Turning them on is the same as giving every plugin author root on the node. That is why they are off by default. Only install plugins you have read or built yourself.

Enabling and installing

Set plugins.enabled: true, either in /etc/featherquilld/config.yml or in the web node's Advanced Config Overrides ({"plugins":{"enabled":true}}). The panel's Plugins Directory field setsplugins.directory. Restart the service after adding or changing plugins.

plugins:
  enabled: true
  directory: /var/lib/featherquilld/plugins
  strict: false
  disabled: []
  settings:
    hello:
      greeting: Hi there
  • Each plugin goes in its own folder under the plugins directory, with its DLL and an optional plugin.yml.
  • disabled lists plugin IDs to skip.
  • settings.<id> overrides the settings in the plugin's manifest.
  • With strict: true a plugin that fails to load, verify or configure stops the daemon. Otherwise it is skipped with a warning.

A plugin is rejected when it has no ID, name or version, when its ID is already loaded, or whenMinHostVersion is newer than the running daemon. GET /api/system/plugins lists the loaded plugins and DELETE /api/system/plugins/<id> unloads one until the next restart.

Plugin structure

A plugin is a class library that references FeatherQuilld.Plugins and has one class implementingIPlugin. Configure(PluginContext) is called at startup. ImplementIPluginLifecycle as well to get StartAsync and StopAsync. The context gives youRoutes, Events, Logger, Settings, Services for dependency injection and Host for daemon information. This is the sample plugin from the repository, shortened:

public sealed class HelloPlugin : IPlugin, IPluginLifecycle
{
    public PluginMetadata Metadata { get; } = new()
    {
        Id = "hello", Name = "Hello Plugin", Version = "0.3.0",
        MinHostVersion = "0.1.0",
        Capabilities = [PluginCapabilities.RoutesPublic, PluginCapabilities.WebSpacesRead],
    };

    public void Configure(PluginContext context)
    {
        context.Routes
            .MapGet("/api/hello", (HttpContext _) => Results.Json(new
            {
                webspaces = context.Host?.WebSpaces.List().Count,
            }))
            .WithName("hello-greeting");

        context.Events.On<WebSpacePowerBeforeEvent>(evt =>
        {
            context.Logger.LogInformation("Power {Action} for {Uuid}", evt.Action, evt.WebSpaceUuid);
            return HookResult.Continue();
        });
    }

    public Task StartAsync(CancellationToken ct = default) => Task.CompletedTask;
    public Task StopAsync(CancellationToken ct = default) => Task.CompletedTask;
}

Its plugin.yml. Values here override the metadata in code; main names the DLL when the folder has more than one.

id: hello
name: Hello Plugin
version: 0.3.0
description: Sample plugin demonstrating full SDK surfaces.
author: FeatherQuilld
main: FeatherQuilld.Plugins.Hello.dll
enabled: true
capabilities:
  - routes.public
  - routes.system
  - host.info
  - webspaces.read
settings:
  greeting: Hello from plugin!

Capabilities

A plugin declares what it needs. The daemon checks these today:

CapabilityAllows
routes.publicMap routes outside /api/system
routes.systemMap routes under /api/system
host.infoRead the daemon version, UUID and uptime
webspaces.readList WebSpaces and read their name, runtime and status

The SDK also defines these, which no host check uses yet:webspaces.write, dns.write, mail.write, ftp.admin, files.read.

Routes

context.Routes has MapGet, MapPost, MapPut, MapDeleteand MapPatch. Every plugin route needs the node token, like the rest of the Quilld API. A route under/api/system needs routes.system, any other route needs routes.public; without it the plugin is not configured.

Before(pattern, hook) and After(pattern, hook) run around matching requests, including the daemon's own routes. A before hook that returns HookResult.Cancel() stops the request.

Events

Subscribe with context.Events.On<TEvent>(handler) and return a HookResult. Most actions come as a pair: a …BeforeEvent and an …AfterEvent. From a before event,HookResult.Cancel() aborts the action (the API answers 403) and HookResult.Replace(value)skips it and uses your value as the result. After events can only observe. The list below is generated from the SDK:162 events in 17 groups.

Access

AccessDeauthorizeBeforeEvent, AccessDeauthorizeAfterEvent, AccessSetPermissionsBeforeEvent, AccessSetPermissionsAfterEvent

Auth

AuthValidateBeforeEvent, AuthValidateAfterEvent

Backup

BackupCreateBeforeEvent, BackupCreateAfterEvent, BackupRestoreBeforeEvent, BackupRestoreAfterEvent, BackupDeleteBeforeEvent, BackupDeleteAfterEvent, BackupImportBeforeEvent, BackupImportAfterEvent, BackupReconcileBeforeEvent, BackupReconcileAfterEvent

BuiltIn

PluginConfiguredEvent, ApplicationStartedEvent, ApplicationStoppingEvent, HttpRequestEvent, RouteExecutingEvent, RouteExecutedEvent, HealthCheckEvent

Dns

DnsZoneCreateBeforeEvent, DnsZoneCreateAfterEvent, DnsRecordCreateBeforeEvent, DnsRecordCreateAfterEvent, DnsRecordUpdateBeforeEvent, DnsRecordUpdateAfterEvent, DnsRecordDeleteBeforeEvent, DnsRecordDeleteAfterEvent, DnsUpsertABeforeEvent, DnsUpsertAAfterEvent, DnsTxtCreateBeforeEvent, DnsTxtCreateAfterEvent, DnsTxtDeleteBeforeEvent, DnsTxtDeleteAfterEvent

File

FileListBeforeEvent, FileListAfterEvent, FileReadBeforeEvent, FileReadAfterEvent, FileWriteBeforeEvent, FileWriteAfterEvent, FileCreateDirectoryBeforeEvent, FileCreateDirectoryAfterEvent, FileRenameBeforeEvent, FileRenameAfterEvent, FileCopyBeforeEvent, FileCopyAfterEvent, FileDeleteBeforeEvent, FileDeleteAfterEvent, FileUploadBeforeEvent, FileUploadAfterEvent, FileCompressBeforeEvent, FileCompressAfterEvent, FileDecompressBeforeEvent, FileDecompressAfterEvent, FileChmodBeforeEvent, FileChmodAfterEvent, FilePullBeforeEvent, FilePullAfterEvent, FileSymlinkBeforeEvent, FileSymlinkAfterEvent, FileDownloadArchiveBeforeEvent, FileDownloadArchiveAfterEvent

Ftp

FtpAuthBeforeEvent, FtpAuthAfterEvent, FtpWriteBeforeEvent, FtpWriteAfterEvent, FtpDeleteBeforeEvent, FtpDeleteAfterEvent, FtpRenameBeforeEvent, FtpRenameAfterEvent, FtpMkdirBeforeEvent, FtpMkdirAfterEvent

Mail

MailDomainAddBeforeEvent, MailDomainAddAfterEvent, MailDomainRemoveBeforeEvent, MailDomainRemoveAfterEvent, MailProvisionBeforeEvent, MailProvisionAfterEvent, MailSpamFilterBeforeEvent, MailSpamFilterAfterEvent

Malware

MalwareScanBeforeEvent, MalwareScanAfterEvent

Package

PackageInstallBeforeEvent, PackageInstallAfterEvent, PackageRemoveBeforeEvent, PackageRemoveAfterEvent

Panel

PanelSyncBeforeEvent, PanelSyncAfterEvent

Proxy

ProxyRebuildBeforeEvent, ProxyRebuildAfterEvent, AcmeEnsureCertsBeforeEvent, AcmeEnsureCertsAfterEvent, AcmeIssueBeforeEvent, AcmeIssueAfterEvent, StaticFileSyncBeforeEvent, StaticFileSyncAfterEvent

Schedule

ScheduleExecuteBeforeEvent, ScheduleExecuteAfterEvent, ScheduleTaskExecuteBeforeEvent, ScheduleTaskExecuteAfterEvent

Sftp

SftpAuthBeforeEvent, SftpAuthAfterEvent, SftpSessionOpenBeforeEvent, SftpSessionOpenAfterEvent, SftpSessionCloseAfterEvent, SftpWriteBeforeEvent, SftpWriteAfterEvent, SftpMkdirBeforeEvent, SftpMkdirAfterEvent, SftpRmdirBeforeEvent, SftpRmdirAfterEvent, SftpRemoveBeforeEvent, SftpRemoveAfterEvent, SftpRenameBeforeEvent, SftpRenameAfterEvent, SftpSetstatBeforeEvent, SftpSetstatAfterEvent, SftpOpenBeforeEvent, SftpOpenAfterEvent, SftpReadBeforeEvent, SftpReadAfterEvent, SftpReaddirBeforeEvent, SftpReaddirAfterEvent, SftpStatBeforeEvent, SftpStatAfterEvent

Transfer

TransferOutgoingBeforeEvent, TransferOutgoingAfterEvent, TransferIncomingBeforeEvent, TransferIncomingAfterEvent

Trash

TrashRestoreBeforeEvent, TrashRestoreAfterEvent, TrashPurgeBeforeEvent, TrashPurgeAfterEvent

WebSpace

WebSpaceCreateBeforeEvent, WebSpaceCreateAfterEvent, WebSpaceSyncBeforeEvent, WebSpaceSyncAfterEvent, WebSpaceDeleteBeforeEvent, WebSpaceDeleteAfterEvent, WebSpaceReinstallBeforeEvent, WebSpaceReinstallAfterEvent, WebSpaceSslRenewBeforeEvent, WebSpaceSslRenewAfterEvent, WebSpacePowerBeforeEvent, WebSpacePowerAfterEvent, WebSpaceConsoleCommandBeforeEvent, WebSpaceConsoleCommandAfterEvent, WebSpaceExecBeforeEvent, WebSpaceExecAfterEvent, WebSpaceRedisBeforeEvent, WebSpaceRedisAfterEvent, WebSpaceAbortInstallBeforeEvent, WebSpaceAbortInstallAfterEvent, WebSpaceRecreateRuntimeBeforeEvent, WebSpaceRecreateRuntimeAfterEvent, WebSpaceSslUploadBeforeEvent, WebSpaceSslUploadAfterEvent, WebSpaceSslDeleteBeforeEvent, WebSpaceSslDeleteAfterEvent