Skip to content

Hello World Full Tutorial

In this tutorial, we will develop the code example from the Hello_World Quick Start in detail.

You will learn how to write your own hyveOS application code and publish it to a Docker Container Registry.

We are going to develop it in Python — it will be asynchronous programming. If you like, you can open Python’s asyncio documentation next to this tutorial should you want to see some concepts of asynchronous programming in Python.

Final application code

You can always jump back here to view the entire application code if you want to understand where you are exactly.

main.py
main.py
import asyncio
from hyveos_sdk import Connection
async def wait_for_peers(discovery):
await asyncio.sleep(5)
async for event in discovery.discovery_events():
if event is not None:
print(f"Discovered a peer: {event}")
break
async def node(topic):
print("--- APPLICATION SCRIPT is RUNNING ---")
async with Connection() as connection:
discovery = connection.get_discovery_service()
gossip_sub = connection.get_gossip_sub_service()
own_id = await discovery.get_own_id()
print(f"Node {own_id} has started.")
while True:
await wait_for_peers(discovery)
stream = await gossip_sub.subscribe(topic)
await asyncio.sleep(12)
message = f"Hello from {own_id}"
await gossip_sub.publish(message, topic)
async for msg in stream:
if msg != message:
print(f"Node {own_id} received: {msg}")
break
async def main():
topic = "greetings"
await node(topic)
if __name__ == "__main__":
asyncio.run(main())

Setup

Directory structure

First, create the following directory tree:

  • Directoryhello_world
    • Dockerfile
    • main.py
    • pyproject.toml

Dependencies

We recommend setting up a pyproject.toml file in order to manage dependecies cleanly. It should have the following form and content: You will need Python 3.11 or above and the latest version of the hyveOS SDK. Also note the arguments in [build-system].

pyproject.toml
[tool.poetry]
name = "hello_world"
version = "0.1.0"
description = "P2P Hello World messaging from node to node"
authors = ["Author <[email protected]>"]
license = "MIT"
[tool.poetry.dependencies]
python = "^3.11"
hyveos-sdk = "^0.1.0"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

Setting up a hyveOS asyncio program

Imports

You will have to import our SDK hyveos_sdk and the asyncio library.

main.py
import asyncio
from hyveos_sdk import Connection

Entry point

The entry point will start to run the coroutine. In main(), we define the topic "greetings", which we are going to give to each node to subscribe to. The Pub/Sub mechanism will be used under "greetings" in this code example.

main.py
async def main():
topic = "greetings"
await node(topic)
if __name__ == "__main__":
asyncio.run(main())

Node discovery and message publication/subscription

Now, let’s develop the main logic of the application.

The first step is to open an async Connection() context through hyveOS, and then get the services you need.

In our case, we need two services:
discovery so that each node can discover its neighbor by id for it to know that someone will be listening if it publishes a message into the Pub/sub.
The gossip_sub service, our Pub/sub, is the second service here.

async def node(topic):
async with Connection() as connection:
discovery = connection.get_discovery_service()
gossip_sub = connection.get_gossip_sub_service()

Let us also quickly notify the programmer about the id of the current node by logging it, enabling easier troubleshooting. Always make sure to know the id of the node you currently care about!

async def node(topic):
async with Connection() as connection:
// continuing the last code block
own_id = await discovery.get_own_id()
print(f"Node {own_id} has started.")

After that, make sure each node finds its neighbor. If they are not found in this scenario with two nodes only, you might risk publishing to a topic where you cannot have made sure that the two nodes have seen each other. We loop through the rest of the section of the code. In order for the two nodes to find each other, write the function wait_for_peers() which will be awaited for each node. It will be periodically checking for a new event regarding the discovery of a new peer.

async def node(topic):
async with Connection() as connection:
// continuing the last code block
while True:
await wait_for_peers(discovery)

The function itself takes the discovery service to wait for an event. In this case, the event tells us that a new node was discovered. If you wanted to make it clear, you should check whether it was a node discovered or node lost event. However, in this small tutorial, we have a setup where they first have to establish mutual sight of each other—they weren’t in a neighbor relationship prior—, so we know the event is a node discovered event.

async def wait_for_peers(discovery):
await asyncio.sleep(5)
async for event in discovery.discovery_events():
if event is not None:
print(f"Discovered a peer: {event}")
break

Moving forward, we can finally use the gossip_sub service.

async def node(topic):
async with Connection() as connection:
// ...
// continuing the last code block
while True:
await wait_for_peers(discovery)
stream = await gossip_sub.subscribe(topic)

subscribe() returns a stream. We will later be listening on this stream for messages published into it.

Let’s finally create the own message from the own node, and publish it into the Pub/sub. Be sure to await gossip_sub.publish()!

async def node(topic):
async with Connection() as connection:
// ...
while True:
// continuing the last code block
message = f"Hello from {own_id}"
await gossip_sub.publish(message, topic)

Remember the stream from the second to last code block? Now, we need to read the previously published message (coming from the other node) from that stream. Also, you don’t want to retreive your own message from the stream so there is an if statement for that.

