package component:uq-process@0.1.0 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, bytes: list, } 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, ipc: option, metadata: option, // to grab payload, use get_payload() } record response { inherit: bool, ipc: option, metadata: option, // 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>), } variant capabilities { none, all, some(list), } record signed-capability { issuer: address, params: json, signature: list, } // 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>>), } // 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, } 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) // **more will be added here with regard to blockchains** import get-eth-block: func() -> u64 // process management: import set-on-panic: func(on-panic: on-panic) import get-state: func() -> option> import set-state: func(bytes: list) import clear-state: func() import spawn: func( name: option, wasm-path: string, // must be located within package's drive on-panic: on-panic, capabilities: capabilities, public: bool ) -> result // capabilities management // gives us all our signed capabilities so we can send them to others import get-capabilities: func() -> list // gets a single specific capability import get-capability: func(issuer: address, params: json) -> option // 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) // 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 // generates a new capability with our process as the issuer and gives it to the target, // which must be a locally-running process. import create-capability: func(to: process-id, params: json) // take a signed capability and save it to a given locally-running process import share-capability: func(to: process-id, capability: signed-capability) // 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>> // gets payload, if any, of the message we just received import get-payload: func() -> option // send message(s) to target(s) import send-request: func(target: address, request: request, context: option, payload: option) import send-requests: func(requests: list, option>>) import send-response: func(response: response, payload: option) // 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) -> result, send-error> }