Extending HiveMind Core

August 11, 2026 · View on GitHub

HiveMind Core is assembled from plugins. Every backend concern (database, transport, agent, binary handling, and admission control) is a separate installable Python package that registers itself under a setuptools entry-point group. It needs no changes to hivemind-core itself.

The abstract base classes are defined in hivemind-plugin-manager. This page summarizes how to wire a plugin to hivemind-core. For the full ABC contracts and complete walkthroughs, see the plugin-manager docs.


Database Plugin

Store client credentials anywhere: SQLite, PostgreSQL, a REST API, an in-memory dict.

Entry-point group: hivemind.database

Base class: hivemind_plugin_manager.database.AbstractDB (local) or AbstractRemoteDB (network-backed)

Minimal contract:

from dataclasses import dataclass
from typing import List, Iterable, Union
from hivemind_plugin_manager.database import AbstractDB, Client

@dataclass
class MyDB(AbstractDB):
    def add_item(self, client: Client) -> bool: ...
    def search_by_value(self, key, val) -> List[Client]: ...
    def __len__(self) -> int: ...
    def __iter__(self) -> Iterable[Client]: ...

Schema migrations: Override migrate(from_version: int) to handle data migrations when SCHEMA_VERSION bumps. Implementations must be idempotent. The base class SCHEMA_VERSION = 2; backends that skip migrate keep working via property shims on Client.

Registration:

[project.entry-points."hivemind.database"]
my-db-plugin = "my_package.db:MyDB"

Activation in server.json:

"database": {
  "module": "my-db-plugin",
  "my-db-plugin": {"host": "..."}
}

Full guide: hivemind-plugin-manager/docs/plugins/database.md


Network Protocol Plugin

Transport HiveMessages over any wire: MQTT, Unix sockets, raw TCP, Usenet.

Entry-point group: hivemind.network.protocol

Base class: hivemind_plugin_manager.protocols.NetworkProtocol

Minimal contract:

from dataclasses import dataclass
from hivemind_plugin_manager.protocols import NetworkProtocol

@dataclass
class MyNetworkProtocol(NetworkProtocol):
    def run(self):
        # Block; accept connections, call self.hm_protocol.handle_message(...)
        # for each received HiveMessage.
        ...

Registration:

[project.entry-points."hivemind.network.protocol"]
my-transport-plugin = "my_package.transport:MyNetworkProtocol"

Activation in server.json:

"network_protocol": {
  "my-transport-plugin": {"host": "0.0.0.0", "port": 9000}
}

Multiple network plugins run simultaneously. Full guide: hivemind-plugin-manager/docs/plugins/network-protocol.md


Agent Protocol Plugin

Route HiveMessage payloads to any AI backend.

Entry-point group: hivemind.agent.protocol

Base class: hivemind_plugin_manager.protocols.AgentProtocol

Mandatory method:

def natural_language_query(self, utterance: str,
                           lang: str) -> Iterator[Optional[str]]:
    # Yield answer chunks; yield None as end-of-query sentinel.
    ...

Yield None immediately if the agent has no answer (triggers upstream escalation).

Entry point hivemind-core calls:

def answer_query(self, utterance: str, lang: str,
                 client: Optional[HiveMindClientConnection] = None
                 ) -> Iterator[Optional[str]]:
    ...

The QUERY and CASCADE handlers call answer_query, not natural_language_query. The base implementation ignores client and delegates to natural_language_query, so a plugin only implements the primitive. Override answer_query when the agent needs the caller's identity, for example to dispatch to one sub-agent per access key.

Registration:

[project.entry-points."hivemind.agent.protocol"]
my-agent-plugin = "my_package.agent:MyAgentProtocol"

Activation in server.json:

"agent_protocol": {
  "module": "my-agent-plugin",
  "my-agent-plugin": {"endpoint": "http://localhost:8080"}
}

Full guide: hivemind-plugin-manager/docs/plugins/agent-protocol.md


Binary Data Handler Plugin

Process server-side audio, images, or arbitrary binary payloads.

Entry-point group: hivemind.binary.protocol

Base class: hivemind_plugin_manager.protocols.BinaryDataHandlerProtocol

Override one or more handler methods:

def handle_microphone_input(self, bin_data, sample_rate, sample_width, client): ...
def handle_stt_transcribe_request(self, bin_data, sample_rate, sample_width, lang, client): ...
def handle_stt_handle_request(self, bin_data, sample_rate, sample_width, lang, client): ...
def handle_numpy_image(self, bin_data, camera_id, client): ...
def handle_receive_tts(self, bin_data, utterance, lang, file_name, client): ...
def handle_receive_file(self, bin_data, file_name, client): ...

The default for each is a logged no-op (warn + ignore).

Registration:

[project.entry-points."hivemind.binary.protocol"]
my-audio-plugin = "my_package.audio:MyAudioProtocol"

Activation in server.json:

"binary_protocol": {"module": "my-audio-plugin"}

Full guide: hivemind-plugin-manager/docs/plugins/binary-protocol.md


Policy Plugin

Implement admission-control, rate limiting, metadata injection, or audit logging.

Entry-point group: hivemind.policy

Base class: hivemind_plugin_manager.policy.PolicyPlugin

from dataclasses import dataclass
from hivemind_plugin_manager.policy import PolicyPlugin, Verdict

@dataclass
class MyPolicy(PolicyPlugin):
    def review(self, message, client) -> Verdict:
        if self._should_deny(message, client):
            return Verdict.deny("my_reason", "human-readable explanation")
        return Verdict.allow()

    def observe(self, message, client) -> None:
        # Called after a message is emitted. For counters/audit. Must not raise.
        pass

Registration:

[project.entry-points."hivemind.policy"]
my-policy = "my_package.policy:MyPolicy"

Activation in server.json:

"policy": {
  "chain": [
    {"module": "hivemind-ovos-agent-policy"},
    {"module": "my-policy", "config": {"limit": 500}}
  ]
}

MessageTypeACLPolicy and DefaultSessionPolicy are implicit and always first. Do not list them. Full guide: docs/policy.md and hivemind-plugin-manager/docs/plugins/policy.md


← Plugins · Home