async def node(topic):
async with Connection() as connection:
// ...
while True:
// continuing the last code block
async for msg in stream:
if msg != message:
print(f"Node {own_id} received: {msg}")
break

So that’s how you finally would receive the message. To view the entire node(topic) method, expand the code beneath:

function node(topic)
main.py
...
async def node(topic):
print("--- APPLICATION SCRIPT is RUNNING ---")
async with Connection() as connection:
discovery = connection.get_discovery_service()
gossip_sub = connection.get_gossip_sub_service()
own_id = await discovery.get_own_id()
print(f"Node {own_id} has started.")
while True:
await wait_for_peers(discovery)
stream = await gossip_sub.subscribe(topic)
await asyncio.sleep(12)
message = f"Hello from {own_id}"
await gossip_sub.publish(message, topic)
async for msg in stream:
if msg != message:
print(f"Node {own_id} received: {msg}")
break
...

That’s it! You have implemented the main logic of our simple program. Next, we need to build a Docker image out of the code and deploy the images on each end node.

Deployment

In hyveOS, all application code will be deployed through Docker containers. This enables easy cross-operability, reuse of images, and sandboxed deployment.

Start deployment by writing the Docker file.

Dockerizing

Dockerfile
ARG BRANCH=main
FROM ghcr.io/p2p-industries/hyveos-base-python:${BRANCH}
RUN apt-get update
RUN apt-get install --yes build-essential
WORKDIR /app/hello_world
COPY poetry.lock poetry.lock
COPY pyproject.toml pyproject.toml
RUN poetry add /app/python/sdks/dist/*.whl
RUN poetry install --no-root
COPY main.py main.py
ENV PYTHONUNBUFFERED=1
CMD ["poetry", "run", "python", "main.py"]

Next up, build the image. Make sure that the working directory is hello_world. Also, publish the image to a Container Registry of your choice. We recommend using GitHub Container Registry.

Deploying and starting at the node

Terminal window
docker pull <IMAGE_AT_YOUR_CONTAINER_REGISTRY>

We are now using hyvectl to deploy the Hello World application to both nodes only from node0.

As initial deployment can take a few seconds, you want to begin with deploying to node 1, from node0.

To deploy and start the application on node1 through node 0, run on node0 first:

Terminal window
hyvectl hyve start <IMAGE_AT_YOUR_CONTAINER_REGISTRY> <ID>

where <IMAGE_AT_YOUR_CONTAINER_REGISTRY> is the link to your container registry, and <ID> is the ID of node 1, which you can find using the command

Terminal window
hyvectl whoami

at node 1.

To then start the application on node0, run on node0

Terminal window
hyvectl hyve start <IMAGE_AT_YOUR_CONTAINER_REGISTRY>

Message outputs in the consoles

As an intermediary step, you can check the output of the application on each node.

Terminal window
sudo docker container ls

On each node, the container ID of the running container is returned.

Grab <ID1> and <ID2> of the container on node0 and node1, respectively.

To see the output on your node, run the following command with the respective container ID:

Terminal window
sudo docker logs <ID1>

If you managed to work out everything smoothly, you should see outputs of the following form on each node:

Terminal window
--- APPLICATION SCRIPT is RUNNING ---
Node 12D3KooWMm4qMVTHFwCgjbxsEJdScBZ9uBpmKzSiXL4q8J6i54VP has started.
Discovered a peer: init {
peers {
peer_id: "12D3KooWHcLfA7Ux82gMz5VDZM1QaDqaoF7RpnQrDMukFe3EPNdh"
}
}
Node 12D3KooWMm4qMVTHFwCgjbxsEJdScBZ9uBpmKzSiXL4q8J6i54VP received: propagation_source {
peer_id: "12D3KooWHcLfA7Ux82gMz5VDZM1QaDqaoF7RpnQrDMukFe3EPNdh"
}
source {
peer_id: "12D3KooWHcLfA7Ux82gMz5VDZM1QaDqaoF7RpnQrDMukFe3EPNdh"
}
msg {
data {
data: "Hello from 12D3KooWHcLfA7Ux82gMz5VDZM1QaDqaoF7RpnQrDMukFe3EPNdh"
}
topic {
topic: "script/greetings"
}
}
msg_id {
id: "12D3KooWHcLfA7Ux82gMz5VDZM1QaDqaoF7RpnQrDMukFe3EPNdh1736669735872847049"
}

The messages periodically repeat starting with the Discovered a peer event.

That’s it! You have successfully used hyveOS to send a message from node1 to node0 and vice versa. Make sure you finish off with terminating the loop as seen in the next box.

Terminate the Loop

Summary

We have seen how to set up a simple application program in Python enabling the sending of messages from one node to another and vice versa. The important steps are:

  • take care of dependencies
  • write async code with the asyncio library—take care of async and await properly!
  • let the nodes discover each other
  • after that, make sure that all nodes first subscribe to the Pub/sub topic and only afterwards start publishing messages
  • deploy the application scripts with Docker
  • run the images on each node

© 2025 P2P Industries. This documentation is licensed under the MIT License.
Cookie Policy    Privacy Policy