Skip to main content

HyperHQ Plugin API Reference

Quick reference for the current HyperHQ plugin runtime.

Manifest file

HyperHQ discovers and validates plugins from plugin.json in each plugin directory. manifest.json is not used for plugins.

Required manifest fields

{
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "Short user-facing description",
"author": "Your Name",
"type": "executable",
"executable": "plugin.exe",
"communication": {
"preferred": "stdio",
"fallback": "socketio",
"stdio": { "enabled": true },
"socketio": { "enabled": true }
},
"capabilities": [
{ "name": "utility", "description": "Adds a utility action", "required": true }
],
"permissions": []
}
FieldNotes
idLowercase letters, numbers, hyphens, and underscores.
name, version, description, authorRequired and non-empty. version must be semver-like, e.g. 1.0.0.
typejavascript or executable.
mainJavaScript entry file (.js or .mjs); defaults to index.js when omitted.
executableExecutable plugin fallback entry point. Defaults to plugin.exe on Windows and plugin on Linux/macOS if omitted in some runners.
executableProvidersPreferred platform-specific executable map: { "windows": "plugin.exe", "linux": "plugin", "macos": "plugin" }.
executableLinux, executableMacLegacy flat platform overrides still supported for compatibility. Prefer executableProviders.
communicationOptional executable transport preference containing preferred, fallback, stdio, and socketio.
capabilitiesRequired non-empty array of capability objects.
permissionsArray of permission objects, not an object map. Empty array is allowed.
platformsOptional array: windows, linux, macos. Omit for all platforms.

Permissions

{
"permissions": [
{ "type": "network", "scope": "api.example.com", "description": "Fetch metadata" },
{ "type": "file", "scope": "plugin", "description": "Read packaged plugin assets" }
]
}

Supported permission type values are file, network, system, and database. scope and description are required. Avoid broad scopes such as *; unrestricted system access is rejected.

Settings schema

Settings are declared in top-level settings. Use label for display text; name is not the current setting label field.

TypeUse for
string, textareaText input.
number, rangeNumeric input; use validation min, max, step.
booleanToggle/checkbox.
email, password, urlSpecialized text inputs.
selectChoice list via options: [{ "value": "...", "label": "..." }].
oauthHyperHQ-managed OAuth/browser capture flow.
array, objectStructured settings.
fileFile selector; use validation.fileTypes to filter extensions.
directoryFolder selector.
colorColor picker.

Path picker fields:

  • file opens a file picker.
  • directory opens a folder picker.
  • string remains a text field, but accepts browse for optional path picking.
  • browse: true defaults to a file picker unless the setting type is directory.
  • browse: "file" or browse: "directory" selects the picker mode.
  • browse: { "type": "directory", "buttonLabel": "Choose Folder" } changes the button text.
{
"settings": [
{
"key": "apiKey",
"type": "password",
"label": "API Key",
"description": "Token for the metadata service",
"required": true,
"validation": { "minLength": 20 }
},
{
"key": "accentColor",
"type": "color",
"label": "Accent Color",
"defaultValue": "#7c3aed"
},
{
"key": "account",
"type": "oauth",
"label": "Account",
"oauth": {
"provider": "example",
"authUrl": "https://example.com/oauth/authorize",
"callbackUrls": ["https://example.com/oauth/callback"],
"requiredCallbackParams": ["code"],
"statusAction": "oauth.status"
}
},
{
"key": "installPath",
"type": "string",
"label": "Install Path",
"browse": "directory",
"placeholder": "Select or enter an install folder"
},
{
"key": "mameExe",
"type": "file",
"label": "MAME Executable",
"validation": {
"fileTypes": [
{ "name": "Executables", "extensions": ["exe"] }
]
}
}
]
}

oauth settings are passed to plugins as runtime connection state/artifacts after HyperHQ captures the OAuth result. Plugins should treat missing/expired OAuth state as recoverable configuration failure.

Required plugin methods

Both executable and JavaScript plugins are called with the same method names.

MethodRequiredPurpose
initialize(data)YesReceive settings and plugin metadata.
execute(data)YesPerform an action or main behavior.
test(data)YesReturn health (true/false or equivalent result).
shutdown(data)NoClean up before stop.

For stdio plugins that emit a complete PluginResponse, a startup configuration failure can be reported with top-level status: "requiresConfig" and a helpful message. Socket.IO and JavaScript plugins should return a clear initialization error or action result until top-level status propagation is supported on those paths.

{
"id": "init-1",
"type": "response",
"status": "requiresConfig",
"message": "Connect your account before starting this plugin.",
"data": null
}

Stdio message protocol

HyperHQ sends one JSON object per line to stdin and expects one JSON object per line on stdout.

Request

{
"id": "msg_123",
"type": "request",
"method": "initialize",
"data": {
"settings": {},
"pluginData": { "id": "my-plugin", "name": "My Plugin", "version": "1.0.0" }
},
"timestamp": 1710000000000
}

Response

{
"id": "msg_123",
"type": "response",
"data": "initialized",
"timestamp": 1710000000100
}

Error

{
"id": "msg_123",
"type": "error",
"error": { "message": "Human-readable error", "recoverable": true },
"timestamp": 1710000000100
}

JavaScript plugin shape

Current JavaScript runner creates a sandbox global named plugin. Define methods on that object; do not use old module.exports hook examples.

plugin.initialize = async (data) => {
plugin._settings = data.settings || {};
return 'initialized';
};

plugin.execute = async (data) => {
return { ok: true, action: data.action || 'default' };
};

plugin.test = async () => true;

plugin.shutdown = async () => 'ok';

Socket.IO events

Socket.IO connects to the root namespace at http://localhost:${HYPERHQ_SOCKET_PORT}. There is no /plugin namespace.

