Blog

An overview from an outcomer

luminem logo

Me and IoT

I finally started my long journey into the world of Web of Things. I have been interested to the topic in the past, but unfortunately this technology still needs some time to get traction from the industry, and from a user standpoint this is the main reason to stay away from the smart electronics world. Ok ok, this could seem a bit harsh, just let me explain.

Internet of Things (IoT) is a very fascinating field, until you realize that everyone is building its own infrastructure, with its own protocols, proprietary firmware blobs and servers. It is pretty easy to find examples, in the last few decades, of discovered in closed or unmaintained pieces software, services shutdown, and privacy data leaks. Personally, I do not like the idea that I subscribed to some kind of online service and, at some point, one of the things I just mentioned happens to that service:

  • If it stops working, I just lost my useful service. If I paid for that, this is much worse.
  • If it uses proprietary closed source code, I can just hope that no one found a zero-day vulnerability in order to gain access to their data and, as a consequence, to my data.
  • If the service is unmaintained, take the two previous points and make them even more plausible, because if no one is actively watching, who knows what could happen.

This point of view unfortunately can be applied to most of the services out there, and we can accept the risk for the benefits of the various services available. I can accept it without too much trouble.

However, if you take everything I said and you consider these sort of risks for your lamps, your fridge, your oven and, badly enough, for the gate of your house, I do not know what other people think but I surely cannot accept it. I already have got working lamps, I cannot trade the convenience of remotely control them with my smartphone with the uncertainty that some bad actors could be able to do the same. And, even worse, to collect some personal information about me, my house, and my family.

But now I am able to work on Web of Things, and I want to help improving the technology around smart devices.

Enter Web of Things

Web of Things (WoT) is a set of standards aimed to give a common way to implement smart devices to the industry. This is extremely beneficial for both users and companies, because there is no need to reinvent the wheel (how should expose the devices API? Which protocols are better for the purpose?) and, at the same time, you automatically gain a compatibility layer with all the WoT technology already available. Let me show you why.

The central concept for WoT is, as you could have expected, the Thing. This is

an abstraction of a physical or virtual entity that provides interactions to and participates in the Web of Things.[1]

This is a one-to-one relationship to the electrical devices I mentioned previously (i.e., a lamp, an oven, a fridge) and, interestingly enough, to eventual virtual devices.

The very interesting concept behind a Thing is that it is self-describing in a standard way, both human and machine readable. Here an example taken from the WoT Thing Description standard:

{
   "@context": [
      "https://www.w3.org/2019/wot/td/v1",
      {
      "cov": "http://www.example.org/coap-binding#"
      }
    ],
    "id": "urn:dev:ops:32473-WoTLamp-1234",
    "title": "MyLampThing",
    "description" : "MyLampThing uses JSON serialization",
    "securityDefinitions": {"psk_sc":{"scheme": "psk"}},
    "security": ["psk_sc"],
    "properties": {
        "status": {
            "description" : "Shows the current status of the lamp",
            "type": "string",
            "forms": [{
                "op": "readproperty",
                "href": "coaps://mylamp.example.com/status",
                "cov:methodName" : "GET" 
            }]
        }
    },
    "actions": {
        "toggle": {
            "description" : "Turn on or off the lamp",
            "forms": [{
                "href": "coaps://mylamp.example.com/toggle",
                "cov:methodName" : "POST" 
            }]
        }
    },
    "events": {
        "overheating": {
            "description" : "Lamp reaches a critical temperature (overheating)",
            "data": {"type": "string"},
            "forms": [{
                "href": "coaps://mylamp.example.com/oh",
                "cov:methodName" : "GET",
                "subprotocol" : "cov:observe" 
            }]
        }
    }
}

This is a plausible response from a smart lamp with a limited set of functionalities, expressed using a JSON-LD format. From the code it is easy to recognise the following:

  • The Thing has a unique identifier, and it replies with a human-readable description;
  • there is an explicit way of establishing a secure connection to the device (security* fields);
  • the properties are explicitly enumerated with the information to query or to set the actual value;
  • the ways to actively interact with the devices are enumerated in the actions field;
  • it is possible to subscribe to events generated by the Thing, and these are enumerated as well;
  • everything is described following the specs given by the @context field, which always includes the Thing Description and, in this example, an auxiliary vocabulary context to expose the APIs through the COAP protocol.

This approach highlights two major characteristics:

  1. The Thing has an incredible expressiveness power in terms of both capabilities and expandability. The basic vocabulary is probably enough for most of the common use cases, and at the same time it is possible to expand the possibilities using arbitrary context extensions based on a standard schema[2].
  2. The complexity of this expressiveness is not reflected on the device, but on hypothetical third-party software that aims to support a big amount of device types and protocols.

