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
exportdefault{
asyncsetup(ctx){
ctx.log.info("server plugin started");
},
asynconEvent(ctx,event){
ctx.log.info(`plugin event from ${event.clientId}`);
},
rpc:{
asynclist(ctx,params){
returnctx.db.query("select * from items").all();
},
},
asyncteardown(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.
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`.
Server-side plugin code, including build hooks, can make trusted outbound requests with `ctx.fetch(url, options)`. These requests run from the Overlord server process and are not subject to browser CORS.
Browser plugin UI is still normal browser JavaScript. If a plugin page needs to call an API that does not allow browser CORS, call the same-origin proxy instead:
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.
| `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:
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`. |