// Versioning: the broker matches a plugin's imports and exports to these
// types by structure, so any change to a function, record, variant, enum or
// flags breaks plugins built for the old shape. New hooks, fields or cases
// therefore need a new minor version, 0.2.0. Releases 0.1.x change only
// documentation.
package mercurio:plugin@0.1.0;
interface types {
enum level { trace, debug, info, warn, error }
enum access { read, write, subscribe }
enum decision { allow, deny, defer }
enum qos { at-most-once, at-least-once, exactly-once }
/// `username` is set only for a user the broker verified, except in
/// `authenticate` and in `auth-step` during CONNECT, where it is the user
/// name the client claims.
record client-info {
client-id: string,
username: option<string>,
peer: option<string>,
}
/// A published message. The properties are MQTT 5's application
/// properties; MQTT 3.x messages have none.
record message {
topic: string,
payload: list<u8>,
qos: qos,
retain: bool,
payload-is-utf8: bool,
message-expiry: option<u32>,
content-type: option<string>,
response-topic: option<string>,
correlation-data: option<list<u8>>,
user-properties: list<tuple<string, string>>,
}
/// `replace` carries the whole changed message.
variant message-action { keep, drop, replace(message) }
record disconnected { client: client-info, reason: option<u8> }
record subscribed { client: client-info, filter: string, qos: qos }
record unsubscribed { client: client-info, filter: string }
variant event {
connected(client-info),
disconnected(disconnected),
subscribed(subscribed),
unsubscribed(unsubscribed),
session-expired(string),
}
/// The hooks a plugin implements; the broker calls no others.
flags hooks { authenticate, authorize, on-message, on-event, on-tick, on-control }
record registration {
hooks: hooks,
/// MQTT 5 enhanced authentication methods it answers `auth-step` for
auth-methods: list<string>,
}
/// What `continue` sends: `data` goes to the client, and the broker
/// keeps `state` for the next step.
record challenge { data: list<u8>, state: list<u8> }
/// The result of one step of enhanced authentication. `continue` sends
/// a challenge to the client; `success` accepts it, as the user it
/// names, if any; `failed` refuses it with an MQTT reason code.
variant auth-result { %continue(challenge), success(option<string>), failed(u8) }
}
interface host {
use types.{level};
log: func(level: level, message: string);
/// A store shared by every copy of the plugin, kept in memory until the
/// broker stops. It is shared but not transactional: a get followed by a
/// set can race with another copy.
kv-get: func(key: string) -> option<list<u8>>;
/// Fails when the store refuses the value, for example over a quota.
kv-set: func(key: string, value: list<u8>) -> result<_, string>;
kv-delete: func(key: string);
kv-list: func(prefix: string) -> list<string>;
}
interface guest {
use types.{client-info, access, decision, message, message-action, event, registration, auth-result};
init: func(config: list<tuple<string, string>>) -> result<registration, string>;
authenticate: func(client: client-info, username: string, password: list<u8>) -> decision;
/// `state` is None on the first step of an exchange. During CONNECT,
/// `client` carries the user name the client claims; on
/// re-authentication, the verified user.
auth-step: func(client: client-info, method: string, state: option<list<u8>>, data: option<list<u8>>) -> auth-result;
authorize: func(client: client-info, topic: string, access: access) -> decision;
on-message: func(client: client-info, message: message) -> message-action;
on-event: func(event: event);
on-tick: func();
on-control: func(client: client-info, path: string, payload: list<u8>) -> option<list<u8>>;
}
world plugin {
import host;
export guest;
}