package uqbar:process@0.4.0; interface standard { // JSON is passed over WASM boundary as a string. type json = string; type node-id = string; // context, like ipc, is a protocol-defined serialized byte array. // 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 = list; 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: list, metadata: option, // to grab payload, use get_payload() } record response { inherit: bool, ipc: list, 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? } // system utils: print-to-terminal: func(verbosity: u8, message: string); // **more will be added here with regard to blockchains** get-eth-block: func() -> u64; // process management: set-on-panic: func(on-panic: on-panic); get-state: func() -> option>; set-state: func(bytes: list); clear-state: func(); 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 get-capabilities: func() -> list; // gets a single specific capability get-capability: func(issuer: address, params: json) -> option; // attaches a specific signed capability to our next message attach-capability: func(capability: signed-capability); // saves capabilities to our store, so we can use them 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. 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. create-capability: func(to: process-id, params: json); // take a signed capability and save it to a given locally-running process 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 receive: func() -> result, tuple>>; // gets payload, if any, of the message we just received get-payload: func() -> option; // send message(s) to target(s) send-request: func(target: address, request: request, context: option, payload: option); send-requests: func(requests: list, option>>); 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 send-and-await-response: func(target: address, request: request, payload: option) -> result, send-error>; } world lib { import standard; } world process { include lib; export init: func(our: string); }