Build native plugins for HaloForge — the Game Dev Team Workbench & AI Assistant.
This repository contains both the Rust crate and the JavaScript/TypeScript SDK that plugin authors need.
- SDK repository: https://github.com/HaloForgeAI/haloforge-plugin-api
- HaloForge homepage: https://github.com/HaloForgeAI
- Hosted plugin docs: https://docs.haloforge.link/plugins
| Package | Language | Registry | Install |
|---|---|---|---|
haloforge-plugin-api |
Rust | crates.io | cargo add haloforge-plugin-api serde_json |
@haloforge/plugin-sdk |
TypeScript | npm | npm i @haloforge/plugin-sdk react react-dom @tauri-apps/api lucide-react |
@haloforge/plugin-pack |
CLI | npm | npx @haloforge/plugin-pack check . |
For production plugin work, start with the full guide and template:
cargo new my-plugin --lib
cd my-plugin
cargo add haloforge-plugin-api serde_jsonThen make sure your crate builds as a dynamic library:
# Cargo.toml
[lib]
crate-type = ["cdylib"]You can use any React-compatible bundler. A minimal setup looks like this:
mkdir frontend
cd frontend
npm init -y
npm i @haloforge/plugin-sdk react react-dom @tauri-apps/api lucide-react
npm i -D typescript @types/react @types/react-domBuild your frontend into the file referenced by manifest.json under entry.frontend.
Every plugin ships with a manifest.json that declares compatibility, capabilities, entry points, and permissions.
Permission names are strict. For AI Chat access, the only valid manifest permission name is host_aichat_access. For enterprise model gateway access, use host_enterprise_gateway_access.
{
"id": "com.example.hello-plugin",
"name": "Hello Plugin",
"version": "0.1.0",
"description": "My first HaloForge plugin",
"author": "You",
"homepage": "https://github.com/you/hello-plugin",
"compatibility": {
"min_app_version": "0.1.0",
"min_host_api_version": "0.1.0"
},
"capability_levels": [2],
"host_capabilities": [
"navigation",
"file_intents",
"file_dialogs",
"aichat",
"theme_read"
],
"integration": {
"level2": {
"slots": ["devkit.toolbar"]
}
},
"entry": {
"native": {
"windows_x64": "native/hello_plugin.dll",
"macos_arm64": "native/libhello_plugin.dylib",
"linux_x64": "native/libhello_plugin.so"
},
"frontend": "frontend/dist/index.js"
},
"permissions": [
{ "type": "ipc_register" }
],
"commands": [
{
"id": "hello",
"description": "Return a greeting"
}
]
}use haloforge_plugin_api::*;
pub struct MyPlugin;
impl MyPlugin {
pub fn new() -> Self { Self }
}
impl HaloForgePlugin for MyPlugin {
fn metadata(&self) -> PluginMetadata {
PluginMetadata {
id: "com.example.my-plugin".into(),
name: "My Plugin".into(),
version: "0.1.0".into(),
description: "A sample HaloForge plugin".into(),
author: "You".into(),
abi_version: PLUGIN_ABI_VERSION,
}
}
fn on_load(
&mut self,
_ctx: &dyn PluginContext,
ipc: &mut dyn IpcRegistrar,
) -> Result<(), PluginError> {
ipc.register("hello", Box::new(|args, _ctx| {
let name = args["name"].as_str().unwrap_or("World");
Ok(serde_json::json!({ "message": format!("Hello, {name}!") }))
}))?;
Ok(())
}
fn on_unload(&mut self) -> Result<(), PluginError> {
Ok(())
}
}
declare_plugin!(MyPlugin, MyPlugin::new);import { definePlugin, invokePlugin, registerPlugin } from "@haloforge/plugin-sdk";
function HelloButton() {
async function handleClick() {
const result = await invokePlugin<{ message: string }>("hello", { name: "HaloForge" });
alert(result.message);
}
return <button onClick={() => void handleClick()}>Greet</button>;
}
export default registerPlugin("com.example.hello-plugin", definePlugin({
slots: {
"devkit.toolbar": HelloButton,
},
}));When HaloForge loads the bundle, it injects the runtime plugin context needed by invokePlugin, slot context, and the public host hooks. Plugin authors should prefer the SDK hooks over touching window.__HF_HOST directly.
For host-owned file pickers and AI transport helpers, the SDK also exports:
pickHostFile()pickHostDirectory()saveHostFile()usePluginDeepLink()/onPluginDeepLink()useHostAI().createSession(...)useHostAI().getStreamState(...)log()andcreatePluginLogger()
Frontend plugin logs written through createPluginLogger() are routed into HaloForge's app log file at ~/.haloforge/logs/haloforge.log.YYYY-MM-DD.
npx @haloforge/plugin-pack check .
npx @haloforge/plugin-pack pack . --release
npx @haloforge/plugin-pack metadata dist/dev.haloforge.example-0.1.0.hfpkg --signing-key-id haloforge-official-2026-05 --signing-key-env HF_PLUGIN_SIGNING_PRIVATE_KEY --pretty --output dist/catalog-draft.json
npx @haloforge/plugin-pack submit dist/catalog-draft.json --token-env HF_ADMIN_TOKENThe packer validates manifest.json, builds the Rust backend, builds the frontend bundle, collects optional assets/ and LICENSE, then writes a .hfpkg archive into dist/.
Rust plugins can write into the same HaloForge log stream through the provided context:
ctx.log(LogLevel::Info, "image request started request_id=hfis-123 model=gpt-image-2.0");
ctx.log(LogLevel::Error, "image request failed request_id=hfis-123 status=502 elapsed_ms=1842");Frontend plugins should use the SDK helper:
import { createPluginLogger } from "@haloforge/plugin-sdk";
const logger = createPluginLogger("gateway");
await logger.info("Generation started", { model: "gpt-image-2.0", size: "1024x1024" });
await logger.error("Generation failed", { status: 502, elapsedMs: 1842 });Log operational fields such as model, size, status, elapsed time, request ID, and output counts. Do not log API keys, bearer tokens, full prompts, or raw image/base64 payloads.
Use the HaloForge hf CLI to install and inspect the package locally:
cd /path/to/HaloForge
npm run hf -- plugin install local /path/to/my-plugin/dist/dev.haloforge.example-0.1.0.hfpkg --json
npm run hf -- plugin list --jsonnpm run hf -- ... is the source-checkout form. Installed Windows builds add hf to PATH, so a new terminal can use hf plugin ... directly. macOS automatic PATH linking is not implemented yet; run command -v hf before assuming the global command exists.
my-plugin/
Cargo.toml
manifest.json
src/
lib.rs
frontend/
package.json
src/
index.tsx
The most important manifest fields are:
capability_levels: which HaloForge extension tiers your plugin uses.integration: per-level configuration, like slot IDs or module metadata.entry.native: the compiled Rust library paths for each platform you ship.entry.frontend: the built JavaScript bundle HaloForge should load.host_capabilities: stable black-box-compatible host features consumed through@haloforge/plugin-sdk.permissions: the host capabilities your plugin needs approved.
See the HaloForge organization for real plugin examples.
Additional docs:
- docs/public-host-api.md
- docs/official-plugin-publishing.md
- docs/plugin-development-guide.md
- docs/zh/plugin-development-guide.md
@haloforge/plugin-pack is the public packager for HaloForge plugins.
hf-pack check <plugin-dir>validatesmanifest.json.hf-pack info <plugin-dir-or-.hfpkg>prints plugin metadata.hf-pack pack <plugin-dir>builds and assembles a distributable.hfpkgarchive.
It rejects invalid permission names such as host_a_i_chat_access and points plugin authors to the canonical host_aichat_access name.
hf-pack metadata <path.hfpkg>emits catalog draft JSON and can sign it directly for official plugin publishing.hf-pack submit <catalog-draft.json>uploads that draft toadmin.haloforge.link.
The CLI supports these plugin layouts:
manifest.jsonat the plugin root.- Rust backend in
backend/Cargo.toml,native/Cargo.toml,rust/Cargo.toml, or rootCargo.toml. - Frontend app in
frontend/package.json,ui/package.json,web/package.json,app/package.json, or rootpackage.json. - Optional
assets/,native/, andLICENSEfiles in the plugin root.
It also supports common build-output layouts where the manifest points to packaged paths like frontend/index.js, while the actual frontend build emits files under frontend/dist/ or frontend/build/.
hf-pack check and hf-pack pack also warn on:
- direct
__HF_HOSTaccess - direct host IPC strings such as
aichat_send_message - direct
plugin_invokeusage instead ofinvokePlugin()/invokeOtherPlugin()
hf-pack is not the same tool as HaloForge's hf CLI. Use hf-pack to create .hfpkg archives; use hf to install those archives into the local HaloForge workspace and verify the installed plugin state.
| Level | Type | Description |
|---|---|---|
| 0 | Module | Full sidebar module |
| 1 | Module Feature | Tab inside an existing module |
| 2 | UI Extension | Inject into UI slots |
| 3 | AI Assistant | Custom AI assistant persona |
| 4 | Service | Workflow step types & background services |
MIT