Comparing this situation with the issues of the IoT, we already see that this approach is a huge improvement, because all the problems related to closed protocols suddenly disappear. In fact, thanks to the Thing Description standard, you could easily choose different implementations to handle all your smart devices, without being tied to the software created by a specific producer for a specific set of smart devices. Moreover, this means that it is easier for WoT devices to interact one each other using links.

As already said, the Thing is not burdened from the complexity[3] of the protocol, which means that risks related to software issues can be heavily mitigated. However, this is not enough in order to have a sane WoT ecosystem, and this is why at Sifis we are developing the fundamental pieces to empower everyone to build safe and secure WoT software.

Enter Rust

Rust is a system programming language with a strong typing system, a minimal runtime and zero cost abstractions. This is the perfect fit in order to create an abstraction layer to safely build software for the WoT. Indeed, we are developing a crate (a sort of Rust library) that can be used to describe the characteristics of a Thing, taking advantage of the type system in order to catch lots of common issues at compile-time and to make impossible states impossible[4].

I want to imagine that it will be possible to write something like the following:


/// MyLampThing
///
/// MyLampThing uses JSON serialization
#[derive(WebThing)]
#[webthing(
    contexts = {
      cov = "http://www.example.org/coap-binding#"
    },
    id = "urn:dev:ops:32473-WoTLamp-1234",
    security = Security::Psk(security::Psk::default()),
    actions = [
        ToggleAction,
    ],
    events = [
        OverHeatingEvent,
    ]
)]
struct MyLampThing {
    /// Shows the current status of the lamp
    #[webthing(
        forms = [{
            op = readproperty, 
            href = "coaps://mylamp.example.com/status",
            cov:methodName = "GET"
        }]
    )]
    status: String
}

/// Turn on or off the lamp
#[derive(WebThingAction)]
#[webthing(
    forms: [{
        href: "coaps://mylamp.example.com/toggle",
        cov:methodName : "POST" 
    }]
)]
struct ToggleAction;

/// Lamp reaches a critical temperature (overheating)
#[derive(WebThingEvent)]
#[webthing(
    forms: [{
        href = "coaps://mylamp.example.com/oh",
        cov:methodName = "GET",
        subprotocol = "cov:observe" 
    }]
)]
struct OverHeatingEvent(String)

For people not familiar with Rust, this code declares three data structures with some metadata information that can be evaluated at compile-time in order to automatically generate code to extend the behavior of the structs.

This is just my idea of how things should look like, therefore it should be taken with a grain of salt. In any case, the main concept I want to express with this code is that writing Things needs to be easy in order to have compile time guarantees and, at the same time, to push toward the growth of the ecosystem. Another important point is that we would like to give the basic building blocks to the developers, without tying to a specific Web framework in order to expose the APIs.

Needless to say, reaching this goal is totally not trivial, there is a lot to consider and to take into account in order to make things work as intended. For instance, performing compile-time checks for the cov namespace involves downloading, parsing of RDF files and performing codegen in a way that independent structs can see properties like methodName.

…and more

It is important to create a solid working ground for Rust developers, but there are a lot of companies that would rather use other languages, like C or Python. Rust is extremely convenient for the interoperability with other languages, because of its support to Foreign Function Interfaces (FFI), code generation through procedural macros and third-party tools from the ecosystem. With all of this, it is possible to create different set of APIs for different languages, using, for instance, cbindgen to expose C functions and PyO3 to create a Python extension module, with cargo-c and maturin to fully streamline the experience.

Obviously, we cannot leverage compile-time checks in these cases, simply because when the Rust library is used from a different language the compilation step is already done. This only implies that all the many coherence checks are performed at runtime, which means that even in this case all the concerns about security and privacy are still addressed. The biggest change is perceived by the developers, which need to carefully check that their usage of the high-level library does not return an error at runtime. This is an acceptable compromise in order to make the Web of Things ecosystem grow in many directions and in many programming languages. 

Thanks for taking the time to read my vision. I will help Sifis to make it real, because we deserve a more secure, reliable, and resilient technology for smart electronics. In the next few years, I would like to buy some smart lamps for my family without any worries, let’s make it real.

[1] https://www.w3.org/TR/wot-thing-description ↩︎

[2] https://www.w3.org/TR/rdf-schema/ ↩︎

[3] The complex term is used with its precise meaning. Saying that something is complex does mean that there are multiple level of concepts that need to be understood in order to have a complete overview. ↩︎

[4] This concept is expressed in a talk by David Khourshid and it is often used in Elm. The concept can (and probably should) be applied in all static typed languages that are able to express this kind of constraints. Obviously, the idea can be used in Rust. ↩︎

About the author

profile-picture

Edoardo Morandi

Full-stack developer, Rust lover, and ex-bioinformatician. I constantly feel guilty for what the FOSS community gave me for two decades and more, and I would like to do my little part to pay back. Part of Luminem.