EventDirectionPayload/notes
authenticatePlugin → HyperHQFirst auth: { pluginId, challenge }. Reconnect during grace period: { pluginId, sessionToken }.
authenticatedHyperHQ → Plugin{ success, pluginId, sessionToken, serverPort, reconnected? }.
requestHyperHQ → PluginCore method request { id, type, method, data, timestamp }.
responsePlugin → HyperHQCore method response; event name is response.
request_data / requestDataPlugin → HyperHQData request. Both names are accepted.
data_response / dataResponseHyperHQ → PluginData response. HyperHQ emits both names for compatibility.
subscribeEventsPlugin → HyperHQArray of event names to join, e.g. ["gameLaunched"].
eventsSubscribedHyperHQ → PluginSubscription acknowledgement.
hyperHqEventHyperHQ → PluginBroadcast payload { type, data, timestamp }.
requestFilePlugin → HyperHQRequest a file from the plugin directory.
fileDataHyperHQ → PluginBase64 file payload or error.
statusUpdatePlugin → HyperHQOptional progress/status for long-running work.

Socket.IO environment variables

Socket.IO plugins launched by HyperHQ receive:

VariableDescription
HYPERHQ_PLUGIN_IDManifest ID.
HYPERHQ_PLUGIN_NAMEManifest display name.
HYPERHQ_PLUGIN_VERSIONManifest version.
HYPERHQ_SOCKET_PORTActual Socket.IO port. Always read this instead of hardcoding.
HYPERHQ_AUTH_CHALLENGEOne-time challenge for initial authentication.
PLUGIN_SETTINGSLauncher-provided compatibility value. Do not treat it as authoritative; use initialize(data).settings.

The auth challenge is one-time-use and expires after 30 seconds. After a disconnect, HyperHQ allows a 30-second reconnect grace period using the existing session token via authenticate({ pluginId, sessionToken }).

Socket.IO data request methods

Request format:

socket.emit('requestData', {
method: 'getSystems',
params: {},
requestId: 'req-1',
sessionToken
});

socket.on('dataResponse', (response) => {
if (response.requestId === 'req-1' && response.success) {
console.log(response.data);
}
});
MethodBehaviorNotes
getSystemsBlockingReturns configured systems.
getMediaFoldersBlockingRequest media folders. Common params include systemReferenceId/system identifier and mediaTypes.
getGamesForSystemBlockingAccepts flexible system identifiers used by the UI/service layer, such as system ID, reference ID, or name when supported by the frontend handler.
createSystemFire-and-forgetDispatches system creation to the frontend; returns immediate acknowledgement.
createEmulatorFire-and-forgetDispatches emulator creation.
addGamesFire-and-forgetPayload can include a games array with names, filenames/paths, reference IDs, metadata, and enabled state.
removeGamesFire-and-forgetDispatches game removal. Payload aliases are handled by the frontend integration; include stable identifiers where possible (gameIds, referenceIds, filenames/paths).
launchGameFire-and-forgetDispatches game launch by game identifier.

Blocking calls wait for a frontend response and time out after 30 seconds. Fire-and-forget calls only confirm that HyperHQ accepted and queued the request.

requestFile constraints

requestFile is intentionally scoped to packaged plugin files.

socket.emit('requestFile', {
requestId: 'asset-1',
filePath: 'assets/schema.json',
sessionToken
});

socket.on('fileData', (msg) => {
if (msg.success) {
const bytes = Buffer.from(msg.data, 'base64');
}
});

Constraints:

  • sessionToken is required and strictly validated.
  • Files must resolve inside the plugin's installation directory.
  • Directory traversal and files outside the plugin directory are rejected.
  • Maximum file size is 10 MB.
  • Response data is base64 encoded.

Actions and onboarding wizards

Plugins can expose UI actions and manifest-driven setup wizards.

{
"actions": [
{
"id": "setup",
"label": "Setup Account",
"description": "Connect and configure this plugin",
"icon": "settings",
"type": "wizard",
"wizard_id": "first-run"
}
],
"onboarding": {
"wizards": [
{
"id": "first-run",
"title": "Set up My Plugin",
"autoStart": "first-run",
"skippable": false,
"steps": [
{ "id": "welcome", "type": "info", "title": "Welcome", "description": "This wizard configures the plugin." },
{ "id": "account", "type": "oauth", "title": "Connect Account", "oauth": { "provider": "example", "settingKey": "account" } },
{ "id": "options", "type": "form", "title": "Options", "form": [ { "key": "sync", "type": "boolean", "label": "Sync on startup", "defaultValue": true } ] }
]
}
]
}
}

Wizard step types: info, oauth, action, async-action, selection-list, and form.

Leaderboard provider plugins

A plugin that provides score/leaderboard data should advertise the leaderboard-provider capability:

{
"capabilities": [
{ "name": "leaderboard-provider", "description": "Provides leaderboards and score submission", "required": false }
]
}

Provider method names routed by HyperHQ:

MethodPurpose
leaderboard.getProviderMetadataReturn provider identity and capabilities.
leaderboard.getLeaderboardFetch leaderboard entries for a game/context.
leaderboard.getScoreFetch one user's score for a game/context.
leaderboard.submitScoreSubmit a score when supported.

Marketplace/version metadata

Marketplace records can expose multiple versions per plugin. Version entries include optional channel and platform fields, so a plugin may publish separate stable/beta and platform-specific builds.

Storage locations

  • Installed plugin packages are stored under the configured HyperSpin installation's plugins/ folder.
  • Plugin-owned cached data and downloaded assets should use _hsm/Plugins/{PluginName}/.
  • Do not rely on a %PLUGIN_DATA% magic path in manifests; use real paths provided by your plugin settings or HyperHQ data APIs.