From Ouroboros
Jump to navigation Jump to search

The python API allows you to write Ouroboros-native programs in Python (>=3.4). It is available as a separate repository. You need Ouroboros installed before installing PyOuroboros. To download and install PyOuroboros (virtual environment recommended)

$ git clone
$ cd pyouroboros
$ python -m venv ./venv
$ source venv/bin/activate
$ pip install --upgrade pip
$ pip install .

Basic Usage

import the Ouroboros dev library:

from import *

On the server side, Accepting a flow:

f = flow_accept()

returns a new allocated flow object.

Client side: Allocating a flow to a certain name:

f = flow_alloc("name")

returns a new allocated Flow object.


f = flow_join("name")

returns a new allocated Flow object to a broadcast layer.

When a flow is not needed anymore, it can be deallocated:


To avoid having to call dealloc(), you can also use the with statement:

with flow_alloc("dst") as f:

After the flow is deallocated, it is not readable or writeable anymore.


will allocate a new flow for an existing Flow object.

To read / write from a flow:             # read up to count bytes and return bytes
f.readline(count)         # read up to count characters as a string
f.write(buf, count)       # write up to count bytes from buffer
f.writeline(str, count)   # write up to count characters from string

Quality of Service (QoS)

You can specify a QoSSpec for flow allocation. Some of these parameters are still experimental and will change.

    delay:        In ms, default 1000s
    bandwidth:    In bits / s, default 0
    availability: Class of 9s, default 0
    loss:         Packet loss in ppm, default MILLION
    ber:          Bit error rate, errors per billion bits. default BILLION
    in_order:     In-order delivery, enables FRCT, default 0
    max_gap:      Maximum interruption in ms, default MILLION
    cypher_s:     Requested encryption strength in bits # will change
    timeout:      Timeout for the flow to declare the peer dead

For instance,

qos = QoSSpec(loss=0, cypher_s=256)
f = flow_alloc("name", qos)

will create a new flow with FRCP retransmission enabled and encrypted using a 256-bit ECDHE-AES-SHA3 cypher. The number of encryption options will further expand as the prototype matures.

Manipulating flows

A number of methods are currently available for how to interact with Flow. This will further expand as the prototype matures.

f.set_snd_timeout(0.5) # set timeout for blocking write
f.set_rcv_timeout(1.0) # set timeout for blocking read
f.get_snd_timeout()    # get timeout for blocking write
f.get_rcv_timeout()    # get timeout for blocking read
f.get_qos()            # get the QoSSpec for this flow
f.get_rx_queue_len()   # get the number of packets in the rx buffer
f.get_tx_queue_len()   # get the number of packets in the tx buffer
f.set_flags(flags)     # set a number of flags for this flow
f.get_flags()          # get the flags for this flow

The following flags are specified as an enum FlowProperties:

class FlowProperties(IntFlag):

See the Ouroboros fccntl documentation for more details.

man fccntl

Event API

Multiple flows can be monitored for activity in parallel using FlowSet and FEventQueue objects.

FlowSets allow grouping a bunch of Flow objects together to listen for activity. It can be constructed with an optional list of Flows, or flows can be added or removed using the following methods:

fset = FlowSet() # create a flow set,
fset.add(f)      # add a Flow 'f' to this set
fset.remove(f)   # remove a Flow 'f' from this set      # remove all Flows in this set

An FEventQueue stores pending events on flows.

The event types are defined as follows:

class FEventType(IntFlag):

and can be obtained by calling the next method:

    f, t = # Return active flow 'f' and type of event 't'

An FEventQueue is populated from a FlowSet.

fq = FEventQueue()             # Create an eventqueue
fset = FlowSet([f1, f2, f3])   # Create a new set with a couple of Flow objects
fset.wait(fq, timeo=1.0)       # Wait for 1 second or until event
while f, t =
    if t == FEventType.FlowPkt:
        msg = f.readline()

A flow_set must be destroyed when it goes out of scope. To avoid having to call destroy, Python’s with statement can be used:

fq = FEventQueue()
with FlowSet([f]) as fset:
f2, t =
if t == FEventType.FlowPkt:
    line = f2.readline()


Some example code is in the repository’s examples folder.

The following example is a clone of the oecho program in Python. The client opens a flow to oecho and sends a brief message. The server will echo this message back to the client.

from import *
import argparse

def client():
    with flow_alloc("oecho") as f:
        f.writeline("Hello, PyOuroboros!")

def server():
    print("Starting the server.")
    while True:
        with flow_accept() as f:
            print("New flow.")
            line = f.readline()
            print("Message from client is " + line)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='A simple echo client/server')
    parser.add_argument('-l', '--listen', help='run as a server', action='store_true')
    args = parser.parse_args()
    server() if args.listen is True else client()

Running it is just the same as the binary program, register the name “oecho”, bind the server to “oecho” (you can even bind both the C and Python programs at the same time), and allocating a flow should reach the server. For a local layer

$ irm i b t local n local l local
$ irm n r oecho l local
$ irm b prog ./ n oecho
$ ./ -l &
# or:
# python -l
$ ./


pyOuorboros is LGPLv2.1. The examples are 3-clause BSD.