Overview

How FeatherFly loads and runs plugins.

FeatherFly plugins are native shared libraries (.so files) loaded at daemon startup. They extend the daemon without recompiling FeatherFly itself.

A plugin exports one symbol: featherfly_plugin_entry. The daemon loads each .so, checks the plugin API version, calls init, and keeps the library in memory until shutdown.

Plugins register hooks during init. Hook systems:

  1. Lifecycle events — callbacks fired at fixed points (config loaded, daemon started, etc.).
  2. Config mutation — rewrite raw YAML before settings are applied.
  3. Request hooks — run before HTTP handlers (request.intercept, middleware.inject).
  4. Plugin routes — register new HTTP endpoints on the daemon router.
  5. JSON mutation hooks — callbacks that receive JSON, may rewrite it, and return modified output for HTTP responses.
  6. CloudPanel command hooks — rewrite or cancel CloudPanel CLI operations before clpctl runs.

All hooks for a given target run in plugin load order (alphabetical by filename in the plugins directory).

Startup sequence

  1. Daemon reads config.yml (raw bytes)
  2. Each .so in the plugins directory is loaded
  3. For each plugin: init(host) runs — register hooks here (including config.mutate)
  4. After each plugin init: plugin.loaded event fires (payload = plugin name)
  5. Registered config.mutate hooks rewrite the YAML pipeline
  6. Final config is parsed and applied (directories, logging, pid file)
  7. config.loaded fires with the post-mutation YAML bytes
  8. daemon.starting fires before HTTP routes are wired
  9. HTTP server starts (core routes + plugin routes; request middleware stack active)
  10. daemon.started fires (payload = listen address, e.g. 127.0.0.1:9090)
  11. On shutdown: daemon.stopping fires, then each plugin's shutdown runs

Logging

Plugin discovery, per-library load, route registration, and HostApi::log_info output use the plugin tracing target at DEBUG. At INFO you get one consolidated FeatherFly ready line with plugin/hook/route counts. Set logging.level: debug (or run with --debug) to see loader detail in latest.log.

Remote management

FeatherPanel can read/edit config.yml, restart the daemon, and trigger plugin reloads over the HTTP API. See remote config, remote restart, and plugin reload. Toggle capabilities with remote.config_edit and remote.restart in config.

Hook systems

Lifecycle
hook!events
Config
hook_config!config.mutate
Request
hook_request!intercept / middleware
Routes
route!route.register
JSON
hook_json!response / actions

All hooks for a target run in plugin load order (alphabetical by .so filename). Register everything inside init.