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.
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.
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
multiprocessinglibrary 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
ServerBaseclass provides theget_stub()utility, and protocol implementations expose their ownget_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
ServicerBase Providesadd_p2p_handlers()
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)
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:
Be
async defAccept two parameters:
A Protobuf request message
A
P2PContextobject (provides metadata about the calling peer)
Return either:
A single Protobuf response (
Awaitable[TOutputProtobuf]), orAn async generator for streaming responses (
AsyncIterator[TOutputStream])
Expected Signature Format
Protobuf Message Examples
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
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.
Last updated