From 01a86244fdb2c889477ce7db7dee45fa7f128cad Mon Sep 17 00:00:00 2001
From: guest271314
Date: Mon, 17 Apr 2023 09:04:15 -0700
Subject: [PATCH 1/8] Include local use cases; remove redundant "service" (#10)
Include local use cases; remove redundant use of the term "service".
---
README.md | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index f66d066..4ab4837 100644
--- a/README.md
+++ b/README.md
@@ -28,11 +28,18 @@ The messaging interfaces aim to provide a generic and flexible way for producers
### Goals
-The messaging service interfaces aim to address the need for a standard way to handle message-based communication in modern software systems. In complex software systems, different components or even different applications need to communicate with each other to exchange information and coordinate their actions.
+The messaging interfaces aim to address the need for a standard way to handle message-based communication in modern software systems. In complex software systems, different components or even different applications need to communicate with each other to exchange information and coordinate their actions.
-However, implementing message-based communication can be challenging, as it requires dealing with the details of message brokers, such as connection management, channel setup, and message serialization. The messaging service interfaces aim to simplify this process by providing a standard way to interact with message brokers, hiding the underlying complexity from the user.
+However, implementing message-based communication can be challenging, as it requires dealing with the details of message brokers, such as connection management, channel setup, and message serialization. The messaging interfaces aim to simplify this process by providing a standard way to interact with message brokers, hiding the underlying complexity from the user.
-This standardization can benefit various scenarios, such as microservice architectures, where each microservice can communicate with other microservices using the messaging service interfaces. Similarly, applications that need to handle event-driven or streaming data can benefit from the push-based message delivery mechanism provided by the `consumer` and `handler` interfaces. Overall, the messaging service interfaces aim to make it easier to build complex and scalable software systems by providing a common foundation for message-based communication.
+This standardization can benefit various scenarios, such as
+
+- Microservice architectures, where each microservice can communicate with other microservices using the messaging service interfaces. Similarly, applications that need to handle event-driven or streaming data can benefit from the push-based message delivery mechanism provided by the `consumer` and `handler` interfaces;
+
+- Local use cases such as communication channels between online and offline browser-based Web applications and local WASI applications.
+
+
+Overall, the messaging interfaces aim to make it easier to build complex and scalable software systems by providing a common foundation for message-based communication.
### Non-goals
@@ -45,4 +52,4 @@ This standardization can benefit various scenarios, such as microservice archite
For a full API walk-through, see [wasi-messaging-demo](https://github.com/danbugs/wasi-messaging-demo).
> Note: This README needs to be expanded to cover a number of additional fields suggested in the
-[WASI Proposal template](https://github.com/WebAssembly/wasi-proposal-template).
\ No newline at end of file
+[WASI Proposal template](https://github.com/WebAssembly/wasi-proposal-template).
From f8b0bea8a809fd543930b2f0303d02ce2cdeb0d4 Mon Sep 17 00:00:00 2001
From: Dan Chiarlone
Date: Thu, 22 Jun 2023 12:01:21 -0700
Subject: [PATCH 2/8] `wasi-messaging` Refactor ft. stakeholder review (#9)
* starting alignment to stakeholder review
Signed-off-by: danbugs
* fresh start
Signed-off-by: danbugs
* ready for review
Signed-off-by: danbugs
* clear
Signed-off-by: danbugs
* fix tabs vs. whitespaces mismatch
Signed-off-by: danbugs
* small change
Signed-off-by: danbugs
* typo
Signed-off-by: danbugs
* typo 2
Signed-off-by: danbugs
* updated wit to latest version + added examples
Signed-off-by: danbugs
---------
Signed-off-by: danbugs
---
examples.md | 178 ++++++++++++++++++++++++++++++++++++++++
wit/consumer.wit | 33 ++++++--
wit/deps/io/streams.wit | 128 -----------------------------
wit/guest.wit | 10 +++
wit/handler.wit | 7 --
wit/messaging.wit | 11 ++-
wit/producer.wit | 12 ++-
wit/types.wit | 77 ++++++++---------
8 files changed, 265 insertions(+), 191 deletions(-)
create mode 100644 examples.md
delete mode 100644 wit/deps/io/streams.wit
create mode 100644 wit/guest.wit
delete mode 100644 wit/handler.wit
diff --git a/examples.md b/examples.md
new file mode 100644
index 0000000..e11bf3e
--- /dev/null
+++ b/examples.md
@@ -0,0 +1,178 @@
+# Examples
+
+## Example 1: Guest Skeleton Usage
+
+The guest will implement the methods in [`wit/guest.wit`](/wit/guest.wit):
+- `configure()`, which will be called by the host to setup long-lived subscriptions, and receive any other implementor specific extensions/config..
+- `handler()`, which will be called by the host to handle messages sent to the channels subscribed to in `configure()`.
+
+As Wasm modules/components are mostly intended to be short-lived, the host is meant to create new instances for configuration and each message handling.
+
+### Rust
+
+```rust
+use crate::messaging_types::{GuestConfigurationResult, MessageResult};
+
+wit_bindgen::generate!({
+ path: "../wit",
+});
+
+struct MyGuest;
+
+impl guest::Guest for MyGuest {
+ fn configure() -> Result {
+ // Configure the messaging system
+ }
+
+ fn handler(_ms: Vec) -> Result<(), u32> {
+ // Handle the message
+ }
+}
+
+export_messaging!(MyGuest);
+```
+
+### C/C++
+
+```c
+#include "messaging.c"
+
+messaging_result_guest_configuration_error_t messaging_configure(void) {
+ // Configure the messaging system
+}
+
+messaging_result_void_error_t messaging_handle(messaging_list_message_t *message) {
+ // Handle the message
+}
+```
+
+### Go
+
+```go
+package main
+
+import (
+ msging "app/gen"
+)
+
+func init() {
+ a := MsgingImpl{}
+ msging.SetExportsWasiMessagingMessagingGuest(a)
+}
+
+type MsgingImpl struct {
+}
+
+func (e MessagingImpl) Configure msging.Result[msging.WasiMessagingConsumerGuestConfiguration, msging.WasiMessagingMessagingGuestError] {
+ // Configure the messaging system
+}
+
+func (e MessagingImpl) Handler msging.Result[struct{}, msging.WasiMessagingMessagingGuestError] {
+ // Handle the message
+}
+
+func main() {}
+```
+
+## Example 2: Guest Usage
+
+### Rust
+
+```rust
+use crate::messaging_types::{GuestConfigurationResult, MessageResult, connect, disconnect};
+
+wit_bindgen::generate!({
+ path: "../wit",
+});
+
+struct MyGuest;
+
+impl guest::Guest for MyGuest {
+ fn configure() -> Result {
+ // This function will be called by the host, who will be maintaining a
+ // long-lived client connection to a broker or other messaging system.
+ // The client will be subscribed to channels a, b, and c) with no extra configuration.
+ // As soon as configuration is set, the host should kill the Wasm instance.
+ Ok(GuestConfigurationResult {
+ channels: vec!["a".to_string(), "b".to_string(), "c".to_string()],
+ ..Default::default()
+ })
+ }
+
+ fn handler(ms: Vec) -> Result<(), u32> {
+ // Whenever a message is received on a subscribed channel (from configure()),
+ // the host will call this function. Once the message has been handled,
+ // the host is expected to kill the Wasm instance.
+
+ for m in ms {
+
+ // match on message metadata for channel name
+ match m.metadata {
+ Some(metadata) => {
+ for (k, v) in metadata {
+ if k == "channel" {
+ match v.as_str() {
+ "a" => {
+ // handle message from channel a
+ // [...]
+
+ // unsubscribe from channel a
+ update_guest_configuration(GuestConfigurationResult {
+ channels: vec!["b".to_string(), "c".to_string()],
+ ..Default::default()
+ }).unwrap()
+
+ // abandon message
+ consumer::abandon_message(m).unwrap();
+ }
+ "b" => {
+ // handle message from channel b
+ // [...]
+
+ // request-reply from channel d
+ let client = connect("some-broker").unwrap();
+ let msgs = subscribe_try_receive(client, "d", 100).unwrap();
+
+ // do something with msgs
+ // [...]
+
+ // disconnect client
+ disconnect(client);
+
+ // complete message
+ consumer::complete_message(m).unwrap();
+ }
+ "c" => {
+ // handle message from channel c
+ // [...]
+
+ // send message to channel d
+ let client = connect("some-broker").unwrap();
+ let message = MessageParam {
+ data: "hello from guest".as_bytes(),
+ format: messaging_types::FormatSpec::Raw,
+ metadata: None,
+ };
+ producer::send(client, "d", &[message]).unwrap();
+ disconnect(client);
+
+ // complete message
+ consumer::complete_message(m).unwrap();
+ }
+ _ => {
+ // handle message from unknown channel
+ }
+ }
+ }
+ }
+ }
+ None => {
+ // handle message with no metadata
+ }
+ }
+ }
+ }
+}
+
+export_messaging!(MyGuest);
+```
\ No newline at end of file
diff --git a/wit/consumer.wit b/wit/consumer.wit
index 1a6a392..156b74c 100644
--- a/wit/consumer.wit
+++ b/wit/consumer.wit
@@ -1,10 +1,27 @@
-/// An interface for a generic consumer.
-default interface consumer {
- use pkg.types.{broker, channel, subscription-token, error}
+interface consumer {
+ // {client, message, channel, error, guest-configuration}
+ use messaging-types.{client, message, channel, error, guest-configuration}
- /// Subscribes to a channel in a broker.
- subscribe: func(b: broker, c: channel) -> result
+ /// Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None
+ subscribe-try-receive: func(c: client, ch: channel, t-milliseconds: u32) -> result>, error>
- /// Unsubscribes from a channel via subscription token.
- unsubscribe: func(b: broker, st: subscription-token) -> result<_, error>
-}
\ No newline at end of file
+ /// Blocking receive until message with ephemeral subscription
+ subscribe-receive: func(c: client, ch: channel) -> result, error>
+
+ /// 'Fit-all' type function for updating a guest's configuration – this could be useful for:
+ /// - unsubscribing from a channel,
+ /// - checkpointing,
+ /// - etc..
+ update-guest-configuration: func(gc: guest-configuration) -> result<_, error>
+
+ /// A message can exist under several statuses:
+ /// (1) available: the message is ready to be read,
+ /// (2) acquired: the message has been sent to a consumer (but still exists in the queue),
+ /// (3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue,
+ /// (4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:
+ /// - deleted,
+ /// - sent to a dead-letter queue, or
+ /// - kept in the queue for further processing.
+ complete-message: func(m: message) -> result<_, error>
+ abandon-message: func(m: message) -> result<_, error>
+}
diff --git a/wit/deps/io/streams.wit b/wit/deps/io/streams.wit
deleted file mode 100644
index 427d379..0000000
--- a/wit/deps/io/streams.wit
+++ /dev/null
@@ -1,128 +0,0 @@
-default interface streams {
- /// An error type returned from a stream operation. Currently this
- /// doesn't provide any additional information.
- record stream-error {}
-
- /// An input bytestream. In the future, this will be replaced by handle
- /// types.
- ///
- /// This conceptually represents a `stream`. It's temporary
- /// scaffolding until component-model's async features are ready.
- ///
- /// And at present, it is a `u32` instead of being an actual handle, until
- /// the wit-bindgen implementation of handles and resources is ready.
- type input-stream = u32
-
- /// Read bytes from a stream.
- ///
- /// This function returns a list of bytes containing the data that was
- /// read, along with a bool indicating whether the end of the stream
- /// was reached. The returned list will contain up to `len` bytes; it
- /// may return fewer than requested, but not more.
- ///
- /// Once a stream has reached the end, subsequent calls to read or
- /// `skip` will always report end-of-stream rather than producing more
- /// data.
- ///
- /// If `len` is 0, it represents a request to read 0 bytes, which should
- /// always succeed, assuming the stream hasn't reached its end yet, and
- /// return an empty list.
- ///
- /// The len here is a `u64`, but some callees may not be able to allocate
- /// a buffer as large as that would imply.
- /// FIXME: describe what happens if allocation fails.
- read: func(
- /// The stream to read from
- src: input-stream,
- /// The maximum number of bytes to read
- len: u64
- ) -> result, bool>, stream-error>
-
- /// Skip bytes from a stream.
- ///
- /// This is similar to the `read` function, but avoids copying the
- /// bytes into the instance.
- ///
- /// Once a stream has reached the end, subsequent calls to read or
- /// `skip` will always report end-of-stream rather than producing more
- /// data.
- ///
- /// This function returns the number of bytes skipped, along with a bool
- /// indicating whether the end of the stream was reached. The returned
- /// value will be at most `len`; it may be less.
- skip: func(
- /// The stream to skip in
- src: input-stream,
- /// The maximum number of bytes to skip.
- len: u64,
- ) -> result, stream-error>
-
- /// An output bytestream. In the future, this will be replaced by handle
- /// types.
- ///
- /// This conceptually represents a `stream`. It's temporary
- /// scaffolding until component-model's async features are ready.
- ///
- /// And at present, it is a `u32` instead of being an actual handle, until
- /// the wit-bindgen implementation of handles and resources is ready.
- type output-stream = u32
-
- /// Write bytes to a stream.
- ///
- /// This function returns a `u64` indicating the number of bytes from
- /// `buf` that were written; it may be less than the full list.
- write: func(
- /// The stream to write to
- dst: output-stream,
- /// Data to write
- buf: list
- ) -> result
-
- /// Write a single byte multiple times to a stream.
- ///
- /// This function returns a `u64` indicating the number of copies of
- /// `byte` that were written; it may be less than `len`.
- write-repeated: func(
- /// The stream to write to
- dst: output-stream,
- /// The byte to write
- byte: u8,
- /// The number of times to write it
- len: u64
- ) -> result
-
- /// Read from one stream and write to another.
- ///
- /// This function returns the number of bytes transferred; it may be less
- /// than `len`.
- splice: func(
- /// The stream to write to
- dst: output-stream,
- /// The stream to read from
- src: input-stream,
- /// The number of bytes to splice
- len: u64,
- ) -> result, stream-error>
-
- /// Forward the entire contents of an input stream to an output stream.
- ///
- /// This function repeatedly reads from the input stream and writes
- /// the data to the output stream, until the end of the input stream
- /// is reached, or an error is encountered.
- ///
- /// This function returns the number of bytes transferred.
- forward: func(
- /// The stream to write to
- dst: output-stream,
- /// The stream to read from
- src: input-stream
- ) -> result
-
- /// Dispose of the specified input-stream, after which it may no longer
- /// be used.
- drop-input-stream: func(f: input-stream)
-
- /// Dispose of the specified output-stream, after which it may no longer
- /// be used.
- drop-output-stream: func(f: output-stream)
-}
\ No newline at end of file
diff --git a/wit/guest.wit b/wit/guest.wit
new file mode 100644
index 0000000..921d587
--- /dev/null
+++ b/wit/guest.wit
@@ -0,0 +1,10 @@
+interface messaging-guest {
+ use messaging-types.{message, guest-configuration, error}
+
+ /// Returns the list of channels (and extension metadata within guest-configuration) that
+ /// this component should subscribe to and be handled by the subsequent handler within guest-configuration
+ configure: func() -> result
+
+ /// Whenever this guest receives a message in one of the subscribed channels, the message is sent to this handler
+ handler: func(ms: list) -> result<_, error>
+}
diff --git a/wit/handler.wit b/wit/handler.wit
deleted file mode 100644
index 59e2986..0000000
--- a/wit/handler.wit
+++ /dev/null
@@ -1,7 +0,0 @@
-/// An interface for a consumer relying on push-based message delivery.
-default interface handler {
- use pkg.types.{event, error}
-
- /// Creates an on-receive handler push-based message delivery.
- on-receive: func(e: event) -> result<_, error>
-}
\ No newline at end of file
diff --git a/wit/messaging.wit b/wit/messaging.wit
index ab0d9cc..b5ad44b 100644
--- a/wit/messaging.wit
+++ b/wit/messaging.wit
@@ -1,5 +1,8 @@
-default world messaging {
- import producer: pkg.producer
- import consumer: pkg.consumer
- export handler: pkg.handler
+package wasi:messaging
+
+world messaging {
+ import producer
+ import consumer
+
+ export messaging-guest
}
\ No newline at end of file
diff --git a/wit/producer.wit b/wit/producer.wit
index d50f234..2c03fe7 100644
--- a/wit/producer.wit
+++ b/wit/producer.wit
@@ -1,7 +1,5 @@
-/// An interface for a producer.
-default interface producer {
- use pkg.types.{broker, channel, event, error}
-
- /// Publishes an event to a channel in a broker.
- publish: func(b: broker, c: channel, e: event) -> result<_, error>
-}
\ No newline at end of file
+interface producer {
+ use messaging-types.{client, channel, message, error}
+
+ send: func(c: client, ch: channel, m: list) -> result<_, error>
+}
diff --git a/wit/types.wit b/wit/types.wit
index ebc55ae..5e03201 100644
--- a/wit/types.wit
+++ b/wit/types.wit
@@ -1,41 +1,44 @@
-/// An interface grouping all necessary types for messaging.
-default interface messaging-types {
- /// A broker type that allows the exchange of messages.
- type broker = u32
- drop-broker: func(b: broker)
- open-broker: func(name: string) -> result
-
- /// An event type that follows the CloudEvents specification (https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md). We
- /// assume the type of the data is a byte sequence. It is up to the data schema to determine what type of the data payload the event
- /// contains.
- record event {
- specversion: string,
- ty: string,
- source: string,
- id: string,
- data: option>,
- datacontenttype: option,
- dataschema: option,
- subject: option,
- time: option,
+interface messaging-types {
+ /// A connection to a message-exchange service (e.g., buffer, broker, etc.).
+ type client = u32
+ disconnect: func(c: client)
+ connect: func(name: string) -> result
+
+ /// TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.
+ type error = u32
+ drop-error: func(e: error)
+ trace: func(e: error) -> string
+
+ /// There are two types of channels:
+ /// - publish-subscribe channel, which is a broadcast channel, and
+ /// - point-to-point channel, which is a unicast channel.
+ ///
+ /// The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.
+ type channel = string
+
+ /// Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs
+ /// (e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).
+ record guest-configuration {
+ channels: list,
extensions: option>>
}
-
- /// Channels specify where a published message should land. There are two types of channels:
- /// - queue: competitive consumers, and
- /// - topic: non-competitive consumers.
- variant channel {
- queue(string),
- topic(string)
+
+ /// Format specification for messages
+ /// - more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats
+ /// - message metadata can further decorate w/ things like format version, and so on.
+ enum format-spec {
+ cloudevents,
+ http,
+ amqp,
+ mqtt,
+ kafka,
+ raw
+ }
+
+ /// A message with a binary payload, a format specification, and decorative metadata.
+ record message {
+ data: list,
+ format: format-spec,
+ metadata: option>>
}
-
- /// A subscription token that allows receives from a specific subscription
- type subscription-token = string
- /// An error resource type.
- /// Currently, this provides only one function to return a string representation
- /// of the error. In the future, this will be extended to provide more information.
- // TODO: switch to `resource error { ... }`
- type error = u32
- drop-error: func(e: error)
- trace-error: func(e: error) -> string
}
\ No newline at end of file
From 1a226612b5b23d4205510e9439bbb82e360c4e05 Mon Sep 17 00:00:00 2001
From: Jiaxiao Zhou
Date: Mon, 15 Jan 2024 11:04:06 -0800
Subject: [PATCH 3/8] feat(*): add semicolons to all the interfaces (#17)
Signed-off-by: Jiaxiao Zhou (Mossaka)
---
wit/consumer.wit | 12 ++++++------
wit/guest.wit | 6 +++---
wit/messaging.wit | 8 ++++----
wit/producer.wit | 4 ++--
wit/types.wit | 14 +++++++-------
5 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/wit/consumer.wit b/wit/consumer.wit
index 156b74c..a8f14d5 100644
--- a/wit/consumer.wit
+++ b/wit/consumer.wit
@@ -1,18 +1,18 @@
interface consumer {
// {client, message, channel, error, guest-configuration}
- use messaging-types.{client, message, channel, error, guest-configuration}
+ use messaging-types.{client, message, channel, error, guest-configuration};
/// Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None
- subscribe-try-receive: func(c: client, ch: channel, t-milliseconds: u32) -> result>, error>
+ subscribe-try-receive: func(c: client, ch: channel, t-milliseconds: u32) -> result >, error>;
/// Blocking receive until message with ephemeral subscription
- subscribe-receive: func(c: client, ch: channel) -> result, error>
+ subscribe-receive: func(c: client, ch: channel) -> result, error>;
/// 'Fit-all' type function for updating a guest's configuration – this could be useful for:
/// - unsubscribing from a channel,
/// - checkpointing,
/// - etc..
- update-guest-configuration: func(gc: guest-configuration) -> result<_, error>
+ update-guest-configuration: func(gc: guest-configuration) -> result<_, error>;
/// A message can exist under several statuses:
/// (1) available: the message is ready to be read,
@@ -22,6 +22,6 @@ interface consumer {
/// - deleted,
/// - sent to a dead-letter queue, or
/// - kept in the queue for further processing.
- complete-message: func(m: message) -> result<_, error>
- abandon-message: func(m: message) -> result<_, error>
+ complete-message: func(m: message) -> result<_, error>;
+ abandon-message: func(m: message) -> result<_, error>;
}
diff --git a/wit/guest.wit b/wit/guest.wit
index 921d587..8e9cb4e 100644
--- a/wit/guest.wit
+++ b/wit/guest.wit
@@ -1,10 +1,10 @@
interface messaging-guest {
- use messaging-types.{message, guest-configuration, error}
+ use messaging-types.{message, guest-configuration, error};
/// Returns the list of channels (and extension metadata within guest-configuration) that
/// this component should subscribe to and be handled by the subsequent handler within guest-configuration
- configure: func() -> result
+ configure: func() -> result;
/// Whenever this guest receives a message in one of the subscribed channels, the message is sent to this handler
- handler: func(ms: list) -> result<_, error>
+ handler: func(ms: list) -> result<_, error>;
}
diff --git a/wit/messaging.wit b/wit/messaging.wit
index b5ad44b..6226ae6 100644
--- a/wit/messaging.wit
+++ b/wit/messaging.wit
@@ -1,8 +1,8 @@
-package wasi:messaging
+package wasi:messaging;
world messaging {
- import producer
- import consumer
+ import producer;
+ import consumer;
- export messaging-guest
+ export messaging-guest;
}
\ No newline at end of file
diff --git a/wit/producer.wit b/wit/producer.wit
index 2c03fe7..fa795d8 100644
--- a/wit/producer.wit
+++ b/wit/producer.wit
@@ -1,5 +1,5 @@
interface producer {
- use messaging-types.{client, channel, message, error}
+ use messaging-types.{client, channel, message, error};
- send: func(c: client, ch: channel, m: list) -> result<_, error>
+ send: func(c: client, ch: channel, m: list) -> result<_, error>;
}
diff --git a/wit/types.wit b/wit/types.wit
index 5e03201..3cbb429 100644
--- a/wit/types.wit
+++ b/wit/types.wit
@@ -1,20 +1,20 @@
interface messaging-types {
/// A connection to a message-exchange service (e.g., buffer, broker, etc.).
- type client = u32
- disconnect: func(c: client)
- connect: func(name: string) -> result
+ type client = u32;
+ disconnect: func(c: client);
+ connect: func(name: string) -> result;
/// TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.
- type error = u32
- drop-error: func(e: error)
- trace: func(e: error) -> string
+ type error = u32;
+ drop-error: func(e: error);
+ trace: func(e: error) -> string;
/// There are two types of channels:
/// - publish-subscribe channel, which is a broadcast channel, and
/// - point-to-point channel, which is a unicast channel.
///
/// The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.
- type channel = string
+ type channel = string;
/// Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs
/// (e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).
From 338f15d9f275876a839d91a9ad575b9b42e20c8f Mon Sep 17 00:00:00 2001
From: Jiaxiao Zhou
Date: Thu, 18 Jan 2024 11:33:52 -0800
Subject: [PATCH 4/8] chore(*): convert to resources and add package version
(#18)
* chore(*): convert to resources and add package version
Signed-off-by: Jiaxiao Zhou (Mossaka)
* ci: renable the workflow
Signed-off-by: Jiaxiao Zhou (Mossaka)
* chore(*): added the generated markdown file
Signed-off-by: Jiaxiao Zhou (Mossaka)
---------
Signed-off-by: Jiaxiao Zhou (Mossaka)
---
.github/workflows/main.yml | 6 +-
messaging.md | 220 +++++++++++++++++++++++++++++++++++++
wit/messaging.wit | 2 +-
wit/types.wit | 14 +--
4 files changed, 231 insertions(+), 11 deletions(-)
create mode 100644 messaging.md
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 4bed256..9ba6465 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -11,6 +11,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- # - uses: WebAssembly/wit-abi-up-to-date@v6
- # with:
- # wit-abi-tag: wit-abi-0.6.0
+ - uses: WebAssembly/wit-abi-up-to-date@v17
+ with:
+ wit-bindgen: '0.16.0'
diff --git a/messaging.md b/messaging.md
new file mode 100644
index 0000000..a41b1ca
--- /dev/null
+++ b/messaging.md
@@ -0,0 +1,220 @@
+
+
+Imports:
+
+
+Exports:
+
+
+
+
+
+Types
+
+A connection to a message-exchange service (e.g., buffer, broker, etc.).
+
+TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.
+
+string
+There are two types of channels:
+- publish-subscribe channel, which is a broadcast channel, and
+- point-to-point channel, which is a unicast channel.
+
The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.
+
+Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs
+(e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).
+Record Fields
+
+
+Format specification for messages
+
+more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats
+message metadata can further decorate w/ things like format version, and so on.
+
+Enum Cases
+
+
+A message with a binary payload, a format specification, and decorative metadata.
+Record Fields
+
+
+Functions
+
+Params
+
+Return values
+
+
+Return values
+
+
+
+Types
+
+client
+
+#### `type channel`
+[`channel`](#channel)
+
+#### `type message`
+[`message`](#message)
+
+#### `type error`
+[`error`](#error)
+
+----
+
Functions
+
+Params
+
+Return values
+
+ result<_, own<error >>
+
+
+
+Types
+
+client
+
+#### `type message`
+[`message`](#message)
+
+#### `type channel`
+[`channel`](#channel)
+
+#### `type error`
+[`error`](#error)
+
+#### `type guest-configuration`
+[`guest-configuration`](#guest_configuration)
+
+----
+
Functions
+
+Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None
+Params
+
+Return values
+
+
+Blocking receive until message with ephemeral subscription
+Params
+
+Return values
+
+
+'Fit-all' type function for updating a guest's configuration – this could be useful for:
+
+unsubscribing from a channel,
+checkpointing,
+etc..
+
+Params
+
+Return values
+
+ result<_, own<error >>
+
+
+A message can exist under several statuses:
+(1) available: the message is ready to be read,
+(2) acquired: the message has been sent to a consumer (but still exists in the queue),
+(3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue,
+(4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:
+
+deleted,
+sent to a dead-letter queue, or
+kept in the queue for further processing.
+
+Params
+
+Return values
+
+ result<_, own<error >>
+
+
+Params
+
+Return values
+
+ result<_, own<error >>
+
+
+
+Types
+
+message
+
+#### `type guest-configuration`
+[`guest-configuration`](#guest_configuration)
+
+#### `type error`
+[`error`](#error)
+
+----
+
Functions
+
+Returns the list of channels (and extension metadata within guest-configuration) that
+this component should subscribe to and be handled by the subsequent handler within guest-configuration
+Return values
+
+
+Whenever this guest receives a message in one of the subscribed channels, the message is sent to this handler
+Params
+
+Return values
+
+ result<_, own<error >>
+
diff --git a/wit/messaging.wit b/wit/messaging.wit
index 6226ae6..bf31a9d 100644
--- a/wit/messaging.wit
+++ b/wit/messaging.wit
@@ -1,4 +1,4 @@
-package wasi:messaging;
+package wasi:messaging@0.1.0;
world messaging {
import producer;
diff --git a/wit/types.wit b/wit/types.wit
index 3cbb429..b7c9803 100644
--- a/wit/types.wit
+++ b/wit/types.wit
@@ -1,13 +1,13 @@
interface messaging-types {
/// A connection to a message-exchange service (e.g., buffer, broker, etc.).
- type client = u32;
- disconnect: func(c: client);
- connect: func(name: string) -> result;
-
+ resource client {
+ connect: static func(name: string) -> result;
+ }
+
/// TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.
- type error = u32;
- drop-error: func(e: error);
- trace: func(e: error) -> string;
+ resource error {
+ trace: static func() -> string;
+ }
/// There are two types of channels:
/// - publish-subscribe channel, which is a broadcast channel, and
From b102727f8870ce9db0294c39fa4eacbbcd4bde63 Mon Sep 17 00:00:00 2001
From: Taylor Thomas
Date: Tue, 6 Feb 2024 14:05:39 -0700
Subject: [PATCH 5/8] chore(*): Add Taylor as champion (#19)
Signed-off-by: Taylor Thomas
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 4ab4837..414f56c 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ A proposed [WebAssembly System Interface](https://github.com/WebAssembly/WASI) A
- [Dan Chiarlone](https://github.com/danbugs)
- [David Justice](https://github.com/devigned)
- [Jiaxiao Zhou](https://github.com/Mossaka)
+- [Taylor Thomas](https://github.com/thomastaylor312)
### Phase 4 Advancement Criteria
From c2a7421ad0aece25d11ab5056a8b98f18bedd64a Mon Sep 17 00:00:00 2001
From: Jiaxiao Zhou
Date: Tue, 20 Feb 2024 16:26:42 -0800
Subject: [PATCH 6/8] chore(*): split world to imports and re-version to
`0.2.0-draft` (#20)
this commit splits the existing messaging world into two worlds.
1. imports
2. messaging that includes imports world
to follow the convention of other WIT packages in WASI.
It also re-versions the wit package to `0.2.0-draft`
Signed-off-by: Jiaxiao Zhou (Mossaka)
---
.github/workflows/main.yml | 3 +-
imports.md | 184 +++++++++++++++++++++++++++++++++++++
messaging.md | 120 ++++++++++++------------
wit/messaging.wit | 7 +-
4 files changed, 251 insertions(+), 63 deletions(-)
create mode 100644 imports.md
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9ba6465..a084cc6 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -13,4 +13,5 @@ jobs:
- uses: actions/checkout@v2
- uses: WebAssembly/wit-abi-up-to-date@v17
with:
- wit-bindgen: '0.16.0'
+ wit-bindgen: '0.18.0'
+ worlds: 'imports messaging'
diff --git a/imports.md b/imports.md
new file mode 100644
index 0000000..cc4b825
--- /dev/null
+++ b/imports.md
@@ -0,0 +1,184 @@
+
+
+ Import interface wasi:messaging/messaging-types@0.2.0-draft
+
+Types
+resource client
+A connection to a message-exchange service (e.g., buffer, broker, etc.).
+resource error
+TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.
+type channel
+string
+There are two types of channels:
+- publish-subscribe channel, which is a broadcast channel, and
+- point-to-point channel, which is a unicast channel.
+
The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.
+record guest-configuration
+Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs
+(e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).
+Record Fields
+
+channels: list<channel >
+extensions: option<list<(string, string)>>
+
+enum format-spec
+Format specification for messages
+
+more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats
+message metadata can further decorate w/ things like format version, and so on.
+
+Enum Cases
+
+cloudevents
+http
+amqp
+mqtt
+kafka
+raw
+
+record message
+A message with a binary payload, a format specification, and decorative metadata.
+Record Fields
+
+data: list<u8>
+format: format-spec
+metadata: option<list<(string, string)>>
+
+
+Functions
+[static]client.connect: func
+Params
+
+Return values
+
+[static]error.trace: func
+Return values
+
+ Import interface wasi:messaging/producer@0.2.0-draft
+
+Types
+type client
+client
+
+#### `type channel`
+[`channel`](#channel)
+
+#### `type message`
+[`message`](#message)
+
+#### `type error`
+[`error`](#error)
+
+----
+
Functions
+send: func
+Params
+
+Return values
+
+ result<_, own<error >>
+
+ Import interface wasi:messaging/consumer@0.2.0-draft
+
+Types
+type client
+client
+
+#### `type message`
+[`message`](#message)
+
+#### `type channel`
+[`channel`](#channel)
+
+#### `type error`
+[`error`](#error)
+
+#### `type guest-configuration`
+[`guest-configuration`](#guest_configuration)
+
+----
+
Functions
+subscribe-try-receive: func
+Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None
+Params
+
+Return values
+
+subscribe-receive: func
+Blocking receive until message with ephemeral subscription
+Params
+
+Return values
+
+update-guest-configuration: func
+'Fit-all' type function for updating a guest's configuration – this could be useful for:
+
+unsubscribing from a channel,
+checkpointing,
+etc..
+
+Params
+
+Return values
+
+ result<_, own<error >>
+
+complete-message: func
+A message can exist under several statuses:
+(1) available: the message is ready to be read,
+(2) acquired: the message has been sent to a consumer (but still exists in the queue),
+(3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue,
+(4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:
+
+deleted,
+sent to a dead-letter queue, or
+kept in the queue for further processing.
+
+Params
+
+Return values
+
+ result<_, own<error >>
+
+abandon-message: func
+Params
+
+Return values
+
+ result<_, own<error >>
+
diff --git a/messaging.md b/messaging.md
index a41b1ca..3774be8 100644
--- a/messaging.md
+++ b/messaging.md
@@ -2,39 +2,39 @@
-
+ Import interface wasi:messaging/messaging-types@0.2.0-draft
Types
-
+resource client
A connection to a message-exchange service (e.g., buffer, broker, etc.).
-
+resource error
TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.
-
+type channel
string
There are two types of channels:
- publish-subscribe channel, which is a broadcast channel, and
- point-to-point channel, which is a unicast channel.
The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.
-
+record guest-configuration
Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs
(e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).
Record Fields
-
+enum format-spec
Format specification for messages
more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats
@@ -42,109 +42,109 @@
Enum Cases
-
+record message
A message with a binary payload, a format specification, and decorative metadata.
Record Fields
Functions
-
+[static]client.connect: func
Params
-name : string
+name: string
Return values
-
+[static]error.trace: func
Return values
-
+ Import interface wasi:messaging/producer@0.2.0-draft
Types
-
+type client
client
-#### `type channel`
+#### `type channel`
[`channel`](#channel)
-#### `type message`
+#### `type message`
[`message`](#message)
-#### `type error`
+#### `type error`
[`error`](#error)
----
Functions
-
+send: func
Params
Return values
-
+ Import interface wasi:messaging/consumer@0.2.0-draft
Types
-
+type client
client
-#### `type message`
+#### `type message`
[`message`](#message)
-#### `type channel`
+#### `type channel`
[`channel`](#channel)
-#### `type error`
+#### `type error`
[`error`](#error)
-#### `type guest-configuration`
+#### `type guest-configuration`
[`guest-configuration`](#guest_configuration)
----
Functions
-
+subscribe-try-receive: func
Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None
Params
Return values
-
+subscribe-receive: func
Blocking receive until message with ephemeral subscription
Params
Return values
-
+update-guest-configuration: func
'Fit-all' type function for updating a guest's configuration – this could be useful for:
unsubscribing from a channel,
@@ -153,13 +153,13 @@
Params
Return values
-
+complete-message: func
A message can exist under several statuses:
(1) available: the message is ready to be read,
(2) acquired: the message has been sent to a consumer (but still exists in the queue),
@@ -172,47 +172,47 @@
Params
Return values
-
+abandon-message: func
Params
Return values
-
+ Export interface wasi:messaging/messaging-guest@0.2.0-draft
Types
-
+type message
message
-#### `type guest-configuration`
+#### `type guest-configuration`
[`guest-configuration`](#guest_configuration)
-#### `type error`
+#### `type error`
[`error`](#error)
----
Functions
-
+configure: func
Returns the list of channels (and extension metadata within guest-configuration) that
this component should subscribe to and be handled by the subsequent handler within guest-configuration
Return values
-
+handler: func
Whenever this guest receives a message in one of the subscribed channels, the message is sent to this handler
Params
Return values
diff --git a/wit/messaging.wit b/wit/messaging.wit
index bf31a9d..26ad817 100644
--- a/wit/messaging.wit
+++ b/wit/messaging.wit
@@ -1,8 +1,11 @@
-package wasi:messaging@0.1.0;
+package wasi:messaging@0.2.0-draft;
-world messaging {
+world imports {
import producer;
import consumer;
+}
+world messaging {
+ include imports;
export messaging-guest;
}
\ No newline at end of file
From 8f63bd5749518ee725bdf097ab8029ba8e79a5fd Mon Sep 17 00:00:00 2001
From: Dan Chiarlone
Date: Wed, 30 Oct 2024 17:50:38 +0000
Subject: [PATCH 7/8] Simplifies wasi-messaging interface with feedback (#24)
* feat(*): Updates messaging to support request-reply
This makes several updates to the messaging interface. Initially the
README said that this wasn't going to support request/reply, but based
on my reading of the Kafka, NATS, MQTT, and SQS APIs, this is a fairly
common pattern. Another piece of evidence here is what I've seen as a
wasmCloud maintainer from our users. Request/reply is one of the more
common things we see with a messaging service. Please note that this
doesn't _require_ the use of a reply-to topic, just exposes it for use.
I also did a few other changes here. First is that I added the topic to
the message. This was common across all systems and is often used by code
to select the appropriate logic to perform. I also removed the format
field as this didn't seem to be a common parameter across various services.
We could definitely add a content-type member to this record in the future
if needed, but I think much of that can be passed via the metadata field.
There are other things I might suggest some changes to, but I want to think
on them some more and open some issues to discuss them first
Signed-off-by: Taylor Thomas
* feat(*): Updates interfaces to be more streamlined
This PR integrates various changes from talking to current users of
messaging in the community as well as conversations among the champions
Signed-off-by: Taylor Thomas
* feat(*)!: Additional changes based on PR feedback
I also deleted the examples.md for now until we settle on the interface.
It will be easier to add back in once we have some real world examples
to point at
Signed-off-by: Taylor Thomas
* feat(types): Updates the message type to have configurable fields
Also removes extensions as a guest configuration option (for now)
Signed-off-by: Taylor Thomas
* feat(types): Renames guest config to just plain simple `config`
In many of the interfaces out there right now, we've moved more towards
just calling these things config
Signed-off-by: Taylor Thomas
* feat(*): Additional changes to request/reply for streamlining
Also removes the channel parameter I forgot to remove in a previous
commit
Signed-off-by: Taylor Thomas
* feat(request): Updates request-multi to support scatter/gather operations
One of the uses of request-multi is to support a scatter/gather operation.
In these cases, you might not know how many requests you are going to
receive, so you can't set expected replies. Generally these wait until
timeout and then return the results. This commit adds the ability to
support all the different use cases for request-multi
Signed-off-by: Taylor Thomas
* ref(*): Simplifies interface and documents scope/portability
After a whole bunch of feedback from the community, I realized we were
still trying to make this interface too much. So I dramatically paired
back the interface to be purely focused on message passing. Further
features like ack/nack, guaranteed delivery, and so on are now out of
scope (see the README for full details).
This was partly inspired by a discussion in the CNCF Wasm WG around this
interface. To be perfectly frank, the level I paired this down to is
essentially the same level of guarantees offered by the wasmCloud
[messaging interface](https://github.com/wasmCloud/messaging). The main
reason being is that there are people actually using that interface for
real applications (with real host implementations). If we can come to
agreement on a simpler interface, it will be easier to add in functionality
such as the things I stripped out in this commit.
Please let me know any feedback you have around this, focusing on whether
or not this covers at least the most basic scenarios
Signed-off-by: Taylor Thomas
* fixed typos, added subscribe, added messaging-request-reply world, and nits
Signed-off-by: danbugs
* update auto-generated files and wit-abi-up-to-date version
Signed-off-by: danbugs
* removed .idea/ and added to .gitignore
Signed-off-by: danbugs
* updated README
Signed-off-by: danbugs
* addressing first round of feedback: added type aliases, added remove-metadata method, added new-line at the end of files, improved argument names, removed .gitignore, improved documentation, changed get-subscriptions function name
Signed-off-by: danbugs
* [pr feedback] added set-metadata method + revisited topic
Signed-off-by: danbugs
* [pr feedback] changing signature of send
Signed-off-by: danbugs
---------
Signed-off-by: Taylor Thomas
Signed-off-by: danbugs
Co-authored-by: Taylor Thomas
---
.github/workflows/main.yml | 4 +-
.gitignore | 1 -
README.md | 100 ++++++++----
examples.md | 178 ---------------------
imports-request-reply.md | 265 ++++++++++++++++++++++++++++++++
imports.md | 240 ++++++++++++++---------------
messaging-core.md | 223 +++++++++++++++++++++++++++
messaging-request-reply.md | 307 +++++++++++++++++++++++++++++++++++++
messaging.md | 256 ++++++++++++++-----------------
wit/consumer.wit | 27 ----
wit/guest.wit | 19 ++-
wit/messaging.wit | 30 +++-
wit/producer.wit | 6 +-
wit/request-reply.wit | 47 ++++++
wit/types.wit | 79 +++++-----
15 files changed, 1232 insertions(+), 550 deletions(-)
delete mode 100644 .gitignore
delete mode 100644 examples.md
create mode 100644 imports-request-reply.md
create mode 100644 messaging-core.md
create mode 100644 messaging-request-reply.md
delete mode 100644 wit/consumer.wit
create mode 100644 wit/request-reply.wit
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a084cc6..7669187 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -13,5 +13,5 @@ jobs:
- uses: actions/checkout@v2
- uses: WebAssembly/wit-abi-up-to-date@v17
with:
- wit-bindgen: '0.18.0'
- worlds: 'imports messaging'
+ wit-bindgen: '0.34.0'
+ worlds: 'imports imports-request-reply messaging-core messaging-request-reply'
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 496ee2c..0000000
--- a/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.DS_Store
\ No newline at end of file
diff --git a/README.md b/README.md
index 414f56c..1d303b8 100644
--- a/README.md
+++ b/README.md
@@ -2,55 +2,103 @@
A proposed [WebAssembly System Interface](https://github.com/WebAssembly/WASI) API.
-### Current Phase
+## Table of Contents
+
+- [`wasi-messaging`](#wasi-messaging)
+ - [Table of Contents](#table-of-contents)
+ - [Current Phase](#current-phase)
+ - [Champions](#champions)
+ - [Phase 4 Advancement Criteria](#phase-4-advancement-criteria)
+ - [Introduction](#introduction)
+ - [Goals](#goals)
+ - [Portability criteria](#portability-criteria)
+ - [Dev notes](#dev-notes)
-`wasi-messaging` is currently in [Phase 1](https://github.com/WebAssembly/WASI/blob/main/Proposals.md#phase-1---feature-proposal-cg).
+## Current Phase
-### Champions
+`wasi-messaging` is currently in [Phase
+1](https://github.com/WebAssembly/WASI/blob/main/Proposals.md#phase-1---feature-proposal-cg).
+
+## Champions
- [Dan Chiarlone](https://github.com/danbugs)
- [David Justice](https://github.com/devigned)
- [Jiaxiao Zhou](https://github.com/Mossaka)
- [Taylor Thomas](https://github.com/thomastaylor312)
-### Phase 4 Advancement Criteria
+## Phase 4 Advancement Criteria
-`wasi-messaging` should have at least two implementations (i.e., from service providers, and or cloud providers), and, at the very minimum, pass the testsuite for Windows, Linux, and MacOS.
+For `wasi-messaging` to advance to Phase 4, it must have at least two independent implementations
+for open-source message brokers (such as Kafka, NATS, MQTT brokers) and two for cloud service providers
+(e.g., Azure Service Bus, AWS SQS).
-## Table of Contents
+## Introduction
-- [Introduction](#introduction)
-- [Goals](#goals)
-- [Non-goals](#non-goals)
+In modern software systems, different components or applications often need to communicate with each other
+to exchange information and coordinate their actions. Messaging systems facilitate this communication by
+allowing messages to be sent and received between different parts of a system.
-### Introduction
+However, implementing message-based communication can be challenging. It requires dealing with the details
+of message brokers, such as connection management, channel setup, and message serialization. This complexity
+can hinder development and lead to inconsistent implementations.
-The messaging interfaces aim to provide a generic and flexible way for producers and consumers to communicate through message brokers. The `producer` interface allows producers to publish events to a specific channel in a broker, while the `consumer` interface allows consumers to subscribe to a channel and receive events through a push-based mechanism. The handler interface provides an on-receive function that can be used to process received events with full abstraction of the underlying broker implementation.
+The `wasi-messaging` interface is a purposefully small interface focused purely on message passing. It is
+designed to express the bare minimum of receiving a message and sending a message, along with an optional
+request/reply interface that allows for message-based APIs and/or RPC.
-### Goals
+By providing a standard way to interact with message brokers, the `wasi-messaging` interface aims to simplify
+this process, hiding the underlying complexity from the user. This aligns with the broader goals of WASI by
+promoting interoperability, modularity, and security in WebAssembly applications.
-The messaging interfaces aim to address the need for a standard way to handle message-based communication in modern software systems. In complex software systems, different components or even different applications need to communicate with each other to exchange information and coordinate their actions.
+## Goals
-However, implementing message-based communication can be challenging, as it requires dealing with the details of message brokers, such as connection management, channel setup, and message serialization. The messaging interfaces aim to simplify this process by providing a standard way to interact with message brokers, hiding the underlying complexity from the user.
+The primary goal of this interface is to focus on message passing. The only guarantee offered is
+that publishing a message is handled successfully. No other guarantees are made about the delivery
+of the message or being able to ack/nack a message directly. This minimalist approach provides the
+most basic foundation of messaging, which can be expanded upon by future interfaces or proposals as
+use cases emerge.
-This standardization can benefit various scenarios, such as
+This simplicity allows:
+- **Ease of Integration**: Components can easily implement the message handler in this interface,
+with details such as work dispatch and queuing handled behind the scenes, invisible to the business logic.
+- **Flexibility**: Anything that can send a message can easily be passed into a queue
+(such as a Kafka stream or NATS JetStream) without the knowledge that it is being sent into a queue.
+- **Extensibility**: The paradigm can be expanded by future interfaces (like a queue-based work interface) to handle
+more complex messaging scenarios. By focusing solely on message passing, the wasi-messaging interface simplifies the
+development of interoperable WebAssembly modules that can communicate over various messaging systems without being
+tied to any specific implementation.
-- Microservice architectures, where each microservice can communicate with other microservices using the messaging service interfaces. Similarly, applications that need to handle event-driven or streaming data can benefit from the push-based message delivery mechanism provided by the `consumer` and `handler` interfaces;
+## Portability criteria
-- Local use cases such as communication channels between online and offline browser-based Web applications and local WASI applications.
+The main portability criteria on which this should be judged is whether a component can receive and send a message
+from all major messaging systems. This includes:
+- Message standards like MQTT and AMQP.
+- Specific technologies like NATS and Kafka.
+- Cloud provider implementations like Azure Service Bus and AWS SQS.
+This _does not_ mean it implements the full set of features of each of the messaging systems. In fact, it is expected
+that most implementations will need to do work to adapt their system to this interface (e.g., in Kafka, you'd have
+to mark the message as completed once the call to handle returns).
-Overall, the messaging interfaces aim to make it easier to build complex and scalable software systems by providing a common foundation for message-based communication.
+As mentioned above, this should still be completely compatible with any more advanced use cases of the various
+message systems. For example, if you have a queue of work that is currently being handled by pre-existing software
+outside of Wasm components, a component could use this interface to publish messages that get ingested into this queue.
-### Non-goals
+Another way to state the portability criteria is that this implementation should not break the possibility of a
+component consuming this interface to be integrated with a more advanced messaging use case.
-- The messaging service interfaces do not aim to provide advanced features of message brokers, such as broker clustering, message persistence, or guaranteed message delivery. These are implementation-specific details that are not addressed by the interfaces.
-- The messaging service interfaces do not aim to provide support for every possible messaging pattern or use case. Instead, they focus on the common use cases of pub-sub and push-based message delivery. Other messaging patterns, such as request-reply or publish-confirm-subscribe, are outside the scope of the interfaces.
-- The messaging service interfaces do not aim to provide a specific implementation of a message broker. Instead, they provide a standard way to interact with any message broker that supports the interfaces.
+## Dev notes
-### API walk-through
+To regenerate the `.md` files, run:
+```sh
+wit-bindgen markdown ./wit/ -w imports --html-in-md
+wit-bindgen markdown ./wit/ -w imports-request-reply --html-in-md
+wit-bindgen markdown ./wit/ -w messaging-core --html-in-md
+wit-bindgen markdown ./wit/ -w messaging-request-reply --html-in-md
+```
-For a full API walk-through, see [wasi-messaging-demo](https://github.com/danbugs/wasi-messaging-demo).
+It would make sense for a lot of these functions to be asynchronous, but that is not currently natively supported in
+the component model. Asynchronous support will be added as part of WASI Preview 3. When async support becomes
+available, we plan to update the wasi-messaging interface to incorporate asynchronous patterns.
-> Note: This README needs to be expanded to cover a number of additional fields suggested in the
-[WASI Proposal template](https://github.com/WebAssembly/wasi-proposal-template).
+> **Note**: Ensure you have version 0.34.0 of `wit-bindgen` installed to avoid compatibility issues.
diff --git a/examples.md b/examples.md
deleted file mode 100644
index e11bf3e..0000000
--- a/examples.md
+++ /dev/null
@@ -1,178 +0,0 @@
-# Examples
-
-## Example 1: Guest Skeleton Usage
-
-The guest will implement the methods in [`wit/guest.wit`](/wit/guest.wit):
-- `configure()`, which will be called by the host to setup long-lived subscriptions, and receive any other implementor specific extensions/config..
-- `handler()`, which will be called by the host to handle messages sent to the channels subscribed to in `configure()`.
-
-As Wasm modules/components are mostly intended to be short-lived, the host is meant to create new instances for configuration and each message handling.
-
-### Rust
-
-```rust
-use crate::messaging_types::{GuestConfigurationResult, MessageResult};
-
-wit_bindgen::generate!({
- path: "../wit",
-});
-
-struct MyGuest;
-
-impl guest::Guest for MyGuest {
- fn configure() -> Result {
- // Configure the messaging system
- }
-
- fn handler(_ms: Vec) -> Result<(), u32> {
- // Handle the message
- }
-}
-
-export_messaging!(MyGuest);
-```
-
-### C/C++
-
-```c
-#include "messaging.c"
-
-messaging_result_guest_configuration_error_t messaging_configure(void) {
- // Configure the messaging system
-}
-
-messaging_result_void_error_t messaging_handle(messaging_list_message_t *message) {
- // Handle the message
-}
-```
-
-### Go
-
-```go
-package main
-
-import (
- msging "app/gen"
-)
-
-func init() {
- a := MsgingImpl{}
- msging.SetExportsWasiMessagingMessagingGuest(a)
-}
-
-type MsgingImpl struct {
-}
-
-func (e MessagingImpl) Configure msging.Result[msging.WasiMessagingConsumerGuestConfiguration, msging.WasiMessagingMessagingGuestError] {
- // Configure the messaging system
-}
-
-func (e MessagingImpl) Handler msging.Result[struct{}, msging.WasiMessagingMessagingGuestError] {
- // Handle the message
-}
-
-func main() {}
-```
-
-## Example 2: Guest Usage
-
-### Rust
-
-```rust
-use crate::messaging_types::{GuestConfigurationResult, MessageResult, connect, disconnect};
-
-wit_bindgen::generate!({
- path: "../wit",
-});
-
-struct MyGuest;
-
-impl guest::Guest for MyGuest {
- fn configure() -> Result {
- // This function will be called by the host, who will be maintaining a
- // long-lived client connection to a broker or other messaging system.
- // The client will be subscribed to channels a, b, and c) with no extra configuration.
- // As soon as configuration is set, the host should kill the Wasm instance.
- Ok(GuestConfigurationResult {
- channels: vec!["a".to_string(), "b".to_string(), "c".to_string()],
- ..Default::default()
- })
- }
-
- fn handler(ms: Vec) -> Result<(), u32> {
- // Whenever a message is received on a subscribed channel (from configure()),
- // the host will call this function. Once the message has been handled,
- // the host is expected to kill the Wasm instance.
-
- for m in ms {
-
- // match on message metadata for channel name
- match m.metadata {
- Some(metadata) => {
- for (k, v) in metadata {
- if k == "channel" {
- match v.as_str() {
- "a" => {
- // handle message from channel a
- // [...]
-
- // unsubscribe from channel a
- update_guest_configuration(GuestConfigurationResult {
- channels: vec!["b".to_string(), "c".to_string()],
- ..Default::default()
- }).unwrap()
-
- // abandon message
- consumer::abandon_message(m).unwrap();
- }
- "b" => {
- // handle message from channel b
- // [...]
-
- // request-reply from channel d
- let client = connect("some-broker").unwrap();
- let msgs = subscribe_try_receive(client, "d", 100).unwrap();
-
- // do something with msgs
- // [...]
-
- // disconnect client
- disconnect(client);
-
- // complete message
- consumer::complete_message(m).unwrap();
- }
- "c" => {
- // handle message from channel c
- // [...]
-
- // send message to channel d
- let client = connect("some-broker").unwrap();
- let message = MessageParam {
- data: "hello from guest".as_bytes(),
- format: messaging_types::FormatSpec::Raw,
- metadata: None,
- };
- producer::send(client, "d", &[message]).unwrap();
- disconnect(client);
-
- // complete message
- consumer::complete_message(m).unwrap();
- }
- _ => {
- // handle message from unknown channel
- }
- }
- }
- }
- }
- None => {
- // handle message with no metadata
- }
- }
- }
- }
-}
-
-export_messaging!(MyGuest);
-```
\ No newline at end of file
diff --git a/imports-request-reply.md b/imports-request-reply.md
new file mode 100644
index 0000000..36f7f59
--- /dev/null
+++ b/imports-request-reply.md
@@ -0,0 +1,265 @@
+ World imports-request-reply
+The imports-request-reply world extends imports by including the request-reply interface.
+This allows the component to perform request/reply messaging patterns.
+
+ Import interface wasi:messaging/types@0.2.0-draft
+
+Types
+type metadata
+metadata
+A type alias for list> to represent metadata attached to a message
+type topic
+string
+A type alias for string to represent a message topic
+
resource client
+A connection to a message-exchange service (e.g., buffer, broker, etc.).
+variant error
+Errors that can occur when using the messaging interface.
+Variant Cases
+
+
+timeout
+The request or operation timed out.
+
+
+connection: string
+An error occurred with the connection. Includes a message for additional context
+
+
+permission-denied: string
+A permission error occurred. Includes a message for additional context
+
+
+other: string
+A catch all for other types of errors
+
+
+resource message
+A message with a binary payload and additional information
+Functions
+[static]client.connect: func
+Params
+
+Return values
+
+[method]client.disconnect: func
+Params
+
+Return values
+
+[constructor]message: func
+Params
+
+Return values
+
+[method]message.topic: func
+The topic/subject/channel this message was received or should be sent on
+Params
+
+Return values
+
+[method]message.content-type: func
+An optional content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
+Params
+
+Return values
+
+[method]message.set-content-type: func
+Set the content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
+Params
+
+self: borrow<message >
+content-type: string
+
+[method]message.data: func
+An opaque blob of data
+Params
+
+Return values
+
+[method]message.set-data: func
+Set the opaque blob of data for this message, discarding the old value
+Params
+
+self: borrow<message >
+data: list<u8>
+
+[method]message.metadata: func
+Optional metadata (also called headers or attributes in some systems) attached to the
+message
+Params
+
+Return values
+
+[method]message.add-metadata: func
+Add a new key-value pair to the metadata, overwriting any existing value for the same key
+Params
+
+self: borrow<message >
+key: string
+value: string
+
+[method]message.set-metadata: func
+Set the metadata
+Params
+
+[method]message.remove-metadata: func
+Remove a key-value pair from the metadata
+Params
+
+self: borrow<message >
+key: string
+
+ Import interface wasi:messaging/request-reply@0.2.0-draft
+The request-reply interface allows a guest to send a message and await a response. This
+interface is considered optional as not all message services support the concept of
+request/reply. However, request/reply is a very common pattern in messaging and as such, we have
+included it as a core interface.
+
+Types
+type client
+client
+
+#### `type message`
+[`message`](#message)
+
+#### `type error`
+[`error`](#error)
+
+#### `resource request-options`
+
Options for a request/reply operation. This is a resource to allow for future expansion of
+options.
+Functions
+[constructor]request-options: func
+Creates a new request options resource with no options set.
+Return values
+
+[method]request-options.set-timeout-ms: func
+The maximum amount of time to wait for a response. If the timeout value is not set, then
+the request/reply operation will block until a message is received in response.
+Params
+
+[method]request-options.set-expected-replies: func
+The maximum number of replies to expect before returning.
+Params
+
+request: func
+Performs a blocking request/reply operation with an optional set of request options.
+The behavior of this function is largely dependent on the options given to the function.
+If no options are provided, then the request/reply operation will block until a single
+message is received in response. If a timeout is provided, then the request/reply operation
+will block for the specified amount of time before returning an error if no messages were
+received (or the list of messages that were received). If both a timeout and an expected
+number of replies are provided, the function should return when either condition is met
+(whichever comes first)—e.g., (1) if no replies were received within the timeout return an
+error, (2) if the maximum expected number of replies were received before timeout, return
+the list of messages, or (3) if the timeout is reached before the expected number of replies,
+return the list of messages received up to that point.
+Params
+
+Return values
+
+reply: func
+Replies to the given message with the given response message. The details of which topic
+the message is sent to is up to the implementation. This allows for reply-to details to be
+handled in the best way possible for the underlying messaging system.
+Please note that this reply functionality is different than something like HTTP because there
+are several use cases in which a reply might not be required for every message (so this would
+be a noop). There are also cases when you might want to reply and then continue processing.
+Additionally, you might want to reply to a message several times (such as providing an
+update). So this function is allowed to be called multiple times, unlike something like HTTP
+where the reply is sent and the connection is closed.
+Params
+
+Return values
+
+ Import interface wasi:messaging/producer@0.2.0-draft
+The producer interface is used to send messages to a channel/topic.
+
+Types
+type client
+client
+
+#### `type message`
+[`message`](#message)
+
+#### `type error`
+[`error`](#error)
+
+#### `type topic`
+[`topic`](#topic)
+
+----
+
Functions
+send: func
+Sends the message using the given client.
+Params
+
+Return values
+
diff --git a/imports.md b/imports.md
index cc4b825..6f19354 100644
--- a/imports.md
+++ b/imports.md
@@ -1,184 +1,182 @@
-
+ World imports
+The imports world defines the interfaces that the component will import from the host.
+It includes the producer interface for sending messages.
- Import interface wasi:messaging/messaging-types@0.2.0-draft
+ Import interface wasi:messaging/types@0.2.0-draft
Types
-resource client
-A connection to a message-exchange service (e.g., buffer, broker, etc.).
-resource error
-TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.
-type channel
+type metadata
+metadata
+A type alias for list> to represent metadata attached to a message
+type topic
string
-There are two types of channels:
-- publish-subscribe channel, which is a broadcast channel, and
-- point-to-point channel, which is a unicast channel.
-
The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.
-record guest-configuration
-Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs
-(e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).
-Record Fields
-
-channels: list<channel >
-extensions: option<list<(string, string)>>
-
-enum format-spec
-Format specification for messages
+A type alias for string to represent a message topic
+
resource client
+A connection to a message-exchange service (e.g., buffer, broker, etc.).
+variant error
+Errors that can occur when using the messaging interface.
+Variant Cases
-more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats
-message metadata can further decorate w/ things like format version, and so on.
+
+timeout
+The request or operation timed out.
+
+
+connection: string
+An error occurred with the connection. Includes a message for additional context
+
+
+permission-denied: string
+A permission error occurred. Includes a message for additional context
+
+
+other: string
+A catch all for other types of errors
+
-Enum Cases
+resource message
+A message with a binary payload and additional information
+Functions
+[static]client.connect: func
+Params
-cloudevents
-http
-amqp
-mqtt
-kafka
-raw
+name: string
-record message
-A message with a binary payload, a format specification, and decorative metadata.
-Record Fields
+Return values
-
-Functions
-[static]client.connect: func
+[method]client.disconnect: func
Params
-name: string
+self: borrow<client >
Return values
+[constructor]message: func
+Params
+
-[static]error.trace: func
Return values
- Import interface wasi:messaging/producer@0.2.0-draft
-
-Types
-type client
-client
-
-#### `type channel`
-[`channel`](#channel)
-
-#### `type message`
-[`message`](#message)
-
-#### `type error`
-[`error`](#error)
-
-----
-
Functions
-send: func
+[method]message.topic: func
+The topic/subject/channel this message was received or should be sent on
Params
Return values
- Import interface wasi:messaging/consumer@0.2.0-draft
-
-Types
-type client
-client
-
-#### `type message`
-[`message`](#message)
-
-#### `type channel`
-[`channel`](#channel)
-
-#### `type error`
-[`error`](#error)
-
-#### `type guest-configuration`
-[`guest-configuration`](#guest_configuration)
-
-----
-
Functions
-subscribe-try-receive: func
-Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None
+[method]message.content-type: func
+An optional content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
Params
Return values
- result<option<list<message >>, own<error >>
+ option<string>
-subscribe-receive: func
-Blocking receive until message with ephemeral subscription
+[method]message.set-content-type: func
+Set the content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
Params
+[method]message.data: func
+An opaque blob of data
+Params
+
Return values
-update-guest-configuration: func
-'Fit-all' type function for updating a guest's configuration – this could be useful for:
+[method]message.set-data: func
+Set the opaque blob of data for this message, discarding the old value
+Params
-unsubscribing from a channel,
-checkpointing,
-etc..
+self: borrow<message >
+data: list<u8>
+[method]message.metadata: func
+Optional metadata (also called headers or attributes in some systems) attached to the
+message
Params
Return values
-complete-message: func
-A message can exist under several statuses:
-(1) available: the message is ready to be read,
-(2) acquired: the message has been sent to a consumer (but still exists in the queue),
-(3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue,
-(4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:
+[method]message.add-metadata: func
+Add a new key-value pair to the metadata, overwriting any existing value for the same key
+Params
-deleted,
-sent to a dead-letter queue, or
-kept in the queue for further processing.
+self: borrow<message >
+key: string
+value: string
+[method]message.set-metadata: func
+Set the metadata
Params
-Return values
+[method]message.remove-metadata: func
+Remove a key-value pair from the metadata
+Params
-abandon-message: func
+ Import interface wasi:messaging/producer@0.2.0-draft
+The producer interface is used to send messages to a channel/topic.
+
+Types
+type client
+client
+
+#### `type message`
+[`message`](#message)
+
+#### `type error`
+[`error`](#error)
+
+#### `type topic`
+[`topic`](#topic)
+
+----
+
Functions
+send: func
+Sends the message using the given client.
Params
Return values
diff --git a/messaging-core.md b/messaging-core.md
new file mode 100644
index 0000000..65b3433
--- /dev/null
+++ b/messaging-core.md
@@ -0,0 +1,223 @@
+ World messaging-core
+The messaging-core world includes the basic imports and exports the incoming-handler,
+enabling the component to handle incoming messages without request/reply capabilities.
+
+Imports:
+
+
+Exports:
+
+
+
+ Import interface wasi:messaging/types@0.2.0-draft
+
+Types
+type metadata
+metadata
+A type alias for list> to represent metadata attached to a message
+type topic
+string
+A type alias for string to represent a message topic
+
resource client
+A connection to a message-exchange service (e.g., buffer, broker, etc.).
+variant error
+Errors that can occur when using the messaging interface.
+Variant Cases
+
+
+timeout
+The request or operation timed out.
+
+
+connection: string
+An error occurred with the connection. Includes a message for additional context
+
+
+permission-denied: string
+A permission error occurred. Includes a message for additional context
+
+
+other: string
+A catch all for other types of errors
+
+
+resource message
+A message with a binary payload and additional information
+Functions
+[static]client.connect: func
+Params
+
+Return values
+
+[method]client.disconnect: func
+Params
+
+Return values
+
+[constructor]message: func
+Params
+
+Return values
+
+[method]message.topic: func
+The topic/subject/channel this message was received or should be sent on
+Params
+
+Return values
+
+[method]message.content-type: func
+An optional content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
+Params
+
+Return values
+
+[method]message.set-content-type: func
+Set the content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
+Params
+
+self: borrow<message >
+content-type: string
+
+[method]message.data: func
+An opaque blob of data
+Params
+
+Return values
+
+[method]message.set-data: func
+Set the opaque blob of data for this message, discarding the old value
+Params
+
+self: borrow<message >
+data: list<u8>
+
+[method]message.metadata: func
+Optional metadata (also called headers or attributes in some systems) attached to the
+message
+Params
+
+Return values
+
+[method]message.add-metadata: func
+Add a new key-value pair to the metadata, overwriting any existing value for the same key
+Params
+
+self: borrow<message >
+key: string
+value: string
+
+[method]message.set-metadata: func
+Set the metadata
+Params
+
+[method]message.remove-metadata: func
+Remove a key-value pair from the metadata
+Params
+
+self: borrow<message >
+key: string
+
+ Import interface wasi:messaging/producer@0.2.0-draft
+The producer interface is used to send messages to a channel/topic.
+
+Types
+type client
+client
+
+#### `type message`
+[`message`](#message)
+
+#### `type error`
+[`error`](#error)
+
+#### `type topic`
+[`topic`](#topic)
+
+----
+
Functions
+send: func
+Sends the message using the given client.
+Params
+
+Return values
+
+ Export interface wasi:messaging/incoming-handler@0.2.0-draft
+
+Types
+type message
+message
+
+#### `type error`
+[`error`](#error)
+
+#### `type topic`
+[`topic`](#topic)
+
+----
+
Functions
+handle: func
+Whenever this guest receives a message in one of the subscribed topics, the message is
+sent to this handler. The guest is responsible for matching on the topic and handling the
+message accordingly. Implementors (such as hosts) calling this interface should make their
+own decisions on how to handle errors returned from this function.
+Params
+
+Return values
+
+get-topics: func
+Returns a list of topics (represented as strings) at runtime the guest should be subscribed
+Implementors should consider also allowing subscriptions to be made at compile time
+via some sort of configuration file. This function is intended to be called at the start of the
+guest's lifecycle before any messages are sent.
+Return values
+
diff --git a/messaging-request-reply.md b/messaging-request-reply.md
new file mode 100644
index 0000000..b1c5f75
--- /dev/null
+++ b/messaging-request-reply.md
@@ -0,0 +1,307 @@
+ World messaging-request-reply
+The messaging-request-reply world combines imports-request-reply with the incoming-handler
+export. This setup allows the host to interact with the component for both sending messages and
+handling incoming messages with request/reply capabilities.
+
+Imports:
+
+
+Exports:
+
+
+
+ Import interface wasi:messaging/types@0.2.0-draft
+
+Types
+type metadata
+metadata
+A type alias for list> to represent metadata attached to a message
+type topic
+string
+A type alias for string to represent a message topic
+
resource client
+A connection to a message-exchange service (e.g., buffer, broker, etc.).
+variant error
+Errors that can occur when using the messaging interface.
+Variant Cases
+
+
+timeout
+The request or operation timed out.
+
+
+connection: string
+An error occurred with the connection. Includes a message for additional context
+
+
+permission-denied: string
+A permission error occurred. Includes a message for additional context
+
+
+other: string
+A catch all for other types of errors
+
+
+resource message
+A message with a binary payload and additional information
+Functions
+[static]client.connect: func
+Params
+
+Return values
+
+[method]client.disconnect: func
+Params
+
+Return values
+
+[constructor]message: func
+Params
+
+Return values
+
+[method]message.topic: func
+The topic/subject/channel this message was received or should be sent on
+Params
+
+Return values
+
+[method]message.content-type: func
+An optional content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
+Params
+
+Return values
+
+[method]message.set-content-type: func
+Set the content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
+Params
+
+self: borrow<message >
+content-type: string
+
+[method]message.data: func
+An opaque blob of data
+Params
+
+Return values
+
+[method]message.set-data: func
+Set the opaque blob of data for this message, discarding the old value
+Params
+
+self: borrow<message >
+data: list<u8>
+
+[method]message.metadata: func
+Optional metadata (also called headers or attributes in some systems) attached to the
+message
+Params
+
+Return values
+
+[method]message.add-metadata: func
+Add a new key-value pair to the metadata, overwriting any existing value for the same key
+Params
+
+self: borrow<message >
+key: string
+value: string
+
+[method]message.set-metadata: func
+Set the metadata
+Params
+
+[method]message.remove-metadata: func
+Remove a key-value pair from the metadata
+Params
+
+self: borrow<message >
+key: string
+
+ Import interface wasi:messaging/request-reply@0.2.0-draft
+The request-reply interface allows a guest to send a message and await a response. This
+interface is considered optional as not all message services support the concept of
+request/reply. However, request/reply is a very common pattern in messaging and as such, we have
+included it as a core interface.
+
+Types
+type client
+client
+
+#### `type message`
+[`message`](#message)
+
+#### `type error`
+[`error`](#error)
+
+#### `resource request-options`
+
Options for a request/reply operation. This is a resource to allow for future expansion of
+options.
+Functions
+[constructor]request-options: func
+Creates a new request options resource with no options set.
+Return values
+
+[method]request-options.set-timeout-ms: func
+The maximum amount of time to wait for a response. If the timeout value is not set, then
+the request/reply operation will block until a message is received in response.
+Params
+
+[method]request-options.set-expected-replies: func
+The maximum number of replies to expect before returning.
+Params
+
+request: func
+Performs a blocking request/reply operation with an optional set of request options.
+The behavior of this function is largely dependent on the options given to the function.
+If no options are provided, then the request/reply operation will block until a single
+message is received in response. If a timeout is provided, then the request/reply operation
+will block for the specified amount of time before returning an error if no messages were
+received (or the list of messages that were received). If both a timeout and an expected
+number of replies are provided, the function should return when either condition is met
+(whichever comes first)—e.g., (1) if no replies were received within the timeout return an
+error, (2) if the maximum expected number of replies were received before timeout, return
+the list of messages, or (3) if the timeout is reached before the expected number of replies,
+return the list of messages received up to that point.
+Params
+
+Return values
+
+reply: func
+Replies to the given message with the given response message. The details of which topic
+the message is sent to is up to the implementation. This allows for reply-to details to be
+handled in the best way possible for the underlying messaging system.
+Please note that this reply functionality is different than something like HTTP because there
+are several use cases in which a reply might not be required for every message (so this would
+be a noop). There are also cases when you might want to reply and then continue processing.
+Additionally, you might want to reply to a message several times (such as providing an
+update). So this function is allowed to be called multiple times, unlike something like HTTP
+where the reply is sent and the connection is closed.
+Params
+
+Return values
+
+ Import interface wasi:messaging/producer@0.2.0-draft
+The producer interface is used to send messages to a channel/topic.
+
+Types
+type client
+client
+
+#### `type message`
+[`message`](#message)
+
+#### `type error`
+[`error`](#error)
+
+#### `type topic`
+[`topic`](#topic)
+
+----
+
Functions
+send: func
+Sends the message using the given client.
+Params
+
+Return values
+
+ Export interface wasi:messaging/incoming-handler@0.2.0-draft
+
+Types
+type message
+message
+
+#### `type error`
+[`error`](#error)
+
+#### `type topic`
+[`topic`](#topic)
+
+----
+
Functions
+handle: func
+Whenever this guest receives a message in one of the subscribed topics, the message is
+sent to this handler. The guest is responsible for matching on the topic and handling the
+message accordingly. Implementors (such as hosts) calling this interface should make their
+own decisions on how to handle errors returned from this function.
+Params
+
+Return values
+
+get-topics: func
+Returns a list of topics (represented as strings) at runtime the guest should be subscribed
+Implementors should consider also allowing subscriptions to be made at compile time
+via some sort of configuration file. This function is intended to be called at the start of the
+guest's lifecycle before any messages are sent.
+Return values
+
diff --git a/messaging.md b/messaging.md
index 3774be8..94e2842 100644
--- a/messaging.md
+++ b/messaging.md
@@ -1,220 +1,188 @@
-
+ World messaging
- Import interface wasi:messaging/messaging-types@0.2.0-draft
+ Import interface wasi:messaging/types@0.2.0-draft
Types
-resource client
+resource client
A connection to a message-exchange service (e.g., buffer, broker, etc.).
-resource error
-TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.
-type channel
-string
-There are two types of channels:
-- publish-subscribe channel, which is a broadcast channel, and
-- point-to-point channel, which is a unicast channel.
-
The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.
-record guest-configuration
-Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs
-(e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).
-Record Fields
-
-channels: list<channel >
-extensions: option<list<(string, string)>>
-
-enum format-spec
-Format specification for messages
-
-more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats
-message metadata can further decorate w/ things like format version, and so on.
-
-Enum Cases
-
-cloudevents
-http
-amqp
-mqtt
-kafka
-raw
-
-record message
-A message with a binary payload, a format specification, and decorative metadata.
-Record Fields
-
-data: list<u8>
-format: format-spec
-metadata: option<list<(string, string)>>
+variant error
+Errors that can occur when using the messaging interface.
+Variant Cases
+
+
+timeout
+The request or operation timed out.
+
+
+connection: string
+An error occurred with the connection. Includes a message for additional context
+
+
+other: string
+A catch all for other types of errors
+
-
+resource message
+A message with a binary payload and additional information
Functions
-[static]client.connect: func
+[static]client.connect: func
Params
-Return values
-
-[static]error.trace: func
Return values
- Import interface wasi:messaging/producer@0.2.0-draft
-
-Types
-type client
-client
-
-#### `type channel`
-[`channel`](#channel)
-
-#### `type message`
-[`message`](#message)
-
-#### `type error`
-[`error`](#error)
-
-----
-
Functions
-send: func
+[constructor]message: func
Params
Return values
- Import interface wasi:messaging/consumer@0.2.0-draft
-
-Types
-type client
-client
-
-#### `type message`
-[`message`](#message)
-
-#### `type channel`
-[`channel`](#channel)
-
-#### `type error`
-[`error`](#error)
-
-#### `type guest-configuration`
-[`guest-configuration`](#guest_configuration)
-
-----
-
Functions
-subscribe-try-receive: func
-Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None
+[method]message.topic: func
+The topic/subject/channel this message was received or should be sent on
Params
Return values
+[method]message.set-topic: func
+Set the topic/subject/channel this message should be sent on
+Params
+
+self: borrow<message >
+topic: string
-subscribe-receive: func
-Blocking receive until message with ephemeral subscription
+[method]message.content-type: func
+An optional content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
Params
Return values
-update-guest-configuration: func
-'Fit-all' type function for updating a guest's configuration – this could be useful for:
+[method]message.set-content-type: func
+Set the content-type describing the format of the data in the message. This is
+sometimes described as the "format" type
+Params
-unsubscribing from a channel,
-checkpointing,
-etc..
+self: borrow<message >
+content-type: string
+[method]message.data: func
+An opaque blob of data
Params
Return values
- result<_, own<error >>
+ list<u8>
-complete-message: func
-A message can exist under several statuses:
-(1) available: the message is ready to be read,
-(2) acquired: the message has been sent to a consumer (but still exists in the queue),
-(3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue,
-(4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:
+[method]message.set-data: func
+Set the opaque blob of data for this message, discarding the old value
+Params
-deleted,
-sent to a dead-letter queue, or
-kept in the queue for further processing.
+self: borrow<message >
+data: list<u8>
+[method]message.metadata: func
+Optional metadata (also called headers or attributes in some systems) attached to the
+message
Params
Return values
- result<_, own<error >>
+ option<list<(string, string)>>
-abandon-message: func
+[method]message.add-metadata: func
+Add a new key-value pair to the metadata, overwriting any existing value for the same key
Params
+ Import interface wasi:messaging/producer@0.2.0-draft
+The producer interface is used to send messages to a channel/topic.
+
+Types
+type client
+client
+
+#### `type message`
+[`message`](#message)
+
+#### `type error`
+[`error`](#error)
+
+----
+
Functions
+send: func
+Sends the message using the given client.
+Params
+
Return values
- Export interface wasi:messaging/messaging-guest@0.2.0-draft
+ Export interface wasi:messaging/incoming-handler@0.2.0-draft
Types
-type message
+type message
message
-#### `type guest-configuration`
-[`guest-configuration`](#guest_configuration)
-
-#### `type error`
+#### `type error`
[`error`](#error)
----
Functions
-configure: func
-Returns the list of channels (and extension metadata within guest-configuration) that
-this component should subscribe to and be handled by the subsequent handler within guest-configuration
-Return values
+handle: func
+Whenever this guest receives a message in one of the subscribed channels, the message is
+sent to this handler. The guest is responsible for matching on the channel and handling the
+message accordingly. Implementors (such as hosts) calling this interface should make their
+own decisions on how to handle errors returned from this function.
+Params
-handler: func
-Whenever this guest receives a message in one of the subscribed channels, the message is sent to this handler
-Params
+Return values
+subscribe: func
+Subscribe to a list of topics (represented as strings) at runtime.
+Implementors should consider also allowing subscriptions to be made at compile time via
+some sort of configuration file.
Return values
- result<_, own<error >>
+ result<list<string>, error >
diff --git a/wit/consumer.wit b/wit/consumer.wit
deleted file mode 100644
index a8f14d5..0000000
--- a/wit/consumer.wit
+++ /dev/null
@@ -1,27 +0,0 @@
-interface consumer {
- // {client, message, channel, error, guest-configuration}
- use messaging-types.{client, message, channel, error, guest-configuration};
-
- /// Blocking receive for t-milliseconds with ephemeral subscription – if no message is received, returns None
- subscribe-try-receive: func(c: client, ch: channel, t-milliseconds: u32) -> result>, error>;
-
- /// Blocking receive until message with ephemeral subscription
- subscribe-receive: func(c: client, ch: channel) -> result, error>;
-
- /// 'Fit-all' type function for updating a guest's configuration – this could be useful for:
- /// - unsubscribing from a channel,
- /// - checkpointing,
- /// - etc..
- update-guest-configuration: func(gc: guest-configuration) -> result<_, error>;
-
- /// A message can exist under several statuses:
- /// (1) available: the message is ready to be read,
- /// (2) acquired: the message has been sent to a consumer (but still exists in the queue),
- /// (3) accepted (result of complete-message): the message has been received and ACK-ed by a consumer and can be safely removed from the queue,
- /// (4) rejected (result of abandon-message): the message has been received and NACK-ed by a consumer, at which point it can be:
- /// - deleted,
- /// - sent to a dead-letter queue, or
- /// - kept in the queue for further processing.
- complete-message: func(m: message) -> result<_, error>;
- abandon-message: func(m: message) -> result<_, error>;
-}
diff --git a/wit/guest.wit b/wit/guest.wit
index 8e9cb4e..29c916d 100644
--- a/wit/guest.wit
+++ b/wit/guest.wit
@@ -1,10 +1,15 @@
-interface messaging-guest {
- use messaging-types.{message, guest-configuration, error};
+interface incoming-handler {
+ use types.{message, error, topic};
- /// Returns the list of channels (and extension metadata within guest-configuration) that
- /// this component should subscribe to and be handled by the subsequent handler within guest-configuration
- configure: func() -> result;
+ /// Whenever this guest receives a message in one of the subscribed topics, the message is
+ /// sent to this handler. The guest is responsible for matching on the topic and handling the
+ /// message accordingly. Implementors (such as hosts) calling this interface should make their
+ /// own decisions on how to handle errors returned from this function.
+ handle: func(message: message) -> result<_, error>;
- /// Whenever this guest receives a message in one of the subscribed channels, the message is sent to this handler
- handler: func(ms: list) -> result<_, error>;
+ // Returns a list of topics (represented as `string`s) at runtime the guest should be subscribed
+ // Implementors should consider also allowing subscriptions to be made at compile time
+ // via some sort of configuration file. This function is intended to be called at the start of the
+ // guest's lifecycle before any messages are sent.
+ get-topics: func() -> result, error>;
}
diff --git a/wit/messaging.wit b/wit/messaging.wit
index 26ad817..d15544b 100644
--- a/wit/messaging.wit
+++ b/wit/messaging.wit
@@ -1,11 +1,29 @@
package wasi:messaging@0.2.0-draft;
+/// The `imports` world defines the interfaces that the component will import from the host.
+/// It includes the `producer` interface for sending messages.
world imports {
- import producer;
- import consumer;
+ import producer;
}
-world messaging {
- include imports;
- export messaging-guest;
-}
\ No newline at end of file
+/// The `imports-request-reply` world extends `imports` by including the `request-reply` interface.
+/// This allows the component to perform request/reply messaging patterns.
+world imports-request-reply {
+ include imports;
+ import request-reply;
+}
+
+/// The `messaging-request-reply` world combines `imports-request-reply` with the `incoming-handler`
+/// export. This setup allows the host to interact with the component for both sending messages and
+/// handling incoming messages with request/reply capabilities.
+world messaging-request-reply {
+ include imports-request-reply;
+ export incoming-handler;
+}
+
+/// The `messaging-core` world includes the basic `imports` and exports the `incoming-handler`,
+/// enabling the component to handle incoming messages without request/reply capabilities.
+world messaging-core {
+ include imports;
+ export incoming-handler;
+}
diff --git a/wit/producer.wit b/wit/producer.wit
index fa795d8..9caba60 100644
--- a/wit/producer.wit
+++ b/wit/producer.wit
@@ -1,5 +1,7 @@
+/// The producer interface is used to send messages to a channel/topic.
interface producer {
- use messaging-types.{client, channel, message, error};
+ use types.{client, message, error, topic};
- send: func(c: client, ch: channel, m: list) -> result<_, error>;
+ /// Sends the message using the given client.
+ send: func(c: borrow, topic: topic, message: message) -> result<_, error>;
}
diff --git a/wit/request-reply.wit b/wit/request-reply.wit
new file mode 100644
index 0000000..da8a543
--- /dev/null
+++ b/wit/request-reply.wit
@@ -0,0 +1,47 @@
+/// The request-reply interface allows a guest to send a message and await a response. This
+/// interface is considered optional as not all message services support the concept of
+/// request/reply. However, request/reply is a very common pattern in messaging and as such, we have
+/// included it as a core interface.
+interface request-reply {
+ use types.{client, message, error};
+
+ /// Options for a request/reply operation. This is a resource to allow for future expansion of
+ /// options.
+ resource request-options {
+ /// Creates a new request options resource with no options set.
+ constructor();
+
+ /// The maximum amount of time to wait for a response. If the timeout value is not set, then
+ /// the request/reply operation will block until a message is received in response.
+ set-timeout-ms: func(timeout-ms: u32);
+
+ /// The maximum number of replies to expect before returning.
+ set-expected-replies: func(expected-replies: u32);
+ }
+
+ /// Performs a blocking request/reply operation with an optional set of request options.
+ ///
+ /// The behavior of this function is largely dependent on the options given to the function.
+ /// If no options are provided, then the request/reply operation will block until a single
+ /// message is received in response. If a timeout is provided, then the request/reply operation
+ /// will block for the specified amount of time before returning an error if no messages were
+ /// received (or the list of messages that were received). If both a timeout and an expected
+ /// number of replies are provided, the function should return when either condition is met
+ /// (whichever comes first)—e.g., (1) if no replies were received within the timeout return an
+ /// error, (2) if the maximum expected number of replies were received before timeout, return
+ /// the list of messages, or (3) if the timeout is reached before the expected number of replies,
+ /// return the list of messages received up to that point.
+ request: func(c: borrow, message: borrow, options: option) -> result, error>;
+
+ /// Replies to the given message with the given response message. The details of which topic
+ /// the message is sent to is up to the implementation. This allows for reply-to details to be
+ /// handled in the best way possible for the underlying messaging system.
+ ///
+ /// Please note that this reply functionality is different than something like HTTP because there
+ /// are several use cases in which a reply might not be required for every message (so this would
+ /// be a noop). There are also cases when you might want to reply and then continue processing.
+ /// Additionally, you might want to reply to a message several times (such as providing an
+ /// update). So this function is allowed to be called multiple times, unlike something like HTTP
+ /// where the reply is sent and the connection is closed.
+ reply: func(reply-to: borrow, message: message) -> result<_, error>;
+}
diff --git a/wit/types.wit b/wit/types.wit
index b7c9803..5c0b417 100644
--- a/wit/types.wit
+++ b/wit/types.wit
@@ -1,44 +1,51 @@
-interface messaging-types {
+interface types {
+ /// A type alias for list> to represent metadata attached to a message
+ type metadata = list>;
+
+ /// A type alias for string to represent a message topic
+ type topic = string;
+
/// A connection to a message-exchange service (e.g., buffer, broker, etc.).
resource client {
connect: static func(name: string) -> result;
+ disconnect: func() -> result<_, error>;
}
- /// TODO(danbugs): This should be eventually extracted as an underlying type for other wasi-cloud-core interfaces.
- resource error {
- trace: static func() -> string;
+ /// Errors that can occur when using the messaging interface.
+ variant error {
+ /// The request or operation timed out.
+ timeout,
+ /// An error occurred with the connection. Includes a message for additional context
+ connection(string),
+ /// A permission error occurred. Includes a message for additional context
+ permission-denied(string),
+ /// A catch all for other types of errors
+ other(string),
}
- /// There are two types of channels:
- /// - publish-subscribe channel, which is a broadcast channel, and
- /// - point-to-point channel, which is a unicast channel.
- ///
- /// The interface doesn't highlight this difference in the type itself as that's uniquely a consumer issue.
- type channel = string;
-
- /// Configuration includes a required list of channels the guest is subscribing to, and an optional list of extensions key-value pairs
- /// (e.g., partitions/offsets to read from in Kafka/EventHubs, QoS etc.).
- record guest-configuration {
- channels: list,
- extensions: option>>
- }
-
- /// Format specification for messages
- /// - more info: https://github.com/clemensv/spec/blob/registry-extensions/registry/spec.md#message-formats
- /// - message metadata can further decorate w/ things like format version, and so on.
- enum format-spec {
- cloudevents,
- http,
- amqp,
- mqtt,
- kafka,
- raw
- }
-
- /// A message with a binary payload, a format specification, and decorative metadata.
- record message {
- data: list,
- format: format-spec,
- metadata: option>>
+ /// A message with a binary payload and additional information
+ resource message {
+ constructor(data: list);
+ /// The topic/subject/channel this message was received or should be sent on
+ topic: func() -> topic;
+ /// An optional content-type describing the format of the data in the message. This is
+ /// sometimes described as the "format" type
+ content-type: func() -> option;
+ /// Set the content-type describing the format of the data in the message. This is
+ /// sometimes described as the "format" type
+ set-content-type: func(content-type: string);
+ /// An opaque blob of data
+ data: func() -> list;
+ /// Set the opaque blob of data for this message, discarding the old value
+ set-data: func(data: list);
+ /// Optional metadata (also called headers or attributes in some systems) attached to the
+ /// message
+ metadata: func() -> option;
+ /// Add a new key-value pair to the metadata, overwriting any existing value for the same key
+ add-metadata: func(key: string, value: string);
+ /// Set the metadata
+ set-metadata: func(meta: metadata);
+ /// Remove a key-value pair from the metadata
+ remove-metadata: func(key: string);
}
-}
\ No newline at end of file
+}
From 55c13e8df56419e22b4e69f00d0883c9a3bfda3c Mon Sep 17 00:00:00 2001
From: Dan Chiarlone
Date: Thu, 7 Nov 2024 00:22:58 +0000
Subject: [PATCH 8/8] Adressing feedback from upstream sync (#27)
* Adressing feedback from upstream sync
- clarified comment on `topic` function.
- added portability comment onto `metadata` to avoid it being used as an escape-hatch.
- removed `get-topics` func in favour of using wasi-config for runtime configuration.
* [chore] update markdown files
Signed-off-by: danbugs
---------
Signed-off-by: danbugs
---
imports-request-reply.md | 5 +++--
imports.md | 5 +++--
messaging-core.md | 14 +++-----------
messaging-request-reply.md | 14 +++-----------
wit/guest.wit | 6 ------
wit/types.wit | 5 +++--
6 files changed, 15 insertions(+), 34 deletions(-)
diff --git a/imports-request-reply.md b/imports-request-reply.md
index 36f7f59..d36307e 100644
--- a/imports-request-reply.md
+++ b/imports-request-reply.md
@@ -73,7 +73,7 @@ This allows the component to perform request/reply messaging patterns.
own<message >
[method]message.topic: func
-The topic/subject/channel this message was received or should be sent on
+The topic/subject/channel this message was received on
Params
self: borrow<message >
@@ -120,7 +120,8 @@ sometimes described as the "format" type
[method]message.metadata: func
Optional metadata (also called headers or attributes in some systems) attached to the
-message
+message. This metadata is simply decoration and should not be interpreted by a host
+to ensure portability across different implementors (e.g., Kafka -> NATS, etc.).
Params
self: borrow<message >
diff --git a/imports.md b/imports.md
index 6f19354..2900330 100644
--- a/imports.md
+++ b/imports.md
@@ -72,7 +72,7 @@ It includes the producer interface for sending messages.
own<message >
[method]message.topic: func
-The topic/subject/channel this message was received or should be sent on
+The topic/subject/channel this message was received on
Params
self: borrow<message >
@@ -119,7 +119,8 @@ sometimes described as the "format" type
[method]message.metadata: func
Optional metadata (also called headers or attributes in some systems) attached to the
-message
+message. This metadata is simply decoration and should not be interpreted by a host
+to ensure portability across different implementors (e.g., Kafka -> NATS, etc.).
Params
self: borrow<message >
diff --git a/messaging-core.md b/messaging-core.md
index 65b3433..1302613 100644
--- a/messaging-core.md
+++ b/messaging-core.md
@@ -77,7 +77,7 @@ enabling the component to handle incoming messages without request/reply capabil
own<message >
[method]message.topic: func
-The topic/subject/channel this message was received or should be sent on
+The topic/subject/channel this message was received on
Params
self: borrow<message >
@@ -124,7 +124,8 @@ sometimes described as the "format" type
[method]message.metadata: func
Optional metadata (also called headers or attributes in some systems) attached to the
-message
+message. This metadata is simply decoration and should not be interpreted by a host
+to ensure portability across different implementors (e.g., Kafka -> NATS, etc.).
Params
self: borrow<message >
@@ -212,12 +213,3 @@ own decisions on how to handle errors returned from this function.
-get-topics: func
-Returns a list of topics (represented as strings) at runtime the guest should be subscribed
-Implementors should consider also allowing subscriptions to be made at compile time
-via some sort of configuration file. This function is intended to be called at the start of the
-guest's lifecycle before any messages are sent.
-Return values
-
diff --git a/messaging-request-reply.md b/messaging-request-reply.md
index b1c5f75..37d61c0 100644
--- a/messaging-request-reply.md
+++ b/messaging-request-reply.md
@@ -79,7 +79,7 @@ handling incoming messages with request/reply capabilities.
own<message >
[method]message.topic: func
-The topic/subject/channel this message was received or should be sent on
+The topic/subject/channel this message was received on
Params
self: borrow<message >
@@ -126,7 +126,8 @@ sometimes described as the "format" type
[method]message.metadata: func
Optional metadata (also called headers or attributes in some systems) attached to the
-message
+message. This metadata is simply decoration and should not be interpreted by a host
+to ensure portability across different implementors (e.g., Kafka -> NATS, etc.).
Params
self: borrow<message >
@@ -296,12 +297,3 @@ own decisions on how to handle errors returned from this function.
-get-topics: func
-Returns a list of topics (represented as strings) at runtime the guest should be subscribed
-Implementors should consider also allowing subscriptions to be made at compile time
-via some sort of configuration file. This function is intended to be called at the start of the
-guest's lifecycle before any messages are sent.
-Return values
-
diff --git a/wit/guest.wit b/wit/guest.wit
index 29c916d..73cfc72 100644
--- a/wit/guest.wit
+++ b/wit/guest.wit
@@ -6,10 +6,4 @@ interface incoming-handler {
/// message accordingly. Implementors (such as hosts) calling this interface should make their
/// own decisions on how to handle errors returned from this function.
handle: func(message: message) -> result<_, error>;
-
- // Returns a list of topics (represented as `string`s) at runtime the guest should be subscribed
- // Implementors should consider also allowing subscriptions to be made at compile time
- // via some sort of configuration file. This function is intended to be called at the start of the
- // guest's lifecycle before any messages are sent.
- get-topics: func() -> result, error>;
}
diff --git a/wit/types.wit b/wit/types.wit
index 5c0b417..d7e8292 100644
--- a/wit/types.wit
+++ b/wit/types.wit
@@ -26,7 +26,7 @@ interface types {
/// A message with a binary payload and additional information
resource message {
constructor(data: list);
- /// The topic/subject/channel this message was received or should be sent on
+ /// The topic/subject/channel this message was received on
topic: func() -> topic;
/// An optional content-type describing the format of the data in the message. This is
/// sometimes described as the "format" type
@@ -39,7 +39,8 @@ interface types {
/// Set the opaque blob of data for this message, discarding the old value
set-data: func(data: list);
/// Optional metadata (also called headers or attributes in some systems) attached to the
- /// message
+ /// message. This metadata is simply decoration and should not be interpreted by a host
+ /// to ensure portability across different implementors (e.g., Kafka -> NATS, etc.).
metadata: func() -> option;
/// Add a new key-value pair to the metadata, overwriting any existing value for the same key
add-metadata: func(key: string, value: string);