Protocols

Custom Protocols define the core logic of your subnet — they are the building blocks that power how nodes communicate, coordinate, and perform general or distributed tasks.

As a subnet developer, protocols are where you implement the application-layer behavior for your decentralized AI system. Whether you're building a model inference engine, a validator network, or a task delegation system, protocols are where the logic lives.

A subnet can have one or multiple protocols, and they can interact with each other.

Each protocol should have a parent and child process, which can be accomplished using the Python builtins multiprocessing library.

The template includes the DHT Protocol built in and should not be altered without P2P expertise. The DHT Protocol is the native protocol of the decentralized network, which handles the core security, storage, and communication logic, and more.

Subnet builders will create custom protocols tailored to the specific use case of the subnet, such as inference or training.

Skip to view the example protocol.

What a Protocol Does

Protocols register RPC methods to the P2P network and expose them to other nodes, enabling nodes to call these RPC methods on each other in a decentralized manner.

If a subnet's use case doesn't require peers to communicate with each other, protocols are not required to register RPC methods.

Structure of a Protocol

Each protocol typically consists of two parts:

  • Parent Process (Main Process) – Manages the core application logic, P2P interactions, and spawns child processes.

  • Child Process (Worker Process) – Handles compute-intensive or blocking tasks (e.g., model inference), often run using Python's multiprocessing library to avoid blocking the event loop.

RPC Registration

Protocols register RPC (Remote Procedure Call) methods that allow other nodes in the subnet to interact with them.

  • These RPC methods are registered with the DHT and can be called remotely using get_stub().

    • All methods in the protocol class that start with rpc_* will be registered. See RPC methods formatting below for more information on proper formatting.

  • The ServerBase class provides the get_stub() utility, and protocol implementations expose their own get_server_stub() for convenience.

  • These RPCs can be:

    • One-shot methods that return data or acknowledge a command.

    • Streaming methods that yield results over time (async generators).

Example use cases include and are not limited to:

  • Submitting inference jobs

  • Fetching model outputs or scores

  • Notifying peers of new tasks or events

  • Automating tasks

  • Requesting data from a node


ServicerBase

Every protocol in a Hypertensor subnet must subclass ServicerBase, which acts as the foundation for defining and handling P2P RPC methods.

What ServicerBase Provides

add_p2p_handlers()

This method registers all rpc_* methods defined in your protocol as RPC endpoints. It inspects the method signatures using type annotations to automatically infer the Protobuf request and response types.

Once registered, these methods become callable by other nodes in the subnet via the DHT-based P2P layer.

🔧 Note: You must call add_p2p_handlers() from within the child process where the protocol is running. Otherwise, the handlers won't be bound correctly.

get_stub(p2p, peer)

This method creates a stub object that exposes all the rpc_* methods of a remote peer.

  • Calling a method on the stub translates into a remote RPC call to the target peer.

  • Under the hood, it uses the same method names and protobuf interfaces as the local implementation.

🔐 Best practice: Always wrap get_stub() with an authentication layer (e.g., proof-of-stake, signature verification, etc.) to ensure the identity and trustworthiness of the peer you're interacting with.

RPC Methods Formatting

When defining RPC methods for your protocol, they must follow a specific signature format that is compatible with add_p2p_handlers() from ServicerBase.

Each rpc_* method must:

  1. Be async def

  2. Accept two parameters:

    • A Protobuf request message

    • A P2PContext object (provides metadata about the calling peer)

  3. Return either:

    • A single Protobuf response (Awaitable[TOutputProtobuf]), or

    • An async generator for streaming responses (AsyncIterator[TOutputStream])

Expected Signature Format

See full examples below


Protobuf Message Examples

Learn more about protobufs.

All messages between peers must have a Protobuf message.

See RPC Method Examples for how to create a function to have a peer request another peer to perform and respond to the request.

See Peer-to-peer Communication for how to request a peer to perform a task.

Here's an example of request/response Protobuf messages used in inference:


Subnets should have mechanisms for nodes to interact with each other, ensuring that nodes can be verified by other nodes, run automated tasks, and make requests to each other, among other purposes.

To expose methods to the DHT for nodes to call on each other, format those functions using rpc_*. Each rpc_* method should have a corresponding function that calls them (See example below).

RPC Method Examples

Each rpc_* method in the protocol must abide by specific formats.

Examples

Unary (Single Request → Single Response)

Streaming (Single Request → Streamed Responses)

Streaming (Stream Request → Streamed Responses)

💡 Ensure your Protobuf messages are imported and type-annotated correctly. The system relies on type hints to automatically register and route handlers via add_p2p_handlers().

Peer-to-peer Communication

To call a method on a peer, you must be a node in the P2P network (subnet).

Calling from within the protocol

Calling from outside the protocol

To call the exposed RPC methods from a protocol, outside of the protocol, the protocol itself should have a @classmethod method that returns the RPC methods of the peer.

Each protocol that needs to be accessed from areas other than from within the protocol should have a method for getting the server's stub, such as:

Calling from outside the protocol example:


Protocol Example

See the full example.

Here is an example of a subnet that allows nodes to stream data from one another:

In this example, hosters serve the model and can run inference when nodes use call_inference_stream on a hoster peer. The call_inference_stream will get the stub of the peer and call rpc_inference_stream on the hoster peer, the hoster will then perform an inference stream and stream the results back to the calling node.

The model is loaded in the child process to avoid a deadlock with CUDA.

Last updated