Allowing the Internet Computer to ingress, egress and execute external wasm

Hello IC Devs!

Background

For some time now, I’ve been pondering ways to leverage the Internet Computer’s unique fully on-chain ecosystem. The IC operates in a secure, isolated environment, supported by its sovereign infrastructure of node providers and nodes. While this architecture is a major strength, it can also be limiting in terms of real-world applicability.

Currently, IC interacts with “external” systems only through HTTP outcalls. While effective, these outcalls are costly, don’t scale well, and are really only designed to be used sparingly. Although the IC’s architecture is technically impressive, these distinctions may not be immediately impactful for non-technical users who want solutions they can feel in their day-to-day lives.

At the end of the day, software is just another tool to help us solve problems and manage life’s challenges. Platforms like DoorDash and Uber are ultimately tools to streamline food delivery or provide transportation services. I believe a major challenge in the Web3 space is that, while there’s a lot of technical talk about a software revolution, there’s little that the society at large can truly appreciate or benefit from in a tangible way.

The Vision

For the IC to stand out as the true next generation of software, we need to empower developers to leverage its unique capabilities in ways that make an impact. I believe a key way to achieve this is to enable direct interactions with hardware, such as IoT devices and real-world machines.

We could have entire IoT systems reporting to and being controlled by canister software. This is not only possible but, in my opinion, inevitable—and WebAssembly (Wasm) is the ideal vehicle to make it happen. With Wasm, we can bring the power of hardware interfacing into IC, unlocking new dimensions for the IC.

WebAssembly programs are organized into modules , which are the unit of deployment, loading, and compilation. A module collects definitions for types, functions, tables, memories, and globals. It can also define imports and exports. This is a very high level and basic example of how i envisioned it in motoko through a WASM library.

Example

import Wasm "mo:base/Wasm"; 
import Blob "mo:base/Blob";

actor Device {
    var wasmModule: Wasm.Module = null;

    public func initialize(wasmCode: Blob): async Text {
        wasmModule := await Wasm.install(wasmCode, null);
        "Hardware initialized successfully"
    };

    public func read(sensorId: Nat): async ?Text {
        switch wasmModule {
            case (null) null;
            case (?wasmModule) {
                let rawData = await wasmModule.call("read_sensor", [sensorId]);
                "Sensor reading: " # rawData.toText()
            }
        }
    };

    public func write(deviceId: Nat, value: Int): async Text {
        switch wasmModule {
            case (null) "Device not initialized";
            case (?wasmModule) {
                await wasmModule.call("write_output", [deviceId, value]);
                "Output written successfully"
            }
        }
    };

    public func update(newWasmCode: Blob): async Text {
        wasmModule := await Wasm.install(newWasmCode, null);
        "WASM module updated successfully"
    };
};

There are many other considerations but I assume the wasmModule variable would need to be inherently stable, and that it can be null since WASM modules can start empty. The install function would load a collection of types, functions, tables, memories, and globals, and crucially, it would define exports that act as the API, allowing us to call its functions. Setting null in install simply applies default configurations. The read and write functions are expected to be defined in these exports, with signatures that must match. The update function would load a new version of the module, though it’s unclear how this might impact stability. As this is a high-level design, many details depend on the actual implementation. Additionally, since Motoko lacks direct hardware access, we’d need to compile the WASM module from a language like Rust or C. I’m interested to see if the IC can technically support this approach.

Conclusion
I’m highly motivated to start a working group to discuss this vision and brainstorm how we can make it a reality(I will be purchasing hardware that i would like to test it on). If this idea seems interesting to you or you have thoughts on this concept, please share!

3 Likes

How do you intend to accomplish direct hardware access from a 13 node subnet distributed across the globe?

Other than the inherent 2-3 second latency I think you’ll be pleased to find you can accomplish all of this today with out a working group. Exchange your actor or the “subnet” and your module for a canister and you are there. You’ll need your device to poll an endpoint and to be able to run a proxy in your device with an IC agent, but you’ll get the behavior you are seeking.

If you want millisecond latency you need to abandon consensus and distributed computing because of the speed of light.

(Also there is a websocket endpoint that may keep you from having to do polling, but your updates are still goin to come through at 2-3s intervals.

1 Like

When I first explored this idea, I reached out to @lastmjs who gave me a similar response. I was initially fixated on achieving native hardware access, thinking it would provide a better developer experience, but I’m now seeing the practical path forward.

If I understand correctly, you’re suggesting:

  1. Create an HTTP agent using dfinity/agent that connects to either local (http://127.0.0.1:4943) or mainnet (https://ic0.app)
  2. Set up the canister interface using IDL to define my function signatures
  3. Create a polling mechanism that checks for new commands/updates
  4. Use standard hardware libraries (like onoff for GPIO) to control hardware eg:Raspberry Pi based on the canister’s responses

So instead of trying to directly access hardware from the canister, I’m essentially creating a bridge where:

Canister (Motoko) ↔ IC Agent (JavaScript) ↔ Hardware Libraries (GPIO, etc.)

Is this what you meant?

The 2-3 second latency isn’t a dealbreaker for my use case, as this can be handled with proper UI design. Given that there’s already a way to achieve this functionality, I’ll explore this approach and share what I learn.

Nailed it.

Search this forum for web sockets if your device can use those as it may save your device some compute while waiting.