Skip to main content

A Tour of WebAssembly Interface Types (wit) Specification

Welcome to Cosmonic's whirlwind tour of wit!

Jargon Decoder Ring

Before going into more detail on the various aspects of the component model and wit, we'll define a few important terms that you will not only see in our documentation, but are everywhere in the Bytecode Alliance documentation and GitHub repositories. There's a nice distillation that can help keep all of this new and confusing terminology in perspective as you read through the rest of this documentation:

Packages contain interfaces and worlds, Worlds give directions to interfaces, Interfaces contain functions and types.

You'll use packages, worlds, and interfaces to describe your components and their requirements. You'll be describing them in wit, or WebAssembly Interface Types. Don't worry if these terms are new to you, you'll get used to them through exposure.

Packages

wit starts with packages, which are collections of interfaces and worlds.

Packages are a logical scoping of interfaces and worlds. Packages need to be unique within the global ecosystem of packages, and so a package identifier has a few key identifying elements:

  • A namespace. Namespaces can be used to identify organizations, registries, or portions of well-known standards like wasi. Packages that are defined by wasmCloud for wasmCloud-specific functionality would likely use wasmcloud as the namespace, e.g. wasmcloud:messaging@0.1.0
  • A package name. The package name identifies the package within the outer scope of a namespace. For example, clocks in wasi:clocks or messaging in wasmcloud:messaging.
  • A version (optional). If the version is supplied, it needs to conform to "full" semver rules.

Wit packages are units of distribution and are usually either single .wit files or a directory containing a number of them. Importing types from one interface into another (even interfaces defined in other packages) is done via the use keyword:

package wasmcloud:sensors@0.1.0

interface types {
    enum sensor_type {
        temperature
        humidity
        pressure
    }
}

interface sensor-device {
    use types.{sensor_type}

    ...
}

The syntax for importing types from an external interface just uses the 3 elements of a package identifier, as shown below:

package cosmonic:demo

interface my-interface {
  use wasi:http/types.{request, response}
}

Here wasi is the namespace, http is the package name, and types is the interface name. The request and response are types imported from the types interface.

Interfaces

Interfaces are collections of functions and types scoped to a package. Note that interfaces are the only place in which types can be defined. They are contracts that are presumed to be satisfied by external entities. Directionality is added to interfaces as part of the world definition (discussed next).

You can use use statements, type definitions, and func definitions in a component model interface:

package cosmonic:lunarfrontiers

interface types {
  use cosmonic:eventsourcing/types.{event, ...}

  record position {
    latitude: f64,
    longitude: f64,
    altitude: f64,
    // ...
  }

  record stat {
    rover-id: u64,
    sensor-id: u64,
    stat-time: datetime,
    // ...
  }

  position-changed: func(rover-id: string, position: position) -> result<bool>
}

Whether the lunar rover is being notified of the position-changed or whether the host (or some other component) is being notified depends entirely on the world definition. Put another way, interfaces are defined without direction, and are only imported or exported by worlds.

Worlds

Worlds are top-level definitions within packages. They apply directionality to interfaces (or individual functions) and can be thought of as the complete description of a component. In fact, when generating code from wit files, the world is usually the main unit of generation.

package cosmonic:lunarfrontiers

// Produces a "rover" Wasm component
world rover {
  import cosmonic:lunarfrontiers/robots
  // ...

  export cosmonic:lunarfrontiers/botcontrol
}

In this definition, the robots interface is being imported. This means that in the final component supported by code generation, your code (not the host runtime) will be able to call functions on that interface like set-destination or turn or attack etc. The botcontrol interface is being exported, which means that the host runtime (or some other component) will be able to call functions on that interface to trigger functionality in the rover like construction-completed to notify the rover construction finished, position-changed to notify the rover that its position has changed, or shutdown to tell a rover to terminate.

The important thing here is not the domain of building lunar rovers. What's important is developing some kind of internal instinct for the flow of function calls when declared via import and export within a world.

Now that we've got the basics covered, we can get into some of the fun stuff and start modeling real-world concepts in wit.