modularize docs
This commit is contained in:
+36
-1535
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
|||||||
|
# Plugin Docs
|
||||||
|
|
||||||
|
These docs are split from `plugins/PLUGINS.md` so each plugin area has a smaller reference.
|
||||||
|
|
||||||
|
| File | Covers |
|
||||||
|
|------|--------|
|
||||||
|
| [bundle-format.md](bundle-format.md) | Bundle layout, manifest fields, nav entries, TypeScript entrypoints. |
|
||||||
|
| [server-side-plugins.md](server-side-plugins.md) | Server-only plugin runtime, RPC/SSE, persistent data, build hooks, build UI settings/actions. |
|
||||||
|
| [wasm-plugins.md](wasm-plugins.md) | Plugin 2.0 WASM runtime and agent-side host bridges. |
|
||||||
|
| [install-and-manage.md](install-and-manage.md) | Uploading, opening, enabling, auto-load, Plugin Manager behavior. |
|
||||||
|
| [samples.md](samples.md) | Included sample plugins and what each demonstrates. |
|
||||||
|
| [legacy-native-plugins.md](legacy-native-plugins.md) | Legacy native plugins, direct OS/API use, native ABI, build scripts, static Linux shim. |
|
||||||
|
| [signing.md](signing.md) | Ed25519 plugin signing and trusted key management. |
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# Bundle Format
|
||||||
|
|
||||||
|
Plugin bundles are zip files named after the plugin ID. `config.json` must be at the zip root.
|
||||||
|
|
||||||
|
## Plugin 2.0 Layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
<pluginId>.zip
|
||||||
|
config.json
|
||||||
|
<pluginId>.html
|
||||||
|
<pluginId>.css
|
||||||
|
<pluginId>.js optional when src/ui.ts exists
|
||||||
|
<pluginId>.wasm optional agent-side WASM module
|
||||||
|
server.js optional server runtime
|
||||||
|
src/
|
||||||
|
ui.ts compiled to assets/<pluginId>.js
|
||||||
|
server.ts compiled to server.js
|
||||||
|
shared.ts optional local module
|
||||||
|
```
|
||||||
|
|
||||||
|
After extraction the server stores assets under `Overlord-Server/plugins/<pluginId>/assets/`, generated metadata in `manifest.json`, optional source in `src/`, and persistent plugin files in `data/`.
|
||||||
|
|
||||||
|
## Runtime Selection
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"apiVersion": 2,
|
||||||
|
"runtime": "wasm",
|
||||||
|
"wasm": "sample-wasm.wasm"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
| Runtime | Use case |
|
||||||
|
|---------|----------|
|
||||||
|
| `server` | Server-only extension, build plugin, dashboard plugin, RPC-backed plugin. |
|
||||||
|
| `wasm` | Sandboxed agent-side Plugin 2.0 module. |
|
||||||
|
| `native` or omitted v1 fields | Legacy shared-library agent plugin. |
|
||||||
|
|
||||||
|
Server-only plugins are not sent to clients and do not use auto-load.
|
||||||
|
|
||||||
|
## Manifest Fields
|
||||||
|
|
||||||
|
`config.json` is merged into the generated `manifest.json`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "sample",
|
||||||
|
"name": "Sample Plugin",
|
||||||
|
"apiVersion": 2,
|
||||||
|
"runtime": "server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "An example plugin",
|
||||||
|
"entry": "sample.html",
|
||||||
|
"assets": {
|
||||||
|
"html": "sample.html",
|
||||||
|
"css": "sample.css",
|
||||||
|
"js": "sample.js"
|
||||||
|
},
|
||||||
|
"navbar": {
|
||||||
|
"label": "Sample",
|
||||||
|
"icon": "fa-cube"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`navbar.icon` accepts a Font Awesome 6 solid icon class such as `fa-cube`, `fa-key`, or `fa-network-wired`. If the plugin is enabled, navbar plugins appear in the Plugin Apps group and open at `/plugins/<id>`.
|
||||||
|
|
||||||
|
## TypeScript UI And Server Logic
|
||||||
|
|
||||||
|
Plugin bundles can ship source-oriented browser and server logic. The server compiles these during extraction:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"apiVersion": 2,
|
||||||
|
"uiEntry": "src/browser/main.ts",
|
||||||
|
"serverEntry": "src/backend/index.ts"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Defaults:
|
||||||
|
|
||||||
|
| Source file | Output |
|
||||||
|
|-------------|--------|
|
||||||
|
| `src/ui.ts` | `assets/<pluginId>.js` |
|
||||||
|
| `src/server.ts` | `server.js` |
|
||||||
|
|
||||||
|
Only files under `src/` are copied for compilation. Extraction does not run `npm install`, download packages, or execute build scripts. Use relative imports and APIs already available in the browser or server plugin runtime.
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# Install And Manage Plugins
|
||||||
|
|
||||||
|
## Install Or Upload
|
||||||
|
|
||||||
|
Install a plugin by uploading its zip from the Plugins page or placing the zip under `Overlord-Server/plugins`. The server extracts the bundle, generates `manifest.json`, and copies browser assets into the plugin assets directory.
|
||||||
|
|
||||||
|
`config.json` must be in the root of the zip. It is merged into the generated manifest on extraction.
|
||||||
|
|
||||||
|
## Open Plugin UI
|
||||||
|
|
||||||
|
Plugin pages are served at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/plugins/<pluginId>
|
||||||
|
```
|
||||||
|
|
||||||
|
Per-client plugins can also receive a client context through:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/plugins/<pluginId>?clientId=<clientId>
|
||||||
|
```
|
||||||
|
|
||||||
|
Navbar plugins appear in the Plugin Apps group when enabled and configured with:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"navbar": {
|
||||||
|
"label": "My Plugin",
|
||||||
|
"icon": "fa-cube"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugin Manager Behavior
|
||||||
|
|
||||||
|
| Plugin kind | Manager behavior |
|
||||||
|
|-------------|------------------|
|
||||||
|
| Server-only (`runtime: "server"`) | Shown as a server extension. Auto-load controls are hidden. It is not sent to clients. |
|
||||||
|
| WASM (`runtime: "wasm"`) | Shows declared needs and approval state. Can be loaded on compatible clients after needs approval. |
|
||||||
|
| Legacy native | Shows native binary/runtime compatibility. Requires matching OS/architecture binary. |
|
||||||
|
|
||||||
|
## Auto-Load
|
||||||
|
|
||||||
|
Auto-load applies to client-side plugins only. It sends enabled client plugins to newly connected clients and can optionally dispatch startup events.
|
||||||
|
|
||||||
|
Server-only plugins do not use auto-load because their code runs on the server rather than inside an agent.
|
||||||
|
|
||||||
|
Auto-load notes:
|
||||||
|
|
||||||
|
| Rule | Detail |
|
||||||
|
|------|--------|
|
||||||
|
| Enabled flag | Disabled plugins are never auto-loaded. |
|
||||||
|
| WASM needs | WASM plugins with unapproved needs are blocked. |
|
||||||
|
| Persistence | Auto-load state is saved in `.plugin-state.json`. |
|
||||||
|
| Platform matching | WASM is universal; legacy native requires a matching binary. |
|
||||||
|
|
||||||
|
## Useful APIs
|
||||||
|
|
||||||
|
| Endpoint | Purpose |
|
||||||
|
|----------|---------|
|
||||||
|
| `GET /api/plugins` | List installed plugins, runtime, needs, enabled state, server state, and auto-load state. |
|
||||||
|
| `POST /api/plugins/<id>/autoload` | Configure auto-load for client-side plugins. Server-only plugins reject this. |
|
||||||
|
| `POST /api/plugins/<id>/rpc` | Call a server-side plugin RPC method. |
|
||||||
|
| `GET /api/plugins/<id>/stream` | Subscribe to server-side plugin broadcasts. |
|
||||||
|
| `GET /api/build/plugins` | List enabled build plugins and their Build page settings/actions. |
|
||||||
|
|
||||||
|
## Per-User Plugin Access
|
||||||
|
|
||||||
|
Admins can limit which plugins a user can use from the Users page through Plugin Access. The policy supports no access, selected plugins, or all plugins.
|
||||||
|
|
||||||
|
That same policy applies to normal plugin pages, server-side plugin RPC/SSE, client-side plugin loading, and build-hook plugins. A user must also have the relevant base permission, such as `clients:build`, before plugin access matters.
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
# Legacy Native Plugins
|
||||||
|
|
||||||
|
Legacy native v1 plugins run as shared libraries inside the agent. They are still supported and still useful when you need capabilities that the WASM runtime intentionally does not expose.
|
||||||
|
|
||||||
|
Use legacy native plugins when you need direct OS APIs, native libraries, custom process/thread behavior, platform-specific integrations, or code that cannot reasonably be compiled to the Plugin 2.0 WASM ABI.
|
||||||
|
|
||||||
|
Native plugins run with the same privileges as the agent. They are not sandboxed.
|
||||||
|
|
||||||
|
## What Legacy Plugins Can Do
|
||||||
|
|
||||||
|
| Capability | Notes |
|
||||||
|
|------------|-------|
|
||||||
|
| Call native OS APIs | Win32, POSIX, Objective-C/C APIs, platform SDKs, and local native libraries. |
|
||||||
|
| Use language runtimes | C, C++, Go, Rust, and other languages that can export a C ABI. |
|
||||||
|
| Run platform-specific code | Package separate binaries per OS/architecture. |
|
||||||
|
| Emit plugin events | Send events back to the UI/server through the host callback. |
|
||||||
|
| Receive UI/server commands | Handle `PluginOnEvent` calls from plugin UI actions or server-side routing. |
|
||||||
|
|
||||||
|
WASM Plugin 2.0 is preferred for sandboxed, portable agent modules. Legacy native plugins are the escape hatch for privileged native behavior.
|
||||||
|
|
||||||
|
## Bundle Layout
|
||||||
|
|
||||||
|
A legacy bundle is a zip with web assets plus one or more platform binaries:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<pluginId>.zip
|
||||||
|
<pluginId>.html
|
||||||
|
<pluginId>.css
|
||||||
|
<pluginId>.js
|
||||||
|
<pluginId>-windows-amd64.dll
|
||||||
|
<pluginId>-linux-amd64.so
|
||||||
|
<pluginId>-darwin-arm64.dylib
|
||||||
|
```
|
||||||
|
|
||||||
|
The browser assets provide the plugin UI. The native binary is sent to a matching client when the plugin is loaded for that client.
|
||||||
|
|
||||||
|
## Binary Naming
|
||||||
|
|
||||||
|
Binary filenames must follow:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<pluginId>-<os>-<arch>.<ext>
|
||||||
|
```
|
||||||
|
|
||||||
|
| OS | Arch | Example |
|
||||||
|
|----|------|---------|
|
||||||
|
| `windows` | `amd64` | `sample-windows-amd64.dll` |
|
||||||
|
| `windows` | `arm64` | `sample-windows-arm64.dll` |
|
||||||
|
| `linux` | `amd64` | `sample-linux-amd64.so` |
|
||||||
|
| `linux` | `arm64` | `sample-linux-arm64.so` |
|
||||||
|
| `darwin` | `amd64` | `sample-darwin-amd64.dylib` |
|
||||||
|
| `darwin` | `arm64` | `sample-darwin-arm64.dylib` |
|
||||||
|
|
||||||
|
The server never sends an x64 native binary to an ARM client, or the reverse. If no binary matches the target client, loading fails for that client.
|
||||||
|
|
||||||
|
## Required ABI
|
||||||
|
|
||||||
|
Every native plugin exports C-callable functions. The ABI differs slightly between Windows and Unix-like systems.
|
||||||
|
|
||||||
|
| Export | Windows signature | Linux/macOS signature | Required |
|
||||||
|
|--------|-------------------|-----------------------|----------|
|
||||||
|
| `PluginOnLoad` | `int PluginOnLoad(char* hostInfo, int hostInfoLen, uint64 callback)` | `int PluginOnLoad(char* hostInfo, int hostInfoLen, uintptr callback, uintptr ctx)` | Yes |
|
||||||
|
| `PluginOnEvent` | `int PluginOnEvent(char* event, int eventLen, char* payload, int payloadLen)` | Same | Yes |
|
||||||
|
| `PluginOnUnload` | `void PluginOnUnload()` | Same | Yes |
|
||||||
|
| `PluginSetCallback` | `void PluginSetCallback(uint64 callback)` | Not used | Windows only |
|
||||||
|
| `PluginGetRuntime` | `const char* PluginGetRuntime()` | Same | Recommended |
|
||||||
|
|
||||||
|
Return `0` from `PluginOnLoad` and `PluginOnEvent` for success. Return a non-zero value when the plugin cannot initialize or handle the event.
|
||||||
|
|
||||||
|
## HostInfo JSON
|
||||||
|
|
||||||
|
`PluginOnLoad` receives host metadata as a JSON byte buffer:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"clientId": "abc123",
|
||||||
|
"os": "windows",
|
||||||
|
"arch": "amd64",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use this to branch by client OS, architecture, client ID, or agent version.
|
||||||
|
|
||||||
|
## Host Callback
|
||||||
|
|
||||||
|
The host callback lets a native plugin emit events back to the agent/server.
|
||||||
|
|
||||||
|
Windows uses `__stdcall`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void __stdcall callback(const char *event, uintptr_t eventLen,
|
||||||
|
const char *payload, uintptr_t payloadLen);
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux/macOS use a normal C function plus the opaque context pointer received by `PluginOnLoad`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void callback(uintptr_t ctx,
|
||||||
|
const char *event, int eventLen,
|
||||||
|
const char *payload, int payloadLen);
|
||||||
|
```
|
||||||
|
|
||||||
|
The event name and payload are byte buffers. JSON payloads are conventional, but the ABI only requires bytes plus lengths.
|
||||||
|
|
||||||
|
## Minimal C Skeleton
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define EXPORT __declspec(dllexport)
|
||||||
|
typedef void (__stdcall *host_callback_t)(
|
||||||
|
const char *event, uintptr_t eventLen,
|
||||||
|
const char *payload, uintptr_t payloadLen);
|
||||||
|
static host_callback_t g_callback = 0;
|
||||||
|
#else
|
||||||
|
#define EXPORT __attribute__((visibility("default")))
|
||||||
|
typedef void (*host_callback_t)(
|
||||||
|
uintptr_t ctx,
|
||||||
|
const char *event, int eventLen,
|
||||||
|
const char *payload, int payloadLen);
|
||||||
|
static host_callback_t g_callback = 0;
|
||||||
|
static uintptr_t g_ctx = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXPORT const char *PluginGetRuntime(void) {
|
||||||
|
return "c";
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
EXPORT void PluginSetCallback(uint64_t callback) {
|
||||||
|
g_callback = (host_callback_t)(uintptr_t)callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT int PluginOnLoad(const char *hostInfo, int hostInfoLen, uint64_t callback) {
|
||||||
|
g_callback = (host_callback_t)(uintptr_t)callback;
|
||||||
|
if (g_callback) g_callback("ready", 5, "{\"ok\":true}", 11);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
EXPORT int PluginOnLoad(const char *hostInfo, int hostInfoLen, uintptr_t callback, uintptr_t ctx) {
|
||||||
|
g_callback = (host_callback_t)callback;
|
||||||
|
g_ctx = ctx;
|
||||||
|
if (g_callback) g_callback(g_ctx, "ready", 5, "{\"ok\":true}", 11);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXPORT int PluginOnEvent(const char *event, int eventLen, const char *payload, int payloadLen) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void PluginOnUnload(void) {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Runtime Detection
|
||||||
|
|
||||||
|
`PluginGetRuntime` should return a pointer to a static, null-terminated string.
|
||||||
|
|
||||||
|
| Return value | Meaning |
|
||||||
|
|--------------|---------|
|
||||||
|
| `c` | C plugin; fully unloadable. |
|
||||||
|
| `cpp` | C++ plugin; fully unloadable. |
|
||||||
|
| `rust` | Rust plugin; fully unloadable when written without unload-hostile globals. |
|
||||||
|
| `go` or absent | Go runtime; intentionally not unloaded. |
|
||||||
|
|
||||||
|
Go shared libraries cannot be fully unloaded safely because the Go runtime owns threads and process-global state. The host treats Go plugins as non-freeable to avoid crashes.
|
||||||
|
|
||||||
|
## Loading Model
|
||||||
|
|
||||||
|
| Platform | Loading behavior |
|
||||||
|
|----------|------------------|
|
||||||
|
| Windows | DLLs are loaded from memory through the custom PE loader. |
|
||||||
|
| Linux | Shared libraries are loaded through the static-agent subprocess shim when needed. |
|
||||||
|
| macOS | Dynamic libraries are loaded through the platform loader. |
|
||||||
|
|
||||||
|
Because Windows uses an in-memory PE loader, some runtime primitives that assume normal `LoadLibrary` initialization can be fragile. The Rust sample avoids `std::sync::Mutex` for this reason and uses C-style globals; the host serializes plugin calls.
|
||||||
|
|
||||||
|
## Static Linux Agent Shim
|
||||||
|
|
||||||
|
Linux agents are compiled as static musl binaries for portability. Static binaries cannot directly call `dlopen`, so Linux native plugin support uses an embedded plugin-host shim.
|
||||||
|
|
||||||
|
When loading a Linux native plugin, the agent:
|
||||||
|
|
||||||
|
1. Writes the plugin `.so` to an anonymous in-memory file.
|
||||||
|
2. Writes the embedded shim to another in-memory file.
|
||||||
|
3. Creates a bidirectional socket.
|
||||||
|
4. Starts the shim and passes file descriptors to it.
|
||||||
|
5. The shim loads the `.so` and forwards events over the socket.
|
||||||
|
|
||||||
|
Plugin authors normally do not need shim-specific code. Export the normal v1 ABI and compile the `.so` for the target libc/architecture.
|
||||||
|
|
||||||
|
## IPC Protocol Notes
|
||||||
|
|
||||||
|
The agent and Linux shim use a length-prefixed binary protocol:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[4-byte little-endian total payload length][1-byte message type][payload bytes]
|
||||||
|
```
|
||||||
|
|
||||||
|
Important message types:
|
||||||
|
|
||||||
|
| Direction | Type | Meaning |
|
||||||
|
|-----------|------|---------|
|
||||||
|
| agent to shim | `0x01` | Load plugin with HostInfo. |
|
||||||
|
| agent to shim | `0x02` | Deliver plugin event. |
|
||||||
|
| agent to shim | `0x03` | Unload plugin. |
|
||||||
|
| shim to agent | `0x10` | Plugin callback event. |
|
||||||
|
| shim to agent | `0x11` | Ready/runtime string. |
|
||||||
|
| shim to agent | `0x12` | Error string. |
|
||||||
|
| shim to agent | `0x13` | Load result. |
|
||||||
|
|
||||||
|
Most plugin authors can ignore this, but it is useful when debugging Linux native plugin load failures.
|
||||||
|
|
||||||
|
## Build Scripts
|
||||||
|
|
||||||
|
Legacy samples include `build.bat` and `build.sh`. They compile native binaries and zip them with web assets.
|
||||||
|
|
||||||
|
Default usage:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd plugins/sample-c
|
||||||
|
./build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple targets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
BUILD_TARGETS="linux-amd64 linux-arm64 windows-amd64 darwin-arm64" ./build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Windows:
|
||||||
|
|
||||||
|
```bat
|
||||||
|
cd plugins\sample-c
|
||||||
|
build.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
The scripts support `BUILD_TARGETS` and usually accept an optional plugin directory argument.
|
||||||
|
|
||||||
|
## Cross-Compilation
|
||||||
|
|
||||||
|
Install the compiler matching the target platform, or set `CC` for C/C++ samples.
|
||||||
|
|
||||||
|
Common compiler names used by the sample scripts:
|
||||||
|
|
||||||
|
| Target | Compiler |
|
||||||
|
|--------|----------|
|
||||||
|
| `linux-amd64` | `x86_64-linux-gnu-gcc` |
|
||||||
|
| `linux-arm64` | `aarch64-linux-gnu-gcc` |
|
||||||
|
| `linux-arm` | `arm-linux-gnueabihf-gcc` |
|
||||||
|
| `windows-amd64` | `x86_64-w64-mingw32-gcc` |
|
||||||
|
| `windows-arm64` | `aarch64-w64-mingw32-gcc` |
|
||||||
|
| `darwin-amd64` | `x86_64-apple-darwin-gcc` |
|
||||||
|
| `darwin-arm64` | `aarch64-apple-darwin-gcc` |
|
||||||
|
|
||||||
|
Rust samples use Cargo targets. Go samples use `go build -buildmode=c-shared` with CGO enabled.
|
||||||
|
|
||||||
|
## Language Notes
|
||||||
|
|
||||||
|
| Sample | Notes |
|
||||||
|
|--------|-------|
|
||||||
|
| `sample-c` | Smallest ABI surface, no runtime, fully unloadable. |
|
||||||
|
| `sample-cpp` | C++ convenience with exported C ABI. Return `cpp` from `PluginGetRuntime`. |
|
||||||
|
| `sample-rust` | Exports a C ABI. Avoid assumptions that require normal OS loader initialization. |
|
||||||
|
| `sample-go` | Uses `-buildmode=c-shared`; treated as non-freeable because the Go runtime cannot be safely unloaded. |
|
||||||
|
|
||||||
|
## UI Event Flow
|
||||||
|
|
||||||
|
The browser UI sends events through the normal plugin event endpoint. The server forwards them to the client. The agent calls:
|
||||||
|
|
||||||
|
```text
|
||||||
|
PluginOnEvent(event, eventLen, payload, payloadLen)
|
||||||
|
```
|
||||||
|
|
||||||
|
The native plugin can reply by invoking the host callback. Those callback events are delivered back to the server/UI as plugin events.
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
Legacy native plugins are trusted executable code. They can call OS APIs, read/write files available to the agent process, spawn subprocesses, open sockets, and interact with native libraries. Use plugin signing, per-user plugin access, and code review before deploying third-party native plugins.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Sample Plugins
|
||||||
|
|
||||||
|
The `plugins/` directory includes small examples that exercise each plugin style.
|
||||||
|
|
||||||
|
| Directory | Demonstrates |
|
||||||
|
|-----------|--------------|
|
||||||
|
| `sample-build-hooks` | Server-only build plugin, custom Build page action, build settings, and replacing the produced artifact with a `.txt` file. |
|
||||||
|
| `sample-ts-fullstack` | TypeScript UI and TypeScript server runtime with shared local modules. |
|
||||||
|
| `sample-wasm` | Plugin 2.0 WASM module built from C/WASI. |
|
||||||
|
| `sample-wasm-hostinfo` | TinyGo WASM plugin that queries HostInfo. |
|
||||||
|
| `sample-wasm-platform-note` | Rust WASM plugin that branches by HostInfo/platform. |
|
||||||
|
| `sample-c` | Legacy native C plugin for the smallest unloadable ABI surface. |
|
||||||
|
| `sample-cpp` | Legacy native C++ plugin using exported C ABI functions. |
|
||||||
|
| `sample-go` | Legacy native Go plugin. Go plugins are intentionally not unloaded because the Go runtime cannot be safely unloaded from shared libraries. |
|
||||||
|
| `sample-rust` | Legacy native Rust plugin with C ABI exports and unload-friendly runtime handling. |
|
||||||
|
|
||||||
|
Use `sample-build-hooks` as the starting point for uploaders, artifact publishers, or custom release buttons.
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
# Server-Side Plugins
|
||||||
|
|
||||||
|
Server-side plugins run inside the Overlord server process environment. They are for global plugin pages, persistent server data, RPC-backed tools, build pipeline customization, uploads, artifact replacement, signing, or internal release workflows.
|
||||||
|
|
||||||
|
They are trusted code. A server-side plugin has normal server filesystem and network access, so treat it like part of the program itself. Use plugin signing and only install code you trust.
|
||||||
|
|
||||||
|
## Server-Only Plugins
|
||||||
|
|
||||||
|
Set `runtime: "server"` in `config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Internal Builder",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"apiVersion": 2,
|
||||||
|
"runtime": "server",
|
||||||
|
"description": "Adds custom build buttons and upload hooks."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Server-only plugins:
|
||||||
|
|
||||||
|
| Behavior | Notes |
|
||||||
|
|----------|-------|
|
||||||
|
| Plugin Manager look | Shown as server extensions, with worker/build-plugin state where available. |
|
||||||
|
| Client loading | Never sent to agents. |
|
||||||
|
| Auto-load | Hidden and unavailable because there is no per-client agent module. |
|
||||||
|
| Build access | Can hook the builder when `server.js` or `src/server.ts` exports build handlers. |
|
||||||
|
|
||||||
|
## Lifecycle
|
||||||
|
|
||||||
|
If a bundle includes `server.js` or compiled `src/server.ts`, the server starts one worker for the plugin when the plugin is enabled.
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
async setup(ctx) {
|
||||||
|
ctx.log.info("server plugin started");
|
||||||
|
},
|
||||||
|
|
||||||
|
async onEvent(ctx, event) {
|
||||||
|
ctx.log.info(`plugin event from ${event.clientId}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
rpc: {
|
||||||
|
async list(ctx, params) {
|
||||||
|
return ctx.db.query("select * from items").all();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
async teardown(ctx) {
|
||||||
|
ctx.log.info("server plugin stopping");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## The `ctx` Object
|
||||||
|
|
||||||
|
| Field | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `ctx.pluginId` | Sanitized plugin ID. |
|
||||||
|
| `ctx.db` | `bun:sqlite` database backed by `plugins/<id>/data/plugin.db`; WAL mode is enabled. |
|
||||||
|
| `ctx.dataDir` | Absolute path to the plugin's persistent `data/` directory. |
|
||||||
|
| `ctx.log` | `{ debug, info, warn, error }` forwarded to the server logger as `[plugin:<id>] ...`. |
|
||||||
|
| `ctx.broadcast(channel, data)` | Sends JSON-serializable data to every UI subscribed to this plugin's SSE stream. |
|
||||||
|
|
||||||
|
## Persistent Data
|
||||||
|
|
||||||
|
Each plugin owns:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Overlord-Server/plugins/<id>/data/
|
||||||
|
```
|
||||||
|
|
||||||
|
The directory is created automatically and is not removed when a plugin is reinstalled or deleted. Use it for SQLite state, cached files, generated artifacts, helper binaries, or plugin-local configuration.
|
||||||
|
|
||||||
|
The server also exposes plugin data APIs for listing, reading, writing, deleting, and running files in that directory. Execution endpoints are restricted to admin/operator roles.
|
||||||
|
|
||||||
|
## UI RPC And Broadcasts
|
||||||
|
|
||||||
|
Browser UI code can call plugin RPC methods:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const res = await fetch(`/api/plugins/my-plugin/rpc`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ method: "list", params: {} }),
|
||||||
|
});
|
||||||
|
const { ok, result, error } = await res.json();
|
||||||
|
```
|
||||||
|
|
||||||
|
Subscribe to server broadcasts:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const stream = new EventSource(`/api/plugins/my-plugin/stream`);
|
||||||
|
stream.addEventListener("changed", (event) => {
|
||||||
|
const payload = JSON.parse(event.data);
|
||||||
|
console.log(payload);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
RPC calls require `clients:control`, time out after 30 seconds, and must target a method exported in the plugin's `rpc` map. SSE streams also require `clients:control`.
|
||||||
|
|
||||||
|
## Build Hooks
|
||||||
|
|
||||||
|
Server-side plugins can hook the agent builder without shipping agent code. Browser UI is never called from the build process; only `server.js` or compiled `src/server.ts` participates.
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
onBuildPrepare(ctx, payload) {
|
||||||
|
return { config: { outputName: "custom-agent" } };
|
||||||
|
},
|
||||||
|
|
||||||
|
onBuildTarget(ctx, payload) {
|
||||||
|
if (payload.platform === "linux-amd64") {
|
||||||
|
return {
|
||||||
|
env: { CGO_ENABLED: "0" },
|
||||||
|
addTags: ["my_feature"],
|
||||||
|
ldflagsAppend: "-X overlord-client/cmd/agent/config.PluginChannel=stable",
|
||||||
|
message: "linux-amd64 template patched",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
buildHooks: {
|
||||||
|
before_donut(ctx, payload) {
|
||||||
|
ctx.log.info(`Donut input: ${payload.file.filename}`);
|
||||||
|
},
|
||||||
|
after_donut(ctx, payload) {
|
||||||
|
ctx.log.info(`Donut output: ${payload.file.filename}`);
|
||||||
|
},
|
||||||
|
before_sgn(ctx, payload) {
|
||||||
|
ctx.log.info(`SGN iterations: ${payload.iterations}`);
|
||||||
|
},
|
||||||
|
after_sgn(ctx, payload) {
|
||||||
|
ctx.log.info(`SGN output: ${payload.file.filename}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
async onBuildArtifact(ctx, payload) {
|
||||||
|
ctx.log.info(`artifact ready: ${payload.file.path}`);
|
||||||
|
return { message: `saw ${payload.file.filename}` };
|
||||||
|
},
|
||||||
|
|
||||||
|
async onBuildComplete(ctx, payload) {
|
||||||
|
ctx.log.info(`build ${payload.buildId} produced ${payload.files.length} files`);
|
||||||
|
},
|
||||||
|
|
||||||
|
onBuildFailed(ctx, payload) {
|
||||||
|
ctx.log.warn(`build failed: ${payload.error}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
`onBuildTarget` can return:
|
||||||
|
|
||||||
|
| Field | Effect |
|
||||||
|
|-------|--------|
|
||||||
|
| `env` | Merges string values into the Go build environment. |
|
||||||
|
| `ldflags` | Replaces the full `-ldflags` string. |
|
||||||
|
| `ldflagsAppend` | Appends to the existing `-ldflags` string. |
|
||||||
|
| `tags` | Replaces the full build tag list. |
|
||||||
|
| `addTags` / `removeTags` | Adds or removes build tags. |
|
||||||
|
| `outputName` | Renames the target output file after server sanitization. |
|
||||||
|
| `skip: true` | Skips that target. |
|
||||||
|
| `message` / `messages` | Writes informational lines into the build stream. |
|
||||||
|
|
||||||
|
## Transform Hook Stages
|
||||||
|
|
||||||
|
Implement transform hooks either in `buildHooks` with the snake-case name, or as a named method such as `onBuildBeforeDonut`.
|
||||||
|
|
||||||
|
| Hook | Named method | When it runs |
|
||||||
|
|------|--------------|--------------|
|
||||||
|
| `post_build` | `onBuildPostBuild` | After the raw Go build output exists. |
|
||||||
|
| `before_upx` / `after_upx` | `onBuildBeforeUpx` / `onBuildAfterUpx` | Around UPX compression. |
|
||||||
|
| `before_script_wrapper` / `after_script_wrapper` | `onBuildBeforeScriptWrapper` / `onBuildAfterScriptWrapper` | Around `.bat`, `.cmd`, and `.ps1` wrapping. |
|
||||||
|
| `before_ipa` / `after_ipa` | `onBuildBeforeIpa` / `onBuildAfterIpa` | Around iOS IPA packaging. |
|
||||||
|
| `before_donut` / `after_donut` | `onBuildBeforeDonut` / `onBuildAfterDonut` | Around Windows PE to shellcode conversion with Donut. |
|
||||||
|
| `before_linux_shellcode` / `after_linux_shellcode` | `onBuildBeforeLinuxShellcode` / `onBuildAfterLinuxShellcode` | Around Linux ELF shellcode wrapping. |
|
||||||
|
| `before_sgn` / `after_sgn` | `onBuildBeforeSgn` / `onBuildAfterSgn` | Around SGN shellcode encoding. |
|
||||||
|
| `before_sgn_txt` / `after_sgn_txt` | `onBuildBeforeSgnTxt` / `onBuildAfterSgnTxt` | Around SGN TXT artifact conversion. |
|
||||||
|
|
||||||
|
Transform hook payloads include:
|
||||||
|
|
||||||
|
| Field | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `buildId` | Current build ID. |
|
||||||
|
| `platform`, `os`, `arch`, `targetKey` | Target identity. |
|
||||||
|
| `outDir`, `clientDir` | Build output and client source directories. |
|
||||||
|
| `config` | Full build config, including selected build plugins. |
|
||||||
|
| `file` | `{ filename, path, platform, size }` for the current artifact. |
|
||||||
|
|
||||||
|
Some stages include extra fields such as `outputFilename`, `outputPath`, `donutArch`, `shellcodeArch`, or `iterations`.
|
||||||
|
|
||||||
|
## Artifact Replacement And Uploads
|
||||||
|
|
||||||
|
`onBuildArtifact` receives `payload.file` with:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
filename,
|
||||||
|
path,
|
||||||
|
platform,
|
||||||
|
version,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Return `file: { filename }` to replace the downloadable build artifact with another file already inside `dist-clients`, or return `files: [...]` to add extra downloadable artifacts from `dist-clients`.
|
||||||
|
|
||||||
|
Paths outside the build output directory are ignored for downloadable metadata, but the hook can still upload, copy, sign, or publish files itself because server-side plugins run with normal server privileges.
|
||||||
|
|
||||||
|
## Build Plugin Settings, Buttons, And Requirements
|
||||||
|
|
||||||
|
Declare builder UI in `config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"runtime": "server",
|
||||||
|
"build": {
|
||||||
|
"label": "Uploader",
|
||||||
|
"description": "Uploads completed builds to an internal bucket.",
|
||||||
|
"enabledByDefault": true,
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"key": "bucket",
|
||||||
|
"label": "Bucket",
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"placeholder": "release-builds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "publish",
|
||||||
|
"label": "Publish after build",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"id": "shellcode-release",
|
||||||
|
"label": "Shellcode Release",
|
||||||
|
"icon": "fa-solid fa-fire",
|
||||||
|
"setBuild": { "useDonut": true, "useSgn": true },
|
||||||
|
"setSettings": { "publish": true }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requires": [
|
||||||
|
{
|
||||||
|
"field": "useSgn",
|
||||||
|
"truthy": true,
|
||||||
|
"message": "Uploader release mode requires SGN to be enabled."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported setting types are `string`, `textarea`, `number`, `boolean`, and `select`.
|
||||||
|
|
||||||
|
Build profiles save these values and send them to hooks as:
|
||||||
|
|
||||||
|
```js
|
||||||
|
payload.config.buildPlugins[ctx.pluginId] // { enabled, settings }
|
||||||
|
```
|
||||||
|
|
||||||
|
Buttons in `build.actions` can set core builder fields through `setBuild` and plugin fields through `setSettings`. Requirements can check:
|
||||||
|
|
||||||
|
| Requirement field | Meaning |
|
||||||
|
|-------------------|---------|
|
||||||
|
| `field` | Core build field such as `useSgn` or `useDonut`. |
|
||||||
|
| `pluginSetting` | Setting from this plugin's build settings. |
|
||||||
|
| `platforms` | Selected build platforms. |
|
||||||
|
|
||||||
|
See `plugins/sample-build-hooks` for a minimal server-only build plugin with its own build button and artifact replacement.
|
||||||
|
|
||||||
|
## RBAC And Build Plugin Access
|
||||||
|
|
||||||
|
Build-hook plugins use the normal per-user plugin access policy. Admins can open a user in the Users page, choose Plugin Access, and set one of these modes:
|
||||||
|
|
||||||
|
| Mode | Effect |
|
||||||
|
|------|--------|
|
||||||
|
| No Access | The user cannot open plugin pages or use build plugins. |
|
||||||
|
| Selected Plugins | The user can only open and use checked plugins. |
|
||||||
|
| All Plugins | The user can use every enabled plugin. |
|
||||||
|
|
||||||
|
For build plugins this means:
|
||||||
|
|
||||||
|
| Path | Enforcement |
|
||||||
|
|------|-------------|
|
||||||
|
| Build page plugin list | `/api/build/plugins` only returns build plugins the user can access. |
|
||||||
|
| Build start | Inaccessible build plugin settings are ignored server-side, so a user cannot invoke a hidden build hook by editing the request. |
|
||||||
|
| Plugin RPC/UI/assets | Plugin pages, assets, RPC, and SSE streams require access to that plugin. |
|
||||||
|
|
||||||
|
Users still need `clients:build` to build at all. Plugin access is the second gate that decides which build-hook plugins they can see and use.
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
| Caveat | Detail |
|
||||||
|
|--------|--------|
|
||||||
|
| Worker trust | Workers are not a security sandbox. They can access server files/network with normal server permissions. |
|
||||||
|
| One worker per plugin | RPC calls into the same plugin serialize; long calls block other RPCs for that plugin. |
|
||||||
|
| SQLite | Each plugin gets a separate DB. Avoid opening the same DB from another process. |
|
||||||
|
| Worker crash | Crashes are logged and `pluginState.lastError` is set; disable and re-enable the plugin to restart it. |
|
||||||
|
| Bundled builds | The production build emits both the main server bundle and `src/server/plugin-runtime/worker-host.ts`. |
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# Plugin Signing
|
||||||
|
|
||||||
|
Plugins can be signed with Ed25519 keys. The server verifies signatures on upload and displays trust state in the Plugin Manager. Unsigned or untrusted plugins require explicit confirmation; invalid signatures are blocked.
|
||||||
|
|
||||||
|
## Trust Levels
|
||||||
|
|
||||||
|
| Status | Behavior |
|
||||||
|
|--------|----------|
|
||||||
|
| Signed + trusted | Loads immediately. |
|
||||||
|
| Signed + untrusted | Requires confirmation. |
|
||||||
|
| Unsigned | Requires confirmation. |
|
||||||
|
| Invalid signature | Blocked. |
|
||||||
|
|
||||||
|
## Generate A Signing Key
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd Overlord-Server
|
||||||
|
bun run scripts/plugin-keygen.ts --out my-signing-key
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates:
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `my-signing-key.key` | Private key. Keep it secret. |
|
||||||
|
| `my-signing-key.pub` | Public key plus fingerprint. |
|
||||||
|
|
||||||
|
## Sign A Plugin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd Overlord-Server
|
||||||
|
bun run scripts/plugin-sign.ts --key my-signing-key.key ../plugins/sample-go/sample.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
The signer injects `signature.json` into the zip.
|
||||||
|
|
||||||
|
## Trust A Key
|
||||||
|
|
||||||
|
Via `config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"plugins": {
|
||||||
|
"trustedKeys": [
|
||||||
|
"a1b2c3d4e5f6...64-char-hex-fingerprint..."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Via environment variable:
|
||||||
|
|
||||||
|
```text
|
||||||
|
TRUSTED_PLUGIN_KEYS=fingerprint1,fingerprint2
|
||||||
|
```
|
||||||
|
|
||||||
|
Via API:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST /api/plugins/trusted-keys \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"fingerprint": "a1b2c3..."}'
|
||||||
|
|
||||||
|
curl /api/plugins/trusted-keys
|
||||||
|
|
||||||
|
curl -X DELETE /api/plugins/trusted-keys/a1b2c3...
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also add trusted keys from the Plugins page.
|
||||||
|
|
||||||
|
## Build Script Integration
|
||||||
|
|
||||||
|
Set `PLUGIN_SIGN_KEY` to sign during plugin build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PLUGIN_SIGN_KEY=path/to/my-signing-key.key ./sample-go/build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
|
||||||
|
```bat
|
||||||
|
set PLUGIN_SIGN_KEY=path\to\my-signing-key.key
|
||||||
|
sample-go\build.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
## How Signing Works
|
||||||
|
|
||||||
|
1. The canonical content digest hashes every zip file except `signature.json`.
|
||||||
|
2. Filenames are sorted alphabetically and combined as `filename:sha256hex`.
|
||||||
|
3. The digest is signed with Ed25519.
|
||||||
|
4. `signature.json` stores the signature, public key, and algorithm.
|
||||||
|
5. Upload verification recomputes the digest and compares the signer fingerprint against trusted keys.
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# WASM Plugins
|
||||||
|
|
||||||
|
Plugin 2.0 WASM plugins run inside the agent through a sandboxed runtime. They cannot access files or OS APIs directly; they declare needs in `config.json`, and the agent exposes approved host bridge operations.
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "My WASM Plugin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"apiVersion": 2,
|
||||||
|
"runtime": "wasm",
|
||||||
|
"wasm": "my-plugin.wasm",
|
||||||
|
"needs": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"bucket": "pluginData",
|
||||||
|
"access": ["read", "write", "list", "mkdir"],
|
||||||
|
"reason": "Store plugin state"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported file buckets are `home`, `desktop`, `documents`, `downloads`, `temp`, `appData`, `pluginData`, and `fullDisk`.
|
||||||
|
|
||||||
|
Supported operations are `read`, `write`, `list`, `delete`, and `mkdir`. Approval is tracked by a stable hash of normalized needs; changing needs requires approval again.
|
||||||
|
|
||||||
|
## Required Exports
|
||||||
|
|
||||||
|
| Export | Signature |
|
||||||
|
|--------|-----------|
|
||||||
|
| `overlord_alloc` | `ptr(uint32 size)` |
|
||||||
|
| `overlord_free` | `void(ptr, uint32 size)` |
|
||||||
|
| `overlord_on_load` | `int32(ptr hostInfo, uint32 hostInfoLen)` |
|
||||||
|
| `overlord_on_event` | `int32(ptr event, uint32 eventLen, ptr payload, uint32 payloadLen)` |
|
||||||
|
| `overlord_on_unload` | `void()` |
|
||||||
|
|
||||||
|
## Host Imports
|
||||||
|
|
||||||
|
Imports are available from both `env` and `overlord`.
|
||||||
|
|
||||||
|
| Import | Behavior |
|
||||||
|
|--------|----------|
|
||||||
|
| `overlord_emit(event, eventLen, payload, payloadLen)` | Sends an event through the plugin event channel. |
|
||||||
|
| `overlord_host_info(out, outLen)` | Writes HostInfo JSON into `out`. |
|
||||||
|
| `overlord_fs_stat(bucket, bucketLen, path, pathLen, out, outLen)` | Writes JSON file metadata into `out`. |
|
||||||
|
| `overlord_fs_list(bucket, bucketLen, path, pathLen, out, outLen)` | Writes a JSON directory listing into `out`. |
|
||||||
|
| `overlord_fs_read(bucket, bucketLen, path, pathLen, out, outLen)` | Writes file bytes into `out`, max 32 MB. |
|
||||||
|
| `overlord_fs_write(bucket, bucketLen, path, pathLen, data, dataLen)` | Writes bytes to a bucket-relative path. |
|
||||||
|
| `overlord_fs_delete(bucket, bucketLen, path, pathLen)` | Deletes a bucket-relative file or directory. |
|
||||||
|
| `overlord_fs_mkdir(bucket, bucketLen, path, pathLen)` | Creates a bucket-relative directory. |
|
||||||
|
|
||||||
|
Bridge functions return non-negative byte counts or `0` on success. Negative return codes mean denied, invalid path/memory, not found, output buffer too small, file too large, or I/O failure.
|
||||||
|
|
||||||
|
## HostInfo
|
||||||
|
|
||||||
|
WASM plugins are universal across OS/architecture. To branch by platform, read HostInfo from `overlord_on_load(hostInfo, hostInfoLen)` or `overlord_host_info(out, outLen)`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"clientId": "abc123",
|
||||||
|
"os": "windows",
|
||||||
|
"arch": "amd64",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Samples
|
||||||
|
|
||||||
|
| Directory | Toolchain |
|
||||||
|
|-----------|-----------|
|
||||||
|
| `plugins/sample-wasm` | C/WASI |
|
||||||
|
| `plugins/sample-wasm-hostinfo` | TinyGo |
|
||||||
|
| `plugins/sample-wasm-platform-note` | Rust |
|
||||||
Reference in New Issue
Block a user