AiHummer docs
v1.0.x
RU EN

Plugin SDK

v1.0.x · updated 2026-06-27

A plugin is described by one manifest.json. The contract that validates the manifest during development is the same one the platform enforces at install time, so a manifest that passes validate is a manifest the marketplace will accept. The aihummer plugin CLI covers the whole lifecycle: from scaffold to signing and publishing.

One manifest, one contract

A plugin has exactly one source of truth — its manifest.json. It declares the plugin kind (kind), how it is configured (config[]), its capabilities, and — for host-native services — the install[] steps and start command that the SystemdDeployer runs. Because development and installation use the same validation contract, “valid manifest” and “installable plugin” mean the same thing.

[!NOTE] The manifest describes a plugin’s contract, not its store-page name. The machine slug comes from the directory/bundle name (private side-load) or from a submission field (public) — see Publishing a plugin.

CLI

aihummer plugin bundles the development, packaging and publishing commands:

# Scaffold a manifest (kind: connector | service | openapi | mcp)
aihummer plugin init <kind> [dir]

# Validate a manifest against the install contract
aihummer plugin validate <manifest.json>

# Generate an ed25519 author key (writes <prefix>.key and <prefix>.pub)
aihummer plugin keygen [--out <prefix>]

# Build and package the plugin into a release tarball + .sha256
aihummer plugin package <dir> [--out <file>] [--slug <slug>] [--build "<cmd>"]

# Sign the release identity (slug\0version\0source_ref); with --manifest the
# signature is embedded into the manifest.signature field
aihummer plugin sign --key <priv> [--manifest <m.json>] <bundle|dir>

# Upload a private plugin into your own instance (side-load)
aihummer plugin publish --private --instance <url> --token <admin> <bundle.tar.gz>

# Open a PR to the public AiHummer/marketplace-catalog
aihummer plugin publish --public --dir <dir> --key <priv> \
  --artifact-url <url> --publisher <name> \
  --description <text> --icon <url> [--screenshot <url> ...] \
  [--channel stable|beta] [--register-key <pub>]
CommandWhat it does
init <kind> [dir]Writes a starter manifest.json for the chosen kind.
validate <m.json>Validates the manifest with the same contract as install.
keygenGenerates the author key pair: .key (private, keep secret) and .pub, prints the key id.
package <dir>Builds (opt. --build) and packs into <slug>-<version>.tar.gz with a --strip-components=1 layout, writes .sha256. Never packs .env, *.key, node_modules, .git.
sign --key <priv>Signs the release identity; prints the signature and key id; with --manifest embeds the signature into the manifest.
publish --privateUploads a bundle to your instance’s POST /v1/admin/modules/upload.
publish --publicOpens a PR to AiHummer/marketplace-catalog.

Both publishing modes are detailed on Publishing a plugin.

Manifest fields

Whether a field is required depends on the kind and on whether the plugin is public. Base and identity fields:

FieldTypeRequiredPurpose
kindstringalwaysKind: connector | service | openapi | mcp.
versionstringyesPlugin version (semver), e.g. 1.0.0.
contractstringfor channelsContract ID, e.g. aihummer.channel.v1.
scopestringnoAccess model: shared (default) or personal.
capabilitiesstring[]noDeclared capabilities.
configobject[]noConfig form fields; each needs key, plus label, secret, required.
oauthobjectnoOAuth2 (authorize_url, token_url, scopes[]) to connect a user’s account.
signaturestringwhen signedbase64 ed25519 signature over the release identity (embedded by sign).

Kind-specific fields — exactly one block is filled depending on kind:

FieldFor kindRequiredPurpose
host_native.exec_startconnector, serviceyesCommand that runs the long-lived service.
host_native.runtimeconnector, service, mcpnonode | python | binary.
host_native.installconnector, service, mcpnoInstall steps (array of shell commands), run on the host after extraction.
host_native.portconnector, servicenoPreferred TCP port (the deployer may reassign via $PORT).
host_native.health_pathconnector, servicenoHealth-check path (default /healthz).
openapi.spec_urlopenapiyesURL of the OpenAPI 3.x spec.
openapi.base_urlopenapinoOverride servers[0].url.
openapi.allowed_hostsopenapinoEgress allowlist for the synthesized tools.
openapi.authopenapinoMap securityScheme → secret name.
openapi.tool_prefixopenapinoTool-name prefix.
mcp.transportmcpyesstdio or http.
mcp.command / mcp.argsmcp (stdio)yes for stdioServer executable and arguments.
mcp.urlmcp (http)yes for httpMCP endpoint URL.
mcp.auth_header / mcp.secret_token_keymcp (http)noHeader and secret key for the bearer token.

Store-page and identity fields (for public plugins)

To enter the public catalog, a manifest must carry the publisher identity and store-page fields. A private side-load needs none of these — such a plugin is trusted at the instance level.

FieldTypeRequiredPurpose
visibilitystringnopublic | private | unlisted. Empty = legacy/first-party (no identity requirement).
publisherstringfor publicPublisher namespace, ^[a-z0-9][a-z0-9-]{1,38}$. Public slugs are named @publisher/slug.
publisher_key_idstringfor publickey id of the key the artifact is signed with.
descriptionstringfor publicStore-page blurb in the catalog.
iconstringfor publicPlugin icon: an https:// URL or a data: URI.
screenshotsstring[]noStore-page screenshots (array of https:// URLs; each non-empty).

[!TIP] Run aihummer plugin validate before every publish. The install and validation contract are identical, so a manifest that passes locally will be accepted both by the marketplace deployer and by the public catalog validator.

Minimal manifests

A service scaffold (what aihummer plugin init service writes):

{
  "version": "1.0.0",
  "kind": "service",
  "scope": "shared",
  "contract": "aihummer.channel.v1",
  "host_native": {
    "runtime": "node",
    "install": ["npm ci --omit=dev"],
    "exec_start": "node dist/main.js",
    "port": 8800,
    "health_path": "/healthz"
  },
  "config": [
    { "key": "api_token", "label": "API token", "secret": true, "required": true }
  ]
}

A zero-code openapi manifest is even shorter — it just points at the spec:

{
  "version": "1.0.0",
  "kind": "openapi",
  "scope": "shared",
  "openapi": {
    "spec_url": "https://api.example.com/openapi.json",
    "tool_prefix": "example_",
    "allowed_hosts": ["api.example.com"],
    "auth": { "bearerAuth": "api_token" }
  },
  "config": [
    { "key": "api_token", "label": "API token", "secret": true, "required": true }
  ]
}

An mcp manifest (stdio transport):

{
  "version": "1.0.0",
  "kind": "mcp",
  "scope": "shared",
  "host_native": { "runtime": "node", "install": ["npm ci --omit=dev"] },
  "mcp": { "transport": "stdio", "command": "node", "args": ["server.js"] }
}

From manifest to marketplace

After validation, a plugin is packaged (package), signed (sign) and published in one of two modes:

  • Private (for yourself) — side-load into your instance via the Admin UI or publish --private. The artifact never leaves the instance.
  • Public (for everyone)publish --public opens a PR to the public catalog; after review AiHummer counter-signs the release and publishes it to the community catalog.

See Publishing a plugin for the full walkthrough.

Where next