Nodes and Workers
Ockam Nodes and Workers decouple applications from the host environment and enable simple interfaces for stateful and asynchronous message-based protocols.
At Ockam’s core are a collection of cryptographic and messaging protocols. These protocols make it possible to create private and secure by design applications that provide end-to-end application layer trust it data.
Ockam is designed to make these powerful protocols easy and safe to use in any application environment, from highly scalable cloud services to tiny battery operated microcontroller based devices.
However, many of these protocols require multiple steps and have complicated internal state that must be managed with care. It can be quite challenging to make them simple to use, secure, and platform independent.
Ockam Nodes and Workers help hide this complexity and decouple from the host environment - to provide simple interfaces for stateful and asynchronous message-based protocols.
Nodes
An Ockam Node is any program that can interact with other Ockam Nodes using various Ockam Protocols like Ockam Routing and Ockam Secure Channels.
Using the Ockam Rust crates, you can easily turn any application into a lightweight Ockam Node. This flexible approach allows your to build secure by design applications that can run efficiently on tiny microcontrollers or scale horizontally in cloud environments.
Rust based Ockam Nodes run very lightweight, concurrent, stateful actors called Ockam Workers. Using Ockam Routing, a node can deliver messages from one worker to another local worker. Using Ockam Transports, nodes can also route messages to workers on other remote nodes.
A node requires an asynchronous runtime to concurrently execute workers. The default Ockam Node implementation in Rust uses tokio
, a popular asynchronous runtime in the Rust ecosystem. We also support Ockam Node implementations for various no_std
embedded targets.
Create a node
The first thing any Ockam rust program must do is initialize and start an Ockam node. This setup can be done manually but the most convenient way is to use the #[ockam::node]
attribute that injects the initialization code. It creates the asynchronous environment, initializes worker management, sets up routing and initializes the node context.
For your new node, create a new file at examples/01-node.rs
in your hello_ockam
project:
Add the following code to this file:
Here we add the #[ockam::node]
attribute to an async
main function that receives the node execution context as a parameter and returns ockam::Result
which helps make our error reporting better.
As soon as the main function starts, we use ctx.stop()
to immediately stop the node that was just started. If we don't add this line, the node will run forever.
To run the node program:
The clear
command is used to clear the terminal before running the program. The OCKAM_LOG=none
environment variable is used to disable logging. You can remove this to see the logs.
This will download various dependencies, compile and then run our code. When it runs, you'll see colorized output showing that the node starts up and then shuts down immediately 🎉.
Workers
Ockam Nodes run very lightweight, concurrent, and stateful actors called Ockam Workers.
When a worker is started on a node, it is given one or more addresses. The node maintains a mailbox for each address and whenever a message arrives for a specific address it delivers that message to the corresponding registered worker.
Workers can handle messages from other workers running on the same or a different node. In response to a message, an worker can: make local decisions, change its internal state, create more workers, or send more messages to other workers running on the same or a different node.
Above we've created our first node, now let's create a new worker, send it a message, and receive a reply.
Echoer worker
To create a worker, we create a struct that can optionally have some fields to store the worker's internal state. If the worker is stateless, it can be defined as a field-less unit struct.
This struct:
Must implement the
ockam::Worker
trait.Must have the
#[ockam::worker]
attribute on the Worker trait implementationMust define two associated types
Context
andMessage
The
Context
type is usually set toockam::Context
which is provided by the node implementation.The
Message
type must be set to the type of message the worker wishes to handle.
For a new Echoer
worker, create a new file at src/echoer.rs
in your hello_ockam project. We're creating this inside the src
directory so we can easily reuse the Echoer
in other examples that we'll write later in this guide:
Add the following code to this file:
Note that we define the Message
associated type of the worker as String
, which specifies that this worker expects to handle String
messages. We then go on to define a handle_message(..)
function that will be called whenever a new message arrives for this worker.
In the Echoer's handle_message(..)
, we print any incoming message, along with the address of the Echoer
. We then take the body of the incoming message and echo it back on its return route (more about routes soon).
To make this Echoer type accessible to our main program, export it from src/lib.rs
file by adding the following to it:
App worker
When a new node starts and calls an async
main function, it turns that function into a worker with address of "app"
. This makes it easy to send and receive messages from the main function (i.e the "app"
worker).
In the code below, we start a new Echoer
worker at address "echoer"
, send this "echoer"
a message "Hello Ockam!"
and then wait to receive a String
reply back from the "echoer"
.
Create a new file at:
Add the following code to this file:
To run this new node program:
You'll see console output that shows "Hello Ockam!"
received by the "echoer"
and then an echo of it received by the "app"
.
Message Flow
The message flow looked like this:
Next, let’s explore how Ockam’s Application Layer Routing enables us to create protocols that provide end-to-end security and privacy guarantees.
Last updated