Plugin packages
A plugin package is a directory under plugins/. The directory name is the plugin name, and the directory must contain manifest.json. The framework reads the manifest before load and rejects packages that break the contract.
Layout
Section titled “Layout”plugins/ hello-csharp/ manifest.json main.cs hello-csharp.confManifest
Section titled “Manifest”{ "name": "hello-csharp", "entry": "main.cs", "type": "hosihikari", "version": "1.0.0", "dependencies": [ { "name": "hosihikari" }, { "name": "hosihikari-csharp" } ], "extraInfo": { "api_version": "2", "manifest_version": "1", "loader": "csharp" }}| Field | Meaning |
|---|---|
name |
Plugin name; must equal the directory name. |
entry |
Path inside the package; absolute paths and .. are rejected. |
type |
Routing key; always hosihikari. |
version |
The plugin's own SemVer version. |
dependencies |
Plugin names with optional SemVer ranges; the dependency graph decides load order and checks versions. |
extraInfo.api_version |
Contract major version; currently 2. |
extraInfo.manifest_version |
Manifest schema version; currently 1. |
extraInfo.loader |
The loader that handles this package. assembly is the plugin manager's built-in path; csharp and fsharp are in-process loaders; rpc is the out-of-process loader. |
extraInfo.command |
rpc only: a JSON array encoded as a string. |
Dependencies are sorted by name. A dependency may carry a NuGet SemVer range:
- omitted, empty, or
*: any version. 1.2.3: 1.2.3 or newer.[1.2.3]: exactly 1.2.3.[1.0.0,2.0.0): 1.0.0 inclusive through 2.0.0 exclusive.
The target plugin must declare a parseable version when a range constrains it. The framework dependency hosihikari is not version-checked; loader packages load before the packages that depend on them. Plugins that use an in-process loader declare hosihikari-csharp or hosihikari-fsharp; out-of-process plugins declare hosihikari-rpc. Precompiled assemblies use the plugin manager's built-in path and need no loader package.
Loaders and entries
Section titled “Loaders and entries”Official loaders come in two classes: in-process loaders compile source AOT to IL; RpcLoader is the out-of-process loader, an RPC bridge. Precompiled assemblies use the plugin manager's built-in path.
loader |
entry |
How it loads |
|---|---|---|
assembly |
.dll |
PluginManager built-in: loads a precompiled IL assembly directly; the assembly declares exactly one [assembly: EntryPoint<T>]. |
csharp |
.cs |
In-process loader: AOT-compiles .cs to IL at load with Roslyn, into a collectible ALC. |
fsharp |
.fsx |
In-process loader: AOT-compiles .fsx to IL at load with FSharp.Compiler.Service. |
rpc |
Entry file | Out-of-process loader: command starts a child process speaking line-delimited JSON-RPC 2.0 over stdio. |
For in-process and precompiled assembly plugins, the entry type must implement IPlugin and have a parameterless constructor; EntryPoint<T> enforces both at compile time. A missing file or the wrong number of entry declarations is reported by the loader.
Checks before load
Section titled “Checks before load”The framework rejects a package before constructing a plugin if:
namediffers from the directory name;entryis empty, absolute, or escapes the package directory;typeis nothosihikari;api_versionis not2;manifest_versionis not1;- the manifest is not valid JSON.
These checks run before the package enters its lifecycle, and the log gives the reason.
Configuration and data
Section titled “Configuration and data”- Package defaults:
plugins/<name>/<name>.conf, updated with the package. - Server override:
config/hosihikari/<name>.conf, which takes precedence. - Environment variables override the same keys (
greetingmaps toGREETING). - Runtime data:
data/hosihikari/<name>/, created by the framework and handed to the plugin.
The configuration format is HOCON. Layering goes through the framework's provider chain; HOCON include does not apply on this path.
Lifecycle
Section titled “Lifecycle”OnLoad → OnEnable → OnDisable → OnUnload.
OnLoadruns after dependencies are ready; the server itself may not be ready.- An allowed
OnEnableopens the scope gate; a refusal keeps the plugin loaded and disabled. - Before
OnDisable, the framework closes the gate and stops delivering events and timers; a refusal reopens it. - Unload goes through disable;
OnUnloadreleases what the plugin holds. - An exception from a hook is a fault: the host unloads the plugin and notifies the carrier.
The gate covers framework-delivered events and timers. Threads, service registrations, and direct references owned by the plugin must be released in OnUnload.
Loader packages are loaded once and cannot be unloaded at runtime; disable or upgrade them by restarting the server.
