nectar/wit/uqbar.wit

200 lines
5.9 KiB
Plaintext

package component:uq-process
interface types {
// JSON is passed over WASM boundary as a string.
type json = string
type node-id = string
// context is a string of UTF-8 JSON.
// it is used when building a Request to save information
// that will not be part of a Response, in order to more
// easily handle ("contextualize") that Response.
type context = json
record process-id {
process-name: string,
package-name: string,
publisher-node: node-id,
}
// TODO better name for this
record address {
node: node-id,
process: process-id,
}
record payload {
mime: option<string>,
bytes: list<u8>,
}
record request {
// if true, this request inherits context AND payload of incipient
// request, and cannot have its own context.
inherit: bool,
// if Some, this request expects a response in the number of seconds given
expects-response: option<u64>,
ipc: option<json>,
metadata: option<json>,
// to grab payload, use get_payload()
}
record response {
ipc: option<json>,
metadata: option<json>,
// to grab payload, use get_payload()
}
// a message can be a request or a response.
// within a response, there is a result which surfaces any error
// that happened because of a request.
// a successful response will contain the context of the request
// it matches, if any was set.
variant message {
request(request),
response(tuple<response, option<context>>),
}
variant capabilities {
none,
all,
some(list<signed-capability>),
}
record signed-capability {
issuer: address,
params: json,
signature: list<u8>,
}
// on-panic is a setting that determines what happens when a process panics.
// NOTE: requests should have expects-response set to false, will always be set to that by kernel
variant on-panic {
none,
restart,
requests(list<tuple<address, request, option<payload>>>),
}
// network errors come from trying to send a message to another node.
// a message can fail by timing out, or by the node being entirely unreachable (offline).
// in either case, the message is not delivered, and the process that sent it
// receives that message along with any assigned context and/or payload,
// and is free to handle it as it sees fit.
// note that if the message is a response, the process can issue a response again,
// and it will be directed to the same (remote) request as the original.
record send-error {
kind: send-error-kind,
message: message,
payload: option<payload>,
}
enum send-error-kind {
offline,
timeout,
}
enum spawn-error {
name-taken,
no-file-at-path,
// TODO more here?
}
}
world uq-process {
use types.{
json,
node-id,
context,
process-id,
address,
payload,
request,
response,
message,
capabilities,
signed-capability,
on-panic,
send-error,
send-error-kind,
spawn-error
}
// entry point to all programs
export init: func(our: address)
// system utils:
import print-to-terminal: func(verbosity: u8, message: string)
import get-unix-time: func() -> u64
import get-eth-block: func() -> u64
// process management:
import set-on-panic: func(on-panic: on-panic)
import get-state: func() -> option<list<u8>>
import set-state: func(bytes: list<u8>)
import clear-state: func()
import spawn: func(
name: option<string>,
wasm-path: string, // must be located within package's drive
on-panic: on-panic,
capabilities: capabilities,
public: bool
) -> result<process-id, spawn-error>
// capabilities management
// gives us all our signed capabilities so we can send them to others
import get-capabilities: func() -> list<signed-capability>
// gets a single specific capability
import get-capability: func(issuer: address, params: json) -> option<signed-capability>
// attaches a specific signed capability to our next message
import attach-capability: func(capability: signed-capability)
// saves capabilities to our store, so we can use them
import save-capabilities: func(capabilities: list<signed-capability>)
// check to see if the sender of a prompting message has a given capability, issued by us
// if the prompting message has a remote source, they must have attached it.
import has-capability: func(params: json) -> bool
// message I/O:
// ingest next message when it arrives along with its source.
// almost all long-running processes will call this in a loop
import receive: func() -> result<tuple<address, message>, tuple<send-error, option<context>>>
// gets payload, if any, of the message we just received
import get-payload: func() -> option<payload>
// send message(s) to target(s)
import send-request:
func(target: address, request: request, context: option<context>, payload: option<payload>)
import send-requests:
func(requests: list<tuple<address, request, option<context>, option<payload>>>)
import send-response:
func(response: response, payload: option<payload>)
// send a single request, then block (internally) until its response
// the type is Message but will always contain Response
import send-and-await-response:
func(target: address, request: request, payload: option<payload>) ->
result<tuple<address, message>, send-error>
// wasi
import wasi:random/insecure
import wasi:random/insecure-seed
import wasi:random/random
}