subconsciousnetwork / noosphere

Noosphere is a protocol for thought; let's discover it together!
Apache License 2.0
663 stars 40 forks source link

Explore extensibility in noosphere via WASM #494

Open jsantell opened 1 year ago

jsantell commented 1 year ago

We want to have an avenue for extensibility in Noosphere across multiple domains, both for internal development and user configuration, including:

These "plugins" would run inside of libnoosphere, potentially running on a desktop, in Subconscious, or in gateway servers. Broadly, this is a good fit for WASM: Well-defined interfaces for modules running in a sandbox.

Goals

Example Noosphere Interface

Illustrative FFI to support decoders as a global plugin. Geists, for example, may be better scoped per sphere.

// Load a decoder module from `bytes`. Returns
// the CID of the decoder.
char* ns_decoders_load(
  ns_noosphere_t const* noosphere,
  slice_ref_uint8_t bytes,
  ns_error_t** error_out);

// Load a decoder module from `cid`. Returns
// the CID of the decoder
char* ns_decoders_load_from_cid(
  ns_noosphere_t const* noosphere,
  char* cid,
  ns_error_t** error_out);

// Get an array of all currently loaded decoders as CIDs.
slice_boxed_char_ptr_t ns_decoders_list(
  ns_noosphere_t const* noosphere,
  ns_error_t** error_out);

// Gets the name of a decoder(?) or other meta.
char* ns_decoders_get(
  ns_noosphere_t const* noosphere,
  char* cid,
  ns_error_t** error_out);

// Unloads a decoder.
void ns_decoders_unload(
  ns_noosphere_t const* noosphere,
  char* cid,
  ns_error_t** error_out);

iOS

With modules being well-known at compile time, arguably running these modules does not fundamentally change features (e.g. a new decoder type), and more restricted than existing scripting apps.

Alternatively:

Implementation

Ideally, we'd have a reusable way of hooking into a plugin definition and building rust components around in within noosphere. Something like:

pub trait NoosphereDecoder {
  fn get_slashlinks(&self, sphere: &Sphere) -> Vec<Slashlink>
}

In lieu of some macro work, an earlier checkpoint may just wrap an underlying wasm engine with well known interfaces:

let module = engine.load_by_cid(cid)?; // load a markdown decoder
engine.call<(&Sphere), Vec<Slashlink>>("get_slashlinks", &sphere);

Component Model

The Web Assembly Components Model proposal defines ways of modularizing WASM artifacts and enabling them to link to each other. A related project, wit-bindgen, enables guest (e.g. decoder) developers to generate bindings from a IDL-like .wit file. Only wasmtime appears to support the component model out of the box.

While components seem preferable for plugin developers (e.g. someone writing a geist or decoder), and allow us to distribute some companion tools (e.g. a wit IDL), they're only implemented currently in wasmtime, and still only a (promising) proposal.

Runtimes

Potential runtimes and their support for rust bindings, the component model, and different execution modes. Some runtimes have support for "web"/"JS", but not quite in the environment we're looking for -- we want libnoosphere as wasm natively loading another wasm artifact, rather than our plugins being ingested by JS/node directly.

Runtime Bindings Components JIT AOT Interpreter
wasmtime :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_multiplication_x: :heavy_multiplication_x:
wasmer :heavy_check_mark: :heavy_multiplication_x: :heavy_check_mark: :heavy_check_mark: :heavy_multiplication_x:
wamr -sys only :heavy_multiplication_x: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
wasmedge :heavy_check_mark: :heavy_multiplication_x: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
wasm3 :heavy_check_mark: :heavy_multiplication_x: :heavy_multiplication_x: :heavy_multiplication_x: :heavy_check_mark:

Result

wasmtime seems to be the most feature complete runtime with a close relationship with corresponding spec writers. With iOS JIT and dynamic loading restrictions, there's still no guarantee that building out an interpreted runtime will pass Apple's muster, and some workarounds are palatable. wasmedge may provide support for all platforms we want, but no component model support (and preferred wasmtime's integration exp so far).

For now, moving forward with wasmtime as non-components so that we can attempt to support other runtimes (iOS/Web) in the future without requiring component support, and have some implementation for iOS that may be rejected by the app store, but can start a conversation, and at least have something like a decoder implementation running via CLI/gateways as we explore the extensible interface.

cdata commented 1 year ago

Linking for posterity this adjacent issue pertaining to alternate content-type support: https://github.com/subconsciousnetwork/noosphere/issues/50